From 37a731bab6a2d2b8601b86288e4b06e86426ee10 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 4 Aug 2025 18:02:58 -0400 Subject: [PATCH] Experimental: use yad for graphical menus that should work better with orca and Cthulhu screen readers. --- linux-game-manager.sh | 249 +++++++++++++++++++++++++++++++++--------- 1 file changed, 196 insertions(+), 53 deletions(-) diff --git a/linux-game-manager.sh b/linux-game-manager.sh index 7c82dc0..518d493 100755 --- a/linux-game-manager.sh +++ b/linux-game-manager.sh @@ -44,6 +44,158 @@ EOF # Dialog accessibility export DIALOGOPTS='--no-lines --visit-items' +# UI wrapper functions for dialog/yad switching +# Automatically switches between dialog (console) and yad (GUI) based on DISPLAY environment + +# Wrapper function for menu selection +# Usage: ui_menu "title" "backtitle" "text" option1 "description1" option2 "description2" ... +ui_menu() { + local title="$1" + local back_title="$2" + local text="$3" + shift 3 + + if [[ "$dialog_type" == "yad" ]]; then + # Build yad list format: Display only, then map back to value + local yad_list="" + declare -A value_map + while [[ $# -gt 0 ]]; do + local option="$1" + local description="$2" + value_map["$description"]="$option" + if [[ -n "$yad_list" ]]; then + yad_list="$yad_list\n" + fi + yad_list="${yad_list}${description}" + shift 2 + done + + local selected_description + selected_description=$(echo -e "$yad_list" | yad --list \ + --title="$title" \ + --text="$text" \ + --column="Option" \ + --no-headers \ + --selectable-labels \ + --search-column=1 \ + --height=400 \ + --width=600) + # Strip trailing newline, pipe, and any other whitespace + selected_description=$(echo "$selected_description" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/|$//') + + # Return the mapped value + if [[ -n "$selected_description" ]]; then + echo "${value_map["$selected_description"]}" + fi + else + # Build dialog menu format with mapping (same approach as yad) + local dialog_args=() + declare -A value_map + while [[ $# -gt 0 ]]; do + local option="$1" + local description="$2" + value_map["$description"]="$option" + dialog_args+=("$description" "$description") + shift 2 + done + + local selected_description + selected_description=$(dialog --backtitle "$back_title" \ + --title "$title" \ + --no-tags \ + --menu "$text" 0 0 0 \ + "${dialog_args[@]}" \ + --stdout) + + # Return the mapped value + if [[ -n "$selected_description" ]]; then + echo "${value_map["$selected_description"]}" + fi + fi +} + +# Wrapper function for message box +# Usage: ui_msgbox "title" "backtitle" "text" +ui_msgbox() { + local title="$1" + local back_title="$2" + local text="$3" + + if [[ "$dialog_type" == "yad" ]]; then + yad --info \ + --title="$title" \ + --text="$text" \ + --selectable-labels \ + --show-cursor \ + --width=400 + else + dialog --backtitle "$back_title" \ + --title "$title" \ + --msgbox "$text" 0 0 + fi +} + +# Wrapper function for yes/no dialog +# Usage: ui_yesno "title" "backtitle" "text" +ui_yesno() { + local title="$1" + local back_title="$2" + local text="$3" + + if [[ "$dialog_type" == "yad" ]]; then + yad --form \ + --title="$title" \ + --field="$text:LBL" \ + --selectable-labels \ + --button="Yes:0" \ + --button="No:1" \ + --width=400 + else + dialog --backtitle "$back_title" \ + --title "$title" \ + --yesno "$text" 0 0 + fi +} + +# Wrapper function for info box (non-blocking message) +# Usage: ui_infobox "title" "backtitle" "text" +ui_infobox() { + local title="$1" + local back_title="$2" + local text="$3" + + if [[ "$dialog_type" == "yad" ]]; then + # For yad, we'll use a notification since infobox is non-blocking + yad --notification \ + --text="$text" \ + --timeout=3 + else + dialog --backtitle "$back_title" \ + --title "$title" \ + --infobox "$text" 0 0 + fi +} + +# Wrapper function for progress box +# Usage: command | ui_progressbox "title" "text" +ui_progressbox() { + local title="$1" + local text="$2" + + if [[ "$dialog_type" == "yad" ]]; then + yad --progress \ + --title="$title" \ + --text="$text" \ + --pulsate \ + --auto-close \ + --no-buttons \ + --show-cursor \ + --width=400 + else + dialog --title "$title" \ + --progressbox "$text" 20 70 + fi +} # Check for updates check_update() { @@ -58,8 +210,7 @@ check_update() { if [[ "$home" == "$remote" ]]; then return fi - dialog --backtitle "Linux Game manager" \ - --yesno "Updates are available. Would you like to update now?" -1 -1 --stdout || return + ui_yesno "Linux Game Manager" "Linux Game manager" "Updates are available. Would you like to update now?" || return # Store the current commit before pulling local beforePull=$(git rev-parse HEAD) git pull @@ -81,8 +232,7 @@ check_architecture() { return fi done - dialog --backtitle "Linux Game Manager" \ - --infobox "This game is not compatible with $architecture architecture." -1 -1 + ui_infobox "Linux Game Manager" "Linux Game Manager" "This game is not compatible with $architecture architecture." exit 1 } @@ -215,18 +365,15 @@ download() { { if ! curl -L4 -C - --retry 10 --output "${cache}/${dest}" "${i}" ; then echo "Could not download \"$i\"..." exit 1 - fi; } | dialog --backtitle "Linux Game Manager" \ - --progressbox "Downloading \"$dest\" from \"$i\"" -1 -1 + fi; } | ui_progressbox "Linux Game Manager" "Downloading \"$dest\" from \"$i\"" local downloadError=1 case "${dest##*.}" in "pk3"|"zip") - unzip -tq "${cache}/${dest}" | dialog --backtitle "Linux Game Manager" \ - --progressbox "Validating ${dest##*.} file" -1 -1 --stdout + unzip -tq "${cache}/${dest}" | ui_progressbox "Linux Game Manager" "Validating ${dest##*.} file" downloadError=$? ;; "7z") - 7z t "${cache}/${dest}" | dialog --backtitle "Linux Game Manager" \ - --progressbox "Validating 7z file" -1 -1 --stdout + 7z t "${cache}/${dest}" | ui_progressbox "Linux Game Manager" "Validating 7z file" downloadError=$? ;; "exe") @@ -249,8 +396,7 @@ download() { esac if [[ $downloadError -ne 0 ]]; then rm -fv "${cache}/${dest}" - dialog --backtitle "Linux Game Manager" \ - --infobox "Error downloading \"${dest}\". Installation cannot continue." -1 -1 --stdout + ui_infobox "Linux Game Manager" "Linux Game Manager" "Error downloading \"${dest}\". Installation cannot continue." alert exit 1 fi @@ -292,9 +438,7 @@ get_installer() { fi echo "Manual intervention required..." alert - dialog --ok-label "Continue" \ - --backtitle "Linux Game Manager" \ - --msgbox "$message" -1 -1 + ui_msgbox "Linux Game Manager" "Linux Game Manager" "$message" # Search the Desktop and Downloads directories for the installation file for i in ~/Downloads ~/Desktop ; do find $i -type f -name "$1" -exec mv -v {} "${cache}/" \; @@ -369,10 +513,7 @@ game_installer() { # Add donation option at the end menuList+=("Donate" "Donate") # Show game selection dialog - game="$(dialog --backtitle "Game Installer" \ - --clear \ - --no-tags \ - --menu "Please select a game to install" 0 0 0 "${menuList[@]}" --stdout)" + game="$(ui_menu "Game Installer" "Game Installer" "Please select a game to install" "${menuList[@]}")" # Handle selection if [[ -n "$game" ]]; then if [[ "$game" == "Donate" ]]; then @@ -386,8 +527,7 @@ game_installer() { # Source and execute the install script source "$installScript" else - dialog --backtitle "Game Installer" \ - --msgbox "Installation script not found for ${game}" -1 -1 + ui_msgbox "Game Installer" "Game Installer" "Installation script not found for ${game}" exit 1 fi fi @@ -412,18 +552,19 @@ game_removal() { fi ) if [[ ${#menuList} -eq 0 ]]; then - dialog --backtitle "Linux Game Manager" \ - --msgbox "No games found." -1 -1 + ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found." exit 0 fi # Create the menu of installed games + # menuList has alternating full_path, display_name pairs + declare -a menuArgs + for ((i=0; i<${#menuList[@]}; i+=2)); do + menuArgs+=("${menuList[i]}" "${menuList[i+1]}") + done local selectedGame - selectedGame=$(dialog --backtitle "Linux Game Manager" \ - --clear \ - --no-tags \ - --menu "Please select a game to delete" 0 0 0 "${menuList[@]}" --stdout) + selectedGame=$(ui_menu "Linux Game Manager" "Linux Game Manager" "Please select a game to delete" "${menuArgs[@]}") exitCode=$? - [[ $exitCode -ne 0 ]] && exit 0 + [[ $exitCode -ne 0 ]] || [[ -z "$selectedGame" ]] && exit 0 # Get the actual game file paths local gameName="${selectedGame##*/}" gameName="${gameName%.sh}" @@ -435,21 +576,17 @@ game_removal() { gameInstallPath="${installPath}/${gameInstallPath%/*}" if [[ -z "$gameInstallPath" ]] || [[ "${gameInstallPath%%/}" == "$installPath" ]]; then # No install path found, just remove from list - dialog --backtitle "Linux Game Manager" \ - --yesno "This will remove the game from your game list, but will not remove any files. Do you want to continue?" -1 -1 || exit 0 + ui_yesno "Linux Game Manager" "Linux Game Manager" "This will remove the game from your game list, but will not remove any files. Do you want to continue?" || exit 0 # Remove only the .sh symlink rm -fv "${0%/*}/.launch/${gameName}.sh" | \ - dialog --backtitle "Linux Game Manager" \ - --progressbox "Removing game from list..." -1 -1 + ui_progressbox "Linux Game Manager" "Removing game from list..." else # Found install path, can remove game files - dialog --backtitle "Linux Game Manager" \ - --yesno "This will remove the directory \"${gameInstallPath}\" and all of its contents. Do you want to continue?" -1 -1 || exit 0 + ui_yesno "Linux Game Manager" "Linux Game Manager" "This will remove the directory \"${gameInstallPath}\" and all of its contents. Do you want to continue?" || exit 0 # Remove the game directory and symlink { rm -rfv "${gameInstallPath}" rm -fv "${0%/*}/.launch/${gameName}.sh"; - } | dialog --backtitle "Linux Game Manager" \ - --progressbox "Removing game..." -1 -1 + } | ui_progressbox "Linux Game Manager" "Removing game..." fi exit 0 } @@ -469,10 +606,7 @@ game_update() { done menuList+=("Donate" "Donate") menuList+=("Become a Patron" "Become a Patron") - local game="$(dialog --backtitle "Audio Game Updater" \ - --clear \ - --no-tags \ - --menu "Please select a game to update" 0 0 0 "${menuList[@]}" --stdout)" + local game="$(ui_menu "Audio Game Updater" "Audio Game Updater" "Please select a game to update" "${menuList[@]}")" if [[ ${#game} -gt 0 ]]; then if [[ "$game" == "Donate" ]]; then open_url "https://ko-fi.com/stormux" @@ -482,8 +616,10 @@ game_update() { open_url "https://2mb.games/product/2mb-patron/" exit 0 fi + source "${game}" + else + exit 0 fi - source "${game}" run_update exit 0 } @@ -504,17 +640,18 @@ game_launcher() { fi ) if [[ ${#menuList} -eq 0 ]]; then - dialog --backtitle "Linux Game Manager" \ - --msgbox "No games found." 5 20 + ui_msgbox "Linux Game Manager" "Linux Game Manager" "No games found." exit 0 fi - # Create the menu of all games - selectedGame="$(dialog --backtitle "Linux Game Launcher" \ - --clear \ - --no-tags \ - --menu "Please select a game to play" 0 0 0 "${menuList[@]}" --stdout)" + # Create the menu of all games + # menuList has alternating full_path, display_name pairs + declare -a menuArgs + for ((i=0; i<${#menuList[@]}; i+=2)); do + menuArgs+=("${menuList[i]}" "${menuList[i+1]}") + done + selectedGame="$(ui_menu "Linux Game Launcher" "Linux Game Launcher" "Please select a game to play" "${menuArgs[@]}")" local menuCode=$? - if [[ $menuCode -eq 1 ]]; then + if [[ $menuCode -ne 0 ]] || [[ -z "$selectedGame" ]]; then exit 0 fi . "${selectedGame}" @@ -538,11 +675,18 @@ migrate_launcher() { done < <(sed '/^$/d' "${configFile}") # Move the old config file and notify user mv "${configFile}" "${configFile}.bak" - dialog --backtitle "Linux Game manager" --msgbox \ - "Games have been converted to the new launch system.\nThe old game launch information has been moved to ${configFile}.bak\nIf everything works like you expect, feel free to delete ${configFile}" -1 -1 + ui_msgbox "Linux Game Manager" "Linux Game manager" "Games have been converted to the new launch system.\nThe old game launch information has been moved to ${configFile}.bak\nIf everything works like you expect, feel free to delete ${configFile}" } +# Detect dialog interface type BEFORE potentially setting DISPLAY +# This must happen before we modify DISPLAY to preserve console detection +if [[ -z "$DISPLAY" ]]; then + dialog_type="dialog" +else + dialog_type="yad" +fi + # If display isn't set assume we are launching from console and an X environment is running using display :0 # Warning, launching games from console is not recommended. if [[ -z "$DISPLAY" ]]; then @@ -618,8 +762,7 @@ while getopts "${args}" i ; do r) game_removal ;; t) gameCount=$(find .install -type f -iname "*.sh" | wc -l) - dialog --backtitle "Linux Game Manager" \ - --infobox "There are currently ${gameCount} games available." -1 -1 + ui_infobox "Linux Game Manager" "Linux Game Manager" "There are currently ${gameCount} games available." exit 0 ;; u) game_update ;;