Initial low vision features added. xzoom needs to be installed for magnification. This first implementation is probably terrible, feedback welcome for improvement. Screen lock can now happen when I38 starts. As a reminder, it's not as secure as one of the system lockers, but it is screen reader accessible and should keep casual snoopers thwarted.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
# Welcome to I38 - Accessible i3 Window Manager
|
||||
|
||||
> **Note:** This help guide has been tailored to your specific configuration. You've chosen **@WEBBROWSER@** as your web browser, **@MODKEY@** as your mod key, and you're using the **@SCREENREADER@** screen reader.
|
||||
> **Note:** This help guide has been tailored to your specific configuration. You've chosen **@WEBBROWSER@** as your web browser, **@MODKEY@** as your mod key, and your screen reader setting is **@SCREENREADER@**.
|
||||
|
||||
## Introduction to I38
|
||||
|
||||
I38 is a configuration for the i3 window manager that makes it more accessible for blind people. It features audio feedback, screen reader integration, and keyboard shortcuts designed for non-visual navigation.
|
||||
I38 is a configuration for the i3 window manager that makes it more accessible for blind and low-vision people. It features audio feedback, optional screen reader integration, low-vision-friendly visual settings, and keyboard shortcuts designed for efficient navigation.
|
||||
|
||||
Unlike traditional desktop environments like GNOME or MATE, i3 is a tiling window manager, which means windows are arranged in a non-overlapping layout. This can be more efficient to navigate by keyboard, as windows are organized in a predictable structure.
|
||||
|
||||
@@ -67,9 +67,9 @@ Common Ratpoison mode commands:
|
||||
| `k` | Kill (close) the current window |
|
||||
| `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 |
|
||||
| `Shift` + `t` | Toggle screen reader |
|
||||
| `Shift` + `c` | Restart Cthulhu screen reader, if installed |
|
||||
| `Shift` + `o` | Restart Orca screen reader, if installed |
|
||||
| `Shift` + `t` | Toggle screen reader, if one was configured |
|
||||
| `Control` + `;` | Reload I38 configuration |
|
||||
| `Control` + `q` | Exit i3 (log out) |
|
||||
| `!` | Open run dialog |
|
||||
@@ -200,10 +200,10 @@ waytray is an optional dependency. If installed, I38 will automatically configur
|
||||
|
||||
### Screen Reader
|
||||
|
||||
I38 is configured to work with your screen reader (@SCREENREADER@). The screen reader will provide spoken feedback about what's happening on screen so long as there is a window. If you don't have a window open and need to change something @SCREENREADER@ related, press Control+Alt+d to bring up the desktop, then screen reader keys should work.
|
||||
If you configured a screen reader, I38 uses @SCREENREADER@. The screen reader will provide spoken feedback about what's happening on screen so long as there is a window. If you don't have a window open and need to change something screen-reader related, press Control+Alt+d to bring up the desktop, then screen reader keys should work.
|
||||
|
||||
- Toggle screen reader: `@RATPOISONKEY@` then `Shift` + `t`
|
||||
- Restart screen reader: `@RATPOISONKEY@` then `Shift` + `o` (for Orca) or `Shift` + `c` (for Cthulhu)
|
||||
- Restart screen reader: `@RATPOISONKEY@` then `Shift` + `o` (for Orca, if installed) or `Shift` + `c` (for Cthulhu, if installed)
|
||||
- Interrupt speech: `@MODKEY@` + `Shift` + `F5`
|
||||
|
||||
*GNOME/MATE comparison:* GNOME uses Orca by default with its own keyboard shortcuts. I38 integrates screen readers more deeply with the window manager.
|
||||
@@ -212,6 +212,17 @@ I38 is configured to work with your screen reader (@SCREENREADER@). The screen r
|
||||
|
||||
If you've enabled braille display support during setup, I38 will start XBrlAPI automatically to provide braille output from your screen reader.
|
||||
|
||||
### Low-Vision Support
|
||||
|
||||
During setup, I38 asks for the window title font size used by i3. The default is larger than standard i3 so window titles and tab labels are easier to see.
|
||||
|
||||
- I38 applies a larger mouse cursor size at startup when xrdb is available.
|
||||
- Find the mouse pointer: `@MODKEY@` + `Control` + `m`
|
||||
- Start xzoom magnifier, if installed when your i3 config was generated: `@MODKEY@` + `Control` + `z`
|
||||
- Adjust screen brightness or enable the screen curtain, if xrandr is available: In Ratpoison mode, `Alt` + `s`
|
||||
|
||||
I38 keeps windows in a tabbed layout by default. This gives the focused app most of the screen and reduces visual clutter, but it also means switching windows is usually done with `Alt` + `Tab`, the window list, or workspace keys rather than by clicking overlapping windows.
|
||||
|
||||
### OCR (Optical Character Recognition)
|
||||
|
||||
If required dependencies are installed, you can use OCR to read text from images or inaccessible applications:
|
||||
@@ -227,6 +238,8 @@ If you enabled the I38 privacy screen lock during setup, press `Control` + `Alt`
|
||||
|
||||
When a screen lock PIN is configured, I38 can also autolock after an idle timeout. The default is never. Autolock is X11/i3-only and requires `xprintidle`; if `playerctl` is installed, I38 will avoid autolocking during likely video playback.
|
||||
|
||||
If you chose to lock the screen when I38 starts, I38 launches the privacy screen shortly after i3 startup. Startup lock is meant to prevent casual snooping, not to secure the system against a determined user.
|
||||
|
||||
### Sound Effects
|
||||
|
||||
I38 provides audio feedback for many actions:
|
||||
@@ -316,6 +329,8 @@ In Ratpoison mode, these are also available with Alt+Shift combinations:
|
||||
- Check battery status: In Ratpoison mode, `Alt` + `b`
|
||||
- Check game controller status: In Ratpoison mode, `g`
|
||||
- Adjust screen brightness (if xrandr is available): In Ratpoison mode, `Alt` + `s`
|
||||
- Find the mouse pointer: `@MODKEY@` + `Control` + `m`
|
||||
- Start xzoom magnifier, if installed: `@MODKEY@` + `Control` + `z`
|
||||
|
||||
*GNOME/MATE comparison:* These functions are typically available through system menus or indicators in GNOME/MATE.
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ Optional features use these packages when installed:
|
||||
- xclip: Clipboard support
|
||||
- xfce4-notifyd: For sending notifications. Replaces notification-daemon
|
||||
- xorg-setxkbmap: [optional] for multiple keyboard layouts
|
||||
- xzoom: [optional] lightweight X11 magnifier bound by I38 when installed
|
||||
|
||||
### AI Assistant (Optional)
|
||||
- python-requests: For Ollama integration
|
||||
@@ -66,6 +67,8 @@ I38 will try to detect your browser, file manager, and text editor and present y
|
||||
|
||||
Ratpoison mode is now enabled by default for better accessibility and ease of use.
|
||||
|
||||
During setup, the screen reader picker includes Cthulhu, Orca, and None. If no supported screen reader is installed, I38 assumes None and skips screen-reader startup. Setup also asks for the generated i3 window-title font size, defaulting to 12. I38 applies a larger mouse cursor size at startup when xrdb is available. If xzoom is installed when the i3 config is generated, I38 binds it as a lightweight magnifier. I38 also includes a keybinding to move the mouse pointer to the center of the focused window.
|
||||
|
||||
## GTK Application Sound Themes (Optional)
|
||||
|
||||
This section is completely optional and separate from I38's window manager sounds. If you want GTK-based applications (like file managers, terminal emulators, text editors, etc.) to play sounds for their own events (button presses, menu navigation, dialog boxes, etc.), you can configure a GTK sound theme.
|
||||
|
||||
@@ -324,6 +324,7 @@ kbd="${kbd[*]}"
|
||||
|
||||
# Volume settings
|
||||
volumeJump="$volumeJump"
|
||||
i3FontSize="${i3FontSize:-12}"
|
||||
|
||||
# Application paths
|
||||
screenReader="$screenReader"
|
||||
@@ -344,6 +345,7 @@ sounds="$sounds"
|
||||
# Screen lock
|
||||
screenlockPinHash="$screenlockPinHash"
|
||||
screenlockAutolockSeconds="${screenlockAutolockSeconds:-0}"
|
||||
screenlockOnStartup="${screenlockOnStartup:-1}"
|
||||
|
||||
# Personal mode
|
||||
personalModeEnabled="${personalModeEnabled:-0}"
|
||||
@@ -514,6 +516,15 @@ write_desktop_shortcuts_template() {
|
||||
EOF
|
||||
}
|
||||
|
||||
write_i38_xresources() {
|
||||
local xresourcesFile="${i3Path}/I38.Xresources"
|
||||
|
||||
cat << 'EOF' > "$xresourcesFile"
|
||||
! Visual settings generated by I38
|
||||
Xcursor.size: 48
|
||||
EOF
|
||||
}
|
||||
|
||||
write_waytray_config() {
|
||||
# Only create config if waytray binaries are detected
|
||||
if ! command -v waytray-daemon &> /dev/null || ! command -v waytray &> /dev/null ; then
|
||||
@@ -641,20 +652,32 @@ fi
|
||||
if [[ -z "$volumeJump" ]]; then
|
||||
volumeJump=$(rangebox "How much should pressing the volume keys change the volume?" 1 15 5)
|
||||
fi
|
||||
if [[ -z "$i3FontSize" ]] || [[ ! "$i3FontSize" =~ ^[0-9]+$ ]]; then
|
||||
configChanged=1
|
||||
i3FontSize=$(rangebox "How large should I38 window title text be?" 8 24 12)
|
||||
if [[ ! "$i3FontSize" =~ ^[0-9]+$ ]]; then
|
||||
i3FontSize=12
|
||||
fi
|
||||
fi
|
||||
# Screen Reader
|
||||
if [[ -z "$screenReader" ]] || ! command -v "$screenReader" &> /dev/null; then
|
||||
if [[ -z "$screenReader" ]] || { [[ "$screenReader" != "none" ]] && ! command -v "$screenReader" &> /dev/null; }; then
|
||||
programList=()
|
||||
for i in cthulhu orca ; do
|
||||
if command -v "${i/#-/}" &> /dev/null ; then
|
||||
programList+=("$i")
|
||||
fi
|
||||
done
|
||||
programList+=("None")
|
||||
if [[ ${#programList[@]} -gt 1 ]]; then
|
||||
screenReader="$(menulist ":Screen Reader" "${programList[@]}")"
|
||||
else
|
||||
screenReader="${programList[0]#-}"
|
||||
fi
|
||||
screenReader="$(command -v "$screenReader")"
|
||||
if [[ "$screenReader" == "None" ]]; then
|
||||
screenReader="none"
|
||||
else
|
||||
screenReader="$(command -v "$screenReader")"
|
||||
fi
|
||||
export screenReader
|
||||
else
|
||||
# Validate and export existing preference
|
||||
@@ -803,7 +826,7 @@ if [[ -z "$dex" ]]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ $dex -eq 0 ]]; then
|
||||
if [[ $dex -eq 0 ]] && [[ "$screenReader" != "none" ]] && [[ -n "$screenReader" ]]; then
|
||||
dex -t "${XDG_CONFIG_HOME:-${HOME}/.config}/autostart" -c "$(command -v "$screenReader")"
|
||||
fi
|
||||
if [[ -z "$batteryAlert" ]]; then
|
||||
@@ -815,7 +838,9 @@ if [[ -z "$batteryAlert" ]]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$brlapi" ]]; then
|
||||
if [[ "$screenReader" == "none" ]] || [[ -z "$screenReader" ]]; then
|
||||
brlapi=1
|
||||
elif [[ -z "$brlapi" ]]; then
|
||||
if yesno "Do you want to use a braille display with ${screenReader##*/}?"; then
|
||||
brlapi=0
|
||||
else
|
||||
@@ -867,6 +892,14 @@ if [[ -z "$screenlockPinHash" ]]; then
|
||||
fi
|
||||
fi
|
||||
if [[ -n "$screenlockPinHash" ]]; then
|
||||
if [[ -z "${screenlockOnStartup+x}" ]]; then
|
||||
configChanged=1
|
||||
if yesno "Lock the screen automatically when I38 starts? This is useful for privacy, but if your screen reader or display setup fails you may need to enter the PIN without feedback."; then
|
||||
screenlockOnStartup=0
|
||||
else
|
||||
screenlockOnStartup=1
|
||||
fi
|
||||
fi
|
||||
if command -v xprintidle &> /dev/null; then
|
||||
if [[ -z "${screenlockAutolockSeconds+x}" ]] || [[ ! "$screenlockAutolockSeconds" =~ ^[0-9]+$ ]]; then
|
||||
configChanged=1
|
||||
@@ -877,6 +910,7 @@ if [[ -n "$screenlockPinHash" ]]; then
|
||||
fi
|
||||
else
|
||||
screenlockAutolockSeconds=0
|
||||
screenlockOnStartup=1
|
||||
fi
|
||||
# Personal mode
|
||||
personalModeExists=1
|
||||
@@ -915,6 +949,7 @@ write_waytray_config
|
||||
mkdir -p "${i3Path}"
|
||||
write_i38_version
|
||||
write_desktop_shortcuts_template
|
||||
write_i38_xresources
|
||||
# Move scripts into place
|
||||
cp -rv scripts/ "${i3Path}/" | dialog --backtitle "I38" --progressbox "Moving scripts into place and writing config..." -1 -1
|
||||
apply_screenlock_pin
|
||||
@@ -943,9 +978,8 @@ workspace_layout tabbed
|
||||
# this fixes some issues in some games that require focus and pause when focus is moved via mouse accidentally
|
||||
focus_follows_mouse no
|
||||
|
||||
# Font for window titles. Will also be used by the bar unless a different font
|
||||
# is used in the bar {} block below.
|
||||
font pango:monospace 8
|
||||
# Font for window titles and tab labels.
|
||||
font pango:monospace ${i3FontSize}
|
||||
|
||||
# Window rules
|
||||
# Fix Wine window issues - improves stability and prevents lockups
|
||||
@@ -963,6 +997,15 @@ bindsym \$mod+Control+b exec ${i3Path}/scripts/bookmarks.sh
|
||||
# Clipboard manager
|
||||
bindsym \$mod+Control+c exec ${i3Path}/scripts/i38-clipboard.py --show
|
||||
|
||||
# Move the mouse pointer to the center of the focused window
|
||||
bindsym \$mod+Control+m exec --no-startup-id ${i3Path}/scripts/find_mouse.sh
|
||||
|
||||
$(if command -v xzoom &> /dev/null; then
|
||||
echo "# Magnify part of the screen with xzoom"
|
||||
echo "bindsym \$mod+Control+z exec --no-startup-id xzoom"
|
||||
echo
|
||||
fi)
|
||||
|
||||
# gtk bar
|
||||
bindsym \$mod+Control+Delete exec --no-startup-id sgtk-bar
|
||||
|
||||
@@ -1242,12 +1285,18 @@ bindsym a exec ${i3Path}/scripts/ai.py, mode "default"
|
||||
bindsym Mod1+Shift+r exec --no-startup-id ${i3Path}/scripts/slideshow_record_toggle.sh, mode "default"
|
||||
# Get a list of windows in the current workspace
|
||||
bindsym apostrophe exec --no-startup-id ${i3Path}/scripts/window_list.sh, mode "default"
|
||||
# Restart Cthulhu
|
||||
bindsym Shift+c exec $(command -v cthulhu) --replace, mode "default"
|
||||
# Restart Orca
|
||||
bindsym Shift+o exec $(command -v orca) --replace, mode "default"
|
||||
# Toggle screen reader
|
||||
bindsym Shift+t exec ${i3Path}/scripts/toggle_screenreader.sh, mode "default"
|
||||
$(if command -v cthulhu &> /dev/null; then
|
||||
echo "# Restart Cthulhu"
|
||||
echo "bindsym Shift+c exec $(command -v cthulhu) --replace, mode \"default\""
|
||||
fi)
|
||||
$(if command -v orca &> /dev/null; then
|
||||
echo "# Restart Orca"
|
||||
echo "bindsym Shift+o exec $(command -v orca) --replace, mode \"default\""
|
||||
fi)
|
||||
$(if [[ "$screenReader" != "none" ]] && [[ -n "$screenReader" ]]; then
|
||||
echo "# Toggle screen reader"
|
||||
echo "bindsym Shift+t exec ${i3Path}/scripts/toggle_screenreader.sh, mode \"default\""
|
||||
fi)
|
||||
# reload the configuration file
|
||||
bindsym Control+semicolon exec bash -c '$i3msg -t run_command reload && spd-say -P important -Cw "I38 Configuration reloaded."', mode "default"
|
||||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||
@@ -1272,6 +1321,9 @@ cat << EOF >> "${i3Path}/config"
|
||||
$(if [[ $sounds -eq 0 ]]; then
|
||||
echo "exec_always --no-startup-id ${i3Path}/scripts/sound.py"
|
||||
fi
|
||||
if command -v xrdb &> /dev/null; then
|
||||
echo "exec --no-startup-id xrdb -merge ${i3Path}/I38.Xresources"
|
||||
fi
|
||||
# Steam game focus handler - focuses games when they open behind Big Picture
|
||||
if command -v steam &> /dev/null; then
|
||||
echo "exec --no-startup-id ${i3Path}/scripts/steam_games.py"
|
||||
@@ -1303,6 +1355,9 @@ fi
|
||||
if [[ -n "$screenlockPinHash" ]] && [[ "${screenlockAutolockSeconds:-0}" -gt 0 ]] && command -v xprintidle &> /dev/null; then
|
||||
echo "exec_always --no-startup-id ${i3Path}/scripts/screenlock_autolock.sh ${screenlockAutolockSeconds}"
|
||||
fi
|
||||
if [[ -n "$screenlockPinHash" ]] && [[ "${screenlockOnStartup:-1}" -eq 0 ]]; then
|
||||
echo "exec --no-startup-id bash -c 'sleep 2; ${i3Path}/scripts/screenlock.sh'"
|
||||
fi
|
||||
# WayTray system tray daemon
|
||||
if command -v waytray-daemon &> /dev/null ; then
|
||||
echo 'exec_always --no-startup-id bash -c "pgrep -x waytray-daemon > /dev/null || waytray-daemon"'
|
||||
@@ -1319,7 +1374,9 @@ else
|
||||
if command -v x11bell &> /dev/null; then
|
||||
echo 'exec --no-startup-id x11bell play -nqV0 synth .1 sq norm -12'
|
||||
fi
|
||||
echo "exec $screenReader"
|
||||
if [[ "$screenReader" != "none" ]] && [[ -n "$screenReader" ]]; then
|
||||
echo "exec $screenReader"
|
||||
fi
|
||||
fi)
|
||||
|
||||
# First run help documentation
|
||||
@@ -1367,7 +1424,11 @@ textEditor="${textEditor##*/}"
|
||||
fileBrowser="${fileBrowser##*/}"
|
||||
ircClient="${ircClient##*/}"
|
||||
webBrowserHelp="${webBrowser:-no web browser was configured}"
|
||||
screenReaderHelp="${screenReader:-no screen reader was configured}"
|
||||
if [[ "$screenReader" == "none" ]] || [[ -z "$screenReader" ]]; then
|
||||
screenReaderHelp="no screen reader was configured"
|
||||
else
|
||||
screenReaderHelp="$screenReader"
|
||||
fi
|
||||
textEditorHelp="${textEditor:-no text editor was configured}"
|
||||
if [[ -n "$fileBrowser" ]]; then
|
||||
fileBrowserHelp="I38 uses ${fileBrowser} for file management. Launch it in Ratpoison mode with the \`f\` key."
|
||||
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Move the mouse pointer to the center of the focused window.
|
||||
|
||||
if ! command -v xdotool &> /dev/null; then
|
||||
if command -v notify-send &> /dev/null; then
|
||||
notify-send "I38" "xdotool is required to move the mouse pointer."
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
activeWindow="$(xdotool getactivewindow 2> /dev/null || true)"
|
||||
if [[ "$activeWindow" =~ ^[0-9]+$ ]]; then
|
||||
while IFS='=' read -r key value; do
|
||||
case "$key" in
|
||||
X|Y|WIDTH|HEIGHT)
|
||||
if [[ "$value" =~ ^-?[0-9]+$ ]]; then
|
||||
printf -v "$key" '%s' "$value"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < <(xdotool getwindowgeometry --shell "$activeWindow" 2> /dev/null || true)
|
||||
fi
|
||||
|
||||
if [[ "${X:-}" =~ ^-?[0-9]+$ ]] &&
|
||||
[[ "${Y:-}" =~ ^-?[0-9]+$ ]] &&
|
||||
[[ "${WIDTH:-}" =~ ^[0-9]+$ ]] &&
|
||||
[[ "${HEIGHT:-}" =~ ^[0-9]+$ ]] &&
|
||||
[[ "$WIDTH" -gt 0 ]] &&
|
||||
[[ "$HEIGHT" -gt 0 ]]; then
|
||||
pointerX=$((X + WIDTH / 2))
|
||||
pointerY=$((Y + HEIGHT / 2))
|
||||
else
|
||||
read -r displayWidth displayHeight < <(xdotool getdisplaygeometry)
|
||||
pointerX=$((displayWidth / 2))
|
||||
pointerY=$((displayHeight / 2))
|
||||
fi
|
||||
|
||||
xdotool mousemove "$pointerX" "$pointerY"
|
||||
@@ -15,6 +15,24 @@ scriptPath="$(readlink -f "$0")"
|
||||
scriptDir="${scriptPath%/*}"
|
||||
i3Path="${scriptDir%/scripts}"
|
||||
pinFile="${i3Path}/.screenpin"
|
||||
runtimeDir="${XDG_RUNTIME_DIR:-/tmp}"
|
||||
lockDir="${runtimeDir}/i38-screenlock"
|
||||
|
||||
if [[ -d "$lockDir" ]]; then
|
||||
if [[ -f "${lockDir}/pid" ]]; then
|
||||
read -r existingPid < "${lockDir}/pid"
|
||||
if [[ "$existingPid" =~ ^[0-9]+$ ]] && kill -0 "$existingPid" 2> /dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
rm -rf "$lockDir"
|
||||
fi
|
||||
|
||||
if ! mkdir "$lockDir" 2> /dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
printf "%s\n" "$$" > "${lockDir}/pid"
|
||||
trap 'rm -rf "$lockDir"' EXIT
|
||||
|
||||
if [[ -f "$pinFile" ]]; then
|
||||
read -r screenlockPinHash < "$pinFile"
|
||||
|
||||
Reference in New Issue
Block a user