/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project .
Copyright ( C ) 1999 , 2000 Matthias Ettrich < ettrich @ kde . org >
Copyright ( C ) 2003 Lubos Lunak < l . lunak @ kde . org >
You can Freely distribute this program under the GNU General Public
License . See the file " COPYING " for the exact licensing terms .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//#define QT_CLEAN_NAMESPACE
# include "workspace.h"
# include <kapplication.h>
# include <kstartupinfo.h>
# include <fixx11h.h>
# include <kconfig.h>
# include <kglobal.h>
# include <qpopupmenu.h>
# include <klocale.h>
# include <qregexp.h>
# include <qpainter.h>
# include <qbitmap.h>
# include <qclipboard.h>
# include <kmenubar.h>
# include <kprocess.h>
# include <kglobalaccel.h>
# include <dcopclient.h>
# include <kipc.h>
# include "plugins.h"
# include "client.h"
# include "popupinfo.h"
# include "tabbox.h"
# include "atoms.h"
# include "placement.h"
# include "notifications.h"
# include "group.h"
# include "rules.h"
# include <X11/extensions/shape.h>
# include <X11/keysym.h>
# include <X11/keysymdef.h>
# include <X11/cursorfont.h>
extern Time qt_x_time ;
namespace KWinInternal
{
extern int screen_number ;
Workspace * Workspace : : _self = 0 ;
KProcess * kompmgr = 0 ;
KSelectionOwner * kompmgr_selection ;
bool allowKompmgrRestart = TRUE ;
// Rikkus: This class is too complex. It needs splitting further.
// It's a nightmare to understand, especially with so few comments :(
// Matthias: Feel free to ask me questions about it. Feel free to add
// comments. I disagree that further splittings makes it easier. 2500
// lines are not too much. It's the task that is complex, not the
// code.
Workspace : : Workspace ( bool restore )
: DCOPObject ( " KWinInterface " ) ,
QObject ( 0 , " workspace " ) ,
current_desktop ( 0 ) ,
number_of_desktops ( 0 ) ,
active_screen ( 0 ) ,
active_popup ( NULL ) ,
active_popup_client ( NULL ) ,
desktop_widget ( 0 ) ,
temporaryRulesMessages ( " _KDE_NET_WM_TEMPORARY_RULES " , NULL , false ) ,
rules_updates_disabled ( false ) ,
active_client ( 0 ) ,
last_active_client ( 0 ) ,
most_recently_raised ( 0 ) ,
movingClient ( 0 ) ,
pending_take_activity ( NULL ) ,
delayfocus_client ( 0 ) ,
showing_desktop ( false ) ,
block_showing_desktop ( 0 ) ,
was_user_interaction ( false ) ,
session_saving ( false ) ,
control_grab ( false ) ,
tab_grab ( false ) ,
mouse_emulation ( false ) ,
block_focus ( 0 ) ,
tab_box ( 0 ) ,
popupinfo ( 0 ) ,
popup ( 0 ) ,
advanced_popup ( 0 ) ,
desk_popup ( 0 ) ,
desk_popup_index ( 0 ) ,
keys ( 0 ) ,
client_keys ( NULL ) ,
client_keys_dialog ( NULL ) ,
client_keys_client ( NULL ) ,
disable_shortcuts_keys ( NULL ) ,
global_shortcuts_disabled ( false ) ,
global_shortcuts_disabled_for_client ( false ) ,
root ( 0 ) ,
workspaceInit ( true ) ,
startup ( 0 ) , electric_have_borders ( false ) ,
electric_current_border ( 0 ) ,
electric_top_border ( None ) ,
electric_bottom_border ( None ) ,
electric_left_border ( None ) ,
electric_right_border ( None ) ,
layoutOrientation ( Qt : : Vertical ) ,
layoutX ( - 1 ) ,
layoutY ( 2 ) ,
workarea ( NULL ) ,
screenarea ( NULL ) ,
managing_topmenus ( false ) ,
topmenu_selection ( NULL ) ,
topmenu_watcher ( NULL ) ,
topmenu_height ( 0 ) ,
topmenu_space ( NULL ) ,
set_active_client_recursion ( 0 ) ,
block_stacking_updates ( 0 ) ,
forced_global_mouse_grab ( false )
{
_self = this ;
mgr = new PluginMgr ;
root = qt_xrootwin ( ) ;
default_colormap = DefaultColormap ( qt_xdisplay ( ) , qt_xscreen ( ) ) ;
installed_colormap = default_colormap ;
session . setAutoDelete ( TRUE ) ;
connect ( & temporaryRulesMessages , SIGNAL ( gotMessage ( const QString & ) ) ,
this , SLOT ( gotTemporaryRulesMessage ( const QString & ) ) ) ;
connect ( & rulesUpdatedTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( writeWindowRules ( ) ) ) ;
updateXTime ( ) ; // needed for proper initialization of user_time in Client ctor
delayFocusTimer = 0 ;
electric_time_first = qt_x_time ;
electric_time_last = qt_x_time ;
if ( restore )
loadSessionInfo ( ) ;
loadWindowRules ( ) ;
( void ) QApplication : : desktop ( ) ; // trigger creation of desktop widget
desktop_widget =
new QWidget (
0 ,
" desktop_widget " ,
Qt : : WType_Desktop | Qt : : WPaintUnclipped
) ;
kapp - > setGlobalMouseTracking ( true ) ; // so that this doesn't mess eventmask on root window later
// call this before XSelectInput() on the root window
startup = new KStartupInfo (
KStartupInfo : : DisableKWinModule | KStartupInfo : : AnnounceSilenceChanges , this ) ;
// select windowmanager privileges
XSelectInput ( qt_xdisplay ( ) , root ,
KeyPressMask |
PropertyChangeMask |
ColormapChangeMask |
SubstructureRedirectMask |
SubstructureNotifyMask |
FocusChangeMask // for NotifyDetailNone
) ;
Shape : : init ( ) ;
// compatibility
long data = 1 ;
XChangeProperty (
qt_xdisplay ( ) ,
qt_xrootwin ( ) ,
atoms - > kwin_running ,
atoms - > kwin_running ,
32 ,
PropModeAppend ,
( unsigned char * ) & data ,
1
) ;
client_keys = new KGlobalAccel ( this ) ;
initShortcuts ( ) ;
tab_box = new TabBox ( this ) ;
popupinfo = new PopupInfo ( this ) ;
init ( ) ;
# if (QT_VERSION-0 >= 0x030200) // XRANDR support
connect ( kapp - > desktop ( ) , SIGNAL ( resized ( int ) ) , SLOT ( desktopResized ( ) ) ) ;
# endif
// start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object
if ( options - > useTranslucency )
{
kompmgr = new KProcess ;
connect ( kompmgr , SIGNAL ( receivedStderr ( KProcess * , char * , int ) ) , SLOT ( handleKompmgrOutput ( KProcess * , char * , int ) ) ) ;
* kompmgr < < " kompmgr " ;
startKompmgr ( ) ;
}
}
void Workspace : : init ( )
{
checkElectricBorders ( ) ;
// not used yet
// topDock = 0L;
// maximizedWindowCounter = 0;
supportWindow = new QWidget ;
XLowerWindow ( qt_xdisplay ( ) , supportWindow - > winId ( ) ) ; // see usage in layers.cpp
XSetWindowAttributes attr ;
attr . override_redirect = 1 ;
null_focus_window = XCreateWindow ( qt_xdisplay ( ) , qt_xrootwin ( ) , - 1 , - 1 , 1 , 1 , 0 , CopyFromParent ,
InputOnly , CopyFromParent , CWOverrideRedirect , & attr ) ;
XMapWindow ( qt_xdisplay ( ) , null_focus_window ) ;
unsigned long protocols [ 5 ] =
{
NET : : Supported |
NET : : SupportingWMCheck |
NET : : ClientList |
NET : : ClientListStacking |
NET : : DesktopGeometry |
NET : : NumberOfDesktops |
NET : : CurrentDesktop |
NET : : ActiveWindow |
NET : : WorkArea |
NET : : CloseWindow |
NET : : DesktopNames |
NET : : KDESystemTrayWindows |
NET : : WMName |
NET : : WMVisibleName |
NET : : WMDesktop |
NET : : WMWindowType |
NET : : WMState |
NET : : WMStrut |
NET : : WMIconGeometry |
NET : : WMIcon |
NET : : WMPid |
NET : : WMMoveResize |
NET : : WMKDESystemTrayWinFor |
NET : : WMFrameExtents |
NET : : WMPing
,
NET : : NormalMask |
NET : : DesktopMask |
NET : : DockMask |
NET : : ToolbarMask |
NET : : MenuMask |
NET : : DialogMask |
NET : : OverrideMask |
NET : : TopMenuMask |
NET : : UtilityMask |
NET : : SplashMask |
0
,
NET : : Modal |
// NET::Sticky | // large desktops not supported (and probably never will be)
NET : : MaxVert |
NET : : MaxHoriz |
NET : : Shaded |
NET : : SkipTaskbar |
NET : : KeepAbove |
// NET::StaysOnTop | the same like KeepAbove
NET : : SkipPager |
NET : : Hidden |
NET : : FullScreen |
NET : : KeepBelow |
NET : : DemandsAttention |
0
,
NET : : WM2UserTime |
NET : : WM2StartupId |
NET : : WM2AllowedActions |
NET : : WM2RestackWindow |
NET : : WM2MoveResizeWindow |
NET : : WM2ExtendedStrut |
NET : : WM2KDETemporaryRules |
NET : : WM2ShowingDesktop |
NET : : WM2FullPlacement |
NET : : WM2DesktopLayout |
0
,
NET : : ActionMove |
NET : : ActionResize |
NET : : ActionMinimize |
NET : : ActionShade |
// NET::ActionStick | // Sticky state is not supported
NET : : ActionMaxVert |
NET : : ActionMaxHoriz |
NET : : ActionFullScreen |
NET : : ActionChangeDesktop |
NET : : ActionClose |
0
,
} ;
rootInfo = new RootInfo ( this , qt_xdisplay ( ) , supportWindow - > winId ( ) , " KWin " ,
protocols , 5 , qt_xscreen ( ) ) ;
loadDesktopSettings ( ) ;
updateDesktopLayout ( ) ;
// extra NETRootInfo instance in Client mode is needed to get the values of the properties
NETRootInfo client_info ( qt_xdisplay ( ) , NET : : ActiveWindow | NET : : CurrentDesktop ) ;
int initial_desktop ;
if ( ! kapp - > isSessionRestored ( ) )
initial_desktop = client_info . currentDesktop ( ) ;
else
{
KConfigGroupSaver saver ( kapp - > sessionConfig ( ) , " Session " ) ;
initial_desktop = kapp - > sessionConfig ( ) - > readNumEntry ( " desktop " , 1 ) ;
}
if ( ! setCurrentDesktop ( initial_desktop ) )
setCurrentDesktop ( 1 ) ;
// now we know how many desktops we'll, thus, we initialise the positioning object
initPositioning = new Placement ( this ) ;
connect ( & reconfigureTimer , SIGNAL ( timeout ( ) ) , this ,
SLOT ( slotReconfigure ( ) ) ) ;
connect ( & updateToolWindowsTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( slotUpdateToolWindows ( ) ) ) ;
connect ( kapp , SIGNAL ( appearanceChanged ( ) ) , this ,
SLOT ( slotReconfigure ( ) ) ) ;
connect ( kapp , SIGNAL ( settingsChanged ( int ) ) , this ,
SLOT ( slotSettingsChanged ( int ) ) ) ;
connect ( kapp , SIGNAL ( kipcMessage ( int , int ) ) , this , SLOT ( kipcMessage ( int , int ) ) ) ;
active_client = NULL ;
rootInfo - > setActiveWindow ( None ) ;
focusToNull ( ) ;
if ( ! kapp - > isSessionRestored ( ) )
+ + block_focus ; // because it will be set below
char nm [ 100 ] ;
sprintf ( nm , " _KDE_TOPMENU_OWNER_S%d " , DefaultScreen ( qt_xdisplay ( ) ) ) ;
Atom topmenu_atom = XInternAtom ( qt_xdisplay ( ) , nm , False ) ;
topmenu_selection = new KSelectionOwner ( topmenu_atom ) ;
topmenu_watcher = new KSelectionWatcher ( topmenu_atom ) ;
// TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
{ // begin updates blocker block
StackingUpdatesBlocker blocker ( this ) ;
if ( options - > topMenuEnabled ( ) & & topmenu_selection - > claim ( false ) )
setupTopMenuHandling ( ) ; // this can call updateStackingOrder()
else
lostTopMenuSelection ( ) ;
unsigned int i , nwins ;
Window root_return , parent_return , * wins ;
XQueryTree ( qt_xdisplay ( ) , root , & root_return , & parent_return , & wins , & nwins ) ;
for ( i = 0 ; i < nwins ; i + + )
{
XWindowAttributes attr ;
XGetWindowAttributes ( qt_xdisplay ( ) , wins [ i ] , & attr ) ;
if ( attr . override_redirect )
continue ;
if ( topmenu_space & & topmenu_space - > winId ( ) = = wins [ i ] )
continue ;
if ( attr . map_state ! = IsUnmapped )
{
if ( addSystemTrayWin ( wins [ i ] ) )
continue ;
Client * c = createClient ( wins [ i ] , true ) ;
if ( c ! = NULL & & root ! = qt_xrootwin ( ) )
{ // TODO what is this?
// TODO may use QWidget:.create
XReparentWindow ( qt_xdisplay ( ) , c - > frameId ( ) , root , 0 , 0 ) ;
c - > move ( 0 , 0 ) ;
}
}
}
if ( wins )
XFree ( ( void * ) wins ) ;
// propagate clients, will really happen at the end of the updates blocker block
updateStackingOrder ( true ) ;
updateClientArea ( ) ;
raiseElectricBorders ( ) ;
// NETWM spec says we have to set it to (0,0) if we don't support it
NETPoint * viewports = new NETPoint [ number_of_desktops ] ;
rootInfo - > setDesktopViewport ( number_of_desktops , * viewports ) ;
delete [ ] viewports ;
QRect geom = QApplication : : desktop ( ) - > geometry ( ) ;
NETSize desktop_geometry ;
desktop_geometry . width = geom . width ( ) ;
desktop_geometry . height = geom . height ( ) ;
rootInfo - > setDesktopGeometry ( - 1 , desktop_geometry ) ;
setShowingDesktop ( false ) ;
} // end updates blocker block
Client * new_active_client = NULL ;
if ( ! kapp - > isSessionRestored ( ) )
{
- - block_focus ;
new_active_client = findClient ( WindowMatchPredicate ( client_info . activeWindow ( ) ) ) ;
}
if ( new_active_client = = NULL
& & activeClient ( ) = = NULL & & should_get_focus . count ( ) = = 0 ) // no client activated in manage()
{
if ( new_active_client = = NULL )
new_active_client = topClientOnDesktop ( currentDesktop ( ) ) ;
if ( new_active_client = = NULL & & ! desktops . isEmpty ( ) )
new_active_client = findDesktop ( true , currentDesktop ( ) ) ;
}
if ( new_active_client ! = NULL )
activateClient ( new_active_client ) ;
// SELI TODO this won't work with unreasonable focus policies,
// and maybe in rare cases also if the selected client doesn't
// want focus
workspaceInit = false ;
// TODO ungrabXServer()
}
Workspace : : ~ Workspace ( )
{
if ( kompmgr )
delete kompmgr ;
blockStackingUpdates ( true ) ;
// TODO grabXServer();
// use stacking_order, so that kwin --replace keeps stacking order
for ( ClientList : : ConstIterator it = stacking_order . begin ( ) ;
it ! = stacking_order . end ( ) ;
+ + it )
{
// only release the window
( * it ) - > releaseWindow ( true ) ;
// No removeClient() is called, it does more than just removing.
// However, remove from some lists to e.g. prevent performTransiencyCheck()
// from crashing.
clients . remove ( * it ) ;
desktops . remove ( * it ) ;
}
delete desktop_widget ;
delete tab_box ;
delete popupinfo ;
delete popup ;
if ( root = = qt_xrootwin ( ) )
XDeleteProperty ( qt_xdisplay ( ) , qt_xrootwin ( ) , atoms - > kwin_running ) ;
writeWindowRules ( ) ;
KGlobal : : config ( ) - > sync ( ) ;
delete rootInfo ;
delete supportWindow ;
delete mgr ;
delete [ ] workarea ;
delete [ ] screenarea ;
delete startup ;
delete initPositioning ;
delete topmenu_watcher ;
delete topmenu_selection ;
delete topmenu_space ;
delete client_keys_dialog ;
while ( ! rules . isEmpty ( ) )
{
delete rules . front ( ) ;
rules . pop_front ( ) ;
}
XDestroyWindow ( qt_xdisplay ( ) , null_focus_window ) ;
// TODO ungrabXServer();
_self = 0 ;
}
Client * Workspace : : createClient ( Window w , bool is_mapped )
{
StackingUpdatesBlocker blocker ( this ) ;
Client * c = new Client ( this ) ;
if ( ! c - > manage ( w , is_mapped ) )
{
Client : : deleteClient ( c , Allowed ) ;
return NULL ;
}
addClient ( c , Allowed ) ;
return c ;
}
void Workspace : : addClient ( Client * c , allowed_t )
{
// waited with trans settings until window figured out if active or not ;)
// qWarning("%s", (const char*)(c->resourceClass()));
c - > setBMP ( c - > resourceName ( ) = = " beep-media-player " | | c - > decorationId ( ) = = None ) ;
// first check if the window has it's own opinion of it's translucency ;)
c - > getWindowOpacity ( ) ;
if ( c - > isDock ( ) )
{
// if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c;
if ( ! c - > hasCustomOpacity ( ) ) // this xould be done slightly more efficient, but we want to support the topDock in future
{
c - > setShadowSize ( options - > dockShadowSize ) ;
c - > setOpacity ( options - > translucentDocks , options - > dockOpacity ) ;
}
}
//------------------------------------------------
Group * grp = findGroup ( c - > window ( ) ) ;
if ( grp ! = NULL )
grp - > gotLeader ( c ) ;
if ( c - > isDesktop ( ) )
{
desktops . append ( c ) ;
if ( active_client = = NULL & & should_get_focus . isEmpty ( ) & & c - > isOnCurrentDesktop ( ) )
requestFocus ( c ) ; // CHECKME? make sure desktop is active after startup if there's no other window active
}
else
{
updateFocusChains ( c , FocusChainUpdate ) ; // add to focus chain if not already there
clients . append ( c ) ;
}
if ( ! unconstrained_stacking_order . contains ( c ) )
unconstrained_stacking_order . append ( c ) ;
if ( ! stacking_order . contains ( c ) ) // it'll be updated later, and updateToolWindows() requires
stacking_order . append ( c ) ; // c to be in stacking_order
if ( c - > isTopMenu ( ) )
addTopMenu ( c ) ;
updateClientArea ( ) ; // this cannot be in manage(), because the client got added only now
updateClientLayer ( c ) ;
if ( c - > isDesktop ( ) )
{
raiseClient ( c ) ;
// if there's no active client, make this desktop the active one
if ( activeClient ( ) = = NULL & & should_get_focus . count ( ) = = 0 )
activateClient ( findDesktop ( true , currentDesktop ( ) ) ) ;
}
c - > checkActiveModal ( ) ;
checkTransients ( c - > window ( ) ) ; // SELI does this really belong here?
updateStackingOrder ( true ) ; // propagate new client
if ( c - > isUtility ( ) | | c - > isMenu ( ) | | c - > isToolbar ( ) )
updateToolWindows ( true ) ;
checkNonExistentClients ( ) ;
}
/*
Destroys the client \ a c
*/
void Workspace : : removeClient ( Client * c , allowed_t )
{
if ( c = = active_popup_client )
closeActivePopup ( ) ;
if ( client_keys_client = = c )
setupWindowShortcutDone ( false ) ;
if ( ! c - > shortcut ( ) . isNull ( ) )
c - > setShortcut ( QString : : null ) ; // remove from client_keys
if ( c - > isDialog ( ) )
Notify : : raise ( Notify : : TransDelete ) ;
if ( c - > isNormalWindow ( ) )
Notify : : raise ( Notify : : Delete ) ;
Q_ASSERT ( clients . contains ( c ) | | desktops . contains ( c ) ) ;
clients . remove ( c ) ;
desktops . remove ( c ) ;
unconstrained_stacking_order . remove ( c ) ;
stacking_order . remove ( c ) ;
for ( int i = 1 ;
i < = numberOfDesktops ( ) ;
+ + i )
focus_chain [ i ] . remove ( c ) ;
global_focus_chain . remove ( c ) ;
attention_chain . remove ( c ) ;
showing_desktop_clients . remove ( c ) ;
if ( c - > isTopMenu ( ) )
removeTopMenu ( c ) ;
Group * group = findGroup ( c - > window ( ) ) ;
if ( group ! = NULL )
group - > lostLeader ( ) ;
if ( c = = most_recently_raised )
most_recently_raised = 0 ;
should_get_focus . remove ( c ) ;
Q_ASSERT ( c ! = active_client ) ;
if ( c = = last_active_client )
last_active_client = 0 ;
if ( c = = pending_take_activity )
pending_take_activity = NULL ;
if ( c = = delayfocus_client )
cancelDelayFocus ( ) ;
updateStackingOrder ( true ) ;
if ( tab_grab )
tab_box - > repaint ( ) ;
updateClientArea ( ) ;
}
void Workspace : : updateFocusChains ( Client * c , FocusChainChange change )
{
if ( ! c - > wantsTabFocus ( ) ) // doesn't want tab focus, remove
{
for ( int i = 1 ;
i < = numberOfDesktops ( ) ;
+ + i )
focus_chain [ i ] . remove ( c ) ;
global_focus_chain . remove ( c ) ;
return ;
}
if ( c - > desktop ( ) = = NET : : OnAllDesktops )
{ //now on all desktops, add it to focus_chains it is not already in
for ( int i = 1 ; i < = numberOfDesktops ( ) ; i + + )
{ // making first/last works only on current desktop, don't affect all desktops
if ( i = = currentDesktop ( )
& & ( change = = FocusChainMakeFirst | | change = = FocusChainMakeLast ) )
{
focus_chain [ i ] . remove ( c ) ;
if ( change = = FocusChainMakeFirst )
focus_chain [ i ] . append ( c ) ;
else
focus_chain [ i ] . prepend ( c ) ;
}
else if ( ! focus_chain [ i ] . contains ( c ) )
{ // add it after the active one
if ( active_client ! = NULL & & active_client ! = c
& & ! focus_chain [ i ] . isEmpty ( ) & & focus_chain [ i ] . last ( ) = = active_client )
focus_chain [ i ] . insert ( focus_chain [ i ] . fromLast ( ) , c ) ;
else
focus_chain [ i ] . append ( c ) ; // otherwise add as the first one
}
}
}
else //now only on desktop, remove it anywhere else
{
for ( int i = 1 ; i < = numberOfDesktops ( ) ; i + + )
{
if ( i = = c - > desktop ( ) )
{
if ( change = = FocusChainMakeFirst )
{
focus_chain [ i ] . remove ( c ) ;
focus_chain [ i ] . append ( c ) ;
}
else if ( change = = FocusChainMakeLast )
{
focus_chain [ i ] . remove ( c ) ;
focus_chain [ i ] . prepend ( c ) ;
}
else if ( ! focus_chain [ i ] . contains ( c ) )
{
if ( active_client ! = NULL & & active_client ! = c
& & ! focus_chain [ i ] . isEmpty ( ) & & focus_chain [ i ] . last ( ) = = active_client )
focus_chain [ i ] . insert ( focus_chain [ i ] . fromLast ( ) , c ) ;
else
focus_chain [ i ] . append ( c ) ; // otherwise add as the first one
}
}
else
focus_chain [ i ] . remove ( c ) ;
}
}
if ( change = = FocusChainMakeFirst )
{
global_focus_chain . remove ( c ) ;
global_focus_chain . append ( c ) ;
}
else if ( change = = FocusChainMakeLast )
{
global_focus_chain . remove ( c ) ;
global_focus_chain . prepend ( c ) ;
}
else if ( ! global_focus_chain . contains ( c ) )
{
if ( active_client ! = NULL & & active_client ! = c
& & ! global_focus_chain . isEmpty ( ) & & global_focus_chain . last ( ) = = active_client )
global_focus_chain . insert ( global_focus_chain . fromLast ( ) , c ) ;
else
global_focus_chain . append ( c ) ; // otherwise add as the first one
}
}
void Workspace : : updateCurrentTopMenu ( )
{
if ( ! managingTopMenus ( ) )
return ;
// toplevel menubar handling
Client * menubar = 0 ;
bool block_desktop_menubar = false ;
if ( active_client )
{
// show the new menu bar first...
Client * menu_client = active_client ;
for ( ; ; )
{
if ( menu_client - > isFullScreen ( ) )
block_desktop_menubar = true ;
for ( ClientList : : ConstIterator it = menu_client - > transients ( ) . begin ( ) ;
it ! = menu_client - > transients ( ) . end ( ) ;
+ + it )
if ( ( * it ) - > isTopMenu ( ) )
{
menubar = * it ;
break ;
}
if ( menubar ! = NULL | | ! menu_client - > isTransient ( ) )
break ;
if ( menu_client - > isModal ( ) | | menu_client - > transientFor ( ) = = NULL )
break ; // don't use mainwindow's menu if this is modal or group transient
menu_client = menu_client - > transientFor ( ) ;
}
if ( ! menubar )
{ // try to find any topmenu from the application (#72113)
for ( ClientList : : ConstIterator it = active_client - > group ( ) - > members ( ) . begin ( ) ;
it ! = active_client - > group ( ) - > members ( ) . end ( ) ;
+ + it )
if ( ( * it ) - > isTopMenu ( ) )
{
menubar = * it ;
break ;
}
}
}
if ( ! menubar & & ! block_desktop_menubar & & options - > desktopTopMenu ( ) )
{
// Find the menubar of the desktop
Client * desktop = findDesktop ( true , currentDesktop ( ) ) ;
if ( desktop ! = NULL )
{
for ( ClientList : : ConstIterator it = desktop - > transients ( ) . begin ( ) ;
it ! = desktop - > transients ( ) . end ( ) ;
+ + it )
if ( ( * it ) - > isTopMenu ( ) )
{
menubar = * it ;
break ;
}
}
// TODO to be cleaned app with window grouping
// Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
// thus the topmenu is not transient for it :-/.
if ( menubar = = NULL )
{
for ( ClientList : : ConstIterator it = topmenus . begin ( ) ;
it ! = topmenus . end ( ) ;
+ + it )
if ( ( * it ) - > wasOriginallyGroupTransient ( ) ) // kdesktop's topmenu has WM_TRANSIENT_FOR
{ // set pointing to the root window
menubar = * it ; // to recognize it here
break ; // Also, with the xroot hack in kdesktop,
} // there's no NET::Desktop window to be transient for
}
}
// kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl;
if ( menubar )
{
if ( active_client & & ! menubar - > isOnDesktop ( active_client - > desktop ( ) ) )
menubar - > setDesktop ( active_client - > desktop ( ) ) ;
menubar - > hideClient ( false ) ;
topmenu_space - > hide ( ) ;
// make it appear like it's been raised manually - it's in the Dock layer anyway,
// and not raising it could mess up stacking order of topmenus within one application,
// and thus break raising of mainclients in raiseClient()
unconstrained_stacking_order . remove ( menubar ) ;
unconstrained_stacking_order . append ( menubar ) ;
}
else if ( ! block_desktop_menubar )
{ // no topmenu active - show the space window, so that there's not empty space
topmenu_space - > show ( ) ;
}
// ... then hide the other ones. Avoids flickers.
for ( ClientList : : ConstIterator it = clients . begin ( ) ; it ! = clients . end ( ) ; + + it )
{
if ( ( * it ) - > isTopMenu ( ) & & ( * it ) ! = menubar )
( * it ) - > hideClient ( true ) ;
}
}
void Workspace : : updateToolWindows ( bool also_hide )
{
// TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
if ( ! options - > hideUtilityWindowsForInactive )
{
for ( ClientList : : ConstIterator it = clients . begin ( ) ;
it ! = clients . end ( ) ;
+ + it )
( * it ) - > hideClient ( false ) ;
return ;
}
const Group * group = NULL ;
const Client * client = active_client ;
// Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
// will be shown; if a group transient is group, all tools in the group will be shown
while ( client ! = NULL )
{
if ( ! client - > isTransient ( ) )
break ;
if ( client - > groupTransient ( ) )
{
group = client - > group ( ) ;
break ;
}
client = client - > transientFor ( ) ;
}
// use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
// i.e. if it's not up to date
// SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
ClientList to_show , to_hide ;
for ( ClientList : : ConstIterator it = stacking_order . begin ( ) ;
it ! = stacking_order . end ( ) ;
+ + it )
{
if ( ( * it ) - > isUtility ( ) | | ( * it ) - > isMenu ( ) | | ( * it ) - > isToolbar ( ) )
{
bool show = true ;
if ( ! ( * it ) - > isTransient ( ) )
{
if ( ( * it ) - > group ( ) - > members ( ) . count ( ) = = 1 ) // has its own group, keep always visible
show = true ;
else if ( client ! = NULL & & ( * it ) - > group ( ) = = client - > group ( ) )
show = true ;
else
show = false ;
}
else
{
if ( group ! = NULL & & ( * it ) - > group ( ) = = group )
show = true ;
else if ( client ! = NULL & & client - > hasTransient ( ( * it ) , true ) )
show = true ;
else
show = false ;
}
if ( ! show & & also_hide )
{
const ClientList mainclients = ( * it ) - > mainClients ( ) ;
// don't hide utility windows which are standalone(?) or
// have e.g. kicker as mainwindow
if ( mainclients . isEmpty ( ) )
show = true ;
for ( ClientList : : ConstIterator it2 = mainclients . begin ( ) ;
it2 ! = mainclients . end ( ) ;
+ + it2 )
{
if ( ( * it2 ) - > isSpecialWindow ( ) )
show = true ;
}
if ( ! show )
to_hide . append ( * it ) ;
}
if ( show )
to_show . append ( * it ) ;
}
} // first show new ones, then hide
for ( ClientList : : ConstIterator it = to_show . fromLast ( ) ;
it ! = to_show . end ( ) ;
- - it ) // from topmost
// TODO since this is in stacking order, the order of taskbar entries changes :(
( * it ) - > hideClient ( false ) ;
if ( also_hide )
{
for ( ClientList : : ConstIterator it = to_hide . begin ( ) ;
it ! = to_hide . end ( ) ;
+ + it ) // from bottommost
( * it ) - > hideClient ( true ) ;
updateToolWindowsTimer . stop ( ) ;
}
else // setActiveClient() is after called with NULL client, quickly followed
{ // by setting a new client, which would result in flickering
updateToolWindowsTimer . start ( 50 , true ) ;
}
}
void Workspace : : slotUpdateToolWindows ( )
{
updateToolWindows ( true ) ;
}
/*!
Updates the current colormap according to the currently active client
*/
void Workspace : : updateColormap ( )
{
Colormap cmap = default_colormap ;
if ( activeClient ( ) & & activeClient ( ) - > colormap ( ) ! = None )
cmap = activeClient ( ) - > colormap ( ) ;
if ( cmap ! = installed_colormap )
{
XInstallColormap ( qt_xdisplay ( ) , cmap ) ;
installed_colormap = cmap ;
}
}
void Workspace : : reconfigure ( )
{
reconfigureTimer . start ( 200 , true ) ;
}
void Workspace : : slotSettingsChanged ( int category )
{
kdDebug ( 1212 ) < < " Workspace::slotSettingsChanged() " < < endl ;
if ( category = = ( int ) KApplication : : SETTINGS_SHORTCUTS )
readShortcuts ( ) ;
}
/*!
Reread settings
*/
KWIN_PROCEDURE ( CheckBorderSizesProcedure , cl - > checkBorderSizes ( ) ) ;
void Workspace : : slotReconfigure ( )
{
kdDebug ( 1212 ) < < " Workspace::slotReconfigure() " < < endl ;
reconfigureTimer . stop ( ) ;
KGlobal : : config ( ) - > reparseConfiguration ( ) ;
unsigned long changed = options - > updateSettings ( ) ;
tab_box - > reconfigure ( ) ;
popupinfo - > reconfigure ( ) ;
initPositioning - > reinitCascading ( 0 ) ;
readShortcuts ( ) ;
forEachClient ( CheckIgnoreFocusStealingProcedure ( ) ) ;
updateToolWindows ( true ) ;
if ( mgr - > reset ( changed ) )
{ // decorations need to be recreated
#if 0 // This actually seems to make things worse now
QWidget curtain ;
curtain . setBackgroundMode ( NoBackground ) ;
curtain . setGeometry ( QApplication : : desktop ( ) - > geometry ( ) ) ;
curtain . show ( ) ;
# endif
for ( ClientList : : ConstIterator it = clients . begin ( ) ;
it ! = clients . end ( ) ;
+ + it )
{
( * it ) - > updateDecoration ( true , true ) ;
}
mgr - > destroyPreviousPlugin ( ) ;
}
else
{
forEachClient ( CheckBorderSizesProcedure ( ) ) ;
}
checkElectricBorders ( ) ;
if ( options - > topMenuEnabled ( ) & & ! managingTopMenus ( ) )
{
if ( topmenu_selection - > claim ( false ) )
setupTopMenuHandling ( ) ;
else
lostTopMenuSelection ( ) ;
}
else if ( ! options - > topMenuEnabled ( ) & & managingTopMenus ( ) )
{
topmenu_selection - > release ( ) ;
lostTopMenuSelection ( ) ;
}
topmenu_height = 0 ; // invalidate used menu height
if ( managingTopMenus ( ) )
{
updateTopMenuGeometry ( ) ;
updateCurrentTopMenu ( ) ;
}
loadWindowRules ( ) ;
for ( ClientList : : Iterator it = clients . begin ( ) ;
it ! = clients . end ( ) ;
+ + it )
{
( * it ) - > setupWindowRules ( true ) ;
( * it ) - > applyWindowRules ( ) ;
discardUsedWindowRules ( * it , false ) ;
}
if ( options - > resetKompmgr ) // need restart
{
bool tmp = options - > useTranslucency ;
stopKompmgr ( ) ;
if ( tmp )
QTimer : : singleShot ( 200 , this , SLOT ( startKompmgr ( ) ) ) ; // wait some time to ensure system's ready for restart
}
}
void Workspace : : loadDesktopSettings ( )
{
KConfig * c = KGlobal : : config ( ) ;
QCString groupname ;
if ( screen_number = = 0 )
groupname = " Desktops " ;
else
groupname . sprintf ( " Desktops-screen-%d " , screen_number ) ;
KConfigGroupSaver saver ( c , groupname ) ;
int n = c - > readNumEntry ( " Number " , 4 ) ;
number_of_desktops = n ;
delete workarea ;
workarea = new QRect [ n + 1 ] ;
delete screenarea ;
screenarea = NULL ;
rootInfo - > setNumberOfDesktops ( number_of_desktops ) ;
desktop_focus_chain . resize ( n ) ;
// make it +1, so that it can be accessed as [1..numberofdesktops]
focus_chain . resize ( n + 1 ) ;
for ( int i = 1 ; i < = n ; i + + )
{
QString s = c - > readEntry ( QString ( " Name_%1 " ) . arg ( i ) ,
i18n ( " Desktop %1 " ) . arg ( i ) ) ;
rootInfo - > setDesktopName ( i , s . utf8 ( ) . data ( ) ) ;
desktop_focus_chain [ i - 1 ] = i ;
}
}
void Workspace : : saveDesktopSettings ( )
{
KConfig * c = KGlobal : : config ( ) ;
QCString groupname ;
if ( screen_number = = 0 )
groupname = " Desktops " ;
else
groupname . sprintf ( " Desktops-screen-%d " , screen_number ) ;
KConfigGroupSaver saver ( c , groupname ) ;
c - > writeEntry ( " Number " , number_of_desktops ) ;
for ( int i = 1 ; i < = number_of_desktops ; i + + )
{
QString s = desktopName ( i ) ;
QString defaultvalue = i18n ( " Desktop %1 " ) . arg ( i ) ;
if ( s . isEmpty ( ) )
{
s = defaultvalue ;
rootInfo - > setDesktopName ( i , s . utf8 ( ) . data ( ) ) ;
}
if ( s ! = defaultvalue )
{
c - > writeEntry ( QString ( " Name_%1 " ) . arg ( i ) , s ) ;
}
else
{
QString currentvalue = c - > readEntry ( QString ( " Name_%1 " ) . arg ( i ) ) ;
if ( currentvalue ! = defaultvalue )
c - > writeEntry ( QString ( " Name_%1 " ) . arg ( i ) , " " ) ;
}
}
}
QStringList Workspace : : configModules ( bool controlCenter )
{
QStringList args ;
args < < " kde-kwindecoration.desktop " ;
if ( controlCenter )
args < < " kde-kwinoptions.desktop " ;
else if ( kapp - > authorizeControlModule ( " kde-kwinoptions.desktop " ) )
args < < " kwinactions " < < " kwinfocus " < < " kwinmoving " < < " kwinadvanced " < < " kwinrules " < < " kwintranslucency " ;
return args ;
}
void Workspace : : configureWM ( )
{
KApplication : : kdeinitExec ( " kcmshell " , configModules ( false ) ) ;
}
/*!
avoids managing a window with title \ a title
*/
void Workspace : : doNotManage ( QString title )
{
doNotManageList . append ( title ) ;
}
/*!
Hack for java applets
*/
bool Workspace : : isNotManaged ( const QString & title )
{
for ( QStringList : : Iterator it = doNotManageList . begin ( ) ; it ! = doNotManageList . end ( ) ; + + it )
{
QRegExp r ( ( * it ) ) ;
if ( r . search ( title ) ! = - 1 )
{
doNotManageList . remove ( it ) ;
return TRUE ;
}
}
return FALSE ;
}
/*!
Refreshes all the client windows
*/
void Workspace : : refresh ( )
{
QWidget w ;
w . setGeometry ( QApplication : : desktop ( ) - > geometry ( ) ) ;
w . show ( ) ;
w . hide ( ) ;
QApplication : : flushX ( ) ;
}
/*!
During virt . desktop switching , desktop areas covered by windows that are
going to be hidden are first obscured by new windows with no background
( i . e . transparent ) placed right below the windows . These invisible windows
are removed after the switch is complete .
Reduces desktop ( wallpaper ) repaints during desktop switching
*/
class ObscuringWindows
{
public :
~ ObscuringWindows ( ) ;
void create ( Client * c ) ;
private :
QValueList < Window > obscuring_windows ;
static QValueList < Window > * cached ;
static unsigned int max_cache_size ;
} ;
QValueList < Window > * ObscuringWindows : : cached = 0 ;
unsigned int ObscuringWindows : : max_cache_size = 0 ;
void ObscuringWindows : : create ( Client * c )
{
if ( cached = = 0 )
cached = new QValueList < Window > ;
Window obs_win ;
XWindowChanges chngs ;
int mask = CWSibling | CWStackMode ;
if ( cached - > count ( ) > 0 )
{
cached - > remove ( obs_win = cached - > first ( ) ) ;
chngs . x = c - > x ( ) ;
chngs . y = c - > y ( ) ;
chngs . width = c - > width ( ) ;
chngs . height = c - > height ( ) ;
mask | = CWX | CWY | CWWidth | CWHeight ;
}
else
{
XSetWindowAttributes a ;
a . background_pixmap = None ;
a . override_redirect = True ;
obs_win = XCreateWindow ( qt_xdisplay ( ) , qt_xrootwin ( ) , c - > x ( ) , c - > y ( ) ,
c - > width ( ) , c - > height ( ) , 0 , CopyFromParent , InputOutput ,
CopyFromParent , CWBackPixmap | CWOverrideRedirect , & a ) ;
}
chngs . sibling = c - > frameId ( ) ;
chngs . stack_mode = Below ;
XConfigureWindow ( qt_xdisplay ( ) , obs_win , mask , & chngs ) ;
XMapWindow ( qt_xdisplay ( ) , obs_win ) ;
obscuring_windows . append ( obs_win ) ;
}
ObscuringWindows : : ~ ObscuringWindows ( )
{
max_cache_size = QMAX ( max_cache_size , obscuring_windows . count ( ) + 4 ) - 1 ;
for ( QValueList < Window > : : ConstIterator it = obscuring_windows . begin ( ) ;
it ! = obscuring_windows . end ( ) ;
+ + it )
{
XUnmapWindow ( qt_xdisplay ( ) , * it ) ;
if ( cached - > count ( ) < max_cache_size )
cached - > prepend ( * it ) ;
else
XDestroyWindow ( qt_xdisplay ( ) , * it ) ;
}
}
/*!
Sets the current desktop to \ a new_desktop
Shows / Hides windows according to the stacking order and finally
propages the new desktop to the world
*/
bool Workspace : : setCurrentDesktop ( int new_desktop )
{
if ( new_desktop < 1 | | new_desktop > number_of_desktops )
return false ;
closeActivePopup ( ) ;
+ + block_focus ;
// TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
StackingUpdatesBlocker blocker ( this ) ;
int old_desktop = current_desktop ;
if ( new_desktop ! = current_desktop )
{
+ + block_showing_desktop ;
/*
optimized Desktop switching : unmapping done from back to front
mapping done from front to back = > less exposure events
*/
Notify : : raise ( ( Notify : : Event ) ( Notify : : DesktopChange + new_desktop ) ) ;
ObscuringWindows obs_wins ;
current_desktop = new_desktop ; // change the desktop (so that Client::updateVisibility() works)
for ( ClientList : : ConstIterator it = stacking_order . begin ( ) ; it ! = stacking_order . end ( ) ; + + it )
if ( ! ( * it ) - > isOnDesktop ( new_desktop ) & & ( * it ) ! = movingClient )
{
if ( ( * it ) - > isShown ( true ) & & ( * it ) - > isOnDesktop ( old_desktop ) )
obs_wins . create ( * it ) ;
( * it ) - > updateVisibility ( ) ;
}
rootInfo - > setCurrentDesktop ( current_desktop ) ; // now propagate the change, after hiding, before showing
if ( movingClient & & ! movingClient - > isOnDesktop ( new_desktop ) )
movingClient - > setDesktop ( new_desktop ) ;
for ( ClientList : : ConstIterator it = stacking_order . fromLast ( ) ; it ! = stacking_order . end ( ) ; - - it )
if ( ( * it ) - > isOnDesktop ( new_desktop ) )
( * it ) - > updateVisibility ( ) ;
- - block_showing_desktop ;
if ( showingDesktop ( ) ) // do this only after desktop change to avoid flicker
resetShowingDesktop ( false ) ;
}
// restore the focus on this desktop
- - block_focus ;
Client * c = 0 ;
if ( options - > focusPolicyIsReasonable ( ) )
{
// Search in focus chain
if ( movingClient ! = NULL & & active_client = = movingClient
& & focus_chain [ currentDesktop ( ) ] . contains ( active_client )
& & active_client - > isShown ( true ) & & active_client - > isOnCurrentDesktop ( ) )
{
c = active_client ; // the requestFocus below will fail, as the client is already active
}
if ( ! c )
{
for ( ClientList : : ConstIterator it = focus_chain [ currentDesktop ( ) ] . fromLast ( ) ;
it ! = focus_chain [ currentDesktop ( ) ] . end ( ) ;
- - it )
{
if ( ( * it ) - > isShown ( false ) & & ( * it ) - > isOnCurrentDesktop ( ) )
{
c = * it ;
break ;
}
}
}
}
//if "unreasonable focus policy"
// and active_client is on_all_desktops and under mouse (hence == old_active_client),
// conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
else if ( active_client & & active_client - > isShown ( true ) & & active_client - > isOnCurrentDesktop ( ) )
c = active_client ;
if ( c = = NULL & & ! desktops . isEmpty ( ) )
c = findDesktop ( true , currentDesktop ( ) ) ;
if ( c ! = active_client )
setActiveClient ( NULL , Allowed ) ;
if ( c )
requestFocus ( c ) ;
else
focusToNull ( ) ;
updateCurrentTopMenu ( ) ;
// Update focus chain:
// If input: chain = { 1, 2, 3, 4 } and current_desktop = 3,
// Output: chain = { 3, 1, 2, 4 }.
// kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n")
// .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
for ( int i = desktop_focus_chain . find ( currentDesktop ( ) ) ; i > 0 ; i - - )
desktop_focus_chain [ i ] = desktop_focus_chain [ i - 1 ] ;
desktop_focus_chain [ 0 ] = currentDesktop ( ) ;
// QString s = "desktop_focus_chain[] = { ";
// for( uint i = 0; i < desktop_focus_chain.size(); i++ )
// s += QString::number(desktop_focus_chain[i]) + ", ";
// kdDebug(1212) << s << "}\n";
if ( old_desktop ! = 0 ) // not for the very first time
popupinfo - > showInfo ( desktopName ( currentDesktop ( ) ) ) ;
return true ;
}
// called only from DCOP
void Workspace : : nextDesktop ( )
{
int desktop = currentDesktop ( ) + 1 ;
setCurrentDesktop ( desktop > numberOfDesktops ( ) ? 1 : desktop ) ;
}
// called only from DCOP
void Workspace : : previousDesktop ( )
{
int desktop = currentDesktop ( ) - 1 ;
setCurrentDesktop ( desktop > 0 ? desktop : numberOfDesktops ( ) ) ;
}
int Workspace : : desktopToRight ( int desktop ) const
{
int x , y ;
calcDesktopLayout ( x , y ) ;
int dt = desktop - 1 ;
if ( layoutOrientation = = Qt : : Vertical )
{
dt + = y ;
if ( dt > = numberOfDesktops ( ) )
{
if ( options - > rollOverDesktops )
dt - = numberOfDesktops ( ) ;
else
return desktop ;
}
}
else
{
int d = ( dt % x ) + 1 ;
if ( d > = x )
{
if ( options - > rollOverDesktops )
d - = x ;
else
return desktop ;
}
dt = dt - ( dt % x ) + d ;
}
return dt + 1 ;
}
int Workspace : : desktopToLeft ( int desktop ) const
{
int x , y ;
calcDesktopLayout ( x , y ) ;
int dt = desktop - 1 ;
if ( layoutOrientation = = Qt : : Vertical )
{
dt - = y ;
if ( dt < 0 )
{
if ( options - > rollOverDesktops )
dt + = numberOfDesktops ( ) ;
else
return desktop ;
}
}
else
{
int d = ( dt % x ) - 1 ;
if ( d < 0 )
{
if ( options - > rollOverDesktops )
d + = x ;
else
return desktop ;
}
dt = dt - ( dt % x ) + d ;
}
return dt + 1 ;
}
int Workspace : : desktopUp ( int desktop ) const
{
int x , y ;
calcDesktopLayout ( x , y ) ;
int dt = desktop - 1 ;
if ( layoutOrientation = = Qt : : Horizontal )
{
dt - = x ;
if ( dt < 0 )
{
if ( options - > rollOverDesktops )
dt + = numberOfDesktops ( ) ;
else
return desktop ;
}
}
else
{
int d = ( dt % y ) - 1 ;
if ( d < 0 )
{
if ( options - > rollOverDesktops )
d + = y ;
else
return desktop ;
}
dt = dt - ( dt % y ) + d ;
}
return dt + 1 ;
}
int Workspace : : desktopDown ( int desktop ) const
{
int x , y ;
calcDesktopLayout ( x , y ) ;
int dt = desktop - 1 ;
if ( layoutOrientation = = Qt : : Horizontal )
{
dt + = x ;
if ( dt > = numberOfDesktops ( ) )
{
if ( options - > rollOverDesktops )
dt - = numberOfDesktops ( ) ;
else
return desktop ;
}
}
else
{
int d = ( dt % y ) + 1 ;
if ( d > = y )
{
if ( options - > rollOverDesktops )
d - = y ;
else
return desktop ;
}
dt = dt - ( dt % y ) + d ;
}
return dt + 1 ;
}
/*!
Sets the number of virtual desktops to \ a n
*/
void Workspace : : setNumberOfDesktops ( int n )
{
if ( n = = number_of_desktops )
return ;
int old_number_of_desktops = number_of_desktops ;
number_of_desktops = n ;
if ( currentDesktop ( ) > numberOfDesktops ( ) )
setCurrentDesktop ( numberOfDesktops ( ) ) ;
// if increasing the number, do the resizing now,
// otherwise after the moving of windows to still existing desktops
if ( old_number_of_desktops < number_of_desktops )
{
rootInfo - > setNumberOfDesktops ( number_of_desktops ) ;
NETPoint * viewports = new NETPoint [ number_of_desktops ] ;
rootInfo - > setDesktopViewport ( number_of_desktops , * viewports ) ;
delete [ ] viewports ;
updateClientArea ( true ) ;
focus_chain . resize ( number_of_desktops + 1 ) ;
}
// if the number of desktops decreased, move all
// windows that would be hidden to the last visible desktop
if ( old_number_of_desktops > number_of_desktops )
{
for ( ClientList : : ConstIterator it = clients . begin ( ) ;
it ! = clients . end ( ) ;
+ + it )
{
if ( ! ( * it ) - > isOnAllDesktops ( ) & & ( * it ) - > desktop ( ) > numberOfDesktops ( ) )
sendClientToDesktop ( * it , numberOfDesktops ( ) , true ) ;
}
}
if ( old_number_of_desktops > number_of_desktops )
{
rootInfo - > setNumberOfDesktops ( number_of_desktops ) ;
NETPoint * viewports = new NETPoint [ number_of_desktops ] ;
rootInfo - > setDesktopViewport ( number_of_desktops , * viewports ) ;
delete [ ] viewports ;
updateClientArea ( true ) ;
focus_chain . resize ( number_of_desktops + 1 ) ;
}
saveDesktopSettings ( ) ;
// Resize and reset the desktop focus chain.
desktop_focus_chain . resize ( n ) ;
for ( int i = 0 ; i < ( int ) desktop_focus_chain . size ( ) ; i + + )
desktop_focus_chain [ i ] = i + 1 ;
}
/*!
Sends client \ a c to desktop \ a desk .
Takes care of transients as well .
*/
void Workspace : : sendClientToDesktop ( Client * c , int desk , bool dont_activate )
{
bool was_on_desktop = c - > isOnDesktop ( desk ) | | c - > isOnAllDesktops ( ) ;
c - > setDesktop ( desk ) ;
if ( c - > desktop ( ) ! = desk ) // no change or desktop forced
return ;
desk = c - > desktop ( ) ; // Client did range checking
if ( c - > isOnDesktop ( currentDesktop ( ) ) )
{
if ( c - > wantsTabFocus ( ) & & options - > focusPolicyIsReasonable ( )
& & ! was_on_desktop // for stickyness changes
& & ! dont_activate )
requestFocus ( c ) ;
else
restackClientUnderActive ( c ) ;
}
else
{
raiseClient ( c ) ;
}
ClientList transients_stacking_order = ensureStackingOrder ( c - > transients ( ) ) ;
for ( ClientList : : ConstIterator it = transients_stacking_order . begin ( ) ;
it ! = transients_stacking_order . end ( ) ;
+ + it )
sendClientToDesktop ( * it , desk , dont_activate ) ;
updateClientArea ( ) ;
}
int Workspace : : numScreens ( ) const
{
if ( ! options - > xineramaEnabled )
return 0 ;
return qApp - > desktop ( ) - > numScreens ( ) ;
}
int Workspace : : activeScreen ( ) const
{
if ( ! options - > xineramaEnabled )
return 0 ;
if ( ! options - > activeMouseScreen )
{
if ( activeClient ( ) ! = NULL & & ! activeClient ( ) - > isOnScreen ( active_screen ) )
return qApp - > desktop ( ) - > screenNumber ( activeClient ( ) - > geometry ( ) . center ( ) ) ;
return active_screen ;
}
return qApp - > desktop ( ) - > screenNumber ( QCursor : : pos ( ) ) ;
}
// check whether a client moved completely out of what's considered the active screen,
// if yes, set a new active screen
void Workspace : : checkActiveScreen ( const Client * c )
{
if ( ! options - > xineramaEnabled )
return ;
if ( ! c - > isActive ( ) )
return ;
if ( ! c - > isOnScreen ( active_screen ) )
active_screen = c - > screen ( ) ;
}
// called e.g. when a user clicks on a window, set active screen to be the screen
// where the click occured
void Workspace : : setActiveScreenMouse ( QPoint mousepos )
{
if ( ! options - > xineramaEnabled )
return ;
active_screen = qApp - > desktop ( ) - > screenNumber ( mousepos ) ;
}
QRect Workspace : : screenGeometry ( int screen ) const
{
if ( ( ! options - > xineramaEnabled ) | | ( kapp - > desktop ( ) - > numScreens ( ) < 2 ) )
return qApp - > desktop ( ) - > geometry ( ) ;
return qApp - > desktop ( ) - > screenGeometry ( screen ) ;
}
int Workspace : : screenNumber ( QPoint pos ) const
{
if ( ! options - > xineramaEnabled )
return 0 ;
return qApp - > desktop ( ) - > screenNumber ( pos ) ;
}
void Workspace : : sendClientToScreen ( Client * c , int screen )
{
if ( c - > screen ( ) = = screen ) // don't use isOnScreen(), that's true even when only partially
return ;
GeometryUpdatesPostponer blocker ( c ) ;
QRect old_sarea = clientArea ( MaximizeArea , c ) ;
QRect sarea = clientArea ( MaximizeArea , screen , c - > desktop ( ) ) ;
c - > setGeometry ( sarea . x ( ) - old_sarea . x ( ) + c - > x ( ) , sarea . y ( ) - old_sarea . y ( ) + c - > y ( ) ,
c - > size ( ) . width ( ) , c - > size ( ) . height ( ) ) ;
c - > checkWorkspacePosition ( ) ;
ClientList transients_stacking_order = ensureStackingOrder ( c - > transients ( ) ) ;
for ( ClientList : : ConstIterator it = transients_stacking_order . begin ( ) ;
it ! = transients_stacking_order . end ( ) ;
+ + it )
sendClientToScreen ( * it , screen ) ;
if ( c - > isActive ( ) )
active_screen = screen ;
}
void Workspace : : setDesktopLayout ( int , int , int )
{ // DCOP-only, unused
}
void Workspace : : updateDesktopLayout ( )
{
// rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to
layoutOrientation = ( rootInfo - > desktopLayoutOrientation ( ) = = NET : : OrientationHorizontal
? Qt : : Horizontal : Qt : : Vertical ) ;
layoutX = rootInfo - > desktopLayoutColumnsRows ( ) . width ( ) ;
layoutY = rootInfo - > desktopLayoutColumnsRows ( ) . height ( ) ;
if ( layoutX = = 0 & & layoutY = = 0 ) // not given, set default layout
layoutY = 2 ;
}
void Workspace : : calcDesktopLayout ( int & x , int & y ) const
{
x = layoutX ; // <= 0 means compute it from the other and total number of desktops
y = layoutY ;
if ( ( x < = 0 ) & & ( y > 0 ) )
x = ( numberOfDesktops ( ) + y - 1 ) / y ;
else if ( ( y < = 0 ) & & ( x > 0 ) )
y = ( numberOfDesktops ( ) + x - 1 ) / x ;
if ( x < = 0 )
x = 1 ;
if ( y < = 0 )
y = 1 ;
}
/*!
Check whether \ a w is a system tray window . If so , add it to the respective
datastructures and propagate it to the world .
*/
bool Workspace : : addSystemTrayWin ( WId w )
{
if ( systemTrayWins . contains ( w ) )
return TRUE ;
NETWinInfo ni ( qt_xdisplay ( ) , w , root , NET : : WMKDESystemTrayWinFor ) ;
WId trayWinFor = ni . kdeSystemTrayWinFor ( ) ;
if ( ! trayWinFor )
return FALSE ;
systemTrayWins . append ( SystemTrayWindow ( w , trayWinFor ) ) ;
XSelectInput ( qt_xdisplay ( ) , w ,
StructureNotifyMask
) ;
XAddToSaveSet ( qt_xdisplay ( ) , w ) ;
propagateSystemTrayWins ( ) ;
return TRUE ;
}
/*!
Check whether \ a w is a system tray window . If so , remove it from
the respective datastructures and propagate this to the world .
*/
bool Workspace : : removeSystemTrayWin ( WId w , bool check )
{
if ( ! systemTrayWins . contains ( w ) )
return FALSE ;
if ( check )
{
// When getting UnmapNotify, it's not clear if it's the systray
// reparenting the window into itself, or if it's the window
// going away. This is obviously a flaw in the design, and we were
// just lucky it worked for so long. Kicker's systray temporarily
// sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while
// embedding it, allowing KWin to figure out. Kicker just mustn't
// crash before removing it again ... *shrug* .
int num_props ;
Atom * props = XListProperties ( qt_xdisplay ( ) , w , & num_props ) ;
if ( props ! = NULL )
{
for ( int i = 0 ;
i < num_props ;
+ + i )
if ( props [ i ] = = atoms - > kde_system_tray_embedding )
{
XFree ( props ) ;
return false ;
}
XFree ( props ) ;
}
}
systemTrayWins . remove ( w ) ;
XRemoveFromSaveSet ( qt_xdisplay ( ) , w ) ;
propagateSystemTrayWins ( ) ;
return TRUE ;
}
/*!
Propagates the systemTrayWins to the world
*/
void Workspace : : propagateSystemTrayWins ( )
{
Window * cl = new Window [ systemTrayWins . count ( ) ] ;
int i = 0 ;
for ( SystemTrayWindowList : : ConstIterator it = systemTrayWins . begin ( ) ; it ! = systemTrayWins . end ( ) ; + + it )
{
cl [ i + + ] = ( * it ) . win ;
}
rootInfo - > setKDESystemTrayWindows ( cl , i ) ;
delete [ ] cl ;
}
void Workspace : : killWindowId ( Window window_to_kill )
{
if ( window_to_kill = = None )
return ;
Window window = window_to_kill ;
Client * client = NULL ;
for ( ; ; )
{
client = findClient ( FrameIdMatchPredicate ( window ) ) ;
if ( client ! = NULL ) // found the client
break ;
Window parent , root ;
Window * children ;
unsigned int children_count ;
XQueryTree ( qt_xdisplay ( ) , window , & root , & parent , & children , & children_count ) ;
if ( children ! = NULL )
XFree ( children ) ;
if ( window = = root ) // we didn't find the client, probably an override-redirect window
break ;
window = parent ; // go up
}
if ( client ! = NULL )
client - > killWindow ( ) ;
else
XKillClient ( qt_xdisplay ( ) , window_to_kill ) ;
}
void Workspace : : sendPingToWindow ( Window window , Time timestamp )
{
rootInfo - > sendPing ( window , timestamp ) ;
}
void Workspace : : sendTakeActivity ( Client * c , Time timestamp , long flags )
{
rootInfo - > takeActivity ( c - > window ( ) , timestamp , flags ) ;
pending_take_activity = c ;
}
/*!
Takes a screenshot of the current window and puts it in the clipboard .
*/
void Workspace : : slotGrabWindow ( )
{
if ( active_client )
{
QPixmap snapshot = QPixmap : : grabWindow ( active_client - > frameId ( ) ) ;
//No XShape - no work.
if ( Shape : : available ( ) )
{
//As the first step, get the mask from XShape.
int count , order ;
XRectangle * rects = XShapeGetRectangles ( qt_xdisplay ( ) , active_client - > frameId ( ) ,
ShapeBounding , & count , & order ) ;
//The ShapeBounding region is the outermost shape of the window;
//ShapeBounding - ShapeClipping is defined to be the border.
//Since the border area is part of the window, we use bounding
// to limit our work region
if ( rects )
{
//Create a QRegion from the rectangles describing the bounding mask.
QRegion contents ;
for ( int pos = 0 ; pos < count ; pos + + )
contents + = QRegion ( rects [ pos ] . x , rects [ pos ] . y ,
rects [ pos ] . width , rects [ pos ] . height ) ;
XFree ( rects ) ;
//Create the bounding box.
QRegion bbox ( 0 , 0 , snapshot . width ( ) , snapshot . height ( ) ) ;
//Get the masked away area.
QRegion maskedAway = bbox - contents ;
QMemArray < QRect > maskedAwayRects = maskedAway . rects ( ) ;
//Construct a bitmap mask from the rectangles
QBitmap mask ( snapshot . width ( ) , snapshot . height ( ) ) ;
QPainter p ( & mask ) ;
p . fillRect ( 0 , 0 , mask . width ( ) , mask . height ( ) , Qt : : color1 ) ;
for ( uint pos = 0 ; pos < maskedAwayRects . count ( ) ; pos + + )
p . fillRect ( maskedAwayRects [ pos ] , Qt : : color0 ) ;
p . end ( ) ;
snapshot . setMask ( mask ) ;
}
}
QClipboard * cb = QApplication : : clipboard ( ) ;
cb - > setPixmap ( snapshot ) ;
}
else
slotGrabDesktop ( ) ;
}
/*!
Takes a screenshot of the whole desktop and puts it in the clipboard .
*/
void Workspace : : slotGrabDesktop ( )
{
QPixmap p = QPixmap : : grabWindow ( qt_xrootwin ( ) ) ;
QClipboard * cb = QApplication : : clipboard ( ) ;
cb - > setPixmap ( p ) ;
}
/*!
Invokes keyboard mouse emulation
*/
void Workspace : : slotMouseEmulation ( )
{
if ( mouse_emulation )
{
XUngrabKeyboard ( qt_xdisplay ( ) , qt_x_time ) ;
mouse_emulation = FALSE ;
return ;
}
if ( XGrabKeyboard ( qt_xdisplay ( ) ,
root , FALSE ,
GrabModeAsync , GrabModeAsync ,
qt_x_time ) = = GrabSuccess )
{
mouse_emulation = TRUE ;
mouse_emulation_state = 0 ;
mouse_emulation_window = 0 ;
}
}
/*!
Returns the child window under the mouse and activates the
respective client if necessary .
Auxiliary function for the mouse emulation system .
*/
WId Workspace : : getMouseEmulationWindow ( )
{
Window root ;
Window child = qt_xrootwin ( ) ;
int root_x , root_y , lx , ly ;
uint state ;
Window w ;
Client * c = 0 ;
do
{
w = child ;
if ( ! c )
c = findClient ( FrameIdMatchPredicate ( w ) ) ;
XQueryPointer ( qt_xdisplay ( ) , w , & root , & child ,
& root_x , & root_y , & lx , & ly , & state ) ;
} while ( child ! = None & & child ! = w ) ;
if ( c & & ! c - > isActive ( ) )
activateClient ( c ) ;
return ( WId ) w ;
}
/*!
Sends a faked mouse event to the specified window . Returns the new button state .
*/
unsigned int Workspace : : sendFakedMouseEvent ( QPoint pos , WId w , MouseEmulation type , int button , unsigned int state )
{
if ( ! w )
return state ;
QWidget * widget = QWidget : : find ( w ) ;
if ( ( ! widget | | widget - > inherits ( " QToolButton " ) ) & & ! findClient ( WindowMatchPredicate ( w ) ) )
{
int x , y ;
Window xw ;
XTranslateCoordinates ( qt_xdisplay ( ) , qt_xrootwin ( ) , w , pos . x ( ) , pos . y ( ) , & x , & y , & xw ) ;
if ( type = = EmuMove )
{ // motion notify events
XEvent e ;
e . type = MotionNotify ;
e . xmotion . window = w ;
e . xmotion . root = qt_xrootwin ( ) ;
e . xmotion . subwindow = w ;
e . xmotion . time = qt_x_time ;
e . xmotion . x = x ;
e . xmotion . y = y ;
e . xmotion . x_root = pos . x ( ) ;
e . xmotion . y_root = pos . y ( ) ;
e . xmotion . state = state ;
e . xmotion . is_hint = NotifyNormal ;
XSendEvent ( qt_xdisplay ( ) , w , TRUE , ButtonMotionMask , & e ) ;
}
else
{
XEvent e ;
e . type = type = = EmuRelease ? ButtonRelease : ButtonPress ;
e . xbutton . window = w ;
e . xbutton . root = qt_xrootwin ( ) ;
e . xbutton . subwindow = w ;
e . xbutton . time = qt_x_time ;
e . xbutton . x = x ;
e . xbutton . y = y ;
e . xbutton . x_root = pos . x ( ) ;
e . xbutton . y_root = pos . y ( ) ;
e . xbutton . state = state ;
e . xbutton . button = button ;
XSendEvent ( qt_xdisplay ( ) , w , TRUE , ButtonPressMask , & e ) ;
if ( type = = EmuPress )
{
switch ( button )
{
case 2 :
state | = Button2Mask ;
break ;
case 3 :
state | = Button3Mask ;
break ;
default : // 1
state | = Button1Mask ;
break ;
}
}
else
{
switch ( button )
{
case 2 :
state & = ~ Button2Mask ;
break ;
case 3 :
state & = ~ Button3Mask ;
break ;
default : // 1
state & = ~ Button1Mask ;
break ;
}
}
}
}
return state ;
}
/*!
Handles keypress event during mouse emulation
*/
bool Workspace : : keyPressMouseEmulation ( XKeyEvent & ev )
{
if ( root ! = qt_xrootwin ( ) )
return FALSE ;
int kc = XKeycodeToKeysym ( qt_xdisplay ( ) , ev . keycode , 0 ) ;
int km = ev . state & ( ControlMask | Mod1Mask | ShiftMask ) ;
bool is_control = km & ControlMask ;
bool is_alt = km & Mod1Mask ;
bool is_shift = km & ShiftMask ;
int delta = is_control ? 1 : is_alt ? 32 : 8 ;
QPoint pos = QCursor : : pos ( ) ;
switch ( kc )
{
case XK_Left :
case XK_KP_Left :
pos . rx ( ) - = delta ;
break ;
case XK_Right :
case XK_KP_Right :
pos . rx ( ) + = delta ;
break ;
case XK_Up :
case XK_KP_Up :
pos . ry ( ) - = delta ;
break ;
case XK_Down :
case XK_KP_Down :
pos . ry ( ) + = delta ;
break ;
case XK_F1 :
if ( ! mouse_emulation_state )
mouse_emulation_window = getMouseEmulationWindow ( ) ;
if ( ( mouse_emulation_state & Button1Mask ) = = 0 )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuPress , Button1 , mouse_emulation_state ) ;
if ( ! is_shift )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button1 , mouse_emulation_state ) ;
break ;
case XK_F2 :
if ( ! mouse_emulation_state )
mouse_emulation_window = getMouseEmulationWindow ( ) ;
if ( ( mouse_emulation_state & Button2Mask ) = = 0 )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuPress , Button2 , mouse_emulation_state ) ;
if ( ! is_shift )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button2 , mouse_emulation_state ) ;
break ;
case XK_F3 :
if ( ! mouse_emulation_state )
mouse_emulation_window = getMouseEmulationWindow ( ) ;
if ( ( mouse_emulation_state & Button3Mask ) = = 0 )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuPress , Button3 , mouse_emulation_state ) ;
if ( ! is_shift )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button3 , mouse_emulation_state ) ;
break ;
case XK_Return :
case XK_space :
case XK_KP_Enter :
case XK_KP_Space :
{
if ( ! mouse_emulation_state )
{
// nothing was pressed, fake a LMB click
mouse_emulation_window = getMouseEmulationWindow ( ) ;
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuPress , Button1 , mouse_emulation_state ) ;
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button1 , mouse_emulation_state ) ;
}
else
{ // release all
if ( mouse_emulation_state & Button1Mask )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button1 , mouse_emulation_state ) ;
if ( mouse_emulation_state & Button2Mask )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button2 , mouse_emulation_state ) ;
if ( mouse_emulation_state & Button3Mask )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuRelease , Button3 , mouse_emulation_state ) ;
}
}
// fall through
case XK_Escape :
XUngrabKeyboard ( qt_xdisplay ( ) , qt_x_time ) ;
mouse_emulation = FALSE ;
return TRUE ;
default :
return FALSE ;
}
QCursor : : setPos ( pos ) ;
if ( mouse_emulation_state )
mouse_emulation_state = sendFakedMouseEvent ( pos , mouse_emulation_window , EmuMove , 0 , mouse_emulation_state ) ;
return TRUE ;
}
/*!
Returns the workspace ' s desktop widget . The desktop widget is
sometimes required by clients to draw on it , for example outlines on
moving or resizing .
*/
QWidget * Workspace : : desktopWidget ( )
{
return desktop_widget ;
}
//Delayed focus functions
void Workspace : : delayFocus ( )
{
requestFocus ( delayfocus_client ) ;
cancelDelayFocus ( ) ;
}
void Workspace : : requestDelayFocus ( Client * c )
{
delayfocus_client = c ;
delete delayFocusTimer ;
delayFocusTimer = new QTimer ( this ) ;
connect ( delayFocusTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( delayFocus ( ) ) ) ;
delayFocusTimer - > start ( options - > delayFocusInterval , TRUE ) ;
}
void Workspace : : cancelDelayFocus ( )
{
delete delayFocusTimer ;
delayFocusTimer = 0 ;
}
// Electric Borders
//========================================================================//
// Electric Border Window management. Electric borders allow a user
// to change the virtual desktop by moving the mouse pointer to the
// borders. Technically this is done with input only windows. Since
// electric borders can be switched on and off, we have these two
// functions to create and destroy them.
void Workspace : : checkElectricBorders ( bool force )
{
if ( force )
destroyBorderWindows ( ) ;
electric_current_border = 0 ;
QRect r = QApplication : : desktop ( ) - > geometry ( ) ;
electricTop = r . top ( ) ;
electricBottom = r . bottom ( ) ;
electricLeft = r . left ( ) ;
electricRight = r . right ( ) ;
if ( options - > electricBorders ( ) = = Options : : ElectricAlways )
createBorderWindows ( ) ;
else
destroyBorderWindows ( ) ;
}
void Workspace : : createBorderWindows ( )
{
if ( electric_have_borders )
return ;
electric_have_borders = true ;
QRect r = QApplication : : desktop ( ) - > geometry ( ) ;
XSetWindowAttributes attributes ;
unsigned long valuemask ;
attributes . override_redirect = True ;
attributes . event_mask = ( EnterWindowMask | LeaveWindowMask ) ;
valuemask = ( CWOverrideRedirect | CWEventMask | CWCursor ) ;
attributes . cursor = XCreateFontCursor ( qt_xdisplay ( ) ,
XC_sb_up_arrow ) ;
electric_top_border = XCreateWindow ( qt_xdisplay ( ) , qt_xrootwin ( ) ,
0 , 0 ,
r . width ( ) , 1 ,
0 ,
CopyFromParent , InputOnly ,
CopyFromParent ,
valuemask , & attributes ) ;
XMapWindow ( qt_xdisplay ( ) , electric_top_border ) ;
attributes . cursor = XCreateFontCursor ( qt_xdisplay ( ) ,
XC_sb_down_arrow ) ;
electric_bottom_border = XCreateWindow ( qt_xdisplay ( ) , qt_xrootwin ( ) ,
0 , r . height ( ) - 1 ,
r . width ( ) , 1 ,
0 ,
CopyFromParent , InputOnly ,
CopyFromParent ,
valuemask , & attributes ) ;
XMapWindow ( qt_xdisplay ( ) , electric_bottom_border ) ;
attributes . cursor = XCreateFontCursor ( qt_xdisplay ( ) ,
XC_sb_left_arrow ) ;
electric_left_border = XCreateWindow ( qt_xdisplay ( ) , qt_xrootwin ( ) ,
0 , 0 ,
1 , r . height ( ) ,
0 ,
CopyFromParent , InputOnly ,
CopyFromParent ,
valuemask , & attributes ) ;
XMapWindow ( qt_xdisplay ( ) , electric_left_border ) ;
attributes . cursor = XCreateFontCursor ( qt_xdisplay ( ) ,
XC_sb_right_arrow ) ;
electric_right_border = XCreateWindow ( qt_xdisplay ( ) , qt_xrootwin ( ) ,
r . width ( ) - 1 , 0 ,
1 , r . height ( ) ,
0 ,
CopyFromParent , InputOnly ,
CopyFromParent ,
valuemask , & attributes ) ;
XMapWindow ( qt_xdisplay ( ) , electric_right_border ) ;
// Set XdndAware on the windows, so that DND enter events are received (#86998)
Atom version = 4 ; // XDND version
XChangeProperty ( qt_xdisplay ( ) , electric_top_border , atoms - > xdnd_aware , XA_ATOM ,
32 , PropModeReplace , ( unsigned char * ) & version , 1 ) ;
XChangeProperty ( qt_xdisplay ( ) , electric_bottom_border , atoms - > xdnd_aware , XA_ATOM ,
32 , PropModeReplace , ( unsigned char * ) & version , 1 ) ;
XChangeProperty ( qt_xdisplay ( ) , electric_left_border , atoms - > xdnd_aware , XA_ATOM ,
32 , PropModeReplace , ( unsigned char * ) & version , 1 ) ;
XChangeProperty ( qt_xdisplay ( ) , electric_right_border , atoms - > xdnd_aware , XA_ATOM ,
32 , PropModeReplace , ( unsigned char * ) & version , 1 ) ;
}
// Electric Border Window management. Electric borders allow a user
// to change the virtual desktop by moving the mouse pointer to the
// borders. Technically this is done with input only windows. Since
// electric borders can be switched on and off, we have these two
// functions to create and destroy them.
void Workspace : : destroyBorderWindows ( )
{
if ( ! electric_have_borders )
return ;
electric_have_borders = false ;
if ( electric_top_border )
XDestroyWindow ( qt_xdisplay ( ) , electric_top_border ) ;
if ( electric_bottom_border )
XDestroyWindow ( qt_xdisplay ( ) , electric_bottom_border ) ;
if ( electric_left_border )
XDestroyWindow ( qt_xdisplay ( ) , electric_left_border ) ;
if ( electric_right_border )
XDestroyWindow ( qt_xdisplay ( ) , electric_right_border ) ;
electric_top_border = None ;
electric_bottom_border = None ;
electric_left_border = None ;
electric_right_border = None ;
}
void Workspace : : clientMoved ( const QPoint & pos , Time now )
{
if ( options - > electricBorders ( ) = = Options : : ElectricDisabled )
return ;
if ( ( pos . x ( ) ! = electricLeft ) & &
( pos . x ( ) ! = electricRight ) & &
( pos . y ( ) ! = electricTop ) & &
( pos . y ( ) ! = electricBottom ) )
return ;
Time treshold_set = options - > electricBorderDelay ( ) ; // set timeout
Time treshold_reset = 250 ; // reset timeout
int distance_reset = 30 ; // Mouse should not move more than this many pixels
int border = 0 ;
if ( pos . x ( ) = = electricLeft )
border = 1 ;
else if ( pos . x ( ) = = electricRight )
border = 2 ;
else if ( pos . y ( ) = = electricTop )
border = 3 ;
else if ( pos . y ( ) = = electricBottom )
border = 4 ;
if ( ( electric_current_border = = border ) & &
( timestampDiff ( electric_time_last , now ) < treshold_reset ) & &
( ( pos - electric_push_point ) . manhattanLength ( ) < distance_reset ) )
{
electric_time_last = now ;
if ( timestampDiff ( electric_time_first , now ) > treshold_set )
{
electric_current_border = 0 ;
QRect r = QApplication : : desktop ( ) - > geometry ( ) ;
int offset ;
int desk_before = currentDesktop ( ) ;
switch ( border )
{
case 1 :
slotSwitchDesktopLeft ( ) ;
if ( currentDesktop ( ) ! = desk_before )
{
offset = r . width ( ) / 5 ;
QCursor : : setPos ( r . width ( ) - offset , pos . y ( ) ) ;
}
break ;
case 2 :
slotSwitchDesktopRight ( ) ;
if ( currentDesktop ( ) ! = desk_before )
{
offset = r . width ( ) / 5 ;
QCursor : : setPos ( offset , pos . y ( ) ) ;
}
break ;
case 3 :
slotSwitchDesktopUp ( ) ;
if ( currentDesktop ( ) ! = desk_before )
{
offset = r . height ( ) / 5 ;
QCursor : : setPos ( pos . x ( ) , r . height ( ) - offset ) ;
}
break ;
case 4 :
slotSwitchDesktopDown ( ) ;
if ( currentDesktop ( ) ! = desk_before )
{
offset = r . height ( ) / 5 ;
QCursor : : setPos ( pos . x ( ) , offset ) ;
}
break ;
}
return ;
}
}
else
{
electric_current_border = border ;
electric_time_first = now ;
electric_time_last = now ;
electric_push_point = pos ;
}
int mouse_warp = 1 ;
// reset the pointer to find out wether the user is really pushing
switch ( border )
{
case 1 : QCursor : : setPos ( pos . x ( ) + mouse_warp , pos . y ( ) ) ; break ;
case 2 : QCursor : : setPos ( pos . x ( ) - mouse_warp , pos . y ( ) ) ; break ;
case 3 : QCursor : : setPos ( pos . x ( ) , pos . y ( ) + mouse_warp ) ; break ;
case 4 : QCursor : : setPos ( pos . x ( ) , pos . y ( ) - mouse_warp ) ; break ;
}
}
// this function is called when the user entered an electric border
// with the mouse. It may switch to another virtual desktop
bool Workspace : : electricBorder ( XEvent * e )
{
if ( ! electric_have_borders )
return false ;
if ( e - > type = = EnterNotify )
{
if ( e - > xcrossing . window = = electric_top_border | |
e - > xcrossing . window = = electric_left_border | |
e - > xcrossing . window = = electric_bottom_border | |
e - > xcrossing . window = = electric_right_border )
// the user entered an electric border
{
clientMoved ( QPoint ( e - > xcrossing . x_root , e - > xcrossing . y_root ) , e - > xcrossing . time ) ;
return true ;
}
}
if ( e - > type = = ClientMessage )
{
if ( e - > xclient . message_type = = atoms - > xdnd_position
& & ( e - > xclient . window = = electric_top_border
| | e - > xclient . window = = electric_bottom_border
| | e - > xclient . window = = electric_left_border
| | e - > xclient . window = = electric_right_border ) )
{
updateXTime ( ) ;
clientMoved ( QPoint ( e - > xclient . data . l [ 2 ] > > 16 , e - > xclient . data . l [ 2 ] & 0xffff ) , qt_x_time ) ;
return true ;
}
}
return false ;
}
// electric borders (input only windows) have to be always on the
// top. For that reason kwm calls this function always after some
// windows have been raised.
void Workspace : : raiseElectricBorders ( )
{
if ( electric_have_borders )
{
XRaiseWindow ( qt_xdisplay ( ) , electric_top_border ) ;
XRaiseWindow ( qt_xdisplay ( ) , electric_left_border ) ;
XRaiseWindow ( qt_xdisplay ( ) , electric_bottom_border ) ;
XRaiseWindow ( qt_xdisplay ( ) , electric_right_border ) ;
}
}
void Workspace : : addTopMenu ( Client * c )
{
assert ( c - > isTopMenu ( ) ) ;
assert ( ! topmenus . contains ( c ) ) ;
topmenus . append ( c ) ;
if ( managingTopMenus ( ) )
{
int minsize = c - > minSize ( ) . height ( ) ;
if ( minsize > topMenuHeight ( ) )
{
topmenu_height = minsize ;
updateTopMenuGeometry ( ) ;
}
updateTopMenuGeometry ( c ) ;
updateCurrentTopMenu ( ) ;
}
// kdDebug() << "NEW TOPMENU:" << c << endl;
}
void Workspace : : removeTopMenu ( Client * c )
{
// if( c->isTopMenu())
// kdDebug() << "REMOVE TOPMENU:" << c << endl;
assert ( c - > isTopMenu ( ) ) ;
assert ( topmenus . contains ( c ) ) ;
topmenus . remove ( c ) ;
updateCurrentTopMenu ( ) ;
// TODO reduce topMenuHeight() if possible?
}
void Workspace : : lostTopMenuSelection ( )
{
// kdDebug() << "lost TopMenu selection" << endl;
// make sure this signal is always set when not owning the selection
disconnect ( topmenu_watcher , SIGNAL ( lostOwner ( ) ) , this , SLOT ( lostTopMenuOwner ( ) ) ) ;
connect ( topmenu_watcher , SIGNAL ( lostOwner ( ) ) , this , SLOT ( lostTopMenuOwner ( ) ) ) ;
if ( ! managing_topmenus )
return ;
connect ( topmenu_watcher , SIGNAL ( lostOwner ( ) ) , this , SLOT ( lostTopMenuOwner ( ) ) ) ;
disconnect ( topmenu_selection , SIGNAL ( lostOwnership ( ) ) , this , SLOT ( lostTopMenuSelection ( ) ) ) ;
managing_topmenus = false ;
delete topmenu_space ;
topmenu_space = NULL ;
updateClientArea ( ) ;
for ( ClientList : : ConstIterator it = topmenus . begin ( ) ;
it ! = topmenus . end ( ) ;
+ + it )
( * it ) - > checkWorkspacePosition ( ) ;
}
void Workspace : : lostTopMenuOwner ( )
{
if ( ! options - > topMenuEnabled ( ) )
return ;
// kdDebug() << "TopMenu selection lost owner" << endl;
if ( ! topmenu_selection - > claim ( false ) )
{
// kdDebug() << "Failed to claim TopMenu selection" << endl;
return ;
}
// kdDebug() << "claimed TopMenu selection" << endl;
setupTopMenuHandling ( ) ;
}
void Workspace : : setupTopMenuHandling ( )
{
if ( managing_topmenus )
return ;
connect ( topmenu_selection , SIGNAL ( lostOwnership ( ) ) , this , SLOT ( lostTopMenuSelection ( ) ) ) ;
disconnect ( topmenu_watcher , SIGNAL ( lostOwner ( ) ) , this , SLOT ( lostTopMenuOwner ( ) ) ) ;
managing_topmenus = true ;
topmenu_space = new QWidget ;
Window stack [ 2 ] ;
stack [ 0 ] = supportWindow - > winId ( ) ;
stack [ 1 ] = topmenu_space - > winId ( ) ;
XRestackWindows ( qt_xdisplay ( ) , stack , 2 ) ;
updateTopMenuGeometry ( ) ;
topmenu_space - > show ( ) ;
updateClientArea ( ) ;
updateCurrentTopMenu ( ) ;
}
int Workspace : : topMenuHeight ( ) const
{
if ( topmenu_height = = 0 )
{ // simply create a dummy menubar and use its preffered height as the menu height
KMenuBar tmpmenu ;
tmpmenu . insertItem ( " dummy " ) ;
topmenu_height = tmpmenu . sizeHint ( ) . height ( ) ;
}
return topmenu_height ;
}
KDecoration * Workspace : : createDecoration ( KDecorationBridge * bridge )
{
return mgr - > createDecoration ( bridge ) ;
}
QString Workspace : : desktopName ( int desk ) const
{
return QString : : fromUtf8 ( rootInfo - > desktopName ( desk ) ) ;
}
bool Workspace : : checkStartupNotification ( Window w , KStartupInfoId & id , KStartupInfoData & data )
{
return startup - > checkStartup ( w , id , data ) = = KStartupInfo : : Match ;
}
/*!
Puts the focus on a dummy window
Just using XSetInputFocus ( ) with None would block keyboard input
*/
void Workspace : : focusToNull ( )
{
XSetInputFocus ( qt_xdisplay ( ) , null_focus_window , RevertToPointerRoot , qt_x_time ) ;
}
void Workspace : : helperDialog ( const QString & message , const Client * c )
{
QStringList args ;
QString type ;
if ( message = = " noborderaltf3 " )
{
QString shortcut = QString ( " %1 (%2) " ) . arg ( keys - > label ( " Window Operations Menu " ) )
. arg ( keys - > shortcut ( " Window Operations Menu " ) . seq ( 0 ) . toString ( ) ) ;
args < < " --msgbox " < <
i18n ( " You have selected to show a window without its border. \n "
" Without the border, you will not be able to enable the border "
" again using the mouse: use the window operations menu instead, "
" activated using the %1 keyboard shortcut. " )
. arg ( shortcut ) ;
type = " altf3warning " ;
}
else if ( message = = " fullscreenaltf3 " )
{
QString shortcut = QString ( " %1 (%2) " ) . arg ( keys - > label ( " Window Operations Menu " ) )
. arg ( keys - > shortcut ( " Window Operations Menu " ) . seq ( 0 ) . toString ( ) ) ;
args < < " --msgbox " < <
i18n ( " You have selected to show a window in fullscreen mode. \n "
" If the application itself does not have an option to turn the fullscreen "
" mode off you will not be able to disable it "
" again using the mouse: use the window operations menu instead, "
" activated using the %1 keyboard shortcut. " )
. arg ( shortcut ) ;
type = " altf3warning " ;
}
else
assert ( false ) ;
KProcess proc ;
proc < < " kdialog " < < args ;
if ( ! type . isEmpty ( ) )
{
KConfig cfg ( " kwin_dialogsrc " ) ;
cfg . setGroup ( " Notification Messages " ) ; // this depends on KMessageBox
if ( ! cfg . readBoolEntry ( type , true ) ) // has don't show again checked
return ; // save launching kdialog
proc < < " --dontagain " < < " kwin_dialogsrc: " + type ;
}
if ( c ! = NULL )
proc < < " --embed " < < QString : : number ( c - > window ( ) ) ;
proc . start ( KProcess : : DontCare ) ;
}
// kompmgr stuff
void Workspace : : startKompmgr ( )
{
if ( ! kompmgr | | kompmgr - > isRunning ( ) )
return ;
if ( ! kompmgr - > start ( KProcess : : OwnGroup , KProcess : : Stderr ) )
{
options - > useTranslucency = FALSE ;
KProcess proc ;
proc < < " kdialog " < < " --error "
< < i18n ( " The Composite Manager could not be started. \\ nMake sure you have \" kompmgr \" in a $PATH directory. " )
< < " --title " < < " Composite Manager Failure " ;
proc . start ( KProcess : : DontCare ) ;
}
else
{
delete kompmgr_selection ;
char selection_name [ 100 ] ;
sprintf ( selection_name , " _NET_WM_CM_S%d " , DefaultScreen ( qt_xdisplay ( ) ) ) ;
kompmgr_selection = new KSelectionOwner ( selection_name ) ;
connect ( kompmgr_selection , SIGNAL ( lostOwnership ( ) ) , SLOT ( stopKompmgr ( ) ) ) ;
kompmgr_selection - > claim ( true ) ;
connect ( kompmgr , SIGNAL ( processExited ( KProcess * ) ) , SLOT ( restartKompmgr ( ) ) ) ;
options - > useTranslucency = TRUE ;
allowKompmgrRestart = FALSE ;
QTimer : : singleShot ( 60000 , this , SLOT ( unblockKompmgrRestart ( ) ) ) ;
QByteArray ba ;
QDataStream arg ( ba , IO_WriteOnly ) ;
arg < < " " ;
kapp - > dcopClient ( ) - > emitDCOPSignal ( " default " , " kompmgrStarted() " , ba ) ;
}
if ( popup ) { delete popup ; popup = 0L ; } // to add/remove opacity slider
}
void Workspace : : stopKompmgr ( )
{
if ( ! kompmgr | | ! kompmgr - > isRunning ( ) )
return ;
delete kompmgr_selection ;
kompmgr_selection = NULL ;
kompmgr - > disconnect ( this , SLOT ( restartKompmgr ( ) ) ) ;
options - > useTranslucency = FALSE ;
if ( popup ) { delete popup ; popup = 0L ; } // to add/remove opacity slider
kompmgr - > kill ( ) ;
QByteArray ba ;
QDataStream arg ( ba , IO_WriteOnly ) ;
arg < < " " ;
kapp - > dcopClient ( ) - > emitDCOPSignal ( " default " , " kompmgrStopped() " , ba ) ;
}
bool Workspace : : kompmgrIsRunning ( )
{
return kompmgr & & kompmgr - > isRunning ( ) ;
}
void Workspace : : unblockKompmgrRestart ( )
{
allowKompmgrRestart = TRUE ;
}
void Workspace : : restartKompmgr ( )
// this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); QTimer::singleShot(200, workspace, SLOT(startKompmgr()));
{
if ( ! allowKompmgrRestart ) // uh-ohh
{
delete kompmgr_selection ;
kompmgr_selection = NULL ;
options - > useTranslucency = FALSE ;
KProcess proc ;
proc < < " kdialog " < < " --error "
< < i18n ( " The Composite Manager crashed twice within a minute and is therefore disabled for this session. " )
< < " --title " < < i18n ( " Composite Manager Failure " ) ;
proc . start ( KProcess : : DontCare ) ;
return ;
}
if ( ! kompmgr )
return ;
// this should be useless, i keep it for maybe future need
// if (!kcompmgr)
// {
// kompmgr = new KProcess;
// kompmgr->clearArguments();
// *kompmgr << "kompmgr";
// }
// -------------------
if ( ! kompmgr - > start ( KProcess : : NotifyOnExit , KProcess : : Stderr ) )
{
delete kompmgr_selection ;
kompmgr_selection = NULL ;
options - > useTranslucency = FALSE ;
KProcess proc ;
proc < < " kdialog " < < " --error "
< < i18n ( " The Composite Manager could not be started. \\ nMake sure you have \" kompmgr \" in a $PATH directory. " )
< < " --title " < < i18n ( " Composite Manager Failure " ) ;
proc . start ( KProcess : : DontCare ) ;
}
else
{
allowKompmgrRestart = FALSE ;
QTimer : : singleShot ( 60000 , this , SLOT ( unblockKompmgrRestart ( ) ) ) ;
}
}
void Workspace : : handleKompmgrOutput ( KProcess * , char * buffer , int buflen )
{
QString message ;
QString output = QString : : fromLocal8Bit ( buffer , buflen ) ;
if ( output . contains ( " Started " , false ) )
; // don't do anything, just pass to the connection release
else if ( output . contains ( " Can't open display " , false ) )
message = i18n ( " <qt><b>kompmgr failed to open the display</b><br>There is probably an invalid display entry in your ~/.xcompmgrrc.</qt> " ) ;
else if ( output . contains ( " No render extension " , false ) )
message = i18n ( " <qt><b>kompmgr cannot find the Xrender extension</b><br>You are using either an outdated or a crippled version of XOrg.<br>Get XOrg ≥ 6.8 from www.freedesktop.org.<br></qt> " ) ;
else if ( output . contains ( " No composite extension " , false ) )
message = i18n ( " <qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br> "
" <i>Section \" Extensions \" <br> "
" Option \" Composite \" \" Enable \" <br> "
" EndSection</i></qt> " ) ;
else if ( output . contains ( " No damage extension " , false ) )
message = i18n ( " <qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt> " ) ;
else if ( output . contains ( " No XFixes extension " , false ) )
message = i18n ( " <qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt> " ) ;
else return ; //skip others
// kompmgr startup failed or succeeded, release connection
kompmgr - > closeStderr ( ) ;
disconnect ( kompmgr , SIGNAL ( receivedStderr ( KProcess * , char * , int ) ) , this , SLOT ( handleKompmgrOutput ( KProcess * , char * , int ) ) ) ;
if ( ! message . isEmpty ( ) )
{
KProcess proc ;
proc < < " kdialog " < < " --error "
< < message
< < " --title " < < i18n ( " Composite Manager Failure " ) ;
proc . start ( KProcess : : DontCare ) ;
}
}
void Workspace : : setOpacity ( unsigned long winId , unsigned int opacityPercent )
{
if ( opacityPercent > 100 ) opacityPercent = 100 ;
for ( ClientList : : ConstIterator it = stackingOrder ( ) . begin ( ) ; it ! = stackingOrder ( ) . end ( ) ; it + + )
if ( winId = = ( * it ) - > window ( ) )
{
( * it ) - > setOpacity ( opacityPercent < 100 , ( unsigned int ) ( ( opacityPercent / 100.0 ) * 0xFFFFFFFF ) ) ;
return ;
}
}
void Workspace : : setShadowSize ( unsigned long winId , unsigned int shadowSizePercent )
{
//this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400%
if ( shadowSizePercent > 400 ) shadowSizePercent = 400 ;
for ( ClientList : : ConstIterator it = stackingOrder ( ) . begin ( ) ; it ! = stackingOrder ( ) . end ( ) ; it + + )
if ( winId = = ( * it ) - > window ( ) )
{
( * it ) - > setShadowSize ( shadowSizePercent ) ;
return ;
}
}
void Workspace : : setUnshadowed ( unsigned long winId )
{
for ( ClientList : : ConstIterator it = stackingOrder ( ) . begin ( ) ; it ! = stackingOrder ( ) . end ( ) ; it + + )
if ( winId = = ( * it ) - > window ( ) )
{
( * it ) - > setShadowSize ( 0 ) ;
return ;
}
}
void Workspace : : setShowingDesktop ( bool showing )
{
rootInfo - > setShowingDesktop ( showing ) ;
showing_desktop = showing ;
+ + block_showing_desktop ;
if ( showing_desktop )
{
showing_desktop_clients . clear ( ) ;
+ + block_focus ;
ClientList cls = stackingOrder ( ) ;
// find them first, then minimize, otherwise transients may get minimized with the window
// they're transient for
for ( ClientList : : ConstIterator it = cls . begin ( ) ;
it ! = cls . end ( ) ;
+ + it )
{
if ( ( * it ) - > isOnCurrentDesktop ( ) & & ( * it ) - > isShown ( true ) & & ! ( * it ) - > isSpecialWindow ( ) )
showing_desktop_clients . prepend ( * it ) ; // topmost first to reduce flicker
}
for ( ClientList : : ConstIterator it = showing_desktop_clients . begin ( ) ;
it ! = showing_desktop_clients . end ( ) ;
+ + it )
( * it ) - > minimize ( true ) ;
- - block_focus ;
if ( Client * desk = findDesktop ( true , currentDesktop ( ) ) )
requestFocus ( desk ) ;
}
else
{
for ( ClientList : : ConstIterator it = showing_desktop_clients . begin ( ) ;
it ! = showing_desktop_clients . end ( ) ;
+ + it )
( * it ) - > unminimize ( true ) ;
if ( showing_desktop_clients . count ( ) > 0 )
requestFocus ( showing_desktop_clients . first ( ) ) ;
showing_desktop_clients . clear ( ) ;
}
- - block_showing_desktop ;
}
// Following Kicker's behavior:
// Changing a virtual desktop resets the state and shows the windows again.
// Unminimizing a window resets the state but keeps the windows hidden (except
// the one that was unminimized).
// A new window resets the state and shows the windows again, with the new window
// being active. Due to popular demand (#67406) by people who apparently
// don't see a difference between "show desktop" and "minimize all", this is not
// true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
// a new window resets the state but doesn't show windows.
void Workspace : : resetShowingDesktop ( bool keep_hidden )
{
if ( block_showing_desktop > 0 )
return ;
rootInfo - > setShowingDesktop ( false ) ;
showing_desktop = false ;
+ + block_showing_desktop ;
if ( ! keep_hidden )
{
for ( ClientList : : ConstIterator it = showing_desktop_clients . begin ( ) ;
it ! = showing_desktop_clients . end ( ) ;
+ + it )
( * it ) - > unminimize ( true ) ;
}
showing_desktop_clients . clear ( ) ;
- - block_showing_desktop ;
}
// Activating/deactivating this feature works like this:
// When nothing is active, and the shortcut is pressed, global shortcuts are disabled
// (using global_shortcuts_disabled)
// When a window that has disabling forced is activated, global shortcuts are disabled.
// (using global_shortcuts_disabled_for_client)
// When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
// or for a client), they are enabled again.
void Workspace : : slotDisableGlobalShortcuts ( )
{
if ( global_shortcuts_disabled | | global_shortcuts_disabled_for_client )
disableGlobalShortcuts ( false ) ;
else
disableGlobalShortcuts ( true ) ;
}
static bool pending_dfc = false ;
void Workspace : : disableGlobalShortcutsForClient ( bool disable )
{
if ( global_shortcuts_disabled_for_client = = disable )
return ;
if ( ! global_shortcuts_disabled )
{
if ( disable )
pending_dfc = true ;
KIPC : : sendMessageAll ( KIPC : : BlockShortcuts , disable ) ;
// kwin will get the kipc message too
}
}
void Workspace : : disableGlobalShortcuts ( bool disable )
{
KIPC : : sendMessageAll ( KIPC : : BlockShortcuts , disable ) ;
// kwin will get the kipc message too
}
void Workspace : : kipcMessage ( int id , int data )
{
if ( id ! = KIPC : : BlockShortcuts )
return ;
if ( pending_dfc & & data )
{
global_shortcuts_disabled_for_client = true ;
pending_dfc = false ;
}
else
{
global_shortcuts_disabled = data ;
global_shortcuts_disabled_for_client = false ;
}
// update also Alt+LMB actions etc.
for ( ClientList : : ConstIterator it = clients . begin ( ) ;
it ! = clients . end ( ) ;
+ + it )
( * it ) - > updateMouseGrab ( ) ;
}
} // namespace
# include "workspace.moc"