commit f3ae7010009200b5fd5e63f3d9d869bd97d824da Author: Jose Falanga Date: Fri Aug 7 01:34:02 2026 -0300 Add Session Switch plugin: 'Switch to Steam' power menu button for OpenGamepadUI diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69a1846 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.godot +dist diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..52acd14 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +PLUGIN_ID ?= $(shell grep 'plugin\.id' plugin.json | grep -o ':.*' | grep -o '"[^"]\+"' | sed 's/"//g') +PLUGIN_NAME ?= $(shell grep 'plugin\.name' plugin.json | grep -o ':.*' | grep -o '"[^"]\+"' | sed 's/"//g') + +GODOT ?= /usr/bin/godot +OPENGAMEPAD_UI_REPO ?= https://github.com/ShadowBlip/OpenGamepadUI.git +OPENGAMEPAD_UI_BASE ?= ../OpenGamepadUI +EXPORT_PRESETS ?= $(OPENGAMEPAD_UI_BASE)/export_presets.cfg +PLUGINS_DIR := $(OPENGAMEPAD_UI_BASE)/plugins +BUILD_DIR := $(OPENGAMEPAD_UI_BASE)/build +INSTALL_DIR := $(HOME)/.local/share/opengamepadui/plugins + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: dist +dist: build ## Build and package plugin + +.PHONY: build +build: $(PLUGINS_DIR)/$(PLUGIN_ID) export_preset ## Build the plugin + @echo "Exporting plugin package" + cd $(OPENGAMEPAD_UI_BASE) && $(MAKE) addons + mkdir -p dist + touch dist/.gdignore + $(GODOT) --headless \ + --path $(OPENGAMEPAD_UI_BASE) \ + --export-pack "$(PLUGIN_NAME)" \ + plugins/$(PLUGIN_ID)/dist/$(PLUGIN_ID).zip + cd dist && sha256sum $(PLUGIN_ID).zip > $(PLUGIN_ID).zip.sha256.txt + +.PHONY: install +install: dist ## Installs the plugin + mkdir -p "$(INSTALL_DIR)" + cp -r dist/* "$(INSTALL_DIR)" + rm -rf $(INSTALL_DIR)/$(PLUGIN_ID) + @echo "Installed plugin to $(INSTALL_DIR)" + +$(OPENGAMEPAD_UI_BASE): + git clone $(OPENGAMEPAD_UI_REPO) $@ + +$(PLUGINS_DIR)/$(PLUGIN_ID): $(OPENGAMEPAD_UI_BASE) + if ! [ -L $(PLUGINS_DIR)/$(PLUGIN_ID) ]; then ln -s $(PWD) $(PLUGINS_DIR)/$(PLUGIN_ID); fi + +.PHONY: export_preset +export_preset: $(OPENGAMEPAD_UI_BASE) ## Configure plugin export preset + $(eval LAST_PRESET=$(shell grep -oEi '^\[preset\.([0-9]+)]' $(EXPORT_PRESETS) | tail -n 1)) + $(eval LAST_PRESET_NUM=$(shell echo "$(LAST_PRESET)" | grep -oE '([0-9]+)')) + $(eval PRESET_NUM=$(shell echo "$(LAST_PRESET_NUM)+1" | bc)) + @if grep 'name="$(PLUGIN_NAME)"' $(EXPORT_PRESETS) > /dev/null; then \ + echo "Export preset already configured"; \ + else \ + echo "Preset not configured"; \ + sed 's/PRESET_NUM/$(PRESET_NUM)/g; s/PLUGIN_NAME/$(PLUGIN_NAME)/g; s/PLUGIN_ID/$(PLUGIN_ID)/g' export_presets.cfg >> $(EXPORT_PRESETS); \ + fi diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..148a142 --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,42 @@ + +[preset.PRESET_NUM] + +name="PLUGIN_NAME" +platform="Linux/X11" +runnable=false +dedicated_server=false +custom_features="" +export_filter="resources" +export_files=PackedStringArray() +include_filter="plugins/PLUGIN_ID/*" +exclude_filter="addons/*" +export_path="build/PLUGIN_ID.x86_64" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_encryption_key="" + +[preset.PRESET_NUM.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_script=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" diff --git a/plugin.gd b/plugin.gd new file mode 100644 index 0000000..caab25b --- /dev/null +++ b/plugin.gd @@ -0,0 +1,78 @@ +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 + + +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.bind("steam")) + + 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 to the given session using the OS session switcher. +func _switch_session(name: String) -> void: + var out: Array = [] + var code := OS.execute(SESSION_SELECT_PATH, [name], out) + if code != OK: + logger.error("Unable to switch sessions: " + out[0]) diff --git a/plugin.json b/plugin.json new file mode 100644 index 0000000..12385ce --- /dev/null +++ b/plugin.json @@ -0,0 +1,14 @@ +{ + "plugin.id": "session-switch", + "plugin.name": "Session Switch", + "plugin.version": "0.1.0", + "plugin.min-api-version": "1.1.0", + "plugin.link": "https://github.com/YOUR_USERNAME/session-switch", + "plugin.source": "https://github.com/YOUR_USERNAME/session-switch", + "plugin.description": "Adds a 'Switch to Steam' button to the power menu so OpenGamepadUI can return to Steam game mode on ChimeraOS and SteamOS.", + "store.tags": [], + "store.images": [], + "author.name": "First Last", + "author.email": "person@example.com", + "entrypoint": "plugin.gd" +}