1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>.
20*********************************************************************/
21// own
22#include "workspace.h"
23// kwin libs
24#include <kdecorationfactory.h>
25#include <kwinglplatform.h>
26// kwin
27#ifdef KWIN_BUILD_ACTIVITIES
28#include "activities.h"
29#endif
30#ifdef KWIN_BUILD_KAPPMENU
31#include "appmenu.h"
32#endif
33#include "atoms.h"
34#include "client.h"
35#include "composite.h"
36#include "cursor.h"
37#include "dbusinterface.h"
38#include "decorations.h"
39#include "deleted.h"
40#include "effects.h"
41#include "focuschain.h"
42#include "group.h"
43#include "killwindow.h"
44#include "netinfo.h"
45#include "outline.h"
46#include "placement.h"
47#include "rules.h"
48#ifdef KWIN_BUILD_SCREENEDGES
49#include "screenedge.h"
50#endif
51#include "screens.h"
52#ifdef KWIN_BUILD_SCRIPTING
53#include "scripting/scripting.h"
54#endif
55#ifdef KWIN_BUILD_TABBOX
56#include "tabbox.h"
57#endif
58#include "unmanaged.h"
59#include "useractions.h"
60#include "virtualdesktops.h"
61#include "xcbutils.h"
62// KDE
63#include <kdeversion.h>
64#include <KDE/KActionCollection>
65#include <KDE/KCmdLineArgs>
66#include <KDE/KConfig>
67#include <KDE/KConfigGroup>
68#include <KDE/KGlobal>
69#include <KDE/KGlobalSettings>
70#include <KDE/KStartupInfo>
71#include <KDE/KWindowInfo>
72#include <KDE/KWindowSystem>
73// Qt
74#include <QtConcurrentRun>
75
76namespace KWin
77{
78
79extern int screen_number;
80extern bool is_multihead;
81
82ColorMapper::ColorMapper(QObject *parent)
83 : QObject(parent)
84 , m_default(defaultScreen()->default_colormap)
85 , m_installed(defaultScreen()->default_colormap)
86{
87}
88
89ColorMapper::~ColorMapper()
90{
91}
92
93void ColorMapper::update()
94{
95 xcb_colormap_t cmap = m_default;
96 if (Client *c = Workspace::self()->activeClient()) {
97 if (c->colormap() != XCB_COLORMAP_NONE) {
98 cmap = c->colormap();
99 }
100 }
101 if (cmap != m_installed) {
102 xcb_install_colormap(connection(), cmap);
103 m_installed = cmap;
104 }
105}
106
107Workspace* Workspace::_self = 0;
108
109Workspace::Workspace(bool restore)
110 : QObject(0)
111 , m_compositor(NULL)
112 // Unsorted
113 , active_popup(NULL)
114 , active_popup_client(NULL)
115 , active_client(0)
116 , last_active_client(0)
117 , most_recently_raised(0)
118 , movingClient(0)
119 , pending_take_activity(NULL)
120 , delayfocus_client(0)
121 , force_restacking(false)
122 , x_stacking_dirty(true)
123 , showing_desktop(false)
124 , block_showing_desktop(0)
125 , was_user_interaction(false)
126 , session_saving(false)
127 , block_focus(0)
128 , m_userActionsMenu(new UserActionsMenu(this))
129 , keys(0)
130 , client_keys(NULL)
131 , disable_shortcuts_keys(NULL)
132 , client_keys_dialog(NULL)
133 , client_keys_client(NULL)
134 , global_shortcuts_disabled_for_client(false)
135 , workspaceInit(true)
136 , startup(0)
137 , set_active_client_recursion(0)
138 , block_stacking_updates(0)
139 , forced_global_mouse_grab(false)
140{
141 // If KWin was already running it saved its configuration after loosing the selection -> Reread
142 QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);
143
144#ifdef KWIN_BUILD_KAPPMENU
145 ApplicationMenu::create(this);
146#endif
147
148 _self = this;
149
150 // first initialize the extensions
151 Extensions::init();
152 Xcb::Extensions::self();
153
154 // start the cursor support
155 Cursor::create(this);
156
157#ifdef KWIN_BUILD_ACTIVITIES
158 Activities *activities = Activities::create(this);
159 connect(activities, SIGNAL(currentChanged(QString)), SLOT(updateCurrentActivity(QString)));
160#endif
161
162 // PluginMgr needs access to the config file, so we need to wait for it for finishing
163 reparseConfigFuture.waitForFinished();
164
165 // get screen support
166 Screens *screens = Screens::create(this);
167 connect(screens, SIGNAL(changed()), SLOT(desktopResized()));
168
169 options->loadConfig();
170 options->loadCompositingConfig(false);
171 DecorationPlugin::create(this);
172 ColorMapper *colormaps = new ColorMapper(this);
173 connect(this, SIGNAL(clientActivated(KWin::Client*)), colormaps, SLOT(update()));
174
175 updateXTime(); // Needed for proper initialization of user_time in Client ctor
176
177 delayFocusTimer = 0;
178
179 if (restore)
180 loadSessionInfo();
181
182 RuleBook::create(this)->load();
183
184 // Call this before XSelectInput() on the root window
185 startup = new KStartupInfo(
186 KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this);
187
188 // Select windowmanager privileges
189 XSelectInput(display(), rootWindow(),
190 KeyPressMask |
191 PropertyChangeMask |
192 ColormapChangeMask |
193 SubstructureRedirectMask |
194 SubstructureNotifyMask |
195 FocusChangeMask | // For NotifyDetailNone
196 ExposureMask
197 );
198
199#ifdef KWIN_BUILD_SCREENEDGES
200 ScreenEdges::create(this);
201#endif
202
203 // VirtualDesktopManager needs to be created prior to init shortcuts
204 // and prior to TabBox, due to TabBox connecting to signals
205 // actual initialization happens in init()
206 VirtualDesktopManager::create(this);
207
208#ifdef KWIN_BUILD_TABBOX
209 // need to create the tabbox before compositing scene is setup
210 TabBox::TabBox::create(this);
211#endif
212
213 m_compositor = Compositor::create(this);
214 connect(this, SIGNAL(currentDesktopChanged(int,KWin::Client*)), m_compositor, SLOT(addRepaintFull()));
215 connect(m_compositor, SIGNAL(compositingToggled(bool)), decorationPlugin(), SLOT(resetCompositing()));
216
217 new DBusInterface(this);
218
219 // Compatibility
220 long data = 1;
221
222 XChangeProperty(
223 display(),
224 rootWindow(),
225 atoms->kwin_running,
226 atoms->kwin_running,
227 32,
228 PropModeAppend,
229 (unsigned char*)(&data),
230 1
231 );
232
233 client_keys = new KActionCollection(this);
234
235 Outline::create(this);
236
237 initShortcuts();
238
239 init();
240}
241
242void Workspace::init()
243{
244 Screens *screens = Screens::self();
245 screens->setConfig(KGlobal::config());
246 screens->reconfigure();
247 connect(options, SIGNAL(configChanged()), screens, SLOT(reconfigure()));
248#ifdef KWIN_BUILD_SCREENEDGES
249 ScreenEdges *screenEdges = ScreenEdges::self();
250 screenEdges->setConfig(KGlobal::config());
251 screenEdges->init();
252 connect(options, SIGNAL(configChanged()), screenEdges, SLOT(reconfigure()));
253 connect(VirtualDesktopManager::self(), SIGNAL(layoutChanged(int,int)), screenEdges, SLOT(updateLayout()));
254 connect(this, SIGNAL(clientActivated(KWin::Client*)), screenEdges, SIGNAL(checkBlocking()));
255#endif
256
257 FocusChain *focusChain = FocusChain::create(this);
258 connect(this, SIGNAL(clientRemoved(KWin::Client*)), focusChain, SLOT(remove(KWin::Client*)));
259 connect(this, SIGNAL(clientActivated(KWin::Client*)), focusChain, SLOT(setActiveClient(KWin::Client*)));
260 connect(VirtualDesktopManager::self(), SIGNAL(countChanged(uint,uint)), focusChain, SLOT(resize(uint,uint)));
261 connect(VirtualDesktopManager::self(), SIGNAL(currentChanged(uint,uint)), focusChain, SLOT(setCurrentDesktop(uint,uint)));
262 connect(options, SIGNAL(separateScreenFocusChanged(bool)), focusChain, SLOT(setSeparateScreenFocus(bool)));
263 focusChain->setSeparateScreenFocus(options->isSeparateScreenFocus());
264
265 const uint32_t nullFocusValues[] = {true};
266 m_nullFocus.reset(new Xcb::Window(QRect(-1, -1, 1, 1), XCB_WINDOW_CLASS_INPUT_ONLY, XCB_CW_OVERRIDE_REDIRECT, nullFocusValues));
267 m_nullFocus->map();
268
269 RootInfo *rootInfo = RootInfo::create();
270
271 // create VirtualDesktopManager and perform dependency injection
272 VirtualDesktopManager *vds = VirtualDesktopManager::self();
273 connect(vds, SIGNAL(desktopsRemoved(uint)), SLOT(moveClientsFromRemovedDesktops()));
274 connect(vds, SIGNAL(countChanged(uint,uint)), SLOT(slotDesktopCountChanged(uint,uint)));
275 connect(vds, SIGNAL(currentChanged(uint,uint)), SLOT(slotCurrentDesktopChanged(uint,uint)));
276 vds->setNavigationWrappingAround(options->isRollOverDesktops());
277 connect(options, SIGNAL(rollOverDesktopsChanged(bool)), vds, SLOT(setNavigationWrappingAround(bool)));
278 vds->setRootInfo(rootInfo);
279 vds->setConfig(KGlobal::config());
280
281 // Now we know how many desktops we'll have, thus we initialize the positioning object
282 Placement::create(this);
283
284 // positioning object needs to be created before the virtual desktops are loaded.
285 vds->load();
286 vds->updateLayout();
287
288 // Extra NETRootInfo instance in Client mode is needed to get the values of the properties
289 NETRootInfo client_info(display(), NET::ActiveWindow | NET::CurrentDesktop);
290 int initial_desktop;
291 if (!kapp->isSessionRestored())
292 initial_desktop = client_info.currentDesktop();
293 else {
294 KConfigGroup group(kapp->sessionConfig(), "Session");
295 initial_desktop = group.readEntry("desktop", 1);
296 }
297 if (!VirtualDesktopManager::self()->setCurrent(initial_desktop))
298 VirtualDesktopManager::self()->setCurrent(1);
299#ifdef KWIN_BUILD_ACTIVITIES
300 Activities::self()->update(false, true);
301#endif
302
303 reconfigureTimer.setSingleShot(true);
304 updateToolWindowsTimer.setSingleShot(true);
305
306 connect(&reconfigureTimer, SIGNAL(timeout()), this, SLOT(slotReconfigure()));
307 connect(&updateToolWindowsTimer, SIGNAL(timeout()), this, SLOT(slotUpdateToolWindows()));
308
309 connect(KGlobalSettings::self(), SIGNAL(appearanceChanged()), this, SLOT(reconfigure()));
310 connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int)));
311 connect(KGlobalSettings::self(), SIGNAL(blockShortcuts(int)), this, SLOT(slotBlockShortcuts(int)));
312
313 active_client = NULL;
314 rootInfo->setActiveWindow(None);
315 focusToNull();
316 if (!kapp->isSessionRestored())
317 ++block_focus; // Because it will be set below
318
319 {
320 // Begin updates blocker block
321 StackingUpdatesBlocker blocker(this);
322
323 bool fixoffset = KCmdLineArgs::parsedArgs()->getOption("crashes").toInt() > 0;
324
325 Xcb::Tree tree(rootWindow());
326 xcb_window_t *wins = xcb_query_tree_children(tree.data());
327
328 QVector<Xcb::WindowAttributes> windowAttributes(tree->children_len);
329 QVector<Xcb::WindowGeometry> windowGeometries(tree->children_len);
330
331 // Request the attributes and geometries of all toplevel windows
332 for (int i = 0; i < tree->children_len; i++) {
333 windowAttributes[i] = Xcb::WindowAttributes(wins[i]);
334 windowGeometries[i] = Xcb::WindowGeometry(wins[i]);
335 }
336
337 // Get the replies
338 for (int i = 0; i < tree->children_len; i++) {
339 Xcb::WindowAttributes attr(windowAttributes.at(i));
340
341 if (attr.isNull()) {
342 continue;
343 }
344
345 if (attr->override_redirect) {
346 if (attr->map_state == XCB_MAP_STATE_VIEWABLE &&
347 attr->_class != XCB_WINDOW_CLASS_INPUT_ONLY)
348 // ### This will request the attributes again
349 createUnmanaged(wins[i]);
350 } else if (attr->map_state != XCB_MAP_STATE_UNMAPPED) {
351 if (fixoffset) {
352 fixPositionAfterCrash(wins[i], windowGeometries[i].data());
353 }
354
355 // ### This will request the attributes again
356 createClient(wins[i], true);
357 }
358 }
359
360 // Propagate clients, will really happen at the end of the updates blocker block
361 updateStackingOrder(true);
362
363 saveOldScreenSizes();
364 updateClientArea();
365
366 // NETWM spec says we have to set it to (0,0) if we don't support it
367 NETPoint* viewports = new NETPoint[VirtualDesktopManager::self()->count()];
368 rootInfo->setDesktopViewport(VirtualDesktopManager::self()->count(), *viewports);
369 delete[] viewports;
370 QRect geom;
371 for (int i = 0; i < screens->count(); i++) {
372 geom |= screens->geometry(i);
373 }
374 NETSize desktop_geometry;
375 desktop_geometry.width = geom.width();
376 desktop_geometry.height = geom.height();
377 rootInfo->setDesktopGeometry(-1, desktop_geometry);
378 setShowingDesktop(false);
379
380 } // End updates blocker block
381
382 Client* new_active_client = NULL;
383 if (!kapp->isSessionRestored()) {
384 --block_focus;
385 new_active_client = findClient(WindowMatchPredicate(client_info.activeWindow()));
386 }
387 if (new_active_client == NULL
388 && activeClient() == NULL && should_get_focus.count() == 0) {
389 // No client activated in manage()
390 if (new_active_client == NULL)
391 new_active_client = topClientOnDesktop(VirtualDesktopManager::self()->current(), -1);
392 if (new_active_client == NULL && !desktops.isEmpty())
393 new_active_client = findDesktop(true, VirtualDesktopManager::self()->current());
394 }
395 if (new_active_client != NULL)
396 activateClient(new_active_client);
397
398
399#ifdef KWIN_BUILD_SCRIPTING
400 Scripting::create(this);
401#endif
402
403 // SELI TODO: This won't work with unreasonable focus policies,
404 // and maybe in rare cases also if the selected client doesn't
405 // want focus
406 workspaceInit = false;
407
408 // broadcast that Workspace is ready, but first process all events.
409 QMetaObject::invokeMethod(this, "workspaceInitialized", Qt::QueuedConnection);
410
411 // TODO: ungrabXServer()
412}
413
414Workspace::~Workspace()
415{
416 delete m_compositor;
417 m_compositor = NULL;
418 blockStackingUpdates(true);
419
420 // TODO: grabXServer();
421
422 // Use stacking_order, so that kwin --replace keeps stacking order
423 const ToplevelList stack = stacking_order;
424 // "mutex" the stackingorder, since anything trying to access it from now on will find
425 // many dangeling pointers and crash
426 stacking_order.clear();
427
428 for (ToplevelList::const_iterator it = stack.constBegin(), end = stack.constEnd(); it != end; ++it) {
429 Client *c = qobject_cast<Client*>(const_cast<Toplevel*>(*it));
430 if (!c) {
431 continue;
432 }
433 // Only release the window
434 c->releaseWindow(true);
435 // No removeClient() is called, it does more than just removing.
436 // However, remove from some lists to e.g. prevent performTransiencyCheck()
437 // from crashing.
438 clients.removeAll(c);
439 desktops.removeAll(c);
440 }
441 for (UnmanagedList::iterator it = unmanaged.begin(), end = unmanaged.end(); it != end; ++it)
442 (*it)->release(true);
443 XDeleteProperty(display(), rootWindow(), atoms->kwin_running);
444
445 delete RuleBook::self();
446 KGlobal::config()->sync();
447
448 RootInfo::destroy();
449 delete startup;
450 delete Placement::self();
451 delete client_keys_dialog;
452 foreach (SessionInfo * s, session)
453 delete s;
454
455 // TODO: ungrabXServer();
456
457 Xcb::Extensions::destroy();
458 _self = 0;
459}
460
461Client* Workspace::createClient(xcb_window_t w, bool is_mapped)
462{
463 StackingUpdatesBlocker blocker(this);
464 Client* c = new Client();
465 connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint()));
466 connect(c, SIGNAL(activeChanged()), m_compositor, SLOT(checkUnredirect()));
467 connect(c, SIGNAL(fullScreenChanged()), m_compositor, SLOT(checkUnredirect()));
468 connect(c, SIGNAL(geometryChanged()), m_compositor, SLOT(checkUnredirect()));
469 connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), m_compositor, SLOT(checkUnredirect()));
470 connect(c, SIGNAL(blockingCompositingChanged(KWin::Client*)), m_compositor, SLOT(updateCompositeBlocking(KWin::Client*)));
471#ifdef KWIN_BUILD_SCREENEDGES
472 connect(c, SIGNAL(clientFullScreenSet(KWin::Client*,bool,bool)), ScreenEdges::self(), SIGNAL(checkBlocking()));
473#endif
474 connect(c, SIGNAL(desktopPresenceChanged(KWin::Client*,int)), SIGNAL(desktopPresenceChanged(KWin::Client*,int)), Qt::QueuedConnection);
475 if (!c->manage(w, is_mapped)) {
476 Client::deleteClient(c);
477 return NULL;
478 }
479 addClient(c);
480 return c;
481}
482
483Unmanaged* Workspace::createUnmanaged(xcb_window_t w)
484{
485 if (m_compositor && m_compositor->checkForOverlayWindow(w))
486 return NULL;
487 Unmanaged* c = new Unmanaged();
488 if (!c->track(w)) {
489 Unmanaged::deleteUnmanaged(c);
490 return NULL;
491 }
492 connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint()));
493 addUnmanaged(c);
494 emit unmanagedAdded(c);
495 return c;
496}
497
498void Workspace::addClient(Client* c)
499{
500 Group* grp = findGroup(c->window());
501
502 KWindowInfo info = KWindowSystem::windowInfo(c->window(), -1U, NET::WM2WindowClass);
503
504 emit clientAdded(c);
505
506 if (grp != NULL)
507 grp->gotLeader(c);
508
509 if (c->isDesktop()) {
510 desktops.append(c);
511 if (active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
512 requestFocus(c); // TODO: Make sure desktop is active after startup if there's no other window active
513 } else {
514 FocusChain::self()->update(c, FocusChain::Update);
515 clients.append(c);
516 }
517 if (!unconstrained_stacking_order.contains(c))
518 unconstrained_stacking_order.append(c); // Raise if it hasn't got any stacking position yet
519 if (!stacking_order.contains(c)) // It'll be updated later, and updateToolWindows() requires
520 stacking_order.append(c); // c to be in stacking_order
521 x_stacking_dirty = true;
522 updateClientArea(); // This cannot be in manage(), because the client got added only now
523 updateClientLayer(c);
524 if (c->isDesktop()) {
525 raiseClient(c);
526 // If there's no active client, make this desktop the active one
527 if (activeClient() == NULL && should_get_focus.count() == 0)
528 activateClient(findDesktop(true, VirtualDesktopManager::self()->current()));
529 }
530 c->checkActiveModal();
531 checkTransients(c->window()); // SELI TODO: Does this really belong here?
532 updateStackingOrder(true); // Propagate new client
533 if (c->isUtility() || c->isMenu() || c->isToolbar())
534 updateToolWindows(true);
535 checkNonExistentClients();
536#ifdef KWIN_BUILD_TABBOX
537 if (TabBox::TabBox::self()->isDisplayed())
538 TabBox::TabBox::self()->reset(true);
539#endif
540#ifdef KWIN_BUILD_KAPPMENU
541 if (ApplicationMenu::self()->hasMenu(c->window()))
542 c->setAppMenuAvailable();
543#endif
544}
545
546void Workspace::addUnmanaged(Unmanaged* c)
547{
548 unmanaged.append(c);
549 x_stacking_dirty = true;
550}
551
552/**
553 * Destroys the client \a c
554 */
555void Workspace::removeClient(Client* c)
556{
557 emit clientRemoved(c);
558
559 if (c == active_popup_client)
560 closeActivePopup();
561 if (m_userActionsMenu->isMenuClient(c)) {
562 m_userActionsMenu->close();
563 }
564
565 c->untab(QRect(), true);
566
567 if (client_keys_client == c)
568 setupWindowShortcutDone(false);
569 if (!c->shortcut().isEmpty()) {
570 c->setShortcut(QString()); // Remove from client_keys
571 clientShortcutUpdated(c); // Needed, since this is otherwise delayed by setShortcut() and wouldn't run
572 }
573
574#ifdef KWIN_BUILD_TABBOX
575 TabBox::TabBox *tabBox = TabBox::TabBox::self();
576 if (tabBox->isDisplayed() && tabBox->currentClient() == c)
577 tabBox->nextPrev(true);
578#endif
579
580 Q_ASSERT(clients.contains(c) || desktops.contains(c));
581 // TODO: if marked client is removed, notify the marked list
582 clients.removeAll(c);
583 desktops.removeAll(c);
584 x_stacking_dirty = true;
585 attention_chain.removeAll(c);
586 showing_desktop_clients.removeAll(c);
587 Group* group = findGroup(c->window());
588 if (group != NULL)
589 group->lostLeader();
590
591 if (c == most_recently_raised)
592 most_recently_raised = 0;
593 should_get_focus.removeAll(c);
594 Q_ASSERT(c != active_client);
595 if (c == last_active_client)
596 last_active_client = 0;
597 if (c == pending_take_activity)
598 pending_take_activity = NULL;
599 if (c == delayfocus_client)
600 cancelDelayFocus();
601
602 updateStackingOrder(true);
603
604#ifdef KWIN_BUILD_TABBOX
605 if (tabBox->isDisplayed())
606 tabBox->reset(true);
607#endif
608
609 updateClientArea();
610}
611
612void Workspace::removeUnmanaged(Unmanaged* c)
613{
614 assert(unmanaged.contains(c));
615 unmanaged.removeAll(c);
616 x_stacking_dirty = true;
617}
618
619void Workspace::addDeleted(Deleted* c, Toplevel *orig)
620{
621 assert(!deleted.contains(c));
622 deleted.append(c);
623 const int unconstraintedIndex = unconstrained_stacking_order.indexOf(orig);
624 if (unconstraintedIndex != -1) {
625 unconstrained_stacking_order.replace(unconstraintedIndex, c);
626 } else {
627 unconstrained_stacking_order.append(c);
628 }
629 const int index = stacking_order.indexOf(orig);
630 if (index != -1) {
631 stacking_order.replace(index, c);
632 } else {
633 stacking_order.append(c);
634 }
635 x_stacking_dirty = true;
636 connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint()));
637}
638
639void Workspace::removeDeleted(Deleted* c)
640{
641 assert(deleted.contains(c));
642 emit deletedRemoved(c);
643 deleted.removeAll(c);
644 unconstrained_stacking_order.removeAll(c);
645 stacking_order.removeAll(c);
646 x_stacking_dirty = true;
647 if (c->wasClient() && m_compositor) {
648 m_compositor->updateCompositeBlocking();
649 }
650}
651
652void Workspace::updateToolWindows(bool also_hide)
653{
654 // TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
655 if (!options->isHideUtilityWindowsForInactive()) {
656 for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
657 if (!(*it)->tabGroup() || (*it)->tabGroup()->current() == *it)
658 (*it)->hideClient(false);
659 return;
660 }
661 const Group* group = NULL;
662 const Client* client = active_client;
663 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
664 // will be shown; if a group transient is group, all tools in the group will be shown
665 while (client != NULL) {
666 if (!client->isTransient())
667 break;
668 if (client->groupTransient()) {
669 group = client->group();
670 break;
671 }
672 client = client->transientFor();
673 }
674 // Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
675 // I.e. if it's not up to date
676
677 // SELI TODO: But maybe it should - what if a new client has been added that's not in stacking order yet?
678 ClientList to_show, to_hide;
679 for (ToplevelList::ConstIterator it = stacking_order.constBegin();
680 it != stacking_order.constEnd();
681 ++it) {
682 Client *c = qobject_cast<Client*>(*it);
683 if (!c) {
684 continue;
685 }
686 if (c->isUtility() || c->isMenu() || c->isToolbar()) {
687 bool show = true;
688 if (!c->isTransient()) {
689 if (c->group()->members().count() == 1) // Has its own group, keep always visible
690 show = true;
691 else if (client != NULL && c->group() == client->group())
692 show = true;
693 else
694 show = false;
695 } else {
696 if (group != NULL && c->group() == group)
697 show = true;
698 else if (client != NULL && client->hasTransient(c, true))
699 show = true;
700 else
701 show = false;
702 }
703 if (!show && also_hide) {
704 const ClientList mainclients = c->mainClients();
705 // Don't hide utility windows which are standalone(?) or
706 // have e.g. kicker as mainwindow
707 if (mainclients.isEmpty())
708 show = true;
709 for (ClientList::ConstIterator it2 = mainclients.constBegin();
710 it2 != mainclients.constEnd();
711 ++it2) {
712 if ((*it2)->isSpecialWindow())
713 show = true;
714 }
715 if (!show)
716 to_hide.append(c);
717 }
718 if (show)
719 to_show.append(c);
720 }
721 } // First show new ones, then hide
722 for (int i = to_show.size() - 1;
723 i >= 0;
724 --i) // From topmost
725 // TODO: Since this is in stacking order, the order of taskbar entries changes :(
726 to_show.at(i)->hideClient(false);
727 if (also_hide) {
728 for (ClientList::ConstIterator it = to_hide.constBegin();
729 it != to_hide.constEnd();
730 ++it) // From bottommost
731 (*it)->hideClient(true);
732 updateToolWindowsTimer.stop();
733 } else // setActiveClient() is after called with NULL client, quickly followed
734 // by setting a new client, which would result in flickering
735 resetUpdateToolWindowsTimer();
736}
737
738
739void Workspace::resetUpdateToolWindowsTimer()
740{
741 updateToolWindowsTimer.start(200);
742}
743
744void Workspace::slotUpdateToolWindows()
745{
746 updateToolWindows(true);
747}
748
749void Workspace::slotReloadConfig()
750{
751 reconfigure();
752}
753
754void Workspace::reconfigure()
755{
756 reconfigureTimer.start(200);
757}
758
759/**
760 * This D-Bus call is used by the compositing kcm. Since the reconfigure()
761 * D-Bus call delays the actual reconfiguring, it is not possible to immediately
762 * call compositingActive(). Therefore the kcm will instead call this to ensure
763 * the reconfiguring has already happened.
764 */
765bool Workspace::waitForCompositingSetup()
766{
767 if (reconfigureTimer.isActive()) {
768 reconfigureTimer.stop();
769 slotReconfigure();
770 }
771 if (m_compositor) {
772 return m_compositor->isActive();
773 }
774 return false;
775}
776
777void Workspace::slotSettingsChanged(int category)
778{
779 kDebug(1212) << "Workspace::slotSettingsChanged()";
780 if (category == KGlobalSettings::SETTINGS_SHORTCUTS)
781 m_userActionsMenu->discard();
782}
783
784/**
785 * Reread settings
786 */
787KWIN_PROCEDURE(CheckBorderSizesProcedure, Client, cl->checkBorderSizes(true));
788
789void Workspace::slotReconfigure()
790{
791 kDebug(1212) << "Workspace::slotReconfigure()";
792 reconfigureTimer.stop();
793
794 bool borderlessMaximizedWindows = options->borderlessMaximizedWindows();
795
796 KGlobal::config()->reparseConfiguration();
797 unsigned long changed = options->updateSettings();
798
799 emit configChanged();
800 m_userActionsMenu->discard();
801 updateToolWindows(true);
802
803 DecorationPlugin *deco = DecorationPlugin::self();
804 if (!deco->isDisabled() && deco->reset(changed)) {
805 // Decorations need to be recreated
806
807 // This actually seems to make things worse now
808 //QWidget curtain;
809 //curtain.setBackgroundMode( NoBackground );
810 //curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
811 //curtain.show();
812
813 for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
814 (*it)->updateDecoration(true, true);
815 // If the new decoration doesn't supports tabs then ungroup clients
816 if (!decorationPlugin()->supportsTabbing()) {
817 foreach (Client * c, clients)
818 c->untab();
819 }
820 deco->destroyPreviousPlugin();
821 } else {
822 forEachClient(CheckBorderSizesProcedure());
823 foreach (Client * c, clients)
824 c->triggerDecorationRepaint();
825 }
826
827 RuleBook::self()->load();
828 for (ClientList::Iterator it = clients.begin();
829 it != clients.end();
830 ++it) {
831 (*it)->setupWindowRules(true);
832 (*it)->applyWindowRules();
833 RuleBook::self()->discardUsed(*it, false);
834 }
835
836 if (borderlessMaximizedWindows != options->borderlessMaximizedWindows() &&
837 !options->borderlessMaximizedWindows()) {
838 // in case borderless maximized windows option changed and new option
839 // is to have borders, we need to unset the borders for all maximized windows
840 for (ClientList::Iterator it = clients.begin();
841 it != clients.end();
842 ++it) {
843 if ((*it)->maximizeMode() == MaximizeFull)
844 (*it)->checkNoBorder();
845 }
846 }
847
848 if (!deco->isDisabled()) {
849 rootInfo()->setSupported(NET::WM2FrameOverlap, deco->factory()->supports(AbilityExtendIntoClientArea));
850 } else {
851 rootInfo()->setSupported(NET::WM2FrameOverlap, false);
852 }
853}
854
855/**
856 * During virt. desktop switching, desktop areas covered by windows that are
857 * going to be hidden are first obscured by new windows with no background
858 * ( i.e. transparent ) placed right below the windows. These invisible windows
859 * are removed after the switch is complete.
860 * Reduces desktop ( wallpaper ) repaints during desktop switching
861 */
862class ObscuringWindows
863{
864public:
865 ~ObscuringWindows();
866 void create(Client* c);
867private:
868 QList<Window> obscuring_windows;
869 static QList<Window>* cached;
870 static unsigned int max_cache_size;
871};
872
873QList<Window>* ObscuringWindows::cached = 0;
874unsigned int ObscuringWindows::max_cache_size = 0;
875
876void ObscuringWindows::create(Client* c)
877{
878 if (cached == 0)
879 cached = new QList<Window>;
880 Window obs_win;
881 XWindowChanges chngs;
882 int mask = CWSibling | CWStackMode;
883 if (cached->count() > 0) {
884 cached->removeAll(obs_win = cached->first());
885 chngs.x = c->x();
886 chngs.y = c->y();
887 chngs.width = c->width();
888 chngs.height = c->height();
889 mask |= CWX | CWY | CWWidth | CWHeight;
890 } else {
891 XSetWindowAttributes a;
892 a.background_pixmap = None;
893 a.override_redirect = True;
894 obs_win = XCreateWindow(display(), rootWindow(), c->x(), c->y(),
895 c->width(), c->height(), 0, CopyFromParent, InputOutput,
896 CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a);
897 }
898 chngs.sibling = c->frameId();
899 chngs.stack_mode = Below;
900 XConfigureWindow(display(), obs_win, mask, &chngs);
901 XMapWindow(display(), obs_win);
902 obscuring_windows.append(obs_win);
903}
904
905ObscuringWindows::~ObscuringWindows()
906{
907 max_cache_size = qMax(int(max_cache_size), obscuring_windows.count() + 4) - 1;
908 for (QList<Window>::ConstIterator it = obscuring_windows.constBegin();
909 it != obscuring_windows.constEnd();
910 ++it) {
911 XUnmapWindow(display(), *it);
912 if (cached->count() < int(max_cache_size))
913 cached->prepend(*it);
914 else
915 XDestroyWindow(display(), *it);
916 }
917}
918
919void Workspace::slotCurrentDesktopChanged(uint oldDesktop, uint newDesktop)
920{
921 closeActivePopup();
922 ++block_focus;
923 StackingUpdatesBlocker blocker(this);
924 updateClientVisibilityOnDesktopChange(oldDesktop, newDesktop);
925 // Restore the focus on this desktop
926 --block_focus;
927
928 activateClientOnNewDesktop(newDesktop);
929 emit currentDesktopChanged(oldDesktop, movingClient);
930}
931
932void Workspace::updateClientVisibilityOnDesktopChange(uint oldDesktop, uint newDesktop)
933{
934 ++block_showing_desktop;
935 ObscuringWindows obs_wins;
936 for (ToplevelList::ConstIterator it = stacking_order.constBegin();
937 it != stacking_order.constEnd();
938 ++it) {
939 Client *c = qobject_cast<Client*>(*it);
940 if (!c) {
941 continue;
942 }
943 if (!c->isOnDesktop(newDesktop) && c != movingClient && c->isOnCurrentActivity()) {
944 if (c->isShown(true) && c->isOnDesktop(oldDesktop) && !compositing())
945 obs_wins.create(c);
946 (c)->updateVisibility();
947 }
948 }
949 // Now propagate the change, after hiding, before showing
950 rootInfo()->setCurrentDesktop(VirtualDesktopManager::self()->current());
951
952 if (movingClient && !movingClient->isOnDesktop(newDesktop)) {
953 movingClient->setDesktop(newDesktop);
954 }
955
956 for (int i = stacking_order.size() - 1; i >= 0 ; --i) {
957 Client *c = qobject_cast<Client*>(stacking_order.at(i));
958 if (!c) {
959 continue;
960 }
961 if (c->isOnDesktop(newDesktop) && c->isOnCurrentActivity())
962 c->updateVisibility();
963 }
964 --block_showing_desktop;
965 if (showingDesktop()) // Do this only after desktop change to avoid flicker
966 resetShowingDesktop(false);
967}
968
969void Workspace::activateClientOnNewDesktop(uint desktop)
970{
971 Client* c = NULL;
972 if (options->focusPolicyIsReasonable()) {
973 c = findClientToActivateOnDesktop(desktop);
974 }
975 // If "unreasonable focus policy" and active_client is on_all_desktops and
976 // under mouse (Hence == old_active_client), conserve focus.
977 // (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
978 else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop())
979 c = active_client;
980
981 if (c == NULL && !desktops.isEmpty())
982 c = findDesktop(true, desktop);
983
984 if (c != active_client)
985 setActiveClient(NULL);
986
987 if (c)
988 requestFocus(c);
989 else if (!desktops.isEmpty())
990 requestFocus(findDesktop(true, desktop));
991 else
992 focusToNull();
993}
994
995Client *Workspace::findClientToActivateOnDesktop(uint desktop)
996{
997 if (movingClient != NULL && active_client == movingClient &&
998 FocusChain::self()->contains(active_client, desktop) &&
999 active_client->isShown(true) && active_client->isOnCurrentDesktop()) {
1000 // A requestFocus call will fail, as the client is already active
1001 return active_client;
1002 }
1003 // from actiavtion.cpp
1004 if (options->isNextFocusPrefersMouse()) {
1005 ToplevelList::const_iterator it = stackingOrder().constEnd();
1006 while (it != stackingOrder().constBegin()) {
1007 Client *client = qobject_cast<Client*>(*(--it));
1008 if (!client) {
1009 continue;
1010 }
1011
1012 if (!(client->isShown(false) && client->isOnDesktop(desktop) &&
1013 client->isOnCurrentActivity() && client->isOnActiveScreen()))
1014 continue;
1015
1016 if (client->geometry().contains(Cursor::pos())) {
1017 if (!client->isDesktop())
1018 return client;
1019 break; // unconditional break - we do not pass the focus to some client below an unusable one
1020 }
1021 }
1022 }
1023 return FocusChain::self()->getForActivation(desktop);
1024}
1025
1026/**
1027 * Updates the current activity when it changes
1028 * do *not* call this directly; it does not set the activity.
1029 *
1030 * Shows/Hides windows according to the stacking order
1031 */
1032
1033void Workspace::updateCurrentActivity(const QString &new_activity)
1034{
1035#ifdef KWIN_BUILD_ACTIVITIES
1036 //closeActivePopup();
1037 ++block_focus;
1038 // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
1039 StackingUpdatesBlocker blocker(this);
1040
1041 ++block_showing_desktop; //FIXME should I be using that?
1042 // Optimized Desktop switching: unmapping done from back to front
1043 // mapping done from front to back => less exposure events
1044 //Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
1045
1046 ObscuringWindows obs_wins;
1047
1048 const QString &old_activity = Activities::self()->previous();
1049
1050 for (ToplevelList::ConstIterator it = stacking_order.constBegin();
1051 it != stacking_order.constEnd();
1052 ++it) {
1053 Client *c = qobject_cast<Client*>(*it);
1054 if (!c) {
1055 continue;
1056 }
1057 if (!c->isOnActivity(new_activity) && c != movingClient && c->isOnCurrentDesktop()) {
1058 if (c->isShown(true) && c->isOnActivity(old_activity) && !compositing())
1059 obs_wins.create(c);
1060 c->updateVisibility();
1061 }
1062 }
1063
1064 // Now propagate the change, after hiding, before showing
1065 //rootInfo->setCurrentDesktop( currentDesktop() );
1066
1067 /* TODO someday enable dragging windows to other activities
1068 if ( movingClient && !movingClient->isOnDesktop( new_desktop ))
1069 {
1070 movingClient->setDesktop( new_desktop );
1071 */
1072
1073 for (int i = stacking_order.size() - 1; i >= 0 ; --i) {
1074 Client *c = qobject_cast<Client*>(stacking_order.at(i));
1075 if (!c) {
1076 continue;
1077 }
1078 if (c->isOnActivity(new_activity))
1079 c->updateVisibility();
1080 }
1081
1082 --block_showing_desktop;
1083 //FIXME not sure if I should do this either
1084 if (showingDesktop()) // Do this only after desktop change to avoid flicker
1085 resetShowingDesktop(false);
1086
1087 // Restore the focus on this desktop
1088 --block_focus;
1089 Client* c = 0;
1090
1091 //FIXME below here is a lot of focuschain stuff, probably all wrong now
1092 if (options->focusPolicyIsReasonable()) {
1093 // Search in focus chain
1094 c = FocusChain::self()->getForActivation(VirtualDesktopManager::self()->current());
1095 }
1096 // If "unreasonable focus policy" and active_client is on_all_desktops and
1097 // under mouse (Hence == old_active_client), conserve focus.
1098 // (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1099 else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop() && active_client->isOnCurrentActivity())
1100 c = active_client;
1101
1102 if (c == NULL && !desktops.isEmpty())
1103 c = findDesktop(true, VirtualDesktopManager::self()->current());
1104
1105 if (c != active_client)
1106 setActiveClient(NULL);
1107
1108 if (c)
1109 requestFocus(c);
1110 else if (!desktops.isEmpty())
1111 requestFocus(findDesktop(true, VirtualDesktopManager::self()->current()));
1112 else
1113 focusToNull();
1114
1115 // Not for the very first time, only if something changed and there are more than 1 desktops
1116
1117 //if ( effects != NULL && old_desktop != 0 && old_desktop != new_desktop )
1118 // static_cast<EffectsHandlerImpl*>( effects )->desktopChanged( old_desktop );
1119 if (compositing() && m_compositor)
1120 m_compositor->addRepaintFull();
1121#else
1122 Q_UNUSED(new_activity)
1123#endif
1124}
1125
1126void Workspace::moveClientsFromRemovedDesktops()
1127{
1128 for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) {
1129 if (!(*it)->isOnAllDesktops() && (*it)->desktop() > static_cast<int>(VirtualDesktopManager::self()->count()))
1130 sendClientToDesktop(*it, VirtualDesktopManager::self()->count(), true);
1131 }
1132}
1133
1134void Workspace::slotDesktopCountChanged(uint previousCount, uint newCount)
1135{
1136 Q_UNUSED(previousCount)
1137 Placement::self()->reinitCascading(0);
1138
1139 resetClientAreas(newCount);
1140}
1141
1142void Workspace::resetClientAreas(uint desktopCount)
1143{
1144 // Make it +1, so that it can be accessed as [1..numberofdesktops]
1145 workarea.clear();
1146 workarea.resize(desktopCount + 1);
1147 restrictedmovearea.clear();
1148 restrictedmovearea.resize(desktopCount + 1);
1149 screenarea.clear();
1150
1151 updateClientArea(true);
1152}
1153
1154/**
1155 * Sends client \a c to desktop \a desk.
1156 *
1157 * Takes care of transients as well.
1158 */
1159void Workspace::sendClientToDesktop(Client* c, int desk, bool dont_activate)
1160{
1161 if ((desk < 1 && desk != NET::OnAllDesktops) || desk > static_cast<int>(VirtualDesktopManager::self()->count()))
1162 return;
1163 int old_desktop = c->desktop();
1164 bool was_on_desktop = c->isOnDesktop(desk) || c->isOnAllDesktops();
1165 c->setDesktop(desk);
1166 if (c->desktop() != desk) // No change or desktop forced
1167 return;
1168 desk = c->desktop(); // Client did range checking
1169
1170 if (c->isOnDesktop(VirtualDesktopManager::self()->current())) {
1171 if (c->wantsTabFocus() && options->focusPolicyIsReasonable() &&
1172 !was_on_desktop && // for stickyness changes
1173 !dont_activate)
1174 requestFocus(c);
1175 else
1176 restackClientUnderActive(c);
1177 } else
1178 raiseClient(c);
1179
1180 c->checkWorkspacePosition( QRect(), old_desktop );
1181
1182 ClientList transients_stacking_order = ensureStackingOrder(c->transients());
1183 for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
1184 it != transients_stacking_order.constEnd();
1185 ++it)
1186 sendClientToDesktop(*it, desk, dont_activate);
1187 updateClientArea();
1188}
1189
1190/**
1191 * checks whether the X Window with the input focus is on our X11 screen
1192 * if the window cannot be determined or inspected, resturn depends on whether there's actually
1193 * more than one screen
1194 *
1195 * this is NOT in any way related to XRandR multiscreen
1196 *
1197 */
1198extern bool is_multihead; // main.cpp
1199bool Workspace::isOnCurrentHead()
1200{
1201 if (!is_multihead) {
1202 return true;
1203 }
1204
1205 Xcb::CurrentInput currentInput;
1206 if (currentInput.window() == XCB_WINDOW_NONE) {
1207 return !is_multihead;
1208 }
1209
1210 Xcb::WindowGeometry geometry(currentInput.window());
1211 if (geometry.isNull()) { // should not happen
1212 return !is_multihead;
1213 }
1214
1215 return rootWindow() == geometry->root;
1216}
1217
1218void Workspace::sendClientToScreen(Client* c, int screen)
1219{
1220 c->sendToScreen(screen);
1221}
1222
1223void Workspace::sendPingToWindow(xcb_window_t window, xcb_timestamp_t timestamp)
1224{
1225 rootInfo()->sendPing(window, timestamp);
1226}
1227
1228void Workspace::sendTakeActivity(KWin::Client *c, xcb_timestamp_t timestamp, long int flags)
1229{
1230 rootInfo()->takeActivity(c->window(), timestamp, flags);
1231 pending_take_activity = c;
1232}
1233
1234/**
1235 * Delayed focus functions
1236 */
1237void Workspace::delayFocus()
1238{
1239 requestFocus(delayfocus_client);
1240 cancelDelayFocus();
1241}
1242
1243void Workspace::requestDelayFocus(Client* c)
1244{
1245 delayfocus_client = c;
1246 delete delayFocusTimer;
1247 delayFocusTimer = new QTimer(this);
1248 connect(delayFocusTimer, SIGNAL(timeout()), this, SLOT(delayFocus()));
1249 delayFocusTimer->setSingleShot(true);
1250 delayFocusTimer->start(options->delayFocusInterval());
1251}
1252
1253void Workspace::cancelDelayFocus()
1254{
1255 delete delayFocusTimer;
1256 delayFocusTimer = 0;
1257}
1258
1259bool Workspace::checkStartupNotification(xcb_window_t w, KStartupInfoId &id, KStartupInfoData &data)
1260{
1261 return startup->checkStartup(w, id, data) == KStartupInfo::Match;
1262}
1263
1264/**
1265 * Puts the focus on a dummy window
1266 * Just using XSetInputFocus() with None would block keyboard input
1267 */
1268void Workspace::focusToNull()
1269{
1270 m_nullFocus->focus();
1271}
1272
1273void Workspace::setShowingDesktop(bool showing)
1274{
1275 rootInfo()->setShowingDesktop(showing);
1276 showing_desktop = showing;
1277 ++block_showing_desktop;
1278 if (showing_desktop) {
1279 showing_desktop_clients.clear();
1280 ++block_focus;
1281 ToplevelList cls = stackingOrder();
1282 // Find them first, then minimize, otherwise transients may get minimized with the window
1283 // they're transient for
1284 for (ToplevelList::ConstIterator it = cls.constBegin();
1285 it != cls.constEnd();
1286 ++it) {
1287 Client *c = qobject_cast<Client*>(*it);
1288 if (!c) {
1289 continue;
1290 }
1291 if (c->isOnCurrentActivity() && c->isOnCurrentDesktop() && c->isShown(true) && !c->isSpecialWindow())
1292 showing_desktop_clients.prepend(c); // Topmost first to reduce flicker
1293 }
1294 for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
1295 it != showing_desktop_clients.constEnd();
1296 ++it)
1297 (*it)->minimize();
1298 --block_focus;
1299 if (Client* desk = findDesktop(true, VirtualDesktopManager::self()->current()))
1300 requestFocus(desk);
1301 } else {
1302 for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
1303 it != showing_desktop_clients.constEnd();
1304 ++it)
1305 (*it)->unminimize();
1306 if (showing_desktop_clients.count() > 0)
1307 requestFocus(showing_desktop_clients.first());
1308 showing_desktop_clients.clear();
1309 }
1310 --block_showing_desktop;
1311}
1312
1313/**
1314 * Following Kicker's behavior:
1315 * Changing a virtual desktop resets the state and shows the windows again.
1316 * Unminimizing a window resets the state but keeps the windows hidden (except
1317 * the one that was unminimized).
1318 * A new window resets the state and shows the windows again, with the new window
1319 * being active. Due to popular demand (#67406) by people who apparently
1320 * don't see a difference between "show desktop" and "minimize all", this is not
1321 * true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
1322 * a new window resets the state but doesn't show windows.
1323 */
1324void Workspace::resetShowingDesktop(bool keep_hidden)
1325{
1326 if (block_showing_desktop > 0)
1327 return;
1328 rootInfo()->setShowingDesktop(false);
1329 showing_desktop = false;
1330 ++block_showing_desktop;
1331 if (!keep_hidden) {
1332 for (ClientList::ConstIterator it = showing_desktop_clients.constBegin();
1333 it != showing_desktop_clients.constEnd();
1334 ++it)
1335 (*it)->unminimize();
1336 }
1337 showing_desktop_clients.clear();
1338 --block_showing_desktop;
1339}
1340
1341static bool pending_dfc = false;
1342
1343void Workspace::disableGlobalShortcutsForClient(bool disable)
1344{
1345 if (global_shortcuts_disabled_for_client == disable)
1346 return;
1347 if (disable)
1348 pending_dfc = true;
1349 KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable);
1350 // KWin will get the kipc message too
1351}
1352
1353void Workspace::slotBlockShortcuts(int data)
1354{
1355 if (pending_dfc && data) {
1356 global_shortcuts_disabled_for_client = true;
1357 pending_dfc = false;
1358 } else {
1359 global_shortcuts_disabled_for_client = false;
1360 }
1361 // Update also Alt+LMB actions etc.
1362 for (ClientList::ConstIterator it = clients.constBegin();
1363 it != clients.constEnd();
1364 ++it)
1365 (*it)->updateMouseGrab();
1366}
1367
1368QString Workspace::supportInformation() const
1369{
1370 QString support;
1371
1372 support.append(ki18nc("Introductory text shown in the support information.",
1373 "KWin Support Information:\n"
1374 "The following information should be used when requesting support on e.g. http://forum.kde.org.\n"
1375 "It provides information about the currently running instance, which options are used,\n"
1376 "what OpenGL driver and which effects are running.\n"
1377 "Please post the information provided underneath this introductory text to a paste bin service\n"
1378 "like http://paste.kde.org instead of pasting into support threads.\n").toString());
1379 support.append("\n==========================\n\n");
1380 // all following strings are intended for support. They need to be pasted to e.g forums.kde.org
1381 // it is expected that the support will happen in English language or that the people providing
1382 // help understand English. Because of that all texts are not translated
1383 support.append("Version\n");
1384 support.append("=======\n");
1385 support.append("KWin version: ");
1386 support.append(KWIN_VERSION_STRING);
1387 support.append('\n');
1388 support.append("KDE SC version (runtime): ");
1389 support.append(KDE::versionString());
1390 support.append('\n');
1391 support.append("KDE SC version (compile): ");
1392 support.append(KDE_VERSION_STRING);
1393 support.append('\n');
1394 support.append("Qt Version: ");
1395 support.append(qVersion());
1396 support.append("\n\n");
1397 support.append("Options\n");
1398 support.append("=======\n");
1399 const QMetaObject *metaOptions = options->metaObject();
1400 for (int i=0; i<metaOptions->propertyCount(); ++i) {
1401 const QMetaProperty property = metaOptions->property(i);
1402 if (QLatin1String(property.name()) == "objectName") {
1403 continue;
1404 }
1405 support.append(QLatin1String(property.name()) % ": " % options->property(property.name()).toString() % '\n');
1406 }
1407#ifdef KWIN_BUILD_SCREENEDGES
1408 support.append("\nScreen Edges\n");
1409 support.append( "============\n");
1410 const QMetaObject *metaScreenEdges = ScreenEdges::self()->metaObject();
1411 for (int i=0; i<metaScreenEdges->propertyCount(); ++i) {
1412 const QMetaProperty property = metaScreenEdges->property(i);
1413 if (QLatin1String(property.name()) == "objectName") {
1414 continue;
1415 }
1416 support.append(QLatin1String(property.name()) % ": " % ScreenEdges::self()->property(property.name()).toString() % '\n');
1417 }
1418#endif
1419 support.append("\nScreens\n");
1420 support.append( "=======\n");
1421 support.append("Multi-Head: ");
1422 if (is_multihead) {
1423 support.append("yes\n");
1424 support.append(QString("Head: %1\n").arg(screen_number));
1425 } else {
1426 support.append("no\n");
1427 }
1428 support.append("Active screen follows mouse: ");
1429 if (screens()->isCurrentFollowsMouse())
1430 support.append(" yes\n");
1431 else
1432 support.append(" no\n");
1433 support.append(QString("Number of Screens: %1\n").arg(screens()->count()));
1434 for (int i=0; i<screens()->count(); ++i) {
1435 const QRect geo = screens()->geometry(i);
1436 support.append(QString("Screen %1 Geometry: %2,%3,%4x%5\n")
1437 .arg(i)
1438 .arg(geo.x())
1439 .arg(geo.y())
1440 .arg(geo.width())
1441 .arg(geo.height()));
1442 }
1443 support.append("\nDecoration\n");
1444 support.append( "==========\n");
1445 support.append(decorationPlugin()->supportInformation());
1446 support.append("\nCompositing\n");
1447 support.append( "===========\n");
1448 support.append("Qt Graphics System: ");
1449 if (Extensions::nonNativePixmaps()) {
1450 support.append("raster\n");
1451 } else {
1452 support.append("native\n");
1453 }
1454 if (effects) {
1455 support.append("Compositing is active\n");
1456 switch (effects->compositingType()) {
1457 case OpenGL1Compositing:
1458 case OpenGL2Compositing:
1459 case OpenGLCompositing: {
1460#ifdef KWIN_HAVE_OPENGLES
1461 support.append("Compositing Type: OpenGL ES 2.0\n");
1462#else
1463 support.append("Compositing Type: OpenGL\n");
1464#endif
1465
1466 GLPlatform *platform = GLPlatform::instance();
1467 support.append("OpenGL vendor string: " % platform->glVendorString() % '\n');
1468 support.append("OpenGL renderer string: " % platform->glRendererString() % '\n');
1469 support.append("OpenGL version string: " % platform->glVersionString() % '\n');
1470
1471 if (platform->supports(LimitedGLSL) || platform->supports(GLSL))
1472 support.append("OpenGL shading language version string: " % platform->glShadingLanguageVersionString() % '\n');
1473
1474 support.append("Driver: " % GLPlatform::driverToString(platform->driver()) % '\n');
1475 if (!platform->isMesaDriver())
1476 support.append("Driver version: " % GLPlatform::versionToString(platform->driverVersion()) % '\n');
1477
1478 support.append("GPU class: " % GLPlatform::chipClassToString(platform->chipClass()) % '\n');
1479
1480 support.append("OpenGL version: " % GLPlatform::versionToString(platform->glVersion()) % '\n');
1481
1482 if (platform->supports(LimitedGLSL) || platform->supports(GLSL))
1483 support.append("GLSL version: " % GLPlatform::versionToString(platform->glslVersion()) % '\n');
1484
1485 if (platform->isMesaDriver())
1486 support.append("Mesa version: " % GLPlatform::versionToString(platform->mesaVersion()) % '\n');
1487 if (platform->serverVersion() > 0)
1488 support.append("X server version: " % GLPlatform::versionToString(platform->serverVersion()) % '\n');
1489 if (platform->kernelVersion() > 0)
1490 support.append("Linux kernel version: " % GLPlatform::versionToString(platform->kernelVersion()) % '\n');
1491
1492 support.append("Direct rendering: ");
1493 if (platform->isDirectRendering()) {
1494 support.append("yes\n");
1495 } else {
1496 support.append("no\n");
1497 }
1498 support.append("Requires strict binding: ");
1499 if (!platform->isLooseBinding()) {
1500 support.append("yes\n");
1501 } else {
1502 support.append("no\n");
1503 }
1504 support.append("GLSL shaders: ");
1505 if (platform->supports(GLSL)) {
1506 if (platform->supports(LimitedGLSL)) {
1507 support.append(" limited\n");
1508 } else {
1509 support.append(" yes\n");
1510 }
1511 } else {
1512 support.append(" no\n");
1513 }
1514 support.append("Texture NPOT support: ");
1515 if (platform->supports(TextureNPOT)) {
1516 if (platform->supports(LimitedNPOT)) {
1517 support.append(" limited\n");
1518 } else {
1519 support.append(" yes\n");
1520 }
1521 } else {
1522 support.append(" no\n");
1523 }
1524 support.append("Virtual Machine: ");
1525 if (platform->isVirtualMachine()) {
1526 support.append(" yes\n");
1527 } else {
1528 support.append(" no\n");
1529 }
1530
1531 if (effects->compositingType() == OpenGL2Compositing) {
1532 support.append("OpenGL 2 Shaders are used\n");
1533 } else {
1534 support.append("OpenGL 2 Shaders are not used. Legacy OpenGL 1.x code path is used.\n");
1535 }
1536 support.append("Painting blocks for vertical retrace: ");
1537 if (m_compositor->scene()->blocksForRetrace())
1538 support.append(" yes\n");
1539 else
1540 support.append(" no\n");
1541 break;
1542 }
1543 case XRenderCompositing:
1544 support.append("Compositing Type: XRender\n");
1545 break;
1546 case NoCompositing:
1547 default:
1548 support.append("Something is really broken, neither OpenGL nor XRender is used");
1549 }
1550 support.append("\nLoaded Effects:\n");
1551 support.append( "---------------\n");
1552 foreach (const QString &effect, static_cast<EffectsHandlerImpl*>(effects)->loadedEffects()) {
1553 support.append(effect % '\n');
1554 }
1555 support.append("\nCurrently Active Effects:\n");
1556 support.append( "-------------------------\n");
1557 foreach (const QString &effect, static_cast<EffectsHandlerImpl*>(effects)->activeEffects()) {
1558 support.append(effect % '\n');
1559 }
1560 support.append("\nEffect Settings:\n");
1561 support.append( "----------------\n");
1562 foreach (const QString &effect, static_cast<EffectsHandlerImpl*>(effects)->loadedEffects()) {
1563 support.append(static_cast<EffectsHandlerImpl*>(effects)->supportInformation(effect));
1564 support.append('\n');
1565 }
1566 } else {
1567 support.append("Compositing is not active\n");
1568 }
1569 return support;
1570}
1571
1572void Workspace::slotToggleCompositing()
1573{
1574 if (m_compositor) {
1575 m_compositor->slotToggleCompositing();
1576 }
1577}
1578
1579} // namespace
1580
1581#include "workspace.moc"
1582