Skip to content

Commit fe2e770

Browse files
committed
nemo-desktop: Don't crash/quit in Wayland when the monitor is removed
Hold the application alive in Wayland mode when monitors disappear, so it can recover when the monitor returns, rather than exiting. Handle GdkDisplay::monitor-removed immediately, by destroying windows cleanly before gtk-layer-shell's closed handler fires, so there's no dangling surface for the frame clock to hit.
1 parent bc5f6a9 commit fe2e770

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

src/nemo-desktop-manager.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ typedef struct {
5858
gulong name_owner_changed_id;
5959
gulong proxy_signals_id;
6060
gulong fallback_size_changed_id;
61+
gulong monitor_added_id;
62+
gulong monitor_removed_id;
63+
64+
gboolean has_wayland_app_hold;
6165
} NemoDesktopManagerPrivate;
6266

6367
struct _NemoDesktopManager
@@ -572,6 +576,37 @@ on_failsafe_timeout (NemoDesktopManager *manager)
572576
return G_SOURCE_REMOVE;
573577
}
574578

579+
static void
580+
on_monitor_removed_wayland (GdkDisplay *display,
581+
GdkMonitor *monitor,
582+
NemoDesktopManager *manager)
583+
{
584+
FETCH_PRIV (manager);
585+
586+
if (priv->current_run_state < RUN_STATE_RUNNING) {
587+
return;
588+
}
589+
590+
/* The compositor sent zwlr_layer_surface_v1.closed for the layer surface
591+
* associated with the removed output. gtk-layer-shell will destroy the
592+
* backing wl_surface in response. We must destroy our GtkWindow first,
593+
* before that happens, or a pending frame clock event will call
594+
* gdk_window_begin_draw_frame() on the dead wl_surface and crash. */
595+
DEBUG ("Wayland output removed - closing desktop windows immediately");
596+
597+
close_all_windows (manager);
598+
queue_update_layout (manager);
599+
}
600+
601+
static void
602+
on_monitor_added_wayland (GdkDisplay *display,
603+
GdkMonitor *monitor,
604+
NemoDesktopManager *manager)
605+
{
606+
DEBUG ("Wayland output added - queuing layout update");
607+
queue_update_layout (manager);
608+
}
609+
575610
static void
576611
connect_fallback_signals (NemoDesktopManager *manager)
577612
{
@@ -583,6 +618,20 @@ connect_fallback_signals (NemoDesktopManager *manager)
583618
"size_changed",
584619
G_CALLBACK (queue_update_layout),
585620
manager);
621+
622+
if (eel_check_is_wayland ()) {
623+
GdkDisplay *display = gdk_display_get_default ();
624+
625+
priv->monitor_removed_id = g_signal_connect (display,
626+
"monitor-removed",
627+
G_CALLBACK (on_monitor_removed_wayland),
628+
manager);
629+
630+
priv->monitor_added_id = g_signal_connect (display,
631+
"monitor-added",
632+
G_CALLBACK (on_monitor_added_wayland),
633+
manager);
634+
}
586635
}
587636

588637
static void
@@ -671,6 +720,25 @@ nemo_desktop_manager_dispose (GObject *object)
671720
priv->fallback_size_changed_id = 0;
672721
}
673722

723+
if (priv->monitor_removed_id > 0 || priv->monitor_added_id > 0) {
724+
GdkDisplay *display = gdk_display_get_default ();
725+
726+
if (priv->monitor_removed_id > 0) {
727+
g_signal_handler_disconnect (display, priv->monitor_removed_id);
728+
priv->monitor_removed_id = 0;
729+
}
730+
731+
if (priv->monitor_added_id > 0) {
732+
g_signal_handler_disconnect (display, priv->monitor_added_id);
733+
priv->monitor_added_id = 0;
734+
}
735+
}
736+
737+
if (priv->has_wayland_app_hold) {
738+
g_application_release (G_APPLICATION (nemo_application_get_singleton ()));
739+
priv->has_wayland_app_hold = FALSE;
740+
}
741+
674742
G_OBJECT_CLASS (nemo_desktop_manager_parent_class)->dispose (object);
675743
}
676744

@@ -764,6 +832,18 @@ nemo_desktop_manager_init (NemoDesktopManager *manager)
764832
}
765833

766834
connect_fallback_signals (manager);
835+
836+
if (eel_check_is_wayland ()) {
837+
/* Hold the application alive independently of any windows so that
838+
* nemo-desktop survives monitor removal (e.g. KVM switch). Without
839+
* this, gtk-layer-shell destroys our windows when the compositor
840+
* removes an output and GtkApplication exits immediately with no
841+
* windows left. The hold is released in nemo_desktop_manager_dispose
842+
* when the application is genuinely quitting. */
843+
g_application_hold (G_APPLICATION (nemo_application_get_singleton ()));
844+
priv->has_wayland_app_hold = TRUE;
845+
}
846+
767847
g_timeout_add (250, (GSourceFunc) fallback_startup_idle_cb, manager);
768848
}
769849

0 commit comments

Comments
 (0)