From 9052116e4f43ab2ee9bd9919908071f679ed8b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Wed, 4 Jul 2007 09:51:10 +0000 Subject: [PATCH] Support for having previews even for unmapped windows - they're actually kept mapped, so that they still have the backing pixmap. Plus some small tricks to prevent such windows from interfering. Only two basic modes are implemented right now. svn path=/trunk/KDE/kdebase/workspace/; revision=683156 --- COMPOSITE_TODO | 5 +++ client.cpp | 108 ++++++++++++++++++++++++++++++++++++++----------- client.h | 12 +++++- layers.cpp | 18 ++++++++- options.cpp | 2 + options.h | 2 + scene.cpp | 2 +- utils.h | 8 ++++ workspace.cpp | 1 + workspace.h | 9 +++++ 10 files changed, 138 insertions(+), 29 deletions(-) diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 4b2f23cfd5..b04b31a166 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 e5020539ba..b3434527e4 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 e273be40d2..c80e5a7087 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 9295392cc3..55fe9f3953 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 384e100dad..8192b5c4eb 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 097b606133..2338078e8a 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 23e79c4cb2..321fdbea5f 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 fde37493b3..b0218f1a5c 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 97c726250c..144b4e5062 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 c05b91db69..23bb0df340 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 ) {