From f60780c922335113bbf2273b09d1b80ed032557d Mon Sep 17 00:00:00 2001 From: flodavid Date: Thu, 28 May 2026 23:44:50 +0200 Subject: [PATCH] Daemon/WindowListener: Prevent auto-suspend/idle when going fullscreen Adds two methods to the gala daemon that are triggered by gala itself via dbus, because the WindowListener is not inside a GTK Application. One method used when any window goes fullscreen, to mark it as "inhibiting"automatic sleep to the session manager. The other method is to "unhibit" for the given window when it exits fullscreen. Multiple windows can inhibit sleeping, they all must exit fullscreen to restore auto-sleep. NB: If any window is fullscreen, auto sleep is disabled, even if it is not on the current virtual desktop. TODO: add gschema setting for disabling auto suspend when fullscreen --- daemon-gtk3/DBus.vala | 66 +++++++++++++++++++++++++++++++++++++++++ daemon/DBus.vala | 66 +++++++++++++++++++++++++++++++++++++++++ src/DaemonManager.vala | 30 +++++++++++++++++++ src/WindowListener.vala | 13 +++++++- src/WindowManager.vala | 2 +- 5 files changed, 175 insertions(+), 2 deletions(-) diff --git a/daemon-gtk3/DBus.vala b/daemon-gtk3/DBus.vala index b00f4148a..6ccb1f330 100644 --- a/daemon-gtk3/DBus.vala +++ b/daemon-gtk3/DBus.vala @@ -32,6 +32,9 @@ public class Gala.Daemon.DBus : GLib.Object { private List monitor_labels = new List (); + private Gee.HashMap suspend_cookies = new Gee.HashMap (); + private Gee.HashMap idle_cookies = new Gee.HashMap (); + construct { Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala); } @@ -114,6 +117,69 @@ public class Gala.Daemon.DBus : GLib.Object { } } + public void prevent_sleep (uint64 window_id) throws GLib.DBusError, GLib.IOError { + unowned var application = (Gtk.Application) GLib.Application.get_default (); + + // TODO add gschema setting for disabling auto suspend when fullscreen + if (application != null) { + var window_id_str = window_id.to_string (); + info ("preventing auto-suspend and auto-idle because a window [%s] went fullscreen", window_id_str); + // Ask the session manager to not automatically turn off the screen or put the system to sleep + prevent_sleep_while_fullscreen (window_id_str, application); + } + } + + public void unprevent_sleep (uint64 window_id) throws GLib.DBusError, GLib.IOError { + unowned var application = (Gtk.Application) GLib.Application.get_default (); + + // Inform the session manager we do not want to prevent turning off the screen or sleeping + if (application != null) { + var window_id_str = window_id.to_string (); + info ("unpreventing auto-suspend and auto-idle by window [%s] because it exited fullscreen", + window_id_str); + + if (suspend_cookies.has_key (window_id_str)) { + end_prevent_suspend_while_fullscreen (window_id_str, application); + } + if (idle_cookies.has_key (window_id_str)) { + end_prevent_idle_while_fullscreen (window_id_str, application); + } + } + } + + private void prevent_sleep_while_fullscreen (string window_id_str, Gtk.Application application) { + // If window was already marked as preventing fullscreen, unhinibit before inhibiting + if (suspend_cookies.has_key (window_id_str)) { + end_prevent_suspend_while_fullscreen (window_id_str, application); + } + if (idle_cookies.has_key (window_id_str)) { + end_prevent_idle_while_fullscreen (window_id_str, application); + } + + suspend_cookies.set (window_id_str, application.inhibit ( + null, + Gtk.ApplicationInhibitFlags.SUSPEND, + "Prevent session from suspending while fullscreen" + )); + idle_cookies.set (window_id_str, application.inhibit ( + null, + Gtk.ApplicationInhibitFlags.IDLE, + "Prevent session from idle while fullscreen" + )); + } + + private void end_prevent_suspend_while_fullscreen (string window_id_str, Gtk.Application application) { + uint suspend_cookie = suspend_cookies.get (window_id_str); + application.uninhibit (suspend_cookie); + suspend_cookies.unset (window_id_str); + } + + private void end_prevent_idle_while_fullscreen (string window_id_str, Gtk.Application application) { + uint idle_cookie = idle_cookies.get (window_id_str); + application.uninhibit (idle_cookie); + idle_cookies.unset (window_id_str); + } + public void show_monitor_labels (MonitorLabelInfo[] label_infos) throws GLib.DBusError, GLib.IOError { hide_monitor_labels (); diff --git a/daemon/DBus.vala b/daemon/DBus.vala index 1dedf953c..3c97c8a31 100644 --- a/daemon/DBus.vala +++ b/daemon/DBus.vala @@ -36,6 +36,9 @@ public class Gala.Daemon.DBus : GLib.Object { private List monitor_labels = new List (); + private Gee.HashMap suspend_cookies = new Gee.HashMap (); + private Gee.HashMap idle_cookies = new Gee.HashMap (); + construct { Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala); @@ -155,6 +158,69 @@ public class Gala.Daemon.DBus : GLib.Object { }); } + public void prevent_sleep (uint64 window_id) throws GLib.DBusError, GLib.IOError { + unowned var application = (Gtk.Application) GLib.Application.get_default (); + + // TODO add gschema setting for disabling auto suspend when fullscreen + if (application != null) { + var window_id_str = window_id.to_string (); + info ("preventing auto-suspend and auto-idle because a window [%s] went fullscreen", window_id_str); + // Ask the session manager to not automatically turn off the screen or put the system to sleep + prevent_sleep_while_fullscreen (window_id_str, application); + } + } + + public void unprevent_sleep (uint64 window_id) throws GLib.DBusError, GLib.IOError { + unowned var application = (Gtk.Application) GLib.Application.get_default (); + + // Inform the session manager we do not want to prevent turning off the screen or sleeping + if (application != null) { + var window_id_str = window_id.to_string (); + info ("unpreventing auto-suspend and auto-idle by window [%s] because it exited fullscreen", + window_id_str); + + if (suspend_cookies.has_key (window_id_str)) { + end_prevent_suspend_while_fullscreen (window_id_str, application); + } + if (idle_cookies.has_key (window_id_str)) { + end_prevent_idle_while_fullscreen (window_id_str, application); + } + } + } + + private void prevent_sleep_while_fullscreen (string window_id_str, Gtk.Application application) { + // If window was already marked as preventing fullscreen, unhinibit before inhibiting + if (suspend_cookies.has_key (window_id_str)) { + end_prevent_suspend_while_fullscreen (window_id_str, application); + } + if (idle_cookies.has_key (window_id_str)) { + end_prevent_idle_while_fullscreen (window_id_str, application); + } + + suspend_cookies.set (window_id_str, application.inhibit ( + null, + Gtk.ApplicationInhibitFlags.SUSPEND, + "Prevent session from suspending while fullscreen" + )); + idle_cookies.set (window_id_str, application.inhibit ( + null, + Gtk.ApplicationInhibitFlags.IDLE, + "Prevent session from idle while fullscreen" + )); + } + + private void end_prevent_suspend_while_fullscreen (string window_id_str, Gtk.Application application) { + uint suspend_cookie = suspend_cookies.get (window_id_str); + application.uninhibit (suspend_cookie); + suspend_cookies.unset (window_id_str); + } + + private void end_prevent_idle_while_fullscreen (string window_id_str, Gtk.Application application) { + uint idle_cookie = idle_cookies.get (window_id_str); + application.uninhibit (idle_cookie); + idle_cookies.unset (window_id_str); + } + public void show_monitor_labels (MonitorLabelInfo[] label_infos) throws GLib.DBusError, GLib.IOError { hide_monitor_labels (); diff --git a/src/DaemonManager.vala b/src/DaemonManager.vala index 472bc0b67..02f667f48 100644 --- a/src/DaemonManager.vala +++ b/src/DaemonManager.vala @@ -14,6 +14,8 @@ public class Gala.DaemonManager : GLib.Object { public interface Daemon: GLib.Object { public abstract async void show_window_menu (WindowFlags flags, int width, int height, int x, int y) throws Error; public abstract async void show_desktop_menu (int display_width, int display_height, int x, int y) throws Error; + public abstract async void prevent_sleep (uint64 window_id) throws Error; + public abstract async void unprevent_sleep (uint64 window_id) throws Error; } public Meta.Display display { get; construct; } @@ -128,4 +130,32 @@ public class Gala.DaemonManager : GLib.Object { warning ("Error invoking MenuManager: %s", e.message); } } + + public async void prevent_sleep (uint64 window_id) { + if (daemon_proxy == null) { + return; + } + + try { + debug ("Sending dbus message to daemon to prevent auto-suspend and auto-idle because a window [%" + + uint64.FORMAT + "] went fullscreen", window_id); + yield daemon_proxy.prevent_sleep (window_id); + } catch (Error e) { + warning ("Error invoking MenuManager: %s", e.message); + } + } + + public async void unprevent_sleep (uint64 window_id) { + if (daemon_proxy == null) { + return; + } + + try { + debug ("Sending dbus message to daemon to unprevent auto-suspend and auto-idle by window [%" + + uint64.FORMAT + "] because it exited fullscreen", window_id); + yield daemon_proxy.unprevent_sleep (window_id); + } catch (Error e) { + warning ("Error invoking MenuManager: %s", e.message); + } + } } diff --git a/src/WindowListener.vala b/src/WindowListener.vala index 5945aca1a..47442fd22 100644 --- a/src/WindowListener.vala +++ b/src/WindowListener.vala @@ -22,13 +22,16 @@ public class Gala.WindowListener : Object { } private static WindowListener? instance = null; + private static unowned DaemonManager shared_daemon_manager; - public static void init (Meta.Display display) { + public static void init (Meta.Display display, DaemonManager daemon_manager) { if (instance != null) return; instance = new WindowListener (); + shared_daemon_manager = daemon_manager; + #if HAS_MUTTER48 unowned List actors = display.get_compositor ().get_window_actors (); #else @@ -92,6 +95,14 @@ public class Gala.WindowListener : Object { break; case "fullscreen": window_fullscreen_changed (window); + if (window.fullscreen) { + // Only prevent sleep for windows on the main monitor + if (window.is_on_primary_monitor ()) { + shared_daemon_manager.prevent_sleep (window.get_id ()); + } + } else { + shared_daemon_manager.unprevent_sleep (window.get_id ()); + } break; } } diff --git a/src/WindowManager.vala b/src/WindowManager.vala index ce9b1dd3b..048d4041d 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -206,7 +206,7 @@ namespace Gala { private void show_stage () { unowned Meta.Display display = get_display (); - WindowListener.init (display); + WindowListener.init (display, daemon_manager); keyboard_manager = new KeyboardManager (display); window_tracker = new WindowTracker (); WindowStateSaver.init (window_tracker);