From fb0bcff1c84eae5cf8d2a7f7a922c6ee1f85754e Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 3 Dec 2020 21:47:42 +0200 Subject: [PATCH] plugins/qpa: Improve handling of output addition and removal With this change, the QPA will try to avoid re-creating all platform screens if a single output has been connected or disconnected. --- main.cpp | 1 + main.h | 1 + plugins/qpa/integration.cpp | 67 ++++++++++++++++++++++++------------- plugins/qpa/integration.h | 14 +++++--- plugins/qpa/screen.cpp | 59 ++++++++++++++++++++++++-------- plugins/qpa/screen.h | 21 ++++++++++-- 6 files changed, 119 insertions(+), 44 deletions(-) diff --git a/main.cpp b/main.cpp index 403fac3ae9..0390925f2b 100644 --- a/main.cpp +++ b/main.cpp @@ -450,6 +450,7 @@ void Application::initPlatform(const KPluginMetaData &plugin) } } } + emit platformCreated(); } } diff --git a/main.h b/main.h index e2a9c562de..6a535763bf 100644 --- a/main.h +++ b/main.h @@ -197,6 +197,7 @@ Q_SIGNALS: void x11ConnectionAboutToBeDestroyed(); void workspaceCreated(); void screensCreated(); + void platformCreated(); void virtualTerminalCreated(); void started(); diff --git a/plugins/qpa/integration.cpp b/plugins/qpa/integration.cpp index ace782a620..b3465d73ed 100644 --- a/plugins/qpa/integration.cpp +++ b/plugins/qpa/integration.cpp @@ -8,6 +8,7 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "integration.h" +#include "abstract_output.h" #include "backingstore.h" #include "eglplatformcontext.h" #include "logging.h" @@ -46,6 +47,9 @@ Integration::~Integration() for (QPlatformScreen *platformScreen : m_screens) { QWindowSystemInterface::handleScreenRemoved(platformScreen); } + if (m_dummyScreen) { + QWindowSystemInterface::handleScreenRemoved(m_dummyScreen); + } } bool Integration::hasCapability(Capability cap) const @@ -71,16 +75,13 @@ bool Integration::hasCapability(Capability cap) const void Integration::initialize() { - connect(kwinApp(), &Application::screensCreated, this, - [this] { - connect(screens(), &Screens::changed, this, &Integration::initScreens); - initScreens(); - } - ); + // The QPA is initialized before the platform plugin is loaded. + connect(kwinApp(), &Application::platformCreated, this, &Integration::handlePlatformCreated); + QPlatformIntegration::initialize(); - auto dummyScreen = new Screen(-1); - QWindowSystemInterface::handleScreenAdded(dummyScreen); - m_screens << dummyScreen; + + m_dummyScreen = new PlaceholderScreen(); + QWindowSystemInterface::handleScreenAdded(m_dummyScreen); } QAbstractEventDispatcher *Integration::createEventDispatcher() const @@ -135,24 +136,44 @@ QPlatformOpenGLContext *Integration::createPlatformOpenGLContext(QOpenGLContext return nullptr; } -void Integration::initScreens() +void Integration::handlePlatformCreated() { - QVector newScreens; - newScreens.reserve(qMax(screens()->count(), 1)); - for (int i = 0; i < screens()->count(); i++) { - auto screen = new Screen(i); - QWindowSystemInterface::handleScreenAdded(screen); - newScreens << screen; + connect(kwinApp()->platform(), &Platform::outputEnabled, + this, &Integration::handleOutputEnabled); + connect(kwinApp()->platform(), &Platform::outputDisabled, + this, &Integration::handleOutputDisabled); + + const QVector outputs = kwinApp()->platform()->enabledOutputs(); + for (AbstractOutput *output : outputs) { + handleOutputEnabled(output); } - if (newScreens.isEmpty()) { - auto dummyScreen = new Screen(-1); - QWindowSystemInterface::handleScreenAdded(dummyScreen); - newScreens << dummyScreen; +} + +void Integration::handleOutputEnabled(AbstractOutput *output) +{ + Screen *platformScreen = new Screen(output); + QWindowSystemInterface::handleScreenAdded(platformScreen); + + if (m_dummyScreen) { + QWindowSystemInterface::handleScreenRemoved(m_dummyScreen); + m_dummyScreen = nullptr; } - while (!m_screens.isEmpty()) { - QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast()); +} + +void Integration::handleOutputDisabled(AbstractOutput *output) +{ + Screen *platformScreen = m_screens.take(output); + if (!platformScreen) { + qCWarning(KWIN_QPA) << "Unknown output" << output; + return; } - m_screens = newScreens; + + if (m_screens.isEmpty()) { + m_dummyScreen = new PlaceholderScreen(); + QWindowSystemInterface::handleScreenAdded(m_dummyScreen); + } + + QWindowSystemInterface::handleScreenRemoved(platformScreen); } } diff --git a/plugins/qpa/integration.h b/plugins/qpa/integration.h index 03c37a15b4..5d31ab6e10 100644 --- a/plugins/qpa/integration.h +++ b/plugins/qpa/integration.h @@ -19,6 +19,9 @@ namespace KWin { + +class AbstractOutput; + namespace QPA { @@ -43,13 +46,16 @@ public: void initialize() override; -private: - void initScreens(); +private Q_SLOTS: + void handleOutputEnabled(AbstractOutput *output); + void handleOutputDisabled(AbstractOutput *output); + void handlePlatformCreated(); +private: QScopedPointer m_fontDb; QPlatformNativeInterface *m_nativeInterface; - Screen *m_dummyScreen = nullptr; - QVector m_screens; + QPlatformPlaceholderScreen *m_dummyScreen = nullptr; + QHash m_screens; }; } diff --git a/plugins/qpa/screen.cpp b/plugins/qpa/screen.cpp index 362bab7633..9dec7698fe 100644 --- a/plugins/qpa/screen.cpp +++ b/plugins/qpa/screen.cpp @@ -7,19 +7,27 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "screen.h" +#include "abstract_output.h" +#include "logging.h" #include "platformcursor.h" -#include "screens.h" + +#include namespace KWin { namespace QPA { -Screen::Screen(int screen) - : QPlatformScreen() - , m_screen(screen) +static int forcedDpi() +{ + return qEnvironmentVariableIsSet("QT_WAYLAND_FORCE_DPI") ? qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_DPI") : -1; +} + +Screen::Screen(AbstractOutput *output) + : m_output(output) , m_cursor(new PlatformCursor) { + connect(output, &AbstractOutput::geometryChanged, this, &Screen::handleGeometryChanged); } Screen::~Screen() = default; @@ -36,12 +44,20 @@ QImage::Format Screen::format() const QRect Screen::geometry() const { - return m_screen != -1 ? screens()->geometry(m_screen) : QRect(0, 0, 1, 1); + if (Q_UNLIKELY(!m_output)) { + qCCritical(KWIN_QPA) << "Attempting to get the geometry of a destroyed output"; + return QRect(); + } + return m_output->geometry(); } QSizeF Screen::physicalSize() const { - return m_screen != -1 ? screens()->physicalSize(m_screen) : QPlatformScreen::physicalSize(); + if (Q_UNLIKELY(!m_output)) { + qCCritical(KWIN_QPA) << "Attempting to get the physical size of a destroyed output"; + return QSizeF(); + } + return m_output->physicalSize(); } QPlatformCursor *Screen::cursor() const @@ -51,22 +67,37 @@ QPlatformCursor *Screen::cursor() const QDpi Screen::logicalDpi() const { - static int forceDpi = qEnvironmentVariableIsSet("QT_WAYLAND_FORCE_DPI") ? qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_DPI") : -1; - if (forceDpi > 0) { - return QDpi(forceDpi, forceDpi); - } - - return QDpi(96, 96); + const int dpi = forcedDpi(); + return dpi > 0 ? QDpi(dpi, dpi) : QDpi(96, 96); } qreal Screen::devicePixelRatio() const { - return m_screen != -1 ? screens()->scale(m_screen) : 1.0; + if (Q_UNLIKELY(!m_output)) { + qCCritical(KWIN_QPA) << "Attempting to get the scale factor of a destroyed output"; + return 1; + } + return m_output->scale(); } QString Screen::name() const { - return m_screen != -1 ? screens()->name(m_screen) : QString(); + if (Q_UNLIKELY(!m_output)) { + qCCritical(KWIN_QPA) << "Attempting to get the name of a destroyed output"; + return QString(); + } + return m_output->name(); +} + +void Screen::handleGeometryChanged() +{ + QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry()); +} + +QDpi PlaceholderScreen::logicalDpi() const +{ + const int dpi = forcedDpi(); + return dpi > 0 ? QDpi(dpi, dpi) : QDpi(96, 96); } } diff --git a/plugins/qpa/screen.h b/plugins/qpa/screen.h index 4a206aa7bc..dc177bd706 100644 --- a/plugins/qpa/screen.h +++ b/plugins/qpa/screen.h @@ -10,18 +10,24 @@ #define KWIN_QPA_SCREEN_H #include + +#include #include namespace KWin { +class AbstractOutput; + namespace QPA { class PlatformCursor; -class Screen : public QPlatformScreen +class Screen : public QObject, public QPlatformScreen { + Q_OBJECT + public: - explicit Screen(int screen); + explicit Screen(AbstractOutput *output); ~Screen() override; QString name() const override; @@ -33,11 +39,20 @@ public: QDpi logicalDpi() const override; qreal devicePixelRatio() const override; +private Q_SLOTS: + void handleGeometryChanged(); + private: - int m_screen; + QPointer m_output; QScopedPointer m_cursor; }; +class PlaceholderScreen : public QPlatformPlaceholderScreen +{ +public: + QDpi logicalDpi() const override; +}; + } }