Dear Maintainer, On GTK3/Wayland with outputs using different scale factors, a SPICE display can be rendered at the wrong size after its window is moved from one output to another. Reproducer used here: 1. Run virt-viewer/virt-manager natively on Wayland. 2. Use a mixed-DPI setup, e.g. internal eDP at 150% and external HDMI at 100%. 3. Open a SPICE guest window on the scaled display. 4. Move the window to the 100% display and resize it. Observed: the guest framebuffer becomes approximately half-size / incorrectly scaled in the destination window. Running virt-viewer under XWayland with GDK_BACKEND=x11 avoids the issue. Root cause: spice-gtk sets the Cairo surface device scale when the primary surface is created, but GTK can later change GtkWidget:scale-factor without recreating the Cairo surface. The existing surface therefore retains a stale device scale. The EGL path likewise needs its display resized on a scale-factor transition. This is fixed upstream: Issue: https://gitlab.freedesktop.org/spice/spice-gtk/-/work_items/204 Commit: https://gitlab.freedesktop.org/spice/spice-gtk/-/commit/3f85c575b0fc7acd0c024ae0f331e09aa7ae0813 The upstream commit is authored by me and reviewed/committed by Marc-André Lureau. Upstream master CI passed. Downstream validation performed before filing: - The patch below applies cleanly to Debian spice-gtk 0.42-4. - It also applies cleanly to Ubuntu 0.42-4build2. - A full Ubuntu 26.10 package build with the backport completed successfully. - Its Meson test suite passed: 14/14 tests, 0 failures. - Upstream master was separately built and tested both with EGL disabled and enabled: 11/11 tests in each configuration. - A real Wayland GL scanout test using SPICE gl=on and virtio-vga-gl remained functional while GTK scale-factor changed 2 -> 1 -> 2. - The original mixed-DPI reproduction on KDE Plasma Wayland was verified fixed across repeated monitor transitions and window resizing. Please consider cherry-picking/backporting upstream commit 3f85c575 into Debian's 0.42 package instead of waiting for the next spice-gtk upstream release. DEP-3 patch follows. Description: Update SPICE display scaling when GTK output scale changes On Wayland, moving a SpiceDisplay between outputs with different scale factors updates GtkWidget:scale-factor without recreating the existing Cairo surface. Re-apply the current device scale to the Cairo surface, resize the EGL display when enabled, and refresh the normal scaling state. Origin: upstream, https://gitlab.freedesktop.org/spice/spice-gtk/-/commit/3f85c575b0fc7acd0c024ae0f331e09aa7ae0813 Bug: https://gitlab.freedesktop.org/spice/spice-gtk/-/work_items/204 Forwarded: not-needed Author: Alex <stp.blgm@gmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Last-Update: 2026-09-17 --- diff --git a/src/spice-widget.c b/src/spice-widget.c index 22cc5be..9ccb007 100644 --- a/src/spice-widget.c +++ b/src/spice-widget.c @@ -204,6 +204,29 @@ static void scaling_updated(SpiceDisplay *display) update_size_request(display); } +static void scale_factor_changed(SpiceDisplay *display, + GParamSpec *pspec G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + SpiceDisplayPrivate *d = display->priv; + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display)); + + /* The cairo surface persists while a Wayland window moves between + * outputs. Re-apply its device scale when GTK changes the widget scale + * factor so display scaling stays correct on mixed-DPI setups. */ + if (d->canvas.surface != NULL) { + cairo_surface_set_device_scale(d->canvas.surface, scale_factor, scale_factor); + } + +#ifdef HAVE_EGL + if (egl_enabled(d)) { + spice_egl_resize_display(display, d->ww * scale_factor, d->wh * scale_factor); + } +#endif + + scaling_updated(display); +} + static void update_size_request(SpiceDisplay *display) { SpiceDisplayPrivate *d = display->priv; @@ -718,6 +741,8 @@ static void spice_display_init(SpiceDisplay *display) g_signal_connect(display, "drag-data-received", G_CALLBACK(drag_data_received_callback), NULL); g_signal_connect(display, "size-allocate", G_CALLBACK(size_allocate), NULL); + g_signal_connect(display, "notify::scale-factor", + G_CALLBACK(scale_factor_changed), NULL); gtk_widget_add_events(widget, GDK_POINTER_MOTION_MASK | Regards, Alex