Client: Support running with no screens

[ChangeLog][QPA plugin] The QPA plugin now supports running with no screens
attached. This is handled by adding a fake screen when the last screen is
disconnected, similarly to what the other QPA plugins do.

Fixes: QTBUG-79111
Change-Id: I4a0e023ae784217dd030f0c62f12487fdff4825c
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2019-10-28 12:05:09 +01:00
parent 058dc12df8
commit b5f100975d
15 changed files with 73 additions and 36 deletions

View File

@ -50,10 +50,11 @@ QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zw
, m_shell(shell)
, m_window(window)
{
auto screen = static_cast<QWaylandScreen *>(m_window->screen());
auto *screen = m_window->waylandScreen();
auto *output = screen ? screen->output() : nullptr;
m_shell->present_surface(m_window->wlSurface(),
QtWayland::zwp_fullscreen_shell_v1::present_method_default,
screen->output());
output);
}
} // namespace QtWaylandClient

View File

@ -188,6 +188,18 @@ QWaylandDisplay::~QWaylandDisplay(void)
wl_display_disconnect(mDisplay);
}
void QWaylandDisplay::ensureScreen()
{
if (!mScreens.empty() || mPlaceholderScreen)
return; // There are real screens or we already have a fake one
qCInfo(lcQpaWayland) << "Creating a fake screen in order for Qt not to crash";
mPlaceholderScreen = new QPlatformPlaceholderScreen();
QWindowSystemInterface::handleScreenAdded(mPlaceholderScreen);
Q_ASSERT(!QGuiApplication::screens().empty());
}
void QWaylandDisplay::checkError() const
{
int ecode = wl_display_get_error(mDisplay);
@ -253,8 +265,7 @@ void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bo
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{
for (int i = 0; i < mScreens.size(); ++i) {
QWaylandScreen *screen = static_cast<QWaylandScreen *>(mScreens.at(i));
for (auto screen : qAsConst(mScreens)) {
if (screen->output() == output)
return screen;
}
@ -267,6 +278,11 @@ void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
return;
mScreens.append(screen);
QWindowSystemInterface::handleScreenAdded(screen);
if (mPlaceholderScreen) {
QWindowSystemInterface::handleScreenRemoved(mPlaceholderScreen);
// handleScreenRemoved deletes the platform screen
mPlaceholderScreen = nullptr;
}
}
void QWaylandDisplay::waitForScreens()
@ -362,6 +378,8 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
for (QWaylandScreen *screen : qAsConst(mScreens)) {
if (screen->outputId() == id) {
mScreens.removeOne(screen);
// If this is the last screen, we have to add a fake screen, or Qt will break.
ensureScreen();
QWindowSystemInterface::handleScreenRemoved(screen);
break;
}

View File

@ -76,6 +76,7 @@ QT_BEGIN_NAMESPACE
class QAbstractEventDispatcher;
class QSocketNotifier;
class QPlatformScreen;
class QPlatformPlaceholderScreen;
namespace QtWayland {
class qt_surface_extension;
@ -124,6 +125,8 @@ public:
#endif
QList<QWaylandScreen *> screens() const { return mScreens; }
QPlatformPlaceholderScreen *placeholderScreen() const { return mPlaceholderScreen; }
void ensureScreen();
QWaylandScreen *screenForOutput(struct wl_output *output) const;
void handleScreenInitialized(QWaylandScreen *screen);
@ -228,6 +231,7 @@ private:
QScopedPointer<QWaylandShm> mShm;
QList<QWaylandScreen *> mWaitingScreens;
QList<QWaylandScreen *> mScreens;
QPlatformPlaceholderScreen *mPlaceholderScreen = nullptr;
QList<QWaylandInputDevice *> mInputDevices;
QList<Listener> mRegistryListeners;
QWaylandIntegration *mWaylandIntegration = nullptr;

View File

@ -165,10 +165,10 @@ QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWindow *window) cons
#if QT_CONFIG(vulkan)
if (window->surfaceType() == QSurface::VulkanSurface)
return new QWaylandVulkanWindow(window);
return new QWaylandVulkanWindow(window, mDisplay.data());
#endif // QT_CONFIG(vulkan)
return new QWaylandShmWindow(window);
return new QWaylandShmWindow(window, mDisplay.data());
}
#if QT_CONFIG(opengl)
@ -182,7 +182,7 @@ QPlatformOpenGLContext *QWaylandIntegration::createPlatformOpenGLContext(QOpenGL
QPlatformBackingStore *QWaylandIntegration::createPlatformBackingStore(QWindow *window) const
{
return new QWaylandShmBackingStore(window);
return new QWaylandShmBackingStore(window, mDisplay.data());
}
QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const
@ -200,10 +200,8 @@ void QWaylandIntegration::initialize()
QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data());
QObject::connect(sn, SIGNAL(activated(int)), mDisplay.data(), SLOT(flushRequests()));
if (mDisplay->screens().isEmpty()) {
qWarning() << "Running on a compositor with no screens is not supported";
::exit(EXIT_FAILURE);
}
// Qt does not support running with no screens
mDisplay->ensureScreen();
}
QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const

View File

@ -165,11 +165,18 @@ QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const
{
QList<QPlatformScreen *> list;
const QList<QWaylandScreen*> screens = mWaylandDisplay->screens();
list.reserve(screens.count());
auto *placeholder = mWaylandDisplay->placeholderScreen();
list.reserve(screens.count() + (placeholder ? 1 : 0));
for (QWaylandScreen *screen : qAsConst(screens)) {
if (screen->screen())
list << screen;
}
if (placeholder)
list << placeholder;
return list;
}
@ -210,9 +217,11 @@ QPlatformCursor *QWaylandScreen::cursor() const
}
#endif // QT_CONFIG(cursor)
QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
QWaylandScreen *QWaylandScreen::waylandScreenFromWindow(QWindow *window)
{
QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window);
if (platformScreen->isPlaceholder())
return nullptr;
return static_cast<QWaylandScreen *>(platformScreen);
}

View File

@ -57,7 +57,6 @@
#include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/private/qwayland-xdg-output-unstable-v1.h>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {

View File

@ -151,9 +151,9 @@ QImage *QWaylandShmBuffer::imageInsideMargins(const QMargins &marginsIn)
}
QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window)
QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display)
: QPlatformBackingStore(window)
, mDisplay(QWaylandScreen::waylandScreenFromWindow(window)->display())
, mDisplay(display)
{
}

View File

@ -88,7 +88,7 @@ private:
class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBackingStore : public QPlatformBackingStore
{
public:
QWaylandShmBackingStore(QWindow *window);
QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display);
~QWaylandShmBackingStore() override;
QPaintDevice *paintDevice() override;

View File

@ -49,8 +49,8 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandShmWindow::QWaylandShmWindow(QWindow *window)
: QWaylandWindow(window)
QWaylandShmWindow::QWaylandShmWindow(QWindow *window, QWaylandDisplay *display)
: QWaylandWindow(window, display)
{
}

View File

@ -61,7 +61,7 @@ namespace QtWaylandClient {
class Q_WAYLAND_CLIENT_EXPORT QWaylandShmWindow : public QWaylandWindow
{
public:
QWaylandShmWindow(QWindow *window);
QWaylandShmWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandShmWindow() override;
WindowType windowType() const override;

View File

@ -72,8 +72,12 @@ QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
{
auto *screen = static_cast<QWaylandScreen *>(qScreen->handle());
if (m_screens.removeOne(screen))
auto *platformScreen = qScreen->handle();
if (platformScreen->isPlaceholder())
return;
auto *waylandScreen = static_cast<QWaylandScreen *>(qScreen->handle());
if (m_screens.removeOne(waylandScreen))
emit screensChanged();
}

View File

@ -43,8 +43,8 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window)
: QWaylandWindow(window)
QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display)
: QWaylandWindow(window, display)
{
}

View File

@ -50,7 +50,7 @@ namespace QtWaylandClient {
class QWaylandVulkanWindow : public QWaylandWindow
{
public:
explicit QWaylandVulkanWindow(QWindow *window);
explicit QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandVulkanWindow() override;
WindowType windowType() const override;

View File

@ -73,9 +73,9 @@ Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore")
QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
QWaylandWindow::QWaylandWindow(QWindow *window)
QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
: QPlatformWindow(window)
, mDisplay(waylandScreen()->display())
, mDisplay(display)
, mFrameQueue(mDisplay->createEventQueue())
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
{
@ -177,7 +177,7 @@ void QWaylandWindow::initWindow()
}
}
mScale = waylandScreen()->scale();
mScale = waylandScreen() ? waylandScreen()->scale() : 1; // fallback to 1 if we don't have a real screen
// Enable high-dpi rendering. Scale() returns the screen scale factor and will
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
@ -402,14 +402,14 @@ void QWaylandWindow::closePopups(QWaylandWindow *parent)
}
}
QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
{
if (mSurface) {
if (auto *screen = mSurface->oldestEnteredScreen())
return screen;
}
return waylandScreen();
return QPlatformWindow::screen();
}
void QWaylandWindow::setVisible(bool visible)
@ -690,7 +690,11 @@ QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const
QWaylandScreen *QWaylandWindow::waylandScreen() const
{
return static_cast<QWaylandScreen *>(QPlatformWindow::screen());
auto *platformScreen = QPlatformWindow::screen();
Q_ASSERT(platformScreen);
if (platformScreen->isPlaceholder())
return nullptr;
return static_cast<QWaylandScreen *>(platformScreen);
}
void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
@ -962,7 +966,7 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
void QWaylandWindow::handleScreensChanged()
{
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
QPlatformScreen *newScreen = calculateScreenFromSurfaceEvents();
if (newScreen == mLastReportedScreen)
return;
@ -970,7 +974,7 @@ void QWaylandWindow::handleScreensChanged()
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
mLastReportedScreen = newScreen;
int scale = newScreen->scale();
int scale = newScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(newScreen)->scale();
if (scale != mScale) {
mScale = scale;
if (mSurface && mDisplay->compositorVersion() >= 3)

View File

@ -93,7 +93,7 @@ public:
Vulkan
};
QWaylandWindow(QWindow *window);
QWaylandWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandWindow() override;
virtual WindowType windowType() const = 0;
@ -241,7 +241,7 @@ protected:
bool mSentInitialResize = false;
QPoint mOffset;
int mScale = 1;
QWaylandScreen *mLastReportedScreen = nullptr;
QPlatformScreen *mLastReportedScreen = nullptr;
QIcon mWindowIcon;
@ -262,7 +262,7 @@ private:
void reset(bool sendDestroyEvent = true);
void sendExposeEvent(const QRect &rect);
static void closePopups(QWaylandWindow *parent);
QWaylandScreen *calculateScreenFromSurfaceEvents() const;
QPlatformScreen *calculateScreenFromSurfaceEvents() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreensChanged();