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:
parent
5f003b9ec3
commit
9052116e4f
10 changed files with 138 additions and 29 deletions
|
@ -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
|
||||||
=================================
|
=================================
|
||||||
|
|
108
client.cpp
108
client.cpp
|
@ -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;
|
||||||
|
|
12
client.h
12
client.h
|
@ -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
|
||||||
|
|
18
layers.cpp
18
layers.cpp
|
@ -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());
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 )
|
||||||
|
|
8
utils.h
8
utils.h
|
@ -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:
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue