Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kwin / workspace.cpp
blobe4b5a5734fc367e590c3567a2c8a21259de18b43
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 <QDesktopWidget>
40 #include <QToolButton>
41 #include <kactioncollection.h>
42 #include <kaction.h>
43 #include <kconfiggroup.h>
44 #include <QtDBus/QtDBus>
46 #include "plugins.h"
47 #include "client.h"
48 #include "popupinfo.h"
49 #include "tabbox.h"
50 #include "atoms.h"
51 #include "placement.h"
52 #include "notifications.h"
53 #include "group.h"
54 #include "rules.h"
55 #include "kwinadaptor.h"
56 #include "unmanaged.h"
57 #include "scene.h"
58 #include "deleted.h"
59 #include "effects.h"
61 #include <X11/extensions/shape.h>
62 #include <X11/keysym.h>
63 #include <X11/keysymdef.h>
64 #include <X11/cursorfont.h>
65 #include <QX11Info>
66 #include <stdio.h>
67 #include <kauthorized.h>
68 #include <ktoolinvocation.h>
69 #include <kglobalsettings.h>
71 namespace KWin
74 extern int screen_number;
76 Workspace *Workspace::_self = 0;
78 // Rikkus: This class is too complex. It needs splitting further.
79 // It's a nightmare to understand, especially with so few comments :(
81 // Matthias: Feel free to ask me questions about it. Feel free to add
82 // comments. I dissagree that further splittings makes it easier. 2500
83 // lines are not too much. It's the task that is complex, not the
84 // code.
85 Workspace::Workspace( bool restore )
86 : QObject (0),
87 current_desktop (0),
88 number_of_desktops(0),
89 active_popup( NULL ),
90 active_popup_client( NULL ),
91 temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ),
92 rules_updates_disabled( false ),
93 active_client (0),
94 last_active_client (0),
95 most_recently_raised (0),
96 movingClient(0),
97 pending_take_activity ( NULL ),
98 active_screen (0),
99 delayfocus_client (0),
100 force_restacking( false ),
101 showing_desktop( false ),
102 block_showing_desktop( 0 ),
103 was_user_interaction (false),
104 session_saving (false),
105 control_grab (false),
106 tab_grab (false),
107 mouse_emulation (false),
108 block_focus (0),
109 tab_box (0),
110 popupinfo (0),
111 popup (0),
112 advanced_popup (0),
113 trans_popup (0),
114 desk_popup (0),
115 keys (0),
116 client_keys ( NULL ),
117 client_keys_dialog ( NULL ),
118 client_keys_client ( NULL ),
119 disable_shortcuts_keys ( NULL ),
120 global_shortcuts_disabled( false ),
121 global_shortcuts_disabled_for_client( false ),
122 workspaceInit (true),
123 startup(0),
124 layoutOrientation(Qt::Vertical),
125 layoutX(-1),
126 layoutY(2),
127 workarea(NULL),
128 screenarea(NULL),
129 managing_topmenus( false ),
130 topmenu_selection( NULL ),
131 topmenu_watcher( NULL ),
132 topmenu_height( 0 ),
133 topmenu_space( NULL ),
134 set_active_client_recursion( 0 ),
135 block_stacking_updates( 0 ),
136 forced_global_mouse_grab( false ),
137 cm_selection( NULL ),
138 compositeRate( 0 ),
139 overlay( None ),
140 overlay_visible( true ),
141 transSlider( NULL ),
142 transButton( NULL )
144 (void) new KWinAdaptor( this );
145 QDBusConnection dbus = QDBusConnection::sessionBus();
146 dbus.registerObject("/KWin", this);
147 dbus.connect(QString(), "/KWin", "org.kde.KWin", "reloadConfig", this, SLOT(slotReloadConfig()));
148 dbus.connect(QString(), "/KWin", "org.kde.KWin", "reinitCompositing", this, SLOT(slotReinitCompositing()));
149 _self = this;
150 mgr = new PluginMgr;
151 QX11Info info;
152 default_colormap = DefaultColormap(display(), info.screen() );
153 installed_colormap = default_colormap;
155 for( int i = 0;
156 i < ELECTRIC_COUNT;
157 ++i )
159 electric_reserved[ i ] = 0;
160 electric_windows[ i ] = None;
163 connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )),
164 this, SLOT( gotTemporaryRulesMessage( const QString& )));
165 connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules()));
167 updateXTime(); // needed for proper initialization of user_time in Client ctor
169 delayFocusTimer = 0;
171 if ( restore )
172 loadSessionInfo();
174 loadWindowRules();
176 (void) QApplication::desktop(); // trigger creation of desktop widget
178 // call this before XSelectInput() on the root window
179 startup = new KStartupInfo(
180 KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this );
182 // select windowmanager privileges
183 XSelectInput(display(), rootWindow(),
184 KeyPressMask |
185 PropertyChangeMask |
186 ColormapChangeMask |
187 SubstructureRedirectMask |
188 SubstructureNotifyMask |
189 FocusChangeMask | // for NotifyDetailNone
190 ExposureMask
193 Extensions::init();
194 setupCompositing();
196 // compatibility
197 long data = 1;
199 XChangeProperty(
200 display(),
201 rootWindow(),
202 atoms->kwin_running,
203 atoms->kwin_running,
205 PropModeAppend,
206 (unsigned char*) &data,
210 client_keys = new KActionCollection( this );
211 initShortcuts();
212 tab_box = new TabBox( this );
213 popupinfo = new PopupInfo( this );
215 init();
217 connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized()));
220 void Workspace::init()
222 if( options->electricBorders() == Options::ElectricAlways )
223 reserveElectricBorderSwitching( true );
224 updateElectricBorders();
226 // not used yet
227 // topDock = 0L;
228 // maximizedWindowCounter = 0;
230 supportWindow = new QWidget;
231 XLowerWindow( display(), supportWindow->winId()); // see usage in layers.cpp
233 XSetWindowAttributes attr;
234 attr.override_redirect = 1;
235 null_focus_window = XCreateWindow( display(), rootWindow(), -1,-1, 1, 1, 0, CopyFromParent,
236 InputOnly, CopyFromParent, CWOverrideRedirect, &attr );
237 XMapWindow(display(), null_focus_window);
239 unsigned long protocols[ 5 ] =
241 NET::Supported |
242 NET::SupportingWMCheck |
243 NET::ClientList |
244 NET::ClientListStacking |
245 NET::DesktopGeometry |
246 NET::NumberOfDesktops |
247 NET::CurrentDesktop |
248 NET::ActiveWindow |
249 NET::WorkArea |
250 NET::CloseWindow |
251 NET::DesktopNames |
252 NET::WMName |
253 NET::WMVisibleName |
254 NET::WMDesktop |
255 NET::WMWindowType |
256 NET::WMState |
257 NET::WMStrut |
258 NET::WMIconGeometry |
259 NET::WMIcon |
260 NET::WMPid |
261 NET::WMMoveResize |
262 NET::WMFrameExtents |
263 NET::WMPing
265 NET::NormalMask |
266 NET::DesktopMask |
267 NET::DockMask |
268 NET::ToolbarMask |
269 NET::MenuMask |
270 NET::DialogMask |
271 NET::OverrideMask |
272 NET::TopMenuMask |
273 NET::UtilityMask |
274 NET::SplashMask |
275 // No compositing window types here unless we support them also as managed window types
278 NET::Modal |
279 // NET::Sticky | // large desktops not supported (and probably never will be)
280 NET::MaxVert |
281 NET::MaxHoriz |
282 NET::Shaded |
283 NET::SkipTaskbar |
284 NET::KeepAbove |
285 // NET::StaysOnTop | the same like KeepAbove
286 NET::SkipPager |
287 NET::Hidden |
288 NET::FullScreen |
289 NET::KeepBelow |
290 NET::DemandsAttention |
293 NET::WM2UserTime |
294 NET::WM2StartupId |
295 NET::WM2AllowedActions |
296 NET::WM2RestackWindow |
297 NET::WM2MoveResizeWindow |
298 NET::WM2ExtendedStrut |
299 NET::WM2KDETemporaryRules |
300 NET::WM2ShowingDesktop |
301 NET::WM2DesktopLayout |
302 NET::WM2FullPlacement |
305 NET::ActionMove |
306 NET::ActionResize |
307 NET::ActionMinimize |
308 NET::ActionShade |
309 // NET::ActionStick | // Sticky state is not supported
310 NET::ActionMaxVert |
311 NET::ActionMaxHoriz |
312 NET::ActionFullScreen |
313 NET::ActionChangeDesktop |
314 NET::ActionClose |
319 QX11Info info;
320 rootInfo = new RootInfo( this, display(), supportWindow->winId(), "KWin",
321 protocols, 5, info.screen() );
323 loadDesktopSettings();
324 updateDesktopLayout();
325 // extra NETRootInfo instance in Client mode is needed to get the values of the properties
326 NETRootInfo client_info( display(), NET::ActiveWindow | NET::CurrentDesktop );
327 int initial_desktop;
328 if( !kapp->isSessionRestored())
329 initial_desktop = client_info.currentDesktop();
330 else
332 KConfigGroup group( kapp->sessionConfig(), "Session" );
333 initial_desktop = group.readEntry( "desktop", 1 );
335 if( !setCurrentDesktop( initial_desktop ))
336 setCurrentDesktop( 1 );
338 // now we know how many desktops we'll, thus, we initialize the positioning object
339 initPositioning = new Placement(this);
341 reconfigureTimer.setSingleShot( true );
342 updateToolWindowsTimer.setSingleShot( true );
344 connect(&reconfigureTimer, SIGNAL(timeout()), this,
345 SLOT(slotReconfigure()));
346 connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows()));
347 connect( &compositeTimer, SIGNAL( timeout()), SLOT( performCompositing()));
349 connect(KGlobalSettings::self(), SIGNAL(appearanceChanged()), this,
350 SLOT(slotReconfigure()));
351 connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this,
352 SLOT(slotSettingsChanged(int)));
353 connect(KGlobalSettings::self(), SIGNAL(blockShortcuts(int)), this,
354 SLOT(slotBlockShortcuts(int)));
356 active_client = NULL;
357 rootInfo->setActiveWindow( None );
358 focusToNull();
359 if( !kapp->isSessionRestored())
360 ++block_focus; // because it will be set below
362 char nm[ 100 ];
363 sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( display()));
364 Atom topmenu_atom = XInternAtom( display(), nm, False );
365 topmenu_selection = new KSelectionOwner( topmenu_atom );
366 topmenu_watcher = new KSelectionWatcher( topmenu_atom );
367 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
369 { // begin updates blocker block
370 StackingUpdatesBlocker blocker( this );
372 if( options->topMenuEnabled() && topmenu_selection->claim( false ))
373 setupTopMenuHandling(); // this can call updateStackingOrder()
374 else
375 lostTopMenuSelection();
377 unsigned int i, nwins;
378 Window root_return, parent_return, *wins;
379 XQueryTree(display(), rootWindow(), &root_return, &parent_return, &wins, &nwins);
380 for (i = 0; i < nwins; i++)
382 XWindowAttributes attr;
383 XGetWindowAttributes(display(), wins[i], &attr);
384 if (attr.override_redirect )
386 createUnmanaged( wins[ i ] );
387 continue;
389 if( topmenu_space && topmenu_space->winId() == wins[ i ] )
390 continue;
391 if (attr.map_state != IsUnmapped)
392 createClient( wins[i], true );
394 if ( wins )
395 XFree((void *) wins);
396 // propagate clients, will really happen at the end of the updates blocker block
397 updateStackingOrder( true );
399 updateClientArea();
401 // NETWM spec says we have to set it to (0,0) if we don't support it
402 NETPoint* viewports = new NETPoint[ number_of_desktops ];
403 rootInfo->setDesktopViewport( number_of_desktops, *viewports );
404 delete[] viewports;
405 QRect geom = QApplication::desktop()->geometry();
406 NETSize desktop_geometry;
407 desktop_geometry.width = geom.width();
408 desktop_geometry.height = geom.height();
409 rootInfo->setDesktopGeometry( -1, desktop_geometry );
410 setShowingDesktop( false );
412 } // end updates blocker block
414 Client* new_active_client = NULL;
415 if( !kapp->isSessionRestored())
417 --block_focus;
418 new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow()));
420 if( new_active_client == NULL
421 && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage()
423 if( new_active_client == NULL )
424 new_active_client = topClientOnDesktop( currentDesktop());
425 if( new_active_client == NULL && !desktops.isEmpty() )
426 new_active_client = findDesktop( true, currentDesktop());
428 if( new_active_client != NULL )
429 activateClient( new_active_client );
430 // SELI TODO this won't work with unreasonable focus policies,
431 // and maybe in rare cases also if the selected client doesn't
432 // want focus
433 workspaceInit = false;
434 // TODO ungrabXServer()
437 Workspace::~Workspace()
439 finishCompositing();
440 blockStackingUpdates( true );
441 // TODO grabXServer();
442 // use stacking_order, so that kwin --replace keeps stacking order
443 for( ClientList::ConstIterator it = stacking_order.begin();
444 it != stacking_order.end();
445 ++it )
447 // only release the window
448 (*it)->releaseWindow( true );
449 // No removeClient() is called, it does more than just removing.
450 // However, remove from some lists to e.g. prevent performTransiencyCheck()
451 // from crashing.
452 clients.removeAll( *it );
453 desktops.removeAll( *it );
455 for( UnmanagedList::ConstIterator it = unmanaged.begin();
456 it != unmanaged.end();
457 ++it )
458 (*it)->release();
459 delete tab_box;
460 delete popupinfo;
461 delete popup;
462 XDeleteProperty(display(), rootWindow(), atoms->kwin_running);
464 writeWindowRules();
465 KGlobal::config()->sync();
467 delete rootInfo;
468 delete supportWindow;
469 delete mgr;
470 delete[] workarea;
471 delete[] screenarea;
472 delete startup;
473 delete initPositioning;
474 delete topmenu_watcher;
475 delete topmenu_selection;
476 delete topmenu_space;
477 delete client_keys_dialog;
478 while( !rules.isEmpty())
480 delete rules.front();
481 rules.pop_front();
483 foreach ( SessionInfo* s, session )
484 delete s;
485 XDestroyWindow( display(), null_focus_window );
486 // TODO ungrabXServer();
487 _self = 0;
490 Client* Workspace::createClient( Window w, bool is_mapped )
492 StackingUpdatesBlocker blocker( this );
493 Client* c = new Client( this );
494 if( !c->manage( w, is_mapped ))
496 Client::deleteClient( c, Allowed );
497 return NULL;
499 addClient( c, Allowed );
500 if( scene )
501 scene->windowAdded( c );
502 if( effects )
503 static_cast<EffectsHandlerImpl*>(effects)->windowAdded( c->effectWindow());
504 return c;
507 Unmanaged* Workspace::createUnmanaged( Window w )
509 if( w == overlay )
510 return NULL;
511 Unmanaged* c = new Unmanaged( this );
512 if( !c->track( w ))
514 Unmanaged::deleteUnmanaged( c, Allowed );
515 return NULL;
517 addUnmanaged( c, Allowed );
518 if( scene )
519 scene->windowAdded( c );
520 if( effects )
521 static_cast<EffectsHandlerImpl*>(effects)->windowAdded( c->effectWindow());
522 return c;
525 void Workspace::addClient( Client* c, allowed_t )
527 Group* grp = findGroup( c->window());
528 if( grp != NULL )
529 grp->gotLeader( c );
531 if ( c->isDesktop() )
533 desktops.append( c );
534 if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
535 requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active
537 else
539 updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there
540 clients.append( c );
542 if( !unconstrained_stacking_order.contains( c ))
543 unconstrained_stacking_order.append( c ); // raise if it hasn't got any stacking position yet
544 if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires
545 stacking_order.append( c ); // c to be in stacking_order
546 if( c->isTopMenu())
547 addTopMenu( c );
548 updateClientArea(); // this cannot be in manage(), because the client got added only now
549 updateClientLayer( c );
550 if( c->isDesktop())
552 raiseClient( c );
553 // if there's no active client, make this desktop the active one
554 if( activeClient() == NULL && should_get_focus.count() == 0 )
555 activateClient( findDesktop( true, currentDesktop()));
557 c->checkActiveModal();
558 checkTransients( c->window()); // SELI does this really belong here?
559 updateStackingOrder( true ); // propagate new client
560 if( c->isUtility() || c->isMenu() || c->isToolbar())
561 updateToolWindows( true );
562 checkNonExistentClients();
563 if( tab_grab )
564 tab_box->reset( true );
567 void Workspace::addUnmanaged( Unmanaged* c, allowed_t )
569 unmanaged.append( c );
570 unmanaged_stacking_order.append( c );
574 Destroys the client \a c
576 void Workspace::removeClient( Client* c, allowed_t )
578 if (c == active_popup_client)
579 closeActivePopup();
581 if( client_keys_client == c )
582 setupWindowShortcutDone( false );
583 if( !c->shortcut().isEmpty())
584 c->setShortcut( QString() ); // remove from client_keys
586 if( c->isDialog())
587 Notify::raise( Notify::TransDelete );
588 if( c->isNormalWindow())
589 Notify::raise( Notify::Delete );
591 if( tab_grab )
593 if( tab_box->currentClient() == c )
594 tab_box->nextPrev( true );
597 Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
598 clients.removeAll( c );
599 desktops.removeAll( c );
600 unconstrained_stacking_order.removeAll( c );
601 stacking_order.removeAll( c );
602 for( int i = 1;
603 i <= numberOfDesktops();
604 ++i )
605 focus_chain[ i ].removeAll( c );
606 global_focus_chain.removeAll( c );
607 attention_chain.removeAll( c );
608 showing_desktop_clients.removeAll( c );
609 if( c->isTopMenu())
610 removeTopMenu( c );
611 Group* group = findGroup( c->window());
612 if( group != NULL )
613 group->lostLeader();
615 if ( c == most_recently_raised )
616 most_recently_raised = 0;
617 should_get_focus.removeAll( c );
618 Q_ASSERT( c != active_client );
619 if ( c == last_active_client )
620 last_active_client = 0;
621 if( c == pending_take_activity )
622 pending_take_activity = NULL;
623 if( c == delayfocus_client )
624 cancelDelayFocus();
626 updateStackingOrder( true );
628 if( tab_grab )
629 tab_box->reset( true );
631 updateClientArea();
634 void Workspace::removeUnmanaged( Unmanaged* c, allowed_t )
636 assert( unmanaged.contains( c ));
637 unmanaged.removeAll( c );
638 unmanaged_stacking_order.removeAll( c );
641 void Workspace::addDeleted( Deleted* c, allowed_t )
643 assert( !deleted.contains( c ));
644 deleted.append( c );
647 void Workspace::removeDeleted( Deleted* c, allowed_t )
649 assert( deleted.contains( c ));
650 if( scene )
651 scene->windowDeleted( c );
652 if( effects )
653 static_cast<EffectsHandlerImpl*>(effects)->windowDeleted( c->effectWindow());
654 deleted.removeAll( c );
657 void Workspace::updateFocusChains( Client* c, FocusChainChange change )
659 if( !c->wantsTabFocus()) // doesn't want tab focus, remove
661 for( int i=1;
662 i<= numberOfDesktops();
663 ++i )
664 focus_chain[i].removeAll(c);
665 global_focus_chain.removeAll( c );
666 return;
668 if(c->desktop() == NET::OnAllDesktops)
669 { //now on all desktops, add it to focus_chains it is not already in
670 for( int i=1; i<= numberOfDesktops(); i++)
671 { // making first/last works only on current desktop, don't affect all desktops
672 if( i == currentDesktop()
673 && ( change == FocusChainMakeFirst || change == FocusChainMakeLast ))
675 focus_chain[ i ].removeAll( c );
676 if( change == FocusChainMakeFirst )
677 focus_chain[ i ].append( c );
678 else
679 focus_chain[ i ].prepend( c );
681 else if( !focus_chain[ i ].contains( c ))
682 { // add it after the active one
683 if( active_client != NULL && active_client != c
684 && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
685 focus_chain[ i ].insert( focus_chain[ i ].size() - 1, c );
686 else
687 focus_chain[ i ].append( c ); // otherwise add as the first one
691 else //now only on desktop, remove it anywhere else
693 for( int i=1; i<= numberOfDesktops(); i++)
695 if( i == c->desktop())
697 if( change == FocusChainMakeFirst )
699 focus_chain[ i ].removeAll( c );
700 focus_chain[ i ].append( c );
702 else if( change == FocusChainMakeLast )
704 focus_chain[ i ].removeAll( c );
705 focus_chain[ i ].prepend( c );
707 else if( !focus_chain[ i ].contains( c ))
708 { // add it after the active one
709 if( active_client != NULL && active_client != c
710 && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
711 focus_chain[ i ].insert( focus_chain[ i ].size() - 1, c );
712 else
713 focus_chain[ i ].append( c ); // otherwise add as the first one
716 else
717 focus_chain[ i ].removeAll( c );
720 if( change == FocusChainMakeFirst )
722 global_focus_chain.removeAll( c );
723 global_focus_chain.append( c );
725 else if( change == FocusChainMakeLast )
727 global_focus_chain.removeAll( c );
728 global_focus_chain.prepend( c );
730 else if( !global_focus_chain.contains( c ))
731 { // add it after the active one
732 if( active_client != NULL && active_client != c
733 && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client )
734 global_focus_chain.insert( global_focus_chain.size() - 1, c );
735 else
736 global_focus_chain.append( c ); // otherwise add as the first one
740 void Workspace::updateCurrentTopMenu()
742 if( !managingTopMenus())
743 return;
744 // toplevel menubar handling
745 Client* menubar = 0;
746 bool block_desktop_menubar = false;
747 if( active_client )
749 // show the new menu bar first...
750 Client* menu_client = active_client;
751 for(;;)
753 if( menu_client->isFullScreen())
754 block_desktop_menubar = true;
755 for( ClientList::ConstIterator it = menu_client->transients().begin();
756 it != menu_client->transients().end();
757 ++it )
758 if( (*it)->isTopMenu())
760 menubar = *it;
761 break;
763 if( menubar != NULL || !menu_client->isTransient())
764 break;
765 if( menu_client->isModal() || menu_client->transientFor() == NULL )
766 break; // don't use mainwindow's menu if this is modal or group transient
767 menu_client = menu_client->transientFor();
769 if( !menubar )
770 { // try to find any topmenu from the application (#72113)
771 for( ClientList::ConstIterator it = active_client->group()->members().begin();
772 it != active_client->group()->members().end();
773 ++it )
774 if( (*it)->isTopMenu())
776 menubar = *it;
777 break;
781 if( !menubar && !block_desktop_menubar && options->desktopTopMenu())
783 // Find the menubar of the desktop
784 Client* desktop = findDesktop( true, currentDesktop());
785 if( desktop != NULL )
787 for( ClientList::ConstIterator it = desktop->transients().begin();
788 it != desktop->transients().end();
789 ++it )
790 if( (*it)->isTopMenu())
792 menubar = *it;
793 break;
796 // TODO to be cleaned app with window grouping
797 // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
798 // thus the topmenu is not transient for it :-/.
799 if( menubar == NULL )
801 for( ClientList::ConstIterator it = topmenus.begin();
802 it != topmenus.end();
803 ++it )
804 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR
805 { // set pointing to the root window
806 menubar = *it; // to recognize it here
807 break; // Also, with the xroot hack in kdesktop,
808 } // there's no NET::Desktop window to be transient for
812 // kDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client;
813 if ( menubar )
815 if( active_client && !menubar->isOnDesktop( active_client->desktop()))
816 menubar->setDesktop( active_client->desktop());
817 menubar->hideClient( false );
818 topmenu_space->hide();
819 // make it appear like it's been raised manually - it's in the Dock layer anyway,
820 // and not raising it could mess up stacking order of topmenus within one application,
821 // and thus break raising of mainclients in raiseClient()
822 unconstrained_stacking_order.removeAll( menubar );
823 unconstrained_stacking_order.append( menubar );
825 else if( !block_desktop_menubar )
826 { // no topmenu active - show the space window, so that there's not empty space
827 topmenu_space->show();
830 // ... then hide the other ones. Avoids flickers.
831 for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it)
833 if( (*it)->isTopMenu() && (*it) != menubar )
834 (*it)->hideClient( true );
839 void Workspace::updateToolWindows( bool also_hide )
841 // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
842 if( !options->hideUtilityWindowsForInactive )
844 for( ClientList::ConstIterator it = clients.begin();
845 it != clients.end();
846 ++it )
847 (*it)->hideClient( false );
848 return;
850 const Group* group = NULL;
851 const Client* client = active_client;
852 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
853 // will be shown; if a group transient is group, all tools in the group will be shown
854 while( client != NULL )
856 if( !client->isTransient())
857 break;
858 if( client->groupTransient())
860 group = client->group();
861 break;
863 client = client->transientFor();
865 // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
866 // i.e. if it's not up to date
868 // SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
869 ClientList to_show, to_hide;
870 for( ClientList::ConstIterator it = stacking_order.begin();
871 it != stacking_order.end();
872 ++it )
874 if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar())
876 bool show = true;
877 if( !(*it)->isTransient())
879 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible
880 show = true;
881 else if( client != NULL && (*it)->group() == client->group())
882 show = true;
883 else
884 show = false;
886 else
888 if( group != NULL && (*it)->group() == group )
889 show = true;
890 else if( client != NULL && client->hasTransient( (*it), true ))
891 show = true;
892 else
893 show = false;
895 if( !show && also_hide )
897 const ClientList mainclients = (*it)->mainClients();
898 // don't hide utility windows which are standalone(?) or
899 // have e.g. kicker as mainwindow
900 if( mainclients.isEmpty())
901 show = true;
902 for( ClientList::ConstIterator it2 = mainclients.begin();
903 it2 != mainclients.end();
904 ++it2 )
906 if( (*it2)->isSpecialWindow())
907 show = true;
909 if( !show )
910 to_hide.append( *it );
912 if( show )
913 to_show.append( *it );
915 } // first show new ones, then hide
916 for( int i = to_show.size() - 1;
917 i >= 0;
918 --i ) //from topmost
919 // TODO since this is in stacking order, the order of taskbar entries changes :(
920 to_show.at( i )->hideClient( false );
921 if( also_hide )
923 for( ClientList::ConstIterator it = to_hide.begin();
924 it != to_hide.end();
925 ++it ) // from bottommost
926 (*it)->hideClient( true );
927 updateToolWindowsTimer.stop();
929 else // setActiveClient() is after called with NULL client, quickly followed
930 { // by setting a new client, which would result in flickering
931 updateToolWindowsTimer.start( 50 );
935 void Workspace::slotUpdateToolWindows()
937 updateToolWindows( true );
941 Updates the current colormap according to the currently active client
943 void Workspace::updateColormap()
945 Colormap cmap = default_colormap;
946 if ( activeClient() && activeClient()->colormap() != None )
947 cmap = activeClient()->colormap();
948 if ( cmap != installed_colormap )
950 XInstallColormap(display(), cmap );
951 installed_colormap = cmap;
955 void Workspace::slotReloadConfig()
957 reconfigure();
960 void Workspace::reconfigure()
962 reconfigureTimer.start( 200 );
966 void Workspace::slotSettingsChanged(int category)
968 kDebug(1212) << "Workspace::slotSettingsChanged()";
969 if( category == KGlobalSettings::SETTINGS_SHORTCUTS )
970 readShortcuts();
974 Reread settings
976 KWIN_PROCEDURE( CheckBorderSizesProcedure, Client, cl->checkBorderSizes() );
978 void Workspace::slotReconfigure()
980 kDebug(1212) << "Workspace::slotReconfigure()";
981 reconfigureTimer.stop();
983 if( options->electricBorders() == Options::ElectricAlways )
984 reserveElectricBorderSwitching( false );
986 KGlobal::config()->reparseConfiguration();
987 unsigned long changed = options->updateSettings();
988 tab_box->reconfigure();
989 popupinfo->reconfigure();
990 initPositioning->reinitCascading( 0 );
991 readShortcuts();
992 forEachClient( CheckIgnoreFocusStealingProcedure());
993 updateToolWindows( true );
995 if( mgr->reset( changed ))
996 { // decorations need to be recreated
997 #if 0 // This actually seems to make things worse now
998 QWidget curtain;
999 curtain.setBackgroundMode( NoBackground );
1000 curtain.setGeometry( QApplication::desktop()->geometry() );
1001 curtain.show();
1002 #endif
1003 for( ClientList::ConstIterator it = clients.begin();
1004 it != clients.end();
1005 ++it )
1007 (*it)->updateDecoration( true, true );
1009 mgr->destroyPreviousPlugin();
1011 else
1013 forEachClient( CheckBorderSizesProcedure());
1016 if( options->electricBorders() == Options::ElectricAlways )
1017 reserveElectricBorderSwitching( true );
1018 updateElectricBorders();
1020 if( options->topMenuEnabled() && !managingTopMenus())
1022 if( topmenu_selection->claim( false ))
1023 setupTopMenuHandling();
1024 else
1025 lostTopMenuSelection();
1027 else if( !options->topMenuEnabled() && managingTopMenus())
1029 topmenu_selection->release();
1030 lostTopMenuSelection();
1032 topmenu_height = 0; // invalidate used menu height
1033 if( managingTopMenus())
1035 updateTopMenuGeometry();
1036 updateCurrentTopMenu();
1039 if( options->useCompositing )
1041 setupCompositing();
1042 if( effects ) // setupCompositing() may fail
1043 effects->reconfigure();
1044 addRepaintFull();
1046 else
1047 finishCompositing();
1049 loadWindowRules();
1050 for( ClientList::Iterator it = clients.begin();
1051 it != clients.end();
1052 ++it )
1054 (*it)->setupWindowRules( true );
1055 (*it)->applyWindowRules();
1056 discardUsedWindowRules( *it, false );
1060 void Workspace::slotReinitCompositing()
1062 // Reparse config. Config options will be reloaded by setupCompositing()
1063 KGlobal::config()->reparseConfiguration();
1064 options->updateSettings();
1066 // Stop any current compositing
1067 finishCompositing();
1068 // And start new one
1069 setupCompositing();
1070 if( effects ) // setupCompositing() may fail
1071 effects->reconfigure();
1074 void Workspace::loadDesktopSettings()
1076 KSharedConfig::Ptr c = KGlobal::config();
1077 QString groupname;
1078 if (screen_number == 0)
1079 groupname = "Desktops";
1080 else
1081 groupname.sprintf("Desktops-screen-%d", screen_number);
1082 KConfigGroup group(c,groupname);
1084 int n = group.readEntry("Number", 4);
1085 number_of_desktops = n;
1086 delete workarea;
1087 workarea = new QRect[ n + 1 ];
1088 delete screenarea;
1089 screenarea = NULL;
1090 rootInfo->setNumberOfDesktops( number_of_desktops );
1091 desktop_focus_chain.resize( n );
1092 // make it +1, so that it can be accessed as [1..numberofdesktops]
1093 focus_chain.resize( n + 1 );
1094 for(int i = 1; i <= n; i++)
1096 QString s = group.readEntry(QString("Name_%1").arg(i),
1097 i18n("Desktop %1", i));
1098 rootInfo->setDesktopName( i, s.toUtf8().data() );
1099 desktop_focus_chain[i-1] = i;
1103 void Workspace::saveDesktopSettings()
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 group.writeEntry("Number", number_of_desktops );
1114 for(int i = 1; i <= number_of_desktops; i++)
1116 QString s = desktopName( i );
1117 QString defaultvalue = i18n("Desktop %1", i);
1118 if ( s.isEmpty() )
1120 s = defaultvalue;
1121 rootInfo->setDesktopName( i, s.toUtf8().data() );
1124 if (s != defaultvalue)
1126 group.writeEntry( QString("Name_%1").arg(i), s );
1128 else
1130 QString currentvalue = group.readEntry(QString("Name_%1").arg(i), QString());
1131 if (currentvalue != defaultvalue)
1132 group.writeEntry( QString("Name_%1").arg(i), "" );
1137 QStringList Workspace::configModules(bool controlCenter)
1139 QStringList args;
1140 args << "kwindecoration";
1141 if (controlCenter)
1142 args << "kwinoptions";
1143 else if (KAuthorized::authorizeControlModule("kde-kwinoptions.desktop"))
1144 args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced" << "kwinrules" << "kwincompositing";
1145 return args;
1148 void Workspace::configureWM()
1150 KToolInvocation::kdeinitExec( "kcmshell4", configModules(false) );
1154 avoids managing a window with title \a title
1156 void Workspace::doNotManage( const QString &title )
1158 doNotManageList.append( title );
1162 Hack for java applets
1164 bool Workspace::isNotManaged( const QString& title )
1166 for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it )
1168 QRegExp r( (*it) );
1169 if (r.indexIn(title) != -1)
1171 doNotManageList.erase( it );
1172 return true;
1175 return false;
1179 Refreshes all the client windows
1181 void Workspace::refresh()
1183 QWidget w;
1184 w.setGeometry( QApplication::desktop()->geometry() );
1185 w.show();
1186 w.hide();
1187 QApplication::flush();
1191 During virt. desktop switching, desktop areas covered by windows that are
1192 going to be hidden are first obscured by new windows with no background
1193 ( i.e. transparent ) placed right below the windows. These invisible windows
1194 are removed after the switch is complete.
1195 Reduces desktop ( wallpaper ) repaints during desktop switching
1197 class ObscuringWindows
1199 public:
1200 ~ObscuringWindows();
1201 void create( Client* c );
1202 private:
1203 QList<Window> obscuring_windows;
1204 static QList<Window>* cached;
1205 static unsigned int max_cache_size;
1208 QList<Window>* ObscuringWindows::cached = 0;
1209 unsigned int ObscuringWindows::max_cache_size = 0;
1211 void ObscuringWindows::create( Client* c )
1213 if( compositing()) // not needed with compositing
1214 return;
1215 if( cached == 0 )
1216 cached = new QList<Window>;
1217 Window obs_win;
1218 XWindowChanges chngs;
1219 int mask = CWSibling | CWStackMode;
1220 if( cached->count() > 0 )
1222 cached->removeAll( obs_win = cached->first());
1223 chngs.x = c->x();
1224 chngs.y = c->y();
1225 chngs.width = c->width();
1226 chngs.height = c->height();
1227 mask |= CWX | CWY | CWWidth | CWHeight;
1229 else
1231 XSetWindowAttributes a;
1232 a.background_pixmap = None;
1233 a.override_redirect = True;
1234 obs_win = XCreateWindow( display(), rootWindow(), c->x(), c->y(),
1235 c->width(), c->height(), 0, CopyFromParent, InputOutput,
1236 CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
1238 chngs.sibling = c->frameId();
1239 chngs.stack_mode = Below;
1240 XConfigureWindow( display(), obs_win, mask, &chngs );
1241 XMapWindow( display(), obs_win );
1242 obscuring_windows.append( obs_win );
1245 ObscuringWindows::~ObscuringWindows()
1247 max_cache_size = qMax( ( int )max_cache_size, obscuring_windows.count() + 4 ) - 1;
1248 for( QList<Window>::ConstIterator it = obscuring_windows.begin();
1249 it != obscuring_windows.end();
1250 ++it )
1252 XUnmapWindow( display(), *it );
1253 if( cached->count() < ( int )max_cache_size )
1254 cached->prepend( *it );
1255 else
1256 XDestroyWindow( display(), *it );
1262 Sets the current desktop to \a new_desktop
1264 Shows/Hides windows according to the stacking order and finally
1265 propages the new desktop to the world
1267 bool Workspace::setCurrentDesktop( int new_desktop )
1269 if (new_desktop < 1 || new_desktop > number_of_desktops )
1270 return false;
1272 closeActivePopup();
1273 ++block_focus;
1274 // TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
1275 StackingUpdatesBlocker blocker( this );
1277 int old_desktop = current_desktop;
1278 if (new_desktop != current_desktop)
1280 ++block_showing_desktop;
1282 optimized Desktop switching: unmapping done from back to front
1283 mapping done from front to back => less exposure events
1285 Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
1287 ObscuringWindows obs_wins;
1289 current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works)
1291 for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
1292 if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
1294 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop ))
1295 obs_wins.create( *it );
1296 (*it)->updateVisibility();
1299 rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing
1301 if( movingClient && !movingClient->isOnDesktop( new_desktop ))
1302 movingClient->setDesktop( new_desktop );
1304 for( int i = stacking_order.size() - 1; i >= 0 ; --i )
1305 if ( stacking_order.at( i )->isOnDesktop( new_desktop ) )
1306 stacking_order.at( i )->updateVisibility();
1308 --block_showing_desktop;
1309 if( showingDesktop()) // do this only after desktop change to avoid flicker
1310 resetShowingDesktop( false );
1313 // restore the focus on this desktop
1314 --block_focus;
1315 Client* c = 0;
1317 if ( options->focusPolicyIsReasonable())
1319 // Search in focus chain
1320 if ( movingClient != NULL && active_client == movingClient
1321 && focus_chain[currentDesktop()].contains( active_client )
1322 && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
1324 c = active_client; // the requestFocus below will fail, as the client is already active
1326 if( !c )
1328 for( int i = focus_chain[ currentDesktop() ].size() - 1;
1329 i >= 0;
1330 --i )
1332 if( focus_chain[ currentDesktop() ].at( i )->isShown( false )
1333 && focus_chain[ currentDesktop() ].at( i )->isOnCurrentDesktop())
1335 c = focus_chain[ currentDesktop() ].at( i );
1336 break;
1342 //if "unreasonable focus policy"
1343 // and active_client is on_all_desktops and under mouse (hence == old_active_client),
1344 // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1345 else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
1346 c = active_client;
1348 if( c == NULL && !desktops.isEmpty())
1349 c = findDesktop( true, currentDesktop());
1351 if( c != active_client )
1352 setActiveClient( NULL, Allowed );
1354 if ( c )
1355 requestFocus( c );
1356 else if( !desktops.isEmpty() )
1357 requestFocus( findDesktop( true, currentDesktop()));
1358 else
1359 focusToNull();
1361 updateCurrentTopMenu();
1363 // Update focus chain:
1364 // If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3,
1365 // Output: chain = { 3, 1, 2, 4 }.
1366 // kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
1367 // .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
1368 for( int i = desktop_focus_chain.indexOf( currentDesktop() ); i > 0; i-- )
1369 desktop_focus_chain[i] = desktop_focus_chain[i-1];
1370 desktop_focus_chain[0] = currentDesktop();
1372 // QString s = "desktop_focus_chain[] = { ";
1373 // for( uint i = 0; i < desktop_focus_chain.size(); i++ )
1374 // s += QString::number(desktop_focus_chain[i]) + ", ";
1375 // kDebug(1212) << s << "}\n";
1377 if( old_desktop != 0 ) // not for the very first time
1378 popupinfo->showInfo( desktopName(currentDesktop()) );
1380 if( effects != NULL && old_desktop != 0 && old_desktop != new_desktop )
1381 static_cast<EffectsHandlerImpl*>(effects)->desktopChanged( old_desktop );
1382 if( compositing())
1383 addRepaintFull();
1385 return true;
1388 // called only from DCOP
1389 void Workspace::nextDesktop()
1391 int desktop = currentDesktop() + 1;
1392 setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
1395 // called only from DCOP
1396 void Workspace::previousDesktop()
1398 int desktop = currentDesktop() - 1;
1399 setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
1402 int Workspace::desktopToRight( int desktop, bool wrap ) const
1404 int x,y;
1405 Qt::Orientation orientation;
1406 calcDesktopLayout( &x, &y, &orientation );
1407 int dt = desktop-1;
1408 if (orientation == Qt::Vertical)
1410 dt += y;
1411 if ( dt >= numberOfDesktops() )
1413 if ( wrap )
1414 dt -= numberOfDesktops();
1415 else
1416 return desktop;
1419 else
1421 int d = (dt % x) + 1;
1422 if ( d >= x )
1424 if ( wrap )
1425 d -= x;
1426 else
1427 return desktop;
1429 dt = dt - (dt % x) + d;
1431 return dt+1;
1434 int Workspace::desktopToLeft( int desktop, bool wrap ) const
1436 int x,y;
1437 Qt::Orientation orientation;
1438 calcDesktopLayout( &x, &y, &orientation );
1439 int dt = desktop-1;
1440 if (orientation == Qt::Vertical)
1442 dt -= y;
1443 if ( dt < 0 )
1445 if ( wrap )
1446 dt += numberOfDesktops();
1447 else
1448 return desktop;
1451 else
1453 int d = (dt % x) - 1;
1454 if ( d < 0 )
1456 if ( wrap )
1457 d += x;
1458 else
1459 return desktop;
1461 dt = dt - (dt % x) + d;
1463 return dt+1;
1466 int Workspace::desktopUp( int desktop, bool wrap ) const
1468 int x,y;
1469 Qt::Orientation orientation;
1470 calcDesktopLayout( &x, &y, &orientation);
1471 int dt = desktop-1;
1472 if (orientation == Qt::Horizontal)
1474 dt -= x;
1475 if ( dt < 0 )
1477 if ( wrap )
1478 dt += numberOfDesktops();
1479 else
1480 return desktop;
1483 else
1485 int d = (dt % y) - 1;
1486 if ( d < 0 )
1488 if ( wrap )
1489 d += y;
1490 else
1491 return desktop;
1493 dt = dt - (dt % y) + d;
1495 return dt+1;
1498 int Workspace::desktopDown( int desktop, bool wrap ) const
1500 int x,y;
1501 Qt::Orientation orientation;
1502 calcDesktopLayout( &x, &y, &orientation);
1503 int dt = desktop-1;
1504 if (orientation == Qt::Horizontal)
1506 dt += x;
1507 if ( dt >= numberOfDesktops() )
1509 if ( wrap )
1510 dt -= numberOfDesktops();
1511 else
1512 return desktop;
1515 else
1517 int d = (dt % y) + 1;
1518 if ( d >= y )
1520 if ( wrap )
1521 d -= y;
1522 else
1523 return desktop;
1525 dt = dt - (dt % y) + d;
1527 return dt+1;
1532 Sets the number of virtual desktops to \a n
1534 void Workspace::setNumberOfDesktops( int n )
1536 if ( n == number_of_desktops )
1537 return;
1538 int old_number_of_desktops = number_of_desktops;
1539 number_of_desktops = n;
1541 if( currentDesktop() > numberOfDesktops())
1542 setCurrentDesktop( numberOfDesktops());
1544 // if increasing the number, do the resizing now,
1545 // otherwise after the moving of windows to still existing desktops
1546 if( old_number_of_desktops < number_of_desktops )
1548 rootInfo->setNumberOfDesktops( number_of_desktops );
1549 NETPoint* viewports = new NETPoint[ number_of_desktops ];
1550 rootInfo->setDesktopViewport( number_of_desktops, *viewports );
1551 delete[] viewports;
1552 updateClientArea( true );
1553 focus_chain.resize( number_of_desktops + 1 );
1556 // if the number of desktops decreased, move all
1557 // windows that would be hidden to the last visible desktop
1558 if( old_number_of_desktops > number_of_desktops )
1560 for( ClientList::ConstIterator it = clients.begin();
1561 it != clients.end();
1562 ++it)
1564 if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
1565 sendClientToDesktop( *it, numberOfDesktops(), true );
1568 if( old_number_of_desktops > number_of_desktops )
1570 rootInfo->setNumberOfDesktops( number_of_desktops );
1571 NETPoint* viewports = new NETPoint[ number_of_desktops ];
1572 rootInfo->setDesktopViewport( number_of_desktops, *viewports );
1573 delete[] viewports;
1574 updateClientArea( true );
1575 focus_chain.resize( number_of_desktops + 1 );
1578 saveDesktopSettings();
1580 // Resize and reset the desktop focus chain.
1581 desktop_focus_chain.resize( n );
1582 for( int i = 0; i < (int)desktop_focus_chain.size(); i++ )
1583 desktop_focus_chain[i] = i+1;
1587 Sends client \a c to desktop \a desk.
1589 Takes care of transients as well.
1591 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
1593 bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
1594 c->setDesktop( desk );
1595 if ( c->desktop() != desk ) // no change or desktop forced
1596 return;
1597 desk = c->desktop(); // Client did range checking
1599 if ( c->isOnDesktop( currentDesktop() ) )
1601 if ( c->wantsTabFocus() && options->focusPolicyIsReasonable()
1602 && !was_on_desktop // for stickyness changes
1603 && !dont_activate )
1604 requestFocus( c );
1605 else
1606 restackClientUnderActive( c );
1608 else
1610 raiseClient( c );
1613 ClientList transients_stacking_order = ensureStackingOrder( c->transients());
1614 for( ClientList::ConstIterator it = transients_stacking_order.begin();
1615 it != transients_stacking_order.end();
1616 ++it )
1617 sendClientToDesktop( *it, desk, dont_activate );
1618 updateClientArea();
1621 int Workspace::numScreens() const
1623 if( !options->xineramaEnabled )
1624 return 1;
1625 return qApp->desktop()->numScreens();
1628 int Workspace::activeScreen() const
1630 if( !options->xineramaEnabled )
1631 return 0;
1632 if( !options->activeMouseScreen )
1634 if( activeClient() != NULL && !activeClient()->isOnScreen( active_screen ))
1635 return qApp->desktop()->screenNumber( activeClient()->geometry().center());
1636 return active_screen;
1638 return qApp->desktop()->screenNumber( cursorPos());
1641 // check whether a client moved completely out of what's considered the active screen,
1642 // if yes, set a new active screen
1643 void Workspace::checkActiveScreen( const Client* c )
1645 if( !options->xineramaEnabled )
1646 return;
1647 if( !c->isActive())
1648 return;
1649 if( !c->isOnScreen( active_screen ))
1650 active_screen = c->screen();
1653 // called e.g. when a user clicks on a window, set active screen to be the screen
1654 // where the click occurred
1655 void Workspace::setActiveScreenMouse( const QPoint &mousepos )
1657 if( !options->xineramaEnabled )
1658 return;
1659 active_screen = qApp->desktop()->screenNumber( mousepos );
1662 QRect Workspace::screenGeometry( int screen ) const
1664 if( !options->xineramaEnabled )
1665 return qApp->desktop()->geometry();
1666 return qApp->desktop()->screenGeometry( screen );
1669 int Workspace::screenNumber( const QPoint &pos ) const
1671 if( !options->xineramaEnabled )
1672 return 0;
1673 return qApp->desktop()->screenNumber( pos );
1676 void Workspace::sendClientToScreen( Client* c, int screen )
1678 if( c->screen() == screen ) // don't use isOnScreen(), that's true even when only partially
1679 return;
1680 GeometryUpdatesBlocker blocker( c );
1681 QRect old_sarea = clientArea( MaximizeArea, c );
1682 QRect sarea = clientArea( MaximizeArea, screen, c->desktop());
1683 c->setGeometry( sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(),
1684 c->size().width(), c->size().height());
1685 c->checkWorkspacePosition();
1686 ClientList transients_stacking_order = ensureStackingOrder( c->transients());
1687 for( ClientList::ConstIterator it = transients_stacking_order.begin();
1688 it != transients_stacking_order.end();
1689 ++it )
1690 sendClientToScreen( *it, screen );
1691 if( c->isActive())
1692 active_screen = screen;
1695 void Workspace::updateDesktopLayout()
1697 // rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to
1698 layoutOrientation = ( rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal
1699 ? Qt::Horizontal : Qt::Vertical );
1700 layoutX = rootInfo->desktopLayoutColumnsRows().width();
1701 layoutY = rootInfo->desktopLayoutColumnsRows().height();
1702 if( layoutX == 0 && layoutY == 0 ) // not given, set default layout
1703 layoutY = 2;
1706 void Workspace::calcDesktopLayout(int* xp, int* yp, Qt::Orientation* orientation) const
1708 int x = layoutX; // <= 0 means compute it from the other and total number of desktops
1709 int y = layoutY;
1710 if((x <= 0) && (y > 0))
1711 x = (numberOfDesktops()+y-1) / y;
1712 else if((y <=0) && (x > 0))
1713 y = (numberOfDesktops()+x-1) / x;
1715 if(x <=0)
1716 x = 1;
1717 if (y <= 0)
1718 y = 1;
1719 *xp = x;
1720 *yp = y;
1721 *orientation = layoutOrientation;
1724 void Workspace::killWindowId( Window window_to_kill )
1726 if( window_to_kill == None )
1727 return;
1728 Window window = window_to_kill;
1729 Client* client = NULL;
1730 for(;;)
1732 client = findClient( FrameIdMatchPredicate( window ));
1733 if( client != NULL ) // found the client
1734 break;
1735 Window parent, root;
1736 Window* children;
1737 unsigned int children_count;
1738 XQueryTree( display(), window, &root, &parent, &children, &children_count );
1739 if( children != NULL )
1740 XFree( children );
1741 if( window == root ) // we didn't find the client, probably an override-redirect window
1742 break;
1743 window = parent; // go up
1745 if( client != NULL )
1746 client->killWindow();
1747 else
1748 XKillClient( display(), window_to_kill );
1752 void Workspace::sendPingToWindow( Window window, Time timestamp )
1754 rootInfo->sendPing( window, timestamp );
1757 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags )
1759 rootInfo->takeActivity( c->window(), timestamp, flags );
1760 pending_take_activity = c;
1765 Takes a screenshot of the current window and puts it in the clipboard.
1767 void Workspace::slotGrabWindow()
1769 if ( active_client )
1771 QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() );
1773 //No XShape - no work.
1774 if( Extensions::shapeAvailable())
1776 //As the first step, get the mask from XShape.
1777 int count, order;
1778 XRectangle* rects = XShapeGetRectangles( display(), active_client->frameId(),
1779 ShapeBounding, &count, &order);
1780 //The ShapeBounding region is the outermost shape of the window;
1781 //ShapeBounding - ShapeClipping is defined to be the border.
1782 //Since the border area is part of the window, we use bounding
1783 // to limit our work region
1784 if (rects)
1786 //Create a QRegion from the rectangles describing the bounding mask.
1787 QRegion contents;
1788 for (int pos = 0; pos < count; pos++)
1789 contents += QRegion(rects[pos].x, rects[pos].y,
1790 rects[pos].width, rects[pos].height);
1791 XFree(rects);
1793 //Create the bounding box.
1794 QRegion bbox(0, 0, snapshot.width(), snapshot.height());
1796 //Get the masked away area.
1797 QRegion maskedAway = bbox - contents;
1798 QVector<QRect> maskedAwayRects = maskedAway.rects();
1800 //Construct a bitmap mask from the rectangles
1801 QBitmap mask( snapshot.width(), snapshot.height());
1802 QPainter p(&mask);
1803 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1);
1804 for (int pos = 0; pos < maskedAwayRects.count(); pos++)
1805 p.fillRect(maskedAwayRects[pos], Qt::color0);
1806 p.end();
1807 snapshot.setMask(mask);
1811 QClipboard *cb = QApplication::clipboard();
1812 cb->setPixmap( snapshot );
1814 else
1815 slotGrabDesktop();
1819 Takes a screenshot of the whole desktop and puts it in the clipboard.
1821 void Workspace::slotGrabDesktop()
1823 QPixmap p = QPixmap::grabWindow( rootWindow() );
1824 QClipboard *cb = QApplication::clipboard();
1825 cb->setPixmap( p );
1830 Invokes keyboard mouse emulation
1832 void Workspace::slotMouseEmulation()
1834 if ( mouse_emulation )
1836 ungrabXKeyboard();
1837 mouse_emulation = false;
1838 return;
1841 if( grabXKeyboard())
1843 mouse_emulation = true;
1844 mouse_emulation_state = 0;
1845 mouse_emulation_window = 0;
1850 Returns the child window under the mouse and activates the
1851 respective client if necessary.
1853 Auxiliary function for the mouse emulation system.
1855 WId Workspace::getMouseEmulationWindow()
1857 Window root;
1858 Window child = rootWindow();
1859 int root_x, root_y, lx, ly;
1860 uint state;
1861 Window w;
1862 Client * c = 0;
1865 w = child;
1866 if (!c)
1867 c = findClient( FrameIdMatchPredicate( w ));
1868 XQueryPointer( display(), w, &root, &child,
1869 &root_x, &root_y, &lx, &ly, &state );
1870 } while ( child != None && child != w );
1872 if ( c && !c->isActive() )
1873 activateClient( c );
1874 return (WId) w;
1878 Sends a faked mouse event to the specified window. Returns the new button state.
1880 unsigned int Workspace::sendFakedMouseEvent( const QPoint &pos, WId w, MouseEmulation type, int button, unsigned int state )
1882 if ( !w )
1883 return state;
1884 QWidget* widget = QWidget::find( w );
1885 if ( (!widget || qobject_cast<QToolButton*>(widget)) && !findClient( WindowMatchPredicate( w )) )
1887 int x, y;
1888 Window xw;
1889 XTranslateCoordinates( display(), rootWindow(), w, pos.x(), pos.y(), &x, &y, &xw );
1890 if ( type == EmuMove )
1891 { // motion notify events
1892 XEvent e;
1893 e.type = MotionNotify;
1894 e.xmotion.window = w;
1895 e.xmotion.root = rootWindow();
1896 e.xmotion.subwindow = w;
1897 e.xmotion.time = xTime();
1898 e.xmotion.x = x;
1899 e.xmotion.y = y;
1900 e.xmotion.x_root = pos.x();
1901 e.xmotion.y_root = pos.y();
1902 e.xmotion.state = state;
1903 e.xmotion.is_hint = NotifyNormal;
1904 XSendEvent( display(), w, true, ButtonMotionMask, &e );
1906 else
1908 XEvent e;
1909 e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
1910 e.xbutton.window = w;
1911 e.xbutton.root = rootWindow();
1912 e.xbutton.subwindow = w;
1913 e.xbutton.time = xTime();
1914 e.xbutton.x = x;
1915 e.xbutton.y = y;
1916 e.xbutton.x_root = pos.x();
1917 e.xbutton.y_root = pos.y();
1918 e.xbutton.state = state;
1919 e.xbutton.button = button;
1920 XSendEvent( display(), w, true, ButtonPressMask, &e );
1922 if ( type == EmuPress )
1924 switch ( button )
1926 case 2:
1927 state |= Button2Mask;
1928 break;
1929 case 3:
1930 state |= Button3Mask;
1931 break;
1932 default: // 1
1933 state |= Button1Mask;
1934 break;
1937 else
1939 switch ( button )
1941 case 2:
1942 state &= ~Button2Mask;
1943 break;
1944 case 3:
1945 state &= ~Button3Mask;
1946 break;
1947 default: // 1
1948 state &= ~Button1Mask;
1949 break;
1954 return state;
1958 Handles keypress event during mouse emulation
1960 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
1962 int kc = XKeycodeToKeysym(display(), ev.keycode, 0);
1963 int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
1965 bool is_control = km & ControlMask;
1966 bool is_alt = km & Mod1Mask;
1967 bool is_shift = km & ShiftMask;
1968 int delta = is_control?1:is_alt?32:8;
1969 QPoint pos = cursorPos();
1971 switch ( kc )
1973 case XK_Left:
1974 case XK_KP_Left:
1975 pos.rx() -= delta;
1976 break;
1977 case XK_Right:
1978 case XK_KP_Right:
1979 pos.rx() += delta;
1980 break;
1981 case XK_Up:
1982 case XK_KP_Up:
1983 pos.ry() -= delta;
1984 break;
1985 case XK_Down:
1986 case XK_KP_Down:
1987 pos.ry() += delta;
1988 break;
1989 case XK_F1:
1990 if ( !mouse_emulation_state )
1991 mouse_emulation_window = getMouseEmulationWindow();
1992 if ( (mouse_emulation_state & Button1Mask) == 0 )
1993 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
1994 if ( !is_shift )
1995 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
1996 break;
1997 case XK_F2:
1998 if ( !mouse_emulation_state )
1999 mouse_emulation_window = getMouseEmulationWindow();
2000 if ( (mouse_emulation_state & Button2Mask) == 0 )
2001 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state );
2002 if ( !is_shift )
2003 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
2004 break;
2005 case XK_F3:
2006 if ( !mouse_emulation_state )
2007 mouse_emulation_window = getMouseEmulationWindow();
2008 if ( (mouse_emulation_state & Button3Mask) == 0 )
2009 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state );
2010 if ( !is_shift )
2011 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
2012 break;
2013 case XK_Return:
2014 case XK_space:
2015 case XK_KP_Enter:
2016 case XK_KP_Space:
2018 if ( !mouse_emulation_state )
2020 // nothing was pressed, fake a LMB click
2021 mouse_emulation_window = getMouseEmulationWindow();
2022 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
2023 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
2025 else
2026 { // release all
2027 if ( mouse_emulation_state & Button1Mask )
2028 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
2029 if ( mouse_emulation_state & Button2Mask )
2030 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
2031 if ( mouse_emulation_state & Button3Mask )
2032 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
2035 // fall through
2036 case XK_Escape:
2037 ungrabXKeyboard();
2038 mouse_emulation = false;
2039 return true;
2040 default:
2041 return false;
2044 QCursor::setPos( pos );
2045 if ( mouse_emulation_state )
2046 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state );
2047 return true;
2051 //Delayed focus functions
2052 void Workspace::delayFocus()
2054 requestFocus( delayfocus_client );
2055 cancelDelayFocus();
2058 void Workspace::requestDelayFocus( Client* c )
2060 delayfocus_client = c;
2061 delete delayFocusTimer;
2062 delayFocusTimer = new QTimer( this );
2063 connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) );
2064 delayFocusTimer->setSingleShot( true );
2065 delayFocusTimer->start( options->delayFocusInterval );
2068 void Workspace::cancelDelayFocus()
2070 delete delayFocusTimer;
2071 delayFocusTimer = 0;
2074 // Electric Borders
2075 //========================================================================//
2076 // Electric Border Window management. Electric borders allow a user
2077 // to change the virtual desktop or activate another features
2078 // by moving the mouse pointer to the borders or corners.
2079 // Technically this is done with input only windows.
2080 void Workspace::updateElectricBorders()
2082 electric_time_first = xTime();
2083 electric_time_last = xTime();
2084 electric_current_border = ElectricNone;
2085 QRect r = QApplication::desktop()->geometry();
2086 electricTop = r.top();
2087 electricBottom = r.bottom();
2088 electricLeft = r.left();
2089 electricRight = r.right();
2091 for( int pos = 0;
2092 pos < ELECTRIC_COUNT;
2093 ++pos )
2095 if( electric_reserved[ pos ] == 0 )
2097 if( electric_windows[ pos ] != None )
2098 XDestroyWindow( display(), electric_windows[ pos ] );
2099 electric_windows[ pos ] = None;
2100 continue;
2102 if( electric_windows[ pos ] != None )
2103 continue;
2104 XSetWindowAttributes attributes;
2105 attributes.override_redirect = True;
2106 attributes.event_mask = EnterWindowMask | LeaveWindowMask;
2107 unsigned long valuemask = CWOverrideRedirect | CWEventMask;
2108 int xywh[ ELECTRIC_COUNT ][ 4 ] =
2110 { r.left() + 1, r.top(), r.width() - 2, 1 }, // top
2111 { r.right(), r.top(), 1, 1 }, // topright
2112 { r.right(), r.top() + 1, 1, r.height() - 2 }, // etc.
2113 { r.right(), r.bottom(), 1, 1 },
2114 { r.left() + 1, r.bottom(), r.width() - 2, 1 },
2115 { r.left(), r.bottom(), 1, 1 },
2116 { r.left(), r.top() + 1, 1, r.height() - 2 },
2117 { r.left(), r.top(), 1, 1 }
2119 electric_windows[ pos ] = XCreateWindow( display(), rootWindow(),
2120 xywh[ pos ][ 0 ], xywh[ pos ][ 1 ], xywh[ pos ][ 2 ], xywh[ pos ][ 3 ],
2121 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes );
2122 XMapWindow( display(), electric_windows[ pos ]);
2123 // Set XdndAware on the windows, so that DND enter events are received (#86998)
2124 Atom version = 4; // XDND version
2125 XChangeProperty( display(), electric_windows[ pos ], atoms->xdnd_aware, XA_ATOM,
2126 32, PropModeReplace, ( unsigned char* )&version, 1 );
2130 void Workspace::destroyElectricBorders()
2132 for( int pos = 0;
2133 pos < ELECTRIC_COUNT;
2134 ++pos )
2136 if( electric_windows[ pos ] != None )
2137 XDestroyWindow( display(), electric_windows[ pos ] );
2138 electric_windows[ pos ] = None;
2142 void Workspace::reserveElectricBorderSwitching( bool reserve )
2144 for( int pos = 0;
2145 pos < ELECTRIC_COUNT;
2146 ++pos )
2147 if( reserve )
2148 reserveElectricBorder( static_cast< ElectricBorder >( pos ));
2149 else
2150 unreserveElectricBorder( static_cast< ElectricBorder >( pos ));
2153 void Workspace::reserveElectricBorder( ElectricBorder border )
2155 if( border == ElectricNone )
2156 return;
2157 if( electric_reserved[ border ]++ == 0 )
2158 QTimer::singleShot( 0, this, SLOT( updateElectricBorders()));
2161 void Workspace::unreserveElectricBorder( ElectricBorder border )
2163 if( border == ElectricNone )
2164 return;
2165 assert( electric_reserved[ border ] > 0 );
2166 if( --electric_reserved[ border ] == 0 )
2167 QTimer::singleShot( 0, this, SLOT( updateElectricBorders()));
2170 void Workspace::checkElectricBorder(const QPoint &pos, Time now)
2172 if ((pos.x() != electricLeft) &&
2173 (pos.x() != electricRight) &&
2174 (pos.y() != electricTop) &&
2175 (pos.y() != electricBottom))
2176 return;
2178 bool have_borders = false;
2179 for( int i = 0;
2180 i < ELECTRIC_COUNT;
2181 ++i )
2182 if( electric_windows[ i ] != None )
2183 have_borders = true;
2184 if( !have_borders )
2185 return;
2187 Time treshold_set = options->electricBorderDelay(); // set timeout
2188 Time treshold_reset = 250; // reset timeout
2189 int distance_reset = 30; // Mouse should not move more than this many pixels
2191 ElectricBorder border;
2192 if( pos.x() == electricLeft && pos.y() == electricTop )
2193 border = ElectricTopLeft;
2194 else if( pos.x() == electricRight && pos.y() == electricTop )
2195 border = ElectricTopRight;
2196 else if( pos.x() == electricLeft && pos.y() == electricBottom )
2197 border = ElectricBottomLeft;
2198 else if( pos.x() == electricRight && pos.y() == electricBottom )
2199 border = ElectricBottomRight;
2200 else if( pos.x() == electricLeft )
2201 border = ElectricLeft;
2202 else if( pos.x() == electricRight )
2203 border = ElectricRight;
2204 else if( pos.y() == electricTop )
2205 border = ElectricTop;
2206 else if( pos.y() == electricBottom )
2207 border = ElectricBottom;
2208 else
2209 abort();
2211 if( electric_windows[ border ] == None )
2212 return;
2214 if ((electric_current_border == border) &&
2215 (timestampDiff(electric_time_last, now) < treshold_reset) &&
2216 ((pos-electric_push_point).manhattanLength() < distance_reset))
2218 electric_time_last = now;
2220 if (timestampDiff(electric_time_first, now) > treshold_set)
2222 electric_current_border = ElectricNone;
2223 if( effects && static_cast<EffectsHandlerImpl*>(effects)->borderActivated( border ))
2224 {} // handled by effects
2225 else
2226 electricBorderSwitchDesktop( border, pos );
2227 return;
2230 else
2232 electric_current_border = border;
2233 electric_time_first = now;
2234 electric_time_last = now;
2235 electric_push_point = pos;
2238 // reset the pointer to find out wether the user is really pushing
2239 // (the direction back from which it came, starting from top clockwise)
2240 const int xdiff[ ELECTRIC_COUNT ] = { 0, -1, -1, -1, 0, 1, 1, 1 };
2241 const int ydiff[ ELECTRIC_COUNT ] = { 1, 1, 0, -1, -1, -1, 0, 1 };
2242 QCursor::setPos( pos.x() + xdiff[ border ], pos.y() + ydiff[ border ] );
2245 void Workspace::electricBorderSwitchDesktop( ElectricBorder border, const QPoint& _pos )
2247 QPoint pos = _pos;
2248 int desk = currentDesktop();
2249 const int OFFSET = 2;
2250 if( border == ElectricLeft || border == ElectricTopLeft || border == ElectricBottomLeft )
2252 desk = desktopToLeft( desk, options->rollOverDesktops );
2253 pos.setX( displayWidth() - 1 - OFFSET );
2255 if( border == ElectricRight || border == ElectricTopRight || border == ElectricBottomRight )
2257 desk = desktopToRight( desk, options->rollOverDesktops );
2258 pos.setX( OFFSET );
2260 if( border == ElectricTop || border == ElectricTopLeft || border == ElectricTopRight )
2262 desk = desktopUp( desk, options->rollOverDesktops );
2263 pos.setY( displayHeight() - 1 - OFFSET );
2265 if( border == ElectricBottom || border == ElectricBottomLeft || border == ElectricBottomRight )
2267 desk = desktopDown( desk, options->rollOverDesktops );
2268 pos.setY( OFFSET );
2270 int desk_before = currentDesktop();
2271 setCurrentDesktop( desk );
2272 if( currentDesktop() != desk_before )
2273 QCursor::setPos( pos );
2276 // this function is called when the user entered an electric border
2277 // with the mouse. It may switch to another virtual desktop
2278 bool Workspace::electricBorderEvent(XEvent *e)
2280 if( e->type == EnterNotify )
2282 for( int i = 0;
2283 i < ELECTRIC_COUNT;
2284 ++i )
2285 if( electric_windows[ i ] != None && e->xcrossing.window == electric_windows[ i ] )
2286 { // the user entered an electric border
2287 checkElectricBorder( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time );
2288 return true;
2291 if( e->type == ClientMessage )
2293 if( e->xclient.message_type == atoms->xdnd_position )
2295 for( int i = 0;
2296 i < ELECTRIC_COUNT;
2297 ++i )
2298 if( electric_windows[ i ] != None && e->xclient.window == electric_windows[ i ] )
2300 updateXTime();
2301 checkElectricBorder( QPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), xTime() );
2302 return true;
2306 return false;
2309 void Workspace::addTopMenu( Client* c )
2311 assert( c->isTopMenu());
2312 assert( !topmenus.contains( c ));
2313 topmenus.append( c );
2314 if( managingTopMenus())
2316 int minsize = c->minSize().height();
2317 if( minsize > topMenuHeight())
2319 topmenu_height = minsize;
2320 updateTopMenuGeometry();
2322 updateTopMenuGeometry( c );
2323 updateCurrentTopMenu();
2325 // kDebug() << "NEW TOPMENU:" << c;
2328 void Workspace::removeTopMenu( Client* c )
2330 // if( c->isTopMenu())
2331 // kDebug() << "REMOVE TOPMENU:" << c;
2332 assert( c->isTopMenu());
2333 assert( topmenus.contains( c ));
2334 topmenus.removeAll( c );
2335 updateCurrentTopMenu();
2336 // TODO reduce topMenuHeight() if possible?
2339 void Workspace::lostTopMenuSelection()
2341 // kDebug() << "lost TopMenu selection";
2342 // make sure this signal is always set when not owning the selection
2343 disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
2344 connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
2345 if( !managing_topmenus )
2346 return;
2347 connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
2348 disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
2349 managing_topmenus = false;
2350 delete topmenu_space;
2351 topmenu_space = NULL;
2352 updateClientArea();
2353 for( ClientList::ConstIterator it = topmenus.begin();
2354 it != topmenus.end();
2355 ++it )
2356 (*it)->checkWorkspacePosition();
2359 void Workspace::lostTopMenuOwner()
2361 if( !options->topMenuEnabled())
2362 return;
2363 // kDebug() << "TopMenu selection lost owner";
2364 if( !topmenu_selection->claim( false ))
2366 // kDebug() << "Failed to claim TopMenu selection";
2367 return;
2369 // kDebug() << "claimed TopMenu selection";
2370 setupTopMenuHandling();
2373 void Workspace::setupTopMenuHandling()
2375 if( managing_topmenus )
2376 return;
2377 connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
2378 disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
2379 managing_topmenus = true;
2380 topmenu_space = new QWidget;
2381 Window stack[ 2 ];
2382 stack[ 0 ] = supportWindow->winId();
2383 stack[ 1 ] = topmenu_space->winId();
2384 XRestackWindows(display(), stack, 2);
2385 updateTopMenuGeometry();
2386 topmenu_space->show();
2387 updateClientArea();
2388 updateCurrentTopMenu();
2391 int Workspace::topMenuHeight() const
2393 if( topmenu_height == 0 )
2394 { // simply create a dummy menubar and use its preffered height as the menu height
2395 KMenuBar tmpmenu;
2396 tmpmenu.addAction( "dummy" );
2397 topmenu_height = tmpmenu.sizeHint().height();
2399 return topmenu_height;
2402 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
2404 return mgr->createDecoration( bridge );
2407 QString Workspace::desktopName( int desk ) const
2409 return QString::fromUtf8( rootInfo->desktopName( desk ) );
2412 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data )
2414 return startup->checkStartup( w, id, data ) == KStartupInfo::Match;
2418 Puts the focus on a dummy window
2419 Just using XSetInputFocus() with None would block keyboard input
2421 void Workspace::focusToNull()
2423 XSetInputFocus(display(), null_focus_window, RevertToPointerRoot, xTime() );
2426 void Workspace::helperDialog( const QString& message, const Client* c )
2428 QStringList args;
2429 QString type;
2430 if( message == "noborderaltf3" )
2432 KAction* action = qobject_cast<KAction*>(keys->action( "Window Operations Menu" ));
2433 if (action==0) assert( false );
2434 QString shortcut = QString( "%1 (%2)" ).arg( action->text() )
2435 .arg( action->globalShortcut().primary().toString());
2436 args << "--msgbox" <<
2437 i18n( "You have selected to show a window without its border.\n"
2438 "Without the border, you will not be able to enable the border "
2439 "again using the mouse: use the window operations menu instead, "
2440 "activated using the %1 keyboard shortcut." ,
2441 shortcut );
2442 type = "altf3warning";
2444 else if( message == "fullscreenaltf3" )
2446 KAction* action = qobject_cast<KAction*>(keys->action( "Window Operations Menu" ));
2447 if (action==0) assert( false );
2448 QString shortcut = QString( "%1 (%2)" ).arg( action->text() )
2449 .arg( action->globalShortcut().primary().toString());
2450 args << "--msgbox" <<
2451 i18n( "You have selected to show a window in fullscreen mode.\n"
2452 "If the application itself does not have an option to turn the fullscreen "
2453 "mode off you will not be able to disable it "
2454 "again using the mouse: use the window operations menu instead, "
2455 "activated using the %1 keyboard shortcut." ,
2456 shortcut );
2457 type = "altf3warning";
2459 else
2460 assert( false );
2461 if( !type.isEmpty())
2463 KConfig cfg( "kwin_dialogsrc" );
2464 KConfigGroup cg(&cfg, "Notification Messages" ); // this depends on KMessageBox
2465 if( !cg.readEntry( type, true )) // has don't show again checked
2466 return; // save launching kdialog
2467 args <<"--dontagain" << "kwin_dialogsrc:" + type;
2469 if( c != NULL )
2470 args <<"--embed" << QString::number( c->window());
2471 KProcess::startDetached("kdialog",args);
2474 void Workspace::setShowingDesktop( bool showing )
2476 rootInfo->setShowingDesktop( showing );
2477 showing_desktop = showing;
2478 ++block_showing_desktop;
2479 if( showing_desktop )
2481 showing_desktop_clients.clear();
2482 ++block_focus;
2483 ClientList cls = stackingOrder();
2484 // find them first, then minimize, otherwise transients may get minimized with the window
2485 // they're transient for
2486 for( ClientList::ConstIterator it = cls.begin();
2487 it != cls.end();
2488 ++it )
2490 if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow())
2491 showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker
2493 for( ClientList::ConstIterator it = showing_desktop_clients.begin();
2494 it != showing_desktop_clients.end();
2495 ++it )
2496 (*it)->minimize();
2497 --block_focus;
2498 if( Client* desk = findDesktop( true, currentDesktop()))
2499 requestFocus( desk );
2501 else
2503 for( ClientList::ConstIterator it = showing_desktop_clients.begin();
2504 it != showing_desktop_clients.end();
2505 ++it )
2506 (*it)->unminimize();
2507 if( showing_desktop_clients.count() > 0 )
2508 requestFocus( showing_desktop_clients.first());
2509 showing_desktop_clients.clear();
2511 --block_showing_desktop;
2514 // Following Kicker's behavior:
2515 // Changing a virtual desktop resets the state and shows the windows again.
2516 // Unminimizing a window resets the state but keeps the windows hidden (except
2517 // the one that was unminimized).
2518 // A new window resets the state and shows the windows again, with the new window
2519 // being active. Due to popular demand (#67406) by people who apparently
2520 // don't see a difference between "show desktop" and "minimize all", this is not
2521 // true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
2522 // a new window resets the state but doesn't show windows.
2523 void Workspace::resetShowingDesktop( bool keep_hidden )
2525 if( block_showing_desktop > 0 )
2526 return;
2527 rootInfo->setShowingDesktop( false );
2528 showing_desktop = false;
2529 ++block_showing_desktop;
2530 if( !keep_hidden )
2532 for( ClientList::ConstIterator it = showing_desktop_clients.begin();
2533 it != showing_desktop_clients.end();
2534 ++it )
2535 (*it)->unminimize();
2537 showing_desktop_clients.clear();
2538 --block_showing_desktop;
2541 // Activating/deactivating this feature works like this:
2542 // When nothing is active, and the shortcut is pressed, global shortcuts are disabled
2543 // (using global_shortcuts_disabled)
2544 // When a window that has disabling forced is activated, global shortcuts are disabled.
2545 // (using global_shortcuts_disabled_for_client)
2546 // When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
2547 // or for a client), they are enabled again.
2548 void Workspace::slotDisableGlobalShortcuts()
2550 if( global_shortcuts_disabled || global_shortcuts_disabled_for_client )
2551 disableGlobalShortcuts( false );
2552 else
2553 disableGlobalShortcuts( true );
2556 static bool pending_dfc = false;
2558 void Workspace::disableGlobalShortcutsForClient( bool disable )
2560 if( global_shortcuts_disabled_for_client == disable )
2561 return;
2562 if( !global_shortcuts_disabled )
2564 if( disable )
2565 pending_dfc = true;
2566 KGlobalSettings::self()->emitChange( KGlobalSettings::BlockShortcuts, disable );
2567 // kwin will get the kipc message too
2571 void Workspace::disableGlobalShortcuts( bool disable )
2573 KGlobalSettings::self()->emitChange( KGlobalSettings::BlockShortcuts, disable );
2574 // kwin will get the kipc message too
2577 void Workspace::slotBlockShortcuts( int data )
2579 if( pending_dfc && data )
2581 global_shortcuts_disabled_for_client = true;
2582 pending_dfc = false;
2584 else
2586 global_shortcuts_disabled = data;
2587 global_shortcuts_disabled_for_client = false;
2589 // update also Alt+LMB actions etc.
2590 for( ClientList::ConstIterator it = clients.begin();
2591 it != clients.end();
2592 ++it )
2593 (*it)->updateMouseGrab();
2596 // Optimized version of QCursor::pos() that tries to avoid X roundtrips
2597 // by updating the value only when the X timestamp changes.
2598 static QPoint last_cursor_pos;
2599 static int last_buttons = 0;
2600 static Time last_cursor_timestamp = CurrentTime;
2602 QPoint Workspace::cursorPos() const
2604 if( last_cursor_timestamp == CurrentTime
2605 || last_cursor_timestamp != QX11Info::appTime())
2607 last_cursor_timestamp = QX11Info::appTime();
2608 Window root;
2609 Window child;
2610 int root_x, root_y, win_x, win_y;
2611 uint state;
2612 XQueryPointer( display(), rootWindow(), &root, &child,
2613 &root_x, &root_y, &win_x, &win_y, &state );
2614 last_cursor_pos = QPoint( root_x, root_y );
2615 last_buttons = state;
2616 QTimer::singleShot( 0, const_cast< Workspace* >( this ), SLOT( resetCursorPosTime()));
2618 return last_cursor_pos;
2621 // Because of QTimer's and the impossibility to get events for all mouse
2622 // movements (at least I haven't figured out how) the position needs
2623 // to be also refetched after each return to the event loop.
2624 void Workspace::resetCursorPosTime()
2626 last_cursor_timestamp = CurrentTime;
2629 void Workspace::checkCursorPos()
2631 QPoint last = last_cursor_pos;
2632 int lastb = last_buttons;
2633 cursorPos(); // update if needed
2634 if( last != last_cursor_pos || lastb != last_buttons )
2635 static_cast< EffectsHandlerImpl* >( effects )->mouseChanged( cursorPos(), last,
2636 x11ToQtMouseButtons( last_buttons ), x11ToQtMouseButtons( lastb ),
2637 x11ToQtKeyboardModifiers( last_buttons ), x11ToQtKeyboardModifiers( lastb ));
2640 } // namespace
2642 #include "workspace.moc"