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();