1 /*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #include "composite.h"
10 #include "abstract_output.h"
11 #include "dbusinterface.h"
12 #include "x11client.h"
13 #include "decorations/decoratedclient.h"
14 #include "deleted.h"
15 #include "effects.h"
16 #include "ftrace.h"
17 #include "internal_client.h"
18 #include "overlaywindow.h"
19 #include "platform.h"
20 #include "renderloop.h"
21 #include "scene.h"
22 #include "screens.h"
23 #include "shadow.h"
24 #include "surfaceitem_x11.h"
25 #include "unmanaged.h"
26 #include "useractions.h"
27 #include "utils.h"
28 #include "wayland_server.h"
29 #include "workspace.h"
30 #include "x11syncmanager.h"
31 #include "xcbutils.h"
32
33 #include <kwingltexture.h>
34
35 #include <KWaylandServer/surface_interface.h>
36
37 #include <KGlobalAccel>
38 #include <KLocalizedString>
39 #include <KPluginLoader>
40 #include <KPluginMetaData>
41 #include <KNotification>
42 #include <KSelectionOwner>
43
44 #include <QDateTime>
45 #include <QFutureWatcher>
46 #include <QMenu>
47 #include <QOpenGLContext>
48 #include <QQuickWindow>
49 #include <QtConcurrentRun>
50 #include <QTextStream>
51 #include <QTimerEvent>
52
53 #include <xcb/composite.h>
54 #include <xcb/damage.h>
55
56 #include <cstdio>
57
58 Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
59
60 namespace KWin
61 {
62
63 // See main.cpp:
64 extern int screen_number;
65
66 extern bool is_multihead;
67
68 Compositor *Compositor::s_compositor = nullptr;
self()69 Compositor *Compositor::self()
70 {
71 return s_compositor;
72 }
73
create(QObject * parent)74 WaylandCompositor *WaylandCompositor::create(QObject *parent)
75 {
76 Q_ASSERT(!s_compositor);
77 auto *compositor = new WaylandCompositor(parent);
78 s_compositor = compositor;
79 return compositor;
80 }
create(QObject * parent)81 X11Compositor *X11Compositor::create(QObject *parent)
82 {
83 Q_ASSERT(!s_compositor);
84 auto *compositor = new X11Compositor(parent);
85 s_compositor = compositor;
86 return compositor;
87 }
88
89 class CompositorSelectionOwner : public KSelectionOwner
90 {
91 Q_OBJECT
92 public:
CompositorSelectionOwner(const char * selection)93 CompositorSelectionOwner(const char *selection)
94 : KSelectionOwner(selection, connection(), rootWindow())
95 , m_owning(false)
96 {
97 connect (this, &CompositorSelectionOwner::lostOwnership,
98 this, [this]() { m_owning = false; });
99 }
owning() const100 bool owning() const {
101 return m_owning;
102 }
setOwning(bool own)103 void setOwning(bool own) {
104 m_owning = own;
105 }
106 private:
107 bool m_owning;
108 };
109
Compositor(QObject * workspace)110 Compositor::Compositor(QObject* workspace)
111 : QObject(workspace)
112 , m_state(State::Off)
113 , m_selectionOwner(nullptr)
114 , m_scene(nullptr)
115 {
116 connect(options, &Options::configChanged, this, &Compositor::configChanged);
117 connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
118
119 // 2 sec which should be enough to restart the compositor.
120 static const int compositorLostMessageDelay = 2000;
121
122 m_releaseSelectionTimer.setSingleShot(true);
123 m_releaseSelectionTimer.setInterval(compositorLostMessageDelay);
124 connect(&m_releaseSelectionTimer, &QTimer::timeout,
125 this, &Compositor::releaseCompositorSelection);
126
127 m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
128 m_unusedSupportPropertyTimer.setSingleShot(true);
129 connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
130 this, &Compositor::deleteUnusedSupportProperties);
131
132 // Delay the call to start by one event cycle.
133 // The ctor of this class is invoked from the Workspace ctor, that means before
134 // Workspace is completely constructed, so calling Workspace::self() would result
135 // in undefined behavior. This is fixed by using a delayed invocation.
136 if (kwinApp()->platform()->isReady()) {
137 QTimer::singleShot(0, this, &Compositor::start);
138 }
139 connect(kwinApp()->platform(), &Platform::readyChanged, this,
140 [this] (bool ready) {
141 if (ready) {
142 start();
143 } else {
144 stop();
145 }
146 }, Qt::QueuedConnection
147 );
148
149 // register DBus
150 new CompositorDBusInterface(this);
151 FTraceLogger::create();
152 }
153
~Compositor()154 Compositor::~Compositor()
155 {
156 deleteUnusedSupportProperties();
157 destroyCompositorSelection();
158 s_compositor = nullptr;
159 }
160
setupStart()161 bool Compositor::setupStart()
162 {
163 if (kwinApp()->isTerminating()) {
164 // Don't start while KWin is terminating. An event to restart might be lingering
165 // in the event queue due to graphics reset.
166 return false;
167 }
168 if (m_state != State::Off) {
169 return false;
170 }
171 m_state = State::Starting;
172
173 options->reloadCompositingSettings(true);
174
175 initializeX11();
176
177 // There might still be a deleted around, needs to be cleared before
178 // creating the scene (BUG 333275).
179 if (Workspace::self()) {
180 while (!Workspace::self()->deletedList().isEmpty()) {
181 Workspace::self()->deletedList().first()->discard();
182 }
183 }
184
185 Q_EMIT aboutToToggleCompositing();
186
187 auto supportedCompositors = kwinApp()->platform()->supportedCompositors();
188 const auto userConfigIt = std::find(supportedCompositors.begin(), supportedCompositors.end(),
189 options->compositingMode());
190
191 if (userConfigIt != supportedCompositors.end()) {
192 supportedCompositors.erase(userConfigIt);
193 supportedCompositors.prepend(options->compositingMode());
194 } else {
195 qCWarning(KWIN_CORE)
196 << "Configured compositor not supported by Platform. Falling back to defaults";
197 }
198
199 const auto availablePlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kwin.scenes"));
200
201 for (const KPluginMetaData &pluginMetaData : availablePlugins) {
202 qCDebug(KWIN_CORE) << "Available scene plugin:" << pluginMetaData.fileName();
203 }
204
205 for (auto type : qAsConst(supportedCompositors)) {
206 switch (type) {
207 case OpenGLCompositing:
208 qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
209 break;
210 case QPainterCompositing:
211 qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene";
212 break;
213 case NoCompositing:
214 qCDebug(KWIN_CORE) << "Starting without compositing...";
215 break;
216 }
217 const auto pluginIt = std::find_if(availablePlugins.begin(), availablePlugins.end(),
218 [type] (const auto &plugin) {
219 const auto &metaData = plugin.rawData();
220 auto it = metaData.find(QStringLiteral("CompositingType"));
221 if (it != metaData.end()) {
222 if ((*it).toInt() == int{type}) {
223 return true;
224 }
225 }
226 return false;
227 });
228 if (pluginIt != availablePlugins.end()) {
229 std::unique_ptr<SceneFactory>
230 factory{ qobject_cast<SceneFactory*>(pluginIt->instantiate()) };
231 if (factory) {
232 m_scene = factory->create(this);
233 if (m_scene) {
234 if (!m_scene->initFailed()) {
235 qCDebug(KWIN_CORE) << "Instantiated compositing plugin:"
236 << pluginIt->name();
237 break;
238 } else {
239 delete m_scene;
240 m_scene = nullptr;
241 }
242 }
243 }
244 }
245 }
246
247 if (m_scene == nullptr || m_scene->initFailed()) {
248 qCCritical(KWIN_CORE) << "Failed to initialize compositing, compositing disabled";
249 m_state = State::Off;
250
251 delete m_scene;
252 m_scene = nullptr;
253
254 if (auto *con = kwinApp()->x11Connection()) {
255 xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(),
256 XCB_COMPOSITE_REDIRECT_MANUAL);
257 }
258 if (m_selectionOwner) {
259 m_selectionOwner->setOwning(false);
260 m_selectionOwner->release();
261 }
262 if (!supportedCompositors.contains(NoCompositing)) {
263 qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
264 qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
265 qApp->quit();
266 }
267 return false;
268 }
269
270 kwinApp()->platform()->setSelectedCompositor(m_scene->compositingType());
271
272 if (!Workspace::self() && m_scene && m_scene->compositingType() == QPainterCompositing) {
273 // Force Software QtQuick on first startup with QPainter.
274 QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
275 }
276
277 connect(m_scene, &Scene::resetCompositing, this, &Compositor::reinitialize);
278 Q_EMIT sceneCreated();
279
280 return true;
281 }
282
initializeX11()283 void Compositor::initializeX11()
284 {
285 xcb_connection_t *connection = kwinApp()->x11Connection();
286 if (!connection) {
287 return;
288 }
289
290 if (!m_selectionOwner) {
291 char selection_name[ 100 ];
292 sprintf(selection_name, "_NET_WM_CM_S%d", Application::x11ScreenNumber());
293 m_selectionOwner = new CompositorSelectionOwner(selection_name);
294 connect(m_selectionOwner, &CompositorSelectionOwner::lostOwnership,
295 this, &Compositor::stop);
296 }
297 if (!m_selectionOwner->owning()) {
298 // Force claim ownership.
299 m_selectionOwner->claim(true);
300 m_selectionOwner->setOwning(true);
301 }
302
303 xcb_composite_redirect_subwindows(connection, kwinApp()->x11RootWindow(),
304 XCB_COMPOSITE_REDIRECT_MANUAL);
305 }
306
cleanupX11()307 void Compositor::cleanupX11()
308 {
309 delete m_selectionOwner;
310 m_selectionOwner = nullptr;
311 }
312
startupWithWorkspace()313 void Compositor::startupWithWorkspace()
314 {
315 connect(kwinApp(), &Application::x11ConnectionChanged,
316 this, &Compositor::initializeX11, Qt::UniqueConnection);
317 connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
318 this, &Compositor::cleanupX11, Qt::UniqueConnection);
319 initializeX11();
320
321 Workspace::self()->markXStackingOrderAsDirty();
322 Q_ASSERT(m_scene);
323
324 const Platform *platform = kwinApp()->platform();
325 if (platform->isPerScreenRenderingEnabled()) {
326 const QVector<AbstractOutput *> outputs = platform->enabledOutputs();
327 for (AbstractOutput *output : outputs) {
328 registerRenderLoop(output->renderLoop(), output);
329 }
330 connect(platform, &Platform::outputEnabled,
331 this, &Compositor::handleOutputEnabled);
332 connect(platform, &Platform::outputDisabled,
333 this, &Compositor::handleOutputDisabled);
334 } else {
335 registerRenderLoop(platform->renderLoop(), nullptr);
336 }
337
338 m_state = State::On;
339
340 // Sets also the 'effects' pointer.
341 kwinApp()->platform()->createEffectsHandler(this, m_scene);
342 connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel);
343 connect(effects, &EffectsHandler::virtualScreenGeometryChanged, this, &Compositor::addRepaintFull);
344
345 for (X11Client *c : Workspace::self()->clientList()) {
346 c->setupCompositing();
347 c->updateShadow();
348 }
349 for (Unmanaged *c : Workspace::self()->unmanagedList()) {
350 c->setupCompositing();
351 c->updateShadow();
352 }
353 for (InternalClient *client : workspace()->internalClients()) {
354 client->setupCompositing();
355 client->updateShadow();
356 }
357
358 if (auto *server = waylandServer()) {
359 const auto clients = server->clients();
360 for (AbstractClient *c : clients) {
361 c->setupCompositing();
362 c->updateShadow();
363 }
364 }
365
366 Q_EMIT compositingToggled(true);
367
368 if (m_releaseSelectionTimer.isActive()) {
369 m_releaseSelectionTimer.stop();
370 }
371
372 // Render at least once.
373 addRepaintFull();
374 }
375
registerRenderLoop(RenderLoop * renderLoop,AbstractOutput * output)376 void Compositor::registerRenderLoop(RenderLoop *renderLoop, AbstractOutput *output)
377 {
378 Q_ASSERT(!m_renderLoops.contains(renderLoop));
379 m_renderLoops.insert(renderLoop, output);
380 connect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
381 }
382
unregisterRenderLoop(RenderLoop * renderLoop)383 void Compositor::unregisterRenderLoop(RenderLoop *renderLoop)
384 {
385 Q_ASSERT(m_renderLoops.contains(renderLoop));
386 m_renderLoops.remove(renderLoop);
387 disconnect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
388 }
389
handleOutputEnabled(AbstractOutput * output)390 void Compositor::handleOutputEnabled(AbstractOutput *output)
391 {
392 registerRenderLoop(output->renderLoop(), output);
393 }
394
handleOutputDisabled(AbstractOutput * output)395 void Compositor::handleOutputDisabled(AbstractOutput *output)
396 {
397 unregisterRenderLoop(output->renderLoop());
398 }
399
scheduleRepaint()400 void Compositor::scheduleRepaint()
401 {
402 for (auto it = m_renderLoops.constBegin(); it != m_renderLoops.constEnd(); ++it) {
403 it.key()->scheduleRepaint();
404 }
405 }
406
stop()407 void Compositor::stop()
408 {
409 if (m_state == State::Off || m_state == State::Stopping) {
410 return;
411 }
412 m_state = State::Stopping;
413 Q_EMIT aboutToToggleCompositing();
414
415 m_releaseSelectionTimer.start();
416
417 // Some effects might need access to effect windows when they are about to
418 // be destroyed, for example to unreference deleted windows, so we have to
419 // make sure that effect windows outlive effects.
420 delete effects;
421 effects = nullptr;
422
423 if (Workspace::self()) {
424 for (X11Client *c : Workspace::self()->clientList()) {
425 m_scene->removeToplevel(c);
426 }
427 for (Unmanaged *c : Workspace::self()->unmanagedList()) {
428 m_scene->removeToplevel(c);
429 }
430 for (InternalClient *client : workspace()->internalClients()) {
431 m_scene->removeToplevel(client);
432 }
433 for (X11Client *c : Workspace::self()->clientList()) {
434 c->finishCompositing();
435 }
436 for (Unmanaged *c : Workspace::self()->unmanagedList()) {
437 c->finishCompositing();
438 }
439 for (InternalClient *client : workspace()->internalClients()) {
440 client->finishCompositing();
441 }
442 if (auto *con = kwinApp()->x11Connection()) {
443 xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(),
444 XCB_COMPOSITE_REDIRECT_MANUAL);
445 }
446 while (!workspace()->deletedList().isEmpty()) {
447 workspace()->deletedList().first()->discard();
448 }
449 }
450
451 if (waylandServer()) {
452 const QList<AbstractClient *> toRemoveTopLevel = waylandServer()->clients();
453 for (AbstractClient *c : toRemoveTopLevel) {
454 m_scene->removeToplevel(c);
455 }
456 const QList<AbstractClient *> toFinishCompositing = waylandServer()->clients();
457 for (AbstractClient *c : toFinishCompositing) {
458 c->finishCompositing();
459 }
460 }
461
462 while (!m_renderLoops.isEmpty()) {
463 unregisterRenderLoop(m_renderLoops.firstKey());
464 }
465
466 disconnect(kwinApp()->platform(), &Platform::outputEnabled,
467 this, &Compositor::handleOutputEnabled);
468 disconnect(kwinApp()->platform(), &Platform::outputDisabled,
469 this, &Compositor::handleOutputDisabled);
470
471 delete m_scene;
472 m_scene = nullptr;
473
474 m_state = State::Off;
475 Q_EMIT compositingToggled(false);
476 }
477
destroyCompositorSelection()478 void Compositor::destroyCompositorSelection()
479 {
480 delete m_selectionOwner;
481 m_selectionOwner = nullptr;
482 }
483
releaseCompositorSelection()484 void Compositor::releaseCompositorSelection()
485 {
486 switch (m_state) {
487 case State::On:
488 // We are compositing at the moment. Don't release.
489 break;
490 case State::Off:
491 if (m_selectionOwner) {
492 qCDebug(KWIN_CORE) << "Releasing compositor selection";
493 m_selectionOwner->setOwning(false);
494 m_selectionOwner->release();
495 }
496 break;
497 case State::Starting:
498 case State::Stopping:
499 // Still starting or shutting down the compositor. Starting might fail
500 // or after stopping a restart might follow. So test again later on.
501 m_releaseSelectionTimer.start();
502 break;
503 }
504 }
505
keepSupportProperty(xcb_atom_t atom)506 void Compositor::keepSupportProperty(xcb_atom_t atom)
507 {
508 m_unusedSupportProperties.removeAll(atom);
509 }
510
removeSupportProperty(xcb_atom_t atom)511 void Compositor::removeSupportProperty(xcb_atom_t atom)
512 {
513 m_unusedSupportProperties << atom;
514 m_unusedSupportPropertyTimer.start();
515 }
516
deleteUnusedSupportProperties()517 void Compositor::deleteUnusedSupportProperties()
518 {
519 if (m_state == State::Starting || m_state == State::Stopping) {
520 // Currently still maybe restarting the compositor.
521 m_unusedSupportPropertyTimer.start();
522 return;
523 }
524 if (auto *con = kwinApp()->x11Connection()) {
525 for (const xcb_atom_t &atom : qAsConst(m_unusedSupportProperties)) {
526 // remove property from root window
527 xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
528 }
529 m_unusedSupportProperties.clear();
530 }
531 }
532
configChanged()533 void Compositor::configChanged()
534 {
535 reinitialize();
536 addRepaintFull();
537 }
538
reinitialize()539 void Compositor::reinitialize()
540 {
541 // Reparse config. Config options will be reloaded by start()
542 kwinApp()->config()->reparseConfiguration();
543
544 // Restart compositing
545 stop();
546 start();
547
548 if (effects) { // start() may fail
549 effects->reconfigure();
550 }
551 }
552
addRepaint(int x,int y,int width,int height)553 void Compositor::addRepaint(int x, int y, int width, int height)
554 {
555 addRepaint(QRegion(x, y, width, height));
556 }
557
addRepaint(const QRect & rect)558 void Compositor::addRepaint(const QRect &rect)
559 {
560 addRepaint(QRegion(rect));
561 }
562
addRepaint(const QRegion & region)563 void Compositor::addRepaint(const QRegion ®ion)
564 {
565 if (m_scene) {
566 m_scene->addRepaint(region);
567 }
568 }
569
addRepaintFull()570 void Compositor::addRepaintFull()
571 {
572 addRepaint(screens()->geometry());
573 }
574
handleFrameRequested(RenderLoop * renderLoop)575 void Compositor::handleFrameRequested(RenderLoop *renderLoop)
576 {
577 composite(renderLoop);
578 }
579
windowsToRender() const580 QList<Toplevel *> Compositor::windowsToRender() const
581 {
582 // Create a list of all windows in the stacking order
583 QList<Toplevel *> windows = Workspace::self()->xStackingOrder();
584
585 // Move elevated windows to the top of the stacking order
586 const QList<EffectWindow *> elevatedList = static_cast<EffectsHandlerImpl *>(effects)->elevatedWindows();
587 for (EffectWindow *c : elevatedList) {
588 Toplevel *t = static_cast<EffectWindowImpl *>(c)->window();
589 windows.removeAll(t);
590 windows.append(t);
591 }
592
593 // Skip windows that are not yet ready for being painted and if screen is locked skip windows
594 // that are neither lockscreen nor inputmethod windows.
595 //
596 // TODO? This cannot be used so carelessly - needs protections against broken clients, the
597 // window should not get focus before it's displayed, handle unredirected windows properly and
598 // so on.
599 for (Toplevel *win : windows) {
600 if (!win->readyForPainting()) {
601 windows.removeAll(win);
602 }
603 if (waylandServer() && waylandServer()->isScreenLocked()) {
604 if(!win->isLockScreen() && !win->isInputMethod()) {
605 windows.removeAll(win);
606 }
607 }
608 }
609 return windows;
610 }
611
composite(RenderLoop * renderLoop)612 void Compositor::composite(RenderLoop *renderLoop)
613 {
614 const auto &output = m_renderLoops[renderLoop];
615
616 fTraceDuration("Paint (", output ? output->name() : QStringLiteral("screens"), ")");
617
618 const auto windows = windowsToRender();
619
620 const QRegion repaints = m_scene->repaints(output);
621 m_scene->resetRepaints(output);
622
623 m_scene->paint(output, repaints, windows, renderLoop);
624
625 if (waylandServer()) {
626 const std::chrono::milliseconds frameTime =
627 std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->lastPresentationTimestamp());
628
629 for (Toplevel *window : windows) {
630 if (!window->readyForPainting()) {
631 continue;
632 }
633 if (waylandServer()->isScreenLocked() &&
634 !(window->isLockScreen() || window->isInputMethod())) {
635 continue;
636 }
637 if (!window->isOnOutput(output)) {
638 continue;
639 }
640 if (auto surface = window->surface()) {
641 surface->frameRendered(frameTime.count());
642 }
643 }
644 if (!kwinApp()->platform()->isCursorHidden()) {
645 Cursors::self()->currentCursor()->markAsRendered();
646 }
647 }
648 }
649
isActive()650 bool Compositor::isActive()
651 {
652 return m_state == State::On;
653 }
654
WaylandCompositor(QObject * parent)655 WaylandCompositor::WaylandCompositor(QObject *parent)
656 : Compositor(parent)
657 {
658 connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
659 this, &WaylandCompositor::destroyCompositorSelection);
660 }
661
~WaylandCompositor()662 WaylandCompositor::~WaylandCompositor()
663 {
664 Q_EMIT aboutToDestroy();
665 stop(); // this can't be called in the destructor of Compositor
666 }
667
toggleCompositing()668 void WaylandCompositor::toggleCompositing()
669 {
670 // For the shortcut. Not possible on Wayland because we always composite.
671 }
672
start()673 void WaylandCompositor::start()
674 {
675 if (!Compositor::setupStart()) {
676 // Internal setup failed, abort.
677 return;
678 }
679
680 if (Workspace::self()) {
681 startupWithWorkspace();
682 } else {
683 connect(kwinApp(), &Application::workspaceCreated,
684 this, &WaylandCompositor::startupWithWorkspace);
685 }
686 }
687
X11Compositor(QObject * parent)688 X11Compositor::X11Compositor(QObject *parent)
689 : Compositor(parent)
690 , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
691 {
692 if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) {
693 m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
694 }
695 }
696
~X11Compositor()697 X11Compositor::~X11Compositor()
698 {
699 Q_EMIT aboutToDestroy();
700 stop(); // this can't be called in the destructor of Compositor
701 }
702
syncManager() const703 X11SyncManager *X11Compositor::syncManager() const
704 {
705 return m_syncManager.data();
706 }
707
toggleCompositing()708 void X11Compositor::toggleCompositing()
709 {
710 if (m_suspended) {
711 // Direct user call; clear all bits.
712 resume(AllReasonSuspend);
713 } else {
714 // But only set the user one (sufficient to suspend).
715 suspend(UserSuspend);
716 }
717 }
718
reinitialize()719 void X11Compositor::reinitialize()
720 {
721 // Resume compositing if suspended.
722 m_suspended = NoReasonSuspend;
723 Compositor::reinitialize();
724 }
725
configChanged()726 void X11Compositor::configChanged()
727 {
728 if (m_suspended) {
729 stop();
730 return;
731 }
732 Compositor::configChanged();
733 }
734
suspend(X11Compositor::SuspendReason reason)735 void X11Compositor::suspend(X11Compositor::SuspendReason reason)
736 {
737 Q_ASSERT(reason != NoReasonSuspend);
738 m_suspended |= reason;
739
740 if (reason & ScriptSuspend) {
741 // When disabled show a shortcut how the user can get back compositing.
742 const auto shortcuts = KGlobalAccel::self()->shortcut(
743 workspace()->findChild<QAction*>(QStringLiteral("Suspend Compositing")));
744 if (!shortcuts.isEmpty()) {
745 // Display notification only if there is the shortcut.
746 const QString message =
747 i18n("Desktop effects have been suspended by another application.<br/>"
748 "You can resume using the '%1' shortcut.",
749 shortcuts.first().toString(QKeySequence::NativeText));
750 KNotification::event(QStringLiteral("compositingsuspendeddbus"), message);
751 }
752 }
753 stop();
754 }
755
resume(X11Compositor::SuspendReason reason)756 void X11Compositor::resume(X11Compositor::SuspendReason reason)
757 {
758 Q_ASSERT(reason != NoReasonSuspend);
759 m_suspended &= ~reason;
760 start();
761 }
762
start()763 void X11Compositor::start()
764 {
765 if (m_suspended) {
766 QStringList reasons;
767 if (m_suspended & UserSuspend) {
768 reasons << QStringLiteral("Disabled by User");
769 }
770 if (m_suspended & BlockRuleSuspend) {
771 reasons << QStringLiteral("Disabled by Window");
772 }
773 if (m_suspended & ScriptSuspend) {
774 reasons << QStringLiteral("Disabled by Script");
775 }
776 qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
777 return;
778 } else if (!kwinApp()->platform()->compositingPossible()) {
779 qCCritical(KWIN_CORE) << "Compositing is not possible";
780 return;
781 }
782 if (!Compositor::setupStart()) {
783 // Internal setup failed, abort.
784 return;
785 }
786 startupWithWorkspace();
787 m_syncManager.reset(X11SyncManager::create());
788 }
789
stop()790 void X11Compositor::stop()
791 {
792 m_syncManager.reset();
793 Compositor::stop();
794 }
795
composite(RenderLoop * renderLoop)796 void X11Compositor::composite(RenderLoop *renderLoop)
797 {
798 if (scene()->overlayWindow() && !isOverlayWindowVisible()) {
799 // Return since nothing is visible.
800 return;
801 }
802
803 QList<Toplevel *> windows = Workspace::self()->xStackingOrder();
804 QList<SurfaceItemX11 *> dirtyItems;
805
806 // Reset the damage state of each window and fetch the damage region
807 // without waiting for a reply
808 for (Toplevel *window : qAsConst(windows)) {
809 SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
810 if (surfaceItem->fetchDamage()) {
811 dirtyItems.append(surfaceItem);
812 }
813 }
814
815 if (dirtyItems.count() > 0) {
816 if (m_syncManager) {
817 m_syncManager->triggerFence();
818 }
819 xcb_flush(kwinApp()->x11Connection());
820 }
821
822 // Get the replies
823 for (SurfaceItemX11 *item : qAsConst(dirtyItems)) {
824 item->waitForDamage();
825 }
826
827 if (m_framesToTestForSafety > 0 && (scene()->compositingType() & OpenGLCompositing)) {
828 kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame);
829 }
830
831 Compositor::composite(renderLoop);
832
833 if (m_syncManager) {
834 if (!m_syncManager->endFrame()) {
835 qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
836 qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
837 m_syncManager.reset();
838 }
839 }
840
841 if (m_framesToTestForSafety > 0) {
842 if (scene()->compositingType() & OpenGLCompositing) {
843 kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame);
844 }
845 m_framesToTestForSafety--;
846 if (m_framesToTestForSafety == 0 && (scene()->compositingType() & OpenGLCompositing)) {
847 kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostLastGuardedFrame);
848 }
849 }
850 }
851
checkForOverlayWindow(WId w) const852 bool X11Compositor::checkForOverlayWindow(WId w) const
853 {
854 if (!scene()) {
855 // No scene, so it cannot be the overlay window.
856 return false;
857 }
858 if (!scene()->overlayWindow()) {
859 // No overlay window, it cannot be the overlay.
860 return false;
861 }
862 // Compare the window ID's.
863 return w == scene()->overlayWindow()->window();
864 }
865
isOverlayWindowVisible() const866 bool X11Compositor::isOverlayWindowVisible() const
867 {
868 if (!scene()) {
869 return false;
870 }
871 if (!scene()->overlayWindow()) {
872 return false;
873 }
874 return scene()->overlayWindow()->isVisible();
875 }
876
updateClientCompositeBlocking(X11Client * c)877 void X11Compositor::updateClientCompositeBlocking(X11Client *c)
878 {
879 if (c) {
880 if (c->isBlockingCompositing()) {
881 // Do NOT attempt to call suspend(true) from within the eventchain!
882 if (!(m_suspended & BlockRuleSuspend))
883 QMetaObject::invokeMethod(this, [this]() {
884 suspend(BlockRuleSuspend);
885 }, Qt::QueuedConnection);
886 }
887 }
888 else if (m_suspended & BlockRuleSuspend) {
889 // If !c we just check if we can resume in case a blocking client was lost.
890 bool shouldResume = true;
891
892 for (auto it = Workspace::self()->clientList().constBegin();
893 it != Workspace::self()->clientList().constEnd(); ++it) {
894 if ((*it)->isBlockingCompositing()) {
895 shouldResume = false;
896 break;
897 }
898 }
899 if (shouldResume) {
900 // Do NOT attempt to call suspend(false) from within the eventchain!
901 QMetaObject::invokeMethod(this, [this]() {
902 resume(BlockRuleSuspend);
903 }, Qt::QueuedConnection);
904 }
905 }
906 }
907
self()908 X11Compositor *X11Compositor::self()
909 {
910 return qobject_cast<X11Compositor *>(Compositor::self());
911 }
912
913 }
914
915 // included for CompositorSelectionOwner
916 #include "composite.moc"
917