~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/workspace.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
 
6
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
 
7
 
 
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.
 
12
 
 
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.
 
17
 
 
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
*********************************************************************/
 
21
 
 
22
//#define QT_CLEAN_NAMESPACE
 
23
 
 
24
#include "workspace.h"
 
25
 
 
26
#include <kapplication.h>
 
27
#include <kstartupinfo.h>
 
28
#include <fixx11h.h>
 
29
#include <kconfig.h>
 
30
#include <kglobal.h>
 
31
#include <klocale.h>
 
32
#include <QRegExp>
 
33
#include <QPainter>
 
34
#include <QBitmap>
 
35
#include <QClipboard>
 
36
#include <kmenubar.h>
 
37
#include <kprocess.h>
 
38
#include <kglobalaccel.h>
 
39
#include <QToolButton>
 
40
#include <kactioncollection.h>
 
41
#include <kaction.h>
 
42
#include <kconfiggroup.h>
 
43
#include <kcmdlineargs.h>
 
44
#include <QtDBus/QtDBus>
 
45
 
 
46
#include "client.h"
 
47
#include "tile.h"
 
48
#include "tabbox.h"
 
49
#include "desktopchangeosd.h"
 
50
#include "atoms.h"
 
51
#include "placement.h"
 
52
#include "notifications.h"
 
53
#include "outline.h"
 
54
#include "group.h"
 
55
#include "rules.h"
 
56
#include "kwinadaptor.h"
 
57
#include "unmanaged.h"
 
58
#include "scene.h"
 
59
#include "deleted.h"
 
60
#include "effects.h"
 
61
#include "tilinglayout.h"
 
62
 
 
63
#include "scripting/scripting.h"
 
64
 
 
65
#include <X11/extensions/shape.h>
 
66
#include <X11/keysym.h>
 
67
#include <X11/keysymdef.h>
 
68
#include <X11/cursorfont.h>
 
69
#include <QX11Info>
 
70
#include <stdio.h>
 
71
#include <kauthorized.h>
 
72
#include <ktoolinvocation.h>
 
73
#include <kglobalsettings.h>
 
74
#include <kwindowsystem.h>
 
75
#include <kwindowinfo.h>
 
76
 
 
77
#include <kephal/screens.h>
 
78
 
 
79
namespace KWin
 
80
{
 
81
 
 
82
extern int screen_number;
 
83
static const int KWIN_MAX_NUMBER_DESKTOPS = 20;
 
84
 
 
85
Workspace* Workspace::_self = 0;
 
86
 
 
87
//-----------------------------------------------------------------------------
 
88
// Rikkus: This class is too complex. It needs splitting further.
 
89
// It's a nightmare to understand, especially with so few comments :(
 
90
//
 
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
 
94
// code.
 
95
//-----------------------------------------------------------------------------
 
96
 
 
97
Workspace::Workspace(bool restore)
 
98
    : QObject(0)
 
99
    // Desktop layout
 
100
    , desktopCount_(0)   // This is an invalid state
 
101
    , desktopGridSize_(1, 2)   // Default to two rows
 
102
    , desktopGrid_(new int[2])
 
103
    , currentDesktop_(0)
 
104
    , tilingEnabled_(false)
 
105
    // Unsorted
 
106
    , active_popup(NULL)
 
107
    , active_popup_client(NULL)
 
108
    , temporaryRulesMessages("_KDE_NET_WM_TEMPORARY_RULES", NULL, false)
 
109
    , rules_updates_disabled(false)
 
110
    , active_client(0)
 
111
    , last_active_client(0)
 
112
    , most_recently_raised(0)
 
113
    , movingClient(0)
 
114
    , pending_take_activity(NULL)
 
115
    , active_screen(0)
 
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)
 
124
    , tab_grab(false)
 
125
    , mouse_emulation(false)
 
126
    , block_focus(0)
 
127
    , tab_box(0)
 
128
    , desktop_change_osd(0)
 
129
    , popup(0)
 
130
    , advanced_popup(0)
 
131
    , trans_popup(0)
 
132
    , desk_popup(0)
 
133
    , activity_popup(0)
 
134
    , add_tabs_popup(0)
 
135
    , switch_to_tab_popup(0)
 
136
    , keys(0)
 
137
    , client_keys(NULL)
 
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)
 
144
    , startup(0)
 
145
    , managing_topmenus(false)
 
146
    , topmenu_selection(NULL)
 
147
    , topmenu_watcher(NULL)
 
148
    , topmenu_height(0)
 
149
    , topmenu_space(NULL)
 
150
    , set_active_client_recursion(0)
 
151
    , block_stacking_updates(0)
 
152
    , forced_global_mouse_grab(false)
 
153
    , cm_selection(NULL)
 
154
    , compositingSuspended(false)
 
155
    , compositingBlocked(false)
 
156
    , xrrRefreshRate(0)
 
157
    , overlay(None)
 
158
    , overlay_visible(true)
 
159
    , overlay_shown(false)
 
160
    , transSlider(NULL)
 
161
    , transButton(NULL)
 
162
    , forceUnredirectCheck(true)
 
163
    , m_finishingCompositing(false)
 
164
{
 
165
    (void) new KWinAdaptor(this);
 
166
 
 
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()));
 
173
 
 
174
    // Initialize desktop grid array
 
175
    desktopGrid_[0] = 0;
 
176
    desktopGrid_[1] = 0;
 
177
 
 
178
    _self = this;
 
179
    mgr = new PluginMgr;
 
180
    QX11Info info;
 
181
    default_colormap = DefaultColormap(display(), info.screen());
 
182
    installed_colormap = default_colormap;
 
183
 
 
184
    for (int i = 0; i < ELECTRIC_COUNT; ++i) {
 
185
        electric_reserved[i] = 0;
 
186
        electric_windows[i] = None;
 
187
    }
 
188
 
 
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);
 
196
 
 
197
    updateXTime(); // Needed for proper initialization of user_time in Client ctor
 
198
 
 
199
    delayFocusTimer = 0;
 
200
 
 
201
    if (restore)
 
202
        loadSessionInfo();
 
203
 
 
204
    loadWindowRules();
 
205
 
 
206
    // Call this before XSelectInput() on the root window
 
207
    startup = new KStartupInfo(
 
208
        KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this);
 
209
 
 
210
    // Select windowmanager privileges
 
211
    XSelectInput(display(), rootWindow(),
 
212
                 KeyPressMask |
 
213
                 PropertyChangeMask |
 
214
                 ColormapChangeMask |
 
215
                 SubstructureRedirectMask |
 
216
                 SubstructureNotifyMask |
 
217
                 FocusChangeMask | // For NotifyDetailNone
 
218
                 ExposureMask
 
219
                );
 
220
 
 
221
    Extensions::init();
 
222
    compositingSuspended = !options->useCompositing;
 
223
    // need to create the tabbox before compositing scene is setup
 
224
    tab_box = new TabBox::TabBox(this);
 
225
    setupCompositing();
 
226
 
 
227
    // Compatibility
 
228
    long data = 1;
 
229
 
 
230
    XChangeProperty(
 
231
        display(),
 
232
        rootWindow(),
 
233
        atoms->kwin_running,
 
234
        atoms->kwin_running,
 
235
        32,
 
236
        PropModeAppend,
 
237
        (unsigned char*)(&data),
 
238
        1
 
239
    );
 
240
 
 
241
    client_keys = new KActionCollection(this);
 
242
    initShortcuts();
 
243
    desktop_change_osd = new DesktopChangeOSD(this);
 
244
    m_outline = new Outline();
 
245
 
 
246
    init();
 
247
 
 
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()));
 
252
 
 
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)));
 
256
}
 
257
 
 
258
void Workspace::init()
 
259
{
 
260
    reserveElectricBorderActions(true);
 
261
    if (options->electricBorders() == Options::ElectricAlways)
 
262
        reserveElectricBorderSwitching(true);
 
263
    updateElectricBorders();
 
264
 
 
265
    // Not used yet
 
266
    //topDock = 0L;
 
267
    //maximizedWindowCounter = 0;
 
268
 
 
269
    supportWindow = new QWidget(NULL, Qt::X11BypassWindowManagerHint);
 
270
    XLowerWindow(display(), supportWindow->winId());   // See usage in layers.cpp
 
271
 
 
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);
 
277
 
 
278
    unsigned long protocols[5] = {
 
279
        NET::Supported |
 
280
        NET::SupportingWMCheck |
 
281
        NET::ClientList |
 
282
        NET::ClientListStacking |
 
283
        NET::DesktopGeometry |
 
284
        NET::NumberOfDesktops |
 
285
        NET::CurrentDesktop |
 
286
        NET::ActiveWindow |
 
287
        NET::WorkArea |
 
288
        NET::CloseWindow |
 
289
        NET::DesktopNames |
 
290
        NET::WMName |
 
291
        NET::WMVisibleName |
 
292
        NET::WMDesktop |
 
293
        NET::WMWindowType |
 
294
        NET::WMState |
 
295
        NET::WMStrut |
 
296
        NET::WMIconGeometry |
 
297
        NET::WMIcon |
 
298
        NET::WMPid |
 
299
        NET::WMMoveResize |
 
300
        NET::WMFrameExtents |
 
301
        NET::WMPing
 
302
        ,
 
303
        NET::NormalMask |
 
304
        NET::DesktopMask |
 
305
        NET::DockMask |
 
306
        NET::ToolbarMask |
 
307
        NET::MenuMask |
 
308
        NET::DialogMask |
 
309
        NET::OverrideMask |
 
310
        NET::TopMenuMask |
 
311
        NET::UtilityMask |
 
312
        NET::SplashMask |
 
313
        // No compositing window types here unless we support them also as managed window types
 
314
        0
 
315
        ,
 
316
        NET::Modal |
 
317
        //NET::Sticky | // Large desktops not supported (and probably never will be)
 
318
        NET::MaxVert |
 
319
        NET::MaxHoriz |
 
320
        NET::Shaded |
 
321
        NET::SkipTaskbar |
 
322
        NET::KeepAbove |
 
323
        //NET::StaysOnTop | // The same like KeepAbove
 
324
        NET::SkipPager |
 
325
        NET::Hidden |
 
326
        NET::FullScreen |
 
327
        NET::KeepBelow |
 
328
        NET::DemandsAttention |
 
329
        0
 
330
        ,
 
331
        NET::WM2UserTime |
 
332
        NET::WM2StartupId |
 
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 |
 
342
        NET::WM2KDEShadow |
 
343
        0
 
344
        ,
 
345
        NET::ActionMove |
 
346
        NET::ActionResize |
 
347
        NET::ActionMinimize |
 
348
        NET::ActionShade |
 
349
        //NET::ActionStick | // Sticky state is not supported
 
350
        NET::ActionMaxVert |
 
351
        NET::ActionMaxHoriz |
 
352
        NET::ActionFullScreen |
 
353
        NET::ActionChangeDesktop |
 
354
        NET::ActionClose |
 
355
        0
 
356
        ,
 
357
    };
 
358
 
 
359
    if (hasDecorationPlugin() && mgr->factory()->supports(AbilityExtendIntoClientArea))
 
360
        protocols[ NETRootInfo::PROTOCOLS2 ] |= NET::WM2FrameOverlap;
 
361
 
 
362
    QX11Info info;
 
363
    rootInfo = new RootInfo(this, display(), supportWindow->winId(), "KWin", protocols, 5, info.screen());
 
364
 
 
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);
 
370
    int initial_desktop;
 
371
    if (!kapp->isSessionRestored())
 
372
        initial_desktop = client_info.currentDesktop();
 
373
    else {
 
374
        KConfigGroup group(kapp->sessionConfig(), "Session");
 
375
        initial_desktop = group.readEntry("desktop", 1);
 
376
    }
 
377
    if (!setCurrentDesktop(initial_desktop))
 
378
        setCurrentDesktop(1);
 
379
    allActivities_ = activityController_.listActivities();
 
380
    updateCurrentActivity(activityController_.currentActivity());
 
381
 
 
382
    // Now we know how many desktops we'll have, thus we initialize the positioning object
 
383
    initPositioning = new Placement(this);
 
384
 
 
385
    reconfigureTimer.setSingleShot(true);
 
386
    updateToolWindowsTimer.setSingleShot(true);
 
387
 
 
388
    connect(&reconfigureTimer, SIGNAL(timeout()), this, SLOT(slotReconfigure()));
 
389
    connect(&updateToolWindowsTimer, SIGNAL(timeout()), this, SLOT(slotUpdateToolWindows()));
 
390
    connect(&mousePollingTimer, SIGNAL(timeout()), SLOT(performMousePoll()));
 
391
 
 
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)));
 
395
 
 
396
    active_client = NULL;
 
397
    rootInfo->setActiveWindow(None);
 
398
    focusToNull();
 
399
    if (!kapp->isSessionRestored())
 
400
        ++block_focus; // Because it will be set below
 
401
 
 
402
    char nm[100];
 
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
 
408
 
 
409
    {
 
410
        // Begin updates blocker block
 
411
        StackingUpdatesBlocker blocker(this);
 
412
 
 
413
        if (options->topMenuEnabled() && topmenu_selection->claim(false))
 
414
            setupTopMenuHandling(); // This can call updateStackingOrder()
 
415
        else
 
416
            lostTopMenuSelection();
 
417
 
 
418
        unsigned int i, nwins;
 
419
        Window root_return, parent_return;
 
420
        Window* wins;
 
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]);
 
428
                continue;
 
429
            }
 
430
            if (topmenu_space && topmenu_space->winId() == wins[i])
 
431
                continue;
 
432
            if (attr.map_state != IsUnmapped) {
 
433
                if (fixoffset)
 
434
                    fixPositionAfterCrash(wins[ i ], attr);
 
435
                createClient(wins[i], true);
 
436
            }
 
437
        }
 
438
        if (wins)
 
439
            XFree((void*)(wins));
 
440
 
 
441
        // Propagate clients, will really happen at the end of the updates blocker block
 
442
        updateStackingOrder(true);
 
443
 
 
444
        updateClientArea();
 
445
 
 
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);
 
449
        delete[] 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);
 
456
 
 
457
    } // End updates blocker block
 
458
 
 
459
    Client* new_active_client = NULL;
 
460
    if (!kapp->isSessionRestored()) {
 
461
        --block_focus;
 
462
        new_active_client = findClient(WindowMatchPredicate(client_info.activeWindow()));
 
463
    }
 
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());
 
471
    }
 
472
    if (new_active_client != NULL)
 
473
        activateClient(new_active_client);
 
474
 
 
475
    // Enable/disable tiling
 
476
    setTilingEnabled(options->tilingOn);
 
477
 
 
478
    // SELI TODO: This won't work with unreasonable focus policies,
 
479
    // and maybe in rare cases also if the selected client doesn't
 
480
    // want focus
 
481
    workspaceInit = false;
 
482
 
 
483
    // TODO: ungrabXServer()
 
484
}
 
485
 
 
486
Workspace::~Workspace()
 
487
{
 
488
    finishCompositing();
 
489
    blockStackingUpdates(true);
 
490
 
 
491
    // TODO: grabXServer();
 
492
 
 
493
    // Use stacking_order, so that kwin --replace keeps stacking order
 
494
    for (ClientList::ConstIterator it = stacking_order.constBegin();
 
495
            it != stacking_order.constEnd();
 
496
            ++it) {
 
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()
 
501
        // from crashing.
 
502
        clients.removeAll(*it);
 
503
        desktops.removeAll(*it);
 
504
    }
 
505
    for (UnmanagedList::ConstIterator it = unmanaged.constBegin();
 
506
            it != unmanaged.constEnd();
 
507
            ++it)
 
508
        (*it)->release();
 
509
    delete tab_box;
 
510
    delete desktop_change_osd;
 
511
    delete m_outline;
 
512
    discardPopup();
 
513
    XDeleteProperty(display(), rootWindow(), atoms->kwin_running);
 
514
 
 
515
    writeWindowRules();
 
516
    KGlobal::config()->sync();
 
517
 
 
518
    delete rootInfo;
 
519
    delete supportWindow;
 
520
    delete mgr;
 
521
    delete startup;
 
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();
 
529
        rules.pop_front();
 
530
    }
 
531
    foreach (SessionInfo * s, session)
 
532
    delete s;
 
533
    XDestroyWindow(display(), null_focus_window);
 
534
 
 
535
    // TODO: ungrabXServer();
 
536
 
 
537
    delete[] desktopGrid_;
 
538
 
 
539
    _self = 0;
 
540
}
 
541
 
 
542
Client* Workspace::createClient(Window w, bool is_mapped)
 
543
{
 
544
    StackingUpdatesBlocker blocker(this);
 
545
    Client* c = new Client(this);
 
546
    if (!c->manage(w, is_mapped)) {
 
547
        Client::deleteClient(c, Allowed);
 
548
        return NULL;
 
549
    }
 
550
    addClient(c, Allowed);
 
551
 
 
552
    tilingLayouts.resize(numberOfDesktops() + 1);
 
553
 
 
554
    createTile(c);
 
555
 
 
556
    if (scene)
 
557
        scene->windowAdded(c);
 
558
    return c;
 
559
}
 
560
 
 
561
Unmanaged* Workspace::createUnmanaged(Window w)
 
562
{
 
563
    if (w == overlay)
 
564
        return NULL;
 
565
    Unmanaged* c = new Unmanaged(this);
 
566
    if (!c->track(w)) {
 
567
        Unmanaged::deleteUnmanaged(c, Allowed);
 
568
        return NULL;
 
569
    }
 
570
    addUnmanaged(c, Allowed);
 
571
    if (scene)
 
572
        scene->windowAdded(c);
 
573
    emit unmanagedAdded(c);
 
574
    return c;
 
575
}
 
576
 
 
577
void Workspace::addClient(Client* c, allowed_t)
 
578
{
 
579
    Group* grp = findGroup(c->window());
 
580
 
 
581
    KWindowInfo info = KWindowSystem::windowInfo(c->window(), -1U, NET::WM2WindowClass);
 
582
 
 
583
    /*
 
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);
 
588
    }
 
589
    }*/
 
590
 
 
591
    emit clientAdded(c);
 
592
 
 
593
    if (grp != NULL)
 
594
        grp->gotLeader(c);
 
595
 
 
596
    if (c->isDesktop()) {
 
597
        desktops.append(c);
 
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
 
600
    } else {
 
601
        updateFocusChains(c, FocusChainUpdate);   // Add to focus chain if not already there
 
602
        clients.append(c);
 
603
    }
 
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
 
608
    if (c->isTopMenu())
 
609
        addTopMenu(c);
 
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()) {
 
614
        raiseClient(c);
 
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()));
 
618
    }
 
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();
 
625
    if (tab_grab)
 
626
        tab_box->reset(true);
 
627
}
 
628
 
 
629
void Workspace::addUnmanaged(Unmanaged* c, allowed_t)
 
630
{
 
631
    unmanaged.append(c);
 
632
    x_stacking_dirty = true;
 
633
}
 
634
 
 
635
/**
 
636
 * Destroys the client \a c
 
637
 */
 
638
void Workspace::removeClient(Client* c, allowed_t)
 
639
{
 
640
    emit clientRemoved(c);
 
641
 
 
642
    if (c == active_popup_client)
 
643
        closeActivePopup();
 
644
 
 
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
 
650
    }
 
651
 
 
652
    if (c->isDialog())
 
653
        Notify::raise(Notify::TransDelete);
 
654
    if (c->isNormalWindow())
 
655
        Notify::raise(Notify::Delete);
 
656
 
 
657
    if (tab_grab && tab_box->currentClient() == c)
 
658
        tab_box->nextPrev(true);
 
659
 
 
660
    Q_ASSERT(clients.contains(c) || desktops.contains(c));
 
661
    if (tilingEnabled() && tilingLayouts.value(c->desktop())) {
 
662
        removeTile(c);
 
663
    }
 
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);
 
675
    if (c->isTopMenu())
 
676
        removeTopMenu(c);
 
677
    Group* group = findGroup(c->window());
 
678
    if (group != NULL)
 
679
        group->lostLeader();
 
680
 
 
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)
 
690
        cancelDelayFocus();
 
691
 
 
692
    updateStackingOrder(true);
 
693
 
 
694
    updateCompositeBlocking();
 
695
 
 
696
    if (tab_grab)
 
697
        tab_box->reset(true);
 
698
 
 
699
    updateClientArea();
 
700
}
 
701
 
 
702
void Workspace::removeUnmanaged(Unmanaged* c, allowed_t)
 
703
{
 
704
    assert(unmanaged.contains(c));
 
705
    unmanaged.removeAll(c);
 
706
    x_stacking_dirty = true;
 
707
}
 
708
 
 
709
void Workspace::addDeleted(Deleted* c, allowed_t)
 
710
{
 
711
    assert(!deleted.contains(c));
 
712
    deleted.append(c);
 
713
    x_stacking_dirty = true;
 
714
}
 
715
 
 
716
void Workspace::removeDeleted(Deleted* c, allowed_t)
 
717
{
 
718
    assert(deleted.contains(c));
 
719
    if (scene)
 
720
        scene->windowDeleted(c);
 
721
    emit deletedRemoved(c);
 
722
    deleted.removeAll(c);
 
723
    x_stacking_dirty = true;
 
724
}
 
725
 
 
726
void Workspace::updateFocusChains(Client* c, FocusChainChange change)
 
727
{
 
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);
 
732
        return;
 
733
    }
 
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);
 
743
                else
 
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);
 
750
                else
 
751
                    focus_chain[i].append(c);   // Otherwise add as the first one
 
752
            }
 
753
        }
 
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);
 
768
                    else
 
769
                        focus_chain[i].append(c);   // Otherwise add as the first one
 
770
                }
 
771
            } else
 
772
                focus_chain[i].removeAll(c);
 
773
        }
 
774
    }
 
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);
 
786
        else
 
787
            global_focus_chain.append(c);   // Otherwise add as the first one
 
788
    }
 
789
}
 
790
 
 
791
void Workspace::updateCurrentTopMenu()
 
792
{
 
793
    if (!managingTopMenus())
 
794
        return;
 
795
    // toplevel menubar handling
 
796
    Client* menubar = 0;
 
797
    bool block_desktop_menubar = false;
 
798
    if (active_client) {
 
799
        // Show the new menu bar first...
 
800
        Client* menu_client = active_client;
 
801
        for (;;) {
 
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();
 
806
                    ++it)
 
807
                if ((*it)->isTopMenu()) {
 
808
                    menubar = *it;
 
809
                    break;
 
810
                }
 
811
            if (menubar != NULL || !menu_client->isTransient())
 
812
                break;
 
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();
 
816
        }
 
817
        if (!menubar) {
 
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();
 
821
                    ++it)
 
822
                if ((*it)->isTopMenu()) {
 
823
                    menubar = *it;
 
824
                    break;
 
825
                }
 
826
        }
 
827
    }
 
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();
 
834
                    ++it)
 
835
                if ((*it)->isTopMenu()) {
 
836
                    menubar = *it;
 
837
                    break;
 
838
                }
 
839
        }
 
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();
 
846
                    ++it)
 
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()) {
 
851
                    menubar = *it;
 
852
                    break;
 
853
                }
 
854
        }
 
855
    }
 
856
 
 
857
    //kDebug( 1212 ) << "CURRENT TOPMENU:" << menubar << ":" << active_client;
 
858
    if (menubar) {
 
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();
 
871
    }
 
872
 
 
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);
 
877
}
 
878
 
 
879
 
 
880
void Workspace::updateToolWindows(bool also_hide)
 
881
{
 
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();
 
886
                ++it)
 
887
            (*it)->hideClient(false);
 
888
        return;
 
889
    }
 
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())
 
896
            break;
 
897
        if (client->groupTransient()) {
 
898
            group = client->group();
 
899
            break;
 
900
        }
 
901
        client = client->transientFor();
 
902
    }
 
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
 
905
 
 
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();
 
910
            ++it) {
 
911
        if ((*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) {
 
912
            bool show = true;
 
913
            if (!(*it)->isTransient()) {
 
914
                if ((*it)->group()->members().count() == 1)   // Has its own group, keep always visible
 
915
                    show = true;
 
916
                else if (client != NULL && (*it)->group() == client->group())
 
917
                    show = true;
 
918
                else
 
919
                    show = false;
 
920
            } else {
 
921
                if (group != NULL && (*it)->group() == group)
 
922
                    show = true;
 
923
                else if (client != NULL && client->hasTransient((*it), true))
 
924
                    show = true;
 
925
                else
 
926
                    show = false;
 
927
            }
 
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())
 
933
                    show = true;
 
934
                for (ClientList::ConstIterator it2 = mainclients.constBegin();
 
935
                        it2 != mainclients.constEnd();
 
936
                        ++it2) {
 
937
                    if ((*it2)->isSpecialWindow())
 
938
                        show = true;
 
939
                }
 
940
                if (!show)
 
941
                    to_hide.append(*it);
 
942
            }
 
943
            if (show)
 
944
                to_show.append(*it);
 
945
        }
 
946
    } // First show new ones, then hide
 
947
    for (int i = to_show.size() - 1;
 
948
            i >= 0;
 
949
            --i)  // From topmost
 
950
        // TODO: Since this is in stacking order, the order of taskbar entries changes :(
 
951
        to_show.at(i)->hideClient(false);
 
952
    if (also_hide) {
 
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();
 
961
}
 
962
 
 
963
 
 
964
void Workspace::resetUpdateToolWindowsTimer()
 
965
{
 
966
    updateToolWindowsTimer.start(200);
 
967
}
 
968
 
 
969
void Workspace::slotUpdateToolWindows()
 
970
{
 
971
    updateToolWindows(true);
 
972
}
 
973
 
 
974
/**
 
975
 * Updates the current colormap according to the currently active client
 
976
 */
 
977
void Workspace::updateColormap()
 
978
{
 
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;
 
985
    }
 
986
}
 
987
 
 
988
void Workspace::slotReloadConfig()
 
989
{
 
990
    reconfigure();
 
991
}
 
992
 
 
993
void Workspace::reconfigure()
 
994
{
 
995
    reconfigureTimer.start(200);
 
996
}
 
997
 
 
998
/**
 
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.
 
1003
 */
 
1004
bool Workspace::waitForCompositingSetup()
 
1005
{
 
1006
    if (reconfigureTimer.isActive()) {
 
1007
        reconfigureTimer.stop();
 
1008
        slotReconfigure();
 
1009
    }
 
1010
    return compositingActive();
 
1011
}
 
1012
 
 
1013
void Workspace::slotSettingsChanged(int category)
 
1014
{
 
1015
    kDebug(1212) << "Workspace::slotSettingsChanged()";
 
1016
    if (category == KGlobalSettings::SETTINGS_SHORTCUTS)
 
1017
        readShortcuts();
 
1018
}
 
1019
 
 
1020
/**
 
1021
 * Reread settings
 
1022
 */
 
1023
KWIN_PROCEDURE(CheckBorderSizesProcedure, Client, cl->checkBorderSizes(true));
 
1024
 
 
1025
void Workspace::slotReconfigure()
 
1026
{
 
1027
    kDebug(1212) << "Workspace::slotReconfigure()";
 
1028
    reconfigureTimer.stop();
 
1029
 
 
1030
    reserveElectricBorderActions(false);
 
1031
    if (options->electricBorders() == Options::ElectricAlways)
 
1032
        reserveElectricBorderSwitching(false);
 
1033
 
 
1034
    bool borderlessMaximizedWindows = options->borderlessMaximizedWindows();
 
1035
 
 
1036
    KGlobal::config()->reparseConfiguration();
 
1037
    unsigned long changed = options->updateSettings();
 
1038
 
 
1039
    tab_box->reconfigure();
 
1040
    desktop_change_osd->reconfigure();
 
1041
    initPositioning->reinitCascading(0);
 
1042
    readShortcuts();
 
1043
    forEachClient(CheckIgnoreFocusStealingProcedure());
 
1044
    updateToolWindows(true);
 
1045
 
 
1046
    if (hasDecorationPlugin() && mgr->reset(changed)) {
 
1047
        // Decorations need to be recreated
 
1048
 
 
1049
        // This actually seems to make things worse now
 
1050
        //QWidget curtain;
 
1051
        //curtain.setBackgroundMode( NoBackground );
 
1052
        //curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
 
1053
        //curtain.show();
 
1054
 
 
1055
        for (ClientList::ConstIterator it = clients.constBegin();
 
1056
                it != clients.constEnd();
 
1057
                ++it)
 
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++)
 
1063
                (*i)->removeAll();
 
1064
        }
 
1065
        mgr->destroyPreviousPlugin();
 
1066
    } else {
 
1067
        forEachClient(CheckBorderSizesProcedure());
 
1068
        foreach (Client * c, clients)
 
1069
        c->triggerDecorationRepaint();
 
1070
    }
 
1071
 
 
1072
    reserveElectricBorderActions(true);
 
1073
    if (options->electricBorders() == Options::ElectricAlways)
 
1074
        reserveElectricBorderSwitching(true);
 
1075
    updateElectricBorders();
 
1076
 
 
1077
    if (options->topMenuEnabled() && !managingTopMenus()) {
 
1078
        if (topmenu_selection->claim(false))
 
1079
            setupTopMenuHandling();
 
1080
        else
 
1081
            lostTopMenuSelection();
 
1082
    } else if (!options->topMenuEnabled() && managingTopMenus()) {
 
1083
        topmenu_selection->release();
 
1084
        lostTopMenuSelection();
 
1085
    }
 
1086
    topmenu_height = 0; // Invalidate used menu height
 
1087
    if (managingTopMenus()) {
 
1088
        updateTopMenuGeometry();
 
1089
        updateCurrentTopMenu();
 
1090
    }
 
1091
 
 
1092
    if (!compositingSuspended) {
 
1093
        setupCompositing();
 
1094
        if (effects)   // setupCompositing() may fail
 
1095
            effects->reconfigure();
 
1096
        addRepaintFull();
 
1097
    } else
 
1098
        finishCompositing();
 
1099
 
 
1100
    loadWindowRules();
 
1101
    for (ClientList::Iterator it = clients.begin();
 
1102
            it != clients.end();
 
1103
            ++it) {
 
1104
        (*it)->setupWindowRules(true);
 
1105
        (*it)->applyWindowRules();
 
1106
        discardUsedWindowRules(*it, false);
 
1107
    }
 
1108
 
 
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();
 
1115
                ++it) {
 
1116
            if ((*it)->maximizeMode() == MaximizeFull)
 
1117
                (*it)->checkNoBorder();
 
1118
        }
 
1119
    }
 
1120
 
 
1121
    setTilingEnabled(options->tilingOn);
 
1122
    foreach (TilingLayout * layout, tilingLayouts) {
 
1123
        if (layout)
 
1124
            layout->reconfigureTiling();
 
1125
    }
 
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));
 
1130
    } else {
 
1131
        rootInfo->setSupported(NET::WM2FrameOverlap, false);
 
1132
    }
 
1133
}
 
1134
 
 
1135
void Workspace::slotReinitCompositing()
 
1136
{
 
1137
    // Reparse config. Config options will be reloaded by setupCompositing()
 
1138
    KGlobal::config()->reparseConfiguration();
 
1139
 
 
1140
    // Update any settings that can be set in the compositing kcm.
 
1141
    updateElectricBorders();
 
1142
 
 
1143
    // Restart compositing
 
1144
    finishCompositing();
 
1145
 
 
1146
    // resume compositing if suspended
 
1147
    compositingSuspended = false;
 
1148
    options->compositingInitialized = false;
 
1149
    setupCompositing();
 
1150
    if (hasDecorationPlugin()) {
 
1151
        KDecorationFactory* factory = mgr->factory();
 
1152
        factory->reset(SettingCompositing);
 
1153
    }
 
1154
 
 
1155
    if (effects) { // setupCompositing() may fail
 
1156
        effects->reconfigure();
 
1157
        emit compositingToggled(true);
 
1158
    }
 
1159
}
 
1160
 
 
1161
static bool _loading_desktop_settings = false;
 
1162
void Workspace::loadDesktopSettings()
 
1163
{
 
1164
    _loading_desktop_settings = true;
 
1165
    KSharedConfig::Ptr c = KGlobal::config();
 
1166
    QString groupname;
 
1167
    if (screen_number == 0)
 
1168
        groupname = "Desktops";
 
1169
    else
 
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;
 
1178
    }
 
1179
    _loading_desktop_settings = false;
 
1180
}
 
1181
 
 
1182
void Workspace::saveDesktopSettings()
 
1183
{
 
1184
    if (_loading_desktop_settings)
 
1185
        return;
 
1186
    KSharedConfig::Ptr c = KGlobal::config();
 
1187
    QString groupname;
 
1188
    if (screen_number == 0)
 
1189
        groupname = "Desktops";
 
1190
    else
 
1191
        groupname.sprintf("Desktops-screen-%d", screen_number);
 
1192
    KConfigGroup group(c, groupname);
 
1193
 
 
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);
 
1198
        if (s.isEmpty()) {
 
1199
            s = defaultvalue;
 
1200
            rootInfo->setDesktopName(i, s.toUtf8().data());
 
1201
        }
 
1202
 
 
1203
        if (s != defaultvalue) {
 
1204
            group.writeEntry(QString("Name_%1").arg(i), s);
 
1205
        } else {
 
1206
            QString currentvalue = group.readEntry(QString("Name_%1").arg(i), QString());
 
1207
            if (currentvalue != defaultvalue)
 
1208
                group.writeEntry(QString("Name_%1").arg(i), "");
 
1209
        }
 
1210
    }
 
1211
 
 
1212
    // Save to disk
 
1213
    group.sync();
 
1214
}
 
1215
 
 
1216
QStringList Workspace::configModules(bool controlCenter)
 
1217
{
 
1218
    QStringList args;
 
1219
    args <<  "kwindecoration";
 
1220
    if (controlCenter)
 
1221
        args << "kwinoptions";
 
1222
    else if (KAuthorized::authorizeControlModule("kde-kwinoptions.desktop"))
 
1223
        args << "kwinactions" << "kwinfocus" <<  "kwinmoving" << "kwinadvanced"
 
1224
             << "kwinrules" << "kwincompositing" << "kwintabbox" << "kwinscreenedges";
 
1225
    return args;
 
1226
}
 
1227
 
 
1228
void Workspace::configureWM()
 
1229
{
 
1230
    QStringList args;
 
1231
    args << "--icon" << "preferences-system-windows" << configModules(false);
 
1232
    KToolInvocation::kdeinitExec("kcmshell4", args);
 
1233
}
 
1234
 
 
1235
/**
 
1236
 * Avoids managing a window with title \a title
 
1237
 */
 
1238
void Workspace::doNotManage(const QString& title)
 
1239
{
 
1240
    doNotManageList.append(title);
 
1241
}
 
1242
 
 
1243
/**
 
1244
 * Hack for java applets
 
1245
 */
 
1246
bool Workspace::isNotManaged(const QString& title)
 
1247
{
 
1248
    for (QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it) {
 
1249
        QRegExp r((*it));
 
1250
        if (r.indexIn(title) != -1) {
 
1251
            doNotManageList.erase(it);
 
1252
            return true;
 
1253
        }
 
1254
    }
 
1255
    return false;
 
1256
}
 
1257
 
 
1258
/**
 
1259
 * Refreshes all the client windows
 
1260
 */
 
1261
void Workspace::refresh()
 
1262
{
 
1263
    QWidget w(NULL, Qt::X11BypassWindowManagerHint);
 
1264
    w.setGeometry(Kephal::ScreenUtils::desktopGeometry());
 
1265
    w.show();
 
1266
    w.hide();
 
1267
    QApplication::flush();
 
1268
}
 
1269
 
 
1270
/**
 
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
 
1276
 */
 
1277
class ObscuringWindows
 
1278
{
 
1279
public:
 
1280
    ~ObscuringWindows();
 
1281
    void create(Client* c);
 
1282
private:
 
1283
    QList<Window> obscuring_windows;
 
1284
    static QList<Window>* cached;
 
1285
    static unsigned int max_cache_size;
 
1286
};
 
1287
 
 
1288
QList<Window>* ObscuringWindows::cached = 0;
 
1289
unsigned int ObscuringWindows::max_cache_size = 0;
 
1290
 
 
1291
void ObscuringWindows::create(Client* c)
 
1292
{
 
1293
    if (compositing())
 
1294
        return; // Not needed with compositing
 
1295
    if (cached == 0)
 
1296
        cached = new QList<Window>;
 
1297
    Window obs_win;
 
1298
    XWindowChanges chngs;
 
1299
    int mask = CWSibling | CWStackMode;
 
1300
    if (cached->count() > 0) {
 
1301
        cached->removeAll(obs_win = cached->first());
 
1302
        chngs.x = c->x();
 
1303
        chngs.y = c->y();
 
1304
        chngs.width = c->width();
 
1305
        chngs.height = c->height();
 
1306
        mask |= CWX | CWY | CWWidth | CWHeight;
 
1307
    } else {
 
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);
 
1314
    }
 
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);
 
1320
}
 
1321
 
 
1322
ObscuringWindows::~ObscuringWindows()
 
1323
{
 
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();
 
1327
            ++it) {
 
1328
        XUnmapWindow(display(), *it);
 
1329
        if (cached->count() < int(max_cache_size))
 
1330
            cached->prepend(*it);
 
1331
        else
 
1332
            XDestroyWindow(display(), *it);
 
1333
    }
 
1334
}
 
1335
 
 
1336
/**
 
1337
 * Sets the current desktop to \a new_desktop
 
1338
 *
 
1339
 * Shows/Hides windows according to the stacking order and finally
 
1340
 * propages the new desktop to the world
 
1341
 */
 
1342
bool Workspace::setCurrentDesktop(int new_desktop)
 
1343
{
 
1344
    if (new_desktop < 1 || new_desktop > numberOfDesktops())
 
1345
        return false;
 
1346
 
 
1347
    closeActivePopup();
 
1348
    ++block_focus;
 
1349
    // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
 
1350
    StackingUpdatesBlocker blocker(this);
 
1351
 
 
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));
 
1358
 
 
1359
        ObscuringWindows obs_wins;
 
1360
 
 
1361
        currentDesktop_ = new_desktop; // Change the desktop (so that Client::updateVisibility() works)
 
1362
 
 
1363
        for (ClientList::ConstIterator it = stacking_order.constBegin();
 
1364
                it != stacking_order.constEnd();
 
1365
                ++it)
 
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();
 
1370
            }
 
1371
 
 
1372
        // Now propagate the change, after hiding, before showing
 
1373
        rootInfo->setCurrentDesktop(currentDesktop());
 
1374
 
 
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);
 
1383
            }
 
1384
        }
 
1385
 
 
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();
 
1389
 
 
1390
        --block_showing_desktop;
 
1391
        if (showingDesktop())   // Do this only after desktop change to avoid flicker
 
1392
            resetShowingDesktop(false);
 
1393
    }
 
1394
 
 
1395
    // Restore the focus on this desktop
 
1396
    --block_focus;
 
1397
    Client* c = 0;
 
1398
 
 
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
 
1405
        if (!c) {
 
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);
 
1410
                    break;
 
1411
                }
 
1412
            }
 
1413
        }
 
1414
    }
 
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())
 
1419
        c = active_client;
 
1420
 
 
1421
    if (c == NULL && !desktops.isEmpty())
 
1422
        c = findDesktop(true, currentDesktop());
 
1423
 
 
1424
    if (c != active_client)
 
1425
        setActiveClient(NULL, Allowed);
 
1426
 
 
1427
    if (c)
 
1428
        requestFocus(c);
 
1429
    else if (!desktops.isEmpty())
 
1430
        requestFocus(findDesktop(true, currentDesktop()));
 
1431
    else
 
1432
        focusToNull();
 
1433
 
 
1434
    updateCurrentTopMenu();
 
1435
 
 
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();
 
1444
 
 
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";
 
1449
 
 
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);
 
1453
 
 
1454
    if (compositing())
 
1455
        addRepaintFull();
 
1456
 
 
1457
    emit currentDesktopChanged(old_desktop);
 
1458
    return true;
 
1459
}
 
1460
 
 
1461
/**
 
1462
 * Updates the current activity when it changes
 
1463
 * do *not* call this directly; it does not set the activity.
 
1464
 *
 
1465
 * Shows/Hides windows according to the stacking order
 
1466
 */
 
1467
void Workspace::updateCurrentActivity(const QString &new_activity)
 
1468
{
 
1469
 
 
1470
    //closeActivePopup();
 
1471
    ++block_focus;
 
1472
    // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
 
1473
    StackingUpdatesBlocker blocker(this);
 
1474
 
 
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));
 
1480
 
 
1481
        ObscuringWindows obs_wins;
 
1482
 
 
1483
        QString old_activity = activity_;
 
1484
        activity_ = new_activity;
 
1485
 
 
1486
        for (ClientList::ConstIterator it = stacking_order.constBegin();
 
1487
                it != stacking_order.constEnd();
 
1488
                ++it)
 
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();
 
1493
            }
 
1494
 
 
1495
        // Now propagate the change, after hiding, before showing
 
1496
        //rootInfo->setCurrentDesktop( currentDesktop() );
 
1497
 
 
1498
        /* TODO someday enable dragging windows to other activities
 
1499
        if ( movingClient && !movingClient->isOnDesktop( new_desktop ))
 
1500
            {
 
1501
            int old_desktop = movingClient->desktop();
 
1502
            movingClient->setDesktop( new_desktop );
 
1503
            if ( tilingEnabled() )
 
1504
                {
 
1505
                notifyWindowDesktopChanged( movingClient, old_desktop );
 
1506
                }
 
1507
            }
 
1508
            */
 
1509
 
 
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();
 
1513
 
 
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);
 
1518
    }
 
1519
 
 
1520
    // Restore the focus on this desktop
 
1521
    --block_focus;
 
1522
    Client* c = 0;
 
1523
 
 
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
 
1531
        if (!c) {
 
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);
 
1536
                    break;
 
1537
                }
 
1538
            }
 
1539
        }
 
1540
    }
 
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())
 
1545
        c = active_client;
 
1546
 
 
1547
    if (c == NULL && !desktops.isEmpty())
 
1548
        c = findDesktop(true, currentDesktop());
 
1549
 
 
1550
    if (c != active_client)
 
1551
        setActiveClient(NULL, Allowed);
 
1552
 
 
1553
    if (c)
 
1554
        requestFocus(c);
 
1555
    else if (!desktops.isEmpty())
 
1556
        requestFocus(findDesktop(true, currentDesktop()));
 
1557
    else
 
1558
        focusToNull();
 
1559
 
 
1560
    updateCurrentTopMenu();
 
1561
 
 
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();
 
1570
 
 
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";
 
1575
 
 
1576
    // Not for the very first time, only if something changed and there are more than 1 desktops
 
1577
 
 
1578
    //if ( effects != NULL && old_desktop != 0 && old_desktop != new_desktop )
 
1579
    //    static_cast<EffectsHandlerImpl*>( effects )->desktopChanged( old_desktop );
 
1580
    if (compositing())
 
1581
        addRepaintFull();
 
1582
 
 
1583
}
 
1584
 
 
1585
/**
 
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.
 
1588
 */
 
1589
void Workspace::activityRemoved(const QString &activity)
 
1590
{
 
1591
    allActivities_.removeOne(activity);
 
1592
    foreach (Client * client, stacking_order) {
 
1593
        client->setOnActivity(activity, false);
 
1594
    }
 
1595
    //toss out any session data for it
 
1596
    KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + activity);
 
1597
    cg.deleteGroup();
 
1598
}
 
1599
 
 
1600
void Workspace::activityAdded(const QString &activity)
 
1601
{
 
1602
    allActivities_ << activity;
 
1603
}
 
1604
 
 
1605
/**
 
1606
 * Called only from D-Bus
 
1607
 */
 
1608
void Workspace::nextDesktop()
 
1609
{
 
1610
    int desktop = currentDesktop() + 1;
 
1611
    setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
 
1612
}
 
1613
 
 
1614
/**
 
1615
 * Called only from D-Bus
 
1616
 */
 
1617
void Workspace::previousDesktop()
 
1618
{
 
1619
    int desktop = currentDesktop() - 1;
 
1620
    setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
 
1621
}
 
1622
 
 
1623
/**
 
1624
 * Sets the number of virtual desktops to \a n
 
1625
 */
 
1626
void Workspace::setNumberOfDesktops(int n)
 
1627
{
 
1628
    if (n > KWIN_MAX_NUMBER_DESKTOPS)
 
1629
        n = KWIN_MAX_NUMBER_DESKTOPS;
 
1630
    if (n < 1 || n == numberOfDesktops())
 
1631
        return;
 
1632
    int old_number_of_desktops = numberOfDesktops();
 
1633
    desktopCount_ = n;
 
1634
    updateDesktopLayout(); // Make sure the layout is still valid
 
1635
 
 
1636
    if (currentDesktop() > n)
 
1637
        setCurrentDesktop(n);
 
1638
 
 
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
 
1645
        }
 
1646
    }
 
1647
    rootInfo->setNumberOfDesktops(n);
 
1648
    NETPoint* viewports = new NETPoint[n];
 
1649
    rootInfo->setDesktopViewport(n, *viewports);
 
1650
    delete[] viewports;
 
1651
 
 
1652
    // Make it +1, so that it can be accessed as [1..numberofdesktops]
 
1653
    focus_chain.resize(n + 1);
 
1654
 
 
1655
    workarea.clear();
 
1656
    workarea.resize(n + 1);
 
1657
    restrictedmovearea.clear();
 
1658
    restrictedmovearea.resize(n + 1);
 
1659
    oldrestrictedmovearea.clear();
 
1660
    oldrestrictedmovearea.resize(n + 1);
 
1661
    screenarea.clear();
 
1662
 
 
1663
    updateClientArea(true);
 
1664
 
 
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;
 
1669
 
 
1670
    tilingLayouts.resize(numberOfDesktops() + 1);
 
1671
 
 
1672
    // reset the desktop change osd
 
1673
    desktop_change_osd->numberDesktopsChanged();
 
1674
 
 
1675
    saveDesktopSettings();
 
1676
    emit numberDesktopsChanged(old_number_of_desktops);
 
1677
}
 
1678
 
 
1679
/**
 
1680
 * Sends client \a c to desktop \a desk.
 
1681
 *
 
1682
 * Takes care of transients as well.
 
1683
 */
 
1684
void Workspace::sendClientToDesktop(Client* c, int desk, bool dont_activate)
 
1685
{
 
1686
    if ((desk < 1 && desk != NET::OnAllDesktops) || desk > numberOfDesktops())
 
1687
        return;
 
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
 
1692
        return;
 
1693
    desk = c->desktop(); // Client did range checking
 
1694
 
 
1695
    emit desktopPresenceChanged(c, old_desktop);
 
1696
 
 
1697
    if (c->isOnDesktop(currentDesktop())) {
 
1698
        if (c->wantsTabFocus() && options->focusPolicyIsReasonable() &&
 
1699
                !was_on_desktop && // for stickyness changes
 
1700
                !dont_activate)
 
1701
            requestFocus(c);
 
1702
        else
 
1703
            restackClientUnderActive(c);
 
1704
    } else
 
1705
        raiseClient(c);
 
1706
 
 
1707
    notifyTilingWindowDesktopChanged(c, old_desktop);
 
1708
 
 
1709
    ClientList transients_stacking_order = ensureStackingOrder(c->transients());
 
1710
    for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
 
1711
            it != transients_stacking_order.constEnd();
 
1712
            ++it)
 
1713
        sendClientToDesktop(*it, desk, dont_activate);
 
1714
    updateClientArea();
 
1715
}
 
1716
 
 
1717
/**
 
1718
 * Adds/removes client \a c to/from \a activity.
 
1719
 *
 
1720
 * Takes care of transients as well.
 
1721
 */
 
1722
void Workspace::toggleClientOnActivity(Client* c, const QString &activity, bool dont_activate)
 
1723
{
 
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
 
1731
        return;
 
1732
 
 
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
 
1737
                !dont_activate)
 
1738
            requestFocus(c);
 
1739
        else
 
1740
            restackClientUnderActive(c);
 
1741
    } else
 
1742
        raiseClient(c);
 
1743
 
 
1744
    //notifyWindowDesktopChanged( c, old_desktop );
 
1745
    //FIXME does tiling break?
 
1746
 
 
1747
    ClientList transients_stacking_order = ensureStackingOrder(c->transients());
 
1748
    for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
 
1749
            it != transients_stacking_order.constEnd();
 
1750
            ++it)
 
1751
        toggleClientOnActivity(*it, activity, dont_activate);
 
1752
    updateClientArea();
 
1753
}
 
1754
 
 
1755
int Workspace::numScreens() const
 
1756
{
 
1757
    if (!options->xineramaEnabled)
 
1758
        return 1;
 
1759
    return Kephal::ScreenUtils::numScreens();
 
1760
}
 
1761
 
 
1762
int Workspace::activeScreen() const
 
1763
{
 
1764
    if (!options->xineramaEnabled)
 
1765
        return 0;
 
1766
    if (!options->activeMouseScreen) {
 
1767
        if (activeClient() != NULL && !activeClient()->isOnScreen(active_screen))
 
1768
            return activeClient()->screen();
 
1769
        return active_screen;
 
1770
    }
 
1771
    return Kephal::ScreenUtils::screenId(cursorPos());
 
1772
}
 
1773
 
 
1774
/**
 
1775
 * Check whether a client moved completely out of what's considered the active screen,
 
1776
 * if yes, set a new active screen.
 
1777
 */
 
1778
void Workspace::checkActiveScreen(const Client* c)
 
1779
{
 
1780
    if (!options->xineramaEnabled)
 
1781
        return;
 
1782
    if (!c->isActive())
 
1783
        return;
 
1784
    if (!c->isOnScreen(active_screen))
 
1785
        active_screen = c->screen();
 
1786
}
 
1787
 
 
1788
/**
 
1789
 * Called e.g. when a user clicks on a window, set active screen to be the screen
 
1790
 * where the click occurred
 
1791
 */
 
1792
void Workspace::setActiveScreenMouse(const QPoint& mousepos)
 
1793
{
 
1794
    if (!options->xineramaEnabled)
 
1795
        return;
 
1796
    active_screen = Kephal::ScreenUtils::screenId(mousepos);
 
1797
}
 
1798
 
 
1799
QRect Workspace::screenGeometry(int screen) const
 
1800
{
 
1801
    if (!options->xineramaEnabled)
 
1802
        return Kephal::ScreenUtils::desktopGeometry();
 
1803
    return Kephal::ScreenUtils::screenGeometry(screen);
 
1804
}
 
1805
 
 
1806
int Workspace::screenNumber(const QPoint& pos) const
 
1807
{
 
1808
    if (!options->xineramaEnabled)
 
1809
        return 0;
 
1810
    return Kephal::ScreenUtils::screenId(pos);
 
1811
}
 
1812
 
 
1813
void Workspace::sendClientToScreen(Client* c, int screen)
 
1814
{
 
1815
    if (c->screen() == screen)   // Don't use isOnScreen(), that's true even when only partially
 
1816
        return;
 
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();
 
1826
            ++it)
 
1827
        sendClientToScreen(*it, screen);
 
1828
    if (c->isActive())
 
1829
        active_screen = screen;
 
1830
}
 
1831
 
 
1832
void Workspace::killWindowId(Window window_to_kill)
 
1833
{
 
1834
    if (window_to_kill == None)
 
1835
        return;
 
1836
    Window window = window_to_kill;
 
1837
    Client* client = NULL;
 
1838
    for (;;) {
 
1839
        client = findClient(FrameIdMatchPredicate(window));
 
1840
        if (client != NULL)
 
1841
            break; // Found the client
 
1842
        Window parent, root;
 
1843
        Window* children;
 
1844
        unsigned int children_count;
 
1845
        XQueryTree(display(), window, &root, &parent, &children, &children_count);
 
1846
        if (children != NULL)
 
1847
            XFree(children);
 
1848
        if (window == root)   // We didn't find the client, probably an override-redirect window
 
1849
            break;
 
1850
        window = parent; // Go up
 
1851
    }
 
1852
    if (client != NULL)
 
1853
        client->killWindow();
 
1854
    else
 
1855
        XKillClient(display(), window_to_kill);
 
1856
}
 
1857
 
 
1858
void Workspace::sendPingToWindow(Window window, Time timestamp)
 
1859
{
 
1860
    rootInfo->sendPing(window, timestamp);
 
1861
}
 
1862
 
 
1863
void Workspace::sendTakeActivity(Client* c, Time timestamp, long flags)
 
1864
{
 
1865
    rootInfo->takeActivity(c->window(), timestamp, flags);
 
1866
    pending_take_activity = c;
 
1867
}
 
1868
 
 
1869
/**
 
1870
 * Invokes keyboard mouse emulation
 
1871
 */
 
1872
void Workspace::slotMouseEmulation()
 
1873
{
 
1874
    if (mouse_emulation) {
 
1875
        ungrabXKeyboard();
 
1876
        mouse_emulation = false;
 
1877
        return;
 
1878
    }
 
1879
 
 
1880
    if (grabXKeyboard()) {
 
1881
        mouse_emulation = true;
 
1882
        mouse_emulation_state = 0;
 
1883
        mouse_emulation_window = 0;
 
1884
    }
 
1885
}
 
1886
 
 
1887
/**
 
1888
 * Returns the child window under the mouse and activates the
 
1889
 * respective client if necessary.
 
1890
 *
 
1891
 * Auxiliary function for the mouse emulation system.
 
1892
 */
 
1893
WId Workspace::getMouseEmulationWindow()
 
1894
{
 
1895
    Window root;
 
1896
    Window child = rootWindow();
 
1897
    int root_x, root_y, lx, ly;
 
1898
    uint state;
 
1899
    Window w;
 
1900
    Client * c = 0;
 
1901
    do {
 
1902
        w = child;
 
1903
        if (!c)
 
1904
            c = findClient(FrameIdMatchPredicate(w));
 
1905
        XQueryPointer(display(), w, &root, &child, &root_x, &root_y, &lx, &ly, &state);
 
1906
    } while (child != None && child != w);
 
1907
 
 
1908
    if (c && !c->isActive())
 
1909
        activateClient(c);
 
1910
    return WId(w);
 
1911
}
 
1912
 
 
1913
/**
 
1914
 * Sends a faked mouse event to the specified window. Returns the new button state.
 
1915
 */
 
1916
unsigned int Workspace::sendFakedMouseEvent(const QPoint& pos, WId w, MouseEmulation type,
 
1917
        int button, unsigned int state)
 
1918
{
 
1919
    if (!w)
 
1920
        return state;
 
1921
    QWidget* widget = QWidget::find(w);
 
1922
    if ((!widget ||  qobject_cast<QToolButton*>(widget)) && !findClient(WindowMatchPredicate(w))) {
 
1923
        int x, y;
 
1924
        Window xw;
 
1925
        XTranslateCoordinates(display(), rootWindow(), w, pos.x(), pos.y(), &x, &y, &xw);
 
1926
        if (type == EmuMove) {
 
1927
            // Motion notify events
 
1928
            XEvent e;
 
1929
            e.type = MotionNotify;
 
1930
            e.xmotion.window = w;
 
1931
            e.xmotion.root = rootWindow();
 
1932
            e.xmotion.subwindow = w;
 
1933
            e.xmotion.time = xTime();
 
1934
            e.xmotion.x = x;
 
1935
            e.xmotion.y = y;
 
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);
 
1941
        } else {
 
1942
            XEvent 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();
 
1948
            e.xbutton.x = x;
 
1949
            e.xbutton.y = y;
 
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);
 
1955
 
 
1956
            if (type == EmuPress) {
 
1957
                switch(button) {
 
1958
                case 2:
 
1959
                    state |= Button2Mask;
 
1960
                    break;
 
1961
                case 3:
 
1962
                    state |= Button3Mask;
 
1963
                    break;
 
1964
                default: // 1
 
1965
                    state |= Button1Mask;
 
1966
                    break;
 
1967
                }
 
1968
            } else {
 
1969
                switch(button) {
 
1970
                case 2:
 
1971
                    state &= ~Button2Mask;
 
1972
                    break;
 
1973
                case 3:
 
1974
                    state &= ~Button3Mask;
 
1975
                    break;
 
1976
                default: // 1
 
1977
                    state &= ~Button1Mask;
 
1978
                    break;
 
1979
                }
 
1980
            }
 
1981
        }
 
1982
    }
 
1983
 
 
1984
    return state;
 
1985
}
 
1986
 
 
1987
/**
 
1988
 * Handles keypress event during mouse emulation
 
1989
 */
 
1990
bool Workspace::keyPressMouseEmulation(XKeyEvent& ev)
 
1991
{
 
1992
    int kc = XKeycodeToKeysym(display(), ev.keycode, 0);
 
1993
    int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
 
1994
 
 
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();
 
2000
 
 
2001
    switch(kc) {
 
2002
    case XK_Left:
 
2003
    case XK_KP_Left:
 
2004
        pos.rx() -= delta;
 
2005
        break;
 
2006
    case XK_Right:
 
2007
    case XK_KP_Right:
 
2008
        pos.rx() += delta;
 
2009
        break;
 
2010
    case XK_Up:
 
2011
    case XK_KP_Up:
 
2012
        pos.ry() -= delta;
 
2013
        break;
 
2014
    case XK_Down:
 
2015
    case XK_KP_Down:
 
2016
        pos.ry() += delta;
 
2017
        break;
 
2018
    case XK_Home:
 
2019
    case XK_KP_Home:
 
2020
        pos.rx() -= delta;
 
2021
        pos.ry() -= delta;
 
2022
        break;
 
2023
    case XK_Page_Up:
 
2024
    case XK_KP_Page_Up:
 
2025
        pos.rx() += delta;
 
2026
        pos.ry() -= delta;
 
2027
        break;
 
2028
    case XK_Page_Down:
 
2029
    case XK_KP_Page_Down:
 
2030
        pos.rx() += delta;
 
2031
        pos.ry() += delta;
 
2032
        break;
 
2033
    case XK_End:
 
2034
    case XK_KP_End:
 
2035
        pos.rx() -= delta;
 
2036
        pos.ry() += delta;
 
2037
        break;
 
2038
    case XK_F1:
 
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);
 
2044
        if (!is_shift)
 
2045
            mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
 
2046
                                    EmuRelease, Button1, mouse_emulation_state);
 
2047
        break;
 
2048
    case XK_F2:
 
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);
 
2054
        if (!is_shift)
 
2055
            mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
 
2056
                                    EmuRelease, Button2, mouse_emulation_state);
 
2057
        break;
 
2058
    case XK_F3:
 
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);
 
2064
        if (!is_shift)
 
2065
            mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
 
2066
                                    EmuRelease, Button3, mouse_emulation_state);
 
2067
        break;
 
2068
    case XK_Return:
 
2069
    case XK_space:
 
2070
    case XK_KP_Enter:
 
2071
    case XK_KP_Space: {
 
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);
 
2079
        } else {
 
2080
            // Release all
 
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);
 
2090
        }
 
2091
    }
 
2092
    // Fall through
 
2093
    case XK_Escape:
 
2094
        ungrabXKeyboard();
 
2095
        mouse_emulation = false;
 
2096
        return true;
 
2097
    default:
 
2098
        return false;
 
2099
    }
 
2100
 
 
2101
    QCursor::setPos(pos);
 
2102
    if (mouse_emulation_state)
 
2103
        mouse_emulation_state = sendFakedMouseEvent(pos, mouse_emulation_window,
 
2104
                                EmuMove, 0, mouse_emulation_state);
 
2105
 
 
2106
    return true;
 
2107
}
 
2108
 
 
2109
/**
 
2110
 * Delayed focus functions
 
2111
 */
 
2112
void Workspace::delayFocus()
 
2113
{
 
2114
    requestFocus(delayfocus_client);
 
2115
    cancelDelayFocus();
 
2116
}
 
2117
 
 
2118
void Workspace::requestDelayFocus(Client* c)
 
2119
{
 
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);
 
2126
}
 
2127
 
 
2128
void Workspace::cancelDelayFocus()
 
2129
{
 
2130
    delete delayFocusTimer;
 
2131
    delayFocusTimer = 0;
 
2132
}
 
2133
 
 
2134
//-----------------------------------------------------------------------------
 
2135
// Electric Borders
 
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
//-----------------------------------------------------------------------------
 
2141
 
 
2142
void Workspace::updateElectricBorders()
 
2143
{
 
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();
 
2153
 
 
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;
 
2159
            continue;
 
2160
        }
 
2161
        if (electric_windows[pos] != None)
 
2162
            continue;
 
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 }
 
2176
        };
 
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]);
 
2181
 
 
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);
 
2186
    }
 
2187
}
 
2188
 
 
2189
void Workspace::destroyElectricBorders()
 
2190
{
 
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;
 
2195
    }
 
2196
}
 
2197
 
 
2198
void Workspace::restoreElectricBorderSize(ElectricBorder border)
 
2199
{
 
2200
    if (electric_windows[border] == None)
 
2201
        return;
 
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 }
 
2212
    };
 
2213
    XMoveResizeWindow(display(), electric_windows[border],
 
2214
                      xywh[border][0], xywh[border][1], xywh[border][2], xywh[border][3]);
 
2215
}
 
2216
 
 
2217
void Workspace::reserveElectricBorderActions(bool reserve)
 
2218
{
 
2219
    for (int pos = 0; pos < ELECTRIC_COUNT; ++pos)
 
2220
        if (options->electricBorderAction(static_cast<ElectricBorder>(pos))) {
 
2221
            if (reserve)
 
2222
                reserveElectricBorder(static_cast<ElectricBorder>(pos));
 
2223
            else
 
2224
                unreserveElectricBorder(static_cast<ElectricBorder>(pos));
 
2225
        }
 
2226
}
 
2227
 
 
2228
void Workspace::reserveElectricBorderSwitching(bool reserve)
 
2229
{
 
2230
    for (int pos = 0; pos < ELECTRIC_COUNT; ++pos)
 
2231
        if (reserve)
 
2232
            reserveElectricBorder(static_cast<ElectricBorder>(pos));
 
2233
        else
 
2234
            unreserveElectricBorder(static_cast<ElectricBorder>(pos));
 
2235
}
 
2236
 
 
2237
void Workspace::reserveElectricBorder(ElectricBorder border)
 
2238
{
 
2239
    if (border == ElectricNone)
 
2240
        return;
 
2241
    if (electric_reserved[border]++ == 0)
 
2242
        QTimer::singleShot(0, this, SLOT(updateElectricBorders()));
 
2243
}
 
2244
 
 
2245
void Workspace::unreserveElectricBorder(ElectricBorder border)
 
2246
{
 
2247
    if (border == ElectricNone)
 
2248
        return;
 
2249
    assert(electric_reserved[border] > 0);
 
2250
    if (--electric_reserved[border] == 0)
 
2251
        QTimer::singleShot(0, this, SLOT(updateElectricBorders()));
 
2252
}
 
2253
 
 
2254
void Workspace::checkElectricBorder(const QPoint& pos, Time now)
 
2255
{
 
2256
    if ((pos.x() != electricLeft) &&
 
2257
            (pos.x() != electricRight) &&
 
2258
            (pos.y() != electricTop) &&
 
2259
            (pos.y() != electricBottom))
 
2260
        return;
 
2261
 
 
2262
    bool have_borders = false;
 
2263
    for (int i = 0; i < ELECTRIC_COUNT; ++i)
 
2264
        if (electric_windows[i] != None)
 
2265
            have_borders = true;
 
2266
    if (!have_borders)
 
2267
        return;
 
2268
 
 
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();
 
2274
 
 
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;
 
2292
    else
 
2293
        abort();
 
2294
 
 
2295
    if (electric_windows[border] == None)
 
2296
        return;
 
2297
 
 
2298
    if (pushback_pixels == 0) {
 
2299
        // no pushback so we have to activate at once
 
2300
        electric_time_last = now;
 
2301
    }
 
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;
 
2307
 
 
2308
        if (timestampDiff(electric_time_first, now) > treshold_set) {
 
2309
            electric_current_border = ElectricNone;
 
2310
            electric_time_last_trigger = now;
 
2311
            if (movingClient) {
 
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
 
2316
            } else {
 
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
 
2324
                }
 
2325
                switch(options->electricBorderAction(border)) {
 
2326
                case ElectricActionDashboard: { // Display Plasma dashboard
 
2327
                    QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App");
 
2328
                    plasmaApp.call("toggleDashboard");
 
2329
                }
 
2330
                break;
 
2331
                case ElectricActionShowDesktop: {
 
2332
                    setShowingDesktop(!showingDesktop());
 
2333
                    break;
 
2334
                }
 
2335
                case ElectricActionLockScreen: { // Lock the screen
 
2336
                    QDBusInterface screenSaver("org.kde.screensaver", "/ScreenSaver");
 
2337
                    screenSaver.call("Lock");
 
2338
                }
 
2339
                break;
 
2340
                case ElectricActionPreventScreenLocking: {
 
2341
                    break;
 
2342
                }
 
2343
                case ElectricActionNone: // Either desktop switching or an effect
 
2344
                default: {
 
2345
                    if (effects && static_cast<EffectsHandlerImpl*>(effects)->borderActivated(border))
 
2346
                        {} // Handled by effects
 
2347
                    else {
 
2348
                        electricBorderSwitchDesktop(border, pos);
 
2349
                        return; // Don't reset cursor position
 
2350
                    }
 
2351
                }
 
2352
                }
 
2353
            }
 
2354
        }
 
2355
    } else {
 
2356
        electric_current_border = border;
 
2357
        electric_time_first = now;
 
2358
        electric_time_last = now;
 
2359
        electric_push_point = pos;
 
2360
    }
 
2361
 
 
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,
 
2365
                                        -pushback_pixels,
 
2366
                                        -pushback_pixels,
 
2367
                                        -pushback_pixels,
 
2368
                                        0,
 
2369
                                        pushback_pixels,
 
2370
                                        pushback_pixels,
 
2371
                                        pushback_pixels
 
2372
                                      };
 
2373
    const int ydiff[ELECTRIC_COUNT] = { pushback_pixels,
 
2374
                                        pushback_pixels,
 
2375
                                        0,
 
2376
                                        -pushback_pixels,
 
2377
                                        -pushback_pixels,
 
2378
                                        -pushback_pixels,
 
2379
                                        0,
 
2380
                                        pushback_pixels
 
2381
                                      };
 
2382
    QCursor::setPos(pos.x() + xdiff[border], pos.y() + ydiff[border]);
 
2383
}
 
2384
 
 
2385
void Workspace::electricBorderSwitchDesktop(ElectricBorder border, const QPoint& _pos)
 
2386
{
 
2387
    QPoint pos = _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);
 
2393
    }
 
2394
    if (border == ElectricRight || border == ElectricTopRight || border == ElectricBottomRight) {
 
2395
        desk = desktopToRight(desk, options->rollOverDesktops);
 
2396
        pos.setX(OFFSET);
 
2397
    }
 
2398
    if (border == ElectricTop || border == ElectricTopLeft || border == ElectricTopRight) {
 
2399
        desk = desktopAbove(desk, options->rollOverDesktops);
 
2400
        pos.setY(displayHeight() - 1 - OFFSET);
 
2401
    }
 
2402
    if (border == ElectricBottom || border == ElectricBottomLeft || border == ElectricBottomRight) {
 
2403
        desk = desktopBelow(desk, options->rollOverDesktops);
 
2404
        pos.setY(OFFSET);
 
2405
    }
 
2406
    int desk_before = currentDesktop();
 
2407
    setCurrentDesktop(desk);
 
2408
    if (currentDesktop() != desk_before)
 
2409
        QCursor::setPos(pos);
 
2410
}
 
2411
 
 
2412
/**
 
2413
 * Called when the user entered an electric border with the mouse.
 
2414
 * It may switch to another virtual desktop.
 
2415
 */
 
2416
bool Workspace::electricBorderEvent(XEvent* e)
 
2417
{
 
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);
 
2423
                return true;
 
2424
            }
 
2425
    }
 
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]) {
 
2430
                    updateXTime();
 
2431
                    checkElectricBorder(QPoint(
 
2432
                                            e->xclient.data.l[2] >> 16, e->xclient.data.l[2] & 0xffff), xTime());
 
2433
                    return true;
 
2434
                }
 
2435
        }
 
2436
    }
 
2437
    return false;
 
2438
}
 
2439
 
 
2440
//-----------------------------------------------------------------------------
 
2441
// Top menu
 
2442
 
 
2443
void Workspace::addTopMenu(Client* c)
 
2444
{
 
2445
    assert(c->isTopMenu());
 
2446
    assert(!topmenus.contains(c));
 
2447
    topmenus.append(c);
 
2448
    if (managingTopMenus()) {
 
2449
        int minsize = c->minSize().height();
 
2450
        if (minsize > topMenuHeight()) {
 
2451
            topmenu_height = minsize;
 
2452
            updateTopMenuGeometry();
 
2453
        }
 
2454
        updateTopMenuGeometry(c);
 
2455
        updateCurrentTopMenu();
 
2456
    }
 
2457
 
 
2458
    //kDebug( 1212 ) << "NEW TOPMENU:" << c;
 
2459
}
 
2460
 
 
2461
void Workspace::removeTopMenu(Client* c)
 
2462
{
 
2463
    //if ( c->isTopMenu() )
 
2464
    //    kDebug( 1212 ) << "REMOVE TOPMENU:" << c;
 
2465
 
 
2466
    assert(c->isTopMenu());
 
2467
    assert(topmenus.contains(c));
 
2468
    topmenus.removeAll(c);
 
2469
    updateCurrentTopMenu();
 
2470
    // TODO: Reduce topMenuHeight() if possible?
 
2471
}
 
2472
 
 
2473
void Workspace::lostTopMenuSelection()
 
2474
{
 
2475
    //kDebug( 1212 ) << "lost TopMenu selection";
 
2476
 
 
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)
 
2481
        return;
 
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;
 
2487
    updateClientArea();
 
2488
    for (ClientList::ConstIterator it = topmenus.constBegin();
 
2489
            it != topmenus.constEnd();
 
2490
            ++it)
 
2491
        (*it)->checkWorkspacePosition();
 
2492
}
 
2493
 
 
2494
void Workspace::lostTopMenuOwner()
 
2495
{
 
2496
    if (!options->topMenuEnabled())
 
2497
        return;
 
2498
    //kDebug( 1212 ) << "TopMenu selection lost owner";
 
2499
    if (!topmenu_selection->claim(false)) {
 
2500
        //kDebug( 1212 ) << "Failed to claim TopMenu selection";
 
2501
        return;
 
2502
    }
 
2503
    //kDebug( 1212 ) << "Claimed TopMenu selection";
 
2504
    setupTopMenuHandling();
 
2505
}
 
2506
 
 
2507
void Workspace::setupTopMenuHandling()
 
2508
{
 
2509
    if (managing_topmenus)
 
2510
        return;
 
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);
 
2515
    Window stack[2];
 
2516
    stack[0] = supportWindow->winId();
 
2517
    stack[1] = topmenu_space->winId();
 
2518
    XRestackWindows(display(), stack, 2);
 
2519
    updateTopMenuGeometry();
 
2520
    topmenu_space->show();
 
2521
    updateClientArea();
 
2522
    updateCurrentTopMenu();
 
2523
}
 
2524
 
 
2525
int Workspace::topMenuHeight() const
 
2526
{
 
2527
    if (topmenu_height == 0) {
 
2528
        // Simply create a dummy menubar and use its preferred height as the menu height
 
2529
        KMenuBar tmpmenu;
 
2530
        tmpmenu.addAction("dummy");
 
2531
        topmenu_height = tmpmenu.sizeHint().height();
 
2532
    }
 
2533
    return topmenu_height;
 
2534
}
 
2535
 
 
2536
KDecoration* Workspace::createDecoration(KDecorationBridge* bridge)
 
2537
{
 
2538
    if (!hasDecorationPlugin()) {
 
2539
        return NULL;
 
2540
    }
 
2541
    return mgr->createDecoration(bridge);
 
2542
}
 
2543
 
 
2544
/**
 
2545
 * Returns a list of all colors (KDecorationDefines::ColorType) the current
 
2546
 * decoration supports
 
2547
 */
 
2548
QList<int> Workspace::decorationSupportedColors() const
 
2549
{
 
2550
    QList<int> ret;
 
2551
    if (!hasDecorationPlugin()) {
 
2552
        return ret;
 
2553
    }
 
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))
 
2559
            ret << ab;
 
2560
    return ret;
 
2561
}
 
2562
 
 
2563
QString Workspace::desktopName(int desk) const
 
2564
{
 
2565
    return QString::fromUtf8(rootInfo->desktopName(desk));
 
2566
}
 
2567
 
 
2568
bool Workspace::checkStartupNotification(Window w, KStartupInfoId& id, KStartupInfoData& data)
 
2569
{
 
2570
    return startup->checkStartup(w, id, data) == KStartupInfo::Match;
 
2571
}
 
2572
 
 
2573
/**
 
2574
 * Puts the focus on a dummy window
 
2575
 * Just using XSetInputFocus() with None would block keyboard input
 
2576
 */
 
2577
void Workspace::focusToNull()
 
2578
{
 
2579
    XSetInputFocus(display(), null_focus_window, RevertToPointerRoot, xTime());
 
2580
}
 
2581
 
 
2582
void Workspace::helperDialog(const QString& message, const Client* c)
 
2583
{
 
2584
    QStringList args;
 
2585
    QString type;
 
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.",
 
2596
                 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.",
 
2609
                 shortcut);
 
2610
        type = "altf3warning";
 
2611
    } else
 
2612
        abort();
 
2613
    if (!type.isEmpty()) {
 
2614
        KConfig cfg("kwin_dialogsrc");
 
2615
        KConfigGroup cg(&cfg, "Notification Messages");  // Depends on KMessageBox
 
2616
        if (!cg.readEntry(type, true))
 
2617
            return;
 
2618
        args << "--dontagain" << "kwin_dialogsrc:" + type;
 
2619
    }
 
2620
    if (c != NULL)
 
2621
        args << "--embed" << QString::number(c->window());
 
2622
    KProcess::startDetached("kdialog", args);
 
2623
}
 
2624
 
 
2625
void Workspace::setShowingDesktop(bool showing)
 
2626
{
 
2627
    rootInfo->setShowingDesktop(showing);
 
2628
    showing_desktop = showing;
 
2629
    ++block_showing_desktop;
 
2630
    if (showing_desktop) {
 
2631
        showing_desktop_clients.clear();
 
2632
        ++block_focus;
 
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();
 
2638
                ++it)
 
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();
 
2643
                ++it)
 
2644
            (*it)->minimize();
 
2645
        --block_focus;
 
2646
        if (Client* desk = findDesktop(true, currentDesktop()))
 
2647
            requestFocus(desk);
 
2648
    } else {
 
2649
        for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
 
2650
                it != showing_desktop_clients.constEnd();
 
2651
                ++it)
 
2652
            (*it)->unminimize();
 
2653
        if (showing_desktop_clients.count() > 0)
 
2654
            requestFocus(showing_desktop_clients.first());
 
2655
        showing_desktop_clients.clear();
 
2656
    }
 
2657
    --block_showing_desktop;
 
2658
}
 
2659
 
 
2660
/**
 
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.
 
2670
 */
 
2671
void Workspace::resetShowingDesktop(bool keep_hidden)
 
2672
{
 
2673
    if (block_showing_desktop > 0)
 
2674
        return;
 
2675
    rootInfo->setShowingDesktop(false);
 
2676
    showing_desktop = false;
 
2677
    ++block_showing_desktop;
 
2678
    if (!keep_hidden) {
 
2679
        for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
 
2680
                it != showing_desktop_clients.constEnd();
 
2681
                ++it)
 
2682
            (*it)->unminimize();
 
2683
    }
 
2684
    showing_desktop_clients.clear();
 
2685
    --block_showing_desktop;
 
2686
}
 
2687
 
 
2688
/**
 
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.
 
2696
 */
 
2697
void Workspace::slotDisableGlobalShortcuts()
 
2698
{
 
2699
    if (global_shortcuts_disabled || global_shortcuts_disabled_for_client)
 
2700
        disableGlobalShortcuts(false);
 
2701
    else
 
2702
        disableGlobalShortcuts(true);
 
2703
}
 
2704
 
 
2705
static bool pending_dfc = false;
 
2706
 
 
2707
void Workspace::disableGlobalShortcutsForClient(bool disable)
 
2708
{
 
2709
    if (global_shortcuts_disabled_for_client == disable)
 
2710
        return;
 
2711
    if (!global_shortcuts_disabled) {
 
2712
        if (disable)
 
2713
            pending_dfc = true;
 
2714
        KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable);
 
2715
        // KWin will get the kipc message too
 
2716
    }
 
2717
}
 
2718
 
 
2719
void Workspace::disableGlobalShortcuts(bool disable)
 
2720
{
 
2721
    KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable);
 
2722
    // KWin will get the kipc message too
 
2723
}
 
2724
 
 
2725
void Workspace::slotBlockShortcuts(int data)
 
2726
{
 
2727
    if (pending_dfc && data) {
 
2728
        global_shortcuts_disabled_for_client = true;
 
2729
        pending_dfc = false;
 
2730
    } else {
 
2731
        global_shortcuts_disabled = data;
 
2732
        global_shortcuts_disabled_for_client = false;
 
2733
    }
 
2734
    // Update also Alt+LMB actions etc.
 
2735
    for (ClientList::ConstIterator it = clients.constBegin();
 
2736
            it != clients.constEnd();
 
2737
            ++it)
 
2738
        (*it)->updateMouseGrab();
 
2739
}
 
2740
 
 
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;
 
2747
 
 
2748
QPoint Workspace::cursorPos() const
 
2749
{
 
2750
    if (last_cursor_timestamp == CurrentTime ||
 
2751
            last_cursor_timestamp != QX11Info::appTime()) {
 
2752
        last_cursor_timestamp = QX11Info::appTime();
 
2753
        Window root;
 
2754
        Window child;
 
2755
        int root_x, root_y, win_x, win_y;
 
2756
        uint state;
 
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()));
 
2766
        }
 
2767
        last_cursor_timer->start(0);
 
2768
    }
 
2769
    return last_cursor_pos;
 
2770
}
 
2771
 
 
2772
/**
 
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.
 
2776
 */
 
2777
void Workspace::resetCursorPosTime()
 
2778
{
 
2779
    last_cursor_timestamp = CurrentTime;
 
2780
}
 
2781
 
 
2782
void Workspace::checkCursorPos()
 
2783
{
 
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));
 
2791
    }
 
2792
}
 
2793
 
 
2794
int Workspace::indexOfClientGroup(ClientGroup* group)
 
2795
{
 
2796
    return clientGroups.indexOf(group);
 
2797
}
 
2798
 
 
2799
void Workspace::moveItemToClientGroup(ClientGroup* oldGroup, int oldIndex,
 
2800
                                      ClientGroup* group, int index)
 
2801
{
 
2802
    Client* c = oldGroup->clients().at(oldIndex);
 
2803
    group->add(c, index, true);
 
2804
}
 
2805
 
 
2806
// To accept "mainwindow#1" to "mainwindow#2"
 
2807
static QByteArray truncatedWindowRole(QByteArray a)
 
2808
{
 
2809
    int i = a.indexOf('#');
 
2810
    if (i == -1)
 
2811
        return a;
 
2812
    QByteArray b(a);
 
2813
    b.truncate(i);
 
2814
    return b;
 
2815
}
 
2816
 
 
2817
Client* Workspace::findSimilarClient(Client* c)
 
2818
{
 
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;
 
2822
 
 
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
 
2829
                    found = NULL;
 
2830
                    break; // Continue to the next test
 
2831
                }
 
2832
                found = cl;
 
2833
            }
 
2834
        }
 
2835
        if (found)
 
2836
            return found;
 
2837
    }
 
2838
 
 
2839
    // If this is a transient window don't take a guess
 
2840
    if (c->isTransient())
 
2841
        return NULL;
 
2842
 
 
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
 
2852
                    return NULL;
 
2853
                found = cl;
 
2854
            }
 
2855
        }
 
2856
    }
 
2857
 
 
2858
    return found;
 
2859
}
 
2860
 
 
2861
Outline* Workspace::outline()
 
2862
{
 
2863
    return m_outline;
 
2864
}
 
2865
 
 
2866
} // namespace
 
2867
 
 
2868
#include "workspace.moc"