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>
38 #include <kglobalaccel.h>
39 #include <QToolButton>
40 #include <kactioncollection.h>
42 #include <kconfiggroup.h>
43 #include <QtDBus/QtDBus>
46 #include "popupinfo.h"
49 #include "placement.h"
50 #include "notifications.h"
53 #include "kwinadaptor.h"
54 #include "unmanaged.h"
59 #include <X11/extensions/shape.h>
60 #include <X11/keysym.h>
61 #include <X11/keysymdef.h>
62 #include <X11/cursorfont.h>
65 #include <kauthorized.h>
66 #include <ktoolinvocation.h>
67 #include <kglobalsettings.h>
69 #include <kephal/screens.h>
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
86 //-----------------------------------------------------------------------------
88 Workspace::Workspace( bool restore
)
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 )
97 , last_active_client( 0 )
98 , most_recently_raised( 0 )
100 , pending_take_activity( NULL
)
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 )
111 , mouse_emulation( false )
116 , advanced_popup( 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 )
128 , layoutOrientation( Qt::Vertical
)
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 )
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() ));
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
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(),
194 SubstructureRedirectMask
|
195 SubstructureNotifyMask
|
196 FocusChangeMask
| // For NotifyDetailNone
213 (unsigned char*)( &data
),
217 client_keys
= new KActionCollection( this );
219 tab_box
= new TabBox( this );
220 popupinfo
= new PopupInfo( this );
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();
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] =
252 NET::SupportingWMCheck
|
254 NET::ClientListStacking
|
255 NET::DesktopGeometry
|
256 NET::NumberOfDesktops
|
257 NET::CurrentDesktop
|
268 NET::WMIconGeometry
|
272 NET::WMFrameExtents
|
285 // No compositing window types here unless we support them also as managed window types
289 //NET::Sticky | // Large desktops not supported (and probably never will be)
295 //NET::StaysOnTop | // The same like KeepAbove
300 NET::DemandsAttention
|
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
|
318 NET::ActionMinimize
|
320 //NET::ActionStick | // Sticky state is not supported
322 NET::ActionMaxHoriz
|
323 NET::ActionFullScreen
|
324 NET::ActionChangeDesktop
|
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
);
338 if( !kapp
->isSessionRestored() )
339 initial_desktop
= client_info
.currentDesktop();
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
);
365 if( !kapp
->isSessionRestored() )
366 ++block_focus
; // Because it will be set below
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()
381 lostTopMenuSelection();
383 unsigned int i
, nwins
;
384 Window root_return
, parent_return
;
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
] );
396 if( topmenu_space
&& topmenu_space
->winId() == wins
[i
] )
398 if( attr
.map_state
!= IsUnmapped
)
399 createClient( wins
[i
], true );
402 XFree( (void*)( wins
));
404 // Propagate clients, will really happen at the end of the updates blocker block
405 updateStackingOrder( true );
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
);
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() )
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
441 workspaceInit
= false;
443 // TODO: ungrabXServer()
446 Workspace::~Workspace()
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();
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()
463 clients
.removeAll( *it
);
464 desktops
.removeAll( *it
);
466 for( UnmanagedList::ConstIterator it
= unmanaged
.constBegin();
467 it
!= unmanaged
.constEnd();
473 XDeleteProperty( display(), rootWindow(), atoms
->kwin_running
);
476 KGlobal::config()->sync();
479 delete supportWindow
;
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();
492 foreach( SessionInfo
* s
, session
)
494 XDestroyWindow( display(), null_focus_window
);
496 // TODO: ungrabXServer();
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
);
510 addClient( c
, Allowed
);
512 scene
->windowAdded( c
);
514 static_cast<EffectsHandlerImpl
*>( effects
)->windowAdded( c
->effectWindow() );
518 Unmanaged
* Workspace::createUnmanaged( Window w
)
522 Unmanaged
* c
= new Unmanaged( this );
525 Unmanaged::deleteUnmanaged( c
, Allowed
);
528 addUnmanaged( c
, Allowed
);
530 scene
->windowAdded( c
);
532 static_cast<EffectsHandlerImpl
*>( effects
)->windowAdded( c
->effectWindow() );
536 void Workspace::addClient( Client
* c
, allowed_t
)
538 Group
* grp
= findGroup( c
->window() );
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
550 updateFocusChains( c
, FocusChainUpdate
); // Add to focus chain if not already there
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
559 x_stacking_dirty
= true;
560 updateClientArea(); // This cannot be in manage(), because the client got added only now
561 updateClientLayer( 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();
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
)
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
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
);
622 Group
* group
= findGroup( c
->window());
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
)
637 updateStackingOrder( true );
640 tab_box
->reset( true );
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
));
656 x_stacking_dirty
= true;
659 void Workspace::removeDeleted( Deleted
* c
, allowed_t
)
661 assert( deleted
.contains( c
));
663 scene
->windowDeleted( c
);
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
);
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
);
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
);
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
);
724 focus_chain
[i
].append( c
); // Otherwise add as the first one
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
);
747 global_focus_chain
.append( c
); // Otherwise add as the first one
751 void Workspace::updateCurrentTopMenu()
753 if( !managingTopMenus() )
755 // toplevel menubar handling
757 bool block_desktop_menubar
= false;
760 // Show the new menu bar first...
761 Client
* menu_client
= active_client
;
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();
769 if( (*it
)->isTopMenu() )
774 if( menubar
!= NULL
|| !menu_client
->isTransient() )
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();
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();
785 if( (*it
)->isTopMenu() )
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();
801 if( (*it
)->isTopMenu() )
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();
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() )
826 //kDebug( 1212 ) << "CURRENT TOPMENU:" << menubar << ":" << active_client;
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();
859 (*it
)->hideClient( false );
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())
870 if( client
->groupTransient())
872 group
= client
->group();
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();
886 if( (*it
)->isUtility() || (*it
)->isMenu() || (*it
)->isToolbar() )
889 if( !(*it
)->isTransient() )
891 if( (*it
)->group()->members().count() == 1 ) // Has its own group, keep always visible
893 else if( client
!= NULL
&& (*it
)->group() == client
->group() )
900 if( group
!= NULL
&& (*it
)->group() == group
)
902 else if( client
!= NULL
&& client
->hasTransient( (*it
), true ))
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())
914 for( ClientList::ConstIterator it2
= mainclients
.constBegin();
915 it2
!= mainclients
.constEnd();
918 if( (*it2
)->isSpecialWindow() )
922 to_hide
.append( *it
);
925 to_show
.append( *it
);
927 } // First show new ones, then hide
928 for( int i
= to_show
.size() - 1;
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 );
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()
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() )
986 reconfigureTimer
.stop();
988 return compositingActive();
991 void Workspace::slotSettingsChanged( int category
)
993 kDebug( 1212 ) << "Workspace::slotSettingsChanged()";
994 if( category
== KGlobalSettings::SETTINGS_SHORTCUTS
)
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 );
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
1026 //curtain.setBackgroundMode( NoBackground );
1027 //curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
1030 for( ClientList::ConstIterator it
= clients
.constBegin();
1031 it
!= clients
.constEnd();
1033 (*it
)->updateDecoration( true, true );
1034 mgr
->destroyPreviousPlugin();
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();
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
)
1069 if( effects
) // setupCompositing() may fail
1070 effects
->reconfigure();
1074 finishCompositing();
1077 for( ClientList::Iterator it
= clients
.begin();
1078 it
!= clients
.end();
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();
1099 if( effects
) // setupCompositing() may fail
1100 effects
->reconfigure();
1103 void Workspace::loadDesktopSettings()
1105 KSharedConfig::Ptr c
= KGlobal::config();
1107 if( screen_number
== 0 )
1108 groupname
= "Desktops";
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
;
1116 workarea
.resize( n
+ 1 );
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();
1134 if (screen_number
== 0)
1135 groupname
= "Desktops";
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
);
1148 rootInfo
->setDesktopName( i
, s
.toUtf8().data() );
1151 if( s
!= defaultvalue
)
1153 group
.writeEntry( QString( "Name_%1" ).arg( i
), s
);
1157 QString currentvalue
= group
.readEntry( QString( "Name_%1" ).arg( i
), QString() );
1158 if( currentvalue
!= defaultvalue
)
1159 group
.writeEntry( QString( "Name_%1" ).arg( i
), "" );
1167 QStringList
Workspace::configModules( bool controlCenter
)
1170 args
<< "kwindecoration";
1172 args
<< "kwinoptions";
1173 else if( KAuthorized::authorizeControlModule( "kde-kwinoptions.desktop" ))
1174 args
<< "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced"
1175 << "kwinrules" << "kwincompositing";
1179 void Workspace::configureWM()
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
)
1202 if( r
.indexIn(title
) != -1 )
1204 doNotManageList
.erase( it
);
1212 * Refreshes all the client windows
1214 void Workspace::refresh()
1216 QWidget
w( NULL
, Qt::X11BypassWindowManagerHint
);
1217 w
.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
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
1233 ~ObscuringWindows();
1234 void create( Client
* c
);
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
)
1247 return; // Not needed with compositing
1249 cached
= new QList
<Window
>;
1251 XWindowChanges chngs
;
1252 int mask
= CWSibling
| CWStackMode
;
1253 if( cached
->count() > 0 )
1255 cached
->removeAll( obs_win
= cached
->first() );
1258 chngs
.width
= c
->width();
1259 chngs
.height
= c
->height();
1260 mask
|= CWX
| CWY
| CWWidth
| CWHeight
;
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();
1285 XUnmapWindow( display(), *it
);
1286 if( cached
->count() < int( max_cache_size
))
1287 cached
->prepend( *it
);
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
)
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();
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
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
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
);
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() )
1375 if( c
== NULL
&& !desktops
.isEmpty() )
1376 c
= findDesktop( true, currentDesktop() );
1378 if( c
!= active_client
)
1379 setActiveClient( NULL
, Allowed
);
1383 else if( !desktops
.isEmpty() )
1384 requestFocus( findDesktop( true, currentDesktop() ));
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
);
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
1436 Qt::Orientation orientation
;
1437 calcDesktopLayout( &x
, &y
, &orientation
);
1438 int dt
= desktop
- 1;
1439 if( orientation
== Qt::Vertical
)
1442 if( dt
>= numberOfDesktops() )
1445 dt
-= numberOfDesktops();
1452 int d
= ( dt
% x
) + 1;
1460 dt
= dt
- ( dt
% x
) + d
;
1465 int Workspace::desktopToLeft( int desktop
, bool wrap
) const
1468 Qt::Orientation orientation
;
1469 calcDesktopLayout( &x
, &y
, &orientation
);
1470 int dt
= desktop
- 1;
1471 if( orientation
== Qt::Vertical
)
1477 dt
+= numberOfDesktops();
1484 int d
= ( dt
% x
) - 1;
1492 dt
= dt
- ( dt
% x
) + d
;
1497 int Workspace::desktopUp( int desktop
, bool wrap
) const
1500 Qt::Orientation orientation
;
1501 calcDesktopLayout( &x
, &y
, &orientation
);
1502 int dt
= desktop
- 1;
1503 if( orientation
== Qt::Horizontal
)
1509 dt
+= numberOfDesktops();
1516 int d
= ( dt
% y
) - 1;
1524 dt
= dt
- ( dt
% y
) + d
;
1529 int Workspace::desktopDown( int desktop
, bool wrap
) const
1532 Qt::Orientation orientation
;
1533 calcDesktopLayout( &x
, &y
, &orientation
);
1534 int dt
= desktop
- 1;
1535 if( orientation
== Qt::Horizontal
)
1538 if( dt
>= numberOfDesktops() )
1541 dt
-= numberOfDesktops();
1548 int d
= ( dt
% y
) + 1;
1556 dt
= dt
- ( dt
% y
) + d
;
1562 * Sets the number of virtual desktops to \a n
1564 void Workspace::setNumberOfDesktops( int n
)
1566 if( n
== number_of_desktops
)
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
);
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();
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
);
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
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
1634 restackClientUnderActive( c
);
1639 ClientList transients_stacking_order
= ensureStackingOrder( c
->transients() );
1640 for( ClientList::ConstIterator it
= transients_stacking_order
.constBegin();
1641 it
!= transients_stacking_order
.constEnd();
1643 sendClientToDesktop( *it
, desk
, dont_activate
);
1647 int Workspace::numScreens() const
1649 if( !options
->xineramaEnabled
)
1651 return Kephal::ScreenUtils::numScreens();
1654 int Workspace::activeScreen() const
1656 if( !options
->xineramaEnabled
)
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
)
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
)
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
)
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
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();
1720 sendClientToScreen( *it
, screen
);
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
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
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
;
1751 *orientation
= layoutOrientation
;
1754 void Workspace::killWindowId( Window window_to_kill
)
1756 if( window_to_kill
== None
)
1758 Window window
= window_to_kill
;
1759 Client
* client
= NULL
;
1762 client
= findClient( FrameIdMatchPredicate( window
));
1763 if( client
!= NULL
)
1764 break; // Found the client
1765 Window parent
, root
;
1767 unsigned int children_count
;
1768 XQueryTree( display(), window
, &root
, &parent
, &children
, &children_count
);
1769 if( children
!= NULL
)
1771 if( window
== root
) // We didn't find the client, probably an override-redirect window
1773 window
= parent
; // Go up
1775 if( client
!= NULL
)
1776 client
->killWindow();
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.
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
1814 // Create a QRegion from the rectangles describing the bounding mask.
1816 for( int pos
= 0; pos
< count
; pos
++ )
1817 contents
+= QRegion( rects
[pos
].x
, rects
[pos
].y
,
1818 rects
[pos
].width
, rects
[pos
].height
);
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
);
1835 snapshot
.setMask( mask
);
1839 QClipboard
* cb
= QApplication::clipboard();
1840 cb
->setPixmap( snapshot
);
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();
1857 * Invokes keyboard mouse emulation
1859 void Workspace::slotMouseEmulation()
1861 if( mouse_emulation
)
1864 mouse_emulation
= false;
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()
1885 Window child
= rootWindow();
1886 int root_x
, root_y
, lx
, ly
;
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
);
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
)
1911 QWidget
* widget
= QWidget::find( w
);
1912 if(( !widget
|| qobject_cast
<QToolButton
*>( widget
)) && !findClient( WindowMatchPredicate( w
)))
1916 XTranslateCoordinates( display(), rootWindow(), w
, pos
.x(), pos
.y(), &x
, &y
, &xw
);
1917 if( type
== EmuMove
)
1918 { // Motion notify events
1920 e
.type
= MotionNotify
;
1921 e
.xmotion
.window
= w
;
1922 e
.xmotion
.root
= rootWindow();
1923 e
.xmotion
.subwindow
= w
;
1924 e
.xmotion
.time
= xTime();
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
);
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();
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
)
1954 state
|= Button2Mask
;
1957 state
|= Button3Mask
;
1960 state
|= Button1Mask
;
1969 state
&= ~Button2Mask
;
1972 state
&= ~Button3Mask
;
1975 state
&= ~Button1Mask
;
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();
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
);
2024 mouse_emulation_state
= sendFakedMouseEvent( pos
, mouse_emulation_window
,
2025 EmuRelease
, Button1
, mouse_emulation_state
);
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
);
2034 mouse_emulation_state
= sendFakedMouseEvent( pos
, mouse_emulation_window
,
2035 EmuRelease
, Button2
, mouse_emulation_state
);
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
);
2044 mouse_emulation_state
= sendFakedMouseEvent( pos
, mouse_emulation_window
,
2045 EmuRelease
, Button3
, mouse_emulation_state
);
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
);
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
);
2076 mouse_emulation
= 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
);
2091 * Delayed focus functions
2093 void Workspace::delayFocus()
2095 requestFocus( delayfocus_client
);
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 //-----------------------------------------------------------------------------
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
;
2143 if( electric_windows
[pos
] != None
)
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
)
2186 reserveElectricBorder( static_cast<ElectricBorder
>( pos
));
2188 unreserveElectricBorder( static_cast<ElectricBorder
>( pos
));
2191 void Workspace::reserveElectricBorder( ElectricBorder border
)
2193 if( border
== ElectricNone
)
2195 if( electric_reserved
[border
]++ == 0 )
2196 QTimer::singleShot( 0, this, SLOT( updateElectricBorders() ));
2199 void Workspace::unreserveElectricBorder( ElectricBorder border
)
2201 if( border
== ElectricNone
)
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
))
2216 bool have_borders
= false;
2217 for( int i
= 0; i
< ELECTRIC_COUNT
; ++i
)
2218 if( electric_windows
[i
] != None
)
2219 have_borders
= true;
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
;
2247 if( electric_windows
[border
] == None
)
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
2262 electricBorderSwitchDesktop( border
, pos
);
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
)
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
);
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
);
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
);
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
] )
2335 checkElectricBorder( QPoint(
2336 e
->xclient
.data
.l
[2]>>16, e
->xclient
.data
.l
[2]&0xffff), xTime() );
2344 //-----------------------------------------------------------------------------
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
)
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
;
2394 for( ClientList::ConstIterator it
= topmenus
.constBegin();
2395 it
!= topmenus
.constEnd();
2397 (*it
)->checkWorkspacePosition();
2400 void Workspace::lostTopMenuOwner()
2402 if( !options
->topMenuEnabled())
2404 //kDebug( 1212 ) << "TopMenu selection lost owner";
2405 if( !topmenu_selection
->claim( false ))
2407 //kDebug( 1212 ) << "Failed to claim TopMenu selection";
2410 //kDebug( 1212 ) << "Claimed TopMenu selection";
2411 setupTopMenuHandling();
2414 void Workspace::setupTopMenuHandling()
2416 if( managing_topmenus
)
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
);
2423 stack
[0] = supportWindow
->winId();
2424 stack
[1] = topmenu_space
->winId();
2425 XRestackWindows( display(), stack
, 2 );
2426 updateTopMenuGeometry();
2427 topmenu_space
->show();
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
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();
2456 for( Ability ab
= ABILITYCOLOR_FIRST
;
2457 ab
< ABILITYCOLOR_END
;
2458 ab
= static_cast<Ability
>( ab
+ 1 ))
2459 if( factory
->supports( ab
))
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
)
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.",
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.",
2514 type
= "altf3warning";
2518 if( !type
.isEmpty() )
2520 KConfig
cfg( "kwin_dialogsrc" );
2521 KConfigGroup
cg(&cfg
, "Notification Messages" ); // Depends on KMessageBox
2522 if( !cg
.readEntry( type
, true ))
2524 args
<< "--dontagain" << "kwin_dialogsrc:" + type
;
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();
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();
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();
2553 if( Client
* desk
= findDesktop( true, currentDesktop() ))
2554 requestFocus( desk
);
2558 for( ClientList::ConstIterator it
= showing_desktop_clients
.constBegin();
2559 it
!= showing_desktop_clients
.constEnd();
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 )
2584 rootInfo
->setShowingDesktop( false );
2585 showing_desktop
= false;
2586 ++block_showing_desktop
;
2589 for( ClientList::ConstIterator it
= showing_desktop_clients
.constBegin();
2590 it
!= showing_desktop_clients
.constEnd();
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 );
2612 disableGlobalShortcuts( true );
2615 static bool pending_dfc
= false;
2617 void Workspace::disableGlobalShortcutsForClient( bool disable
)
2619 if( global_shortcuts_disabled_for_client
== disable
)
2621 if( !global_shortcuts_disabled
)
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;
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();
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();
2670 int root_x
, root_y
, win_x
, win_y
;
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
));
2711 #include "workspace.moc"