diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp index 26f598895c3..636ef08074b 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp @@ -50,10 +50,11 @@ QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zw , m_shell(shell) , m_window(window) { - auto screen = static_cast(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 diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index 0464d3a4273..a8d19dc6c51 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -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(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; } diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h index 14bb7719841..cd845b44e35 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h +++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h @@ -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 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 mShm; QList mWaitingScreens; QList mScreens; + QPlatformPlaceholderScreen *mPlaceholderScreen = nullptr; QList mInputDevices; QList mRegistryListeners; QWaylandIntegration *mWaylandIntegration = nullptr; diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 9bdd9cc129f..33e64c6e890 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -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 diff --git a/src/plugins/platforms/wayland/qwaylandscreen.cpp b/src/plugins/platforms/wayland/qwaylandscreen.cpp index e707968322d..4f0cd9b0a60 100644 --- a/src/plugins/platforms/wayland/qwaylandscreen.cpp +++ b/src/plugins/platforms/wayland/qwaylandscreen.cpp @@ -165,11 +165,18 @@ QList QWaylandScreen::virtualSiblings() const { QList list; const QList 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(platformScreen); } diff --git a/src/plugins/platforms/wayland/qwaylandscreen_p.h b/src/plugins/platforms/wayland/qwaylandscreen_p.h index e9e07d9cd50..ae91c621111 100644 --- a/src/plugins/platforms/wayland/qwaylandscreen_p.h +++ b/src/plugins/platforms/wayland/qwaylandscreen_p.h @@ -57,7 +57,6 @@ #include #include - QT_BEGIN_NAMESPACE namespace QtWaylandClient { diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp index 9b5971a21e9..8d5b977d586 100644 --- a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp +++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp @@ -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) { } diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h index 8a85cd7f34a..e01632daf75 100644 --- a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h +++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h @@ -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; diff --git a/src/plugins/platforms/wayland/qwaylandshmwindow.cpp b/src/plugins/platforms/wayland/qwaylandshmwindow.cpp index 52833803db1..e305d028d56 100644 --- a/src/plugins/platforms/wayland/qwaylandshmwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandshmwindow.cpp @@ -49,8 +49,8 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandShmWindow::QWaylandShmWindow(QWindow *window) - : QWaylandWindow(window) +QWaylandShmWindow::QWaylandShmWindow(QWindow *window, QWaylandDisplay *display) + : QWaylandWindow(window, display) { } diff --git a/src/plugins/platforms/wayland/qwaylandshmwindow_p.h b/src/plugins/platforms/wayland/qwaylandshmwindow_p.h index ae17278593b..81251b3aa96 100644 --- a/src/plugins/platforms/wayland/qwaylandshmwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandshmwindow_p.h @@ -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; diff --git a/src/plugins/platforms/wayland/qwaylandsurface.cpp b/src/plugins/platforms/wayland/qwaylandsurface.cpp index c35f01b56c3..21e70ce4fe8 100644 --- a/src/plugins/platforms/wayland/qwaylandsurface.cpp +++ b/src/plugins/platforms/wayland/qwaylandsurface.cpp @@ -72,8 +72,12 @@ QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface) void QWaylandSurface::handleScreenRemoved(QScreen *qScreen) { - auto *screen = static_cast(qScreen->handle()); - if (m_screens.removeOne(screen)) + auto *platformScreen = qScreen->handle(); + if (platformScreen->isPlaceholder()) + return; + + auto *waylandScreen = static_cast(qScreen->handle()); + if (m_screens.removeOne(waylandScreen)) emit screensChanged(); } diff --git a/src/plugins/platforms/wayland/qwaylandvulkanwindow.cpp b/src/plugins/platforms/wayland/qwaylandvulkanwindow.cpp index 4c67b6b3287..eb341529aeb 100644 --- a/src/plugins/platforms/wayland/qwaylandvulkanwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandvulkanwindow.cpp @@ -43,8 +43,8 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window) - : QWaylandWindow(window) +QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display) + : QWaylandWindow(window, display) { } diff --git a/src/plugins/platforms/wayland/qwaylandvulkanwindow_p.h b/src/plugins/platforms/wayland/qwaylandvulkanwindow_p.h index d0b2de75d03..3fd394e62ea 100644 --- a/src/plugins/platforms/wayland/qwaylandvulkanwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandvulkanwindow_p.h @@ -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; diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 950486c0cfe..751058d8914 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -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(QPlatformWindow::screen()); + auto *platformScreen = QPlatformWindow::screen(); + Q_ASSERT(platformScreen); + if (platformScreen->isPlaceholder()) + return nullptr; + return static_cast(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(newScreen)->scale(); if (scale != mScale) { mScale = scale; if (mSurface && mDisplay->compositorVersion() >= 3) diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index c488c2e3f42..36cbee89b62 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -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();