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
- 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
=================================

View file

@ -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<EffectsHandlerImpl*>(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<EffectsHandlerImpl*>(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;

View file

@ -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

View file

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

View file

@ -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

View file

@ -18,6 +18,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include <kdecoration_p.h>
#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,

View file

@ -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 )

View file

@ -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:

View file

@ -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),

View file

@ -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 )
{