chimera-steam-switch/plugin.gd
Jose Falanga ff63dfad65 Never return to the OpenGamepadUI session when switching to Steam (v0.1.2)
A return file pointing at gamescope-session-opengamepadui is a stale
self-reference: os-session-select writes the current autologin session to the
return file on every switch, and once an exclusive OGPU session is active that
session is OpenGamepadUI itself. Returning to it bounces straight back instead
of landing in Steam, so drop it from the valid return sessions.
2026-08-09 15:43:35 -03:00

127 lines
4.4 KiB
GDScript

extends Plugin
## Adds a "Switch to Steam" button to the power menu on ChimeraOS and SteamOS.
##
## OpenGamepadUI's built-in ChimeraOS platform support adds a "Switch to
## Desktop" button but has no way to return to Steam game mode. This plugin
## adds that missing button so users can round-trip between an exclusive
## OpenGamepadUI session and Steam.
const SESSION_SELECT_PATH := "/usr/lib/os-session-select"
const POWER_MENU_GROUP := "power_menu"
const BUTTON_NAME := "SwitchToSteamButton"
const MAX_ATTEMPTS := 600
# ChimeraOS records the session that was active before OpenGamepadUI took over
# in this file when the OpenGamepadUI session is entered. We return to that
# session instead of hardcoding a name so the autologin config is not mangled.
const RETURN_SESSION_FILE := "/.config/chimeraos-session-return"
const DEFAULT_RETURN_SESSION := "gamescope-session-steam-plus"
# Sessions os-session-select can be told to return to (matching the values it
# writes to the return-session file). Returning to the OpenGamepadUI session is
# deliberately excluded: this plugin only runs inside OpenGamepadUI, so a return
# file pointing at it is a stale self-reference (the last os-session-select run
# recorded whatever session autologin was set to, which is OpenGamepadUI itself
# once an exclusive OGPU session is active) and would just bounce back here.
const RETURN_SESSIONS := [
"gamescope-session-steam-plus",
"gamescope-session-steam",
]
func _ready() -> void:
logger = Log.get_logger("SessionSwitch", Log.LEVEL.INFO)
if not _has_session_switcher():
logger.info("No session switcher script detected: " + SESSION_SELECT_PATH)
return
_add_button_when_ready()
func _add_button_when_ready() -> void:
for i in MAX_ATTEMPTS:
if _try_add_button():
return
await get_tree().process_frame
logger.info("Power menu never became available. Skipping 'Switch to Steam' button.")
func _try_add_button() -> bool:
var power_menu := get_tree().get_first_node_in_group(POWER_MENU_GROUP)
if not power_menu:
return false
var exit_button = power_menu.get("exit_button")
if not exit_button:
return false
var container = exit_button.get_parent()
if not container:
return false
if container.has_node(BUTTON_NAME):
return true
var button_scene := load("res://core/ui/components/card_button.tscn") as PackedScene
if not button_scene:
logger.error("Unable to load card_button scene.")
return true
var button := button_scene.instantiate()
button.name = BUTTON_NAME
button.set("click_focuses", false)
button.set("text", "Switch to Steam")
button.pressed.connect(_switch_session)
container.add_child(button)
container.move_child(button, exit_button.get_index())
var focus_group = power_menu.get("focus_group")
if focus_group and focus_group.has_method("recalculate_focus"):
focus_group.recalculate_focus()
logger.info("Added 'Switch to Steam' button to the power menu.")
return true
## Returns true if the session switcher script is present.
func _has_session_switcher() -> bool:
return FileAccess.file_exists(SESSION_SELECT_PATH)
## Switches back to the session that was active before OpenGamepadUI took
## over, using the OS session switcher.
func _switch_session() -> void:
_restart_inputplumber()
var target := _get_return_session()
var out: Array = []
var code := OS.execute(SESSION_SELECT_PATH, [target], out)
if code != OK:
logger.error("Unable to switch sessions: " + _result_text(out))
## Restarts InputPlumber before switching back so the exclusive OpenGamepadUI
## session does not leave the gamepad intercepted (blocked) for Steam.
## ChimeraOS ships passwordless sudo for exactly this command in its default
## sudoers, so the switch itself remains non-interactive.
func _restart_inputplumber() -> void:
var out: Array = []
var code := OS.execute("sudo", ["-n", "systemctl", "restart", "inputplumber"], out)
if code != OK:
logger.warning("Unable to restart InputPlumber: " + _result_text(out))
## Returns the session recorded by os-session-select before OpenGamepadUI was
## entered, falling back to the ChimeraOS default Steam session.
func _get_return_session() -> String:
var home := OS.get_environment("HOME")
if home.is_empty():
return DEFAULT_RETURN_SESSION
var file := FileAccess.open(home + RETURN_SESSION_FILE, FileAccess.READ)
if file:
var session := file.get_as_text().strip_edges()
if session in RETURN_SESSIONS:
return session
return DEFAULT_RETURN_SESSION
func _result_text(out: Array) -> String:
return out[0] if not out.is_empty() else ""