[plasma/kwin] src: main_wayland: only restart Xwayland if the GPU it's using was removed

Xaver Hugl <[email protected]> Tue, 4 Aug 2026 15:18:41 +0000 (UTC)
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 63081f49911a5f3e9744acb0390f20ddfa6badcb by Xaver Hugl.
Committed on 04/08/2026 at 14:17.
Pushed by zamundaaa into branch 'master'.

main_wayland: only restart Xwayland if the GPU it's using was removed

That way it becomes possible to change the primary GPU without killing
Xwayland.

M  +15   -4    src/main_wayland.cpp
M  +7    -7    src/wayland/linuxdmabufv1clientbuffer.cpp
M  +1    -1    src/wayland/linuxdmabufv1clientbuffer.h

https://invent.kde.org/plasma/kwin/-/commit/63081f49911a5f3e9744acb0390f20ddfa6badcb

diff --git a/src/main_wayland.cpp b/src/main_wayland.cpp
index 848f43f5c5e..b0cb35d78f7 100644
--- a/src/main_wayland.cpp
+++ b/src/main_wayland.cpp
@@ -14,13 +14,17 @@
 #include "backends/virtual/virtual_backend.h"
 #include "backends/wayland/wayland_backend.h"
 #include "compositor.h"
+#include "core/gpumanager.h"
 #include "core/outputbackend.h"
+#include "core/renderdevice.h"
 #include "core/session.h"
 #include "effect/effecthandler.h"
 #include "inputmethod.h"
 #include "tabletmodemanager.h"
 #include "utils/realtime.h"
+#include "wayland/clientconnection.h"
 #include "wayland/display.h"
+#include "wayland/linuxdmabufv1clientbuffer.h"
 #include "wayland/seat.h"
 #include "wayland_server.h"
 #include "workspace.h"
@@ -171,10 +175,17 @@ void ApplicationWayland::performStartup()
         m_xwayland->xwaylandLauncher()->addEnvironmentVariables(m_xwaylandExtraEnvironment);
         m_xwayland->xwaylandLauncher()->passFileDescriptors(std::move(m_xwaylandFds));
         m_xwayland->init();
-        connect(Compositor::self(), &Compositor::primaryGpuChanged, m_xwayland.get(), [this]() {
-            // Xwayland will automatically be started again
-            // once a client tries to connect to it
-            m_xwayland->xwaylandLauncher()->stop();
+        connect(GpuManager::self(), &GpuManager::renderDeviceRemoved, m_xwayland.get(), [this](RenderDevice *removed) {
+            if (!waylandServer()->xWaylandConnection() || !waylandServer()->linuxDmabuf()) {
+                return;
+            }
+            const auto mainDevice = waylandServer()->linuxDmabuf()->mainDevice(waylandServer()->xWaylandConnection()->client());
+            if (mainDevice == removed->deviceId()) {
+                // Xwayland breaks if the GPU it's using was hotunplugged,
+                // so we manually stop it here. It will automatically be
+                // started again once a client tries to connect to it
+                m_xwayland->xwaylandLauncher()->stop();
+            }
         });
     }
 #endif
diff --git a/src/wayland/linuxdmabufv1clientbuffer.cpp b/src/wayland/linuxdmabufv1clientbuffer.cpp
index 989bb5fccd9..ebd6b06e9ee 100644
--- a/src/wayland/linuxdmabufv1clientbuffer.cpp
+++ b/src/wayland/linuxdmabufv1clientbuffer.cpp
@@ -178,7 +178,7 @@ void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource,
     m_attrs.format = format;
 
     auto clientBuffer = new LinuxDmaBufV1ClientBuffer(std::move(m_attrs));
-    const dev_t target = m_targetDevice.value_or(m_integration->mainDevice(resource->client()));
+    const dev_t target = m_targetDevice.value_or(m_integration->mainDevice(resource->client()).value());
     const auto &devices = GpuManager::s_self->renderDevices();
     bool success = renderBackend->testImportBuffer(clientBuffer, target);
     if (success) {
@@ -246,7 +246,7 @@ void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *reso
     m_attrs.format = format;
 
     auto clientBuffer = new LinuxDmaBufV1ClientBuffer(std::move(m_attrs));
-    const dev_t target = m_targetDevice.value_or(m_integration->mainDevice(resource->client()));
+    const dev_t target = m_targetDevice.value_or(m_integration->mainDevice(resource->client()).value());
     const auto &devices = GpuManager::s_self->renderDevices();
     bool success = renderBackend->testImportBuffer(clientBuffer, target);
     if (success) {
@@ -406,10 +406,10 @@ void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(cons
     }
 }
 
-dev_t LinuxDmaBufV1ClientBufferIntegration::mainDevice(wl_client *client) const
+std::optional<dev_t> LinuxDmaBufV1ClientBufferIntegration::mainDevice(wl_client *client) const
 {
-    Q_ASSERT(d->mainDevices.contains(client));
-    return d->mainDevices[client];
+    const auto it = d->mainDevices.find(client);
+    return it == d->mainDevices.end() ? std::nullopt : std::optional(*it);
 }
 
 void LinuxDmaBufV1ClientBuffer::buffer_destroy_resource(wl_resource *resource)
@@ -483,7 +483,7 @@ LinuxDmaBufV1Feedback::~LinuxDmaBufV1Feedback() = default;
 void LinuxDmaBufV1Feedback::setScanoutTranches(DrmDevice *scanoutDevice, const FormatModifierMap &formats)
 {
     Q_ASSERT(d->m_client);
-    const dev_t mainDevice = d->m_bufferintegration->q->mainDevice(d->m_client);
+    const dev_t mainDevice = d->m_bufferintegration->q->mainDevice(d->m_client).value();
     setScanoutTranches(createScanoutTranches(d->m_bufferintegration->defaultTranches, mainDevice, scanoutDevice, formats));
 }
 
@@ -545,7 +545,7 @@ LinuxDmaBufV1FeedbackPrivate::LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBu
 
 void LinuxDmaBufV1FeedbackPrivate::send(Resource *resource)
 {
-    const dev_t mainDevice = m_bufferintegration->q->mainDevice(resource->client());
+    const dev_t mainDevice = m_bufferintegration->q->mainDevice(resource->client()).value();
     send_format_table(resource->handle, m_bufferintegration->table->file.fd(), m_bufferintegration->table->file.size());
     if (resource->version() < ZWP_LINUX_BUFFER_PARAMS_V1_SET_SAMPLING_DEVICE_SINCE_VERSION) {
         QByteArray bytes;
diff --git a/src/wayland/linuxdmabufv1clientbuffer.h b/src/wayland/linuxdmabufv1clientbuffer.h
index 3a37449f9c1..a2b630b7984 100644
--- a/src/wayland/linuxdmabufv1clientbuffer.h
+++ b/src/wayland/linuxdmabufv1clientbuffer.h
@@ -78,7 +78,7 @@ public:
 
     void setSupportedFormatsWithModifiers(const QList<LinuxDmaBufV1Feedback::Tranche> &tranches);
 
-    dev_t mainDevice(wl_client *client) const;
+    std::optional<dev_t> mainDevice(wl_client *client) const;
 
 private:
     friend class LinuxDmaBufV1ClientBufferIntegrationPrivate;