Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion core/systems/power/power_saver.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class_name PowerSaver
var display := load("res://core/global/display_manager.tres") as DisplayManager
var settings := load("res://core/global/settings_manager.tres") as SettingsManager
var power_manager := load("res://core/systems/power/power_manager.tres") as UPowerInstance
var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance

const MINUTE := 60

Expand All @@ -22,16 +23,19 @@ const MINUTE := 60

@onready var dim_timer := $%DimTimer as Timer
@onready var suspend_timer := $%SuspendTimer as Timer
@onready var gamescope_timer := $%GamescopeCheckTimer as Timer

var dimmed := false
var prev_brightness := {}
var supports_brightness := display.supports_brightness()
var has_battery := false
var display_device := power_manager.get_display_device()
var gamescope_input_counters: Dictionary[int, int] = {}
var logger := Log.get_logger("PowerSaver")


func _ready() -> void:
gamescope_timer.timeout.connect(_on_gamescope_check_timeout)
if display_device:
has_battery = display_device.is_present

Expand Down Expand Up @@ -84,7 +88,7 @@ func _on_suspend_timer_timeout() -> void:
logger.warn("Failed to suspend: '" + output[0] + "'")


func _input(event: InputEvent) -> void:
func _input(_event: InputEvent) -> void:
if dim_screen_enabled and supports_brightness:
if dimmed:
dimmed = false
Expand All @@ -98,3 +102,47 @@ func _input(event: InputEvent) -> void:
dim_timer.start(dim_after_inactivity_mins * MINUTE)
if auto_suspend_enabled:
suspend_timer.start(suspend_after_inactivity_mins * MINUTE)


## Called at a regular interval to check if gamescope input counters have changed.
## Gamepad inputs will be routed to all running applications (including OpenGamepadUI)
## which can be used to check for inactivity, but keyboard/mouse inputs will not.
## To work around this, this method will check the input counter atom in gamescope
## which will change whenever keyboard/mouse input is detected.
func _on_gamescope_check_timeout() -> void:
# Loop through each xwayland instance to see if any have received mouse or
# keyboard inputs since the last check
var detected_input := false
for xwayland_type in [gamescope.XWAYLAND_TYPE_PRIMARY, gamescope.XWAYLAND_TYPE_OGUI]:
if not _has_gamescope_input_counter_changed(xwayland_type):
continue
detected_input = true
var xwayland := gamescope.get_xwayland(xwayland_type)
if not xwayland:
continue
self.gamescope_input_counters[xwayland_type] = xwayland.get_input_counter()

if not detected_input:
return

self._input(null)


## Returns true if input counter for the given gamescope type is different
## than the one recorded in `self.gamescope_input_counters`
func _has_gamescope_input_counter_changed(xwayland_type: int) -> bool:
var xwayland := gamescope.get_xwayland(xwayland_type)
if not xwayland:
return false
var last := _get_last_input_counter_for(xwayland_type)
var current := xwayland.get_input_counter()

return last != current


## Returns the last set input counter for the given XWayland type.
func _get_last_input_counter_for(xwayland_type: int) -> int:
var counter := 0
if xwayland_type in self.gamescope_input_counters:
counter = self.gamescope_input_counters[xwayland_type]
return counter
4 changes: 4 additions & 0 deletions core/systems/power/power_saver.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ one_shot = true
[node name="SuspendTimer" type="Timer" parent="."]
unique_name_in_owner = true
one_shot = true

[node name="GamescopeCheckTimer" type="Timer" parent="."]
unique_name_in_owner = true
autostart = true
4 changes: 2 additions & 2 deletions core/ui/card_ui/card_ui.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ instance = ExtResource("11_nk5v7")
[node name="Launcher" type="Node" parent="."]
script = ExtResource("14_fs00k")

[node name="PowerSaver" parent="." instance=ExtResource("8_hyc1j")]

[node name="PowerStation" type="Node" parent="."]
script = ExtResource("13_tag7s")
instance = ExtResource("14_a5vk3")
Expand Down Expand Up @@ -373,4 +371,6 @@ autoplay = true

[node name="InputManager" parent="." instance=ExtResource("1_34t85")]

[node name="PowerSaver" parent="." instance=ExtResource("8_hyc1j")]

[editable path="AlwaysVisibleContent/OnScreenKeyboard"]
19 changes: 19 additions & 0 deletions extensions/core/src/gamescope/x11_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,25 @@ impl GamescopeXWayland {
}
}

/// Returns the value of the Gamescope input counter. This is useful to detect
/// whether or not Gamescope has been receiving mouse or keyboard inputs.
#[func]
pub fn get_input_counter(&self) -> u32 {
let Ok(root_id) = self.xwayland.get_root_window_id() else {
return 0;
};
let result = self
.xwayland
.get_one_xprop(root_id, GamescopeAtom::InputCounter);
match result {
Ok(counter) => counter.unwrap_or_default(),
Err(e) => {
log::trace!("No input counter found: {e}");
0
}
}
}

/// Returns the list of currently watched windows.
#[func]
pub fn get_watched_windows(&self) -> PackedInt64Array {
Expand Down