From d9134dd1a5c0b606c680749c0b73c0b48e033bfe Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Nov 2026 15:36:58 -0500 Subject: [PATCH] Add desktop shortcuts for installed games. --- .includes/desktop.sh | 54 +++++++++++++++++++++++++++++++++++ .includes/dialog-interface.sh | 15 ++++++++-- README.md | 7 +++++ audiogame-manager.sh | 19 ++++++++++-- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/.includes/desktop.sh b/.includes/desktop.sh index 50542a8..8bc1a8e 100644 --- a/.includes/desktop.sh +++ b/.includes/desktop.sh @@ -1,5 +1,59 @@ # shellcheck shell=bash +# Escape a Desktop Entry string value without interpreting its contents. +desktop_entry_value() { + local value="$1" + value="${value//\\/\\\\}" + value="${value//$'\n'/\\n}" + value="${value//$'\r'/\\r}" + value="${value//$'\t'/\\t}" + printf '%s' "$value" +} + +# Exec arguments have their own quoting, followed by Desktop Entry escaping. +desktop_exec_argument() { + local value="$1" + value="${value//%/%%}" + value="${value//\\/\\\\}" + value="${value//\"/\\\"}" + value="${value//\$/\\\$}" + value="${value//\`/\\\`}" + desktop_entry_value "\"${value}\"" +} + +game_desktop_shortcut() { + local gameTitle="$1" + local selectedGame="$2" + local fileName="${gameTitle//\//_}" + fileName="${fileName//[$'\n\r\t']/_}" + local desktopFile="${HOME}/Desktop/audiogame-manager-${fileName}.desktop" + local launcherPath + # shellcheck disable=SC2154 # Set by the main script before sourcing helpers. + launcherPath="$(readlink -e "${scriptDir}/audiogame-manager.sh")" || return 1 + + if ! mkdir -p "${HOME}/Desktop"; then + agm_msgbox "Desktop Shortcut" "" "Could not create ~/Desktop." + return 1 + fi + # Refuse to overwrite an existing shortcut, including symbolic links. + if ! ( + set -o noclobber + printf '%s\n' '[Desktop Entry]' \ + "Name=$(desktop_entry_value "$gameTitle")" \ + "Exec=$(desktop_exec_argument "$launcherPath") -l $(desktop_exec_argument "$selectedGame")" \ + 'Terminal=true' 'Type=Application' 'Categories=Game;' \ + > "$desktopFile" + ); then + agm_msgbox "Desktop Shortcut" "" "Could not create ${desktopFile}. Check permissions and whether a shortcut already exists." + return 1 + fi + if ! chmod +x "$desktopFile"; then + agm_msgbox "Desktop Shortcut" "" "Could not make ${desktopFile} executable." + return 1 + fi + agm_msgbox "Desktop Shortcut" "" "Created ${desktopFile}." +} + # Create desktop launcher file desktop_launcher() { local desktopFile="${HOME}/audiogame-manager.desktop" diff --git a/.includes/dialog-interface.sh b/.includes/dialog-interface.sh index a1bc1e1..210d9da 100644 --- a/.includes/dialog-interface.sh +++ b/.includes/dialog-interface.sh @@ -83,9 +83,9 @@ agm_menu() { fi } -# Specialized game launcher menu with "Read Documentation" button +# Specialized game launcher menu with documentation and desktop shortcut buttons # Usage: agm_game_menu "title" "backtitle" "text" option1 "description1" option2 "description2" ... -# Returns: exit code 0=Launch, 1=Cancel, 2/3=Documentation (yad/dialog) +# Returns: 0=Launch, 1=Cancel, 2/3=Documentation (yad/dialog), 4=Shortcut agm_game_menu() { local title="$1" local backTitle="$2" @@ -93,7 +93,7 @@ agm_game_menu() { shift 3 if [[ "$dialogType" == "yad" ]]; then - # Use custom buttons to match dialog behavior (launch/doc/cancel) + # Use custom buttons to match dialog behavior. local yadList="" declare -A valueMap while [[ $# -gt 0 ]]; do @@ -119,6 +119,7 @@ agm_game_menu() { --width=600 \ --button="Launch:0" \ --button="Read Documentation:2" \ + --button="Desktop Shortcut:4" \ --button="Cancel:1") local menuCode=$? @@ -145,12 +146,20 @@ agm_game_menu() { --title "$title" \ --extra-button \ --extra-label "Read Documentation" \ + --help-button \ + --help-label "Desktop Shortcut" \ --no-tags \ --menu "$text" 0 0 0 \ "${dialogArgs[@]}" \ --stdout) local menuCode=$? + # Dialog's help button prefixes the selected tag with "HELP ". + if [[ $menuCode -eq 2 ]]; then + selectedDescription="${selectedDescription#HELP }" + menuCode=4 + fi + # Return the mapped value if [[ -n "$selectedDescription" ]]; then echo "${valueMap["$selectedDescription"]}" diff --git a/README.md b/README.md index 23c0802..ad070b7 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,13 @@ shortcuts with `-D` to use it. With no arguments, the list of installed games will be displayed. In all the menus, both installer and launcher, use the up and down arrow keys to find the game you want. Press enter on the game to perform the action. For more detailed help, try the -h flag. +In the game launch menu, select a game and choose **Desktop Shortcut** to create +an executable `.desktop` file in `~/Desktop`. The shortcut calls Audiogame +Manager with `-l` to launch that game directly. Existing files are left intact, +and the game menu opens again after creating the shortcut. This button is +available in both the graphical and console menus. Your desktop environment +may ask you to trust the shortcut before launching it. + Use `./audiogame-manager.sh -s` to open the launcher without automatically replacing NVDA controller DLLs with the nvda2speechd versions. This also works with launch options, for example `./audiogame-manager.sh -s -l bottle-name`. diff --git a/audiogame-manager.sh b/audiogame-manager.sh index 51b71af..28c752c 100755 --- a/audiogame-manager.sh +++ b/audiogame-manager.sh @@ -793,7 +793,7 @@ game_launcher() { menuList+=("Donate" "Donate") menuList+=("Become a Patron" "Become a Patron") - # Loop to handle documentation views + # Return to the game list after documentation or shortcut creation. local selectedGame="" local menuCode=0 while true; do @@ -802,6 +802,18 @@ game_launcher() { if [[ $menuCode -eq 1 ]] || [[ $menuCode -eq 255 ]]; then exit 0 + elif [[ $menuCode -eq 4 ]]; then + if [[ "$selectedGame" == "Donate" || "$selectedGame" == "Become a Patron" ]]; then + agm_msgbox "Desktop Shortcut" "" "Please select an installed game." + elif [[ -n "$selectedGame" ]]; then + game=() + create_game_array "$selectedGame" + if [[ ${#game[@]} -gt 0 ]]; then + game_desktop_shortcut "${game[2]}" "$selectedGame" + fi + game=() + fi + continue elif [[ $menuCode -eq 2 ]] || [[ $menuCode -eq 3 ]]; then # Documentation button pressed if [[ -n "$selectedGame" ]]; then @@ -811,9 +823,10 @@ game_launcher() { fi # Return to menu after viewing docs continue - else - # dialog mode with OK (0) - proceed with launch + elif [[ $menuCode -eq 0 ]]; then break + else + exit 0 fi done