From 77453bfd03223fe422ae9a9f198176dc3652970a Mon Sep 17 00:00:00 2001 From: Franklin-Qi Date: Sat, 10 Oct 2026 18:27:09 +0800 Subject: [PATCH] Re-run display-setup-script on resume from sleep (#428) Suspend/resume resets display settings such as DPMS; listen for logind PrepareForSleep and re-run the seat script so greeter setup applies again. --- data/lightdm.conf | 2 +- src/lightdm.c | 16 ++++++++ src/login1.c | 15 +++++++ src/login1.h | 2 + src/seat.c | 26 ++++++++++++ src/seat.h | 3 ++ tests/Makefile.am | 2 + .../script-hook-display-setup-resume.conf | 40 +++++++++++++++++++ tests/src/test-runner.c | 17 ++++++++ tests/test-script-hook-display-setup-resume | 2 + 10 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 tests/scripts/script-hook-display-setup-resume.conf create mode 100755 tests/test-script-hook-display-setup-resume diff --git a/data/lightdm.conf b/data/lightdm.conf index 60e3e8b4..a65efb5e 100644 --- a/data/lightdm.conf +++ b/data/lightdm.conf @@ -71,7 +71,7 @@ # session-wrapper = Wrapper script to run session with # greeter-wrapper = Wrapper script to run greeter with # guest-wrapper = Wrapper script to run guest sessions with -# display-setup-script = Script to run when starting a greeter session (runs as root) +# display-setup-script = Script to run when starting a greeter session (runs as root); also re-run on resume from sleep # display-stopped-script = Script to run after stopping the display server (runs as root) # greeter-setup-script = Script to run when starting a greeter (runs as root) # session-setup-script = Script to run when starting a user session (runs as root) diff --git a/src/lightdm.c b/src/lightdm.c index 2be05347..aba9a4ee 100644 --- a/src/lightdm.c +++ b/src/lightdm.c @@ -558,6 +558,18 @@ login1_service_seat_attention_key_cb (Login1Service *service, Login1Seat *login1 seat_switch_to_greeter (seat); } +static void +login1_service_prepare_for_sleep_cb (Login1Service *service, gboolean active) +{ + /* active=TRUE means the system is about to sleep; FALSE means it resumed. */ + if (active) + return; + + g_debug ("System resumed from sleep, re-running display setup scripts"); + for (GList *link = display_manager_get_seats (display_manager); link; link = link->next) + seat_run_display_setup_scripts (link->data); +} + int main (int argc, char **argv) { @@ -894,6 +906,10 @@ main (int argc, char **argv) /* Load dynamic seats from logind */ g_debug ("Monitoring logind for seats"); + /* Resume is system-wide; re-run display-setup even when seats are not + * started from logind's default seat list. */ + g_signal_connect (login1_service_get_instance (), LOGIN1_SERVICE_SIGNAL_PREPARE_FOR_SLEEP, G_CALLBACK (login1_service_prepare_for_sleep_cb), NULL); + if (config_get_boolean (config_get_instance (), "LightDM", "start-default-seat")) { g_signal_connect (login1_service_get_instance (), LOGIN1_SERVICE_SIGNAL_SEAT_ADDED, G_CALLBACK (login1_service_seat_added_cb), NULL); diff --git a/src/login1.c b/src/login1.c index 1bc6303f..6fcd6256 100644 --- a/src/login1.c +++ b/src/login1.c @@ -24,6 +24,7 @@ enum { SEAT_ADDED, SEAT_REMOVED, SEAT_ATTENTION_KEY, + PREPARE_FOR_SLEEP, LAST_SERVICE_SIGNAL }; static guint service_signals[LAST_SERVICE_SIGNAL] = { 0 }; @@ -257,6 +258,12 @@ signal_cb (GDBusConnection *connection, } } + else if (strcmp (signal_name, "PrepareForSleep") == 0) + { + gboolean active; + g_variant_get (parameters, "(b)", &active); + g_signal_emit (service, service_signals[PREPARE_FOR_SLEEP], 0, active); + } } gboolean @@ -515,6 +522,14 @@ login1_service_class_init (Login1ServiceClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE); + service_signals[PREPARE_FOR_SLEEP] = + g_signal_new (LOGIN1_SERVICE_SIGNAL_PREPARE_FOR_SLEEP, + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (Login1ServiceClass, prepare_for_sleep), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } const gchar * diff --git a/src/login1.h b/src/login1.h index b815e222..203b5c7b 100644 --- a/src/login1.h +++ b/src/login1.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS #define LOGIN1_SERVICE_SIGNAL_SEAT_ADDED "seat-added" #define LOGIN1_SERVICE_SIGNAL_SEAT_REMOVED "seat-removed" #define LOGIN1_SERVICE_SIGNAL_SEAT_ATTENTION_KEY "seat-attention-key" +#define LOGIN1_SERVICE_SIGNAL_PREPARE_FOR_SLEEP "prepare-for-sleep" #define LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED "can-graphical-changed" #define LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED "active-session-changed" @@ -52,6 +53,7 @@ typedef struct void (*seat_added)(Login1Service *service, Login1Seat *seat); void (*seat_removed)(Login1Service *service, Login1Seat *seat); void (*seat_attention_key)(Login1Service *service, Login1Seat *seat); + void (*prepare_for_sleep)(Login1Service *service, gboolean active); } Login1ServiceClass; GType login1_service_get_type (void); diff --git a/src/seat.c b/src/seat.c index 0b55906c..fc0d6456 100644 --- a/src/seat.c +++ b/src/seat.c @@ -1338,6 +1338,32 @@ find_session_for_display_server (Seat *seat, DisplayServer *display_server) return NULL; } +void +seat_run_display_setup_scripts (Seat *seat) +{ + SeatPrivate *priv = seat_get_instance_private (seat); + const gchar *script; + + g_return_if_fail (seat != NULL); + + script = seat_get_string_property (seat, "display-setup-script"); + if (!script) + return; + + for (GList *link = priv->display_servers; link; link = link->next) + { + DisplayServer *display_server = link->data; + + if (!display_server_get_is_ready (display_server) || + display_server_get_is_stopping (display_server)) + continue; + + l_debug (seat, "Running display setup script %s", script); + if (!run_script (seat, display_server, script, NULL, NULL)) + l_debug (seat, "Display setup script failed"); + } +} + static void display_server_ready_cb (DisplayServer *display_server, Seat *seat) { diff --git a/src/seat.h b/src/seat.h index 31cd05a2..e5d6ac85 100644 --- a/src/seat.h +++ b/src/seat.h @@ -87,6 +87,9 @@ void seat_set_can_tty (Seat *seat, gboolean can_tty); gboolean seat_start (Seat *seat); +/* Re-run display-setup-script for ready display servers (e.g. after resume). */ +void seat_run_display_setup_scripts (Seat *seat); + GList *seat_get_sessions (Seat *seat); void seat_set_active_session (Seat *seat, Session *session); diff --git a/tests/Makefile.am b/tests/Makefile.am index fc85348e..aed26236 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -128,6 +128,7 @@ TESTS = \ test-script-hooks \ test-script-hook-display-setup-fail \ test-script-hook-display-setup-missing \ + test-script-hook-display-setup-resume \ test-script-hook-greeter-setup-fail \ test-script-hook-greeter-setup-missing \ test-script-hook-session-setup-fail \ @@ -544,6 +545,7 @@ EXTRA_DIST = \ scripts/script-hooks.conf \ scripts/script-hook-display-setup-fail.conf \ scripts/script-hook-display-setup-missing.conf \ + scripts/script-hook-display-setup-resume.conf \ scripts/script-hook-greeter-setup-fail.conf \ scripts/script-hook-greeter-setup-missing.conf \ scripts/script-hook-session-setup-fail.conf \ diff --git a/tests/scripts/script-hook-display-setup-resume.conf b/tests/scripts/script-hook-display-setup-resume.conf new file mode 100644 index 00000000..b6a4307a --- /dev/null +++ b/tests/scripts/script-hook-display-setup-resume.conf @@ -0,0 +1,40 @@ +# +# Check display-setup-script is re-run when resuming from sleep (issue #428) +# + +[Seat:*] +display-setup-script=test-script-hook DISPLAY-SETUP + +#?*START-DAEMON +#?RUNNER DAEMON-START + +# X server starts +#?XSERVER-0 START VT=7 SEAT=seat0 + +# Daemon connects when X server is ready +#?*XSERVER-0 INDICATE-READY +#?XSERVER-0 INDICATE-READY +#?XSERVER-0 ACCEPT-CONNECT + +# Display setup runs when the display server becomes ready +#?SCRIPT-HOOK DISPLAY-SETUP XDG_SEAT=seat0 + +# Greeter starts +#?GREETER-X-0 START XDG_SEAT=seat0 XDG_VTNR=7 XDG_SESSION_CLASS=greeter +#?LOGIN1 ACTIVATE-SESSION SESSION=c0 +#?XSERVER-0 ACCEPT-CONNECT +#?GREETER-X-0 CONNECT-XSERVER +#?GREETER-X-0 CONNECT-TO-DAEMON +#?GREETER-X-0 CONNECTED-TO-DAEMON + +# Simulate resume from sleep (PrepareForSleep active=false) +#?*LOGIN1-PREPARE-FOR-SLEEP ACTIVE=FALSE + +# Display setup must run again so greeter DPMS / layout scripts apply +#?SCRIPT-HOOK DISPLAY-SETUP XDG_SEAT=seat0 + +# Cleanup +#?*STOP-DAEMON +#?GREETER-X-0 TERMINATE SIGNAL=15 +#?XSERVER-0 TERMINATE SIGNAL=15 +#?RUNNER DAEMON-EXIT STATUS=0 diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c index 64b7fb86..09fcfa64 100644 --- a/tests/src/test-runner.c +++ b/tests/src/test-runner.c @@ -594,6 +594,20 @@ handle_command (const gchar *command) const gchar *id = g_hash_table_lookup (params, "ID"); remove_login1_seat (dbus_conn, id); } + else if (strcmp (name, "LOGIN1-PREPARE-FOR-SLEEP") == 0) + { + const gchar *v = g_hash_table_lookup (params, "ACTIVE"); + gboolean active = v && strcmp (v, "TRUE") == 0; + g_autoptr(GError) error = NULL; + if (!g_dbus_connection_emit_signal (dbus_conn, + NULL, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PrepareForSleep", + g_variant_new ("(b)", active), + &error)) + g_warning ("Failed to emit PrepareForSleep: %s", error->message); + } else if (strcmp (name, "LIST-SEATS") == 0) { g_autoptr(GError) error = NULL; @@ -1933,6 +1947,9 @@ login1_name_acquired_cb (GDBusConnection *connection, " " " " " " + " " + " " + " " " " ""; g_autoptr(GError) error = NULL; diff --git a/tests/test-script-hook-display-setup-resume b/tests/test-script-hook-display-setup-resume new file mode 100755 index 00000000..087101f9 --- /dev/null +++ b/tests/test-script-hook-display-setup-resume @@ -0,0 +1,2 @@ +#!/bin/sh +./src/dbus-env ./src/test-runner script-hook-display-setup-resume test-gobject-greeter