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 | extern int screen_number; // main.cpp | ||
72 | extern bool is_multihead; | ||||
73 | | ||||
74 | static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } | ||||
75 | static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } | ||||
76 | | ||||
77 | Compositor *Compositor::s_compositor = nullptr; | ||||
78 | Compositor* Compositor::self() | ||||
79 | { | ||||
80 | return s_compositor; | ||||
81 | } | ||||
82 | | ||||
83 | WaylandCompositor* WaylandCompositor::create(QObject *parent) | ||||
84 | { | ||||
85 | Q_ASSERT(!s_compositor); | ||||
86 | auto *compositor = new WaylandCompositor(parent); | ||||
87 | s_compositor = compositor; | ||||
88 | return compositor; | ||||
89 | } | ||||
90 | X11Compositor* X11Compositor::create(QObject *parent) | ||||
91 | { | ||||
92 | Q_ASSERT(!s_compositor); | ||||
93 | auto *compositor = new X11Compositor(parent); | ||||
94 | s_compositor = compositor; | ||||
95 | return compositor; | ||||
96 | } | ||||
71 | 97 | | |||
72 | class CompositorSelectionOwner : public KSelectionOwner | 98 | class CompositorSelectionOwner : public KSelectionOwner | ||
73 | { | 99 | { | ||
74 | Q_OBJECT | 100 | Q_OBJECT | ||
75 | public: | 101 | public: | ||
76 | CompositorSelectionOwner(const char *selection) | 102 | CompositorSelectionOwner(const char *selection) | ||
77 | : KSelectionOwner(selection, connection(), rootWindow()) | 103 | : KSelectionOwner(selection, connection(), rootWindow()) | ||
78 | , m_owning(false) | 104 | , m_owning(false) | ||
79 | { | 105 | { | ||
80 | connect (this, &CompositorSelectionOwner::lostOwnership, | 106 | connect (this, &CompositorSelectionOwner::lostOwnership, | ||
81 | this, [this]() { m_owning = false; }); | 107 | this, [this]() { m_owning = false; }); | ||
82 | } | 108 | } | ||
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 { | 109 | bool owning() const { | ||
84 | return m_owning; | 110 | return m_owning; | ||
85 | } | 111 | } | ||
86 | void setOwning(bool own) { | 112 | void setOwning(bool own) { | ||
87 | m_owning = own; | 113 | m_owning = own; | ||
88 | } | 114 | } | ||
89 | private: | 115 | private: | ||
90 | bool m_owning; | 116 | bool m_owning; | ||
91 | }; | 117 | }; | ||
92 | 118 | | |||
93 | KWIN_SINGLETON_FACTORY_VARIABLE(Compositor, s_compositor) | 119 | Compositor::Compositor(QObject* parent) | ||
94 | 120 | : QObject(parent) | |||
95 | static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } | 121 | , m_finishing(false) | ||
96 | static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } | 122 | , m_starting(false) | ||
97 | 123 | , m_scene(NULL) | |||
98 | Compositor::Compositor(QObject* workspace) | | |||
99 | : QObject(workspace) | | |||
100 | , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) | | |||
101 | , m_selectionOwner(NULL) | 124 | , m_selectionOwner(NULL) | ||
102 | , vBlankInterval(0) | 125 | , vBlankInterval(0) | ||
103 | , fpsInterval(0) | 126 | , fpsInterval(0) | ||
104 | , m_xrrRefreshRate(0) | | |||
105 | , m_finishing(false) | | |||
106 | , m_starting(false) | | |||
107 | , m_timeSinceLastVBlank(0) | 127 | , m_timeSinceLastVBlank(0) | ||
108 | , m_scene(NULL) | | |||
109 | , m_bufferSwapPending(false) | 128 | , m_bufferSwapPending(false) | ||
110 | , m_composeAtSwapCompletion(false) | 129 | , m_composeAtSwapCompletion(false) | ||
111 | { | 130 | { | ||
112 | qRegisterMetaType<Compositor::SuspendReason>("Compositor::SuspendReason"); | | |||
113 | connect(&compositeResetTimer, SIGNAL(timeout()), SLOT(restart())); | | |||
114 | connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged); | 131 | connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged); | ||
115 | compositeResetTimer.setSingleShot(true); | | |||
116 | m_monotonicClock.start(); | 132 | m_monotonicClock.start(); | ||
117 | 133 | | |||
118 | // 2 sec which should be enough to restart the compositor | 134 | // 2 sec which should be enough to restart the compositor | ||
119 | static const int compositorLostMessageDelay = 2000; | 135 | static const int compositorLostMessageDelay = 2000; | ||
120 | 136 | | |||
121 | m_releaseSelectionTimer.setSingleShot(true); | | |||
122 | m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); | | |||
123 | connect(&m_releaseSelectionTimer, SIGNAL(timeout()), SLOT(releaseCompositorSelection())); | | |||
124 | | ||||
125 | m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); | 137 | m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); | ||
126 | m_unusedSupportPropertyTimer.setSingleShot(true); | 138 | m_unusedSupportPropertyTimer.setSingleShot(true); | ||
127 | connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties())); | 139 | connect(&m_unusedSupportPropertyTimer, &QTimer::timeout, this, &Compositor::deleteUnusedSupportProperties); | ||
128 | 140 | | |||
129 | // delay the call to setup by one event cycle | 141 | // delay the call to setup by one event cycle | ||
130 | // The ctor of this class is invoked from the Workspace ctor, that means before | 142 | // 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 | 143 | // Workspace is completely constructed, so calling Workspace::self() would result | ||
132 | // in undefined behavior. This is fixed by using a delayed invocation. | 144 | // in undefined behavior. This is fixed by using a delayed invocation. | ||
145 | // | ||||
146 | // TODO: This is a hack anyway but in Wayland case this explanation is also not correct, | ||||
147 | // since the Compositor is started from Application directly. Still it does not | ||||
148 | // work here either. So the TODO is to clean this up in general. | ||||
133 | if (kwinApp()->platform()->isReady()) { | 149 | if (kwinApp()->platform()->isReady()) { | ||
134 | QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); | 150 | QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); | ||
135 | } | 151 | } | ||
152 | | ||||
136 | connect(kwinApp()->platform(), &Platform::readyChanged, this, | 153 | connect(kwinApp()->platform(), &Platform::readyChanged, this, | ||
137 | [this] (bool ready) { | 154 | [this] (bool ready) { | ||
138 | if (ready) { | 155 | if (ready) { | ||
139 | setup(); | 156 | setup(); | ||
140 | } else { | 157 | } else { | ||
141 | finish(); | 158 | finish(); | ||
142 | } | 159 | } | ||
143 | }, Qt::QueuedConnection | 160 | }, Qt::QueuedConnection | ||
Show All 15 Lines | |||||
159 | Compositor::~Compositor() | 176 | Compositor::~Compositor() | ||
160 | { | 177 | { | ||
161 | emit aboutToDestroy(); | 178 | emit aboutToDestroy(); | ||
162 | finish(); | 179 | finish(); | ||
163 | deleteUnusedSupportProperties(); | 180 | deleteUnusedSupportProperties(); | ||
164 | delete m_selectionOwner; | 181 | delete m_selectionOwner; | ||
165 | s_compositor = NULL; | 182 | s_compositor = NULL; | ||
166 | } | 183 | } | ||
167 | 184 | | |||
168 | | ||||
169 | void Compositor::setup() | 185 | void 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 | { | 186 | { | ||
171 | if (kwinApp()->isTerminating()) { | 187 | 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 | 188 | // don't setup while KWin is terminating. An event to restart might be lingering in the event queue due to graphics reset | ||
173 | return; | 189 | return; | ||
174 | } | 190 | } | ||
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 | } | | |||
194 | m_starting = true; | | |||
195 | 191 | | |||
196 | if (!options->isCompositingInitialized()) { | 192 | m_starting = true; | ||
197 | options->reloadCompositingSettings(true); | 193 | options->reloadCompositingSettings(true); | ||
198 | } | | |||
199 | slotCompositingOptionsInitialized(); | | |||
200 | } | | |||
201 | | ||||
202 | extern int screen_number; // main.cpp | | |||
203 | extern bool is_multihead; | | |||
204 | | ||||
205 | void Compositor::slotCompositingOptionsInitialized() | | |||
206 | { | | |||
207 | setupX11Support(); | 194 | setupX11Support(); | ||
208 | 195 | | |||
209 | // There might still be a deleted around, needs to be cleared before creating the scene (BUG 333275) | 196 | // There might still be a deleted around, needs to be cleared before creating the scene (BUG 333275) | ||
210 | if (Workspace::self()) { | 197 | if (Workspace::self()) { | ||
211 | while (!Workspace::self()->deletedList().isEmpty()) { | 198 | while (!Workspace::self()->deletedList().isEmpty()) { | ||
212 | Workspace::self()->deletedList().first()->discard(); | 199 | Workspace::self()->deletedList().first()->discard(); | ||
213 | } | 200 | } | ||
214 | } | 201 | } | ||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Line(s) | |||||
274 | 261 | | |||
275 | kwinApp()->platform()->setSelectedCompositor(m_scene->compositingType() & OpenGLCompositing ? OpenGLCompositing : m_scene->compositingType()); | 262 | kwinApp()->platform()->setSelectedCompositor(m_scene->compositingType() & OpenGLCompositing ? OpenGLCompositing : m_scene->compositingType()); | ||
276 | 263 | | |||
277 | if (!Workspace::self() && m_scene && m_scene->compositingType() == QPainterCompositing) { | 264 | if (!Workspace::self() && m_scene && m_scene->compositingType() == QPainterCompositing) { | ||
278 | // Force Software QtQuick on first startup with QPainter | 265 | // Force Software QtQuick on first startup with QPainter | ||
279 | QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); | 266 | QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); | ||
280 | } | 267 | } | ||
281 | 268 | | |||
282 | connect(m_scene, &Scene::resetCompositing, this, &Compositor::restart); | 269 | connect(m_scene, &Scene::resetCompositing, this, &Compositor::reinitialize); | ||
283 | emit sceneCreated(); | 270 | emit sceneCreated(); | ||
284 | 271 | | |||
285 | if (Workspace::self()) { | 272 | if (Workspace::self()) { | ||
286 | startupWithWorkspace(); | 273 | startupWithWorkspace(); | ||
287 | } else { | 274 | } else { | ||
288 | connect(kwinApp(), &Application::workspaceCreated, this, &Compositor::startupWithWorkspace); | 275 | connect(kwinApp(), &Application::workspaceCreated, this, &Compositor::startupWithWorkspace); | ||
289 | } | 276 | } | ||
290 | } | 277 | } | ||
Show All 23 Lines | 300 | if (!c) { | |||
314 | delete m_selectionOwner; | 301 | delete m_selectionOwner; | ||
315 | m_selectionOwner = nullptr; | 302 | m_selectionOwner = nullptr; | ||
316 | return; | 303 | return; | ||
317 | } | 304 | } | ||
318 | claimCompositorSelection(); | 305 | claimCompositorSelection(); | ||
319 | xcb_composite_redirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); | 306 | xcb_composite_redirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); | ||
320 | } | 307 | } | ||
321 | 308 | | |||
322 | void Compositor::startupWithWorkspace() | 309 | bool Compositor::startupWithWorkspace() | ||
323 | { | 310 | { | ||
324 | if (!m_starting) { | 311 | if (!m_starting) { | ||
325 | return; | 312 | return false; | ||
326 | } | 313 | } | ||
327 | connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::setupX11Support, Qt::UniqueConnection); | 314 | connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::setupX11Support, Qt::UniqueConnection); | ||
328 | Workspace::self()->markXStackingOrderAsDirty(); | 315 | Workspace::self()->markXStackingOrderAsDirty(); | ||
329 | Q_ASSERT(m_scene); | 316 | Q_ASSERT(m_scene); | ||
330 | connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); | 317 | connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); | ||
331 | setupX11Support(); | 318 | setupX11Support(); | ||
332 | m_xrrRefreshRate = KWin::currentRefreshRate(); | 319 | | ||
333 | fpsInterval = options->maxFpsInterval(); | 320 | fpsInterval = options->maxFpsInterval(); | ||
334 | if (m_scene->syncsToVBlank()) { // if we do vsync, set the fps to the next multiple of the vblank rate | 321 | 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; | 322 | vBlankInterval = milliToNano(1000) / Options::currentRefreshRate(); | ||
336 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | 323 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | ||
337 | } else | 324 | } else | ||
338 | vBlankInterval = milliToNano(1); // no sync - DO NOT set "0", would cause div-by-zero segfaults. | 325 | 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 | 326 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" - we don't have even a slight idea when the first vsync will occur | ||
327 | | ||||
340 | scheduleRepaint(); | 328 | scheduleRepaint(); | ||
341 | kwinApp()->platform()->createEffectsHandler(this, m_scene); // sets also the 'effects' pointer | 329 | kwinApp()->platform()->createEffectsHandler(this, m_scene); // sets also the 'effects' pointer | ||
330 | | ||||
342 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | 331 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | ||
343 | connect(effects, SIGNAL(screenGeometryChanged(QSize)), SLOT(addRepaintFull())); | 332 | connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull); | ||
333 | | ||||
344 | addRepaintFull(); | 334 | addRepaintFull(); | ||
335 | | ||||
345 | foreach (Client * c, Workspace::self()->clientList()) { | 336 | foreach (Client * c, Workspace::self()->clientList()) { | ||
346 | c->setupCompositing(); | 337 | c->setupCompositing(); | ||
347 | c->getShadow(); | 338 | c->getShadow(); | ||
348 | } | 339 | } | ||
349 | foreach (Client * c, Workspace::self()->desktopList()) | 340 | foreach (Client * c, Workspace::self()->desktopList()) | ||
350 | c->setupCompositing(); | 341 | c->setupCompositing(); | ||
351 | foreach (Unmanaged * c, Workspace::self()->unmanagedList()) { | 342 | foreach (Unmanaged * c, Workspace::self()->unmanagedList()) { | ||
352 | c->setupCompositing(); | 343 | c->setupCompositing(); | ||
Show All 10 Lines | 353 | for (auto c : internalClients) { | |||
363 | c->setupCompositing(); | 354 | c->setupCompositing(); | ||
364 | c->getShadow(); | 355 | c->getShadow(); | ||
365 | } | 356 | } | ||
366 | } | 357 | } | ||
367 | 358 | | |||
368 | emit compositingToggled(true); | 359 | emit compositingToggled(true); | ||
369 | 360 | | |||
370 | m_starting = false; | 361 | m_starting = false; | ||
371 | if (m_releaseSelectionTimer.isActive()) { | | |||
372 | m_releaseSelectionTimer.stop(); | | |||
373 | } | | |||
374 | 362 | | |||
375 | // render at least once | 363 | // render at least once | ||
376 | performCompositing(); | 364 | performCompositing(); | ||
365 | return true; | ||||
377 | } | 366 | } | ||
378 | 367 | | |||
379 | void Compositor::scheduleRepaint() | 368 | void Compositor::scheduleRepaint() | ||
380 | { | 369 | { | ||
381 | if (!compositeTimer.isActive()) | 370 | if (!compositeTimer.isActive()) | ||
382 | setCompositeTimer(); | 371 | setCompositeTimer(); | ||
383 | } | 372 | } | ||
384 | 373 | | |||
385 | void Compositor::finish() | 374 | void Compositor::finish() | ||
386 | { | 375 | { | ||
387 | if (!hasScene()) | 376 | if (!hasScene()) | ||
388 | return; | 377 | return; | ||
389 | m_finishing = true; | 378 | m_finishing = true; | ||
390 | m_releaseSelectionTimer.start(); | | |||
391 | 379 | | |||
392 | emit aboutToToggleCompositing(); | 380 | emit aboutToToggleCompositing(); | ||
393 | 381 | | |||
394 | // Some effects might need access to effect windows when they are about to | 382 | // Some effects might need access to effect windows when they are about to | ||
395 | // be destroyed, for example to unreference deleted windows, so we have to | 383 | // be destroyed, for example to unreference deleted windows, so we have to | ||
396 | // make sure that effect windows outlive effects. | 384 | // make sure that effect windows outlive effects. | ||
397 | delete effects; | 385 | delete effects; | ||
398 | effects = nullptr; | 386 | effects = nullptr; | ||
Show All 35 Lines | |||||
434 | delete m_scene; | 422 | delete m_scene; | ||
435 | m_scene = NULL; | 423 | m_scene = NULL; | ||
436 | compositeTimer.stop(); | 424 | compositeTimer.stop(); | ||
437 | repaints_region = QRegion(); | 425 | repaints_region = QRegion(); | ||
438 | m_finishing = false; | 426 | m_finishing = false; | ||
439 | emit compositingToggled(false); | 427 | emit compositingToggled(false); | ||
440 | } | 428 | } | ||
441 | 429 | | |||
442 | void Compositor::releaseCompositorSelection() | | |||
443 | { | | |||
444 | if (hasScene() && !m_finishing) { | | |||
445 | // compositor is up and running again, no need to release the selection | | |||
446 | return; | | |||
447 | } | | |||
448 | if (m_starting) { | | |||
449 | // currently still starting the compositor, it might fail, so restart the timer to test again | | |||
450 | m_releaseSelectionTimer.start(); | | |||
451 | return; | | |||
452 | } | | |||
453 | | ||||
454 | if (m_finishing) { | | |||
455 | // still shutting down, a restart might follow, so restart the timer to test again | | |||
456 | m_releaseSelectionTimer.start(); | | |||
457 | return; | | |||
458 | } | | |||
459 | qCDebug(KWIN_CORE) << "Releasing compositor selection"; | | |||
460 | if (m_selectionOwner) { | | |||
461 | m_selectionOwner->setOwning(false); | | |||
462 | m_selectionOwner->release(); | | |||
463 | } | | |||
464 | } | | |||
465 | | ||||
466 | void Compositor::keepSupportProperty(xcb_atom_t atom) | 430 | void Compositor::keepSupportProperty(xcb_atom_t atom) | ||
467 | { | 431 | { | ||
468 | m_unusedSupportProperties.removeAll(atom); | 432 | m_unusedSupportProperties.removeAll(atom); | ||
469 | } | 433 | } | ||
470 | 434 | | |||
471 | void Compositor::removeSupportProperty(xcb_atom_t atom) | 435 | void Compositor::removeSupportProperty(xcb_atom_t atom) | ||
472 | { | 436 | { | ||
473 | m_unusedSupportProperties << atom; | 437 | m_unusedSupportProperties << atom; | ||
Show All 17 Lines | 454 | foreach (const xcb_atom_t &atom, m_unusedSupportProperties) { | |||
491 | // remove property from root window | 455 | // remove property from root window | ||
492 | xcb_delete_property(c, kwinApp()->x11RootWindow(), atom); | 456 | xcb_delete_property(c, kwinApp()->x11RootWindow(), atom); | ||
493 | } | 457 | } | ||
494 | } | 458 | } | ||
495 | } | 459 | } | ||
496 | 460 | | |||
497 | void Compositor::slotConfigChanged() | 461 | void Compositor::slotConfigChanged() | ||
498 | { | 462 | { | ||
499 | if (!m_suspended) { | | |||
500 | setup(); | 463 | setup(); | ||
501 | if (effects) // setupCompositing() may fail | 464 | if (effects) // setupCompositing() may fail | ||
502 | effects->reconfigure(); | 465 | effects->reconfigure(); | ||
503 | addRepaintFull(); | 466 | addRepaintFull(); | ||
504 | } else | | |||
505 | finish(); | | |||
506 | } | 467 | } | ||
507 | 468 | | |||
508 | void Compositor::slotReinitialize() | 469 | void Compositor::reinitialize() | ||
509 | { | 470 | { | ||
510 | // Reparse config. Config options will be reloaded by setup() | 471 | // Reparse config. Config options will be reloaded by setup() | ||
511 | kwinApp()->config()->reparseConfiguration(); | 472 | kwinApp()->config()->reparseConfiguration(); | ||
512 | 473 | | |||
513 | // Restart compositing | 474 | // Restart compositing | ||
514 | finish(); | 475 | finish(); | ||
515 | // resume compositing if suspended | | |||
516 | m_suspended = NoReasonSuspend; | | |||
517 | options->setCompositingInitialized(false); | | |||
518 | setup(); | 476 | setup(); | ||
519 | 477 | | |||
520 | if (effects) { // setup() may fail | 478 | if (effects) { // setup() may fail | ||
521 | effects->reconfigure(); | 479 | effects->reconfigure(); | ||
522 | } | 480 | } | ||
523 | } | 481 | } | ||
524 | 482 | | |||
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() | | |||
597 | { | | |||
598 | if (hasScene()) { | | |||
599 | finish(); | | |||
600 | QTimer::singleShot(0, this, SLOT(setup())); | | |||
601 | } | | |||
602 | } | | |||
603 | | ||||
604 | void Compositor::addRepaint(int x, int y, int w, int h) | 483 | void Compositor::addRepaint(int x, int y, int w, int h) | ||
605 | { | 484 | { | ||
606 | if (!hasScene()) | 485 | if (!hasScene()) | ||
607 | return; | 486 | return; | ||
608 | repaints_region += QRegion(x, y, w, h); | 487 | repaints_region += QRegion(x, y, w, h); | ||
609 | scheduleRepaint(); | 488 | scheduleRepaint(); | ||
610 | } | 489 | } | ||
611 | 490 | | |||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | 532 | { | |||
659 | if (m_composeAtSwapCompletion) { | 538 | if (m_composeAtSwapCompletion) { | ||
660 | m_composeAtSwapCompletion = false; | 539 | m_composeAtSwapCompletion = false; | ||
661 | performCompositing(); | 540 | performCompositing(); | ||
662 | } | 541 | } | ||
663 | } | 542 | } | ||
664 | 543 | | |||
665 | void Compositor::performCompositing() | 544 | void Compositor::performCompositing() | ||
666 | { | 545 | { | ||
667 | if (m_scene->usesOverlayWindow() && !isOverlayWindowVisible()) | | |||
668 | return; // nothing is visible anyway | | |||
669 | | ||||
670 | // If a buffer swap is still pending, we return to the event loop and | 546 | // If a buffer swap is still pending, we return to the event loop and | ||
671 | // continue processing events until the swap has completed. | 547 | // continue processing events until the swap has completed. | ||
672 | if (m_bufferSwapPending) { | 548 | if (m_bufferSwapPending) { | ||
673 | m_composeAtSwapCompletion = true; | 549 | m_composeAtSwapCompletion = true; | ||
674 | compositeTimer.stop(); | 550 | compositeTimer.stop(); | ||
675 | return; | 551 | return; | ||
676 | } | 552 | } | ||
677 | 553 | | |||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Line(s) | 687 | if (auto w = waylandServer()) { | |||
822 | }; | 698 | }; | ||
823 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | 699 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | ||
824 | return true; | 700 | return true; | ||
825 | } | 701 | } | ||
826 | } | 702 | } | ||
827 | return false; | 703 | return false; | ||
828 | } | 704 | } | ||
829 | 705 | | |||
830 | void Compositor::setCompositeResetTimer(int msecs) | | |||
831 | { | | |||
832 | compositeResetTimer.start(msecs); | | |||
833 | } | | |||
834 | | ||||
835 | void Compositor::setCompositeTimer() | 706 | void Compositor::setCompositeTimer() | ||
836 | { | 707 | { | ||
837 | if (!hasScene()) // should not really happen, but there may be e.g. some damage events still pending | 708 | if (!hasScene()) // should not really happen, but there may be e.g. some damage events still pending | ||
838 | return; | 709 | return; | ||
839 | if (m_starting || !Workspace::self()) { | 710 | if (m_starting || !Workspace::self()) { | ||
840 | return; | 711 | return; | ||
841 | } | 712 | } | ||
842 | 713 | | |||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Line(s) | |||||
900 | compositeTimer.start(qMin(waitTime, 250u), this); // force 4fps minimum | 771 | compositeTimer.start(qMin(waitTime, 250u), this); // force 4fps minimum | ||
901 | } | 772 | } | ||
902 | 773 | | |||
903 | bool Compositor::isActive() | 774 | bool Compositor::isActive() | ||
904 | { | 775 | { | ||
905 | return !m_finishing && hasScene(); | 776 | return !m_finishing && hasScene(); | ||
906 | } | 777 | } | ||
907 | 778 | | |||
908 | bool Compositor::checkForOverlayWindow(WId w) const | 779 | WaylandCompositor::WaylandCompositor(QObject *parent) | ||
780 | : Compositor(parent) | ||||
781 | { | ||||
782 | } | ||||
783 | | ||||
784 | X11Compositor::X11Compositor(QObject *parent) | ||||
785 | : Compositor(parent) | ||||
786 | , m_xrrRefreshRate(0) | ||||
787 | { | ||||
788 | qRegisterMetaType<X11Compositor::SuspendReason>("X11Compositor::SuspendReason"); | ||||
789 | | ||||
790 | // 2 sec which should be enough to restart the compositor | ||||
791 | static const int compositorLostMessageDelay = 2000; | ||||
792 | | ||||
793 | m_releaseSelectionTimer.setSingleShot(true); | ||||
794 | m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); | ||||
795 | connect(&m_releaseSelectionTimer, &QTimer::timeout, this, &X11Compositor::releaseCompositorSelection); | ||||
796 | | ||||
797 | connect(&m_compositeResetTimer, &QTimer::timeout, this, &X11Compositor::reinitialize); | ||||
798 | m_compositeResetTimer.setSingleShot(true); | ||||
799 | | ||||
800 | connect(this, &X11Compositor::compositingToggled, this, [this](bool toggled) { | ||||
801 | if (toggled) { | ||||
802 | if (m_releaseSelectionTimer.isActive()) { | ||||
803 | m_releaseSelectionTimer.stop(); | ||||
804 | } | ||||
805 | } else if (!kwinApp()->isTerminating()) { | ||||
806 | // TODO: this can lead to a crash on destroy when compositingToggled | ||||
807 | // is emitted from ~Compositor | ||||
808 | m_releaseSelectionTimer.start(); | ||||
809 | } | ||||
810 | }); | ||||
811 | } | ||||
812 | | ||||
813 | void X11Compositor::suspend(X11Compositor::SuspendReason reason) | ||||
814 | { | ||||
815 | Q_ASSERT(reason != NoReasonSuspend); | ||||
816 | m_suspended |= reason; | ||||
817 | if (reason & ScriptSuspend) { | ||||
818 | // when disabled show a shortcut how the user can get back compositing | ||||
819 | const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild<QAction*>(QStringLiteral("Suspend Compositing"))); | ||||
820 | if (!shortcuts.isEmpty()) { | ||||
821 | // display notification only if there is the shortcut | ||||
822 | const QString message = i18n("Desktop effects have been suspended by another application.<br/>" | ||||
823 | "You can resume using the '%1' shortcut.", shortcuts.first().toString(QKeySequence::NativeText)); | ||||
824 | KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); | ||||
825 | } | ||||
826 | } | ||||
827 | finish(); | ||||
828 | } | ||||
829 | | ||||
830 | void X11Compositor::resume(X11Compositor::SuspendReason reason) | ||||
831 | { | ||||
832 | Q_ASSERT(reason != NoReasonSuspend); | ||||
833 | m_suspended &= ~reason; | ||||
834 | setup(); // signal "toggled" is eventually emitted from within setup | ||||
835 | } | ||||
836 | | ||||
837 | void X11Compositor::toggleCompositing() | ||||
838 | { | ||||
839 | if (m_suspended) { // direct user call; clear all bits | ||||
840 | resume(AllReasonSuspend); | ||||
841 | } else { // but only set the user one (sufficient to suspend) | ||||
842 | suspend(UserSuspend); | ||||
843 | } | ||||
844 | } | ||||
845 | | ||||
846 | void X11Compositor::updateCompositeBlocking() | ||||
847 | { | ||||
848 | updateClientCompositeBlocking(NULL); | ||||
849 | } | ||||
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()`. | |||||
850 | | ||||
851 | void X11Compositor::updateClientCompositeBlocking(Client *c) | ||||
852 | { | ||||
853 | if (c) { // if c == 0 we just check if we can resume | ||||
854 | if (c->isBlockingCompositing()) { | ||||
855 | if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain! | ||||
856 | QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, BlockRuleSuspend)); | ||||
857 | } | ||||
858 | } | ||||
859 | else if (m_suspended & BlockRuleSuspend) { // lost a client and we're blocked - can we resume? | ||||
860 | bool resume = true; | ||||
861 | for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { | ||||
862 | if ((*it)->isBlockingCompositing()) { | ||||
863 | resume = false; | ||||
864 | break; | ||||
865 | } | ||||
866 | } | ||||
867 | if (resume) { // do NOT attempt to call suspend(false); from within the eventchain! | ||||
868 | QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, BlockRuleSuspend)); | ||||
869 | } | ||||
870 | } | ||||
871 | } | ||||
872 | | ||||
873 | bool X11Compositor::startupWithWorkspace() | ||||
874 | { | ||||
875 | if (!Compositor::startupWithWorkspace()) { | ||||
876 | return false; | ||||
877 | } | ||||
878 | m_xrrRefreshRate = Options::currentRefreshRate(); | ||||
879 | return true; | ||||
880 | } | ||||
881 | | ||||
882 | bool X11Compositor::checkForOverlayWindow(WId w) const | ||||
909 | { | 883 | { | ||
910 | if (!hasScene()) { | 884 | if (!hasScene()) { | ||
911 | // no scene, so it cannot be the overlay window | 885 | // no scene, so it cannot be the overlay window | ||
912 | return false; | 886 | return false; | ||
913 | } | 887 | } | ||
914 | if (!m_scene->overlayWindow()) { | 888 | if (!m_scene->overlayWindow()) { | ||
915 | // no overlay window, it cannot be the overlay | 889 | // no overlay window, it cannot be the overlay | ||
916 | return false; | 890 | return false; | ||
917 | } | 891 | } | ||
918 | // and compare the window ID's | 892 | // and compare the window ID's | ||
919 | return w == m_scene->overlayWindow()->window(); | 893 | return w == m_scene->overlayWindow()->window(); | ||
920 | } | 894 | } | ||
921 | 895 | | |||
922 | bool Compositor::isOverlayWindowVisible() const | 896 | void X11Compositor::setup() | ||
897 | { | ||||
898 | if (kwinApp()->isTerminating() || hasScene()) { | ||||
899 | return; | ||||
900 | } | ||||
901 | | ||||
902 | if (m_suspended) { | ||||
903 | QStringList reasons; | ||||
904 | if (m_suspended & UserSuspend) { | ||||
905 | reasons << QStringLiteral("Disabled by User"); | ||||
906 | } | ||||
907 | if (m_suspended & BlockRuleSuspend) { | ||||
908 | reasons << QStringLiteral("Disabled by Window"); | ||||
909 | } | ||||
910 | if (m_suspended & ScriptSuspend) { | ||||
911 | reasons << QStringLiteral("Disabled by Script"); | ||||
912 | } | ||||
913 | qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; | ||||
914 | return; | ||||
915 | | ||||
916 | } else if (!kwinApp()->platform()->compositingPossible()) { | ||||
917 | qCCritical(KWIN_CORE) << "Compositing is not possible"; | ||||
918 | return; | ||||
919 | } | ||||
920 | | ||||
921 | Compositor::setup(); | ||||
922 | } | ||||
923 | | ||||
924 | void X11Compositor::slotConfigChanged() | ||||
925 | { | ||||
926 | if (m_suspended) { | ||||
927 | finish(); | ||||
928 | } else { | ||||
929 | Compositor::slotConfigChanged(); | ||||
930 | } | ||||
931 | } | ||||
932 | | ||||
933 | void X11Compositor::setCompositeResetTimer(int msecs) | ||||
934 | { | ||||
935 | m_compositeResetTimer.start(msecs); | ||||
936 | } | ||||
937 | | ||||
938 | bool X11Compositor::isOverlayWindowVisible() const | ||||
923 | { | 939 | { | ||
924 | if (!hasScene()) { | 940 | if (!hasScene()) { | ||
925 | return false; | 941 | return false; | ||
926 | } | 942 | } | ||
927 | if (!m_scene->overlayWindow()) { | 943 | if (!m_scene->overlayWindow()) { | ||
928 | return false; | 944 | return false; | ||
929 | } | 945 | } | ||
930 | return m_scene->overlayWindow()->isVisible(); | 946 | return m_scene->overlayWindow()->isVisible(); | ||
931 | } | 947 | } | ||
932 | 948 | | |||
933 | } // namespace | 949 | void X11Compositor::performCompositing() | ||
950 | { | ||||
951 | if (m_scene->usesOverlayWindow() && !isOverlayWindowVisible()) { | ||||
952 | return; // nothing is visible anyway | ||||
953 | } | ||||
954 | Compositor::performCompositing(); | ||||
955 | } | ||||
956 | | ||||
957 | void X11Compositor::releaseCompositorSelection() | ||||
958 | { | ||||
959 | if (hasScene() && !m_finishing) { | ||||
960 | // compositor is up and running again, no need to release the selection | ||||
961 | return; | ||||
962 | } | ||||
963 | if (m_starting) { | ||||
964 | // currently still starting the compositor, it might fail, so restart the timer to test again | ||||
965 | m_releaseSelectionTimer.start(); | ||||
966 | return; | ||||
967 | } | ||||
968 | | ||||
969 | if (m_finishing) { | ||||
970 | // still shutting down, a restart might follow, so restart the timer to test again | ||||
971 | m_releaseSelectionTimer.start(); | ||||
972 | return; | ||||
973 | } | ||||
974 | qCDebug(KWIN_CORE) << "Releasing compositor selection"; | ||||
975 | if (m_selectionOwner) { | ||||
976 | m_selectionOwner->setOwning(false); | ||||
977 | m_selectionOwner->release(); | ||||
978 | } | ||||
979 | } | ||||
980 | | ||||
981 | } | ||||
934 | 982 | | |||
935 | // included for CompositorSelectionOwner | 983 | // included for CompositorSelectionOwner | ||
936 | #include "composite.moc" | 984 | #include "composite.moc" |
Is there a reason for not initializing s_compositor in constructor of Compositor class?