diff --git a/autotests/integration/dont_crash_reinitialize_compositor.cpp b/autotests/integration/dont_crash_reinitialize_compositor.cpp --- a/autotests/integration/dont_crash_reinitialize_compositor.cpp +++ b/autotests/integration/dont_crash_reinitialize_compositor.cpp @@ -158,7 +158,7 @@ QVERIFY(effect->isActive()); // Re-initialize the compositor, effects will be destroyed and created again. - Compositor::self()->slotReinitialize(); + Compositor::self()->reinitialize(); // By this time, KWin should still be alive. } diff --git a/autotests/integration/generic_scene_opengl_test.cpp b/autotests/integration/generic_scene_opengl_test.cpp --- a/autotests/integration/generic_scene_opengl_test.cpp +++ b/autotests/integration/generic_scene_opengl_test.cpp @@ -102,7 +102,7 @@ QSignalSpy sceneCreatedSpy(KWin::Compositor::self(), &Compositor::sceneCreated); QVERIFY(sceneCreatedSpy.isValid()); - KWin::Compositor::self()->slotReinitialize(); + KWin::Compositor::self()->reinitialize(); if (sceneCreatedSpy.isEmpty()) { QVERIFY(sceneCreatedSpy.wait()); } diff --git a/autotests/integration/kwin_wayland_test.cpp b/autotests/integration/kwin_wayland_test.cpp --- a/autotests/integration/kwin_wayland_test.cpp +++ b/autotests/integration/kwin_wayland_test.cpp @@ -126,7 +126,7 @@ { disconnect(kwinApp()->platform(), &Platform::screensQueried, this, &WaylandTestApplication::continueStartupWithScreens); createScreens(); - createCompositor(); + WaylandCompositor::create(); connect(Compositor::self(), &Compositor::sceneCreated, this, &WaylandTestApplication::continueStartupWithScene); } diff --git a/autotests/integration/scene_qpainter_test.cpp b/autotests/integration/scene_qpainter_test.cpp --- a/autotests/integration/scene_qpainter_test.cpp +++ b/autotests/integration/scene_qpainter_test.cpp @@ -283,7 +283,7 @@ QVERIFY(oldScene); QSignalSpy sceneCreatedSpy(KWin::Compositor::self(), &KWin::Compositor::sceneCreated); QVERIFY(sceneCreatedSpy.isValid()); - KWin::Compositor::self()->slotReinitialize(); + KWin::Compositor::self()->reinitialize(); if (sceneCreatedSpy.isEmpty()) { QVERIFY(sceneCreatedSpy.wait()); } diff --git a/composite.h b/composite.h --- a/composite.h +++ b/composite.h @@ -2,8 +2,9 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 2011 Arthur Arlt -Copyright (C) 2012 Martin Gräßlin +Copyright © 2011 Arthur Arlt +Copyright © 2012 Martin Gräßlin +Copyright © 2019 Roman Gilg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,12 +19,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ +#pragma once -#ifndef KWIN_COMPOSITE_H -#define KWIN_COMPOSITE_H -// KWin #include -// Qt + #include #include #include @@ -36,54 +35,37 @@ class CompositorSelectionOwner; class Scene; -class KWIN_EXPORT Compositor : public QObject { +class KWIN_EXPORT Compositor : public QObject +{ Q_OBJECT public: - enum SuspendReason { NoReasonSuspend = 0, UserSuspend = 1<<0, BlockRuleSuspend = 1<<1, ScriptSuspend = 1<<2, AllReasonSuspend = 0xff }; - Q_DECLARE_FLAGS(SuspendReasons, SuspendReason) - ~Compositor(); + virtual ~Compositor(); + // when adding repaints caused by a window, you probably want to use // either Toplevel::addRepaint() or Toplevel::addWorkspaceRepaint() void addRepaint(const QRect& r); void addRepaint(const QRegion& r); void addRepaint(int x, int y, int w, int h); + /** * Whether the Compositor is active. That is a Scene is present and the Compositor is * not shutting down itself. **/ bool isActive(); - int xrrRefreshRate() const { - return m_xrrRefreshRate; - } - void setCompositeResetTimer(int msecs); bool hasScene() const { return m_scene != NULL; } /** * Checks whether @p w is the Scene's overlay window. **/ - bool checkForOverlayWindow(WId w) const; - /** - * @returns Whether the Scene's Overlay X Window is visible. - **/ - bool isOverlayWindowVisible() const; + virtual bool checkForOverlayWindow(WId w) const { Q_UNUSED(w); return false; } Scene *scene() { return m_scene; } - /** - * @brief Checks whether the Compositor has already been created by the Workspace. - * - * This method can be used to check whether self will return the Compositor instance or @c null. - * - * @return bool @c true if the Compositor has been created, @c false otherwise - **/ - static bool isCreated() { - return s_compositor != NULL; - } /** * @brief Static check to test whether the Compositor is available and active. * @@ -97,132 +79,189 @@ void keepSupportProperty(xcb_atom_t atom); void removeSupportProperty(xcb_atom_t atom); -public Q_SLOTS: - void addRepaintFull(); - /** - * @brief Suspends the Compositor if it is currently active. - * - * Note: it is possible that the Compositor is not able to suspend. Use isActive to check - * whether the Compositor has been suspended. - * - * @return void - * @see resume - * @see isActive - **/ - void suspend(Compositor::SuspendReason reason); - /** - * @brief Resumes the Compositor if it is currently suspended. - * - * Note: it is possible that the Compositor cannot be resumed, that is there might be Clients - * blocking the usage of Compositing or the Scene might be broken. Use isActive to check - * whether the Compositor has been resumed. Also check isCompositingPossible and - * isOpenGLBroken. - * - * Note: The starting of the Compositor can require some time and is partially done threaded. - * After this method returns the setup may not have been completed. - * - * @return void - * @see suspend - * @see isActive - * @see isCompositingPossible - * @see isOpenGLBroken - **/ - void resume(Compositor::SuspendReason reason); /** * Actual slot to perform the toggling compositing. * That is if the Compositor is suspended it will be resumed and if the Compositor is active * it will be suspended. - * Invoked primarily by the keybinding. + * Invoked primarily by the keybinding (shortcut default: Shift + Alt + F12). * TODO: make private slot **/ - void slotToggleCompositing(); + virtual void toggleCompositing() {} /** - * Re-initializes the Compositor completely. - * Connected to the D-Bus signal org.kde.KWin /KWin reinitCompositing + * Notifies the compositor that SwapBuffers() is about to be called. + * Rendering of the next frame will be deferred until bufferSwapComplete() + * is called. **/ - void slotReinitialize(); + void aboutToSwapBuffers(); /** * Schedules a new repaint if no repaint is currently scheduled. **/ void scheduleRepaint(); - void updateCompositeBlocking(); - void updateCompositeBlocking(KWin::Client* c); - /** - * Notifies the compositor that SwapBuffers() is about to be called. - * Rendering of the next frame will be deferred until bufferSwapComplete() - * is called. + * Re-initializes the Compositor completely. + * Connected to the D-Bus signal org.kde.KWin /KWin reinitCompositing **/ - void aboutToSwapBuffers(); - + virtual void reinitialize(); + void addRepaintFull(); /** * Notifies the compositor that a pending buffer swap has completed. **/ void bufferSwapComplete(); + static Compositor* self(); + static Compositor *s_compositor; + Q_SIGNALS: void compositingToggled(bool active); void aboutToDestroy(); void aboutToToggleCompositing(); void sceneCreated(); void bufferSwapCompleted(); protected: + explicit Compositor(QObject *parent); + + Q_INVOKABLE virtual bool setup(); + + virtual void performCompositing(); void timerEvent(QTimerEvent *te); + /** + * Continues the startup after Scene And Workspace are created + **/ + virtual bool startupWithWorkspace(); -private Q_SLOTS: - void setup(); /** * Called from setupCompositing() when the CompositingPrefs are ready. **/ void slotCompositingOptionsInitialized(); + virtual void slotConfigChanged(); void finish(); + + bool m_finishing; // finish() sets this variable while shutting down + bool m_starting; // start() sets this variable while starting + Scene *m_scene; + CompositorSelectionOwner *m_selectionOwner; + /** * Restarts the Compositor if running. * That is the Compositor will be stopped and started again. **/ void restart(); - void performCompositing(); - void slotConfigChanged(); - void releaseCompositorSelection(); - void deleteUnusedSupportProperties(); private: + void deleteUnusedSupportProperties(); + void claimCompositorSelection(); void setCompositeTimer(); bool windowRepaintsPending() const; - /** - * Continues the startup after Scene And Workspace are created - **/ - void startupWithWorkspace(); void setupX11Support(); - /** - * Whether the Compositor is currently suspended, 8 bits encoding the reason - **/ - SuspendReasons m_suspended; - QBasicTimer compositeTimer; - CompositorSelectionOwner *m_selectionOwner; - QTimer m_releaseSelectionTimer; QList m_unusedSupportProperties; QTimer m_unusedSupportPropertyTimer; qint64 vBlankInterval, fpsInterval; - int m_xrrRefreshRate; QRegion repaints_region; - QTimer compositeResetTimer; // for compressing composite resets - bool m_finishing; // finish() sets this variable while shutting down - bool m_starting; // start() sets this variable while starting qint64 m_timeSinceLastVBlank; - Scene *m_scene; bool m_bufferSwapPending; bool m_composeAtSwapCompletion; int m_framesToTestForSafety = 3; QElapsedTimer m_monotonicClock; +}; + +class KWIN_EXPORT WaylandCompositor : public Compositor +{ + Q_OBJECT +public: + explicit WaylandCompositor(QObject *parent = nullptr); + ~WaylandCompositor() override = default; + + + static WaylandCompositor* create(QObject *parent = nullptr); + +private: + bool setup() override; - KWIN_SINGLETON_VARIABLE(Compositor, s_compositor) }; -} -# endif // KWIN_COMPOSITE_H +class KWIN_EXPORT X11Compositor : public Compositor +{ + Q_OBJECT +public: + enum SuspendReason { NoReasonSuspend = 0, + UserSuspend = 1 << 0, + BlockRuleSuspend = 1 << 1, + ScriptSuspend = 1 << 2, + AllReasonSuspend = 0xff + }; + Q_DECLARE_FLAGS(SuspendReasons, SuspendReason) + + explicit X11Compositor(QObject *parent = nullptr); + ~X11Compositor() override = default; + + static X11Compositor* create(QObject *parent = nullptr); + + bool startupWithWorkspace() override; + void performCompositing() override; + + /** + * @brief Suspends the Compositor if it is currently active. + * + * Note: it is possible that the Compositor is not able to suspend. Use isActive to check + * whether the Compositor has been suspended. + * + * @return void + * @see resume + * @see isActive + **/ + void suspend(X11Compositor::SuspendReason reason); + /** + * @brief Resumes the Compositor if it is currently suspended. + * + * Note: it is possible that the Compositor cannot be resumed, that is there might be Clients + * blocking the usage of Compositing or the Scene might be broken. Use isActive to check + * whether the Compositor has been resumed. Also check isCompositingPossible and + * isOpenGLBroken. + * + * Note: The starting of the Compositor can require some time and is partially done threaded. + * After this method returns the setup may not have been completed. + * + * @return void + * @see suspend + * @see isActive + * @see isCompositingPossible + * @see isOpenGLBroken + **/ + void resume(X11Compositor::SuspendReason reason); + + void toggleCompositing() override; + + void updateCompositeBlocking(); + void updateClientCompositeBlocking(KWin::Client* c); + + int xrrRefreshRate() const { + return m_xrrRefreshRate; + } + + bool checkForOverlayWindow(WId w) const override; + + void setCompositeResetTimer(int msecs); + + void reinitialize() override; + +private: + bool setup() override; + void slotConfigChanged() override; + bool isOverlayWindowVisible() const; + void releaseCompositorSelection(); + + /** + * Whether the Compositor is currently suspended, 8 bits encoding the reason + **/ + SuspendReasons m_suspended; + int m_xrrRefreshRate; + QTimer m_compositeResetTimer; // for compressing composite resets + QTimer m_releaseSelectionTimer; +}; + +} diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -2,7 +2,8 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 2006 Lubos Lunak +Copyright © 2006 Lubos Lunak +Copyright © 2019 Roman Gilg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -62,12 +63,34 @@ #include -Q_DECLARE_METATYPE(KWin::Compositor::SuspendReason) +Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason) namespace KWin { -extern int currentRefreshRate(); +static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } +static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } + +Compositor *Compositor::s_compositor = nullptr; +Compositor* Compositor::self() +{ + return s_compositor; +} + +WaylandCompositor* WaylandCompositor::create(QObject *parent) +{ + Q_ASSERT(!s_compositor); + auto *compositor = new WaylandCompositor(parent); + s_compositor = compositor; + return compositor; +} +X11Compositor* X11Compositor::create(QObject *parent) +{ + Q_ASSERT(!s_compositor); + auto *compositor = new X11Compositor(parent); + s_compositor = compositor; + return compositor; +} class CompositorSelectionOwner : public KSelectionOwner { @@ -90,49 +113,40 @@ bool m_owning; }; -KWIN_SINGLETON_FACTORY_VARIABLE(Compositor, s_compositor) - -static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } -static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } - -Compositor::Compositor(QObject* workspace) - : QObject(workspace) - , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) +Compositor::Compositor(QObject* parent) + : QObject(parent) + , m_finishing(false) + , m_starting(false) + , m_scene(NULL) , m_selectionOwner(NULL) , vBlankInterval(0) , fpsInterval(0) - , m_xrrRefreshRate(0) - , m_finishing(false) - , m_starting(false) , m_timeSinceLastVBlank(0) - , m_scene(NULL) , m_bufferSwapPending(false) , m_composeAtSwapCompletion(false) { - qRegisterMetaType("Compositor::SuspendReason"); - connect(&compositeResetTimer, SIGNAL(timeout()), SLOT(restart())); connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged); - compositeResetTimer.setSingleShot(true); m_monotonicClock.start(); // 2 sec which should be enough to restart the compositor static const int compositorLostMessageDelay = 2000; - m_releaseSelectionTimer.setSingleShot(true); - m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); - connect(&m_releaseSelectionTimer, SIGNAL(timeout()), SLOT(releaseCompositorSelection())); - m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); m_unusedSupportPropertyTimer.setSingleShot(true); - connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties())); + connect(&m_unusedSupportPropertyTimer, &QTimer::timeout, this, &Compositor::deleteUnusedSupportProperties); // delay the call to setup by one event cycle // The ctor of this class is invoked from the Workspace ctor, that means before // Workspace is completely constructed, so calling Workspace::self() would result // in undefined behavior. This is fixed by using a delayed invocation. + // + // TODO: This is a hack anyway but in Wayland case this explanation is also not correct, + // since the Compositor is started from Application directly. Still it does not + // work here either. So the TODO is to clean this up in general. if (kwinApp()->platform()->isReady()) { QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } + connect(kwinApp()->platform(), &Platform::readyChanged, this, [this] (bool ready) { if (ready) { @@ -166,37 +180,16 @@ } -void Compositor::setup() +bool Compositor::setup() { if (kwinApp()->isTerminating()) { // don't setup while KWin is terminating. An event to restart might be lingering in the event queue due to graphics reset - return; - } - if (hasScene()) - return; - if (m_suspended) { - QStringList reasons; - if (m_suspended & UserSuspend) { - reasons << QStringLiteral("Disabled by User"); - } - if (m_suspended & BlockRuleSuspend) { - reasons << QStringLiteral("Disabled by Window"); - } - if (m_suspended & ScriptSuspend) { - reasons << QStringLiteral("Disabled by Script"); - } - qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; - return; - } else if (!kwinApp()->platform()->compositingPossible()) { - qCCritical(KWIN_CORE) << "Compositing is not possible"; - return; + return false; } - m_starting = true; - - if (!options->isCompositingInitialized()) { - options->reloadCompositingSettings(true); + if (hasScene()) { + return false; } - slotCompositingOptionsInitialized(); + return true; } extern int screen_number; // main.cpp @@ -319,29 +312,33 @@ xcb_composite_redirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); } -void Compositor::startupWithWorkspace() +bool Compositor::startupWithWorkspace() { if (!m_starting) { - return; + return false; } connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::setupX11Support, Qt::UniqueConnection); Workspace::self()->markXStackingOrderAsDirty(); Q_ASSERT(m_scene); connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); setupX11Support(); - m_xrrRefreshRate = KWin::currentRefreshRate(); + fpsInterval = options->maxFpsInterval(); if (m_scene->syncsToVBlank()) { // if we do vsync, set the fps to the next multiple of the vblank rate - vBlankInterval = milliToNano(1000) / m_xrrRefreshRate; + vBlankInterval = milliToNano(1000) / Options::currentRefreshRate(); fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); } else vBlankInterval = milliToNano(1); // no sync - DO NOT set "0", would cause div-by-zero segfaults. m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" - we don't have even a slight idea when the first vsync will occur + scheduleRepaint(); kwinApp()->platform()->createEffectsHandler(this, m_scene); // sets also the 'effects' pointer + connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); - connect(effects, SIGNAL(screenGeometryChanged(QSize)), SLOT(addRepaintFull())); + connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull); + addRepaintFull(); + foreach (Client * c, Workspace::self()->clientList()) { c->setupCompositing(); c->getShadow(); @@ -368,12 +365,10 @@ emit compositingToggled(true); m_starting = false; - if (m_releaseSelectionTimer.isActive()) { - m_releaseSelectionTimer.stop(); - } // render at least once performCompositing(); + return true; } void Compositor::scheduleRepaint() @@ -387,7 +382,6 @@ if (!hasScene()) return; m_finishing = true; - m_releaseSelectionTimer.start(); emit aboutToToggleCompositing(); @@ -439,30 +433,6 @@ emit compositingToggled(false); } -void Compositor::releaseCompositorSelection() -{ - if (hasScene() && !m_finishing) { - // compositor is up and running again, no need to release the selection - return; - } - if (m_starting) { - // currently still starting the compositor, it might fail, so restart the timer to test again - m_releaseSelectionTimer.start(); - return; - } - - if (m_finishing) { - // still shutting down, a restart might follow, so restart the timer to test again - m_releaseSelectionTimer.start(); - return; - } - qCDebug(KWIN_CORE) << "Releasing compositor selection"; - if (m_selectionOwner) { - m_selectionOwner->setOwning(false); - m_selectionOwner->release(); - } -} - void Compositor::keepSupportProperty(xcb_atom_t atom) { m_unusedSupportProperties.removeAll(atom); @@ -496,103 +466,26 @@ void Compositor::slotConfigChanged() { - if (!m_suspended) { - setup(); - if (effects) // setupCompositing() may fail - effects->reconfigure(); - addRepaintFull(); - } else - finish(); + setup(); + if (effects) // setupCompositing() may fail + effects->reconfigure(); + addRepaintFull(); } -void Compositor::slotReinitialize() +void Compositor::reinitialize() { // Reparse config. Config options will be reloaded by setup() kwinApp()->config()->reparseConfiguration(); // Restart compositing finish(); - // resume compositing if suspended - m_suspended = NoReasonSuspend; - options->setCompositingInitialized(false); setup(); if (effects) { // setup() may fail effects->reconfigure(); } } -// for the shortcut -void Compositor::slotToggleCompositing() -{ - if (kwinApp()->platform()->requiresCompositing()) { - // we are not allowed to turn on/off compositing - return; - } - if (m_suspended) { // direct user call; clear all bits - resume(AllReasonSuspend); - } else { // but only set the user one (sufficient to suspend) - suspend(UserSuspend); - } -} - -void Compositor::updateCompositeBlocking() -{ - updateCompositeBlocking(NULL); -} - -void Compositor::updateCompositeBlocking(Client *c) -{ - if (kwinApp()->platform()->requiresCompositing()) { - return; - } - if (c) { // if c == 0 we just check if we can resume - if (c->isBlockingCompositing()) { - if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain! - QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend)); - } - } - else if (m_suspended & BlockRuleSuspend) { // lost a client and we're blocked - can we resume? - bool resume = true; - for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { - if ((*it)->isBlockingCompositing()) { - resume = false; - break; - } - } - if (resume) { // do NOT attempt to call suspend(false); from within the eventchain! - QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend)); - } - } -} - -void Compositor::suspend(Compositor::SuspendReason reason) -{ - if (kwinApp()->platform()->requiresCompositing()) { - return; - } - Q_ASSERT(reason != NoReasonSuspend); - m_suspended |= reason; - if (reason & KWin::Compositor::ScriptSuspend) { - // when disabled show a shortcut how the user can get back compositing - const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild(QStringLiteral("Suspend Compositing"))); - if (!shortcuts.isEmpty()) { - // display notification only if there is the shortcut - const QString message = i18n("Desktop effects have been suspended by another application.
" - "You can resume using the '%1' shortcut.", shortcuts.first().toString(QKeySequence::NativeText)); - KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); - } - } - finish(); -} - -void Compositor::resume(Compositor::SuspendReason reason) -{ - Q_ASSERT(reason != NoReasonSuspend); - m_suspended &= ~reason; - setup(); // signal "toggled" is eventually emitted from within setup -} - void Compositor::restart() { if (hasScene()) { @@ -664,9 +557,6 @@ void Compositor::performCompositing() { - if (m_scene->usesOverlayWindow() && !isOverlayWindowVisible()) - return; // nothing is visible anyway - // If a buffer swap is still pending, we return to the event loop and // continue processing events until the swap has completed. if (m_bufferSwapPending) { @@ -827,11 +717,6 @@ return false; } -void Compositor::setCompositeResetTimer(int msecs) -{ - compositeResetTimer.start(msecs); -} - void Compositor::setCompositeTimer() { if (!hasScene()) // should not really happen, but there may be e.g. some damage events still pending @@ -905,7 +790,131 @@ return !m_finishing && hasScene(); } -bool Compositor::checkForOverlayWindow(WId w) const +WaylandCompositor::WaylandCompositor(QObject *parent) + : Compositor(parent) +{ +} + +bool WaylandCompositor::setup() +{ + if (!Compositor::setup()) { + return false; + } + + m_starting = true; + + options->reloadCompositingSettings(true); + slotCompositingOptionsInitialized(); + return true; +} + +X11Compositor::X11Compositor(QObject *parent) + : Compositor(parent) + , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) + , m_xrrRefreshRate(0) +{ + qRegisterMetaType("X11Compositor::SuspendReason"); + + // 2 sec which should be enough to restart the compositor + static const int compositorLostMessageDelay = 2000; + + m_releaseSelectionTimer.setSingleShot(true); + m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); + connect(&m_releaseSelectionTimer, &QTimer::timeout, this, &X11Compositor::releaseCompositorSelection); + + connect(&m_compositeResetTimer, &QTimer::timeout, this, &X11Compositor::restart); + m_compositeResetTimer.setSingleShot(true); + + connect(this, &X11Compositor::compositingToggled, this, [this](bool toggled) { + if (toggled) { + if (m_releaseSelectionTimer.isActive()) { + m_releaseSelectionTimer.stop(); + } + } else { + // TODO: this can lead to a crash on destroy when compositingToggled + // is emitted from ~Compositor + m_releaseSelectionTimer.start(); + } + }); +} + +void X11Compositor::suspend(X11Compositor::SuspendReason reason) +{ + Q_ASSERT(reason != NoReasonSuspend); + m_suspended |= reason; + if (reason & ScriptSuspend) { + // when disabled show a shortcut how the user can get back compositing + const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild(QStringLiteral("Suspend Compositing"))); + if (!shortcuts.isEmpty()) { + // display notification only if there is the shortcut + const QString message = i18n("Desktop effects have been suspended by another application.
" + "You can resume using the '%1' shortcut.", shortcuts.first().toString(QKeySequence::NativeText)); + KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); + } + } + finish(); +} + +void X11Compositor::reinitialize() +{ + // resume compositing if suspended + m_suspended = NoReasonSuspend; + Compositor::reinitialize(); +} + +void X11Compositor::resume(X11Compositor::SuspendReason reason) +{ + Q_ASSERT(reason != NoReasonSuspend); + m_suspended &= ~reason; + setup(); // signal "toggled" is eventually emitted from within setup +} + +void X11Compositor::toggleCompositing() +{ + if (m_suspended) { // direct user call; clear all bits + resume(AllReasonSuspend); + } else { // but only set the user one (sufficient to suspend) + suspend(UserSuspend); + } +} + +void X11Compositor::updateCompositeBlocking() +{ + updateClientCompositeBlocking(NULL); +} + +void X11Compositor::updateClientCompositeBlocking(Client *c) +{ + if (c) { // if c == 0 we just check if we can resume + if (c->isBlockingCompositing()) { + if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain! + QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, BlockRuleSuspend)); + } + } + else if (m_suspended & BlockRuleSuspend) { // lost a client and we're blocked - can we resume? + bool resume = true; + for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { + if ((*it)->isBlockingCompositing()) { + resume = false; + break; + } + } + if (resume) { // do NOT attempt to call suspend(false); from within the eventchain! + QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, BlockRuleSuspend)); + } + } +} + +bool X11Compositor::startupWithWorkspace() +{ + if (!Compositor::startupWithWorkspace()) { + return false; + } + m_xrrRefreshRate = Options::currentRefreshRate(); + return true; +} + +bool X11Compositor::checkForOverlayWindow(WId w) const { if (!hasScene()) { // no scene, so it cannot be the overlay window @@ -919,7 +928,52 @@ return w == m_scene->overlayWindow()->window(); } -bool Compositor::isOverlayWindowVisible() const +bool X11Compositor::setup() +{ + if (!Compositor::setup()) { + return false; + } + + if (m_suspended) { + QStringList reasons; + if (m_suspended & UserSuspend) { + reasons << QStringLiteral("Disabled by User"); + } + if (m_suspended & BlockRuleSuspend) { + reasons << QStringLiteral("Disabled by Window"); + } + if (m_suspended & ScriptSuspend) { + reasons << QStringLiteral("Disabled by Script"); + } + qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; + return false; + + } else if (!kwinApp()->platform()->compositingPossible()) { + qCCritical(KWIN_CORE) << "Compositing is not possible"; + return false; + } + m_starting = true; + + options->reloadCompositingSettings(true); + slotCompositingOptionsInitialized(); + return true; +} + +void X11Compositor::slotConfigChanged() +{ + if (m_suspended) { + finish(); + } else { + Compositor::slotConfigChanged(); + } +} + +void X11Compositor::setCompositeResetTimer(int msecs) +{ + m_compositeResetTimer.start(msecs); +} + +bool X11Compositor::isOverlayWindowVisible() const { if (!hasScene()) { return false; @@ -930,7 +984,39 @@ return m_scene->overlayWindow()->isVisible(); } -} // namespace +void X11Compositor::performCompositing() +{ + if (m_scene->usesOverlayWindow() && !isOverlayWindowVisible()) { + return; // nothing is visible anyway + } + Compositor::performCompositing(); +} + +void X11Compositor::releaseCompositorSelection() +{ + if (hasScene() && !m_finishing) { + // compositor is up and running again, no need to release the selection + return; + } + if (m_starting) { + // currently still starting the compositor, it might fail, so restart the timer to test again + m_releaseSelectionTimer.start(); + return; + } + + if (m_finishing) { + // still shutting down, a restart might follow, so restart the timer to test again + m_releaseSelectionTimer.start(); + return; + } + qCDebug(KWIN_CORE) << "Releasing compositor selection"; + if (m_selectionOwner) { + m_selectionOwner->setOwning(false); + m_selectionOwner->release(); + } +} + +} // included for CompositorSelectionOwner #include "composite.moc" diff --git a/dbusinterface.h b/dbusinterface.h --- a/dbusinterface.h +++ b/dbusinterface.h @@ -166,6 +166,8 @@ **/ void resume(); + void reinitialize(); + Q_SIGNALS: void compositingToggled(bool active); diff --git a/dbusinterface.cpp b/dbusinterface.cpp --- a/dbusinterface.cpp +++ b/dbusinterface.cpp @@ -254,10 +254,17 @@ { connect(m_compositor, &Compositor::compositingToggled, this, &CompositorDBusInterface::compositingToggled); new CompositingAdaptor(this); + + // TODO: this does not seem to work even without the change. no interface available in qdbusviewer QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Compositor"), this); dbus.connect(QString(), QStringLiteral("/Compositor"), QStringLiteral("org.kde.kwin.Compositing"), - QStringLiteral("reinit"), m_compositor, SLOT(slotReinitialize())); + QStringLiteral("reinit"), this, SLOT(reinitialize())); +} + +void CompositorDBusInterface::reinitialize() +{ + m_compositor->reinitialize(); } QString CompositorDBusInterface::compositingNotPossibleReason() const @@ -309,12 +316,16 @@ void CompositorDBusInterface::resume() { - m_compositor->resume(Compositor::ScriptSuspend); + if (kwinApp()->operationMode() == Application::OperationModeX11) { + static_cast(m_compositor)->resume(X11Compositor::ScriptSuspend); + } } void CompositorDBusInterface::suspend() { - m_compositor->suspend(Compositor::ScriptSuspend); + if (kwinApp()->operationMode() == Application::OperationModeX11) { + static_cast(m_compositor)->suspend(X11Compositor::ScriptSuspend); + } } QStringList CompositorDBusInterface::supportedOpenGLPlatformInterfaces() const diff --git a/kwinbindings.cpp b/kwinbindings.cpp --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -159,7 +159,7 @@ DEF(I18N_NOOP("Switch to Previous Screen"), 0, slotSwitchToPrevScreen); DEF(I18N_NOOP("Kill Window"), Qt::CTRL + Qt::ALT + Qt::Key_Escape, slotKillWindow); -DEF6(I18N_NOOP("Suspend Compositing"), Qt::SHIFT + Qt::ALT + Qt::Key_F12, Compositor::self(), Compositor::slotToggleCompositing); +DEF6(I18N_NOOP("Suspend Compositing"), Qt::SHIFT + Qt::ALT + Qt::Key_F12, Compositor::self(), Compositor::toggleCompositing); DEF6(I18N_NOOP("Invert Screen Colors"), 0, kwinApp()->platform(), Platform::invertScreen); #undef DEF diff --git a/main.h b/main.h --- a/main.h +++ b/main.h @@ -212,7 +212,6 @@ void createWorkspace(); void createAtoms(); void createOptions(); - void createCompositor(); void setupEventFilters(); void destroyWorkspace(); void destroyCompositor(); diff --git a/main.cpp b/main.cpp --- a/main.cpp +++ b/main.cpp @@ -310,11 +310,6 @@ options = new Options; } -void Application::createCompositor() -{ - Compositor::create(this); -} - void Application::setupEventFilters() { installNativeEventFilter(m_eventFilter.data()); diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -180,7 +180,7 @@ { disconnect(kwinApp()->platform(), &Platform::screensQueried, this, &ApplicationWayland::continueStartupWithScreens); createScreens(); - createCompositor(); + WaylandCompositor::create(kwinApp()); connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithScene); } diff --git a/options.h b/options.h --- a/options.h +++ b/options.h @@ -167,7 +167,6 @@ Q_PROPERTY(bool autogroupInForeground READ isAutogroupInForeground WRITE setAutogroupInForeground NOTIFY autogroupInForegroundChanged) Q_PROPERTY(int compositingMode READ compositingMode WRITE setCompositingMode NOTIFY compositingModeChanged) Q_PROPERTY(bool useCompositing READ isUseCompositing WRITE setUseCompositing NOTIFY useCompositingChanged) - Q_PROPERTY(bool compositingInitialized READ isCompositingInitialized WRITE setCompositingInitialized NOTIFY compositingInitializedChanged) Q_PROPERTY(int hiddenPreviews READ hiddenPreviews WRITE setHiddenPreviews NOTIFY hiddenPreviewsChanged) /** * 0 = no, 1 = yes when transformed, @@ -558,9 +557,6 @@ } // Separate to mode so the user can toggle bool isUseCompositing() const; - bool isCompositingInitialized() const { - return m_compositingInitialized; - } // General preferences HiddenPreviews hiddenPreviews() const { @@ -662,7 +658,6 @@ void setAutogroupInForeground(bool autogroupInForeground); void setCompositingMode(int compositingMode); void setUseCompositing(bool useCompositing); - void setCompositingInitialized(bool compositingInitialized); void setHiddenPreviews(int hiddenPreviews); void setGlSmoothScale(int glSmoothScale); void setXrenderSmoothScale(bool xrenderSmoothScale); @@ -746,9 +741,6 @@ static bool defaultUseCompositing() { return true; } - static bool defaultCompositingInitialized() { - return false; - } static HiddenPreviews defaultHiddenPreviews() { return HiddenPreviewsShown; } @@ -853,7 +845,6 @@ void autogroupInForegroundChanged(); void compositingModeChanged(); void useCompositingChanged(); - void compositingInitializedChanged(); void hiddenPreviewsChanged(); void glSmoothScaleChanged(); void xrenderSmoothScaleChanged(); @@ -898,7 +889,6 @@ CompositingType m_compositingMode; bool m_useCompositing; - bool m_compositingInitialized; HiddenPreviews m_hiddenPreviews; int m_glSmoothScale; bool m_xrenderSmoothScale; diff --git a/options.cpp b/options.cpp --- a/options.cpp +++ b/options.cpp @@ -41,11 +41,6 @@ #ifndef KCMRULES -int currentRefreshRate() -{ - return Options::currentRefreshRate(); -} - int Options::currentRefreshRate() { int rate = -1; @@ -112,7 +107,6 @@ , m_autogroupInForeground(false) , m_compositingMode(Options::defaultCompositingMode()) , m_useCompositing(Options::defaultUseCompositing()) - , m_compositingInitialized(Options::defaultCompositingInitialized()) , m_hiddenPreviews(Options::defaultHiddenPreviews()) , m_glSmoothScale(Options::defaultGlSmoothScale()) , m_xrenderSmoothScale(Options::defaultXrenderSmoothScale()) @@ -615,15 +609,6 @@ emit useCompositingChanged(); } -void Options::setCompositingInitialized(bool compositingInitialized) -{ - if (m_compositingInitialized == compositingInitialized) { - return; - } - m_compositingInitialized = compositingInitialized; - emit compositingInitializedChanged(); -} - void Options::setHiddenPreviews(int hiddenPreviews) { if (m_hiddenPreviews == static_cast(hiddenPreviews)) { @@ -787,7 +772,6 @@ // KDE4 this probably needs to be done manually in clients // Driver-specific config detection - setCompositingInitialized(false); reloadCompositingSettings(); emit configChanged(); @@ -958,7 +942,6 @@ syncFromKcfgc(); // from now on we've an initial setup and don't have to reload settings on compositing activation // see Workspace::setupCompositing(), composite.cpp - setCompositingInitialized(true); // Compositing settings KConfigGroup config(m_settings->config(), "Compositing"); diff --git a/plugins/platforms/x11/standalone/screens_xrandr.cpp b/plugins/platforms/x11/standalone/screens_xrandr.cpp --- a/plugins/platforms/x11/standalone/screens_xrandr.cpp +++ b/plugins/platforms/x11/standalone/screens_xrandr.cpp @@ -80,8 +80,9 @@ // desktopResized() should take care of when the size or // shape of the desktop has changed, but we also want to // catch refresh rate changes - if (Compositor::self()->xrrRefreshRate() != Options::currentRefreshRate()) - Compositor::self()->setCompositeResetTimer(0); + auto *compositor = static_cast(Compositor::self()); + if (compositor->xrrRefreshRate() != Options::currentRefreshRate()) + compositor->setCompositeResetTimer(0); } #endif diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -84,9 +84,6 @@ namespace KWin { -extern int currentRefreshRate(); - - /** * SyncObject represents a fence used to synchronize operations in * the kwin command stream with operations in the X command stream. @@ -823,11 +820,15 @@ } bool SceneOpenGL::viewportLimitsMatched(const QSize &size) const { + if (kwinApp()->operationMode() != Application::OperationModeX11) { + // TODO: On Wayland we can't suspend. Find a solution that works here as well! + return true; + } GLint limit[2]; glGetIntegerv(GL_MAX_VIEWPORT_DIMS, limit); if (limit[0] < size.width() || limit[1] < size.height()) { - QMetaObject::invokeMethod(Compositor::self(), "suspend", - Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, Compositor::AllReasonSuspend)); + QMetaObject::invokeMethod(static_cast(Compositor::self()), "suspend", + Qt::QueuedConnection, Q_ARG(X11Compositor::SuspendReason, X11Compositor::AllReasonSuspend)); return false; } return true; diff --git a/thumbnailitem.cpp b/thumbnailitem.cpp --- a/thumbnailitem.cpp +++ b/thumbnailitem.cpp @@ -41,7 +41,6 @@ , m_saturation(1.0) , m_clipToItem() { - Q_ASSERT(Compositor::isCreated()); connect(Compositor::self(), SIGNAL(compositingToggled(bool)), SLOT(compositingToggled())); compositingToggled(); QTimer::singleShot(0, this, SLOT(init())); diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -176,7 +176,8 @@ if (Compositor::self()) { m_compositor = Compositor::self(); } else { - m_compositor = Compositor::create(this); + Q_ASSERT(kwinApp()->operationMode() == Application::OperationMode::OperationModeX11); + m_compositor = X11Compositor::create(this); } connect(this, &Workspace::currentDesktopChanged, m_compositor, &Compositor::addRepaintFull); connect(m_compositor, &QObject::destroyed, this, [this] { m_compositor = nullptr; }); @@ -586,7 +587,10 @@ StackingUpdatesBlocker blocker(this); Client* c = new Client(); setupClientConnections(c); - connect(c, SIGNAL(blockingCompositingChanged(KWin::Client*)), m_compositor, SLOT(updateCompositeBlocking(KWin::Client*))); + + if (kwinApp()->operationMode() == Application::OperationModeX11) { + connect(c, &Client::blockingCompositingChanged, static_cast(m_compositor), &X11Compositor::updateClientCompositeBlocking); + } connect(c, SIGNAL(clientFullScreenSet(KWin::Client*,bool,bool)), ScreenEdges::self(), SIGNAL(checkBlocking())); if (!c->manage(w, is_mapped)) { Client::deleteClient(c); @@ -605,7 +609,7 @@ Unmanaged::deleteUnmanaged(c); return NULL; } - connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint())); + connect(c, &Unmanaged::needsRepaint, m_compositor, &Compositor::scheduleRepaint); addUnmanaged(c); emit unmanagedAdded(c); return c; @@ -737,7 +741,7 @@ stacking_order.append(c); } markXStackingOrderAsDirty(); - connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint())); + connect(c, &Deleted::needsRepaint, m_compositor, &Compositor::scheduleRepaint); } void Workspace::removeDeleted(Deleted* c) @@ -748,8 +752,8 @@ unconstrained_stacking_order.removeAll(c); stacking_order.removeAll(c); markXStackingOrderAsDirty(); - if (c->wasClient() && m_compositor) { - m_compositor->updateCompositeBlocking(); + if (c->wasClient() && m_compositor && kwinApp()->operationMode() == Application::OperationModeX11) { + static_cast(m_compositor)->updateCompositeBlocking(); } }