add more spacing
[personal-kdebase.git] / workspace / kwin / workspace.cpp
blobc46c6272bab2c2c77e25804264b84de05199693c
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
22 //#define QT_CLEAN_NAMESPACE
24 #include "workspace.h"
26 #include <kapplication.h>
27 #include <kstartupinfo.h>
28 #include <fixx11h.h>
29 #include <kconfig.h>
30 #include <kglobal.h>
31 #include <klocale.h>
32 #include <QRegExp>
33 #include <QPainter>
34 #include <QBitmap>
35 #include <QClipboard>
36 #include <kmenubar.h>
37 #include <kprocess.h>
38 #include <kglobalaccel.h>
39 #include <QToolButton>
40 #include <kactioncollection.h>
41 #include <kaction.h>
42 #include <kconfiggroup.h>
43 #include <QtDBus/QtDBus>
45 #include "client.h"
46 #include "popupinfo.h"
47 #include "tabbox.h"
48 #include "atoms.h"
49 #include "placement.h"
50 #include "notifications.h"
51 #include "group.h"
52 #include "rules.h"
53 #include "kwinadaptor.h"
54 #include "unmanaged.h"
55 #include "scene.h"
56 #include "deleted.h"
57 #include "effects.h"
59 #include <X11/extensions/shape.h>
60 #include <X11/keysym.h>
61 #include <X11/keysymdef.h>
62 #include <X11/cursorfont.h>
63 #include <QX11Info>
64 #include <stdio.h>
65 #include <kauthorized.h>
66 #include <ktoolinvocation.h>
67 #include <kglobalsettings.h>
69 #include <kephal/screens.h>
71 namespace KWin
74 extern int screen_number;
76 Workspace* Workspace::_self = 0;
78 //-----------------------------------------------------------------------------
79 // Rikkus: This class is too complex. It needs splitting further.
80 // It's a nightmare to understand, especially with so few comments :(
82 // Matthias: Feel free to ask me questions about it. Feel free to add
83 // comments. I dissagree that further splittings makes it easier. 2500
84 // lines are not too much. It's the task that is complex, not the
85 // code.
86 //-----------------------------------------------------------------------------
88 Workspace::Workspace( bool restore )
89 : QObject( 0 )
90 , current_desktop( 0 )
91 , number_of_desktops( 0 )
92 , active_popup( NULL )
93 , active_popup_client( NULL )
94 , temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false )
95 , rules_updates_disabled( false )
96 , active_client( 0 )
97 , last_active_client( 0 )
98 , most_recently_raised( 0 )
99 , movingClient( 0 )
100 , pending_take_activity( NULL )
101 , active_screen( 0 )
102 , delayfocus_client( 0 )
103 , force_restacking( false )
104 , x_stacking_dirty( true )
105 , showing_desktop( false )
106 , block_showing_desktop( 0 )
107 , was_user_interaction( false )
108 , session_saving( false )
109 , control_grab( false )
110 , tab_grab( false )
111 , mouse_emulation( false )
112 , block_focus( 0 )
113 , tab_box( 0 )
114 , popupinfo( 0 )
115 , popup( 0 )
116 , advanced_popup( 0 )
117 , trans_popup( 0 )
118 , desk_popup( 0 )
119 , keys( 0 )
120 , client_keys( NULL )
121 , client_keys_dialog( NULL )
122 , client_keys_client( NULL )
123 , disable_shortcuts_keys( NULL )
124 , global_shortcuts_disabled( false )
125 , global_shortcuts_disabled_for_client( false )
126 , workspaceInit( true )
127 , startup( 0 )
128 , layoutOrientation( Qt::Vertical )
129 , layoutX( -1 )
130 , layoutY( 2 )
131 , managing_topmenus( false )
132 , topmenu_selection( NULL )
133 , topmenu_watcher( NULL )
134 , topmenu_height( 0 )
135 , topmenu_space( NULL )
136 , set_active_client_recursion( 0 )
137 , block_stacking_updates( 0 )
138 , forced_global_mouse_grab( false )
139 , cm_selection( NULL )
140 , compositingSuspended( false )
141 , compositeRate( 0 )
142 , overlay( None )
143 , overlay_visible( true )
144 , overlay_shown( false )
145 , transSlider( NULL )
146 , transButton( NULL )
147 , forceUnredirectCheck( true )
149 (void) new KWinAdaptor( this );
151 QDBusConnection dbus = QDBusConnection::sessionBus();
152 dbus.registerObject( "/KWin", this );
153 dbus.connect( QString(), "/KWin", "org.kde.KWin", "reloadConfig",
154 this, SLOT( slotReloadConfig() ));
155 dbus.connect( QString(), "/KWin", "org.kde.KWin", "reinitCompositing",
156 this, SLOT( slotReinitCompositing() ));
158 _self = this;
159 mgr = new PluginMgr;
160 QX11Info info;
161 default_colormap = DefaultColormap( display(), info.screen() );
162 installed_colormap = default_colormap;
164 for( int i = 0; i < ELECTRIC_COUNT; ++i )
166 electric_reserved[i] = 0;
167 electric_windows[i] = None;
170 connect( &temporaryRulesMessages, SIGNAL( gotMessage(const QString&) ),
171 this, SLOT( gotTemporaryRulesMessage(const QString&) ));
172 connect( &rulesUpdatedTimer, SIGNAL( timeout() ), this, SLOT( writeWindowRules() ));
173 connect( &unredirectTimer, SIGNAL( timeout() ), this, SLOT( delayedCheckUnredirect() ));
174 unredirectTimer.setSingleShot( true );
176 updateXTime(); // Needed for proper initialization of user_time in Client ctor
178 delayFocusTimer = 0;
180 if( restore )
181 loadSessionInfo();
183 loadWindowRules();
185 // Call this before XSelectInput() on the root window
186 startup = new KStartupInfo(
187 KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this );
189 // Select windowmanager privileges
190 XSelectInput( display(), rootWindow(),
191 KeyPressMask |
192 PropertyChangeMask |
193 ColormapChangeMask |
194 SubstructureRedirectMask |
195 SubstructureNotifyMask |
196 FocusChangeMask | // For NotifyDetailNone
197 ExposureMask
200 Extensions::init();
201 setupCompositing();
203 // Compatibility
204 long data = 1;
206 XChangeProperty(
207 display(),
208 rootWindow(),
209 atoms->kwin_running,
210 atoms->kwin_running,
212 PropModeAppend,
213 (unsigned char*)( &data ),
217 client_keys = new KActionCollection( this );
218 initShortcuts();
219 tab_box = new TabBox( this );
220 popupinfo = new PopupInfo( this );
222 init();
224 connect( Kephal::Screens::self(), SIGNAL( screenAdded(Kephal::Screen*) ), SLOT( desktopResized() ));
225 connect( Kephal::Screens::self(), SIGNAL( screenRemoved(int) ), SLOT( desktopResized() ));
226 connect( Kephal::Screens::self(), SIGNAL( screenResized(Kephal::Screen*, QSize, QSize) ), SLOT( desktopResized() ));
227 connect( Kephal::Screens::self(), SIGNAL( screenMoved(Kephal::Screen*, QPoint, QPoint) ), SLOT( desktopResized() ));
230 void Workspace::init()
232 if( options->electricBorders() == Options::ElectricAlways )
233 reserveElectricBorderSwitching( true );
234 updateElectricBorders();
236 // Not used yet
237 //topDock = 0L;
238 //maximizedWindowCounter = 0;
240 supportWindow = new QWidget( NULL, Qt::X11BypassWindowManagerHint );
241 XLowerWindow( display(), supportWindow->winId() ); // See usage in layers.cpp
243 XSetWindowAttributes attr;
244 attr.override_redirect = 1;
245 null_focus_window = XCreateWindow( display(), rootWindow(), -1,-1, 1, 1, 0, CopyFromParent,
246 InputOnly, CopyFromParent, CWOverrideRedirect, &attr );
247 XMapWindow( display(), null_focus_window );
249 unsigned long protocols[5] =
251 NET::Supported |
252 NET::SupportingWMCheck |
253 NET::ClientList |
254 NET::ClientListStacking |
255 NET::DesktopGeometry |
256 NET::NumberOfDesktops |
257 NET::CurrentDesktop |
258 NET::ActiveWindow |
259 NET::WorkArea |
260 NET::CloseWindow |
261 NET::DesktopNames |
262 NET::WMName |
263 NET::WMVisibleName |
264 NET::WMDesktop |
265 NET::WMWindowType |
266 NET::WMState |
267 NET::WMStrut |
268 NET::WMIconGeometry |
269 NET::WMIcon |
270 NET::WMPid |
271 NET::WMMoveResize |
272 NET::WMFrameExtents |
273 NET::WMPing
275 NET::NormalMask |
276 NET::DesktopMask |
277 NET::DockMask |
278 NET::ToolbarMask |
279 NET::MenuMask |
280 NET::DialogMask |
281 NET::OverrideMask |
282 NET::TopMenuMask |
283 NET::UtilityMask |
284 NET::SplashMask |
285 // No compositing window types here unless we support them also as managed window types
288 NET::Modal |
289 //NET::Sticky | // Large desktops not supported (and probably never will be)
290 NET::MaxVert |
291 NET::MaxHoriz |
292 NET::Shaded |
293 NET::SkipTaskbar |
294 NET::KeepAbove |
295 //NET::StaysOnTop | // The same like KeepAbove
296 NET::SkipPager |
297 NET::Hidden |
298 NET::FullScreen |
299 NET::KeepBelow |
300 NET::DemandsAttention |
303 NET::WM2UserTime |
304 NET::WM2StartupId |
305 NET::WM2AllowedActions |
306 NET::WM2RestackWindow |
307 NET::WM2MoveResizeWindow |
308 NET::WM2ExtendedStrut |
309 NET::WM2KDETemporaryRules |
310 NET::WM2ShowingDesktop |
311 NET::WM2DesktopLayout |
312 NET::WM2FullPlacement |
313 NET::WM2FullscreenMonitors |
316 NET::ActionMove |
317 NET::ActionResize |
318 NET::ActionMinimize |
319 NET::ActionShade |
320 //NET::ActionStick | // Sticky state is not supported
321 NET::ActionMaxVert |
322 NET::ActionMaxHoriz |
323 NET::ActionFullScreen |
324 NET::ActionChangeDesktop |
325 NET::ActionClose |
330 QX11Info info;
331 rootInfo = new RootInfo( this, display(), supportWindow->winId(), "KWin", protocols, 5, info.screen() );
333 loadDesktopSettings();
334 updateDesktopLayout();
335 // Extra NETRootInfo instance in Client mode is needed to get the values of the properties
336 NETRootInfo client_info( display(), NET::ActiveWindow | NET::CurrentDesktop );
337 int initial_desktop;
338 if( !kapp->isSessionRestored() )
339 initial_desktop = client_info.currentDesktop();
340 else
342 KConfigGroup group( kapp->sessionConfig(), "Session" );
343 initial_desktop = group.readEntry( "desktop", 1 );
345 if( !setCurrentDesktop( initial_desktop ))
346 setCurrentDesktop( 1 );
348 // Now we know how many desktops we'll have, thus we initialize the positioning object
349 initPositioning = new Placement( this );
351 reconfigureTimer.setSingleShot( true );
352 updateToolWindowsTimer.setSingleShot( true );
354 connect( &reconfigureTimer, SIGNAL( timeout() ), this, SLOT( slotReconfigure() ));
355 connect( &updateToolWindowsTimer, SIGNAL( timeout() ), this, SLOT( slotUpdateToolWindows() ));
356 connect( &compositeTimer, SIGNAL( timeout() ), SLOT( performCompositing() ));
358 connect( KGlobalSettings::self(), SIGNAL( appearanceChanged() ), this, SLOT( reconfigure() ));
359 connect( KGlobalSettings::self(), SIGNAL( settingsChanged(int) ), this, SLOT( slotSettingsChanged(int) ));
360 connect( KGlobalSettings::self(), SIGNAL( blockShortcuts(int) ), this, SLOT( slotBlockShortcuts(int) ));
362 active_client = NULL;
363 rootInfo->setActiveWindow( None );
364 focusToNull();
365 if( !kapp->isSessionRestored() )
366 ++block_focus; // Because it will be set below
368 char nm[100];
369 sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( display()));
370 Atom topmenu_atom = XInternAtom( display(), nm, False );
371 topmenu_selection = new KSelectionOwner( topmenu_atom );
372 topmenu_watcher = new KSelectionWatcher( topmenu_atom );
373 //TODO: grabXServer(); // Where exactly put this? topmenu selection claiming down belong must be before
375 { // Begin updates blocker block
376 StackingUpdatesBlocker blocker( this );
378 if( options->topMenuEnabled() && topmenu_selection->claim( false ))
379 setupTopMenuHandling(); // This can call updateStackingOrder()
380 else
381 lostTopMenuSelection();
383 unsigned int i, nwins;
384 Window root_return, parent_return;
385 Window* wins;
386 XQueryTree( display(), rootWindow(), &root_return, &parent_return, &wins, &nwins );
387 for( i = 0; i < nwins; i++ )
389 XWindowAttributes attr;
390 XGetWindowAttributes( display(), wins[i], &attr );
391 if( attr.override_redirect )
393 createUnmanaged( wins[i] );
394 continue;
396 if( topmenu_space && topmenu_space->winId() == wins[i] )
397 continue;
398 if( attr.map_state != IsUnmapped )
399 createClient( wins[i], true );
401 if( wins )
402 XFree( (void*)( wins ));
404 // Propagate clients, will really happen at the end of the updates blocker block
405 updateStackingOrder( true );
407 updateClientArea();
409 // NETWM spec says we have to set it to (0,0) if we don't support it
410 NETPoint* viewports = new NETPoint[number_of_desktops];
411 rootInfo->setDesktopViewport( number_of_desktops, *viewports );
412 delete[] viewports;
413 QRect geom = Kephal::ScreenUtils::desktopGeometry();
414 NETSize desktop_geometry;
415 desktop_geometry.width = geom.width();
416 desktop_geometry.height = geom.height();
417 rootInfo->setDesktopGeometry( -1, desktop_geometry );
418 setShowingDesktop( false );
420 } // End updates blocker block
422 Client* new_active_client = NULL;
423 if( !kapp->isSessionRestored() )
425 --block_focus;
426 new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow() ));
428 if( new_active_client == NULL
429 && activeClient() == NULL && should_get_focus.count() == 0 )
430 { // No client activated in manage()
431 if( new_active_client == NULL )
432 new_active_client = topClientOnDesktop( currentDesktop(), -1 );
433 if( new_active_client == NULL && !desktops.isEmpty() )
434 new_active_client = findDesktop( true, currentDesktop() );
436 if( new_active_client != NULL )
437 activateClient( new_active_client );
438 // SELI TODO: This won't work with unreasonable focus policies,
439 // and maybe in rare cases also if the selected client doesn't
440 // want focus
441 workspaceInit = false;
443 // TODO: ungrabXServer()
446 Workspace::~Workspace()
448 finishCompositing();
449 blockStackingUpdates( true );
451 // TODO: grabXServer();
453 // Use stacking_order, so that kwin --replace keeps stacking order
454 for( ClientList::ConstIterator it = stacking_order.constBegin();
455 it != stacking_order.constEnd();
456 ++it )
458 // Only release the window
459 (*it)->releaseWindow( true );
460 // No removeClient() is called, it does more than just removing.
461 // However, remove from some lists to e.g. prevent performTransiencyCheck()
462 // from crashing.
463 clients.removeAll( *it );
464 desktops.removeAll( *it );
466 for( UnmanagedList::ConstIterator it = unmanaged.constBegin();
467 it != unmanaged.constEnd();
468 ++it )
469 (*it)->release();
470 delete tab_box;
471 delete popupinfo;
472 discardPopup();
473 XDeleteProperty( display(), rootWindow(), atoms->kwin_running );
475 writeWindowRules();
476 KGlobal::config()->sync();
478 delete rootInfo;
479 delete supportWindow;
480 delete mgr;
481 delete startup;
482 delete initPositioning;
483 delete topmenu_watcher;
484 delete topmenu_selection;
485 delete topmenu_space;
486 delete client_keys_dialog;
487 while( !rules.isEmpty() )
489 delete rules.front();
490 rules.pop_front();
492 foreach( SessionInfo* s, session )
493 delete s;
494 XDestroyWindow( display(), null_focus_window );
496 // TODO: ungrabXServer();
498 _self = 0;
501 Client* Workspace::createClient( Window w, bool is_mapped )
503 StackingUpdatesBlocker blocker( this );
504 Client* c = new Client( this );
505 if( !c->manage( w, is_mapped ))
507 Client::deleteClient( c, Allowed );
508 return NULL;
510 addClient( c, Allowed );
511 if( scene )
512 scene->windowAdded( c );
513 if( effects )
514 static_cast<EffectsHandlerImpl*>( effects )->windowAdded( c->effectWindow() );
515 return c;
518 Unmanaged* Workspace::createUnmanaged( Window w )
520 if( w == overlay )
521 return NULL;
522 Unmanaged* c = new Unmanaged( this );
523 if( !c->track( w ))
525 Unmanaged::deleteUnmanaged( c, Allowed );
526 return NULL;
528 addUnmanaged( c, Allowed );
529 if( scene )
530 scene->windowAdded( c );
531 if( effects )
532 static_cast<EffectsHandlerImpl*>( effects )->windowAdded( c->effectWindow() );
533 return c;
536 void Workspace::addClient( Client* c, allowed_t )
538 Group* grp = findGroup( c->window() );
539 if( grp != NULL )
540 grp->gotLeader( c );
542 if( c->isDesktop() )
544 desktops.append( c );
545 if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop() )
546 requestFocus( c ); // TODO: Make sure desktop is active after startup if there's no other window active
548 else
550 updateFocusChains( c, FocusChainUpdate ); // Add to focus chain if not already there
551 clients.append( c );
553 if( !unconstrained_stacking_order.contains( c ))
554 unconstrained_stacking_order.append( c ); // Raise if it hasn't got any stacking position yet
555 if( !stacking_order.contains( c )) // It'll be updated later, and updateToolWindows() requires
556 stacking_order.append( c ); // c to be in stacking_order
557 if( c->isTopMenu())
558 addTopMenu( c );
559 x_stacking_dirty = true;
560 updateClientArea(); // This cannot be in manage(), because the client got added only now
561 updateClientLayer( c );
562 if( c->isDesktop())
564 raiseClient( c );
565 // If there's no active client, make this desktop the active one
566 if( activeClient() == NULL && should_get_focus.count() == 0 )
567 activateClient( findDesktop( true, currentDesktop() ));
569 c->checkActiveModal();
570 checkTransients( c->window() ); // SELI TODO: Does this really belong here?
571 updateStackingOrder( true ); // Propagate new client
572 if( c->isUtility() || c->isMenu() || c->isToolbar() )
573 updateToolWindows( true );
574 checkNonExistentClients();
575 if( tab_grab )
576 tab_box->reset( true );
579 void Workspace::addUnmanaged( Unmanaged* c, allowed_t )
581 unmanaged.append( c );
582 x_stacking_dirty = true;
586 * Destroys the client \a c
588 void Workspace::removeClient( Client* c, allowed_t )
590 if (c == active_popup_client)
591 closeActivePopup();
593 if( client_keys_client == c )
594 setupWindowShortcutDone( false );
595 if( !c->shortcut().isEmpty() )
597 c->setShortcut( QString() ); // Remove from client_keys
598 clientShortcutUpdated( c ); // Needed, since this is otherwise delayed by setShortcut() and wouldn't run
601 if( c->isDialog())
602 Notify::raise( Notify::TransDelete );
603 if( c->isNormalWindow())
604 Notify::raise( Notify::Delete );
606 if( tab_grab && tab_box->currentClient() == c )
607 tab_box->nextPrev( true );
609 Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
610 clients.removeAll( c );
611 desktops.removeAll( c );
612 unconstrained_stacking_order.removeAll( c );
613 stacking_order.removeAll( c );
614 x_stacking_dirty = true;
615 for( int i = 1; i <= numberOfDesktops(); ++i )
616 focus_chain[i].removeAll( c );
617 global_focus_chain.removeAll( c );
618 attention_chain.removeAll( c );
619 showing_desktop_clients.removeAll( c );
620 if( c->isTopMenu() )
621 removeTopMenu( c );
622 Group* group = findGroup( c->window());
623 if( group != NULL )
624 group->lostLeader();
626 if( c == most_recently_raised )
627 most_recently_raised = 0;
628 should_get_focus.removeAll( c );
629 Q_ASSERT( c != active_client );
630 if( c == last_active_client )
631 last_active_client = 0;
632 if( c == pending_take_activity )
633 pending_take_activity = NULL;
634 if( c == delayfocus_client )
635 cancelDelayFocus();
637 updateStackingOrder( true );
639 if( tab_grab )
640 tab_box->reset( true );
642 updateClientArea();
645 void Workspace::removeUnmanaged( Unmanaged* c, allowed_t )
647 assert( unmanaged.contains( c ));
648 unmanaged.removeAll( c );
649 x_stacking_dirty = true;
652 void Workspace::addDeleted( Deleted* c, allowed_t )
654 assert( !deleted.contains( c ));
655 deleted.append( c );
656 x_stacking_dirty = true;
659 void Workspace::removeDeleted( Deleted* c, allowed_t )
661 assert( deleted.contains( c ));
662 if( scene )
663 scene->windowDeleted( c );
664 if( effects )
665 static_cast<EffectsHandlerImpl*>( effects )->windowDeleted( c->effectWindow() );
666 deleted.removeAll( c );
667 x_stacking_dirty = true;
670 void Workspace::updateFocusChains( Client* c, FocusChainChange change )
672 if( !c->wantsTabFocus() ) // Doesn't want tab focus, remove
674 for( int i = 1; i <= numberOfDesktops(); ++i )
675 focus_chain[i].removeAll( c );
676 global_focus_chain.removeAll( c );
677 return;
679 if( c->desktop() == NET::OnAllDesktops )
680 { // Now on all desktops, add it to focus_chains it is not already in
681 for( int i = 1; i <= numberOfDesktops(); i++)
682 { // Making first/last works only on current desktop, don't affect all desktops
683 if( i == currentDesktop()
684 && ( change == FocusChainMakeFirst || change == FocusChainMakeLast ))
686 focus_chain[i].removeAll( c );
687 if( change == FocusChainMakeFirst )
688 focus_chain[i].append( c );
689 else
690 focus_chain[i].prepend( c );
692 else if( !focus_chain[i].contains( c ))
693 { // Add it after the active one
694 if( active_client != NULL && active_client != c &&
695 !focus_chain[i].isEmpty() && focus_chain[i].last() == active_client )
696 focus_chain[i].insert( focus_chain[i].size() - 1, c );
697 else
698 focus_chain[i].append( c ); // Otherwise add as the first one
702 else // Now only on desktop, remove it anywhere else
704 for( int i = 1; i <= numberOfDesktops(); i++)
706 if( i == c->desktop() )
708 if( change == FocusChainMakeFirst )
710 focus_chain[i].removeAll( c );
711 focus_chain[i].append( c );
713 else if( change == FocusChainMakeLast )
715 focus_chain[i].removeAll( c );
716 focus_chain[i].prepend( c );
718 else if( !focus_chain[i].contains( c ))
719 { // Add it after the active one
720 if( active_client != NULL && active_client != c &&
721 !focus_chain[i].isEmpty() && focus_chain[i].last() == active_client )
722 focus_chain[i].insert( focus_chain[i].size() - 1, c );
723 else
724 focus_chain[i].append( c ); // Otherwise add as the first one
727 else
728 focus_chain[i].removeAll( c );
731 if( change == FocusChainMakeFirst )
733 global_focus_chain.removeAll( c );
734 global_focus_chain.append( c );
736 else if( change == FocusChainMakeLast )
738 global_focus_chain.removeAll( c );
739 global_focus_chain.prepend( c );
741 else if( !global_focus_chain.contains( c ))
742 { // Add it after the active one
743 if( active_client != NULL && active_client != c &&
744 !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client )
745 global_focus_chain.insert( global_focus_chain.size() - 1, c );
746 else
747 global_focus_chain.append( c ); // Otherwise add as the first one
751 void Workspace::updateCurrentTopMenu()
753 if( !managingTopMenus() )
754 return;
755 // toplevel menubar handling
756 Client* menubar = 0;
757 bool block_desktop_menubar = false;
758 if( active_client )
760 // Show the new menu bar first...
761 Client* menu_client = active_client;
762 for( ;; )
764 if( menu_client->isFullScreen() )
765 block_desktop_menubar = true;
766 for( ClientList::ConstIterator it = menu_client->transients().constBegin();
767 it != menu_client->transients().constEnd();
768 ++it )
769 if( (*it)->isTopMenu() )
771 menubar = *it;
772 break;
774 if( menubar != NULL || !menu_client->isTransient() )
775 break;
776 if( menu_client->isModal() || menu_client->transientFor() == NULL )
777 break; // Don't use mainwindow's menu if this is modal or group transient
778 menu_client = menu_client->transientFor();
780 if( !menubar )
781 { // Try to find any topmenu from the application (#72113)
782 for( ClientList::ConstIterator it = active_client->group()->members().constBegin();
783 it != active_client->group()->members().constEnd();
784 ++it )
785 if( (*it)->isTopMenu() )
787 menubar = *it;
788 break;
792 if( !menubar && !block_desktop_menubar && options->desktopTopMenu() )
794 // Find the menubar of the desktop
795 Client* desktop = findDesktop( true, currentDesktop() );
796 if( desktop != NULL )
798 for( ClientList::ConstIterator it = desktop->transients().constBegin();
799 it != desktop->transients().constEnd();
800 ++it )
801 if( (*it)->isTopMenu() )
803 menubar = *it;
804 break;
807 // TODO: To be cleaned app with window grouping
808 // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
809 // thus the topmenu is not transient for it :-/.
810 if( menubar == NULL )
812 for( ClientList::ConstIterator it = topmenus.constBegin();
813 it != topmenus.constEnd();
814 ++it )
815 // kdesktop's topmenu has WM_TRANSIENT_FOR set pointing to the root window
816 // to recognize it here. Also, with the xroot hack in kdesktop, there's
817 // no NET::Desktop window to be transient for.
818 if( (*it)->wasOriginallyGroupTransient() )
820 menubar = *it;
821 break;
826 //kDebug( 1212 ) << "CURRENT TOPMENU:" << menubar << ":" << active_client;
827 if( menubar )
829 if( active_client && !menubar->isOnDesktop( active_client->desktop() ))
830 menubar->setDesktop( active_client->desktop() );
831 menubar->hideClient( false );
832 topmenu_space->hide();
833 // Make it appear like it's been raised manually - it's in the Dock layer anyway,
834 // and not raising it could mess up stacking order of topmenus within one application,
835 // and thus break raising of mainclients in raiseClient()
836 unconstrained_stacking_order.removeAll( menubar );
837 unconstrained_stacking_order.append( menubar );
839 else if( !block_desktop_menubar )
840 { // No topmenu active - show the space window, so that there's not empty space
841 topmenu_space->show();
844 // ... Then hide the other ones. Avoids flickers.
845 for( ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it )
846 if( (*it)->isTopMenu() && (*it) != menubar )
847 (*it)->hideClient( true );
851 void Workspace::updateToolWindows( bool also_hide )
853 // TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
854 if( !options->hideUtilityWindowsForInactive )
856 for( ClientList::ConstIterator it = clients.constBegin();
857 it != clients.constEnd();
858 ++it )
859 (*it)->hideClient( false );
860 return;
862 const Group* group = NULL;
863 const Client* client = active_client;
864 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
865 // will be shown; if a group transient is group, all tools in the group will be shown
866 while( client != NULL )
868 if( !client->isTransient())
869 break;
870 if( client->groupTransient())
872 group = client->group();
873 break;
875 client = client->transientFor();
877 // Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
878 // I.e. if it's not up to date
880 // SELI TODO: But maybe it should - what if a new client has been added that's not in stacking order yet?
881 ClientList to_show, to_hide;
882 for( ClientList::ConstIterator it = stacking_order.constBegin();
883 it != stacking_order.constEnd();
884 ++it )
886 if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar() )
888 bool show = true;
889 if( !(*it)->isTransient() )
891 if( (*it)->group()->members().count() == 1 ) // Has its own group, keep always visible
892 show = true;
893 else if( client != NULL && (*it)->group() == client->group() )
894 show = true;
895 else
896 show = false;
898 else
900 if( group != NULL && (*it)->group() == group )
901 show = true;
902 else if( client != NULL && client->hasTransient( (*it), true ))
903 show = true;
904 else
905 show = false;
907 if( !show && also_hide )
909 const ClientList mainclients = (*it)->mainClients();
910 // Don't hide utility windows which are standalone(?) or
911 // have e.g. kicker as mainwindow
912 if( mainclients.isEmpty())
913 show = true;
914 for( ClientList::ConstIterator it2 = mainclients.constBegin();
915 it2 != mainclients.constEnd();
916 ++it2 )
918 if( (*it2)->isSpecialWindow() )
919 show = true;
921 if( !show )
922 to_hide.append( *it );
924 if( show )
925 to_show.append( *it );
927 } // First show new ones, then hide
928 for( int i = to_show.size() - 1;
929 i >= 0;
930 --i ) // From topmost
931 // TODO: Since this is in stacking order, the order of taskbar entries changes :(
932 to_show.at( i )->hideClient( false );
933 if( also_hide )
935 for( ClientList::ConstIterator it = to_hide.constBegin();
936 it != to_hide.constEnd();
937 ++it ) // From bottommost
938 (*it)->hideClient( true );
939 updateToolWindowsTimer.stop();
941 else // setActiveClient() is after called with NULL client, quickly followed
942 // by setting a new client, which would result in flickering
943 updateToolWindowsTimer.start( 50 );
946 void Workspace::slotUpdateToolWindows()
948 updateToolWindows( true );
952 * Updates the current colormap according to the currently active client
954 void Workspace::updateColormap()
956 Colormap cmap = default_colormap;
957 if( activeClient() && activeClient()->colormap() != None )
958 cmap = activeClient()->colormap();
959 if( cmap != installed_colormap )
961 XInstallColormap( display(), cmap );
962 installed_colormap = cmap;
966 void Workspace::slotReloadConfig()
968 reconfigure();
971 void Workspace::reconfigure()
973 reconfigureTimer.start( 200 );
977 * This D-Bus call is used by the compositing kcm. Since the reconfigure()
978 * D-Bus call delays the actual reconfiguring, it is not possible to immediately
979 * call compositingActive(). Therefore the kcm will instead call this to ensure
980 * the reconfiguring has already happened.
982 bool Workspace::waitForCompositingSetup()
984 if( !reconfigureTimer.isActive() )
985 return false;
986 reconfigureTimer.stop();
987 slotReconfigure();
988 return compositingActive();
991 void Workspace::slotSettingsChanged( int category )
993 kDebug( 1212 ) << "Workspace::slotSettingsChanged()";
994 if( category == KGlobalSettings::SETTINGS_SHORTCUTS )
995 readShortcuts();
999 * Reread settings
1001 KWIN_PROCEDURE( CheckBorderSizesProcedure, Client, cl->checkBorderSizes( true ));
1003 void Workspace::slotReconfigure()
1005 kDebug( 1212 ) << "Workspace::slotReconfigure()";
1006 reconfigureTimer.stop();
1008 if( options->electricBorders() == Options::ElectricAlways )
1009 reserveElectricBorderSwitching( false );
1011 KGlobal::config()->reparseConfiguration();
1012 unsigned long changed = options->updateSettings();
1014 tab_box->reconfigure();
1015 popupinfo->reconfigure();
1016 initPositioning->reinitCascading( 0 );
1017 readShortcuts();
1018 forEachClient( CheckIgnoreFocusStealingProcedure());
1019 updateToolWindows( true );
1021 if( mgr->reset( changed ))
1022 { // Decorations need to be recreated
1024 // This actually seems to make things worse now
1025 //QWidget curtain;
1026 //curtain.setBackgroundMode( NoBackground );
1027 //curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
1028 //curtain.show();
1030 for( ClientList::ConstIterator it = clients.constBegin();
1031 it != clients.constEnd();
1032 ++it )
1033 (*it)->updateDecoration( true, true );
1034 mgr->destroyPreviousPlugin();
1036 else
1038 forEachClient( CheckBorderSizesProcedure() );
1039 foreach( Client* c, clients )
1040 c->repaintDecoration();
1043 if( options->electricBorders() == Options::ElectricAlways )
1044 reserveElectricBorderSwitching( true );
1045 updateElectricBorders();
1047 if( options->topMenuEnabled() && !managingTopMenus() )
1049 if( topmenu_selection->claim( false ))
1050 setupTopMenuHandling();
1051 else
1052 lostTopMenuSelection();
1054 else if( !options->topMenuEnabled() && managingTopMenus() )
1056 topmenu_selection->release();
1057 lostTopMenuSelection();
1059 topmenu_height = 0; // Invalidate used menu height
1060 if( managingTopMenus() )
1062 updateTopMenuGeometry();
1063 updateCurrentTopMenu();
1066 if( options->useCompositing && !compositingSuspended )
1068 setupCompositing();
1069 if( effects ) // setupCompositing() may fail
1070 effects->reconfigure();
1071 addRepaintFull();
1073 else
1074 finishCompositing();
1076 loadWindowRules();
1077 for( ClientList::Iterator it = clients.begin();
1078 it != clients.end();
1079 ++it )
1081 (*it)->setupWindowRules( true );
1082 (*it)->applyWindowRules();
1083 discardUsedWindowRules( *it, false );
1087 void Workspace::slotReinitCompositing()
1089 // Reparse config. Config options will be reloaded by setupCompositing()
1090 KGlobal::config()->reparseConfiguration();
1091 options->updateSettings();
1093 // Update any settings that can be set in the compositing kcm.
1094 updateElectricBorders();
1096 // Restart compositing
1097 finishCompositing();
1098 setupCompositing();
1099 if( effects ) // setupCompositing() may fail
1100 effects->reconfigure();
1103 void Workspace::loadDesktopSettings()
1105 KSharedConfig::Ptr c = KGlobal::config();
1106 QString groupname;
1107 if( screen_number == 0 )
1108 groupname = "Desktops";
1109 else
1110 groupname.sprintf( "Desktops-screen-%d", screen_number );
1111 KConfigGroup group( c, groupname );
1113 int n = group.readEntry( "Number", 4 );
1114 number_of_desktops = n;
1115 workarea.clear();
1116 workarea.resize( n + 1 );
1117 screenarea.clear();
1118 rootInfo->setNumberOfDesktops( number_of_desktops );
1119 desktop_focus_chain.resize( n );
1120 // Make it +1, so that it can be accessed as [1..numberofdesktops]
1121 focus_chain.resize( n + 1 );
1122 for( int i = 1; i <= n; i++ )
1124 QString s = group.readEntry( QString( "Name_%1" ).arg( i ), i18n( "Desktop %1", i ));
1125 rootInfo->setDesktopName( i, s.toUtf8().data() );
1126 desktop_focus_chain[i-1] = i;
1130 void Workspace::saveDesktopSettings()
1132 KSharedConfig::Ptr c = KGlobal::config();
1133 QString groupname;
1134 if (screen_number == 0)
1135 groupname = "Desktops";
1136 else
1137 groupname.sprintf( "Desktops-screen-%d", screen_number );
1138 KConfigGroup group( c, groupname );
1140 group.writeEntry( "Number", number_of_desktops );
1141 for( int i = 1; i <= number_of_desktops; i++ )
1143 QString s = desktopName( i );
1144 QString defaultvalue = i18n( "Desktop %1", i );
1145 if( s.isEmpty() )
1147 s = defaultvalue;
1148 rootInfo->setDesktopName( i, s.toUtf8().data() );
1151 if( s != defaultvalue )
1153 group.writeEntry( QString( "Name_%1" ).arg( i ), s );
1155 else
1157 QString currentvalue = group.readEntry( QString( "Name_%1" ).arg( i ), QString() );
1158 if( currentvalue != defaultvalue )
1159 group.writeEntry( QString( "Name_%1" ).arg( i ), "" );
1163 // Save to disk
1164 group.sync();
1167 QStringList Workspace::configModules( bool controlCenter )
1169 QStringList args;
1170 args << "kwindecoration";
1171 if( controlCenter )
1172 args << "kwinoptions";
1173 else if( KAuthorized::authorizeControlModule( "kde-kwinoptions.desktop" ))
1174 args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced"
1175 << "kwinrules" << "kwincompositing";
1176 return args;
1179 void Workspace::configureWM()
1181 QStringList args;
1182 args << "--icon" << "preferences-system-windows" << configModules( false );
1183 KToolInvocation::kdeinitExec( "kcmshell4", args );
1187 * Avoids managing a window with title \a title
1189 void Workspace::doNotManage( const QString& title )
1191 doNotManageList.append( title );
1195 * Hack for java applets
1197 bool Workspace::isNotManaged( const QString& title )
1199 for( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it )
1201 QRegExp r( (*it) );
1202 if( r.indexIn(title) != -1 )
1204 doNotManageList.erase( it );
1205 return true;
1208 return false;
1212 * Refreshes all the client windows
1214 void Workspace::refresh()
1216 QWidget w( NULL, Qt::X11BypassWindowManagerHint );
1217 w.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
1218 w.show();
1219 w.hide();
1220 QApplication::flush();
1224 * During virt. desktop switching, desktop areas covered by windows that are
1225 * going to be hidden are first obscured by new windows with no background
1226 * ( i.e. transparent ) placed right below the windows. These invisible windows
1227 * are removed after the switch is complete.
1228 * Reduces desktop ( wallpaper ) repaints during desktop switching
1230 class ObscuringWindows
1232 public:
1233 ~ObscuringWindows();
1234 void create( Client* c );
1235 private:
1236 QList<Window> obscuring_windows;
1237 static QList<Window>* cached;
1238 static unsigned int max_cache_size;
1241 QList<Window>* ObscuringWindows::cached = 0;
1242 unsigned int ObscuringWindows::max_cache_size = 0;
1244 void ObscuringWindows::create( Client* c )
1246 if( compositing() )
1247 return; // Not needed with compositing
1248 if( cached == 0 )
1249 cached = new QList<Window>;
1250 Window obs_win;
1251 XWindowChanges chngs;
1252 int mask = CWSibling | CWStackMode;
1253 if( cached->count() > 0 )
1255 cached->removeAll( obs_win = cached->first() );
1256 chngs.x = c->x();
1257 chngs.y = c->y();
1258 chngs.width = c->width();
1259 chngs.height = c->height();
1260 mask |= CWX | CWY | CWWidth | CWHeight;
1262 else
1264 XSetWindowAttributes a;
1265 a.background_pixmap = None;
1266 a.override_redirect = True;
1267 obs_win = XCreateWindow( display(), rootWindow(), c->x(), c->y(),
1268 c->width(), c->height(), 0, CopyFromParent, InputOutput,
1269 CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
1271 chngs.sibling = c->frameId();
1272 chngs.stack_mode = Below;
1273 XConfigureWindow( display(), obs_win, mask, &chngs );
1274 XMapWindow( display(), obs_win );
1275 obscuring_windows.append( obs_win );
1278 ObscuringWindows::~ObscuringWindows()
1280 max_cache_size = qMax( int( max_cache_size ), obscuring_windows.count() + 4 ) - 1;
1281 for( QList<Window>::ConstIterator it = obscuring_windows.constBegin();
1282 it != obscuring_windows.constEnd();
1283 ++it )
1285 XUnmapWindow( display(), *it );
1286 if( cached->count() < int( max_cache_size ))
1287 cached->prepend( *it );
1288 else
1289 XDestroyWindow( display(), *it );
1294 * Sets the current desktop to \a new_desktop
1296 * Shows/Hides windows according to the stacking order and finally
1297 * propages the new desktop to the world
1299 bool Workspace::setCurrentDesktop( int new_desktop )
1301 if( new_desktop < 1 || new_desktop > number_of_desktops )
1302 return false;
1304 closeActivePopup();
1305 ++block_focus;
1306 // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
1307 StackingUpdatesBlocker blocker( this );
1309 int old_desktop = current_desktop;
1310 if (new_desktop != current_desktop )
1312 ++block_showing_desktop;
1313 // Optimized Desktop switching: unmapping done from back to front
1314 // mapping done from front to back => less exposure events
1315 Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
1317 ObscuringWindows obs_wins;
1319 current_desktop = new_desktop; // Change the desktop (so that Client::updateVisibility() works)
1321 for( ClientList::ConstIterator it = stacking_order.constBegin();
1322 it != stacking_order.constEnd();
1323 ++it )
1324 if( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
1326 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop ))
1327 obs_wins.create( *it );
1328 (*it)->updateVisibility();
1331 // Now propagate the change, after hiding, before showing
1332 rootInfo->setCurrentDesktop( current_desktop );
1334 if( movingClient && !movingClient->isOnDesktop( new_desktop ))
1335 movingClient->setDesktop( new_desktop );
1337 for( int i = stacking_order.size() - 1; i >= 0 ; --i )
1338 if( stacking_order.at( i )->isOnDesktop( new_desktop ))
1339 stacking_order.at( i )->updateVisibility();
1341 --block_showing_desktop;
1342 if( showingDesktop() ) // Do this only after desktop change to avoid flicker
1343 resetShowingDesktop( false );
1346 // Restore the focus on this desktop
1347 --block_focus;
1348 Client* c = 0;
1350 if( options->focusPolicyIsReasonable() )
1351 { // Search in focus chain
1352 if( movingClient != NULL && active_client == movingClient &&
1353 focus_chain[currentDesktop()].contains( active_client ) &&
1354 active_client->isShown( true ) && active_client->isOnCurrentDesktop())
1355 c = active_client; // The requestFocus below will fail, as the client is already active
1356 if( !c )
1358 for( int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i )
1360 if( focus_chain[currentDesktop()].at( i )->isShown( false ) &&
1361 focus_chain[currentDesktop()].at( i )->isOnCurrentDesktop() )
1363 c = focus_chain[currentDesktop()].at( i );
1364 break;
1369 // If "unreasonable focus policy" and active_client is on_all_desktops and
1370 // under mouse (Hence == old_active_client), conserve focus.
1371 // (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1372 else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop() )
1373 c = active_client;
1375 if( c == NULL && !desktops.isEmpty() )
1376 c = findDesktop( true, currentDesktop() );
1378 if( c != active_client )
1379 setActiveClient( NULL, Allowed );
1381 if ( c )
1382 requestFocus( c );
1383 else if( !desktops.isEmpty() )
1384 requestFocus( findDesktop( true, currentDesktop() ));
1385 else
1386 focusToNull();
1388 updateCurrentTopMenu();
1390 // Update focus chain:
1391 // If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3,
1392 // Output: chain = { 3, 1, 2, 4 }.
1393 //kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
1394 // .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
1395 for( int i = desktop_focus_chain.indexOf( currentDesktop() ); i > 0; i-- )
1396 desktop_focus_chain[i] = desktop_focus_chain[i-1];
1397 desktop_focus_chain[0] = currentDesktop();
1399 //QString s = "desktop_focus_chain[] = { ";
1400 //for( uint i = 0; i < desktop_focus_chain.size(); i++ )
1401 // s += QString::number( desktop_focus_chain[i] ) + ", ";
1402 //kDebug( 1212 ) << s << "}\n";
1404 if( old_desktop != 0 ) // Not for the very first time
1405 popupinfo->showInfo( desktopName( currentDesktop() ));
1407 if( effects != NULL && old_desktop != 0 && old_desktop != new_desktop )
1408 static_cast<EffectsHandlerImpl*>( effects )->desktopChanged( old_desktop );
1409 if( compositing())
1410 addRepaintFull();
1412 return true;
1416 * Called only from D-Bus
1418 void Workspace::nextDesktop()
1420 int desktop = currentDesktop() + 1;
1421 setCurrentDesktop( desktop > numberOfDesktops() ? 1 : desktop );
1425 * Called only from D-Bus
1427 void Workspace::previousDesktop()
1429 int desktop = currentDesktop() - 1;
1430 setCurrentDesktop( desktop > 0 ? desktop : numberOfDesktops() );
1433 int Workspace::desktopToRight( int desktop, bool wrap ) const
1435 int x,y;
1436 Qt::Orientation orientation;
1437 calcDesktopLayout( &x, &y, &orientation );
1438 int dt = desktop - 1;
1439 if( orientation == Qt::Vertical )
1441 dt += y;
1442 if( dt >= numberOfDesktops() )
1444 if( wrap )
1445 dt -= numberOfDesktops();
1446 else
1447 return desktop;
1450 else
1452 int d = ( dt % x ) + 1;
1453 if( d >= x )
1455 if( wrap )
1456 d -= x;
1457 else
1458 return desktop;
1460 dt = dt - ( dt % x ) + d;
1462 return dt + 1;
1465 int Workspace::desktopToLeft( int desktop, bool wrap ) const
1467 int x,y;
1468 Qt::Orientation orientation;
1469 calcDesktopLayout( &x, &y, &orientation );
1470 int dt = desktop - 1;
1471 if( orientation == Qt::Vertical )
1473 dt -= y;
1474 if( dt < 0 )
1476 if( wrap )
1477 dt += numberOfDesktops();
1478 else
1479 return desktop;
1482 else
1484 int d = ( dt % x ) - 1;
1485 if( d < 0 )
1487 if( wrap )
1488 d += x;
1489 else
1490 return desktop;
1492 dt = dt - ( dt % x ) + d;
1494 return dt + 1;
1497 int Workspace::desktopUp( int desktop, bool wrap ) const
1499 int x,y;
1500 Qt::Orientation orientation;
1501 calcDesktopLayout( &x, &y, &orientation);
1502 int dt = desktop - 1;
1503 if( orientation == Qt::Horizontal )
1505 dt -= x;
1506 if( dt < 0 )
1508 if( wrap )
1509 dt += numberOfDesktops();
1510 else
1511 return desktop;
1514 else
1516 int d = ( dt % y ) - 1;
1517 if( d < 0 )
1519 if( wrap )
1520 d += y;
1521 else
1522 return desktop;
1524 dt = dt - ( dt % y ) + d;
1526 return dt + 1;
1529 int Workspace::desktopDown( int desktop, bool wrap ) const
1531 int x,y;
1532 Qt::Orientation orientation;
1533 calcDesktopLayout( &x, &y, &orientation);
1534 int dt = desktop - 1;
1535 if( orientation == Qt::Horizontal )
1537 dt += x;
1538 if( dt >= numberOfDesktops() )
1540 if( wrap )
1541 dt -= numberOfDesktops();
1542 else
1543 return desktop;
1546 else
1548 int d = ( dt % y ) + 1;
1549 if( d >= y )
1551 if( wrap )
1552 d -= y;
1553 else
1554 return desktop;
1556 dt = dt - ( dt % y ) + d;
1558 return dt + 1;
1562 * Sets the number of virtual desktops to \a n
1564 void Workspace::setNumberOfDesktops( int n )
1566 if( n == number_of_desktops )
1567 return;
1568 int old_number_of_desktops = number_of_desktops;
1569 number_of_desktops = n;
1571 if( currentDesktop() > numberOfDesktops() )
1572 setCurrentDesktop( numberOfDesktops() );
1574 // If increasing the number, do the resizing now, otherwise
1575 // after the moving of windows to still existing desktops
1576 if( old_number_of_desktops < number_of_desktops )
1578 rootInfo->setNumberOfDesktops( number_of_desktops );
1579 NETPoint* viewports = new NETPoint[number_of_desktops];
1580 rootInfo->setDesktopViewport( number_of_desktops, *viewports );
1581 delete[] viewports;
1582 updateClientArea( true );
1583 focus_chain.resize( number_of_desktops + 1 );
1586 // If the number of desktops decreased, move all windows
1587 // that would be hidden to the last visible desktop
1588 if( old_number_of_desktops > number_of_desktops )
1590 for( ClientList::ConstIterator it = clients.constBegin();
1591 it != clients.constEnd();
1592 ++it)
1593 if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops() )
1594 sendClientToDesktop( *it, numberOfDesktops(), true );
1596 if( old_number_of_desktops > number_of_desktops )
1598 rootInfo->setNumberOfDesktops( number_of_desktops );
1599 NETPoint* viewports = new NETPoint[number_of_desktops];
1600 rootInfo->setDesktopViewport( number_of_desktops, *viewports );
1601 delete[] viewports;
1602 updateClientArea( true );
1603 focus_chain.resize( number_of_desktops + 1 );
1606 saveDesktopSettings();
1608 // Resize and reset the desktop focus chain.
1609 desktop_focus_chain.resize( n );
1610 for( int i = 0; i < int( desktop_focus_chain.size() ); i++ )
1611 desktop_focus_chain[i] = i+1;
1615 * Sends client \a c to desktop \a desk.
1617 * Takes care of transients as well.
1619 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
1621 bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
1622 c->setDesktop( desk );
1623 if( c->desktop() != desk ) // No change or desktop forced
1624 return;
1625 desk = c->desktop(); // Client did range checking
1627 if( c->isOnDesktop( currentDesktop() ))
1629 if( c->wantsTabFocus() && options->focusPolicyIsReasonable() &&
1630 !was_on_desktop && // for stickyness changes
1631 !dont_activate )
1632 requestFocus( c );
1633 else
1634 restackClientUnderActive( c );
1636 else
1637 raiseClient( c );
1639 ClientList transients_stacking_order = ensureStackingOrder( c->transients() );
1640 for( ClientList::ConstIterator it = transients_stacking_order.constBegin();
1641 it != transients_stacking_order.constEnd();
1642 ++it )
1643 sendClientToDesktop( *it, desk, dont_activate );
1644 updateClientArea();
1647 int Workspace::numScreens() const
1649 if( !options->xineramaEnabled )
1650 return 1;
1651 return Kephal::ScreenUtils::numScreens();
1654 int Workspace::activeScreen() const
1656 if( !options->xineramaEnabled )
1657 return 0;
1658 if( !options->activeMouseScreen )
1660 if( activeClient() != NULL && !activeClient()->isOnScreen( active_screen ))
1661 return activeClient()->screen();
1662 return active_screen;
1664 return Kephal::ScreenUtils::screenId( cursorPos());
1668 * Check whether a client moved completely out of what's considered the active screen,
1669 * if yes, set a new active screen.
1671 void Workspace::checkActiveScreen( const Client* c )
1673 if( !options->xineramaEnabled )
1674 return;
1675 if( !c->isActive())
1676 return;
1677 if( !c->isOnScreen( active_screen ))
1678 active_screen = c->screen();
1682 * Called e.g. when a user clicks on a window, set active screen to be the screen
1683 * where the click occurred
1685 void Workspace::setActiveScreenMouse( const QPoint& mousepos )
1687 if( !options->xineramaEnabled )
1688 return;
1689 active_screen = Kephal::ScreenUtils::screenId( mousepos );
1692 QRect Workspace::screenGeometry( int screen ) const
1694 if( !options->xineramaEnabled )
1695 return Kephal::ScreenUtils::desktopGeometry();
1696 return Kephal::ScreenUtils::screenGeometry( screen );
1699 int Workspace::screenNumber( const QPoint& pos ) const
1701 if( !options->xineramaEnabled )
1702 return 0;
1703 return Kephal::ScreenUtils::screenId( pos );
1706 void Workspace::sendClientToScreen( Client* c, int screen )
1708 if( c->screen() == screen ) // Don't use isOnScreen(), that's true even when only partially
1709 return;
1710 GeometryUpdatesBlocker blocker( c );
1711 QRect old_sarea = clientArea( MaximizeArea, c );
1712 QRect sarea = clientArea( MaximizeArea, screen, c->desktop() );
1713 c->setGeometry( sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(),
1714 c->size().width(), c->size().height() );
1715 c->checkWorkspacePosition();
1716 ClientList transients_stacking_order = ensureStackingOrder( c->transients() );
1717 for( ClientList::ConstIterator it = transients_stacking_order.constBegin();
1718 it != transients_stacking_order.constEnd();
1719 ++it )
1720 sendClientToScreen( *it, screen );
1721 if( c->isActive() )
1722 active_screen = screen;
1725 void Workspace::updateDesktopLayout()
1727 //rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to
1728 layoutOrientation = ( rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal
1729 ? Qt::Horizontal : Qt::Vertical );
1730 layoutX = rootInfo->desktopLayoutColumnsRows().width();
1731 layoutY = rootInfo->desktopLayoutColumnsRows().height();
1732 if( layoutX == 0 && layoutY == 0 ) // Not given, set default layout
1733 layoutY = 2;
1736 void Workspace::calcDesktopLayout( int* xp, int* yp, Qt::Orientation* orientation ) const
1738 int x = layoutX; // <= 0 means compute it from the other and total number of desktops
1739 int y = layoutY;
1740 if(( x <= 0 ) && ( y > 0 ))
1741 x = ( numberOfDesktops() + y - 1 ) / y;
1742 else if(( y <= 0) && ( x > 0 ))
1743 y = ( numberOfDesktops() + x - 1 ) / x;
1745 if( x <= 0 )
1746 x = 1;
1747 if( y <= 0 )
1748 y = 1;
1749 *xp = x;
1750 *yp = y;
1751 *orientation = layoutOrientation;
1754 void Workspace::killWindowId( Window window_to_kill )
1756 if( window_to_kill == None )
1757 return;
1758 Window window = window_to_kill;
1759 Client* client = NULL;
1760 for( ;; )
1762 client = findClient( FrameIdMatchPredicate( window ));
1763 if( client != NULL )
1764 break; // Found the client
1765 Window parent, root;
1766 Window* children;
1767 unsigned int children_count;
1768 XQueryTree( display(), window, &root, &parent, &children, &children_count );
1769 if( children != NULL )
1770 XFree( children );
1771 if( window == root ) // We didn't find the client, probably an override-redirect window
1772 break;
1773 window = parent; // Go up
1775 if( client != NULL )
1776 client->killWindow();
1777 else
1778 XKillClient( display(), window_to_kill );
1781 void Workspace::sendPingToWindow( Window window, Time timestamp )
1783 rootInfo->sendPing( window, timestamp );
1786 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags )
1788 rootInfo->takeActivity( c->window(), timestamp, flags );
1789 pending_take_activity = c;
1793 * Takes a screenshot of the current window and puts it in the clipboard.
1795 void Workspace::slotGrabWindow()
1797 if ( active_client )
1799 QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() );
1801 // No XShape - no work.
1802 if( Extensions::shapeAvailable() )
1804 // As the first step, get the mask from XShape.
1805 int count, order;
1806 XRectangle* rects = XShapeGetRectangles( display(), active_client->frameId(),
1807 ShapeBounding, &count, &order);
1808 // The ShapeBounding region is the outermost shape of the window;
1809 // ShapeBounding - ShapeClipping is defined to be the border.
1810 // Since the border area is part of the window, we use bounding
1811 // to limit our work region
1812 if( rects )
1814 // Create a QRegion from the rectangles describing the bounding mask.
1815 QRegion contents;
1816 for( int pos = 0; pos < count; pos++ )
1817 contents += QRegion( rects[pos].x, rects[pos].y,
1818 rects[pos].width, rects[pos].height);
1819 XFree( rects );
1821 // Create the bounding box.
1822 QRegion bbox( 0, 0, snapshot.width(), snapshot.height() );
1824 // Get the masked away area.
1825 QRegion maskedAway = bbox - contents;
1826 QVector<QRect> maskedAwayRects = maskedAway.rects();
1828 // Construct a bitmap mask from the rectangles
1829 QBitmap mask( snapshot.width(), snapshot.height() );
1830 QPainter p( &mask );
1831 p.fillRect( 0, 0, mask.width(), mask.height(), Qt::color1 );
1832 for( int pos = 0; pos < maskedAwayRects.count(); pos++ )
1833 p.fillRect( maskedAwayRects[pos], Qt::color0 );
1834 p.end();
1835 snapshot.setMask( mask );
1839 QClipboard* cb = QApplication::clipboard();
1840 cb->setPixmap( snapshot );
1842 else
1843 slotGrabDesktop();
1847 * Takes a screenshot of the whole desktop and puts it in the clipboard.
1849 void Workspace::slotGrabDesktop()
1851 QPixmap p = QPixmap::grabWindow( rootWindow() );
1852 QClipboard* cb = QApplication::clipboard();
1853 cb->setPixmap( p );
1857 * Invokes keyboard mouse emulation
1859 void Workspace::slotMouseEmulation()
1861 if( mouse_emulation )
1863 ungrabXKeyboard();
1864 mouse_emulation = false;
1865 return;
1868 if( grabXKeyboard() )
1870 mouse_emulation = true;
1871 mouse_emulation_state = 0;
1872 mouse_emulation_window = 0;
1877 * Returns the child window under the mouse and activates the
1878 * respective client if necessary.
1880 * Auxiliary function for the mouse emulation system.
1882 WId Workspace::getMouseEmulationWindow()
1884 Window root;
1885 Window child = rootWindow();
1886 int root_x, root_y, lx, ly;
1887 uint state;
1888 Window w;
1889 Client * c = 0;
1892 w = child;
1893 if( !c )
1894 c = findClient( FrameIdMatchPredicate( w ));
1895 XQueryPointer( display(), w, &root, &child, &root_x, &root_y, &lx, &ly, &state );
1896 } while ( child != None && child != w );
1898 if( c && !c->isActive() )
1899 activateClient( c );
1900 return WId( w );
1904 * Sends a faked mouse event to the specified window. Returns the new button state.
1906 unsigned int Workspace::sendFakedMouseEvent( const QPoint& pos, WId w, MouseEmulation type,
1907 int button, unsigned int state )
1909 if ( !w )
1910 return state;
1911 QWidget* widget = QWidget::find( w );
1912 if(( !widget || qobject_cast<QToolButton*>( widget )) && !findClient( WindowMatchPredicate( w )))
1914 int x, y;
1915 Window xw;
1916 XTranslateCoordinates( display(), rootWindow(), w, pos.x(), pos.y(), &x, &y, &xw );
1917 if( type == EmuMove )
1918 { // Motion notify events
1919 XEvent e;
1920 e.type = MotionNotify;
1921 e.xmotion.window = w;
1922 e.xmotion.root = rootWindow();
1923 e.xmotion.subwindow = w;
1924 e.xmotion.time = xTime();
1925 e.xmotion.x = x;
1926 e.xmotion.y = y;
1927 e.xmotion.x_root = pos.x();
1928 e.xmotion.y_root = pos.y();
1929 e.xmotion.state = state;
1930 e.xmotion.is_hint = NotifyNormal;
1931 XSendEvent( display(), w, true, ButtonMotionMask, &e );
1933 else
1935 XEvent e;
1936 e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
1937 e.xbutton.window = w;
1938 e.xbutton.root = rootWindow();
1939 e.xbutton.subwindow = w;
1940 e.xbutton.time = xTime();
1941 e.xbutton.x = x;
1942 e.xbutton.y = y;
1943 e.xbutton.x_root = pos.x();
1944 e.xbutton.y_root = pos.y();
1945 e.xbutton.state = state;
1946 e.xbutton.button = button;
1947 XSendEvent( display(), w, true, ButtonPressMask, &e );
1949 if( type == EmuPress )
1951 switch( button )
1953 case 2:
1954 state |= Button2Mask;
1955 break;
1956 case 3:
1957 state |= Button3Mask;
1958 break;
1959 default: // 1
1960 state |= Button1Mask;
1961 break;
1964 else
1966 switch( button )
1968 case 2:
1969 state &= ~Button2Mask;
1970 break;
1971 case 3:
1972 state &= ~Button3Mask;
1973 break;
1974 default: // 1
1975 state &= ~Button1Mask;
1976 break;
1982 return state;
1986 * Handles keypress event during mouse emulation
1988 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
1990 int kc = XKeycodeToKeysym( display(), ev.keycode, 0 );
1991 int km = ev.state & ( ControlMask | Mod1Mask | ShiftMask );
1993 bool is_control = km & ControlMask;
1994 bool is_alt = km & Mod1Mask;
1995 bool is_shift = km & ShiftMask;
1996 int delta = is_control ? 1 : ( is_alt ? 32 : 8 );
1997 QPoint pos = cursorPos();
1999 switch( kc )
2001 case XK_Left:
2002 case XK_KP_Left:
2003 pos.rx() -= delta;
2004 break;
2005 case XK_Right:
2006 case XK_KP_Right:
2007 pos.rx() += delta;
2008 break;
2009 case XK_Up:
2010 case XK_KP_Up:
2011 pos.ry() -= delta;
2012 break;
2013 case XK_Down:
2014 case XK_KP_Down:
2015 pos.ry() += delta;
2016 break;
2017 case XK_F1:
2018 if( !mouse_emulation_state )
2019 mouse_emulation_window = getMouseEmulationWindow();
2020 if(( mouse_emulation_state & Button1Mask ) == 0 )
2021 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2022 EmuPress, Button1, mouse_emulation_state );
2023 if( !is_shift )
2024 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2025 EmuRelease, Button1, mouse_emulation_state );
2026 break;
2027 case XK_F2:
2028 if( !mouse_emulation_state )
2029 mouse_emulation_window = getMouseEmulationWindow();
2030 if(( mouse_emulation_state & Button2Mask ) == 0 )
2031 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2032 EmuPress, Button2, mouse_emulation_state );
2033 if( !is_shift )
2034 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2035 EmuRelease, Button2, mouse_emulation_state );
2036 break;
2037 case XK_F3:
2038 if( !mouse_emulation_state )
2039 mouse_emulation_window = getMouseEmulationWindow();
2040 if(( mouse_emulation_state & Button3Mask ) == 0 )
2041 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2042 EmuPress, Button3, mouse_emulation_state );
2043 if( !is_shift )
2044 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2045 EmuRelease, Button3, mouse_emulation_state );
2046 break;
2047 case XK_Return:
2048 case XK_space:
2049 case XK_KP_Enter:
2050 case XK_KP_Space:
2052 if( !mouse_emulation_state )
2053 { // Nothing was pressed, fake a LMB click
2054 mouse_emulation_window = getMouseEmulationWindow();
2055 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2056 EmuPress, Button1, mouse_emulation_state );
2057 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2058 EmuRelease, Button1, mouse_emulation_state );
2060 else
2061 { // Release all
2062 if( mouse_emulation_state & Button1Mask )
2063 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2064 EmuRelease, Button1, mouse_emulation_state );
2065 if( mouse_emulation_state & Button2Mask )
2066 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2067 EmuRelease, Button2, mouse_emulation_state );
2068 if( mouse_emulation_state & Button3Mask )
2069 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2070 EmuRelease, Button3, mouse_emulation_state );
2073 // Fall through
2074 case XK_Escape:
2075 ungrabXKeyboard();
2076 mouse_emulation = false;
2077 return true;
2078 default:
2079 return false;
2082 QCursor::setPos( pos );
2083 if( mouse_emulation_state )
2084 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window,
2085 EmuMove, 0, mouse_emulation_state );
2087 return true;
2091 * Delayed focus functions
2093 void Workspace::delayFocus()
2095 requestFocus( delayfocus_client );
2096 cancelDelayFocus();
2099 void Workspace::requestDelayFocus( Client* c )
2101 delayfocus_client = c;
2102 delete delayFocusTimer;
2103 delayFocusTimer = new QTimer( this );
2104 connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) );
2105 delayFocusTimer->setSingleShot( true );
2106 delayFocusTimer->start( options->delayFocusInterval );
2109 void Workspace::cancelDelayFocus()
2111 delete delayFocusTimer;
2112 delayFocusTimer = 0;
2115 //-----------------------------------------------------------------------------
2116 // Electric Borders
2117 //-----------------------------------------------------------------------------
2118 // Electric Border Window management. Electric borders allow a user to change
2119 // the virtual desktop or activate another features by moving the mouse pointer
2120 // to the borders or corners. Technically this is done with input only windows.
2121 //-----------------------------------------------------------------------------
2123 void Workspace::updateElectricBorders()
2125 electric_time_first = xTime();
2126 electric_time_last = xTime();
2127 electric_current_border = ElectricNone;
2128 QRect r = Kephal::ScreenUtils::desktopGeometry();
2129 electricTop = r.top();
2130 electricBottom = r.bottom();
2131 electricLeft = r.left();
2132 electricRight = r.right();
2134 for( int pos = 0; pos < ELECTRIC_COUNT; ++pos )
2136 if( electric_reserved[pos] == 0 )
2138 if( electric_windows[pos] != None )
2139 XDestroyWindow( display(), electric_windows[pos] );
2140 electric_windows[pos] = None;
2141 continue;
2143 if( electric_windows[pos] != None )
2144 continue;
2145 XSetWindowAttributes attributes;
2146 attributes.override_redirect = True;
2147 attributes.event_mask = EnterWindowMask | LeaveWindowMask;
2148 unsigned long valuemask = CWOverrideRedirect | CWEventMask;
2149 int xywh[ELECTRIC_COUNT][4] =
2151 { r.left() + 1, r.top(), r.width() - 2, 1 }, // Top
2152 { r.right(), r.top(), 1, 1 }, // Top-right
2153 { r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc.
2154 { r.right(), r.bottom(), 1, 1 },
2155 { r.left() + 1, r.bottom(), r.width() - 2, 1 },
2156 { r.left(), r.bottom(), 1, 1 },
2157 { r.left(), r.top() + 1, 1, r.height() - 2 },
2158 { r.left(), r.top(), 1, 1 }
2160 electric_windows[pos] = XCreateWindow( display(), rootWindow(),
2161 xywh[pos][0], xywh[pos][1], xywh[pos][2], xywh[pos][3],
2162 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes );
2163 XMapWindow( display(), electric_windows[pos] );
2165 // Set XdndAware on the windows, so that DND enter events are received (#86998)
2166 Atom version = 4; // XDND version
2167 XChangeProperty( display(), electric_windows[pos], atoms->xdnd_aware, XA_ATOM,
2168 32, PropModeReplace, (unsigned char*)( &version ), 1 );
2172 void Workspace::destroyElectricBorders()
2174 for( int pos = 0; pos < ELECTRIC_COUNT; ++pos )
2176 if( electric_windows[pos] != None )
2177 XDestroyWindow( display(), electric_windows[pos] );
2178 electric_windows[pos] = None;
2182 void Workspace::reserveElectricBorderSwitching( bool reserve )
2184 for( int pos = 0; pos < ELECTRIC_COUNT; ++pos )
2185 if( reserve )
2186 reserveElectricBorder( static_cast<ElectricBorder>( pos ));
2187 else
2188 unreserveElectricBorder( static_cast<ElectricBorder>( pos ));
2191 void Workspace::reserveElectricBorder( ElectricBorder border )
2193 if( border == ElectricNone )
2194 return;
2195 if( electric_reserved[border]++ == 0 )
2196 QTimer::singleShot( 0, this, SLOT( updateElectricBorders() ));
2199 void Workspace::unreserveElectricBorder( ElectricBorder border )
2201 if( border == ElectricNone )
2202 return;
2203 assert( electric_reserved[border] > 0 );
2204 if( --electric_reserved[border] == 0 )
2205 QTimer::singleShot( 0, this, SLOT( updateElectricBorders() ));
2208 void Workspace::checkElectricBorder(const QPoint& pos, Time now)
2210 if(( pos.x() != electricLeft ) &&
2211 ( pos.x() != electricRight ) &&
2212 ( pos.y() != electricTop ) &&
2213 ( pos.y() != electricBottom ))
2214 return;
2216 bool have_borders = false;
2217 for( int i = 0; i < ELECTRIC_COUNT; ++i )
2218 if( electric_windows[i] != None )
2219 have_borders = true;
2220 if( !have_borders )
2221 return;
2223 Time treshold_set = options->electricBorderDelay(); // Set timeout
2224 Time treshold_reset = 250; // Reset timeout
2225 int distance_reset = 30; // Mouse should not move more than this many pixels
2227 ElectricBorder border;
2228 if( pos.x() == electricLeft && pos.y() == electricTop )
2229 border = ElectricTopLeft;
2230 else if( pos.x() == electricRight && pos.y() == electricTop )
2231 border = ElectricTopRight;
2232 else if( pos.x() == electricLeft && pos.y() == electricBottom )
2233 border = ElectricBottomLeft;
2234 else if( pos.x() == electricRight && pos.y() == electricBottom )
2235 border = ElectricBottomRight;
2236 else if( pos.x() == electricLeft )
2237 border = ElectricLeft;
2238 else if( pos.x() == electricRight )
2239 border = ElectricRight;
2240 else if( pos.y() == electricTop )
2241 border = ElectricTop;
2242 else if( pos.y() == electricBottom )
2243 border = ElectricBottom;
2244 else
2245 abort();
2247 if( electric_windows[border] == None )
2248 return;
2250 if(( electric_current_border == border ) &&
2251 ( timestampDiff( electric_time_last, now ) < treshold_reset ) &&
2252 (( pos-electric_push_point ).manhattanLength() < distance_reset ))
2254 electric_time_last = now;
2256 if( timestampDiff( electric_time_first, now ) > treshold_set )
2258 electric_current_border = ElectricNone;
2259 if( effects && static_cast<EffectsHandlerImpl*>( effects )->borderActivated( border ))
2260 {} // Handled by effects
2261 else
2262 electricBorderSwitchDesktop( border, pos );
2263 return;
2266 else
2268 electric_current_border = border;
2269 electric_time_first = now;
2270 electric_time_last = now;
2271 electric_push_point = pos;
2274 // Reset the pointer to find out whether the user is really pushing
2275 // (the direction back from which it came, starting from top clockwise)
2276 const int xdiff[ELECTRIC_COUNT] = { 0, -1, -1, -1, 0, 1, 1, 1 };
2277 const int ydiff[ELECTRIC_COUNT] = { 1, 1, 0, -1, -1, -1, 0, 1 };
2278 QCursor::setPos( pos.x() + xdiff[border], pos.y() + ydiff[border] );
2281 void Workspace::electricBorderSwitchDesktop( ElectricBorder border, const QPoint& _pos )
2283 QPoint pos = _pos;
2284 int desk = currentDesktop();
2285 const int OFFSET = 2;
2286 if( border == ElectricLeft || border == ElectricTopLeft || border == ElectricBottomLeft )
2288 desk = desktopToLeft( desk, options->rollOverDesktops );
2289 pos.setX( displayWidth() - 1 - OFFSET );
2291 if( border == ElectricRight || border == ElectricTopRight || border == ElectricBottomRight )
2293 desk = desktopToRight( desk, options->rollOverDesktops );
2294 pos.setX( OFFSET );
2296 if( border == ElectricTop || border == ElectricTopLeft || border == ElectricTopRight )
2298 desk = desktopUp( desk, options->rollOverDesktops );
2299 pos.setY( displayHeight() - 1 - OFFSET );
2301 if( border == ElectricBottom || border == ElectricBottomLeft || border == ElectricBottomRight )
2303 desk = desktopDown( desk, options->rollOverDesktops );
2304 pos.setY( OFFSET );
2306 int desk_before = currentDesktop();
2307 setCurrentDesktop( desk );
2308 if( currentDesktop() != desk_before )
2309 QCursor::setPos( pos );
2313 * Called when the user entered an electric border with the mouse.
2314 * It may switch to another virtual desktop.
2316 bool Workspace::electricBorderEvent( XEvent* e )
2318 if( e->type == EnterNotify )
2320 for( int i = 0; i < ELECTRIC_COUNT; ++i )
2321 if( electric_windows[i] != None && e->xcrossing.window == electric_windows[i] )
2322 { // The user entered an electric border
2323 checkElectricBorder( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time );
2324 return true;
2327 if( e->type == ClientMessage )
2329 if( e->xclient.message_type == atoms->xdnd_position )
2331 for( int i = 0; i < ELECTRIC_COUNT; ++i )
2332 if( electric_windows[i] != None && e->xclient.window == electric_windows[i] )
2334 updateXTime();
2335 checkElectricBorder( QPoint(
2336 e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), xTime() );
2337 return true;
2341 return false;
2344 //-----------------------------------------------------------------------------
2345 // Top menu
2347 void Workspace::addTopMenu( Client* c )
2349 assert( c->isTopMenu() );
2350 assert( !topmenus.contains( c ));
2351 topmenus.append( c );
2352 if( managingTopMenus() )
2354 int minsize = c->minSize().height();
2355 if( minsize > topMenuHeight() )
2357 topmenu_height = minsize;
2358 updateTopMenuGeometry();
2360 updateTopMenuGeometry( c );
2361 updateCurrentTopMenu();
2364 //kDebug( 1212 ) << "NEW TOPMENU:" << c;
2367 void Workspace::removeTopMenu( Client* c )
2369 //if( c->isTopMenu() )
2370 // kDebug( 1212 ) << "REMOVE TOPMENU:" << c;
2372 assert( c->isTopMenu() );
2373 assert( topmenus.contains( c ));
2374 topmenus.removeAll( c );
2375 updateCurrentTopMenu();
2376 // TODO: Reduce topMenuHeight() if possible?
2379 void Workspace::lostTopMenuSelection()
2381 //kDebug( 1212 ) << "lost TopMenu selection";
2383 // Make sure this signal is always set when not owning the selection
2384 disconnect( topmenu_watcher, SIGNAL( lostOwner() ), this, SLOT( lostTopMenuOwner() ));
2385 connect( topmenu_watcher, SIGNAL( lostOwner() ), this, SLOT( lostTopMenuOwner() ));
2386 if( !managing_topmenus )
2387 return;
2388 connect( topmenu_watcher, SIGNAL( lostOwner() ), this, SLOT( lostTopMenuOwner() ));
2389 disconnect( topmenu_selection, SIGNAL( lostOwnership() ), this, SLOT( lostTopMenuSelection() ));
2390 managing_topmenus = false;
2391 delete topmenu_space;
2392 topmenu_space = NULL;
2393 updateClientArea();
2394 for( ClientList::ConstIterator it = topmenus.constBegin();
2395 it != topmenus.constEnd();
2396 ++it )
2397 (*it)->checkWorkspacePosition();
2400 void Workspace::lostTopMenuOwner()
2402 if( !options->topMenuEnabled())
2403 return;
2404 //kDebug( 1212 ) << "TopMenu selection lost owner";
2405 if( !topmenu_selection->claim( false ))
2407 //kDebug( 1212 ) << "Failed to claim TopMenu selection";
2408 return;
2410 //kDebug( 1212 ) << "Claimed TopMenu selection";
2411 setupTopMenuHandling();
2414 void Workspace::setupTopMenuHandling()
2416 if( managing_topmenus )
2417 return;
2418 connect( topmenu_selection, SIGNAL( lostOwnership() ), this, SLOT( lostTopMenuSelection() ));
2419 disconnect( topmenu_watcher, SIGNAL( lostOwner() ), this, SLOT( lostTopMenuOwner() ));
2420 managing_topmenus = true;
2421 topmenu_space = new QWidget( NULL, Qt::X11BypassWindowManagerHint );
2422 Window stack[2];
2423 stack[0] = supportWindow->winId();
2424 stack[1] = topmenu_space->winId();
2425 XRestackWindows( display(), stack, 2 );
2426 updateTopMenuGeometry();
2427 topmenu_space->show();
2428 updateClientArea();
2429 updateCurrentTopMenu();
2432 int Workspace::topMenuHeight() const
2434 if( topmenu_height == 0 )
2435 { // Simply create a dummy menubar and use its preffered height as the menu height
2436 KMenuBar tmpmenu;
2437 tmpmenu.addAction( "dummy" );
2438 topmenu_height = tmpmenu.sizeHint().height();
2440 return topmenu_height;
2443 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
2445 return mgr->createDecoration( bridge );
2449 * Returns a list of all colors (KDecorationDefines::ColorType) the current
2450 * decoration supports
2452 QList<int> Workspace::decorationSupportedColors() const
2454 KDecorationFactory* factory = mgr->factory();
2455 QList<int> ret;
2456 for( Ability ab = ABILITYCOLOR_FIRST;
2457 ab < ABILITYCOLOR_END;
2458 ab = static_cast<Ability>( ab + 1 ))
2459 if( factory->supports( ab ))
2460 ret << ab;
2461 return ret;
2464 QString Workspace::desktopName( int desk ) const
2466 return QString::fromUtf8( rootInfo->desktopName( desk ));
2469 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data )
2471 return startup->checkStartup( w, id, data ) == KStartupInfo::Match;
2475 * Puts the focus on a dummy window
2476 * Just using XSetInputFocus() with None would block keyboard input
2478 void Workspace::focusToNull()
2480 XSetInputFocus( display(), null_focus_window, RevertToPointerRoot, xTime() );
2483 void Workspace::helperDialog( const QString& message, const Client* c )
2485 QStringList args;
2486 QString type;
2487 if( message == "noborderaltf3" )
2489 KAction* action = qobject_cast<KAction*>( keys->action( "Window Operations Menu" ));
2490 assert( action != NULL );
2491 QString shortcut = QString( "%1 (%2)" ).arg( action->text() )
2492 .arg( action->globalShortcut().primary().toString( QKeySequence::NativeText ));
2493 args << "--msgbox" << i18n(
2494 "You have selected to show a window without its border.\n"
2495 "Without the border, you will not be able to enable the border "
2496 "again using the mouse: use the window operations menu instead, "
2497 "activated using the %1 keyboard shortcut.",
2498 shortcut );
2499 type = "altf3warning";
2501 else if( message == "fullscreenaltf3" )
2503 KAction* action = qobject_cast<KAction*>( keys->action( "Window Operations Menu" ));
2504 assert( action != NULL );
2505 QString shortcut = QString( "%1 (%2)" ).arg( action->text() )
2506 .arg( action->globalShortcut().primary().toString( QKeySequence::NativeText ));
2507 args << "--msgbox" << i18n(
2508 "You have selected to show a window in fullscreen mode.\n"
2509 "If the application itself does not have an option to turn the fullscreen "
2510 "mode off you will not be able to disable it "
2511 "again using the mouse: use the window operations menu instead, "
2512 "activated using the %1 keyboard shortcut.",
2513 shortcut );
2514 type = "altf3warning";
2516 else
2517 abort();
2518 if( !type.isEmpty() )
2520 KConfig cfg( "kwin_dialogsrc" );
2521 KConfigGroup cg(&cfg, "Notification Messages" ); // Depends on KMessageBox
2522 if( !cg.readEntry( type, true ))
2523 return;
2524 args << "--dontagain" << "kwin_dialogsrc:" + type;
2526 if( c != NULL )
2527 args << "--embed" << QString::number( c->window() );
2528 KProcess::startDetached( "kdialog",args );
2531 void Workspace::setShowingDesktop( bool showing )
2533 rootInfo->setShowingDesktop( showing );
2534 showing_desktop = showing;
2535 ++block_showing_desktop;
2536 if( showing_desktop )
2538 showing_desktop_clients.clear();
2539 ++block_focus;
2540 ClientList cls = stackingOrder();
2541 // Find them first, then minimize, otherwise transients may get minimized with the window
2542 // they're transient for
2543 for( ClientList::ConstIterator it = cls.constBegin();
2544 it != cls.constEnd();
2545 ++it )
2546 if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow() )
2547 showing_desktop_clients.prepend( *it ); // Topmost first to reduce flicker
2548 for( ClientList::ConstIterator it = showing_desktop_clients.constBegin();
2549 it != showing_desktop_clients.constEnd();
2550 ++it )
2551 (*it)->minimize();
2552 --block_focus;
2553 if( Client* desk = findDesktop( true, currentDesktop() ))
2554 requestFocus( desk );
2556 else
2558 for( ClientList::ConstIterator it = showing_desktop_clients.constBegin();
2559 it != showing_desktop_clients.constEnd();
2560 ++it )
2561 (*it)->unminimize();
2562 if( showing_desktop_clients.count() > 0 )
2563 requestFocus( showing_desktop_clients.first() );
2564 showing_desktop_clients.clear();
2566 --block_showing_desktop;
2570 * Following Kicker's behavior:
2571 * Changing a virtual desktop resets the state and shows the windows again.
2572 * Unminimizing a window resets the state but keeps the windows hidden (except
2573 * the one that was unminimized).
2574 * A new window resets the state and shows the windows again, with the new window
2575 * being active. Due to popular demand (#67406) by people who apparently
2576 * don't see a difference between "show desktop" and "minimize all", this is not
2577 * true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
2578 * a new window resets the state but doesn't show windows.
2580 void Workspace::resetShowingDesktop( bool keep_hidden )
2582 if( block_showing_desktop > 0 )
2583 return;
2584 rootInfo->setShowingDesktop( false );
2585 showing_desktop = false;
2586 ++block_showing_desktop;
2587 if( !keep_hidden )
2589 for( ClientList::ConstIterator it = showing_desktop_clients.constBegin();
2590 it != showing_desktop_clients.constEnd();
2591 ++it )
2592 (*it)->unminimize();
2594 showing_desktop_clients.clear();
2595 --block_showing_desktop;
2599 * Activating/deactivating this feature works like this:
2600 * When nothing is active, and the shortcut is pressed, global shortcuts are disabled
2601 * (using global_shortcuts_disabled)
2602 * When a window that has disabling forced is activated, global shortcuts are disabled.
2603 * (using global_shortcuts_disabled_for_client)
2604 * When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
2605 * or for a client), they are enabled again.
2607 void Workspace::slotDisableGlobalShortcuts()
2609 if( global_shortcuts_disabled || global_shortcuts_disabled_for_client )
2610 disableGlobalShortcuts( false );
2611 else
2612 disableGlobalShortcuts( true );
2615 static bool pending_dfc = false;
2617 void Workspace::disableGlobalShortcutsForClient( bool disable )
2619 if( global_shortcuts_disabled_for_client == disable )
2620 return;
2621 if( !global_shortcuts_disabled )
2623 if( disable )
2624 pending_dfc = true;
2625 KGlobalSettings::self()->emitChange( KGlobalSettings::BlockShortcuts, disable );
2626 // KWin will get the kipc message too
2630 void Workspace::disableGlobalShortcuts( bool disable )
2632 KGlobalSettings::self()->emitChange( KGlobalSettings::BlockShortcuts, disable );
2633 // KWin will get the kipc message too
2636 void Workspace::slotBlockShortcuts( int data )
2638 if( pending_dfc && data )
2640 global_shortcuts_disabled_for_client = true;
2641 pending_dfc = false;
2643 else
2645 global_shortcuts_disabled = data;
2646 global_shortcuts_disabled_for_client = false;
2648 // Update also Alt+LMB actions etc.
2649 for( ClientList::ConstIterator it = clients.constBegin();
2650 it != clients.constEnd();
2651 ++it )
2652 (*it)->updateMouseGrab();
2655 // Optimized version of QCursor::pos() that tries to avoid X roundtrips
2656 // by updating the value only when the X timestamp changes.
2657 static QPoint last_cursor_pos;
2658 static int last_buttons = 0;
2659 static Time last_cursor_timestamp = CurrentTime;
2660 static QTimer* last_cursor_timer;
2662 QPoint Workspace::cursorPos() const
2664 if( last_cursor_timestamp == CurrentTime ||
2665 last_cursor_timestamp != QX11Info::appTime() )
2667 last_cursor_timestamp = QX11Info::appTime();
2668 Window root;
2669 Window child;
2670 int root_x, root_y, win_x, win_y;
2671 uint state;
2672 XQueryPointer( display(), rootWindow(), &root, &child,
2673 &root_x, &root_y, &win_x, &win_y, &state );
2674 last_cursor_pos = QPoint( root_x, root_y );
2675 last_buttons = state;
2676 if( last_cursor_timer == NULL )
2678 Workspace* ws = const_cast<Workspace*>( this );
2679 last_cursor_timer = new QTimer( ws );
2680 last_cursor_timer->setSingleShot( true );
2681 connect( last_cursor_timer, SIGNAL( timeout() ), ws, SLOT( resetCursorPosTime() ));
2683 last_cursor_timer->start( 0 );
2685 return last_cursor_pos;
2689 * Because of QTimer's and the impossibility to get events for all mouse
2690 * movements (at least I haven't figured out how) the position needs
2691 * to be also refetched after each return to the event loop.
2693 void Workspace::resetCursorPosTime()
2695 last_cursor_timestamp = CurrentTime;
2698 void Workspace::checkCursorPos()
2700 QPoint last = last_cursor_pos;
2701 int lastb = last_buttons;
2702 cursorPos(); // Update if needed
2703 if( last != last_cursor_pos || lastb != last_buttons )
2704 static_cast<EffectsHandlerImpl*>( effects )->mouseChanged( cursorPos(), last,
2705 x11ToQtMouseButtons( last_buttons ), x11ToQtMouseButtons( lastb ),
2706 x11ToQtKeyboardModifiers( last_buttons ), x11ToQtKeyboardModifiers( lastb ));
2709 } // namespace
2711 #include "workspace.moc"