From 67524f9d8e4ac5e09ea4427e11f2284e6a6f93fe Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sat, 26 Apr 2025 16:19:07 -0700 Subject: [PATCH] wayland/lock: fix protocol errors with Qt 6.9.0 QWaylandWindow::initWindow now forces a null surface commit which is illegal. This change swaps the surface out for a dummy during initWindow. --- src/wayland/session_lock/surface.cpp | 73 +++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/wayland/session_lock/surface.cpp b/src/wayland/session_lock/surface.cpp index 91e939d..6b1f652 100644 --- a/src/wayland/session_lock/surface.cpp +++ b/src/wayland/session_lock/surface.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -103,6 +102,10 @@ void QSWaylandSessionLockSurface::ext_session_lock_surface_v1_configure( } } +#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0) + +#include + void QSWaylandSessionLockSurface::initVisible() { this->visible = true; @@ -117,3 +120,71 @@ void QSWaylandSessionLockSurface::initVisible() { this->window()->waylandSurface()->attach(this->initBuf->buffer(), 0, 0); this->window()->window()->setVisible(true); } + +#else + +#include + +#include +#include +#include +#include +#include + +// As of Qt 6.9, a null buffer is unconditionally comitted to the surface. To avoid this, we +// cast the window to a subclass with access to mSurface, then swap mSurface during +// QWaylandWindow::initWindow to avoid the null commit. + +// Since QWaylandWindow::mSurface is protected and not private, we can just pretend our +// QWaylandWindow is a subclass that can access it. +class SurfaceAccessor: public QtWaylandClient::QWaylandWindow { +public: + QScopedPointer& surfacePointer() { return this->mSurface; } +}; + +// QWaylandSurface is not exported, meaning we can't link to its constructor or destructor. +// As such, the best alternative is to create a class that can take its place during +// QWaylandWindow::init. +// +// Fortunately, QWaylandSurface's superclasses are both exported, and are therefore identical +// for the purpose of accepting calls during initWindow. +class HackSurface + : public QObject + , public QtWayland::wl_surface { +public: + HackSurface(QtWaylandClient::QWaylandDisplay* display) + : wl_surface(display->createSurface(this)) {} + ~HackSurface() override { this->destroy(); } + Q_DISABLE_COPY_MOVE(HackSurface); +}; + +void QSWaylandSessionLockSurface::initVisible() { + this->visible = true; + + // See note above HackSurface. + auto* dummySurface = new HackSurface(this->window()->display()); + auto* tempSurface = + new QScopedPointer(reinterpret_cast(dummySurface)); + + auto& surfacePointer = reinterpret_cast(this->window())->surfacePointer(); + + // Swap out the surface for a dummy during initWindow. + QT_WARNING_DISABLE_DEPRECATED // swap() + { + surfacePointer.swap(*tempSurface); + this->window()->window()->setVisible(true); + surfacePointer.swap(*tempSurface); + } + QT_WARNING_POP + + // Cast to a HackSurface pointer so ~HackSurface is called instead of ~QWaylandSurface. + delete reinterpret_cast*>(tempSurface); + + // This would normally be run in QWaylandWindow::initWindow but isn't because we + // fed that a dummy surface. + if (surfacePointer->version() >= 3) { + surfacePointer->set_buffer_scale(std::ceil(this->window()->scale())); + } +} + +#endif