@@ -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
@@ -98,3 +102,50 @@ 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+ if dim_screen_enabled and supports_brightness :
129+ dim_timer .start (dim_after_inactivity_mins * MINUTE )
130+ if auto_suspend_enabled :
131+ suspend_timer .start (suspend_after_inactivity_mins * MINUTE )
132+
133+
134+ ## Returns true if input counter for the given gamescope type is different
135+ ## than the one recorded in `self.gamescope_input_counters`
136+ func _has_gamescope_input_counter_changed (xwayland_type : int ) -> bool :
137+ var xwayland := gamescope .get_xwayland (xwayland_type )
138+ if not xwayland :
139+ return false
140+ var last := _get_last_input_counter_for (xwayland_type )
141+ var current := xwayland .get_input_counter ()
142+
143+ return last != current
144+
145+
146+ ## Returns the last set input counter for the given XWayland type.
147+ func _get_last_input_counter_for (xwayland_type : int ) -> int :
148+ var counter := 0
149+ if xwayland_type in self .gamescope_input_counters :
150+ counter = self .gamescope_input_counters [xwayland_type ]
151+ return counter
0 commit comments