Fixed some shellcheck errors. Added the ability to save answers to i38 questions thereby making future configuration regeneration much easier.

This commit is contained in:
Storm Dragon
2025-12-06 20:09:53 -05:00
parent fc3a2abb51
commit e1d6b71121
2 changed files with 271 additions and 118 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
I38_preferences.conf
**/__pycache__/
*.pyc
*.pyo

388
i38.sh
View File

@@ -15,6 +15,7 @@
usingSway=1 # Not by default.
i3Path="${XDG_CONFIG_HOME:-$HOME/.config}/i3"
i3msg="i3-msg"
configFile="${PWD}/I38_preferences.conf"
# Dialog accessibility
export DIALOGOPTS='--no-lines --visit-items'
@@ -28,7 +29,7 @@ done
if ! python3 -c 'import i3ipc' &> /dev/null ; then
missing+=("python-i3ipc")
fi
if [[ -n "${missing}" ]]; then
if [[ ${#missing[@]} -gt 0 ]]; then
echo "Please install the following packages and run this script again:"
echo "${missing[*]}"
exit 1
@@ -175,7 +176,7 @@ yesno() {
# Returns: Yes 0 or No 1
# Args: Question to user.
dialog --clear --title "I38" --yesno "$*" -1 -1 --stdout
echo $?
return $?
}
# Custom application keybinding functions
@@ -333,7 +334,80 @@ addCustomApplication() {
break
done
done
}
}
load_config() {
# Load existing configuration if available
if [[ -f "$configFile" ]]; then
# shellcheck source=/dev/null
source "$configFile"
# Convert kbd string back to array if present
if [[ -n "$kbd" ]]; then
IFS=' ' read -ra kbd <<< "$kbd"
fi
# Reconstruct customApps array from numbered entries
customApps=()
local i=0
local varName
while : ; do
varName="customApp_$i"
if [[ -n "${!varName}" ]]; then
customApps+=("${!varName}")
((i++))
else
break
fi
done
return 0
fi
return 1
}
save_config() {
if yesno "Save this configuration for future runs?"; then
cat > "$configFile" << EOF
# I38 Configuration Preferences
# Generated by i38.sh on $(date)
# Edit this file to change saved preferences or delete to reconfigure from scratch
# Keyboard configuration
escapeKey="$escapeKey"
mod="$mod"
# Keyboard layouts (space-separated)
kbd="${kbd[*]}"
# Volume settings
volumeJump="$volumeJump"
# Application paths
screenReader="$screenReader"
emailClient="$emailClient"
webBrowser="$webBrowser"
textEditor="$textEditor"
fileBrowser="$fileBrowser"
ircClient="$ircClient"
# Boolean settings (0=yes, 1=no)
udiskie="$udiskie"
dex="$dex"
batteryAlert="${batteryAlert:-1}"
brlapi="$brlapi"
sounds="$sounds"
# Custom applications
EOF
# Save custom apps with numbered keys
for i in "${!customApps[@]}"; do
echo "customApp_$i=\"${customApps[$i]}\"" >> "$configFile"
done
dialog --title "I38" --msgbox "Configuration saved to $configFile\n\nYou can edit this file manually or delete it to reconfigure from scratch." 0 0
fi
}
help() {
echo "${0##*/}"
@@ -344,6 +418,9 @@ help() {
for i in "${!command[@]}" ; do
echo "-${i/:/ <parameter>}: ${command[${i}]}"
done | sort
echo ""
echo "Configuration preferences can be saved to I38_preferences.conf in the current directory."
echo "Delete this file to reconfigure from scratch."
exit 0
}
@@ -378,8 +455,7 @@ chmod +x ~/.xinitrc
write_xprofile() {
if [[ -f "$HOME/.xprofile" ]]; then
continue="$(yesno "Would you like to add accessibility variables to your $HOME/.xprofile? Without these, accessibility will be limited or may not work at all. Do you want to continue?")"
if [ "$continue" = "no" ]; then
if ! yesno "Would you like to add accessibility variables to your $HOME/.xprofile? Without these, accessibility will be limited or may not work at all. Do you want to continue?"; then
exit 0
fi
fi
@@ -467,6 +543,13 @@ while getopts "${args}" i ; do
esac
done
# Load saved configuration if available
configLoaded=0
if load_config; then
configLoaded=1
dialog --title "I38" --msgbox "Loaded saved preferences from $configFile\n\nMissing or invalid values will be prompted." 0 0
fi
# Mod1 alt
# Mod4 super
# Mod2 and Mod3 not usually defined.
@@ -475,155 +558,224 @@ done
# Ratpoison mode is enabled by default
export i3Mode=0
# Prevent setting ratpoison mode key to the same as default mode key
while [[ "$escapeKey" == "$mod" ]] || [[ "$escapeKey" =~ ^Super_ && "$mod" == "Mod4" ]] || [[ "$mod" == "Mod4" && "$escapeKey" =~ ^Super_ ]]; do
escapeKey="$(menulist "Ratpoison mode key:" Control+t Control+z Control+Escape Alt+Escape Control+space "Super Left" "Super Right")"
escapeKey="${escapeKey//Alt/Mod1}"
escapeKey="${escapeKey//Super Left/Super_L}"
escapeKey="${escapeKey//Super Right/Super_R}"
mod="$(menulist "I3 mod key, for top level bindings:" Alt Super)"
mod="${mod//Alt/Mod1}"
mod="${mod//Super/Mod4}"
if [ "$escapeKey" == "$mod" ]; then
dialog --title "I38" --msgbox "Ratpoison and mod key cannot be the same key." -1 -1
elif [[ "$escapeKey" =~ ^Super_ && "$mod" == "Mod4" ]]; then
dialog --title "I38" --msgbox "Ratpoison mode key cannot be a Super key when mod key is Super." -1 -1
fi
done
# Multiple keyboard layouts
if [[ $(yesno "Do you want to use multiple keyboard layouts?") -eq 0 ]]; then
unset kbd
while : ; do
kbd+=("$(keyboard_menu)") || break
if [[ -z "$escapeKey" ]] || [[ -z "$mod" ]]; then
while [[ "$escapeKey" == "$mod" ]] || [[ "$escapeKey" =~ ^Super_ && "$mod" == "Mod4" ]] || [[ "$mod" == "Mod4" && "$escapeKey" =~ ^Super_ ]]; do
escapeKey="$(menulist "Ratpoison mode key:" Control+t Control+z Control+Escape Alt+Escape Control+space "Super Left" "Super Right")"
escapeKey="${escapeKey//Alt/Mod1}"
escapeKey="${escapeKey//Super Left/Super_L}"
escapeKey="${escapeKey//Super Right/Super_R}"
mod="$(menulist "I3 mod key, for top level bindings:" Alt Super)"
mod="${mod//Alt/Mod1}"
mod="${mod//Super/Mod4}"
if [ "$escapeKey" == "$mod" ]; then
dialog --title "I38" --msgbox "Ratpoison and mod key cannot be the same key." -1 -1
elif [[ "$escapeKey" =~ ^Super_ && "$mod" == "Mod4" ]]; then
dialog --title "I38" --msgbox "Ratpoison mode key cannot be a Super key when mod key is Super." -1 -1
fi
done
fi
# Multiple keyboard layouts
if [[ ${#kbd[@]} -eq 0 ]]; then
if yesno "Do you want to use multiple keyboard layouts?"; then
unset kbd
while : ; do
kbd+=("$(keyboard_menu)") || break
done
fi
fi
# Volume jump
volumeJump=$(rangebox "How much should pressing the volume keys change the volume?" 1 15 5)
if [[ -z "$volumeJump" ]]; then
volumeJump=$(rangebox "How much should pressing the volume keys change the volume?" 1 15 5)
fi
# Screen Reader
unset programList
for i in cthulhu orca ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
if [[ -z "$screenReader" ]] || ! command -v "$screenReader" &> /dev/null; then
unset programList
for i in cthulhu orca ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
screenReader="$(menulist ":Screen Reader" $programList)"
else
screenReader="${programList/#-/}"
fi
done
if [ "$programList" != "${programList// /}" ]; then
screenReader="$(menulist ":Screen Reader" $programList)"
export screenReader="$(command -v $screenReader)"
else
screenReader="${programList/#-/}"
# Validate and export existing preference
export screenReader
fi
export screenReader="$(command -v $screenReader)"
# Email client
unset programList
for i in betterbird evolution thunderbird ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
if [[ -z "$emailClient" ]] || ! command -v "$emailClient" &> /dev/null; then
unset programList
for i in betterbird evolution thunderbird ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
emailClient="$(menulist "Email client:" $programList)"
else
emailClient="${programList/#-/}"
fi
done
if [ "$programList" != "${programList// /}" ]; then
emailClient="$(menulist "Email client:" $programList)"
export emailClient="$(command -v $emailClient)"
else
emailClient="${programList/#-/}"
# Validate and export existing preference
export emailClient
fi
export emailClient="$(command -v $emailClient)"
# Web browser
unset programList
for i in brave chromium epiphany firefox google-chrome-stable google-chrome-unstable microsoft-edge-stable microsoft-edge-beta microsoft-edge-dev midori seamonkey vivaldi ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
if [[ -z "$webBrowser" ]] || ! command -v "$webBrowser" &> /dev/null; then
unset programList
for i in brave chromium epiphany firefox google-chrome-stable google-chrome-unstable microsoft-edge-stable microsoft-edge-beta microsoft-edge-dev midori seamonkey vivaldi ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
webBrowser="$(menulist "Web browser:" $programList)"
else
webBrowser="${programList/#-/}"
fi
done
if [ "$programList" != "${programList// /}" ]; then
webBrowser="$(menulist "Web browser:" $programList)"
export webBrowser="$(command -v $webBrowser)"
else
webBrowser="${programList/#-/}"
# Validate and export existing preference
export webBrowser
fi
export webBrowser="$(command -v $webBrowser)"
# Text editor
unset programList
for i in emacs geany gedit kate kwrite l3afpad leafpad libreoffice mousepad pluma ; do
if hash ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
if [[ -z "$textEditor" ]] || ! command -v "$textEditor" &> /dev/null; then
unset programList
for i in emacs geany gedit kate kwrite l3afpad leafpad libreoffice mousepad pluma ; do
if hash ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
textEditor="$(menulist "Text editor:" $programList)"
else
textEditor="${programList/#-/}"
fi
export textEditor="$(command -v $textEditor)"
else
programList="$i"
# Validate and export existing preference
export textEditor
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
textEditor="$(menulist "Text editor:" $programList)"
else
textEditor="${programList/#-/}"
fi
export textEditor="$(command -v $textEditor)"
# File browser
# Configure file browser
unset programList
for i in caja nemo nautilus pcmanfm pcmanfm-qt thunar ; do
if hash ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
if [[ -z "$fileBrowser" ]] || ! command -v "$fileBrowser" &> /dev/null; then
unset programList
for i in caja nemo nautilus pcmanfm pcmanfm-qt thunar ; do
if hash ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
fileBrowser="$(menulist "File browser:" $programList)"
else
fileBrowser="${programList/#-/}"
fi
done
if [ "$programList" != "${programList// /}" ]; then
fileBrowser="$(menulist "File browser:" $programList)"
export fileBrowser="$(command -v $fileBrowser)"
else
fileBrowser="${programList/#-/}"
# Validate and export existing preference
export fileBrowser
fi
export fileBrowser="$(command -v $fileBrowser)"
# IRC client
unset programList
for i in albikirc Albikirc access-irc ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
if [[ -z "$ircClient" ]] || ! command -v "$ircClient" &> /dev/null; then
unset programList
for i in albikirc Albikirc access-irc ; do
if command -v ${i/#-/} &> /dev/null ; then
if [ -n "$programList" ]; then
programList="$programList $i"
else
programList="$i"
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
ircClient="$(menulist "IRC client:" $programList)"
else
ircClient="${programList/#-/}"
fi
export ircClient="$(command -v $ircClient)"
else
# Validate and export existing preference
export ircClient
fi
# Auto mount removable media
if [[ -z "$udiskie" ]]; then
udiskie=1
if command -v udiskie &> /dev/null ; then
if yesno "Would you like removable drives to automatically mount when plugged in?"; then
export udiskie=0
else
programList="$i"
export udiskie=1
fi
fi
done
if [ "$programList" != "${programList// /}" ]; then
ircClient="$(menulist "IRC client:" $programList)"
else
ircClient="${programList/#-/}"
fi
export ircClient="$(command -v $ircClient)"
# Auto mount removable media
udiskie=1
if command -v udiskie &> /dev/null ; then
export udiskie=$(yesno "Would you like removable drives to automatically mount when plugged in?")
fi
# Auto start with dex
dex=1
if command -v dex &> /dev/null ; then
export dex=$(yesno "Would you like to autostart applications with dex?")
if [[ -z "$dex" ]]; then
dex=1
if command -v dex &> /dev/null ; then
if yesno "Would you like to autostart applications with dex?"; then
export dex=0
else
export dex=1
fi
fi
fi
if [[ $dex -eq 0 ]]; then
dex -t "${XDG_CONFIG_HOME:-${HOME}/.config}/autostart" -c $(command -v $screenReader)
fi
if command -v acpi &> /dev/null ; then
batteryAlert=1
batteryAlert=$(yesno "Do you want low battery notifications?")
if [[ -z "$batteryAlert" ]]; then
if command -v acpi &> /dev/null ; then
if yesno "Do you want low battery notifications?"; then
batteryAlert=0
else
batteryAlert=1
fi
fi
fi
if [[ -z "$brlapi" ]]; then
if yesno "Do you want to use a braille display with ${screenReader##*/}?"; then
brlapi=0
else
brlapi=1
fi
fi
if [[ -z "$sounds" ]]; then
if yesno "Do you want window event sounds?"; then
sounds=0
else
sounds=1
fi
fi
brlapi=1
brlapi=$(yesno "Do you want to use a braille display with ${screenReader##*/}?")
sounds=1
sounds=$(yesno "Do you want window event sounds?")
# Custom applications for ratpoison mode
addCustomApplication
if [[ ${#customApps[@]} -eq 0 ]]; then
addCustomApplication
fi
# Save configuration if requested (only on first run)
if [[ $configLoaded -eq 0 ]]; then
save_config
fi
if [[ -d "${i3Path}" ]]; then
yesno "This will replace your existing configuration at ${i3Path}. Do you want to continue?" || exit 0
@@ -823,7 +975,7 @@ echo "bindsym ${mod}+Shift+F5 exec spd-say -C" >> ${i3Path}/config
# Multiple keyboard layouts if requested.
if [[ ${#kbd[@]} -gt 1 ]]; then
echo "bindsym Mod4+space exec ${i3Path}/scripts/keyboard.sh cycle ${kbd[@]}" >> ${i3Path}/config
echo "bindsym Mod4+space exec ${i3Path}/scripts/keyboard.sh cycle ${kbd[*]}" >> ${i3Path}/config
fi
# Create panel mode
@@ -887,7 +1039,7 @@ mode "ratpoison" {
# Ratpoison mode keybindings help bound to F1
bindsym F1 exec ${i3Path}/scripts/i38-help-rp.sh, mode "default"
# Terminal emulator bound to c
bindsym c exec $sensibleTerminal, mode "default"
bindsym c exec ${i3Path}/scripts/i3-sensible-terminal.sh, mode "default"
# Text editor bound to e
bindsym e exec $textEditor, mode "default"
$(if [[ ${#fileBrowser} -gt 3 ]]; then