From 14ae8d2dc9cc23c34b7433b1ed8851a4f4d36f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Sun, 24 Aug 2008 13:32:57 +0000 Subject: [PATCH] Support for unredirecting fullscreen windows, i.e. games etc. can paint directly and not be slowed down by going through compositing. Turned on and no UI option in the naive hope that it won't cause any real problems. Maybe effects doing window previews should get API to suspend unredirect though. svn path=/trunk/KDE/kdebase/workspace/; revision=851742 --- client.cpp | 3 + client.h | 1 + composite.cpp | 152 +++++++++++++++++++++++++++++++++++++++++++++- deleted.h | 1 + geometry.cpp | 4 ++ layers.cpp | 1 + options.cpp | 2 + options.h | 1 + scene.cpp | 11 +++- scene.h | 7 +++ scene_xrender.cpp | 2 + toplevel.cpp | 2 + toplevel.h | 11 ++++ unmanaged.h | 1 + workspace.cpp | 5 +- workspace.h | 7 ++- 16 files changed, 206 insertions(+), 5 deletions(-) diff --git a/client.cpp b/client.cpp index 77682670e2..d4f5440772 100644 --- a/client.cpp +++ b/client.cpp @@ -901,6 +901,7 @@ void Client::internalShow( allowed_t ) map( Allowed ); if( old == Kept ) updateHiddenPreview(); + workspace()->checkUnredirect(); } void Client::internalHide( allowed_t ) @@ -915,6 +916,7 @@ void Client::internalHide( allowed_t ) updateHiddenPreview(); addWorkspaceRepaint( geometry()); workspace()->clientHidden( this ); + workspace()->checkUnredirect(); } void Client::internalKeep( allowed_t ) @@ -929,6 +931,7 @@ void Client::internalKeep( allowed_t ) updateHiddenPreview(); addWorkspaceRepaint( geometry()); workspace()->clientHidden( this ); + workspace()->checkUnredirect(); } /*! diff --git a/client.h b/client.h index a50c57a41c..e316c60b18 100644 --- a/client.h +++ b/client.h @@ -329,6 +329,7 @@ class Client protected: virtual void debug( kdbgstream& stream ) const; + virtual bool shouldUnredirect() const; private slots: void pingTimeout(); diff --git a/composite.cpp b/composite.cpp index c6d1489a71..7881c8c2e7 100644 --- a/composite.cpp +++ b/composite.cpp @@ -319,6 +319,10 @@ void Workspace::performCompositing() // does not actually caused heavy load by firing the timer often too quickly. if( compositeTimer.interval() != compositeRate ) compositeTimer.start( compositeRate ); + // Note: It would seem here we should undo suspended unredirect, but when scenes need + // it for some reason, e.g. transformations or translucency, the next pass that does not + // need this anymore and paints normally will also reset the suspended unredirect. + // Otherwise the window would not be painted normally anyway. return; } // create a list of all windows in the stacking order @@ -335,7 +339,7 @@ void Workspace::performCompositing() #if 0 // There is a bug somewhere that prevents this from working properly (#160393), but additionally // this cannot be used so carelessly - needs protections against broken clients, the window - // should not get focus before it's displayed and so on. + // should not get focus before it's displayed, handle unredirected windows properly and so on. foreach( Toplevel* c, tmp ) if( c->readyForPainting()) windows.append( c ); @@ -406,6 +410,8 @@ void Workspace::setupOverlay( Window w ) assert( Extensions::shapeInputAvailable()); XSetWindowBackgroundPixmap( display(), overlay, None ); XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted ); + XRectangle rec = { 0, 0, displayWidth(), displayHeight() }; + XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, &rec, 1, ShapeSet, Unsorted ); if( w != None ) { XSetWindowBackgroundPixmap( display(), w, None ); @@ -428,6 +434,10 @@ void Workspace::destroyOverlay() { if( overlay == None ) return; + // reset the overlay shape + XRectangle rec = { 0, 0, displayWidth(), displayHeight() }; + XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, &rec, 1, ShapeSet, Unsorted ); + XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, &rec, 1, ShapeSet, Unsorted ); #ifdef HAVE_XCOMPOSITE_OVERLAY XCompositeReleaseOverlayWindow( display(), overlay ); #endif @@ -440,6 +450,58 @@ bool Workspace::compositingActive() return compositing(); } +// force is needed when the list of windows changes (e.g. a window goes away) +void Workspace::checkUnredirect( bool force ) + { + if( !compositing() || overlay == None || !options->unredirectFullscreen ) + return; + if( force ) + forceUnredirectCheck = true; + if( !unredirectTimer.isActive()) + unredirectTimer.start( 0 ); + } + +void Workspace::delayedCheckUnredirect() + { + ToplevelList list; + bool changed = forceUnredirectCheck; + foreach( Client* c, clients ) + list.append( c ); + foreach( Unmanaged* c, unmanaged ) + list.append( c ); + foreach( Toplevel* c, list ) + { + if( c->updateUnredirectedState()) + changed = true; + } + // no desktops, no Deleted ones + if( !changed ) + return; + forceUnredirectCheck = false; + // Cut out parts from the overlay window where unredirected windows are, + // so that they are actually visible. + QRegion reg( 0, 0, displayWidth(), displayHeight()); + foreach( Toplevel* c, list ) + { + if( c->unredirected()) + reg -= c->geometry(); + } + QVector< QRect > rects = reg.rects(); + XRectangle* xrects = new XRectangle[ rects.count() ]; + for( int i = 0; + i < rects.count(); + ++i ) + { + xrects[ i ].x = rects[ i ].x(); + xrects[ i ].y = rects[ i ].y(); + xrects[ i ].width = rects[ i ].width(); + xrects[ i ].height = rects[ i ].height(); + } + XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, + xrects, rects.count(), ShapeSet, Unsorted ); + delete[] xrects; + } + //**************************************** // Toplevel //**************************************** @@ -455,6 +517,8 @@ void Toplevel::setupCompositing() damage_region = QRegion( 0, 0, width(), height()); effect_window = new EffectWindowImpl(); effect_window->setWindow( this ); + unredirect = false; + workspace()->checkUnredirect( true ); #endif } @@ -463,6 +527,7 @@ void Toplevel::finishCompositing() #ifdef KWIN_HAVE_COMPOSITING if( damage_handle == None ) return; + workspace()->checkUnredirect( true ); if( effect_window->window() == this ) // otherwise it's already passed to Deleted, don't free data { discardWindowPixmap(); @@ -491,6 +556,8 @@ Pixmap Toplevel::createWindowPixmap() { #ifdef KWIN_HAVE_COMPOSITING assert( compositing()); + if( unredirected()) + return None; grabXServer(); KXErrorHandler err; Pixmap pix = XCompositeNameWindowPixmap( display(), frameId()); @@ -641,6 +708,34 @@ void Toplevel::addWorkspaceRepaint( const QRect& r2 ) workspace()->addRepaint( r ); } +bool Toplevel::updateUnredirectedState() + { + assert( compositing()); + bool should = shouldUnredirect() && !unredirectSuspend && !shape() && !hasAlpha() && opacity() == 1.0; + if( should && !unredirect ) + { + unredirect = true; + kDebug( 1212 ) << "Unredirecting:" << this; + XCompositeUnredirectWindow( display(), frameId(), CompositeRedirectManual ); + return true; + } + else if( !should && unredirect ) + { + unredirect = false; + kDebug( 1212 ) << "Redirecting:" << this; + XCompositeRedirectWindow( display(), frameId(), CompositeRedirectManual ); + discardWindowPixmap(); + return true; + } + return false; + } + +void Toplevel::suspendUnredirect( bool suspend ) + { + unredirectSuspend = suspend; + workspace()->checkUnredirect(); + } + //**************************************** // Client //**************************************** @@ -657,4 +752,59 @@ void Client::finishCompositing() updateVisibility(); } +bool Client::shouldUnredirect() const + { + if( isActiveFullScreen()) + { + ToplevelList stacking = workspace()->xStackingOrder(); + for( int pos = stacking.count() - 1; + pos >= 0; + --pos ) + { + Toplevel* c = stacking.at( pos ); + if( c == this ) // is not covered by any other window, ok to unredirect + return true; + if( c->geometry().intersects( geometry())) + return false; + } + abort(); + } + return false; + } + +//**************************************** +// Unmanaged +//**************************************** + +bool Unmanaged::shouldUnredirect() const + { +// it must cover whole display or one xinerama screen, and be the topmost there + if( geometry() == workspace()->clientArea( FullArea, geometry().center(), workspace()->currentDesktop()) + || geometry() == workspace()->clientArea( ScreenArea, geometry().center(), workspace()->currentDesktop())) + { + ToplevelList stacking = workspace()->xStackingOrder(); + for( int pos = stacking.count() - 1; + pos >= 0; + --pos ) + { + Toplevel* c = stacking.at( pos ); + if( c == this ) // is not covered by any other window, ok to unredirect + return true; + if( c->geometry().intersects( geometry())) + return false; + } + abort(); + } + return false; + } + +//**************************************** +// Deleted +//**************************************** + +bool Deleted::shouldUnredirect() const + { + return false; + } + } // namespace diff --git a/deleted.h b/deleted.h index 9483d6ff92..a8192662c6 100644 --- a/deleted.h +++ b/deleted.h @@ -41,6 +41,7 @@ class Deleted virtual QSize clientSize() const; protected: virtual void debug( kdbgstream& stream ) const; + virtual bool shouldUnredirect() const; private: Deleted( Workspace *ws ); // use create() void copyToDeleted( Toplevel* c ); diff --git a/geometry.cpp b/geometry.cpp index f270e1cf3a..2b13cbd7c5 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1861,6 +1861,7 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) checkMaximizeGeometry(); workspace()->checkActiveScreen( this ); workspace()->updateStackingOrder(); + workspace()->checkUnredirect(); if( resized ) { discardWindowPixmap(); @@ -1934,6 +1935,7 @@ void Client::plainResize( int w, int h, ForceGeometry_t force ) checkMaximizeGeometry(); workspace()->checkActiveScreen( this ); workspace()->updateStackingOrder(); + workspace()->checkUnredirect(); discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); @@ -1977,6 +1979,7 @@ void Client::move( int x, int y, ForceGeometry_t force ) checkMaximizeGeometry(); workspace()->checkActiveScreen( this ); workspace()->updateStackingOrder(); + workspace()->checkUnredirect(); // client itself is not damaged addWorkspaceRepaint( geom_before_block ); addWorkspaceRepaint( geom ); // trigger repaint of window's new location @@ -2328,6 +2331,7 @@ void Client::setFullScreen( bool set, bool user ) } } updateWindowRules(); + workspace()->checkUnredirect(); } int Client::checkFullScreenHack( const QRect& geom ) const diff --git a/layers.cpp b/layers.cpp index 5e4adef4bf..7734fc2a27 100644 --- a/layers.cpp +++ b/layers.cpp @@ -748,6 +748,7 @@ ToplevelList Workspace::xStackingOrder() const x_stacking.append( c ); if( windows != NULL ) XFree( windows ); + const_cast< Workspace* >( this )->checkUnredirect(); return x_stacking; } diff --git a/options.cpp b/options.cpp index 42b39f083d..087cd0b962 100644 --- a/options.cpp +++ b/options.cpp @@ -233,6 +233,8 @@ void Options::reloadCompositingSettings(const CompositingPrefs& prefs) hiddenPreviews = HiddenPreviewsShown; else if( hps == 1 ) hiddenPreviews = HiddenPreviewsAlways; + + unredirectFullscreen = config.readEntry( "UnredirectFullscreen", true ); } diff --git a/options.h b/options.h index a5ca3eb970..cb67938496 100644 --- a/options.h +++ b/options.h @@ -294,6 +294,7 @@ class Options : public KDecorationOptions bool useCompositing; CompositingType compositingMode; HiddenPreviews hiddenPreviews; + bool unredirectFullscreen; uint refreshRate; // This is for OpenGL mode diff --git a/scene.cpp b/scene.cpp index 5c48cdc40e..a07614c99b 100644 --- a/scene.cpp +++ b/scene.cpp @@ -116,7 +116,7 @@ void Scene::paintScreen( int* mask, QRegion* region ) *mask &= ~PAINT_SCREEN_REGION; *region = infiniteRegion(); } - else if( *mask & PAINT_SCREEN_REGION ) + else if( *mask & PAINT_SCREEN_REGION ) { // make sure not to go outside visible screen *region &= QRegion( 0, 0, displayWidth(), displayHeight()); } @@ -198,6 +198,9 @@ void Scene::paintGenericScreen( int orig_mask, ScreenPaintData ) if( !w->isPaintingEnabled()) continue; phase2.append( Phase2Data( w, infiniteRegion(), data.clip, data.mask, data.quads )); + // transformations require window pixmap + w->suspendUnredirect( data.mask + & ( PAINT_WINDOW_TRANSLUCENT | PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED )); } foreach( const Phase2Data &d, phase2 ) @@ -212,6 +215,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region ) // TODO PAINT_WINDOW_* flags don't belong here, that's why it's in the assert, // perhaps the two enums should be separated assert(( orig_mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED + | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_OPAQUE )) == 0 ); QHash< Window*, Phase2Data > phase2data; // Draw each opaque window top to bottom, subtracting the bounding rect of @@ -221,6 +225,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region ) --i ) { Window* w = stacking_order[ i ]; + w->suspendUnredirect( true ); WindowPrePaintData data; data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT ); w->resetPaintingEnabled(); @@ -233,6 +238,8 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region ) foreach( const WindowQuad &q, data.quads ) if( q.isTransformed()) kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ; + if( data.mask & PAINT_WINDOW_TRANSFORMED ) + kFatal( 1212 ) << "PAINT_WINDOW_TRANSFORMED without PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS!"; #endif if( !w->isPaintingEnabled()) continue; @@ -240,6 +247,8 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region ) painted_region |= data.paint; // make sure it makes it to the screen // Schedule the window for painting phase2data[w] = Phase2Data( w, data.paint, data.clip, data.mask, data.quads ); + // no transformations, but translucency requires window pixmap + w->suspendUnredirect( data.mask & PAINT_WINDOW_TRANSLUCENT ); } // Do the actual painting // First opaque windows, top to bottom diff --git a/scene.h b/scene.h index 084bcbee96..369c895d3b 100644 --- a/scene.h +++ b/scene.h @@ -182,6 +182,7 @@ class Scene::Window void updateToplevel( Toplevel* c ); // creates initial quad list for the window virtual WindowQuadList buildQuads() const; + void suspendUnredirect( bool suspend ); protected: WindowQuadList makeQuads( WindowQuadType type, const QRegion& reg ) const; Toplevel* toplevel; @@ -256,6 +257,12 @@ void Scene::Window::updateToplevel( Toplevel* c ) toplevel = c; } +inline +void Scene::Window::suspendUnredirect( bool suspend ) + { + toplevel->suspendUnredirect( suspend ); + } + } // namespace #endif diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 434bc5eafa..72c3512013 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -261,6 +261,8 @@ void SceneXrender::paintTransformedScreen( int orig_mask ) // The window can clip by its opaque parts the windows below. region -= w->transformedShape(); } + // translucency or window transformed require window pixmap + w->suspendUnredirect( data.mask & ( PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED )); } if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST )) paintBackground( region ); // Fill any areas of the root window not covered by windows diff --git a/toplevel.cpp b/toplevel.cpp index 93aacc6a98..9ac3da10bc 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -43,6 +43,8 @@ Toplevel::Toplevel( Workspace* ws ) , is_shape( false ) , effect_window( NULL ) , wmClientLeaderWin( 0 ) + , unredirect( false ) + , unredirectSuspend( false ) { } diff --git a/toplevel.h b/toplevel.h index c7d5bba06d..99476a9b2c 100644 --- a/toplevel.h +++ b/toplevel.h @@ -110,6 +110,9 @@ class Toplevel bool hasAlpha() const; virtual void setupCompositing(); virtual void finishCompositing(); + bool updateUnredirectedState(); + bool unredirected() const; + void suspendUnredirect( bool suspend ); void addRepaint( const QRect& r ); void addRepaint( int x, int y, int w, int h ); void addRepaintFull(); @@ -144,6 +147,7 @@ class Toplevel void disownDataPassedToDeleted(); friend kdbgstream& operator<<( kdbgstream& stream, const Toplevel* ); void deleteEffectWindow(); + virtual bool shouldUnredirect() const = 0; QRect geom; Visual* vis; int bit_depth; @@ -172,6 +176,8 @@ class Toplevel QByteArray client_machine; WId wmClientLeaderWin; QByteArray window_role; + bool unredirect; + bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily // when adding new data members, check also copyToDeleted() }; @@ -396,6 +402,11 @@ inline pid_t Toplevel::pid() const return info->pid(); } +inline bool Toplevel::unredirected() const + { + return unredirect; + } + kdbgstream& operator<<( kdbgstream& stream, const Toplevel* ); kdbgstream& operator<<( kdbgstream& stream, const ToplevelList& ); kdbgstream& operator<<( kdbgstream& stream, const ConstToplevelList& ); diff --git a/unmanaged.h b/unmanaged.h index 0be2ce4c8c..42c4f9299a 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -43,6 +43,7 @@ class Unmanaged virtual QSize clientSize() const; protected: virtual void debug( kdbgstream& stream ) const; + virtual bool shouldUnredirect() const; private: virtual ~Unmanaged(); // use release() // handlers for X11 events diff --git a/workspace.cpp b/workspace.cpp index 21c5334177..701fb54083 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -141,7 +141,8 @@ Workspace::Workspace( bool restore ) overlay_visible( true ), overlay_shown( false ), transSlider( NULL ), - transButton( NULL ) + transButton( NULL ), + forceUnredirectCheck( true ) { (void) new KWinAdaptor( this ); QDBusConnection dbus = QDBusConnection::sessionBus(); @@ -165,6 +166,8 @@ Workspace::Workspace( bool restore ) connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )), this, SLOT( gotTemporaryRulesMessage( const QString& ))); connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules())); + connect( &unredirectTimer, SIGNAL( timeout()), this, SLOT( delayedCheckUnredirect())); + unredirectTimer.setSingleShot( true ); updateXTime(); // needed for proper initialization of user_time in Client ctor diff --git a/workspace.h b/workspace.h index 05d73a8ee3..b930797b9b 100644 --- a/workspace.h +++ b/workspace.h @@ -194,7 +194,7 @@ class Workspace : public QObject, public KDecorationDefines * at the last position */ const ClientList& stackingOrder() const; - + ToplevelList xStackingOrder() const; ClientList ensureStackingOrder( const ClientList& clients ) const; Client* topClientOnDesktop( int desktop, int screen, bool unconstrained = false, bool only_normal = true ) const; @@ -330,6 +330,7 @@ class Workspace : public QObject, public KDecorationDefines // destroys XComposite overlay window void destroyOverlay(); Window overlayWindow(); + void checkUnredirect( bool force = false ); public slots: void addRepaintFull(); @@ -491,6 +492,7 @@ class Workspace : public QObject, public KDecorationDefines void lostCMSelection(); void updateElectricBorders(); void resetCursorPosTime(); + void delayedCheckUnredirect(); protected: bool keyPressMouseEmulation( XKeyEvent& ev ); @@ -518,7 +520,6 @@ class Workspace : public QObject, public KDecorationDefines bool establishTabBoxGrab(); void removeTabBoxGrab(); - ToplevelList xStackingOrder() const; void propagateClients( bool propagate_new_clients ); // called only from updateStackingOrder ClientList constrainedStackingOrder(); void raiseClientWithinApplication( Client* c ); @@ -740,6 +741,8 @@ class Workspace : public QObject, public KDecorationDefines bool overlay_shown; // for showOverlay() QSlider *transSlider; QPushButton *transButton; + QTimer unredirectTimer; + bool forceUnredirectCheck; private: friend bool performTransiencyCheck();