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.
127 lines
4.4 KiB
GDScript
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 ""
|