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 <kcmdlineargs.h>
44
#include <QtDBus/QtDBus>
49
#include "desktopchangeosd.h"
51
#include "placement.h"
52
#include "notifications.h"
56
#include "kwinadaptor.h"
57
#include "unmanaged.h"
61
#include "tilinglayout.h"
63
#include "scripting/scripting.h"
65
#include <X11/extensions/shape.h>
66
#include <X11/keysym.h>
67
#include <X11/keysymdef.h>
68
#include <X11/cursorfont.h>
71
#include <kauthorized.h>
72
#include <ktoolinvocation.h>
73
#include <kglobalsettings.h>
74
#include <kwindowsystem.h>
75
#include <kwindowinfo.h>
77
#include <kephal/screens.h>
82
extern int screen_number;
83
static const int KWIN_MAX_NUMBER_DESKTOPS = 20;
85
Workspace* Workspace::_self = 0;
87
//-----------------------------------------------------------------------------
88
// Rikkus: This class is too complex. It needs splitting further.
89
// It's a nightmare to understand, especially with so few comments :(
91
// Matthias: Feel free to ask me questions about it. Feel free to add
92
// comments. I dissagree that further splittings makes it easier. 2500
93
// lines are not too much. It's the task that is complex, not the
95
//-----------------------------------------------------------------------------
97
Workspace::Workspace(bool restore)
100
, desktopCount_(0) // This is an invalid state
101
, desktopGridSize_(1, 2) // Default to two rows
102
, desktopGrid_(new int[2])
104
, tilingEnabled_(false)
107
, active_popup_client(NULL)
108
, temporaryRulesMessages("_KDE_NET_WM_TEMPORARY_RULES", NULL, false)
109
, rules_updates_disabled(false)
111
, last_active_client(0)
112
, most_recently_raised(0)
114
, pending_take_activity(NULL)
116
, delayfocus_client(0)
117
, force_restacking(false)
118
, x_stacking_dirty(true)
119
, showing_desktop(false)
120
, block_showing_desktop(0)
121
, was_user_interaction(false)
122
, session_saving(false)
123
, control_grab(false)
125
, mouse_emulation(false)
128
, desktop_change_osd(0)
135
, switch_to_tab_popup(0)
138
, client_keys_dialog(NULL)
139
, client_keys_client(NULL)
140
, disable_shortcuts_keys(NULL)
141
, global_shortcuts_disabled(false)
142
, global_shortcuts_disabled_for_client(false)
143
, workspaceInit(true)
145
, managing_topmenus(false)
146
, topmenu_selection(NULL)
147
, topmenu_watcher(NULL)
149
, topmenu_space(NULL)
150
, set_active_client_recursion(0)
151
, block_stacking_updates(0)
152
, forced_global_mouse_grab(false)
154
, compositingSuspended(false)
155
, compositingBlocked(false)
158
, overlay_visible(true)
159
, overlay_shown(false)
162
, forceUnredirectCheck(true)
163
, m_finishingCompositing(false)
165
(void) new KWinAdaptor(this);
167
QDBusConnection dbus = QDBusConnection::sessionBus();
168
dbus.registerObject("/KWin", this);
169
dbus.connect(QString(), "/KWin", "org.kde.KWin", "reloadConfig",
170
this, SLOT(slotReloadConfig()));
171
dbus.connect(QString(), "/KWin", "org.kde.KWin", "reinitCompositing",
172
this, SLOT(slotReinitCompositing()));
174
// Initialize desktop grid array
181
default_colormap = DefaultColormap(display(), info.screen());
182
installed_colormap = default_colormap;
184
for (int i = 0; i < ELECTRIC_COUNT; ++i) {
185
electric_reserved[i] = 0;
186
electric_windows[i] = None;
189
connect(&temporaryRulesMessages, SIGNAL(gotMessage(const QString&)),
190
this, SLOT(gotTemporaryRulesMessage(const QString&)));
191
connect(&rulesUpdatedTimer, SIGNAL(timeout()), this, SLOT(writeWindowRules()));
192
connect(&unredirectTimer, SIGNAL(timeout()), this, SLOT(delayedCheckUnredirect()));
193
connect(&compositeResetTimer, SIGNAL(timeout()), this, SLOT(resetCompositing()));
194
unredirectTimer.setSingleShot(true);
195
compositeResetTimer.setSingleShot(true);
197
updateXTime(); // Needed for proper initialization of user_time in Client ctor
206
// Call this before XSelectInput() on the root window
207
startup = new KStartupInfo(
208
KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this);
210
// Select windowmanager privileges
211
XSelectInput(display(), rootWindow(),
215
SubstructureRedirectMask |
216
SubstructureNotifyMask |
217
FocusChangeMask | // For NotifyDetailNone
222
compositingSuspended = !options->useCompositing;
223
// need to create the tabbox before compositing scene is setup
224
tab_box = new TabBox::TabBox(this);
237
(unsigned char*)(&data),
241
client_keys = new KActionCollection(this);
243
desktop_change_osd = new DesktopChangeOSD(this);
244
m_outline = new Outline();
248
connect(Kephal::Screens::self(), SIGNAL(screenAdded(Kephal::Screen*)), SLOT(desktopResized()));
249
connect(Kephal::Screens::self(), SIGNAL(screenRemoved(int)), SLOT(desktopResized()));
250
connect(Kephal::Screens::self(), SIGNAL(screenResized(Kephal::Screen*, QSize, QSize)), SLOT(desktopResized()));
251
connect(Kephal::Screens::self(), SIGNAL(screenMoved(Kephal::Screen*, QPoint, QPoint)), SLOT(desktopResized()));
253
connect(&activityController_, SIGNAL(currentActivityChanged(QString)), SLOT(updateCurrentActivity(QString)));
254
connect(&activityController_, SIGNAL(activityRemoved(QString)), SLOT(activityRemoved(QString)));
255
connect(&activityController_, SIGNAL(activityAdded(QString)), SLOT(activityAdded(QString)));
258
void Workspace::init()
260
reserveElectricBorderActions(true);
261
if (options->electricBorders() == Options::ElectricAlways)
262
reserveElectricBorderSwitching(true);
263
updateElectricBorders();
267
//maximizedWindowCounter = 0;
269
supportWindow = new QWidget(NULL, Qt::X11BypassWindowManagerHint);
270
XLowerWindow(display(), supportWindow->winId()); // See usage in layers.cpp
272
XSetWindowAttributes attr;
273
attr.override_redirect = 1;
274
null_focus_window = XCreateWindow(display(), rootWindow(), -1, -1, 1, 1, 0, CopyFromParent,
275
InputOnly, CopyFromParent, CWOverrideRedirect, &attr);
276
XMapWindow(display(), null_focus_window);
278
unsigned long protocols[5] = {
280
NET::SupportingWMCheck |
282
NET::ClientListStacking |
283
NET::DesktopGeometry |
284
NET::NumberOfDesktops |
285
NET::CurrentDesktop |
296
NET::WMIconGeometry |
300
NET::WMFrameExtents |
313
// No compositing window types here unless we support them also as managed window types
317
//NET::Sticky | // Large desktops not supported (and probably never will be)
323
//NET::StaysOnTop | // The same like KeepAbove
328
NET::DemandsAttention |
333
NET::WM2AllowedActions |
334
NET::WM2RestackWindow |
335
NET::WM2MoveResizeWindow |
336
NET::WM2ExtendedStrut |
337
NET::WM2KDETemporaryRules |
338
NET::WM2ShowingDesktop |
339
NET::WM2DesktopLayout |
340
NET::WM2FullPlacement |
341
NET::WM2FullscreenMonitors |
347
NET::ActionMinimize |
349
//NET::ActionStick | // Sticky state is not supported
351
NET::ActionMaxHoriz |
352
NET::ActionFullScreen |
353
NET::ActionChangeDesktop |
359
if (hasDecorationPlugin() && mgr->factory()->supports(AbilityExtendIntoClientArea))
360
protocols[ NETRootInfo::PROTOCOLS2 ] |= NET::WM2FrameOverlap;
363
rootInfo = new RootInfo(this, display(), supportWindow->winId(), "KWin", protocols, 5, info.screen());
365
loadDesktopSettings();
366
updateDesktopLayout();
367
desktop_change_osd->numberDesktopsChanged();
368
// Extra NETRootInfo instance in Client mode is needed to get the values of the properties
369
NETRootInfo client_info(display(), NET::ActiveWindow | NET::CurrentDesktop);
371
if (!kapp->isSessionRestored())
372
initial_desktop = client_info.currentDesktop();
374
KConfigGroup group(kapp->sessionConfig(), "Session");
375
initial_desktop = group.readEntry("desktop", 1);
377
if (!setCurrentDesktop(initial_desktop))
378
setCurrentDesktop(1);
379
allActivities_ = activityController_.listActivities();
380
updateCurrentActivity(activityController_.currentActivity());
382
// Now we know how many desktops we'll have, thus we initialize the positioning object
383
initPositioning = new Placement(this);
385
reconfigureTimer.setSingleShot(true);
386
updateToolWindowsTimer.setSingleShot(true);
388
connect(&reconfigureTimer, SIGNAL(timeout()), this, SLOT(slotReconfigure()));
389
connect(&updateToolWindowsTimer, SIGNAL(timeout()), this, SLOT(slotUpdateToolWindows()));
390
connect(&mousePollingTimer, SIGNAL(timeout()), SLOT(performMousePoll()));
392
connect(KGlobalSettings::self(), SIGNAL(appearanceChanged()), this, SLOT(reconfigure()));
393
connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int)));
394
connect(KGlobalSettings::self(), SIGNAL(blockShortcuts(int)), this, SLOT(slotBlockShortcuts(int)));
396
active_client = NULL;
397
rootInfo->setActiveWindow(None);
399
if (!kapp->isSessionRestored())
400
++block_focus; // Because it will be set below
403
sprintf(nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen(display()));
404
Atom topmenu_atom = XInternAtom(display(), nm, False);
405
topmenu_selection = new KSelectionOwner(topmenu_atom);
406
topmenu_watcher = new KSelectionWatcher(topmenu_atom);
407
//TODO: grabXServer(); // Where exactly put this? topmenu selection claiming down belong must be before
410
// Begin updates blocker block
411
StackingUpdatesBlocker blocker(this);
413
if (options->topMenuEnabled() && topmenu_selection->claim(false))
414
setupTopMenuHandling(); // This can call updateStackingOrder()
416
lostTopMenuSelection();
418
unsigned int i, nwins;
419
Window root_return, parent_return;
421
XQueryTree(display(), rootWindow(), &root_return, &parent_return, &wins, &nwins);
422
bool fixoffset = KCmdLineArgs::parsedArgs()->getOption("crashes").toInt() > 0;
423
for (i = 0; i < nwins; i++) {
424
XWindowAttributes attr;
425
XGetWindowAttributes(display(), wins[i], &attr);
426
if (attr.override_redirect) {
427
createUnmanaged(wins[i]);
430
if (topmenu_space && topmenu_space->winId() == wins[i])
432
if (attr.map_state != IsUnmapped) {
434
fixPositionAfterCrash(wins[ i ], attr);
435
createClient(wins[i], true);
439
XFree((void*)(wins));
441
// Propagate clients, will really happen at the end of the updates blocker block
442
updateStackingOrder(true);
446
// NETWM spec says we have to set it to (0,0) if we don't support it
447
NETPoint* viewports = new NETPoint[numberOfDesktops()];
448
rootInfo->setDesktopViewport(numberOfDesktops(), *viewports);
450
QRect geom = Kephal::ScreenUtils::desktopGeometry();
451
NETSize desktop_geometry;
452
desktop_geometry.width = geom.width();
453
desktop_geometry.height = geom.height();
454
rootInfo->setDesktopGeometry(-1, desktop_geometry);
455
setShowingDesktop(false);
457
} // End updates blocker block
459
Client* new_active_client = NULL;
460
if (!kapp->isSessionRestored()) {
462
new_active_client = findClient(WindowMatchPredicate(client_info.activeWindow()));
464
if (new_active_client == NULL
465
&& activeClient() == NULL && should_get_focus.count() == 0) {
466
// No client activated in manage()
467
if (new_active_client == NULL)
468
new_active_client = topClientOnDesktop(currentDesktop(), -1);
469
if (new_active_client == NULL && !desktops.isEmpty())
470
new_active_client = findDesktop(true, currentDesktop());
472
if (new_active_client != NULL)
473
activateClient(new_active_client);
475
// Enable/disable tiling
476
setTilingEnabled(options->tilingOn);
478
// SELI TODO: This won't work with unreasonable focus policies,
479
// and maybe in rare cases also if the selected client doesn't
481
workspaceInit = false;
483
// TODO: ungrabXServer()
486
Workspace::~Workspace()
489
blockStackingUpdates(true);
491
// TODO: grabXServer();
493
// Use stacking_order, so that kwin --replace keeps stacking order
494
for (ClientList::ConstIterator it = stacking_order.constBegin();
495
it != stacking_order.constEnd();
497
// Only release the window
498
(*it)->releaseWindow(true);
499
// No removeClient() is called, it does more than just removing.
500
// However, remove from some lists to e.g. prevent performTransiencyCheck()
502
clients.removeAll(*it);
503
desktops.removeAll(*it);
505
for (UnmanagedList::ConstIterator it = unmanaged.constBegin();
506
it != unmanaged.constEnd();
510
delete desktop_change_osd;
513
XDeleteProperty(display(), rootWindow(), atoms->kwin_running);
516
KGlobal::config()->sync();
519
delete supportWindow;
522
delete initPositioning;
523
delete topmenu_watcher;
524
delete topmenu_selection;
525
delete topmenu_space;
526
delete client_keys_dialog;
527
while (!rules.isEmpty()) {
528
delete rules.front();
531
foreach (SessionInfo * s, session)
533
XDestroyWindow(display(), null_focus_window);
535
// TODO: ungrabXServer();
537
delete[] desktopGrid_;
542
Client* Workspace::createClient(Window w, bool is_mapped)
544
StackingUpdatesBlocker blocker(this);
545
Client* c = new Client(this);
546
if (!c->manage(w, is_mapped)) {
547
Client::deleteClient(c, Allowed);
550
addClient(c, Allowed);
552
tilingLayouts.resize(numberOfDesktops() + 1);
557
scene->windowAdded(c);
561
Unmanaged* Workspace::createUnmanaged(Window w)
565
Unmanaged* c = new Unmanaged(this);
567
Unmanaged::deleteUnmanaged(c, Allowed);
570
addUnmanaged(c, Allowed);
572
scene->windowAdded(c);
573
emit unmanagedAdded(c);
577
void Workspace::addClient(Client* c, allowed_t)
579
Group* grp = findGroup(c->window());
581
KWindowInfo info = KWindowSystem::windowInfo(c->window(), -1U, NET::WM2WindowClass);
584
if (info.windowClassName() == QString("krunner")) {
585
SWrapper::Workspace* ws_object = KWin::Scripting::workspace();
586
if (ws_object != 0) {
587
ws_object->sl_killWindowCalled(c);
596
if (c->isDesktop()) {
598
if (active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
599
requestFocus(c); // TODO: Make sure desktop is active after startup if there's no other window active
601
updateFocusChains(c, FocusChainUpdate); // Add to focus chain if not already there
604
if (!unconstrained_stacking_order.contains(c))
605
unconstrained_stacking_order.append(c); // Raise if it hasn't got any stacking position yet
606
if (!stacking_order.contains(c)) // It'll be updated later, and updateToolWindows() requires
607
stacking_order.append(c); // c to be in stacking_order
610
x_stacking_dirty = true;
611
updateClientArea(); // This cannot be in manage(), because the client got added only now
612
updateClientLayer(c);
613
if (c->isDesktop()) {
615
// If there's no active client, make this desktop the active one
616
if (activeClient() == NULL && should_get_focus.count() == 0)
617
activateClient(findDesktop(true, currentDesktop()));
619
c->checkActiveModal();
620
checkTransients(c->window()); // SELI TODO: Does this really belong here?
621
updateStackingOrder(true); // Propagate new client
622
if (c->isUtility() || c->isMenu() || c->isToolbar())
623
updateToolWindows(true);
624
checkNonExistentClients();
626
tab_box->reset(true);
629
void Workspace::addUnmanaged(Unmanaged* c, allowed_t)
632
x_stacking_dirty = true;
636
* Destroys the client \a c
638
void Workspace::removeClient(Client* c, allowed_t)
640
emit clientRemoved(c);
642
if (c == active_popup_client)
645
if (client_keys_client == c)
646
setupWindowShortcutDone(false);
647
if (!c->shortcut().isEmpty()) {
648
c->setShortcut(QString()); // Remove from client_keys
649
clientShortcutUpdated(c); // Needed, since this is otherwise delayed by setShortcut() and wouldn't run
653
Notify::raise(Notify::TransDelete);
654
if (c->isNormalWindow())
655
Notify::raise(Notify::Delete);
657
if (tab_grab && tab_box->currentClient() == c)
658
tab_box->nextPrev(true);
660
Q_ASSERT(clients.contains(c) || desktops.contains(c));
661
if (tilingEnabled() && tilingLayouts.value(c->desktop())) {
664
// TODO: if marked client is removed, notify the marked list
665
clients.removeAll(c);
666
desktops.removeAll(c);
667
unconstrained_stacking_order.removeAll(c);
668
stacking_order.removeAll(c);
669
x_stacking_dirty = true;
670
for (int i = 1; i <= numberOfDesktops(); ++i)
671
focus_chain[i].removeAll(c);
672
global_focus_chain.removeAll(c);
673
attention_chain.removeAll(c);
674
showing_desktop_clients.removeAll(c);
677
Group* group = findGroup(c->window());
681
if (c == most_recently_raised)
682
most_recently_raised = 0;
683
should_get_focus.removeAll(c);
684
Q_ASSERT(c != active_client);
685
if (c == last_active_client)
686
last_active_client = 0;
687
if (c == pending_take_activity)
688
pending_take_activity = NULL;
689
if (c == delayfocus_client)
692
updateStackingOrder(true);
694
updateCompositeBlocking();
697
tab_box->reset(true);
702
void Workspace::removeUnmanaged(Unmanaged* c, allowed_t)
704
assert(unmanaged.contains(c));
705
unmanaged.removeAll(c);
706
x_stacking_dirty = true;
709
void Workspace::addDeleted(Deleted* c, allowed_t)
711
assert(!deleted.contains(c));
713
x_stacking_dirty = true;
716
void Workspace::removeDeleted(Deleted* c, allowed_t)
718
assert(deleted.contains(c));
720
scene->windowDeleted(c);
721
emit deletedRemoved(c);
722
deleted.removeAll(c);
723
x_stacking_dirty = true;
726
void Workspace::updateFocusChains(Client* c, FocusChainChange change)
728
if (!c->wantsTabFocus()) { // Doesn't want tab focus, remove
729
for (int i = 1; i <= numberOfDesktops(); ++i)
730
focus_chain[i].removeAll(c);
731
global_focus_chain.removeAll(c);
734
if (c->desktop() == NET::OnAllDesktops) {
735
// Now on all desktops, add it to focus_chains it is not already in
736
for (int i = 1; i <= numberOfDesktops(); i++) {
737
// Making first/last works only on current desktop, don't affect all desktops
738
if (i == currentDesktop()
739
&& (change == FocusChainMakeFirst || change == FocusChainMakeLast)) {
740
focus_chain[i].removeAll(c);
741
if (change == FocusChainMakeFirst)
742
focus_chain[i].append(c);
744
focus_chain[i].prepend(c);
745
} else if (!focus_chain[i].contains(c)) {
746
// Add it after the active one
747
if (active_client != NULL && active_client != c &&
748
!focus_chain[i].isEmpty() && focus_chain[i].last() == active_client)
749
focus_chain[i].insert(focus_chain[i].size() - 1, c);
751
focus_chain[i].append(c); // Otherwise add as the first one
754
} else { // Now only on desktop, remove it anywhere else
755
for (int i = 1; i <= numberOfDesktops(); i++) {
756
if (i == c->desktop()) {
757
if (change == FocusChainMakeFirst) {
758
focus_chain[i].removeAll(c);
759
focus_chain[i].append(c);
760
} else if (change == FocusChainMakeLast) {
761
focus_chain[i].removeAll(c);
762
focus_chain[i].prepend(c);
763
} else if (!focus_chain[i].contains(c)) {
764
// Add it after the active one
765
if (active_client != NULL && active_client != c &&
766
!focus_chain[i].isEmpty() && focus_chain[i].last() == active_client)
767
focus_chain[i].insert(focus_chain[i].size() - 1, c);
769
focus_chain[i].append(c); // Otherwise add as the first one
772
focus_chain[i].removeAll(c);
775
if (change == FocusChainMakeFirst) {
776
global_focus_chain.removeAll(c);
777
global_focus_chain.append(c);
778
} else if (change == FocusChainMakeLast) {
779
global_focus_chain.removeAll(c);
780
global_focus_chain.prepend(c);
781
} else if (!global_focus_chain.contains(c)) {
782
// Add it after the active one
783
if (active_client != NULL && active_client != c &&
784
!global_focus_chain.isEmpty() && global_focus_chain.last() == active_client)
785
global_focus_chain.insert(global_focus_chain.size() - 1, c);
787
global_focus_chain.append(c); // Otherwise add as the first one
791
void Workspace::updateCurrentTopMenu()
793
if (!managingTopMenus())
795
// toplevel menubar handling
797
bool block_desktop_menubar = false;
799
// Show the new menu bar first...
800
Client* menu_client = active_client;
802
if (menu_client->isFullScreen())
803
block_desktop_menubar = true;
804
for (ClientList::ConstIterator it = menu_client->transients().constBegin();
805
it != menu_client->transients().constEnd();
807
if ((*it)->isTopMenu()) {
811
if (menubar != NULL || !menu_client->isTransient())
813
if (menu_client->isModal() || menu_client->transientFor() == NULL)
814
break; // Don't use mainwindow's menu if this is modal or group transient
815
menu_client = menu_client->transientFor();
818
// Try to find any topmenu from the application (#72113)
819
for (ClientList::ConstIterator it = active_client->group()->members().constBegin();
820
it != active_client->group()->members().constEnd();
822
if ((*it)->isTopMenu()) {
828
if (!menubar && !block_desktop_menubar && options->desktopTopMenu()) {
829
// Find the menubar of the desktop
830
Client* desktop = findDesktop(true, currentDesktop());
831
if (desktop != NULL) {
832
for (ClientList::ConstIterator it = desktop->transients().constBegin();
833
it != desktop->transients().constEnd();
835
if ((*it)->isTopMenu()) {
840
// TODO: To be cleaned app with window grouping
841
// Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
842
// thus the topmenu is not transient for it :-/.
843
if (menubar == NULL) {
844
for (ClientList::ConstIterator it = topmenus.constBegin();
845
it != topmenus.constEnd();
847
// kdesktop's topmenu has WM_TRANSIENT_FOR set pointing to the root window
848
// to recognize it here. Also, with the xroot hack in kdesktop, there's
849
// no NET::Desktop window to be transient for.
850
if ((*it)->wasOriginallyGroupTransient()) {
857
//kDebug( 1212 ) << "CURRENT TOPMENU:" << menubar << ":" << active_client;
859
if (active_client && !menubar->isOnDesktop(active_client->desktop()))
860
menubar->setDesktop(active_client->desktop());
861
menubar->hideClient(false);
862
topmenu_space->hide();
863
// Make it appear like it's been raised manually - it's in the Dock layer anyway,
864
// and not raising it could mess up stacking order of topmenus within one application,
865
// and thus break raising of mainclients in raiseClient()
866
unconstrained_stacking_order.removeAll(menubar);
867
unconstrained_stacking_order.append(menubar);
868
} else if (!block_desktop_menubar) {
869
// No topmenu active - show the space window, so that there's not empty space
870
topmenu_space->show();
873
// ... Then hide the other ones. Avoids flickers.
874
for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
875
if ((*it)->isTopMenu() && (*it) != menubar)
876
(*it)->hideClient(true);
880
void Workspace::updateToolWindows(bool also_hide)
882
// TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
883
if (!options->hideUtilityWindowsForInactive) {
884
for (ClientList::ConstIterator it = clients.constBegin();
885
it != clients.constEnd();
887
(*it)->hideClient(false);
890
const Group* group = NULL;
891
const Client* client = active_client;
892
// Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
893
// will be shown; if a group transient is group, all tools in the group will be shown
894
while (client != NULL) {
895
if (!client->isTransient())
897
if (client->groupTransient()) {
898
group = client->group();
901
client = client->transientFor();
903
// Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
904
// I.e. if it's not up to date
906
// SELI TODO: But maybe it should - what if a new client has been added that's not in stacking order yet?
907
ClientList to_show, to_hide;
908
for (ClientList::ConstIterator it = stacking_order.constBegin();
909
it != stacking_order.constEnd();
911
if ((*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) {
913
if (!(*it)->isTransient()) {
914
if ((*it)->group()->members().count() == 1) // Has its own group, keep always visible
916
else if (client != NULL && (*it)->group() == client->group())
921
if (group != NULL && (*it)->group() == group)
923
else if (client != NULL && client->hasTransient((*it), true))
928
if (!show && also_hide) {
929
const ClientList mainclients = (*it)->mainClients();
930
// Don't hide utility windows which are standalone(?) or
931
// have e.g. kicker as mainwindow
932
if (mainclients.isEmpty())
934
for (ClientList::ConstIterator it2 = mainclients.constBegin();
935
it2 != mainclients.constEnd();
937
if ((*it2)->isSpecialWindow())
946
} // First show new ones, then hide
947
for (int i = to_show.size() - 1;
950
// TODO: Since this is in stacking order, the order of taskbar entries changes :(
951
to_show.at(i)->hideClient(false);
953
for (ClientList::ConstIterator it = to_hide.constBegin();
954
it != to_hide.constEnd();
955
++it) // From bottommost
956
(*it)->hideClient(true);
957
updateToolWindowsTimer.stop();
958
} else // setActiveClient() is after called with NULL client, quickly followed
959
// by setting a new client, which would result in flickering
960
resetUpdateToolWindowsTimer();
964
void Workspace::resetUpdateToolWindowsTimer()
966
updateToolWindowsTimer.start(200);
969
void Workspace::slotUpdateToolWindows()
971
updateToolWindows(true);
975
* Updates the current colormap according to the currently active client
977
void Workspace::updateColormap()
979
Colormap cmap = default_colormap;
980
if (activeClient() && activeClient()->colormap() != None)
981
cmap = activeClient()->colormap();
982
if (cmap != installed_colormap) {
983
XInstallColormap(display(), cmap);
984
installed_colormap = cmap;
988
void Workspace::slotReloadConfig()
993
void Workspace::reconfigure()
995
reconfigureTimer.start(200);
999
* This D-Bus call is used by the compositing kcm. Since the reconfigure()
1000
* D-Bus call delays the actual reconfiguring, it is not possible to immediately
1001
* call compositingActive(). Therefore the kcm will instead call this to ensure
1002
* the reconfiguring has already happened.
1004
bool Workspace::waitForCompositingSetup()
1006
if (reconfigureTimer.isActive()) {
1007
reconfigureTimer.stop();
1010
return compositingActive();
1013
void Workspace::slotSettingsChanged(int category)
1015
kDebug(1212) << "Workspace::slotSettingsChanged()";
1016
if (category == KGlobalSettings::SETTINGS_SHORTCUTS)
1023
KWIN_PROCEDURE(CheckBorderSizesProcedure, Client, cl->checkBorderSizes(true));
1025
void Workspace::slotReconfigure()
1027
kDebug(1212) << "Workspace::slotReconfigure()";
1028
reconfigureTimer.stop();
1030
reserveElectricBorderActions(false);
1031
if (options->electricBorders() == Options::ElectricAlways)
1032
reserveElectricBorderSwitching(false);
1034
bool borderlessMaximizedWindows = options->borderlessMaximizedWindows();
1036
KGlobal::config()->reparseConfiguration();
1037
unsigned long changed = options->updateSettings();
1039
tab_box->reconfigure();
1040
desktop_change_osd->reconfigure();
1041
initPositioning->reinitCascading(0);
1043
forEachClient(CheckIgnoreFocusStealingProcedure());
1044
updateToolWindows(true);
1046
if (hasDecorationPlugin() && mgr->reset(changed)) {
1047
// Decorations need to be recreated
1049
// This actually seems to make things worse now
1051
//curtain.setBackgroundMode( NoBackground );
1052
//curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
1055
for (ClientList::ConstIterator it = clients.constBegin();
1056
it != clients.constEnd();
1058
(*it)->updateDecoration(true, true);
1059
// If the new decoration doesn't supports tabs then ungroup clients
1060
if (!decorationSupportsClientGrouping()) {
1061
QList<ClientGroup*> tmpGroups = clientGroups; // Prevent crashing
1062
for (QList<ClientGroup*>::const_iterator i = tmpGroups.constBegin(); i != tmpGroups.constEnd(); i++)
1065
mgr->destroyPreviousPlugin();
1067
forEachClient(CheckBorderSizesProcedure());
1068
foreach (Client * c, clients)
1069
c->triggerDecorationRepaint();
1072
reserveElectricBorderActions(true);
1073
if (options->electricBorders() == Options::ElectricAlways)
1074
reserveElectricBorderSwitching(true);
1075
updateElectricBorders();
1077
if (options->topMenuEnabled() && !managingTopMenus()) {
1078
if (topmenu_selection->claim(false))
1079
setupTopMenuHandling();
1081
lostTopMenuSelection();
1082
} else if (!options->topMenuEnabled() && managingTopMenus()) {
1083
topmenu_selection->release();
1084
lostTopMenuSelection();
1086
topmenu_height = 0; // Invalidate used menu height
1087
if (managingTopMenus()) {
1088
updateTopMenuGeometry();
1089
updateCurrentTopMenu();
1092
if (!compositingSuspended) {
1094
if (effects) // setupCompositing() may fail
1095
effects->reconfigure();
1098
finishCompositing();
1101
for (ClientList::Iterator it = clients.begin();
1102
it != clients.end();
1104
(*it)->setupWindowRules(true);
1105
(*it)->applyWindowRules();
1106
discardUsedWindowRules(*it, false);
1109
if (borderlessMaximizedWindows != options->borderlessMaximizedWindows() &&
1110
!options->borderlessMaximizedWindows()) {
1111
// in case borderless maximized windows option changed and new option
1112
// is to have borders, we need to unset the borders for all maximized windows
1113
for (ClientList::Iterator it = clients.begin();
1114
it != clients.end();
1116
if ((*it)->maximizeMode() == MaximizeFull)
1117
(*it)->checkNoBorder();
1121
setTilingEnabled(options->tilingOn);
1122
foreach (TilingLayout * layout, tilingLayouts) {
1124
layout->reconfigureTiling();
1126
// just so that we reset windows in the right manner, 'activate' the current active window
1127
notifyTilingWindowActivated(activeClient());
1128
if (hasDecorationPlugin()) {
1129
rootInfo->setSupported(NET::WM2FrameOverlap, mgr->factory()->supports(AbilityExtendIntoClientArea));
1131
rootInfo->setSupported(NET::WM2FrameOverlap, false);
1135
void Workspace::slotReinitCompositing()
1137
// Reparse config. Config options will be reloaded by setupCompositing()
1138
KGlobal::config()->reparseConfiguration();
1140
// Update any settings that can be set in the compositing kcm.
1141
updateElectricBorders();
1143
// Restart compositing
1144
finishCompositing();
1146
// resume compositing if suspended
1147
compositingSuspended = false;
1148
options->compositingInitialized = false;
1150
if (hasDecorationPlugin()) {
1151
KDecorationFactory* factory = mgr->factory();
1152
factory->reset(SettingCompositing);
1155
if (effects) { // setupCompositing() may fail
1156
effects->reconfigure();
1157
emit compositingToggled(true);
1161
static bool _loading_desktop_settings = false;
1162
void Workspace::loadDesktopSettings()
1164
_loading_desktop_settings = true;
1165
KSharedConfig::Ptr c = KGlobal::config();
1167
if (screen_number == 0)
1168
groupname = "Desktops";
1170
groupname.sprintf("Desktops-screen-%d", screen_number);
1171
KConfigGroup group(c, groupname);
1172
const int n = group.readEntry("Number", 1);
1173
setNumberOfDesktops(n);
1174
for (int i = 1; i <= n; i++) {
1175
QString s = group.readEntry(QString("Name_%1").arg(i), i18n("Desktop %1", i));
1176
rootInfo->setDesktopName(i, s.toUtf8().data());
1177
desktop_focus_chain[i-1] = i;
1179
_loading_desktop_settings = false;
1182
void Workspace::saveDesktopSettings()
1184
if (_loading_desktop_settings)
1186
KSharedConfig::Ptr c = KGlobal::config();
1188
if (screen_number == 0)
1189
groupname = "Desktops";
1191
groupname.sprintf("Desktops-screen-%d", screen_number);
1192
KConfigGroup group(c, groupname);
1194
group.writeEntry("Number", numberOfDesktops());
1195
for (int i = 1; i <= numberOfDesktops(); i++) {
1196
QString s = desktopName(i);
1197
QString defaultvalue = i18n("Desktop %1", i);
1200
rootInfo->setDesktopName(i, s.toUtf8().data());
1203
if (s != defaultvalue) {
1204
group.writeEntry(QString("Name_%1").arg(i), s);
1206
QString currentvalue = group.readEntry(QString("Name_%1").arg(i), QString());
1207
if (currentvalue != defaultvalue)
1208
group.writeEntry(QString("Name_%1").arg(i), "");
1216
QStringList Workspace::configModules(bool controlCenter)
1219
args << "kwindecoration";
1221
args << "kwinoptions";
1222
else if (KAuthorized::authorizeControlModule("kde-kwinoptions.desktop"))
1223
args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced"
1224
<< "kwinrules" << "kwincompositing" << "kwintabbox" << "kwinscreenedges";
1228
void Workspace::configureWM()
1231
args << "--icon" << "preferences-system-windows" << configModules(false);
1232
KToolInvocation::kdeinitExec("kcmshell4", args);
1236
* Avoids managing a window with title \a title
1238
void Workspace::doNotManage(const QString& title)
1240
doNotManageList.append(title);
1244
* Hack for java applets
1246
bool Workspace::isNotManaged(const QString& title)
1248
for (QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it) {
1250
if (r.indexIn(title) != -1) {
1251
doNotManageList.erase(it);
1259
* Refreshes all the client windows
1261
void Workspace::refresh()
1263
QWidget w(NULL, Qt::X11BypassWindowManagerHint);
1264
w.setGeometry(Kephal::ScreenUtils::desktopGeometry());
1267
QApplication::flush();
1271
* During virt. desktop switching, desktop areas covered by windows that are
1272
* going to be hidden are first obscured by new windows with no background
1273
* ( i.e. transparent ) placed right below the windows. These invisible windows
1274
* are removed after the switch is complete.
1275
* Reduces desktop ( wallpaper ) repaints during desktop switching
1277
class ObscuringWindows
1280
~ObscuringWindows();
1281
void create(Client* c);
1283
QList<Window> obscuring_windows;
1284
static QList<Window>* cached;
1285
static unsigned int max_cache_size;
1288
QList<Window>* ObscuringWindows::cached = 0;
1289
unsigned int ObscuringWindows::max_cache_size = 0;
1291
void ObscuringWindows::create(Client* c)
1294
return; // Not needed with compositing
1296
cached = new QList<Window>;
1298
XWindowChanges chngs;
1299
int mask = CWSibling | CWStackMode;
1300
if (cached->count() > 0) {
1301
cached->removeAll(obs_win = cached->first());
1304
chngs.width = c->width();
1305
chngs.height = c->height();
1306
mask |= CWX | CWY | CWWidth | CWHeight;
1308
XSetWindowAttributes a;
1309
a.background_pixmap = None;
1310
a.override_redirect = True;
1311
obs_win = XCreateWindow(display(), rootWindow(), c->x(), c->y(),
1312
c->width(), c->height(), 0, CopyFromParent, InputOutput,
1313
CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a);
1315
chngs.sibling = c->frameId();
1316
chngs.stack_mode = Below;
1317
XConfigureWindow(display(), obs_win, mask, &chngs);
1318
XMapWindow(display(), obs_win);
1319
obscuring_windows.append(obs_win);
1322
ObscuringWindows::~ObscuringWindows()
1324
max_cache_size = qMax(int(max_cache_size), obscuring_windows.count() + 4) - 1;
1325
for (QList<Window>::ConstIterator it = obscuring_windows.constBegin();
1326
it != obscuring_windows.constEnd();
1328
XUnmapWindow(display(), *it);
1329
if (cached->count() < int(max_cache_size))
1330
cached->prepend(*it);
1332
XDestroyWindow(display(), *it);
1337
* Sets the current desktop to \a new_desktop
1339
* Shows/Hides windows according to the stacking order and finally
1340
* propages the new desktop to the world
1342
bool Workspace::setCurrentDesktop(int new_desktop)
1344
if (new_desktop < 1 || new_desktop > numberOfDesktops())
1349
// TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
1350
StackingUpdatesBlocker blocker(this);
1352
int old_desktop = currentDesktop();
1353
if (new_desktop != currentDesktop()) {
1354
++block_showing_desktop;
1355
// Optimized Desktop switching: unmapping done from back to front
1356
// mapping done from front to back => less exposure events
1357
Notify::raise((Notify::Event)(Notify::DesktopChange + new_desktop));
1359
ObscuringWindows obs_wins;
1361
currentDesktop_ = new_desktop; // Change the desktop (so that Client::updateVisibility() works)
1363
for (ClientList::ConstIterator it = stacking_order.constBegin();
1364
it != stacking_order.constEnd();
1366
if (!(*it)->isOnDesktop(new_desktop) && (*it) != movingClient && (*it)->isOnCurrentActivity()) {
1367
if ((*it)->isShown(true) && (*it)->isOnDesktop(old_desktop))
1368
obs_wins.create(*it);
1369
(*it)->updateVisibility();
1372
// Now propagate the change, after hiding, before showing
1373
rootInfo->setCurrentDesktop(currentDesktop());
1375
// if the client is moved to another desktop, that desktop may
1376
// not have an existing layout. In addition this tiling layout
1377
// will require rearrangement, so notify about desktop changes.
1378
if (movingClient && !movingClient->isOnDesktop(new_desktop)) {
1379
int old_desktop = movingClient->desktop();
1380
movingClient->setDesktop(new_desktop);
1381
if (tilingEnabled()) {
1382
notifyTilingWindowDesktopChanged(movingClient, old_desktop);
1386
for (int i = stacking_order.size() - 1; i >= 0 ; --i)
1387
if (stacking_order.at(i)->isOnDesktop(new_desktop) && stacking_order.at(i)->isOnCurrentActivity())
1388
stacking_order.at(i)->updateVisibility();
1390
--block_showing_desktop;
1391
if (showingDesktop()) // Do this only after desktop change to avoid flicker
1392
resetShowingDesktop(false);
1395
// Restore the focus on this desktop
1399
if (options->focusPolicyIsReasonable()) {
1400
// Search in focus chain
1401
if (movingClient != NULL && active_client == movingClient &&
1402
focus_chain[currentDesktop()].contains(active_client) &&
1403
active_client->isShown(true) && active_client->isOnCurrentDesktop())
1404
c = active_client; // The requestFocus below will fail, as the client is already active
1406
for (int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i) {
1407
if (focus_chain[currentDesktop()].at(i)->isShown(false) &&
1408
focus_chain[currentDesktop()].at(i)->isOnCurrentActivity()) {
1409
c = focus_chain[currentDesktop()].at(i);
1415
// If "unreasonable focus policy" and active_client is on_all_desktops and
1416
// under mouse (Hence == old_active_client), conserve focus.
1417
// (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1418
else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop())
1421
if (c == NULL && !desktops.isEmpty())
1422
c = findDesktop(true, currentDesktop());
1424
if (c != active_client)
1425
setActiveClient(NULL, Allowed);
1429
else if (!desktops.isEmpty())
1430
requestFocus(findDesktop(true, currentDesktop()));
1434
updateCurrentTopMenu();
1436
// Update focus chain:
1437
// If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3,
1438
// Output: chain = { 3, 1, 2, 4 }.
1439
//kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
1440
// .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
1441
for (int i = desktop_focus_chain.indexOf(currentDesktop()); i > 0; i--)
1442
desktop_focus_chain[i] = desktop_focus_chain[i-1];
1443
desktop_focus_chain[0] = currentDesktop();
1445
//QString s = "desktop_focus_chain[] = { ";
1446
//for ( uint i = 0; i < desktop_focus_chain.size(); i++ )
1447
// s += QString::number( desktop_focus_chain[i] ) + ", ";
1448
//kDebug( 1212 ) << s << "}\n";
1450
// Not for the very first time, only if something changed and there are more than 1 desktops
1451
if (old_desktop != 0 && old_desktop != new_desktop && numberOfDesktops() > 1)
1452
desktop_change_osd->desktopChanged(old_desktop);
1457
emit currentDesktopChanged(old_desktop);
1462
* Updates the current activity when it changes
1463
* do *not* call this directly; it does not set the activity.
1465
* Shows/Hides windows according to the stacking order
1467
void Workspace::updateCurrentActivity(const QString &new_activity)
1470
//closeActivePopup();
1472
// TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
1473
StackingUpdatesBlocker blocker(this);
1475
if (new_activity != activity_) {
1476
++block_showing_desktop; //FIXME should I be using that?
1477
// Optimized Desktop switching: unmapping done from back to front
1478
// mapping done from front to back => less exposure events
1479
//Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
1481
ObscuringWindows obs_wins;
1483
QString old_activity = activity_;
1484
activity_ = new_activity;
1486
for (ClientList::ConstIterator it = stacking_order.constBegin();
1487
it != stacking_order.constEnd();
1489
if (!(*it)->isOnActivity(new_activity) && (*it) != movingClient && (*it)->isOnCurrentDesktop()) {
1490
if ((*it)->isShown(true) && (*it)->isOnActivity(old_activity))
1491
obs_wins.create(*it);
1492
(*it)->updateVisibility();
1495
// Now propagate the change, after hiding, before showing
1496
//rootInfo->setCurrentDesktop( currentDesktop() );
1498
/* TODO someday enable dragging windows to other activities
1499
if ( movingClient && !movingClient->isOnDesktop( new_desktop ))
1501
int old_desktop = movingClient->desktop();
1502
movingClient->setDesktop( new_desktop );
1503
if ( tilingEnabled() )
1505
notifyWindowDesktopChanged( movingClient, old_desktop );
1510
for (int i = stacking_order.size() - 1; i >= 0 ; --i)
1511
if (stacking_order.at(i)->isOnActivity(new_activity))
1512
stacking_order.at(i)->updateVisibility();
1514
--block_showing_desktop;
1515
//FIXME not sure if I should do this either
1516
if (showingDesktop()) // Do this only after desktop change to avoid flicker
1517
resetShowingDesktop(false);
1520
// Restore the focus on this desktop
1524
//FIXME below here is a lot of focuschain stuff, probably all wrong now
1525
if (options->focusPolicyIsReasonable()) {
1526
// Search in focus chain
1527
if (movingClient != NULL && active_client == movingClient &&
1528
focus_chain[currentDesktop()].contains(active_client) &&
1529
active_client->isShown(true) && active_client->isOnCurrentDesktop())
1530
c = active_client; // The requestFocus below will fail, as the client is already active
1532
for (int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i) {
1533
if (focus_chain[currentDesktop()].at(i)->isShown(false) &&
1534
focus_chain[currentDesktop()].at(i)->isOnCurrentActivity()) {
1535
c = focus_chain[currentDesktop()].at(i);
1541
// If "unreasonable focus policy" and active_client is on_all_desktops and
1542
// under mouse (Hence == old_active_client), conserve focus.
1543
// (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1544
else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop() && active_client->isOnCurrentActivity())
1547
if (c == NULL && !desktops.isEmpty())
1548
c = findDesktop(true, currentDesktop());
1550
if (c != active_client)
1551
setActiveClient(NULL, Allowed);
1555
else if (!desktops.isEmpty())
1556
requestFocus(findDesktop(true, currentDesktop()));
1560
updateCurrentTopMenu();
1562
// Update focus chain:
1563
// If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3,
1564
// Output: chain = { 3, 1, 2, 4 }.
1565
//kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
1566
// .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
1567
for (int i = desktop_focus_chain.indexOf(currentDesktop()); i > 0; i--)
1568
desktop_focus_chain[i] = desktop_focus_chain[i-1];
1569
desktop_focus_chain[0] = currentDesktop();
1571
//QString s = "desktop_focus_chain[] = { ";
1572
//for ( uint i = 0; i < desktop_focus_chain.size(); i++ )
1573
// s += QString::number( desktop_focus_chain[i] ) + ", ";
1574
//kDebug( 1212 ) << s << "}\n";
1576
// Not for the very first time, only if something changed and there are more than 1 desktops
1578
//if ( effects != NULL && old_desktop != 0 && old_desktop != new_desktop )
1579
// static_cast<EffectsHandlerImpl*>( effects )->desktopChanged( old_desktop );
1586
* updates clients when an activity is destroyed.
1587
* this ensures that a client does not get 'lost' if the only activity it's on is removed.
1589
void Workspace::activityRemoved(const QString &activity)
1591
allActivities_.removeOne(activity);
1592
foreach (Client * client, stacking_order) {
1593
client->setOnActivity(activity, false);
1595
//toss out any session data for it
1596
KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + activity);
1600
void Workspace::activityAdded(const QString &activity)
1602
allActivities_ << activity;
1606
* Called only from D-Bus
1608
void Workspace::nextDesktop()
1610
int desktop = currentDesktop() + 1;
1611
setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
1615
* Called only from D-Bus
1617
void Workspace::previousDesktop()
1619
int desktop = currentDesktop() - 1;
1620
setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
1624
* Sets the number of virtual desktops to \a n
1626
void Workspace::setNumberOfDesktops(int n)
1628
if (n > KWIN_MAX_NUMBER_DESKTOPS)
1629
n = KWIN_MAX_NUMBER_DESKTOPS;
1630
if (n < 1 || n == numberOfDesktops())
1632
int old_number_of_desktops = numberOfDesktops();
1634
updateDesktopLayout(); // Make sure the layout is still valid
1636
if (currentDesktop() > n)
1637
setCurrentDesktop(n);
1639
// move all windows that would be hidden to the last visible desktop
1640
if (old_number_of_desktops > numberOfDesktops()) {
1641
for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) {
1642
if (!(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
1643
sendClientToDesktop(*it, numberOfDesktops(), true);
1644
// TODO: Tile should have a method allClients, push them into other tiles
1647
rootInfo->setNumberOfDesktops(n);
1648
NETPoint* viewports = new NETPoint[n];
1649
rootInfo->setDesktopViewport(n, *viewports);
1652
// Make it +1, so that it can be accessed as [1..numberofdesktops]
1653
focus_chain.resize(n + 1);
1656
workarea.resize(n + 1);
1657
restrictedmovearea.clear();
1658
restrictedmovearea.resize(n + 1);
1659
oldrestrictedmovearea.clear();
1660
oldrestrictedmovearea.resize(n + 1);
1663
updateClientArea(true);
1665
// Resize and reset the desktop focus chain.
1666
desktop_focus_chain.resize(n);
1667
for (int i = 0; i < int(desktop_focus_chain.size()); i++)
1668
desktop_focus_chain[i] = i + 1;
1670
tilingLayouts.resize(numberOfDesktops() + 1);
1672
// reset the desktop change osd
1673
desktop_change_osd->numberDesktopsChanged();
1675
saveDesktopSettings();
1676
emit numberDesktopsChanged(old_number_of_desktops);
1680
* Sends client \a c to desktop \a desk.
1682
* Takes care of transients as well.
1684
void Workspace::sendClientToDesktop(Client* c, int desk, bool dont_activate)
1686
if ((desk < 1 && desk != NET::OnAllDesktops) || desk > numberOfDesktops())
1688
int old_desktop = c->desktop();
1689
bool was_on_desktop = c->isOnDesktop(desk) || c->isOnAllDesktops();
1690
c->setDesktop(desk);
1691
if (c->desktop() != desk) // No change or desktop forced
1693
desk = c->desktop(); // Client did range checking
1695
emit desktopPresenceChanged(c, old_desktop);
1697
if (c->isOnDesktop(currentDesktop())) {
1698
if (c->wantsTabFocus() && options->focusPolicyIsReasonable() &&
1699
!was_on_desktop && // for stickyness changes
1703
restackClientUnderActive(c);
1707
notifyTilingWindowDesktopChanged(c, old_desktop);
1709
ClientList transients_stacking_order = ensureStackingOrder(c->transients());
1710
for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
1711
it != transients_stacking_order.constEnd();
1713
sendClientToDesktop(*it, desk, dont_activate);
1718
* Adds/removes client \a c to/from \a activity.
1720
* Takes care of transients as well.
1722
void Workspace::toggleClientOnActivity(Client* c, const QString &activity, bool dont_activate)
1724
//int old_desktop = c->desktop();
1725
bool was_on_activity = c->isOnActivity(activity);
1726
bool was_on_all = c->isOnAllActivities();
1727
//note: all activities === no activities
1728
bool enable = was_on_all || !was_on_activity;
1729
c->setOnActivity(activity, enable);
1730
if (c->isOnActivity(activity) == was_on_activity && c->isOnAllActivities() == was_on_all) // No change
1733
if (c->isOnCurrentActivity()) {
1734
if (c->wantsTabFocus() && options->focusPolicyIsReasonable() &&
1735
!was_on_activity && // for stickyness changes
1736
//FIXME not sure if the line above refers to the correct activity
1740
restackClientUnderActive(c);
1744
//notifyWindowDesktopChanged( c, old_desktop );
1745
//FIXME does tiling break?
1747
ClientList transients_stacking_order = ensureStackingOrder(c->transients());
1748
for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
1749
it != transients_stacking_order.constEnd();
1751
toggleClientOnActivity(*it, activity, dont_activate);
1755
int Workspace::numScreens() const
1757
if (!options->xineramaEnabled)
1759
return Kephal::ScreenUtils::numScreens();
1762
int Workspace::activeScreen() const
1764
if (!options->xineramaEnabled)
1766
if (!options->activeMouseScreen) {
1767
if (activeClient() != NULL && !activeClient()->isOnScreen(active_screen))
1768
return activeClient()->screen();
1769
return active_screen;
1771
return Kephal::ScreenUtils::screenId(cursorPos());
1775
* Check whether a client moved completely out of what's considered the active screen,
1776
* if yes, set a new active screen.
1778
void Workspace::checkActiveScreen(const Client* c)
1780
if (!options->xineramaEnabled)
1784
if (!c->isOnScreen(active_screen))
1785
active_screen = c->screen();
1789
* Called e.g. when a user clicks on a window, set active screen to be the screen
1790
* where the click occurred
1792
void Workspace::setActiveScreenMouse(const QPoint& mousepos)
1794
if (!options->xineramaEnabled)
1796
active_screen = Kephal::ScreenUtils::screenId(mousepos);
1799
QRect Workspace::screenGeometry(int screen) const
1801
if (!options->xineramaEnabled)
1802
return Kephal::ScreenUtils::desktopGeometry();
1803
return Kephal::ScreenUtils::screenGeometry(screen);
1806
int Workspace::screenNumber(const QPoint& pos) const
1808
if (!options->xineramaEnabled)
1810
return Kephal::ScreenUtils::screenId(pos);
1813
void Workspace::sendClientToScreen(Client* c, int screen)
1815
if (c->screen() == screen) // Don't use isOnScreen(), that's true even when only partially
1817
GeometryUpdatesBlocker blocker(c);
1818
QRect old_sarea = clientArea(MaximizeArea, c);
1819
QRect sarea = clientArea(MaximizeArea, screen, c->desktop());
1820
c->setGeometry(sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(),
1821
c->size().width(), c->size().height());
1822
c->checkWorkspacePosition();
1823
ClientList transients_stacking_order = ensureStackingOrder(c->transients());
1824
for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
1825
it != transients_stacking_order.constEnd();
1827
sendClientToScreen(*it, screen);
1829
active_screen = screen;
1832
void Workspace::killWindowId(Window window_to_kill)
1834
if (window_to_kill == None)
1836
Window window = window_to_kill;
1837
Client* client = NULL;
1839
client = findClient(FrameIdMatchPredicate(window));
1841
break; // Found the client
1842
Window parent, root;
1844
unsigned int children_count;
1845
XQueryTree(display(), window, &root, &parent, &children, &children_count);
1846
if (children != NULL)
1848
if (window == root) // We didn't find the client, probably an override-redirect window
1850
window = parent; // Go up
1853
client->killWindow();
1855
XKillClient(display(), window_to_kill);
1858
void Workspace::sendPingToWindow(Window window, Time timestamp)
1860
rootInfo->sendPing(window, timestamp);
1863
void Workspace::sendTakeActivity(Client* c, Time timestamp, long flags)
1865
rootInfo->takeActivity(c->window(), timestamp, flags);
1866
pending_take_activity = c;
1870
* Invokes keyboard mouse emulation
1872
void Workspace::slotMouseEmulation()
1874
if (mouse_emulation) {
1876
mouse_emulation = false;
1880
if (grabXKeyboard()) {
1881
mouse_emulation = true;
1882
mouse_emulation_state = 0;
1883
mouse_emulation_window = 0;
1888
* Returns the child window under the mouse and activates the
1889
* respective client if necessary.
1891
* Auxiliary function for the mouse emulation system.
1893
WId Workspace::getMouseEmulationWindow()
1896
Window child = rootWindow();
1897
int root_x, root_y, lx, ly;
1904
c = findClient(FrameIdMatchPredicate(w));
1905
XQueryPointer(display(), w, &root, &child, &root_x, &root_y, &lx, &ly, &state);
1906
} while (child != None && child != w);
1908
if (c && !c->isActive())
1914
* Sends a faked mouse event to the specified window. Returns the new button state.
1916
unsigned int Workspace::sendFakedMouseEvent(const QPoint& pos, WId w, MouseEmulation type,
1917
int button, unsigned int state)
1921
QWidget* widget = QWidget::find(w);
1922
if ((!widget || qobject_cast<QToolButton*>(widget)) && !findClient(WindowMatchPredicate(w))) {
1925
XTranslateCoordinates(display(), rootWindow(), w, pos.x(), pos.y(), &x, &y, &xw);
1926
if (type == EmuMove) {
1927
// Motion notify events
1929
e.type = MotionNotify;
1930
e.xmotion.window = w;
1931
e.xmotion.root = rootWindow();
1932
e.xmotion.subwindow = w;
1933
e.xmotion.time = xTime();
1936
e.xmotion.x_root = pos.x();
1937
e.xmotion.y_root = pos.y();
1938
e.xmotion.state = state;
1939
e.xmotion.is_hint = NotifyNormal;
1940
XSendEvent(display(), w, true, ButtonMotionMask, &e);
1943
e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
1944
e.xbutton.window = w;
1945
e.xbutton.root = rootWindow();
1946
e.xbutton.subwindow = w;
1947
e.xbutton.time = xTime();
1950
e.xbutton.x_root = pos.x();
1951
e.xbutton.y_root = pos.y();
1952
e.xbutton.state = state;
1953
e.xbutton.button = button;
1954
XSendEvent(display(), w, true, ButtonPressMask, &e);
1956
if (type == EmuPress) {
1959
state |= Button2Mask;
1962
state |= Button3Mask;
1965
state |= Button1Mask;
1971
state &= ~Button2Mask;
1974
state &= ~Button3Mask;
1977
state &= ~Button1Mask;
1988
* Handles keypress event during mouse emulation
1990
bool Workspace::keyPressMouseEmulation(XKeyEvent& ev)
1992
int kc = XKeycodeToKeysym(display(), ev.keycode, 0);
1993
int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
1995
bool is_control = km & ControlMask;
1996
bool is_alt = km & Mod1Mask;
1997
bool is_shift = km & ShiftMask;
1998
int delta = is_control ? 1 : (is_alt ? 32 : 8);
1999
QPoint pos = cursorPos();
2029
case XK_KP_Page_Down:
2039
if (!mouse_emulation_state)
2040
mouse_emulation_window = getMouseEmulationWindow();
2041
if ((mouse_emulation_state & Button1Mask) == 0)
2042
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2043
EmuPress, Button1, mouse_emulation_state);
2045
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2046
EmuRelease, Button1, mouse_emulation_state);
2049
if (!mouse_emulation_state)
2050
mouse_emulation_window = getMouseEmulationWindow();
2051
if ((mouse_emulation_state & Button2Mask) == 0)
2052
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2053
EmuPress, Button2, mouse_emulation_state);
2055
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2056
EmuRelease, Button2, mouse_emulation_state);
2059
if (!mouse_emulation_state)
2060
mouse_emulation_window = getMouseEmulationWindow();
2061
if ((mouse_emulation_state & Button3Mask) == 0)
2062
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2063
EmuPress, Button3, mouse_emulation_state);
2065
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2066
EmuRelease, Button3, mouse_emulation_state);
2072
if (!mouse_emulation_state) {
2073
// Nothing was pressed, fake a LMB click
2074
mouse_emulation_window = getMouseEmulationWindow();
2075
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2076
EmuPress, Button1, mouse_emulation_state);
2077
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2078
EmuRelease, Button1, mouse_emulation_state);
2081
if (mouse_emulation_state & Button1Mask)
2082
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2083
EmuRelease, Button1, mouse_emulation_state);
2084
if (mouse_emulation_state & Button2Mask)
2085
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2086
EmuRelease, Button2, mouse_emulation_state);
2087
if (mouse_emulation_state & Button3Mask)
2088
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2089
EmuRelease, Button3, mouse_emulation_state);
2095
mouse_emulation = false;
2101
QCursor::setPos(pos);
2102
if (mouse_emulation_state)
2103
mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
2104
EmuMove, 0, mouse_emulation_state);
2110
* Delayed focus functions
2112
void Workspace::delayFocus()
2114
requestFocus(delayfocus_client);
2118
void Workspace::requestDelayFocus(Client* c)
2120
delayfocus_client = c;
2121
delete delayFocusTimer;
2122
delayFocusTimer = new QTimer(this);
2123
connect(delayFocusTimer, SIGNAL(timeout()), this, SLOT(delayFocus()));
2124
delayFocusTimer->setSingleShot(true);
2125
delayFocusTimer->start(options->delayFocusInterval);
2128
void Workspace::cancelDelayFocus()
2130
delete delayFocusTimer;
2131
delayFocusTimer = 0;
2134
//-----------------------------------------------------------------------------
2136
//-----------------------------------------------------------------------------
2137
// Electric Border Window management. Electric borders allow a user to change
2138
// the virtual desktop or activate another features by moving the mouse pointer
2139
// to the borders or corners. Technically this is done with input only windows.
2140
//-----------------------------------------------------------------------------
2142
void Workspace::updateElectricBorders()
2144
electric_time_first = xTime();
2145
electric_time_last = xTime();
2146
electric_time_last_trigger = xTime();
2147
electric_current_border = ElectricNone;
2148
QRect r = Kephal::ScreenUtils::desktopGeometry();
2149
electricTop = r.top();
2150
electricBottom = r.bottom();
2151
electricLeft = r.left();
2152
electricRight = r.right();
2154
for (int pos = 0; pos < ELECTRIC_COUNT; ++pos) {
2155
if (electric_reserved[pos] == 0) {
2156
if (electric_windows[pos] != None)
2157
XDestroyWindow(display(), electric_windows[pos]);
2158
electric_windows[pos] = None;
2161
if (electric_windows[pos] != None)
2163
XSetWindowAttributes attributes;
2164
attributes.override_redirect = True;
2165
attributes.event_mask = EnterWindowMask | LeaveWindowMask;
2166
unsigned long valuemask = CWOverrideRedirect | CWEventMask;
2167
int xywh[ELECTRIC_COUNT][4] = {
2168
{ r.left() + 1, r.top(), r.width() - 2, 1 }, // Top
2169
{ r.right(), r.top(), 1, 1 }, // Top-right
2170
{ r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc.
2171
{ r.right(), r.bottom(), 1, 1 },
2172
{ r.left() + 1, r.bottom(), r.width() - 2, 1 },
2173
{ r.left(), r.bottom(), 1, 1 },
2174
{ r.left(), r.top() + 1, 1, r.height() - 2 },
2175
{ r.left(), r.top(), 1, 1 }
2177
electric_windows[pos] = XCreateWindow(display(), rootWindow(),
2178
xywh[pos][0], xywh[pos][1], xywh[pos][2], xywh[pos][3],
2179
0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes);
2180
XMapWindow(display(), electric_windows[pos]);
2182
// Set XdndAware on the windows, so that DND enter events are received (#86998)
2183
Atom version = 4; // XDND version
2184
XChangeProperty(display(), electric_windows[pos], atoms->xdnd_aware, XA_ATOM,
2185
32, PropModeReplace, (unsigned char*)(&version), 1);
2189
void Workspace::destroyElectricBorders()
2191
for (int pos = 0; pos < ELECTRIC_COUNT; ++pos) {
2192
if (electric_windows[pos] != None)
2193
XDestroyWindow(display(), electric_windows[pos]);
2194
electric_windows[pos] = None;
2198
void Workspace::restoreElectricBorderSize(ElectricBorder border)
2200
if (electric_windows[border] == None)
2202
QRect r = Kephal::ScreenUtils::desktopGeometry();
2203
int xywh[ELECTRIC_COUNT][4] = {
2204
{ r.left() + 1, r.top(), r.width() - 2, 1 }, // Top
2205
{ r.right(), r.top(), 1, 1 }, // Top-right
2206
{ r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc.
2207
{ r.right(), r.bottom(), 1, 1 },
2208
{ r.left() + 1, r.bottom(), r.width() - 2, 1 },
2209
{ r.left(), r.bottom(), 1, 1 },
2210
{ r.left(), r.top() + 1, 1, r.height() - 2 },
2211
{ r.left(), r.top(), 1, 1 }
2213
XMoveResizeWindow(display(), electric_windows[border],
2214
xywh[border][0], xywh[border][1], xywh[border][2], xywh[border][3]);
2217
void Workspace::reserveElectricBorderActions(bool reserve)
2219
for (int pos = 0; pos < ELECTRIC_COUNT; ++pos)
2220
if (options->electricBorderAction(static_cast<ElectricBorder>(pos))) {
2222
reserveElectricBorder(static_cast<ElectricBorder>(pos));
2224
unreserveElectricBorder(static_cast<ElectricBorder>(pos));
2228
void Workspace::reserveElectricBorderSwitching(bool reserve)
2230
for (int pos = 0; pos < ELECTRIC_COUNT; ++pos)
2232
reserveElectricBorder(static_cast<ElectricBorder>(pos));
2234
unreserveElectricBorder(static_cast<ElectricBorder>(pos));
2237
void Workspace::reserveElectricBorder(ElectricBorder border)
2239
if (border == ElectricNone)
2241
if (electric_reserved[border]++ == 0)
2242
QTimer::singleShot(0, this, SLOT(updateElectricBorders()));
2245
void Workspace::unreserveElectricBorder(ElectricBorder border)
2247
if (border == ElectricNone)
2249
assert(electric_reserved[border] > 0);
2250
if (--electric_reserved[border] == 0)
2251
QTimer::singleShot(0, this, SLOT(updateElectricBorders()));
2254
void Workspace::checkElectricBorder(const QPoint& pos, Time now)
2256
if ((pos.x() != electricLeft) &&
2257
(pos.x() != electricRight) &&
2258
(pos.y() != electricTop) &&
2259
(pos.y() != electricBottom))
2262
bool have_borders = false;
2263
for (int i = 0; i < ELECTRIC_COUNT; ++i)
2264
if (electric_windows[i] != None)
2265
have_borders = true;
2269
Time treshold_set = options->electricBorderDelay(); // Set timeout
2270
Time treshold_reset = 250; // Reset timeout
2271
Time treshold_trigger = options->electricBorderCooldown(); // Minimum time between triggers
2272
int distance_reset = 30; // Mouse should not move more than this many pixels
2273
int pushback_pixels = options->electricBorderPushbackPixels();
2275
ElectricBorder border;
2276
if (pos.x() == electricLeft && pos.y() == electricTop)
2277
border = ElectricTopLeft;
2278
else if (pos.x() == electricRight && pos.y() == electricTop)
2279
border = ElectricTopRight;
2280
else if (pos.x() == electricLeft && pos.y() == electricBottom)
2281
border = ElectricBottomLeft;
2282
else if (pos.x() == electricRight && pos.y() == electricBottom)
2283
border = ElectricBottomRight;
2284
else if (pos.x() == electricLeft)
2285
border = ElectricLeft;
2286
else if (pos.x() == electricRight)
2287
border = ElectricRight;
2288
else if (pos.y() == electricTop)
2289
border = ElectricTop;
2290
else if (pos.y() == electricBottom)
2291
border = ElectricBottom;
2295
if (electric_windows[border] == None)
2298
if (pushback_pixels == 0) {
2299
// no pushback so we have to activate at once
2300
electric_time_last = now;
2302
if ((electric_current_border == border) &&
2303
(timestampDiff(electric_time_last, now) < treshold_reset) &&
2304
(timestampDiff(electric_time_last_trigger, now) > treshold_trigger) &&
2305
((pos - electric_push_point).manhattanLength() < distance_reset)) {
2306
electric_time_last = now;
2308
if (timestampDiff(electric_time_first, now) > treshold_set) {
2309
electric_current_border = ElectricNone;
2310
electric_time_last_trigger = now;
2312
// If moving a client or have force doing the desktop switch
2313
if (options->electricBorders() != Options::ElectricDisabled)
2314
electricBorderSwitchDesktop(border, pos);
2315
return; // Don't reset cursor position
2317
if (options->electricBorders() == Options::ElectricAlways &&
2318
(border == ElectricTop || border == ElectricRight ||
2319
border == ElectricBottom || border == ElectricLeft)) {
2320
// If desktop switching is always enabled don't apply it to the corners if
2321
// an effect is applied to it (We will check that later).
2322
electricBorderSwitchDesktop(border, pos);
2323
return; // Don't reset cursor position
2325
switch(options->electricBorderAction(border)) {
2326
case ElectricActionDashboard: { // Display Plasma dashboard
2327
QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App");
2328
plasmaApp.call("toggleDashboard");
2331
case ElectricActionShowDesktop: {
2332
setShowingDesktop(!showingDesktop());
2335
case ElectricActionLockScreen: { // Lock the screen
2336
QDBusInterface screenSaver("org.kde.screensaver", "/ScreenSaver");
2337
screenSaver.call("Lock");
2340
case ElectricActionPreventScreenLocking: {
2343
case ElectricActionNone: // Either desktop switching or an effect
2345
if (effects && static_cast<EffectsHandlerImpl*>(effects)->borderActivated(border))
2346
{} // Handled by effects
2348
electricBorderSwitchDesktop(border, pos);
2349
return; // Don't reset cursor position
2356
electric_current_border = border;
2357
electric_time_first = now;
2358
electric_time_last = now;
2359
electric_push_point = pos;
2362
// Reset the pointer to find out whether the user is really pushing
2363
// (the direction back from which it came, starting from top clockwise)
2364
const int xdiff[ELECTRIC_COUNT] = { 0,
2373
const int ydiff[ELECTRIC_COUNT] = { pushback_pixels,
2382
QCursor::setPos(pos.x() + xdiff[border], pos.y() + ydiff[border]);
2385
void Workspace::electricBorderSwitchDesktop(ElectricBorder border, const QPoint& _pos)
2388
int desk = currentDesktop();
2389
const int OFFSET = 2;
2390
if (border == ElectricLeft || border == ElectricTopLeft || border == ElectricBottomLeft) {
2391
desk = desktopToLeft(desk, options->rollOverDesktops);
2392
pos.setX(displayWidth() - 1 - OFFSET);
2394
if (border == ElectricRight || border == ElectricTopRight || border == ElectricBottomRight) {
2395
desk = desktopToRight(desk, options->rollOverDesktops);
2398
if (border == ElectricTop || border == ElectricTopLeft || border == ElectricTopRight) {
2399
desk = desktopAbove(desk, options->rollOverDesktops);
2400
pos.setY(displayHeight() - 1 - OFFSET);
2402
if (border == ElectricBottom || border == ElectricBottomLeft || border == ElectricBottomRight) {
2403
desk = desktopBelow(desk, options->rollOverDesktops);
2406
int desk_before = currentDesktop();
2407
setCurrentDesktop(desk);
2408
if (currentDesktop() != desk_before)
2409
QCursor::setPos(pos);
2413
* Called when the user entered an electric border with the mouse.
2414
* It may switch to another virtual desktop.
2416
bool Workspace::electricBorderEvent(XEvent* e)
2418
if (e->type == EnterNotify) {
2419
for (int i = 0; i < ELECTRIC_COUNT; ++i)
2420
if (electric_windows[i] != None && e->xcrossing.window == electric_windows[i]) {
2421
// The user entered an electric border
2422
checkElectricBorder(QPoint(e->xcrossing.x_root, e->xcrossing.y_root), e->xcrossing.time);
2426
if (e->type == ClientMessage) {
2427
if (e->xclient.message_type == atoms->xdnd_position) {
2428
for (int i = 0; i < ELECTRIC_COUNT; ++i)
2429
if (electric_windows[i] != None && e->xclient.window == electric_windows[i]) {
2431
checkElectricBorder(QPoint(
2432
e->xclient.data.l[2] >> 16, e->xclient.data.l[2] & 0xffff), xTime());
2440
//-----------------------------------------------------------------------------
2443
void Workspace::addTopMenu(Client* c)
2445
assert(c->isTopMenu());
2446
assert(!topmenus.contains(c));
2448
if (managingTopMenus()) {
2449
int minsize = c->minSize().height();
2450
if (minsize > topMenuHeight()) {
2451
topmenu_height = minsize;
2452
updateTopMenuGeometry();
2454
updateTopMenuGeometry(c);
2455
updateCurrentTopMenu();
2458
//kDebug( 1212 ) << "NEW TOPMENU:" << c;
2461
void Workspace::removeTopMenu(Client* c)
2463
//if ( c->isTopMenu() )
2464
// kDebug( 1212 ) << "REMOVE TOPMENU:" << c;
2466
assert(c->isTopMenu());
2467
assert(topmenus.contains(c));
2468
topmenus.removeAll(c);
2469
updateCurrentTopMenu();
2470
// TODO: Reduce topMenuHeight() if possible?
2473
void Workspace::lostTopMenuSelection()
2475
//kDebug( 1212 ) << "lost TopMenu selection";
2477
// Make sure this signal is always set when not owning the selection
2478
disconnect(topmenu_watcher, SIGNAL(lostOwner()), this, SLOT(lostTopMenuOwner()));
2479
connect(topmenu_watcher, SIGNAL(lostOwner()), this, SLOT(lostTopMenuOwner()));
2480
if (!managing_topmenus)
2482
connect(topmenu_watcher, SIGNAL(lostOwner()), this, SLOT(lostTopMenuOwner()));
2483
disconnect(topmenu_selection, SIGNAL(lostOwnership()), this, SLOT(lostTopMenuSelection()));
2484
managing_topmenus = false;
2485
delete topmenu_space;
2486
topmenu_space = NULL;
2488
for (ClientList::ConstIterator it = topmenus.constBegin();
2489
it != topmenus.constEnd();
2491
(*it)->checkWorkspacePosition();
2494
void Workspace::lostTopMenuOwner()
2496
if (!options->topMenuEnabled())
2498
//kDebug( 1212 ) << "TopMenu selection lost owner";
2499
if (!topmenu_selection->claim(false)) {
2500
//kDebug( 1212 ) << "Failed to claim TopMenu selection";
2503
//kDebug( 1212 ) << "Claimed TopMenu selection";
2504
setupTopMenuHandling();
2507
void Workspace::setupTopMenuHandling()
2509
if (managing_topmenus)
2511
connect(topmenu_selection, SIGNAL(lostOwnership()), this, SLOT(lostTopMenuSelection()));
2512
disconnect(topmenu_watcher, SIGNAL(lostOwner()), this, SLOT(lostTopMenuOwner()));
2513
managing_topmenus = true;
2514
topmenu_space = new QWidget(NULL, Qt::X11BypassWindowManagerHint);
2516
stack[0] = supportWindow->winId();
2517
stack[1] = topmenu_space->winId();
2518
XRestackWindows(display(), stack, 2);
2519
updateTopMenuGeometry();
2520
topmenu_space->show();
2522
updateCurrentTopMenu();
2525
int Workspace::topMenuHeight() const
2527
if (topmenu_height == 0) {
2528
// Simply create a dummy menubar and use its preferred height as the menu height
2530
tmpmenu.addAction("dummy");
2531
topmenu_height = tmpmenu.sizeHint().height();
2533
return topmenu_height;
2536
KDecoration* Workspace::createDecoration(KDecorationBridge* bridge)
2538
if (!hasDecorationPlugin()) {
2541
return mgr->createDecoration(bridge);
2545
* Returns a list of all colors (KDecorationDefines::ColorType) the current
2546
* decoration supports
2548
QList<int> Workspace::decorationSupportedColors() const
2551
if (!hasDecorationPlugin()) {
2554
KDecorationFactory* factory = mgr->factory();
2555
for (Ability ab = ABILITYCOLOR_FIRST;
2556
ab < ABILITYCOLOR_END;
2557
ab = static_cast<Ability>(ab + 1))
2558
if (factory->supports(ab))
2563
QString Workspace::desktopName(int desk) const
2565
return QString::fromUtf8(rootInfo->desktopName(desk));
2568
bool Workspace::checkStartupNotification(Window w, KStartupInfoId& id, KStartupInfoData& data)
2570
return startup->checkStartup(w, id, data) == KStartupInfo::Match;
2574
* Puts the focus on a dummy window
2575
* Just using XSetInputFocus() with None would block keyboard input
2577
void Workspace::focusToNull()
2579
XSetInputFocus(display(), null_focus_window, RevertToPointerRoot, xTime());
2582
void Workspace::helperDialog(const QString& message, const Client* c)
2586
if (message == "noborderaltf3") {
2587
KAction* action = qobject_cast<KAction*>(keys->action("Window Operations Menu"));
2588
assert(action != NULL);
2589
QString shortcut = QString("%1 (%2)").arg(action->text())
2590
.arg(action->globalShortcut().primary().toString(QKeySequence::NativeText));
2591
args << "--msgbox" << i18n(
2592
"You have selected to show a window without its border.\n"
2593
"Without the border, you will not be able to enable the border "
2594
"again using the mouse: use the window operations menu instead, "
2595
"activated using the %1 keyboard shortcut.",
2597
type = "altf3warning";
2598
} else if (message == "fullscreenaltf3") {
2599
KAction* action = qobject_cast<KAction*>(keys->action("Window Operations Menu"));
2600
assert(action != NULL);
2601
QString shortcut = QString("%1 (%2)").arg(action->text())
2602
.arg(action->globalShortcut().primary().toString(QKeySequence::NativeText));
2603
args << "--msgbox" << i18n(
2604
"You have selected to show a window in fullscreen mode.\n"
2605
"If the application itself does not have an option to turn the fullscreen "
2606
"mode off you will not be able to disable it "
2607
"again using the mouse: use the window operations menu instead, "
2608
"activated using the %1 keyboard shortcut.",
2610
type = "altf3warning";
2613
if (!type.isEmpty()) {
2614
KConfig cfg("kwin_dialogsrc");
2615
KConfigGroup cg(&cfg, "Notification Messages"); // Depends on KMessageBox
2616
if (!cg.readEntry(type, true))
2618
args << "--dontagain" << "kwin_dialogsrc:" + type;
2621
args << "--embed" << QString::number(c->window());
2622
KProcess::startDetached("kdialog", args);
2625
void Workspace::setShowingDesktop(bool showing)
2627
rootInfo->setShowingDesktop(showing);
2628
showing_desktop = showing;
2629
++block_showing_desktop;
2630
if (showing_desktop) {
2631
showing_desktop_clients.clear();
2633
ClientList cls = stackingOrder();
2634
// Find them first, then minimize, otherwise transients may get minimized with the window
2635
// they're transient for
2636
for (ClientList::ConstIterator it = cls.constBegin();
2637
it != cls.constEnd();
2639
if ((*it)->isOnCurrentActivity() && (*it)->isOnCurrentDesktop() && (*it)->isShown(true) && !(*it)->isSpecialWindow())
2640
showing_desktop_clients.prepend(*it); // Topmost first to reduce flicker
2641
for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
2642
it != showing_desktop_clients.constEnd();
2646
if (Client* desk = findDesktop(true, currentDesktop()))
2649
for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
2650
it != showing_desktop_clients.constEnd();
2652
(*it)->unminimize();
2653
if (showing_desktop_clients.count() > 0)
2654
requestFocus(showing_desktop_clients.first());
2655
showing_desktop_clients.clear();
2657
--block_showing_desktop;
2661
* Following Kicker's behavior:
2662
* Changing a virtual desktop resets the state and shows the windows again.
2663
* Unminimizing a window resets the state but keeps the windows hidden (except
2664
* the one that was unminimized).
2665
* A new window resets the state and shows the windows again, with the new window
2666
* being active. Due to popular demand (#67406) by people who apparently
2667
* don't see a difference between "show desktop" and "minimize all", this is not
2668
* true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
2669
* a new window resets the state but doesn't show windows.
2671
void Workspace::resetShowingDesktop(bool keep_hidden)
2673
if (block_showing_desktop > 0)
2675
rootInfo->setShowingDesktop(false);
2676
showing_desktop = false;
2677
++block_showing_desktop;
2679
for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
2680
it != showing_desktop_clients.constEnd();
2682
(*it)->unminimize();
2684
showing_desktop_clients.clear();
2685
--block_showing_desktop;
2689
* Activating/deactivating this feature works like this:
2690
* When nothing is active, and the shortcut is pressed, global shortcuts are disabled
2691
* (using global_shortcuts_disabled)
2692
* When a window that has disabling forced is activated, global shortcuts are disabled.
2693
* (using global_shortcuts_disabled_for_client)
2694
* When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
2695
* or for a client), they are enabled again.
2697
void Workspace::slotDisableGlobalShortcuts()
2699
if (global_shortcuts_disabled || global_shortcuts_disabled_for_client)
2700
disableGlobalShortcuts(false);
2702
disableGlobalShortcuts(true);
2705
static bool pending_dfc = false;
2707
void Workspace::disableGlobalShortcutsForClient(bool disable)
2709
if (global_shortcuts_disabled_for_client == disable)
2711
if (!global_shortcuts_disabled) {
2714
KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable);
2715
// KWin will get the kipc message too
2719
void Workspace::disableGlobalShortcuts(bool disable)
2721
KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable);
2722
// KWin will get the kipc message too
2725
void Workspace::slotBlockShortcuts(int data)
2727
if (pending_dfc && data) {
2728
global_shortcuts_disabled_for_client = true;
2729
pending_dfc = false;
2731
global_shortcuts_disabled = data;
2732
global_shortcuts_disabled_for_client = false;
2734
// Update also Alt+LMB actions etc.
2735
for (ClientList::ConstIterator it = clients.constBegin();
2736
it != clients.constEnd();
2738
(*it)->updateMouseGrab();
2741
// Optimized version of QCursor::pos() that tries to avoid X roundtrips
2742
// by updating the value only when the X timestamp changes.
2743
static QPoint last_cursor_pos;
2744
static int last_buttons = 0;
2745
static Time last_cursor_timestamp = CurrentTime;
2746
static QTimer* last_cursor_timer;
2748
QPoint Workspace::cursorPos() const
2750
if (last_cursor_timestamp == CurrentTime ||
2751
last_cursor_timestamp != QX11Info::appTime()) {
2752
last_cursor_timestamp = QX11Info::appTime();
2755
int root_x, root_y, win_x, win_y;
2757
XQueryPointer(display(), rootWindow(), &root, &child,
2758
&root_x, &root_y, &win_x, &win_y, &state);
2759
last_cursor_pos = QPoint(root_x, root_y);
2760
last_buttons = state;
2761
if (last_cursor_timer == NULL) {
2762
Workspace* ws = const_cast<Workspace*>(this);
2763
last_cursor_timer = new QTimer(ws);
2764
last_cursor_timer->setSingleShot(true);
2765
connect(last_cursor_timer, SIGNAL(timeout()), ws, SLOT(resetCursorPosTime()));
2767
last_cursor_timer->start(0);
2769
return last_cursor_pos;
2773
* Because of QTimer's and the impossibility to get events for all mouse
2774
* movements (at least I haven't figured out how) the position needs
2775
* to be also refetched after each return to the event loop.
2777
void Workspace::resetCursorPosTime()
2779
last_cursor_timestamp = CurrentTime;
2782
void Workspace::checkCursorPos()
2784
QPoint last = last_cursor_pos;
2785
int lastb = last_buttons;
2786
cursorPos(); // Update if needed
2787
if (last != last_cursor_pos || lastb != last_buttons) {
2788
emit mouseChanged(last_cursor_pos, last,
2789
x11ToQtMouseButtons(last_buttons), x11ToQtMouseButtons(lastb),
2790
x11ToQtKeyboardModifiers(last_buttons), x11ToQtKeyboardModifiers(lastb));
2794
int Workspace::indexOfClientGroup(ClientGroup* group)
2796
return clientGroups.indexOf(group);
2799
void Workspace::moveItemToClientGroup(ClientGroup* oldGroup, int oldIndex,
2800
ClientGroup* group, int index)
2802
Client* c = oldGroup->clients().at(oldIndex);
2803
group->add(c, index, true);
2806
// To accept "mainwindow#1" to "mainwindow#2"
2807
static QByteArray truncatedWindowRole(QByteArray a)
2809
int i = a.indexOf('#');
2817
Client* Workspace::findSimilarClient(Client* c)
2819
// Attempt to find a similar window to the input. If we find multiple possibilities that are in
2820
// different groups then ignore all of them. This function is for automatic window grouping.
2821
Client* found = NULL;
2823
// See if the window has a group ID to match with
2824
QString wGId = c->rules()->checkAutogroupById(QString());
2825
if (!wGId.isEmpty()) {
2826
foreach (Client * cl, clients) {
2827
if (wGId == cl->rules()->checkAutogroupById(QString())) {
2828
if (found && found->clientGroup() != cl->clientGroup()) { // We've found two, ignore both
2830
break; // Continue to the next test
2839
// If this is a transient window don't take a guess
2840
if (c->isTransient())
2843
// If we don't have an ID take a guess
2844
if (c->rules()->checkAutogrouping(options->autogroupSimilarWindows)) {
2845
QByteArray wRole = truncatedWindowRole(c->windowRole());
2846
foreach (Client * cl, clients) {
2847
QByteArray wRoleB = truncatedWindowRole(cl->windowRole());
2848
if (c->resourceClass() == cl->resourceClass() && // Same resource class
2849
wRole == wRoleB && // Same window role
2850
cl->isNormalWindow()) { // Normal window TODO: Can modal windows be "normal"?
2851
if (found && found->clientGroup() != cl->clientGroup()) // We've found two, ignore both
2861
Outline* Workspace::outline()
2868
#include "workspace.moc"