Changeset View
Standalone View
composite.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> | 5 | Copyright © 2006 Lubos Lunak <l.lunak@kde.org> | ||
6 | Copyright © 2019 Roman Gilg <subdiff@gmail.com> | ||||
6 | 7 | | |||
7 | This program is free software; you can redistribute it and/or modify | 8 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | 9 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | 10 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | 11 | (at your option) any later version. | ||
11 | 12 | | |||
12 | This program is distributed in the hope that it will be useful, | 13 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Line(s) | |||||
57 | #include <QTextStream> | 58 | #include <QTextStream> | ||
58 | #include <QTimerEvent> | 59 | #include <QTimerEvent> | ||
59 | 60 | | |||
60 | #include <xcb/composite.h> | 61 | #include <xcb/composite.h> | ||
61 | #include <xcb/damage.h> | 62 | #include <xcb/damage.h> | ||
62 | 63 | | |||
63 | #include <cstdio> | 64 | #include <cstdio> | ||
64 | 65 | | |||
65 | Q_DECLARE_METATYPE(KWin::Compositor::SuspendReason) | 66 | Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason) | ||
66 | 67 | | |||
67 | namespace KWin | 68 | namespace KWin | ||
68 | { | 69 | { | ||
69 | 70 | | |||
70 | extern int currentRefreshRate(); | 71 | static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } | ||
72 | static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } | ||||
73 | | ||||
74 | Compositor *Compositor::s_compositor = nullptr; | ||||
75 | Compositor* Compositor::self() | ||||
76 | { | ||||
77 | return s_compositor; | ||||
78 | } | ||||
79 | | ||||
80 | WaylandCompositor* WaylandCompositor::create(QObject *parent) | ||||
81 | { | ||||
82 | Q_ASSERT(!s_compositor); | ||||
83 | auto *compositor = new WaylandCompositor(parent); | ||||
84 | s_compositor = compositor; | ||||
85 | return compositor; | ||||
86 | } | ||||
87 | X11Compositor* X11Compositor::create(QObject *parent) | ||||
88 | { | ||||
89 | Q_ASSERT(!s_compositor); | ||||
90 | auto *compositor = new X11Compositor(parent); | ||||
91 | s_compositor = compositor; | ||||
92 | return compositor; | ||||
93 | } | ||||
71 | 94 | | |||
72 | class CompositorSelectionOwner : public KSelectionOwner | 95 | class CompositorSelectionOwner : public KSelectionOwner | ||
73 | { | 96 | { | ||
74 | Q_OBJECT | 97 | Q_OBJECT | ||
75 | public: | 98 | public: | ||
76 | CompositorSelectionOwner(const char *selection) | 99 | CompositorSelectionOwner(const char *selection) | ||
77 | : KSelectionOwner(selection, connection(), rootWindow()) | 100 | : KSelectionOwner(selection, connection(), rootWindow()) | ||
78 | , m_owning(false) | 101 | , m_owning(false) | ||
79 | { | 102 | { | ||
80 | connect (this, &CompositorSelectionOwner::lostOwnership, | 103 | connect (this, &CompositorSelectionOwner::lostOwnership, | ||
81 | this, [this]() { m_owning = false; }); | 104 | this, [this]() { m_owning = false; }); | ||
82 | } | 105 | } | ||
zzag: Is there a reason for not initializing s_compositor in constructor of Compositor class? | |||||
Not in particular. Setting the static variable in the static method seems more readable to me. Also since we assert here. And its the same like the kwinglobals factory methods do it. romangg: Not in particular. Setting the static variable in the static method seems more readable to me. | |||||
83 | bool owning() const { | 106 | bool owning() const { | ||
84 | return m_owning; | 107 | return m_owning; | ||
85 | } | 108 | } | ||
86 | void setOwning(bool own) { | 109 | void setOwning(bool own) { | ||
87 | m_owning = own; | 110 | m_owning = own; | ||
88 | } | 111 | } | ||
89 | private: | 112 | private: | ||
90 | bool m_owning; | 113 | bool m_owning; | ||
91 | }; | 114 | }; | ||
92 | 115 | | |||
93 | KWIN_SINGLETON_FACTORY_VARIABLE(Compositor, s_compositor) | 116 | Compositor::Compositor(QObject* parent) | ||
94 | 117 | : QObject(parent) | |||
95 | static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } | 118 | , m_finishing(false) | ||
96 | static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } | 119 | , m_starting(false) | ||
97 | | ||||
98 | Compositor::Compositor(QObject* workspace) | | |||
99 | : QObject(workspace) | | |||
100 | , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) | | |||
101 | , m_selectionOwner(NULL) | 120 | , m_selectionOwner(NULL) | ||
102 | , vBlankInterval(0) | 121 | , vBlankInterval(0) | ||
103 | , fpsInterval(0) | 122 | , fpsInterval(0) | ||
104 | , m_xrrRefreshRate(0) | | |||
105 | , m_finishing(false) | | |||
106 | , m_starting(false) | | |||
107 | , m_timeSinceLastVBlank(0) | 123 | , m_timeSinceLastVBlank(0) | ||
108 | , m_scene(NULL) | 124 | , m_scene(NULL) | ||
109 | , m_bufferSwapPending(false) | 125 | , m_bufferSwapPending(false) | ||
110 | , m_composeAtSwapCompletion(false) | 126 | , m_composeAtSwapCompletion(false) | ||
111 | { | 127 | { | ||
112 | qRegisterMetaType<Compositor::SuspendReason>("Compositor::SuspendReason"); | | |||
113 | connect(&compositeResetTimer, SIGNAL(timeout()), SLOT(restart())); | | |||
114 | connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged); | 128 | connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged); | ||
115 | compositeResetTimer.setSingleShot(true); | | |||
116 | m_monotonicClock.start(); | 129 | m_monotonicClock.start(); | ||
117 | 130 | | |||
118 | // 2 sec which should be enough to restart the compositor | 131 | // 2 sec which should be enough to restart the compositor | ||
119 | static const int compositorLostMessageDelay = 2000; | 132 | static const int compositorLostMessageDelay = 2000; | ||
120 | 133 | | |||
121 | m_releaseSelectionTimer.setSingleShot(true); | 134 | m_releaseSelectionTimer.setSingleShot(true); | ||
122 | m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); | 135 | m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); | ||
123 | connect(&m_releaseSelectionTimer, SIGNAL(timeout()), SLOT(releaseCompositorSelection())); | 136 | connect(&m_releaseSelectionTimer, &QTimer::timeout, this, &Compositor::releaseCompositorSelection); | ||
124 | 137 | | |||
125 | m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); | 138 | m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); | ||
126 | m_unusedSupportPropertyTimer.setSingleShot(true); | 139 | m_unusedSupportPropertyTimer.setSingleShot(true); | ||
127 | connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties())); | 140 | connect(&m_unusedSupportPropertyTimer, &QTimer::timeout, this, &Compositor::deleteUnusedSupportProperties); | ||
128 | 141 | | |||
129 | // delay the call to setup by one event cycle | 142 | // delay the call to setup by one event cycle | ||
130 | // The ctor of this class is invoked from the Workspace ctor, that means before | 143 | // The ctor of this class is invoked from the Workspace ctor, that means before | ||
131 | // Workspace is completely constructed, so calling Workspace::self() would result | 144 | // Workspace is completely constructed, so calling Workspace::self() would result | ||
132 | // in undefined behavior. This is fixed by using a delayed invocation. | 145 | // in undefined behavior. This is fixed by using a delayed invocation. | ||
146 | // | ||||
147 | // TODO: This is a hack anyway but in Wayland case this explanation is also not correct, | ||||
148 | // since the Compositor is started from Application directly. Still it does not | ||||
149 | // work here either. So the TODO is to clean this up in general. | ||||
133 | if (kwinApp()->platform()->isReady()) { | 150 | if (kwinApp()->platform()->isReady()) { | ||
134 | QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); | 151 | QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); | ||
135 | } | 152 | } | ||
153 | | ||||
136 | connect(kwinApp()->platform(), &Platform::readyChanged, this, | 154 | connect(kwinApp()->platform(), &Platform::readyChanged, this, | ||
137 | [this] (bool ready) { | 155 | [this] (bool ready) { | ||
138 | if (ready) { | 156 | if (ready) { | ||
139 | setup(); | 157 | setup(); | ||
140 | } else { | 158 | } else { | ||
141 | finish(); | 159 | finish(); | ||
142 | } | 160 | } | ||
143 | }, Qt::QueuedConnection | 161 | }, Qt::QueuedConnection | ||
Show All 16 Lines | |||||
160 | { | 178 | { | ||
161 | emit aboutToDestroy(); | 179 | emit aboutToDestroy(); | ||
162 | finish(); | 180 | finish(); | ||
163 | deleteUnusedSupportProperties(); | 181 | deleteUnusedSupportProperties(); | ||
164 | delete m_selectionOwner; | 182 | delete m_selectionOwner; | ||
165 | s_compositor = NULL; | 183 | s_compositor = NULL; | ||
166 | } | 184 | } | ||
167 | 185 | | |||
168 | 186 | | |||
169 | void Compositor::setup() | 187 | bool Compositor::setup() | ||
AbstractClient has generic methods that call "platform-specific" methods to do the dirty work, e.g. doResize, doSetKeepAbove, etc. I've been wondering whether we could apply the same pattern here in the Compositor class. zzag: AbstractClient has generic methods that call "platform-specific" methods to do the dirty work… | |||||
Maybe. Is it a blocker or a necessity to this patch here? Otherwise let's tackle one idea after the other, shall we? romangg: Maybe. Is it a blocker or a necessity to this patch here? Otherwise let's tackle one idea after… | |||||
Well, some parts of KWin(as well Qt) follow this pattern, e.g. EffectsHandlerImpl, so it's a good idea to stick with this pattern in Compositor class. Given that the Compositor class still has X11 bits, I guess we can go with this approach for now. zzag: Well, some parts of KWin(as well Qt) follow this pattern, e.g. EffectsHandlerImpl, so it's a… | |||||
170 | { | 188 | { | ||
171 | if (kwinApp()->isTerminating()) { | 189 | if (kwinApp()->isTerminating()) { | ||
172 | // don't setup while KWin is terminating. An event to restart might be lingering in the event queue due to graphics reset | 190 | // don't setup while KWin is terminating. An event to restart might be lingering in the event queue due to graphics reset | ||
173 | return; | 191 | return false; | ||
174 | } | | |||
175 | if (hasScene()) | | |||
176 | return; | | |||
177 | if (m_suspended) { | | |||
178 | QStringList reasons; | | |||
179 | if (m_suspended & UserSuspend) { | | |||
180 | reasons << QStringLiteral("Disabled by User"); | | |||
181 | } | | |||
182 | if (m_suspended & BlockRuleSuspend) { | | |||
183 | reasons << QStringLiteral("Disabled by Window"); | | |||
184 | } | | |||
185 | if (m_suspended & ScriptSuspend) { | | |||
186 | reasons << QStringLiteral("Disabled by Script"); | | |||
187 | } | | |||
188 | qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; | | |||
189 | return; | | |||
190 | } else if (!kwinApp()->platform()->compositingPossible()) { | | |||
191 | qCCritical(KWIN_CORE) << "Compositing is not possible"; | | |||
192 | return; | | |||
193 | } | 192 | } | ||
194 | m_starting = true; | 193 | if (hasScene()) { | ||
195 | 194 | return false; | |||
196 | if (!options->isCompositingInitialized()) { | | |||
197 | options->reloadCompositingSettings(true); | | |||
198 | } | 195 | } | ||
199 | slotCompositingOptionsInitialized(); | 196 | return true; | ||
200 | } | 197 | } | ||
201 | 198 | | |||
202 | extern int screen_number; // main.cpp | 199 | extern int screen_number; // main.cpp | ||
203 | extern bool is_multihead; | 200 | extern bool is_multihead; | ||
204 | 201 | | |||
205 | void Compositor::slotCompositingOptionsInitialized() | 202 | void Compositor::slotCompositingOptionsInitialized() | ||
206 | { | 203 | { | ||
207 | setupX11Support(); | 204 | setupX11Support(); | ||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Line(s) | 310 | if (!c) { | |||
314 | delete m_selectionOwner; | 311 | delete m_selectionOwner; | ||
315 | m_selectionOwner = nullptr; | 312 | m_selectionOwner = nullptr; | ||
316 | return; | 313 | return; | ||
317 | } | 314 | } | ||
318 | claimCompositorSelection(); | 315 | claimCompositorSelection(); | ||
319 | xcb_composite_redirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); | 316 | xcb_composite_redirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); | ||
320 | } | 317 | } | ||
321 | 318 | | |||
322 | void Compositor::startupWithWorkspace() | 319 | bool Compositor::startupWithWorkspace() | ||
323 | { | 320 | { | ||
324 | if (!m_starting) { | 321 | if (!m_starting) { | ||
325 | return; | 322 | return false; | ||
326 | } | 323 | } | ||
327 | connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::setupX11Support, Qt::UniqueConnection); | 324 | connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::setupX11Support, Qt::UniqueConnection); | ||
328 | Workspace::self()->markXStackingOrderAsDirty(); | 325 | Workspace::self()->markXStackingOrderAsDirty(); | ||
329 | Q_ASSERT(m_scene); | 326 | Q_ASSERT(m_scene); | ||
330 | connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); | 327 | connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); | ||
331 | setupX11Support(); | 328 | setupX11Support(); | ||
332 | m_xrrRefreshRate = KWin::currentRefreshRate(); | 329 | | ||
333 | fpsInterval = options->maxFpsInterval(); | 330 | fpsInterval = options->maxFpsInterval(); | ||
334 | if (m_scene->syncsToVBlank()) { // if we do vsync, set the fps to the next multiple of the vblank rate | 331 | if (m_scene->syncsToVBlank()) { // if we do vsync, set the fps to the next multiple of the vblank rate | ||
335 | vBlankInterval = milliToNano(1000) / m_xrrRefreshRate; | 332 | vBlankInterval = milliToNano(1000) / Options::currentRefreshRate(); | ||
336 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | 333 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | ||
337 | } else | 334 | } else | ||
338 | vBlankInterval = milliToNano(1); // no sync - DO NOT set "0", would cause div-by-zero segfaults. | 335 | vBlankInterval = milliToNano(1); // no sync - DO NOT set "0", would cause div-by-zero segfaults. | ||
339 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" - we don't have even a slight idea when the first vsync will occur | 336 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" - we don't have even a slight idea when the first vsync will occur | ||
340 | scheduleRepaint(); | 337 | scheduleRepaint(); | ||
341 | kwinApp()->platform()->createEffectsHandler(this, m_scene); // sets also the 'effects' pointer | 338 | kwinApp()->platform()->createEffectsHandler(this, m_scene); // sets also the 'effects' pointer | ||
342 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | 339 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | ||
343 | connect(effects, SIGNAL(screenGeometryChanged(QSize)), SLOT(addRepaintFull())); | 340 | connect(effects, SIGNAL(screenGeometryChanged(QSize)), SLOT(addRepaintFull())); | ||
Show All 25 Lines | |||||
369 | 366 | | |||
370 | m_starting = false; | 367 | m_starting = false; | ||
371 | if (m_releaseSelectionTimer.isActive()) { | 368 | if (m_releaseSelectionTimer.isActive()) { | ||
372 | m_releaseSelectionTimer.stop(); | 369 | m_releaseSelectionTimer.stop(); | ||
373 | } | 370 | } | ||
374 | 371 | | |||
375 | // render at least once | 372 | // render at least once | ||
376 | performCompositing(); | 373 | performCompositing(); | ||
374 | return true; | ||||
377 | } | 375 | } | ||
378 | 376 | | |||
379 | void Compositor::scheduleRepaint() | 377 | void Compositor::scheduleRepaint() | ||
380 | { | 378 | { | ||
381 | if (!compositeTimer.isActive()) | 379 | if (!compositeTimer.isActive()) | ||
382 | setCompositeTimer(); | 380 | setCompositeTimer(); | ||
383 | } | 381 | } | ||
384 | 382 | | |||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Line(s) | 488 | foreach (const xcb_atom_t &atom, m_unusedSupportProperties) { | |||
491 | // remove property from root window | 489 | // remove property from root window | ||
492 | xcb_delete_property(c, kwinApp()->x11RootWindow(), atom); | 490 | xcb_delete_property(c, kwinApp()->x11RootWindow(), atom); | ||
493 | } | 491 | } | ||
494 | } | 492 | } | ||
495 | } | 493 | } | ||
496 | 494 | | |||
497 | void Compositor::slotConfigChanged() | 495 | void Compositor::slotConfigChanged() | ||
498 | { | 496 | { | ||
499 | if (!m_suspended) { | | |||
500 | setup(); | 497 | setup(); | ||
501 | if (effects) // setupCompositing() may fail | 498 | if (effects) // setupCompositing() may fail | ||
502 | effects->reconfigure(); | 499 | effects->reconfigure(); | ||
503 | addRepaintFull(); | 500 | addRepaintFull(); | ||
504 | } else | | |||
505 | finish(); | | |||
506 | } | 501 | } | ||
507 | 502 | | |||
508 | void Compositor::slotReinitialize() | 503 | void Compositor::slotReinitialize() | ||
509 | { | 504 | { | ||
510 | // Reparse config. Config options will be reloaded by setup() | 505 | // Reparse config. Config options will be reloaded by setup() | ||
511 | kwinApp()->config()->reparseConfiguration(); | 506 | kwinApp()->config()->reparseConfiguration(); | ||
512 | 507 | | |||
513 | // Restart compositing | 508 | // Restart compositing | ||
514 | finish(); | 509 | finish(); | ||
515 | // resume compositing if suspended | 510 | options->setCompositingInitialized(false); // TODO: why? | ||
516 | m_suspended = NoReasonSuspend; | | |||
517 | options->setCompositingInitialized(false); | | |||
518 | setup(); | 511 | setup(); | ||
519 | 512 | | |||
520 | if (effects) { // setup() may fail | 513 | if (effects) { // setup() may fail | ||
521 | effects->reconfigure(); | 514 | effects->reconfigure(); | ||
522 | } | 515 | } | ||
523 | } | 516 | } | ||
524 | 517 | | |||
525 | // for the shortcut | | |||
526 | void Compositor::slotToggleCompositing() | | |||
527 | { | | |||
528 | if (kwinApp()->platform()->requiresCompositing()) { | | |||
529 | // we are not allowed to turn on/off compositing | | |||
530 | return; | | |||
531 | } | | |||
532 | if (m_suspended) { // direct user call; clear all bits | | |||
533 | resume(AllReasonSuspend); | | |||
534 | } else { // but only set the user one (sufficient to suspend) | | |||
535 | suspend(UserSuspend); | | |||
536 | } | | |||
537 | } | | |||
538 | | ||||
539 | void Compositor::updateCompositeBlocking() | | |||
540 | { | | |||
541 | updateCompositeBlocking(NULL); | | |||
542 | } | | |||
543 | | ||||
544 | void Compositor::updateCompositeBlocking(Client *c) | | |||
545 | { | | |||
546 | if (kwinApp()->platform()->requiresCompositing()) { | | |||
547 | return; | | |||
548 | } | | |||
549 | if (c) { // if c == 0 we just check if we can resume | | |||
550 | if (c->isBlockingCompositing()) { | | |||
551 | if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain! | | |||
552 | QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend)); | | |||
553 | } | | |||
554 | } | | |||
555 | else if (m_suspended & BlockRuleSuspend) { // lost a client and we're blocked - can we resume? | | |||
556 | bool resume = true; | | |||
557 | for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { | | |||
558 | if ((*it)->isBlockingCompositing()) { | | |||
559 | resume = false; | | |||
560 | break; | | |||
561 | } | | |||
562 | } | | |||
563 | if (resume) { // do NOT attempt to call suspend(false); from within the eventchain! | | |||
564 | QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend)); | | |||
565 | } | | |||
566 | } | | |||
567 | } | | |||
568 | | ||||
569 | void Compositor::suspend(Compositor::SuspendReason reason) | | |||
570 | { | | |||
571 | if (kwinApp()->platform()->requiresCompositing()) { | | |||
572 | return; | | |||
573 | } | | |||
574 | Q_ASSERT(reason != NoReasonSuspend); | | |||
575 | m_suspended |= reason; | | |||
576 | if (reason & KWin::Compositor::ScriptSuspend) { | | |||
577 | // when disabled show a shortcut how the user can get back compositing | | |||
578 | const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild<QAction*>(QStringLiteral("Suspend Compositing"))); | | |||
579 | if (!shortcuts.isEmpty()) { | | |||
580 | // display notification only if there is the shortcut | | |||
581 | const QString message = i18n("Desktop effects have been suspended by another application.<br/>" | | |||
582 | "You can resume using the '%1' shortcut.", shortcuts.first().toString(QKeySequence::NativeText)); | | |||
583 | KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); | | |||
584 | } | | |||
585 | } | | |||
586 | finish(); | | |||
587 | } | | |||
588 | | ||||
589 | void Compositor::resume(Compositor::SuspendReason reason) | | |||
590 | { | | |||
591 | Q_ASSERT(reason != NoReasonSuspend); | | |||
592 | m_suspended &= ~reason; | | |||
593 | setup(); // signal "toggled" is eventually emitted from within setup | | |||
594 | } | | |||
595 | | ||||
596 | void Compositor::restart() | 518 | void Compositor::restart() | ||
597 | { | 519 | { | ||
598 | if (hasScene()) { | 520 | if (hasScene()) { | ||
599 | finish(); | 521 | finish(); | ||
600 | QTimer::singleShot(0, this, SLOT(setup())); | 522 | QTimer::singleShot(0, this, SLOT(setup())); | ||
601 | } | 523 | } | ||
602 | } | 524 | } | ||
603 | 525 | | |||
▲ Show 20 Lines • Show All 218 Lines • ▼ Show 20 Line(s) | 733 | if (auto w = waylandServer()) { | |||
822 | }; | 744 | }; | ||
823 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | 745 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | ||
824 | return true; | 746 | return true; | ||
825 | } | 747 | } | ||
826 | } | 748 | } | ||
827 | return false; | 749 | return false; | ||
828 | } | 750 | } | ||
829 | 751 | | |||
830 | void Compositor::setCompositeResetTimer(int msecs) | | |||
831 | { | | |||
832 | compositeResetTimer.start(msecs); | | |||
833 | } | | |||
834 | | ||||
835 | void Compositor::setCompositeTimer() | 752 | void Compositor::setCompositeTimer() | ||
836 | { | 753 | { | ||
837 | if (!hasScene()) // should not really happen, but there may be e.g. some damage events still pending | 754 | if (!hasScene()) // should not really happen, but there may be e.g. some damage events still pending | ||
838 | return; | 755 | return; | ||
839 | if (m_starting || !Workspace::self()) { | 756 | if (m_starting || !Workspace::self()) { | ||
840 | return; | 757 | return; | ||
841 | } | 758 | } | ||
842 | 759 | | |||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Line(s) | 841 | if (!hasScene()) { | |||
925 | return false; | 842 | return false; | ||
926 | } | 843 | } | ||
927 | if (!m_scene->overlayWindow()) { | 844 | if (!m_scene->overlayWindow()) { | ||
928 | return false; | 845 | return false; | ||
929 | } | 846 | } | ||
930 | return m_scene->overlayWindow()->isVisible(); | 847 | return m_scene->overlayWindow()->isVisible(); | ||
931 | } | 848 | } | ||
932 | 849 | | |||
933 | } // namespace | 850 | WaylandCompositor::WaylandCompositor(QObject *parent) | ||
851 | : Compositor(parent) | ||||
852 | { | ||||
853 | } | ||||
854 | | ||||
855 | X11Compositor::X11Compositor(QObject *parent) | ||||
856 | : Compositor(parent) | ||||
857 | , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) | ||||
858 | , m_xrrRefreshRate(0) | ||||
859 | { | ||||
860 | qRegisterMetaType<X11Compositor::SuspendReason>("X11Compositor::SuspendReason"); | ||||
861 | connect(&m_compositeResetTimer, &QTimer::timeout, this, &X11Compositor::restart); | ||||
862 | m_compositeResetTimer.setSingleShot(true); | ||||
863 | } | ||||
864 | | ||||
865 | void X11Compositor::suspend(X11Compositor::SuspendReason reason) | ||||
866 | { | ||||
867 | Q_ASSERT(reason != NoReasonSuspend); | ||||
868 | m_suspended |= reason; | ||||
869 | if (reason & ScriptSuspend) { | ||||
870 | // when disabled show a shortcut how the user can get back compositing | ||||
871 | const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild<QAction*>(QStringLiteral("Suspend Compositing"))); | ||||
872 | if (!shortcuts.isEmpty()) { | ||||
873 | // display notification only if there is the shortcut | ||||
874 | const QString message = i18n("Desktop effects have been suspended by another application.<br/>" | ||||
875 | "You can resume using the '%1' shortcut.", shortcuts.first().toString(QKeySequence::NativeText)); | ||||
876 | KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); | ||||
877 | } | ||||
878 | } | ||||
879 | finish(); | ||||
880 | } | ||||
881 | | ||||
882 | void X11Compositor::slotReinitialize() | ||||
883 | { | ||||
884 | // resume compositing if suspended | ||||
885 | m_suspended = NoReasonSuspend; | ||||
886 | X11Compositor::slotReinitialize(); | ||||
887 | } | ||||
888 | | ||||
889 | void X11Compositor::resume(X11Compositor::SuspendReason reason) | ||||
890 | { | ||||
891 | Q_ASSERT(reason != NoReasonSuspend); | ||||
892 | m_suspended &= ~reason; | ||||
893 | setup(); // signal "toggled" is eventually emitted from within setup | ||||
894 | } | ||||
895 | | ||||
anthonyfieroni: It looks incorrect.
```
resume(AllReasonSuspend);
```
? | |||||
True, what I wanted to do there was calling the parent's method Compositor::reinitialize(). Thanks! romangg: True, what I wanted to do there was calling the parent's method `Compositor::reinitialize()`. | |||||
896 | void X11Compositor::slotToggleCompositing() | ||||
897 | { | ||||
898 | if (m_suspended) { // direct user call; clear all bits | ||||
899 | resume(AllReasonSuspend); | ||||
900 | } else { // but only set the user one (sufficient to suspend) | ||||
901 | suspend(UserSuspend); | ||||
902 | } | ||||
903 | } | ||||
904 | | ||||
905 | void X11Compositor::updateCompositeBlocking() | ||||
906 | { | ||||
907 | updateCompositeBlocking(NULL); | ||||
908 | } | ||||
909 | | ||||
910 | void X11Compositor::updateCompositeBlocking(Client *c) | ||||
911 | { | ||||
912 | if (c) { // if c == 0 we just check if we can resume | ||||
913 | if (c->isBlockingCompositing()) { | ||||
914 | if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain! | ||||
915 | QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, BlockRuleSuspend)); | ||||
916 | } | ||||
917 | } | ||||
918 | else if (m_suspended & BlockRuleSuspend) { // lost a client and we're blocked - can we resume? | ||||
919 | bool resume = true; | ||||
920 | for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { | ||||
921 | if ((*it)->isBlockingCompositing()) { | ||||
922 | resume = false; | ||||
923 | break; | ||||
924 | } | ||||
925 | } | ||||
926 | if (resume) { // do NOT attempt to call suspend(false); from within the eventchain! | ||||
927 | QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, BlockRuleSuspend)); | ||||
928 | } | ||||
929 | } | ||||
930 | } | ||||
931 | | ||||
932 | bool X11Compositor::startupWithWorkspace() | ||||
933 | { | ||||
934 | if (!Compositor::startupWithWorkspace()) { | ||||
935 | return false; | ||||
936 | } | ||||
937 | m_xrrRefreshRate = Options::currentRefreshRate(); | ||||
938 | return true; | ||||
939 | } | ||||
940 | | ||||
941 | bool WaylandCompositor::setup() | ||||
942 | { | ||||
943 | if (!Compositor::setup()) { | ||||
944 | return false; | ||||
945 | } | ||||
946 | | ||||
947 | m_starting = true; | ||||
948 | | ||||
949 | // TODO: is this at all relevant for Wayland? | ||||
950 | if (!options->isCompositingInitialized()) { | ||||
951 | options->reloadCompositingSettings(true); | ||||
952 | } | ||||
953 | slotCompositingOptionsInitialized(); | ||||
954 | return true; | ||||
955 | } | ||||
956 | | ||||
957 | bool X11Compositor::setup() | ||||
958 | { | ||||
959 | if (!Compositor::setup()) { | ||||
960 | return false; | ||||
961 | } | ||||
962 | | ||||
963 | if (m_suspended) { | ||||
964 | QStringList reasons; | ||||
965 | if (m_suspended & UserSuspend) { | ||||
966 | reasons << QStringLiteral("Disabled by User"); | ||||
967 | } | ||||
968 | if (m_suspended & BlockRuleSuspend) { | ||||
969 | reasons << QStringLiteral("Disabled by Window"); | ||||
970 | } | ||||
971 | if (m_suspended & ScriptSuspend) { | ||||
972 | reasons << QStringLiteral("Disabled by Script"); | ||||
973 | } | ||||
974 | qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; | ||||
975 | return false; | ||||
976 | | ||||
977 | } else if (!kwinApp()->platform()->compositingPossible()) { | ||||
978 | qCCritical(KWIN_CORE) << "Compositing is not possible"; | ||||
979 | return false; | ||||
980 | } | ||||
981 | m_starting = true; | ||||
982 | | ||||
983 | if (!options->isCompositingInitialized()) { | ||||
984 | options->reloadCompositingSettings(true); | ||||
985 | } | ||||
986 | slotCompositingOptionsInitialized(); | ||||
987 | return true; | ||||
988 | } | ||||
989 | | ||||
990 | void X11Compositor::slotConfigChanged() | ||||
991 | { | ||||
992 | if (m_suspended) { | ||||
993 | finish(); | ||||
994 | } else { | ||||
995 | Compositor::slotConfigChanged(); | ||||
996 | } | ||||
997 | } | ||||
998 | | ||||
999 | void X11Compositor::setCompositeResetTimer(int msecs) | ||||
1000 | { | ||||
1001 | m_compositeResetTimer.start(msecs); | ||||
1002 | } | ||||
1003 | | ||||
1004 | } | ||||
934 | 1005 | | |||
935 | // included for CompositorSelectionOwner | 1006 | // included for CompositorSelectionOwner | ||
936 | #include "composite.moc" | 1007 | #include "composite.moc" |
Is there a reason for not initializing s_compositor in constructor of Compositor class?