Some experimental changes. Mode aware help added. Boosted the original help document so that it contains the keybindings. Fixed super key for rat poison mode, you have to choose super_l or super_r.

This commit is contained in:
Storm Dragon
2025-12-02 00:14:43 -05:00
parent 9999306675
commit 10386114fc
4 changed files with 276 additions and 19 deletions

9
I38.md
View File

@@ -50,6 +50,8 @@ Windows in I38 are arranged in a tabbed layout by default, which means windows t
This is the standard mode for working with applications. Most commands start with your mod key (`MODKEY`).
TOPLEVELKEYBINDINGS
### Ratpoison Mode
Ratpoison mode allows quick access to common actions using shorter key combinations. To enter Ratpoison mode, press `RATPOISONKEY`. After pressing this key, you can execute commands with single keystrokes.
@@ -62,7 +64,7 @@ Common Ratpoison mode commands:
| `e` | Open text editor (TEXTEDITOR) |
| `w` | Launch web browser (BROWSER) |
| `k` | Kill (close) the current window |
| `?` | Show I38 help |
| `F1` | Show ratpoison mode keybindings |
| `Escape` or `Control` + `g` | Exit Ratpoison mode without taking action |
| `Shift` + `c` | Restart Cthulhu screen reader |
| `Shift` + `o` | Restart Orca screen reader |
@@ -89,6 +91,7 @@ In Panel Mode, single keypresses launch different information panels:
| Key | Action |
|-----|--------|
| `F1` | Show panel mode keybindings |
| `w` | Display weather information |
| `Shift` + `w` | Open Magic Wormhole file transfer GUI |
| `s` | Show system information |
@@ -282,7 +285,9 @@ I38 includes clipboard management features:
- **Use the window list**: When you're lost, use `RATPOISONKEY` then `'` to show all windows in the current workspace.
- **Bookmark important websites**: Use `MODKEY` + `Control` + `b` to access bookmarks.
- **Remember the help shortcut**: `MODKEY` + `Shift` + `F1` is your friend when you need guidance.
- **Remember the help shortcuts**:
- `MODKEY` + `Shift` + `F1` opens the complete I38 HTML guide
- Press `F1` in ratpoison or panel mode for quick keybinding reference specific to that mode
- **Let the sound effects guide you**: Pay attention to the audio cues to understand what's happening.
- **Take advantage of OCR**: If an application isn't accessible, try the OCR function.

198
i38.sh
View File

@@ -437,15 +437,18 @@ 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" ]]; do
escapeKey="$(menulist "Ratpoison mode key:" Control+t Control+z Control+Escape Alt+Escape Control+space Super)"
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/Mod4}"
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
@@ -613,8 +616,8 @@ focus_follows_mouse no
# is used in the bar {} block below.
font pango:monospace 8
# I38 help
bindsym \$mod+Shift+F1 exec ${i3Path}/scripts/i38-help.sh
# I38 help - Open comprehensive HTML guide
bindsym \$mod+Shift+F1 exec $webBrowser ${i3Path}/I38.html
# Run dialog
bindsym \$mod+F2 exec ${i3Path}/scripts/run_dialog.sh
@@ -629,19 +632,29 @@ bindsym \$mod+Control+c exec clipster -s
bindsym \$mod+Control+Delete exec --no-startup-id sgtk-bar
# Use pactl to adjust volume in PulseAudio.
# Increase system volume
bindsym \$mod+XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +${volumeJump}% & play -qnG synth 0.03 sin 440
# Decrease system volume
bindsym \$mod+XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -${volumeJump}% & play -qnG synth 0.03 sin 440
# Mute/unmute system volume
bindsym \$mod+XF86AudioMute exec --no-startup-id ${i3Path}/scrip/ts/mute-unmute.sh
# Music player controls
# Requires playerctl.
# Increase music volume
bindsym XF86AudioRaiseVolume exec --no-startup-id ${i3Path}/scripts/music_controler.sh incvol $volumeJump
# Decrease music volume
bindsym XF86AudioLowerVolume exec --no-startup-id ${i3Path}/scripts/music_controler.sh decvol $volumeJump
# Previous track
bindsym XF86AudioPrev exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh prev
# Pause music playback
bindsym XF86AudioMute exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh pause
# Play music
bindsym XF86AudioPlay exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh play
# Get music player information
bindsym \$mod+XF86AudioPlay exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh info
# Stop music playback
bindsym XF86AudioStop exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh stop
# Next track
bindsym XF86AudioNext exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 2000 pad 0 .02" "| sox -np synth 0.03 sin 2000" norm 1.0 vol 0.4 & ${i3Path}/scripts/music_controler.sh next
# start a terminal
@@ -657,8 +670,9 @@ bindsym \$mod+F1 exec --no-startup-id "${i3Path}/scripts/menu.py"
bindsym \$mod+Control+d exec --no-startup-id ${i3Path}/scripts/desktop.sh
# change focus
# alt+tab and alt+shift+tab
# Focus previous window (alt+shift+tab)
bindsym Mod1+Shift+Tab focus left
# Focus next window (alt+tab)
bindsym Mod1+Tab focus right
# enter fullscreen mode for the focused container
@@ -690,32 +704,52 @@ set \$ws9 "9"
set \$ws10 "10"
# switch to workspace
# Switch to workspace 1
bindsym Control+F1 workspace number \$ws1, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 2
bindsym Control+F2 workspace number \$ws2, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 3
bindsym Control+F3 workspace number \$ws3, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 4
bindsym Control+F4 workspace number \$ws4, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 5
bindsym Control+F5 workspace number \$ws5, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 6
bindsym Control+F6 workspace number \$ws6, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 7
bindsym Control+F7 workspace number \$ws7, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 8
bindsym Control+F8 workspace number \$ws8, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 9
bindsym Control+F9 workspace number \$ws9, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# Switch to workspace 10
bindsym Control+F10 workspace number \$ws10, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh
# move focused container to workspace
# Move window to workspace 1
bindsym Control+Shift+F1 move container to workspace number \$ws1, exec spd-say -P important -Cw "moved to workspace 1"
# Move window to workspace 2
bindsym Control+Shift+F2 move container to workspace number \$ws2, exec spd-say -P important -Cw "moved to workspace 2"
# Move window to workspace 3
bindsym Control+Shift+F3 move container to workspace number \$ws3, exec spd-say -P important -Cw "moved to workspace 3"
# Move window to workspace 4
bindsym Control+Shift+F4 move container to workspace number \$ws4, exec spd-say -P important -Cw "moved to workspace 4"
# Move window to workspace 5
bindsym Control+Shift+F5 move container to workspace number \$ws5, exec spd-say -P important -Cw "moved to workspace 5"
# Move window to workspace 6
bindsym Control+Shift+F6 move container to workspace number \$ws6, exec spd-say -P important -Cw "moved to workspace 6"
# Move window to workspace 7
bindsym Control+Shift+F7 move container to workspace number \$ws7, exec spd-say -P important -Cw "moved to workspace 7"
# Move window to workspace 8
bindsym Control+Shift+F8 move container to workspace number \$ws8, exec spd-say -P important -Cw "moved to workspace 8"
# Move window to workspace 9
bindsym Control+Shift+F9 move container to workspace number \$ws9, exec spd-say -P important -Cw "moved to workspace 9"
# Move window to workspace 10
bindsym Control+Shift+F10 move container to workspace number \$ws10, exec spd-say -P important -Cw "moved to workspace 10"
# A mode that will pass all keys except $mod+shift+backspace to the current application.
# Use $mod+shift+backspace to exit the mode.
# Enter bypass mode
bindsym $mod+shift+BackSpace mode "bypass"
mode "bypass" {
# Exit bypass mode.
@@ -725,9 +759,9 @@ bindsym $mod+Shift+BackSpace mode "default"
EOF
# ocr through speech-dispatcher
echo "bindsym ${mod}+F5 exec ${i3Path}/scripts/ocr.py" >> ${i3Path}/config
# Interrupt speech-dispatcher output
# Perform OCR on screen
echo "bindsym ${mod}+F5 exec ${i3Path}/scripts/ocr.py" >> ${i3Path}/config
# Interrupt speech output
echo "bindsym ${mod}+Shift+F5 exec spd-say -C" >> ${i3Path}/config
# Multiple keyboard layouts if requested.
@@ -740,6 +774,9 @@ cat << EOF >> ${i3Path}/config
# Panel mode configuration
bindsym Control+Mod1+Tab mode "panel"
mode "panel" {
# Panel mode keybindings help bound to F1
bindsym F1 exec ${i3Path}/scripts/i38-help-panel.sh, mode "default"
# Weather information bound to w
bindsym w exec --no-startup-id ${i3Path}/scripts/weather.sh, mode "default"
@@ -776,12 +813,11 @@ EOF
# Create ratpoison mode if requested.
if [[ -n "${escapeKey}" ]]; then
cat << EOF >> ${i3Path}/config
# Enter ratpoison mode
bindsym $escapeKey mode "ratpoison"
mode "ratpoison" {
# I38 help bound to ?
bindsym Shift+slash exec ${i3Path}/scripts/i38-help.sh, mode "default"
# I38 HTML documentation bound to F1
bindsym F1 exec $webBrowser ${i3Path}/I38.html, mode "default"
# 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"
# Text editor bound to e
@@ -952,7 +988,7 @@ else
fi)
# First run help documentation
exec --no-startup-id bash -c 'if [[ -f "${i3Path}/firstrun" ]]; then ${webBrowser} "${i3Path}/I38.html"& rm "${i3Path}/firstrun"; fi'
exec --no-startup-id bash -c 'if [[ -f "${i3Path}/firstrun" ]]; then ${webBrowser} "${i3Path}/I38.html" & sleep 1 && rm "${i3Path}/firstrun"; fi'
# If you want to add personal customizations to i3, add them in ${i3Path}/customizations
# It is not overwritten when the config file is recreated.
@@ -986,7 +1022,8 @@ fi
# More readable version of variables.
escapeKey="${escapeKey//Mod1/Alt}"
escapeKey="${escapeKey//Mod4/Super}"
escapeKey="${escapeKey//Super_L/Super Left}"
escapeKey="${escapeKey//Super_R/Super Right}"
mod="${mod//Mod1/Alt}"
mod="${mod//Mod4/Super}"
webBrowser="${webBrowser##*/}"
@@ -994,6 +1031,133 @@ screenReader="${screenReader##*/}"
textEditor="${textEditor##*/}"
fileBrowser="${fileBrowser##*/}"
# Extract and categorize top-level keybindings for documentation
# Generate HTML directly since we're inserting into an already-converted HTML file
# Extract comment+bindsym pairs from config (excluding mode blocks)
declare -a applications workspaceSwitch workspaceMove windowMgmt utilities modes media
# Read the config and extract top-level bindings with their comments
while IFS= read -r line; do
if [[ "$line" =~ ^#.*$ ]]; then
# This is a comment line - save it
lastComment="${line#\# }"
elif [[ "$line" =~ ^bindsym.*$ ]]; then
# This is a bindsym line - pair it with the last comment
keybinding="${line#bindsym }"
keybinding="${keybinding//\\$/}"
# Extract just the key combination (first 1-2 words before 'exec', 'workspace', etc)
keyCombo=$(echo "$keybinding" | awk '{print $1, $2}' | sed 's/workspace.*//' | sed 's/exec.*//' | sed 's/kill.*//' | sed 's/fullscreen.*//' | sed 's/scratchpad.*//' | sed 's/mode.*//' | sed 's/ *$//')
# Clean up key combo
keyCombo="${keyCombo//\$mod/$mod}"
keyCombo="${keyCombo//Mod1/Alt}"
keyCombo="${keyCombo//Mod4/Super}"
# Categorize based on the comment or the action
if [[ "$keybinding" =~ workspace.*number ]] && [[ "$keybinding" =~ Control+F ]]; then
# Switch to workspace
workspaceSwitch+=("$keyCombo|$lastComment")
elif [[ "$keybinding" =~ move.*container.*workspace ]] && [[ "$keybinding" =~ Control+Shift+F ]]; then
# Move to workspace
workspaceMove+=("$keyCombo|$lastComment")
elif [[ "$lastComment" =~ (menu|terminal|editor|browser|Run dialog) ]] || [[ "$keybinding" =~ (menu.py|sensible-terminal|run_dialog) ]]; then
applications+=("$keyCombo|$lastComment")
elif [[ "$lastComment" =~ (focus|window|fullscreen|scratchpad|kill|close) ]] || [[ "$keybinding" =~ (focus|kill|fullscreen|scratchpad) ]]; then
windowMgmt+=("$keyCombo|$lastComment")
elif [[ "$lastComment" =~ (mode|ratpoison|panel|bypass) ]] || [[ "$keybinding" =~ mode ]]; then
modes+=("$keyCombo|$lastComment")
elif [[ "$keybinding" =~ (Audio|music_controler|pactl) ]]; then
media+=("$keyCombo|$lastComment")
else
utilities+=("$keyCombo|$lastComment")
fi
lastComment=""
fi
done < <(sed -n '/^mode "/,/^}$/!p' "${i3Path}/config" | grep -E '^#|^bindsym')
# Build HTML tables
{
echo "<h4>Default Mode Keybindings</h4>"
# Applications
if [[ ${#applications[@]} -gt 0 ]]; then
echo "<p><strong>Applications:</strong></p>"
echo "<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody>"
for item in "${applications[@]}"; do
IFS='|' read -r key desc <<< "$item"
echo "<tr><td><code>$key</code></td><td>$desc</td></tr>"
done
echo "</tbody></table>"
fi
# Workspace Navigation
if [[ ${#workspaceSwitch[@]} -gt 0 ]] || [[ ${#workspaceMove[@]} -gt 0 ]]; then
echo "<p><strong>Workspace Navigation:</strong></p>"
echo "<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody>"
echo "<tr><td><code>Control+F1</code> through <code>F10</code></td><td>Switch to workspace 1-10</td></tr>"
echo "<tr><td><code>Control+Shift+F1</code> through <code>F10</code></td><td>Move window to workspace 1-10</td></tr>"
echo "</tbody></table>"
fi
# Window Management
if [[ ${#windowMgmt[@]} -gt 0 ]]; then
echo "<p><strong>Window Management:</strong></p>"
echo "<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody>"
for item in "${windowMgmt[@]}"; do
IFS='|' read -r key desc <<< "$item"
echo "<tr><td><code>$key</code></td><td>$desc</td></tr>"
done
echo "</tbody></table>"
fi
# Utilities
if [[ ${#utilities[@]} -gt 0 ]]; then
echo "<p><strong>Utilities:</strong></p>"
echo "<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody>"
for item in "${utilities[@]}"; do
IFS='|' read -r key desc <<< "$item"
echo "<tr><td><code>$key</code></td><td>$desc</td></tr>"
done
echo "</tbody></table>"
fi
# Modes
if [[ ${#modes[@]} -gt 0 ]]; then
echo "<p><strong>Modes:</strong></p>"
echo "<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody>"
for item in "${modes[@]}"; do
IFS='|' read -r key desc <<< "$item"
echo "<tr><td><code>$key</code></td><td>$desc</td></tr>"
done
echo "</tbody></table>"
fi
# Media (if any)
if [[ ${#media[@]} -gt 0 ]]; then
echo "<p><strong>Media Controls:</strong></p>"
echo "<table><thead><tr><th>Key</th><th>Action</th></tr></thead><tbody>"
for item in "${media[@]}"; do
IFS='|' read -r key desc <<< "$item"
echo "<tr><td><code>$key</code></td><td>$desc</td></tr>"
done
echo "</tbody></table>"
fi
} > "${i3Path}/toplevel_temp.txt"
# Replace TOPLEVELKEYBINDINGS placeholder with the generated HTML content
awk '
/TOPLEVELKEYBINDINGS/ {
system("cat '"${i3Path}"'/toplevel_temp.txt")
next
}
{ print }
' "${i3Path}/I38.html" > "${i3Path}/I38.html.tmp" && mv "${i3Path}/I38.html.tmp" "${i3Path}/I38.html"
rm -f "${i3Path}/toplevel_temp.txt"
# Customize the html file to the user's choices.
sed -i -e "s|BROWSER|${webBrowser}|g" \
-e "s|MODKEY|${mod}|g" \

43
scripts/i38-help-panel.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# This file is part of I38.
# I38 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
# I38 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
configPath="$(readlink -f "$0")"
configPath="${configPath%/*/*}"
if [[ -f "${configPath}/config" ]]; then
mod="$(grep 'set $mod ' "${configPath}/config" | cut -d ' ' -f3)"
mod="${mod//Mod1/Alt}"
mod="${mod//Mod4/Super}"
mapfile helpText < <(sed -n '/^mode "panel"/,/^}$/p' "${configPath}/config" | \
sed -e '/^mode "panel"/d' \
-e '/^}$/d' \
-e 's/bindsym/Key:/g' \
-e 's/Mod1/Alt/g' \
-e 's/, mode "default"//g' \
-e 's/--no-startup-id //g' \
-e "s/\$mod/$mod/g")
else
exit 1
fi
for i in "${!helpText[@]}" ; do
helpText[$i]="${helpText[$i]//${configPath}\/scripts\//}"
helpText[$i]="${helpText[$i]/.sh/}"
helpText[$i]="${helpText[$i]/.py/}"
done
header="Panel Mode Keybindings\n\nPress Control+Alt+Tab to enter panel mode, then use these shortcuts:\n\n"
helpText=("$header" "${helpText[@]}" "End of panel mode help. Press Control+Home to jump to the beginning.")
echo "${helpText[@]}" | yad --text-info --show-cursor --title "I38 - Panel Mode Help" --button "Close:0" --listen
exit 0

45
scripts/i38-help-rp.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# This file is part of I38.
# I38 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
# I38 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
configPath="$(readlink -f "$0")"
configPath="${configPath%/*/*}"
if [[ -f "${configPath}/config" ]]; then
mod="$(grep 'set $mod ' "${configPath}/config" | cut -d ' ' -f3)"
mod="${mod//Mod1/Alt}"
mod="${mod//Mod4/Super}"
mapfile helpText < <(sed -n '/^mode "ratpoison"/,/^}$/p' "${configPath}/config" | \
sed -e '/^mode "ratpoison"/d' \
-e '/^}$/d' \
-e 's/bindsym/Key:/g' \
-e 's/Mod1/Alt/g' \
-e 's/, mode "default"//g' \
-e 's/--no-startup-id //g' \
-e 's/play \(.*\)& //g' \
-e "s/\$mod/$mod/g")
else
exit 1
fi
for i in "${!helpText[@]}" ; do
helpText[$i]="${helpText[$i]//${configPath}\/scripts\//}"
helpText[$i]="${helpText[$i]/.sh/}"
helpText[$i]="${helpText[$i]/, exec announce*/$'\n'}"
helpText[$i]="${helpText[$i]/, exec spd-say*/$'\n'}"
done
header="Ratpoison Mode Keybindings\n\nEnter ratpoison mode with your configured escape key, then use these shortcuts:\n\n"
helpText=("$header" "${helpText[@]}" "End of ratpoison mode help. Press Control+Home to jump to the beginning.")
echo "${helpText[@]}" | yad --text-info --show-cursor --title "I38 - Ratpoison Mode Help" --button "Close:0" --listen
exit 0