diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 4b2f23cfd..b04b31a16 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -173,6 +173,11 @@ General TODO % installed headers currently include config.h files to find out about e.g. OpenGL - this needs to be sorted out somehow, installed headers shouldn't do this +? hidden previews currently needs input shape extension, otherwise the window can possibly interfere + - not very likely though, so is this worth bothering at all? + ++ hidden preview has two modes unimplemented + OpenGL TODO ================================= diff --git a/client.cpp b/client.cpp index e5020539b..b3434527e 100644 --- a/client.cpp +++ b/client.cpp @@ -497,7 +497,19 @@ void Client::updateShape() } // !shape() mask setting is done in setMask() when the decoration // calls it or when the decoration is created/destroyed + updateInputShape(); + if( compositing()) + addDamageFull(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + static_cast(effects)->windowGeometryShapeChanged( effectWindow(), geometry()); + } +void Client::updateInputShape() + { + if( hidden_preview ) // sets it to none, don't change + return; if( Extensions::shapeInputAvailable()) { // There appears to be no way to find out if a window has input // shape set or not, so always propagate the input shape @@ -524,12 +536,6 @@ void Client::updateShape() XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0, helper_window, ShapeInput, ShapeSet ); } - if( compositing()) - addDamageFull(); - if( scene != NULL ) - scene->windowGeometryShapeChanged( this ); - if( effects != NULL ) - static_cast(effects)->windowGeometryShapeChanged( effectWindow(), geometry()); } void Client::setMask( const QRegion& reg, int mode ) @@ -867,11 +873,19 @@ void Client::rawShow() XMapWindow( display(), wrapper ); XMapWindow( display(), client ); } - // XComposite invalidates backing pixmaps on unmap (minimize, different - // virtual desktop, etc.). We kept the last known good pixmap around - // for use in effects, but now we want to have access to the new pixmap - if( compositing() ) - discardWindowPixmap(); + if( options->hiddenPreviews == HiddenPreviewsNever ) + { + // XComposite invalidates backing pixmaps on unmap (minimize, different + // virtual desktop, etc.). We kept the last known good pixmap around + // for use in effects, but now we want to have access to the new pixmap + if( compositing() ) + discardWindowPixmap(); + } + else + { + if( hidden_preview ) + setHiddenPreview( false, Allowed ); + } } /*! @@ -881,23 +895,69 @@ void Client::rawShow() */ void Client::rawHide() { + StackingUpdatesBlocker blocker( workspace()); addWorkspaceRepaint( geometry()); -// Here it may look like a race condition, as some other client might try to unmap -// the window between these two XSelectInput() calls. However, they're supposed to -// use XWithdrawWindow(), which also sends a synthetic event to the root window, -// which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify -// will be missed is also very minimal, so I don't think it's needed to grab the server -// here. - XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify - XUnmapWindow( display(), frameId()); - XUnmapWindow( display(), wrapper ); - XUnmapWindow( display(), client ); - XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); - if( decoration != NULL ) - decoration->widget()->hide(); // not really necessary, but let it know the state + if( options->hiddenPreviews == HiddenPreviewsNever ) + { + // Here it may look like a race condition, as some other client might try to unmap + // the window between these two XSelectInput() calls. However, they're supposed to + // use XWithdrawWindow(), which also sends a synthetic event to the root window, + // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify + // will be missed is also very minimal, so I don't think it's needed to grab the server + // here. + XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify + XUnmapWindow( display(), frameId()); + XUnmapWindow( display(), wrapper ); + XUnmapWindow( display(), client ); + XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); + if( decoration != NULL ) + decoration->widget()->hide(); // not really necessary, but let it know the state + } + else + { + if( !hidden_preview ) + { + setHiddenPreview( true, Allowed ); + // actually keep the window mapped (taken from rawShow()) + if( decoration != NULL ) + decoration->widget()->show(); // not really necessary, but let it know the state + XMapWindow( display(), frameId()); + if( !isShade()) + { + XMapWindow( display(), wrapper ); + XMapWindow( display(), client ); + } + } + } workspace()->clientHidden( this ); } +// XComposite doesn't keep window pixmaps of unmapped windows, which means +// there wouldn't be any previews of windows that are minimized or on another +// virtual desktop. Therefore rawHide() actually keeps such windows mapped. +// However special care needs to be taken so that such windows don't interfere. +// Therefore they're put very low in the stacking order and they have input shape +// set to none, which hopefully is enough. If there's no input shape available, +// then it's hoped that there will be some other desktop above it *shrug*. +// Using normal shape would be better, but that'd affect other things, e.g. painting +// of the actual preview. +void Client::setHiddenPreview( bool set, allowed_t ) + { + if( set && !hidden_preview ) + { // set + hidden_preview = true; + workspace()->forceRestacking(); + if( Extensions::shapeInputAvailable()) + XShapeCombineRectangles( display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted ); + } + else if( !set && hidden_preview ) + { // unset + hidden_preview = false; + workspace()->forceRestacking(); + updateInputShape(); + } + } + void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3) { XEvent ev; diff --git a/client.h b/client.h index e273be40d..c80e5a708 100644 --- a/client.h +++ b/client.h @@ -219,6 +219,7 @@ class Client void updateVisibility(); // hides a client - basically like minimize, but without effects, it's simply hidden void hideClient( bool hide ); + bool hiddenPreview() const; // window is mapped in order to get a window pixmap QString caption( bool full = true ) const; void updateCaption(); @@ -322,8 +323,7 @@ class Client void syncTimeout(); private: - // ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1 - void setMappingState( int s ); + void setMappingState( int s ); // ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1 int mappingState() const; bool isIconicState() const; bool isNormalState() const; @@ -382,6 +382,8 @@ class Client void rawShow(); // just shows it void rawHide(); // just hides it + void setHiddenPreview( bool set, allowed_t ); + void updateInputShape(); Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, bool session ) const; @@ -446,6 +448,7 @@ class Client uint urgency : 1; // XWMHints, UrgencyHint uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client uint demands_attention : 1; + uint hidden_preview : 1; // mapped only to get a window pixmap for compositing WindowRules client_rules; void getWMHints(); void readIcons(); @@ -768,6 +771,11 @@ inline void Client::removeRule( Rules* rule ) client_rules.remove( rule ); } +inline bool Client::hiddenPreview() const + { + return hidden_preview; + } + KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value ); } // namespace diff --git a/layers.cpp b/layers.cpp index 9295392cc..55fe9f395 100644 --- a/layers.cpp +++ b/layers.cpp @@ -105,7 +105,8 @@ void Workspace::updateStackingOrder( bool propagate_new_clients ) return; } ClientList new_stacking_order = constrainedStackingOrder(); - bool changed = ( new_stacking_order != stacking_order ); + bool changed = ( new_stacking_order != stacking_order || force_restacking ); + force_restacking = false; stacking_order = new_stacking_order; #if 0 kDebug() << "stacking:" << changed << endl; @@ -151,8 +152,10 @@ void Workspace::propagateClients( bool propagate_new_clients ) if( electric_windows[ i ] != None ) new_stack[ pos++ ] = electric_windows[ i ]; int topmenu_space_pos = 1; // not 0, that's supportWindow !!! - for ( int i = stacking_order.size() - 1; i >= 0; i-- ) + for ( int i = stacking_order.size() - 1; i >= 0; i-- ) { + if( stacking_order.at( i )->hiddenPreview()) + continue; new_stack[ pos++ ] = stacking_order.at( i )->frameId(); if( stacking_order.at( i )->belongsToLayer() >= DockLayer ) topmenu_space_pos = pos; @@ -166,6 +169,17 @@ void Workspace::propagateClients( bool propagate_new_clients ) new_stack[ topmenu_space_pos ] = topmenu_space->winId(); ++pos; } + // when having hidden previews, stack hidden windows below everything else + // (as far as pure X stacking order is concerned), in order to avoid having + // these windows that should be unmapped to interfere with other windows + for ( int i = stacking_order.size() - 1; i >= 0; i-- ) + { + if( !stacking_order.at( i )->hiddenPreview()) + continue; + new_stack[ pos++ ] = stacking_order.at( i )->frameId(); + if( stacking_order.at( i )->belongsToLayer() >= DockLayer ) + topmenu_space_pos = pos; + } // TODO isn't it too inefficient to restack always all clients? // TODO don't restack not visible windows? assert( new_stack[ 0 ] == supportWindow->winId()); diff --git a/options.cpp b/options.cpp index 384e100da..8192b5c4e 100644 --- a/options.cpp +++ b/options.cpp @@ -193,6 +193,8 @@ unsigned long Options::updateSettings() glDirect = config.readEntry("GLDirect", true ); glVSync = config.readEntry("GLVSync", true ); glStrictBinding = config.readEntry( "GLStrictBinding", false ); + const HiddenPreviews hps[] = { HiddenPreviewsNever, HiddenPreviewsKeep, HiddenPreviewUpdate, HiddenPreviewsActive }; + hiddenPreviews = hps[ qBound( 0, config.readEntry( "HiddenPreviews", 3 ), 3 ) ]; // Read button tooltip animation effect from kdeglobals // Since we want to allow users to enable window decoration tooltips diff --git a/options.h b/options.h index 097b60613..2338078e8 100644 --- a/options.h +++ b/options.h @@ -18,6 +18,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include "placement.h" +#include "utils.h" namespace KWin { @@ -290,6 +291,7 @@ class Options : public KDecorationOptions //translucency settings bool useTranslucency; + HiddenPreviews hiddenPreviews; uint refreshRate; int smoothScale; // 0 = no, 1 = yes when transformed, diff --git a/scene.cpp b/scene.cpp index 23e79c4cb..321fdbea5 100644 --- a/scene.cpp +++ b/scene.cpp @@ -290,7 +290,7 @@ void Scene::Window::discardShape() } // Find out the shape of the window using the XShape extension -// or if not shape is set then simply it's the window geometry. +// or if shape is not set then simply it's the window geometry. QRegion Scene::Window::shape() const { if( !shape_valid ) diff --git a/utils.h b/utils.h index fde37493b..b0218f1a5 100644 --- a/utils.h +++ b/utils.h @@ -125,6 +125,14 @@ enum ShadeMode ShadeActivated // "shaded", but visible due to alt+tab to the window }; +enum HiddenPreviews // whether to keep all windows mapped when compositing + { // do not reorder (config file) + HiddenPreviewsNever, // don't keep pixmaps of unmapped windows at all +/**/ HiddenPreviewsKeep, // only keep pixmaps, but unmap windows +/**/ HiddenPreviewUpdate, // unmap, keep, but when needed map back and wait + HiddenPreviewsActive // keep windows mapped + }; + class Extensions { public: diff --git a/workspace.cpp b/workspace.cpp index 97c726250..144b4e506 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -88,6 +88,7 @@ Workspace::Workspace( bool restore ) pending_take_activity ( NULL ), active_screen (0), delayfocus_client (0), + force_restacking( false ), showing_desktop( false ), block_showing_desktop( 0 ), was_user_interaction (false), diff --git a/workspace.h b/workspace.h index c05b91db6..23bb0df34 100644 --- a/workspace.h +++ b/workspace.h @@ -131,6 +131,7 @@ class Workspace : public QObject, public KDecorationDefines void restoreSessionStackingOrder( Client* c ); void restackUnmanaged( Unmanaged* c, Window above ); void reconfigure(); + void forceRestacking(); void clientHidden( Client* ); void clientAttentionChanged( Client* c, bool set ); @@ -591,6 +592,7 @@ class Workspace : public QObject, public KDecorationDefines ClientList unconstrained_stacking_order; // topmost last ClientList stacking_order; // topmost last UnmanagedList unmanaged_stacking_order; + bool force_restacking; QVector< ClientList > focus_chain; // currently ative last ClientList global_focus_chain; // this one is only for things like tabbox's MRU ClientList should_get_focus; // last is most recent @@ -870,6 +872,13 @@ bool Workspace::rulesUpdatesDisabled() const return rules_updates_disabled; } +inline +void Workspace::forceRestacking() + { + force_restacking = true; + StackingUpdatesBlocker blocker( this ); // do restacking if not blocked + } + template< typename T > inline Client* Workspace::findClient( T predicate ) {