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.
This commit is contained in:
Vlad Zahorodnii 2020-12-03 21:47:42 +02:00
parent 5943eea4c9
commit fb0bcff1c8
6 changed files with 119 additions and 44 deletions

View file

@ -450,6 +450,7 @@ void Application::initPlatform(const KPluginMetaData &plugin)
}
}
}
emit platformCreated();
}
}

1
main.h
View file

@ -197,6 +197,7 @@ Q_SIGNALS:
void x11ConnectionAboutToBeDestroyed();
void workspaceCreated();
void screensCreated();
void platformCreated();
void virtualTerminalCreated();
void started();

View file

@ -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<Screen*> 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<AbstractOutput *> 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);
}
}

View file

@ -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<QPlatformFontDatabase> m_fontDb;
QPlatformNativeInterface *m_nativeInterface;
Screen *m_dummyScreen = nullptr;
QVector<Screen*> m_screens;
QPlatformPlaceholderScreen *m_dummyScreen = nullptr;
QHash<AbstractOutput *, Screen *> m_screens;
};
}

View file

@ -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 <qpa/qwindowsysteminterface.h>
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);
}
}

View file

@ -10,18 +10,24 @@
#define KWIN_QPA_SCREEN_H
#include <qpa/qplatformscreen.h>
#include <QPointer>
#include <QScopedPointer>
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<AbstractOutput> m_output;
QScopedPointer<PlatformCursor> m_cursor;
};
class PlaceholderScreen : public QPlatformPlaceholderScreen
{
public:
QDpi logicalDpi() const override;
};
}
}