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
This commit is contained in:
Luboš Luňák 2007-07-04 09:51:10 +00:00
parent 5f003b9ec3
commit 9052116e4f
10 changed files with 138 additions and 29 deletions

View file

@ -173,6 +173,11 @@ General TODO
% installed headers currently include config.h files to find out about e.g. OpenGL % 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 - 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 OpenGL TODO
================================= =================================

View file

@ -497,7 +497,19 @@ void Client::updateShape()
} }
// !shape() mask setting is done in setMask() when the decoration // !shape() mask setting is done in setMask() when the decoration
// calls it or when the decoration is created/destroyed // calls it or when the decoration is created/destroyed
updateInputShape();
if( compositing())
addDamageFull();
if( scene != NULL )
scene->windowGeometryShapeChanged( this );
if( effects != NULL )
static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
}
void Client::updateInputShape()
{
if( hidden_preview ) // sets it to none, don't change
return;
if( Extensions::shapeInputAvailable()) if( Extensions::shapeInputAvailable())
{ // There appears to be no way to find out if a window has input { // There appears to be no way to find out if a window has input
// shape set or not, so always propagate the input shape // shape set or not, so always propagate the input shape
@ -524,12 +536,6 @@ void Client::updateShape()
XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0, XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0,
helper_window, ShapeInput, ShapeSet ); helper_window, ShapeInput, ShapeSet );
} }
if( compositing())
addDamageFull();
if( scene != NULL )
scene->windowGeometryShapeChanged( this );
if( effects != NULL )
static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
} }
void Client::setMask( const QRegion& reg, int mode ) void Client::setMask( const QRegion& reg, int mode )
@ -867,11 +873,19 @@ void Client::rawShow()
XMapWindow( display(), wrapper ); XMapWindow( display(), wrapper );
XMapWindow( display(), client ); XMapWindow( display(), client );
} }
// XComposite invalidates backing pixmaps on unmap (minimize, different if( options->hiddenPreviews == HiddenPreviewsNever )
// 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 // XComposite invalidates backing pixmaps on unmap (minimize, different
if( compositing() ) // virtual desktop, etc.). We kept the last known good pixmap around
discardWindowPixmap(); // 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() void Client::rawHide()
{ {
StackingUpdatesBlocker blocker( workspace());
addWorkspaceRepaint( geometry()); addWorkspaceRepaint( geometry());
// Here it may look like a race condition, as some other client might try to unmap if( options->hiddenPreviews == HiddenPreviewsNever )
// the window between these two XSelectInput() calls. However, they're supposed to {
// use XWithdrawWindow(), which also sends a synthetic event to the root window, // Here it may look like a race condition, as some other client might try to unmap
// which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify // the window between these two XSelectInput() calls. However, they're supposed to
// will be missed is also very minimal, so I don't think it's needed to grab the server // use XWithdrawWindow(), which also sends a synthetic event to the root window,
// here. // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify // will be missed is also very minimal, so I don't think it's needed to grab the server
XUnmapWindow( display(), frameId()); // here.
XUnmapWindow( display(), wrapper ); XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
XUnmapWindow( display(), client ); XUnmapWindow( display(), frameId());
XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); XUnmapWindow( display(), wrapper );
if( decoration != NULL ) XUnmapWindow( display(), client );
decoration->widget()->hide(); // not really necessary, but let it know the state 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 ); 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) void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
{ {
XEvent ev; XEvent ev;

View file

@ -219,6 +219,7 @@ class Client
void updateVisibility(); void updateVisibility();
// hides a client - basically like minimize, but without effects, it's simply hidden // hides a client - basically like minimize, but without effects, it's simply hidden
void hideClient( bool hide ); void hideClient( bool hide );
bool hiddenPreview() const; // window is mapped in order to get a window pixmap
QString caption( bool full = true ) const; QString caption( bool full = true ) const;
void updateCaption(); void updateCaption();
@ -322,8 +323,7 @@ class Client
void syncTimeout(); void syncTimeout();
private: private:
// ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1 void setMappingState( int s ); // ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1
void setMappingState( int s );
int mappingState() const; int mappingState() const;
bool isIconicState() const; bool isIconicState() const;
bool isNormalState() const; bool isNormalState() const;
@ -382,6 +382,8 @@ class Client
void rawShow(); // just shows it void rawShow(); // just shows it
void rawHide(); // just hides it void rawHide(); // just hides it
void setHiddenPreview( bool set, allowed_t );
void updateInputShape();
Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data,
bool session ) const; bool session ) const;
@ -446,6 +448,7 @@ class Client
uint urgency : 1; // XWMHints, UrgencyHint uint urgency : 1; // XWMHints, UrgencyHint
uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client
uint demands_attention : 1; uint demands_attention : 1;
uint hidden_preview : 1; // mapped only to get a window pixmap for compositing
WindowRules client_rules; WindowRules client_rules;
void getWMHints(); void getWMHints();
void readIcons(); void readIcons();
@ -768,6 +771,11 @@ inline void Client::removeRule( Rules* rule )
client_rules.remove( rule ); client_rules.remove( rule );
} }
inline bool Client::hiddenPreview() const
{
return hidden_preview;
}
KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value ); KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value );
} // namespace } // namespace

View file

@ -105,7 +105,8 @@ void Workspace::updateStackingOrder( bool propagate_new_clients )
return; return;
} }
ClientList new_stacking_order = constrainedStackingOrder(); 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; stacking_order = new_stacking_order;
#if 0 #if 0
kDebug() << "stacking:" << changed << endl; kDebug() << "stacking:" << changed << endl;
@ -151,8 +152,10 @@ void Workspace::propagateClients( bool propagate_new_clients )
if( electric_windows[ i ] != None ) if( electric_windows[ i ] != None )
new_stack[ pos++ ] = electric_windows[ i ]; new_stack[ pos++ ] = electric_windows[ i ];
int topmenu_space_pos = 1; // not 0, that's supportWindow !!! 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(); new_stack[ pos++ ] = stacking_order.at( i )->frameId();
if( stacking_order.at( i )->belongsToLayer() >= DockLayer ) if( stacking_order.at( i )->belongsToLayer() >= DockLayer )
topmenu_space_pos = pos; topmenu_space_pos = pos;
@ -166,6 +169,17 @@ void Workspace::propagateClients( bool propagate_new_clients )
new_stack[ topmenu_space_pos ] = topmenu_space->winId(); new_stack[ topmenu_space_pos ] = topmenu_space->winId();
++pos; ++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 isn't it too inefficient to restack always all clients?
// TODO don't restack not visible windows? // TODO don't restack not visible windows?
assert( new_stack[ 0 ] == supportWindow->winId()); assert( new_stack[ 0 ] == supportWindow->winId());

View file

@ -193,6 +193,8 @@ unsigned long Options::updateSettings()
glDirect = config.readEntry("GLDirect", true ); glDirect = config.readEntry("GLDirect", true );
glVSync = config.readEntry("GLVSync", true ); glVSync = config.readEntry("GLVSync", true );
glStrictBinding = config.readEntry( "GLStrictBinding", false ); 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 // Read button tooltip animation effect from kdeglobals
// Since we want to allow users to enable window decoration tooltips // Since we want to allow users to enable window decoration tooltips

View file

@ -18,6 +18,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include <kdecoration_p.h> #include <kdecoration_p.h>
#include "placement.h" #include "placement.h"
#include "utils.h"
namespace KWin namespace KWin
{ {
@ -290,6 +291,7 @@ class Options : public KDecorationOptions
//translucency settings //translucency settings
bool useTranslucency; bool useTranslucency;
HiddenPreviews hiddenPreviews;
uint refreshRate; uint refreshRate;
int smoothScale; // 0 = no, 1 = yes when transformed, int smoothScale; // 0 = no, 1 = yes when transformed,

View file

@ -290,7 +290,7 @@ void Scene::Window::discardShape()
} }
// Find out the shape of the window using the XShape extension // 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 QRegion Scene::Window::shape() const
{ {
if( !shape_valid ) if( !shape_valid )

View file

@ -125,6 +125,14 @@ enum ShadeMode
ShadeActivated // "shaded", but visible due to alt+tab to the window 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 class Extensions
{ {
public: public:

View file

@ -88,6 +88,7 @@ Workspace::Workspace( bool restore )
pending_take_activity ( NULL ), pending_take_activity ( NULL ),
active_screen (0), active_screen (0),
delayfocus_client (0), delayfocus_client (0),
force_restacking( false ),
showing_desktop( false ), showing_desktop( false ),
block_showing_desktop( 0 ), block_showing_desktop( 0 ),
was_user_interaction (false), was_user_interaction (false),

View file

@ -131,6 +131,7 @@ class Workspace : public QObject, public KDecorationDefines
void restoreSessionStackingOrder( Client* c ); void restoreSessionStackingOrder( Client* c );
void restackUnmanaged( Unmanaged* c, Window above ); void restackUnmanaged( Unmanaged* c, Window above );
void reconfigure(); void reconfigure();
void forceRestacking();
void clientHidden( Client* ); void clientHidden( Client* );
void clientAttentionChanged( Client* c, bool set ); void clientAttentionChanged( Client* c, bool set );
@ -591,6 +592,7 @@ class Workspace : public QObject, public KDecorationDefines
ClientList unconstrained_stacking_order; // topmost last ClientList unconstrained_stacking_order; // topmost last
ClientList stacking_order; // topmost last ClientList stacking_order; // topmost last
UnmanagedList unmanaged_stacking_order; UnmanagedList unmanaged_stacking_order;
bool force_restacking;
QVector< ClientList > focus_chain; // currently ative last QVector< ClientList > focus_chain; // currently ative last
ClientList global_focus_chain; // this one is only for things like tabbox's MRU ClientList global_focus_chain; // this one is only for things like tabbox's MRU
ClientList should_get_focus; // last is most recent ClientList should_get_focus; // last is most recent
@ -870,6 +872,13 @@ bool Workspace::rulesUpdatesDisabled() const
return rules_updates_disabled; return rules_updates_disabled;
} }
inline
void Workspace::forceRestacking()
{
force_restacking = true;
StackingUpdatesBlocker blocker( this ); // do restacking if not blocked
}
template< typename T > template< typename T >
inline Client* Workspace::findClient( T predicate ) inline Client* Workspace::findClient( T predicate )
{ {