@@ -7,6 +7,7 @@ class_name PowerSaver
77var display := load ("res://core/global/display_manager.tres" ) as DisplayManager
88var settings := load ("res://core/global/settings_manager.tres" ) as SettingsManager
99var power_manager := load ("res://core/systems/power/power_manager.tres" ) as UPowerInstance
10+ var gamescope := load ("res://core/systems/gamescope/gamescope.tres" ) as GamescopeInstance
1011
1112const MINUTE := 60
1213
@@ -22,16 +23,19 @@ const MINUTE := 60
2223
2324@onready var dim_timer := $% DimTimer as Timer
2425@onready var suspend_timer := $% SuspendTimer as Timer
26+ @onready var gamescope_timer := $% GamescopeCheckTimer as Timer
2527
2628var dimmed := false
2729var prev_brightness := {}
2830var supports_brightness := display .supports_brightness ()
2931var has_battery := false
3032var display_device := power_manager .get_display_device ()
33+ var gamescope_input_counters : Dictionary [int , int ] = {}
3134var logger := Log .get_logger ("PowerSaver" )
3235
3336
3437func _ready () -> void :
38+ gamescope_timer .timeout .connect (_on_gamescope_check_timeout )
3539 if display_device :
3640 has_battery = display_device .is_present
3741
@@ -84,7 +88,7 @@ func _on_suspend_timer_timeout() -> void:
8488 logger .warn ("Failed to suspend: '" + output [0 ] + "'" )
8589
8690
87- func _input (event : InputEvent ) -> void :
91+ func _input (_event : InputEvent ) -> void :
8892 if dim_screen_enabled and supports_brightness :
8993 if dimmed :
9094 dimmed = false
@@ -98,3 +102,47 @@ func _input(event: InputEvent) -> void:
98102 dim_timer .start (dim_after_inactivity_mins * MINUTE )
99103 if auto_suspend_enabled :
100104 suspend_timer .start (suspend_after_inactivity_mins * MINUTE )
105+
106+
107+ ## Called at a regular interval to check if gamescope input counters have changed.
108+ ## Gamepad inputs will be routed to all running applications (including OpenGamepadUI)
109+ ## which can be used to check for inactivity, but keyboard/mouse inputs will not.
110+ ## To work around this, this method will check the input counter atom in gamescope
111+ ## which will change whenever keyboard/mouse input is detected.
112+ func _on_gamescope_check_timeout () -> void :
113+ # Loop through each xwayland instance to see if any have received mouse or
114+ # keyboard inputs since the last check
115+ var detected_input := false
116+ for xwayland_type in [gamescope .XWAYLAND_TYPE_PRIMARY , gamescope .XWAYLAND_TYPE_OGUI ]:
117+ if not _has_gamescope_input_counter_changed (xwayland_type ):
118+ continue
119+ detected_input = true
120+ var xwayland := gamescope .get_xwayland (xwayland_type )
121+ if not xwayland :
122+ continue
123+ self .gamescope_input_counters [xwayland_type ] = xwayland .get_input_counter ()
124+
125+ if not detected_input :
126+ return
127+
128+ self ._input (null )
129+
130+
131+ ## Returns true if input counter for the given gamescope type is different
132+ ## than the one recorded in `self.gamescope_input_counters`
133+ func _has_gamescope_input_counter_changed (xwayland_type : int ) -> bool :
134+ var xwayland := gamescope .get_xwayland (xwayland_type )
135+ if not xwayland :
136+ return false
137+ var last := _get_last_input_counter_for (xwayland_type )
138+ var current := xwayland .get_input_counter ()
139+
140+ return last != current
141+
142+
143+ ## Returns the last set input counter for the given XWayland type.
144+ func _get_last_input_counter_for (xwayland_type : int ) -> int :
145+ var counter := 0
146+ if xwayland_type in self .gamescope_input_counters :
147+ counter = self .gamescope_input_counters [xwayland_type ]
148+ return counter
0 commit comments