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 &region)
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