From 37360dc091d2cbefbf3927b42575ed4134523158 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 17 Jul 2025 22:20:49 -0400 Subject: [PATCH] unified screen reader settings across images. --- CLAUDE.md | 153 ++++++++++++ etc/fenrirscreenreader/settings/settings.conf | 232 ++++++++++++++++++ home/stormux/.local/share/orca/__init__.py | 0 .../share/orca/app-settings/gzdoom.conf | 13 + .../.local/share/orca/orca-customizations.py | 0 .../share/orca/orca-scripts/__init__.py | 0 .../.local/share/orca/user-settings.conf | 151 ++++++++++++ 7 files changed, 549 insertions(+) create mode 100644 CLAUDE.md create mode 100644 etc/fenrirscreenreader/settings/settings.conf create mode 100755 home/stormux/.local/share/orca/__init__.py create mode 100644 home/stormux/.local/share/orca/app-settings/gzdoom.conf create mode 100755 home/stormux/.local/share/orca/orca-customizations.py create mode 100755 home/stormux/.local/share/orca/orca-scripts/__init__.py create mode 100644 home/stormux/.local/share/orca/user-settings.conf diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c8af673 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,153 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is the gaming-image-files repository for the Stormux gaming image - a specialized Linux distribution designed for blind and visually impaired gamers. The repository contains system configuration files, utilities, and scripts that are deployed to create a gaming-focused live USB/installable image. + +## Repository Structure + +``` +/ +├── etc/ # System configuration files +│ ├── speech-dispatcher/ # Speech synthesis configuration +│ ├── sudoers.d/ # Sudo permissions +│ ├── systemd/ # System services +│ └── vconsole.conf # Virtual console configuration +├── home/stormux/ # User home directory files +│ └── Documents/ # User documentation +├── root/ # Root user scripts +│ ├── live-update.sh # Live system update script +│ └── sanitize.sh # Image preparation/cleanup script +└── usr/ # System utilities and applications + ├── lib/systemd/ # System service files + ├── local/bin/ # Custom utilities and launchers + └── share/ # Shared resources (sounds, keymaps) +``` + +## Key Applications and Architecture + +### Game Launcher (`game_launcher.py`) +The main menu system for the gaming image. Built with: +- **curses** for terminal UI +- **speechd** for speech synthesis +- **configparser** for settings management +- **Section-based menu system** with dynamic service management +- **Sound effects** with simpleaudio +- **Terminal state management** for launching games + +### Music Player (`music_player.py`) +Self-voiced music player with: +- **Artist/Album navigation** with folder structure support +- **MPV integration** for playback +- **Random/Sequential playback modes** +- **Nested menu navigation** with breadcrumb support + +### ROM Launcher (`rom_launcher.py`) +Emulator ROM management system: +- **Auto-discovery** of ROM files from ~/Roms directory +- **Section-based organization** by emulator type +- **Dynamic menu generation** from filesystem structure + +### Specialized Utilities +- **OCR system** (`ocr.py`) - Screen reading with Tesseract +- **Apple IIe launcher** (`apple_2e.py`) - Disk image management +- **Voice configuration** (`set-voice.py`) - Speech-dispatcher module selection +- **Speech rate control** (`speechd_rate.py`) - System-wide speech rate configuration + +## Development Commands + +### Image Preparation +```bash +# Clean system for image creation +./root/sanitize.sh + +# Clean with zero-fill for better compression +./root/sanitize.sh -0 + +# Update live system from git +./root/live-update.sh +``` + +### System Installation +```bash +# Install system to hard drive (x86_64 only) +./usr/local/bin/install_to_disk.sh + +# Run system diagnostics +./usr/local/bin/diagnostics.sh +``` + +### Common Python Application Pattern +Most Python applications follow this pattern: +1. **Speech initialization** with speechd +2. **Curses-based UI** with proper cleanup +3. **Configuration management** with configparser +4. **Graceful terminal handling** for game launches +5. **Sound feedback** for navigation + +## Accessibility Architecture + +### Speech Integration +- All applications use **speech-dispatcher** via Python speechd module +- **Configurable speech rates** with persistent settings +- **Interrupt-based speech** for responsive navigation +- **Module selection** system for different TTS engines + +### Audio Feedback +- **Sound effects** for menu navigation (menu_move.wav, menu_select.wav, etc.) +- **Audio cues** for different menu sections +- **Volume control** integrated into applications + +### Terminal Management +- **Proper curses cleanup** when launching external applications +- **Terminal state restoration** after game exits +- **Process group management** for signal handling + +## File Organization Patterns + +### Configuration Files +- User configs in `~/.config/stormux/` +- System configs follow standard Linux paths +- **Backup configurations** (e.g., speechd.conf.bak) + +### Game Integration +- Games launched via `startx` with `GAME` environment variable +- **ROM organization** by emulator type in ~/Roms/ +- **Music organization** by Artist/Album in ~/Music/ + +### System Services +- **Accessibility services** (Fenrir, Braille support) +- **Media services** (DLNA server) +- **Optional services** (SSH, Bluetooth) with toggle support + +## Key Design Principles + +1. **Accessibility First** - All interfaces must be fully navigable by speech +2. **Self-Voiced** - Applications provide their own speech feedback +3. **Consistent Navigation** - Arrow keys, Enter, Escape patterns across applications +4. **Graceful Degradation** - Applications handle speech failures gracefully +5. **Clean Exits** - Proper resource cleanup before launching games or exiting + +## Security Considerations + +- **Sudo integration** for system configuration changes +- **File path sanitization** in ROM/disk launchers +- **Secure temporary file handling** in utilities +- **Process isolation** for game launches + +## Testing and Validation + +- Test speech functionality with different TTS engines +- Verify terminal state restoration after game exits +- Test accessibility with screen readers (Fenrir) +- Validate file handling with special characters in filenames +- Test system service toggle functionality + +## Known Limitations + +- **Platform-specific features** (e.g., Steam on x86_64 only) +- **USB detection** for installer availability +- **Sudo requirements** for system configuration changes +- **Terminal dependency** for proper cleanup and restoration \ No newline at end of file diff --git a/etc/fenrirscreenreader/settings/settings.conf b/etc/fenrirscreenreader/settings/settings.conf new file mode 100644 index 0000000..4d3aa09 --- /dev/null +++ b/etc/fenrirscreenreader/settings/settings.conf @@ -0,0 +1,232 @@ +[sound] +# Turn sound on or off: +enabled=True + +# Select the driver used to play sounds, choices are genericDriver and gstreamerDriver. +# Sox is the default. +#driver=gstreamerDriver +driver=genericDriver + +# Sound themes. These are the pack of sounds used for sound alerts. +# Sound packs may be located at /usr/share/sounds +# For system wide availability, or ~/.local/share/fenrirscreenreader/sounds +# For the current user. +theme=default + +# Sound volume controls how loud the sounds for your selected soundpack are. +# 0 is quietest, 1.0 is loudest. +volume=1.0 + +# shell commands for generic sound driver +# the folowing variable are substituted +# fenrirVolume = the current volume setting +# fenrirSoundFile = the soundfile for an soundicon +# fenrirFrequence = the frequency to play +# fenrirDuration = the duration of the frequency +# the following command is used to play a soundfile +genericPlayFileCommand=play -q -v fenrirVolume fenrirSoundFile +#the following command is used to generate a frequency beep +genericFrequencyCommand=play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence + +# Enable progress bar monitoring with ascending tones by default +progressMonitoring=True + +[speech] +# Turn speech on or off: +enabled=True + +# Select speech driver, options are speechdDriver or genericDriver: +driver=speechdDriver +#driver=genericDriver + +# The rate selects how fast Fenrir will speak. Options range from 0, slowest, to 1.0, fastest. +rate=0.55 + +# Pitch controls the pitch of the voice, select from 0, lowest, to 1.0, highest. +pitch=0.5 +# Pitch for capital letters +capitalPitch=0.9 + +# Volume controls the loudness of the voice, select from 0, quietest, to 1.0, loudest. +volume=1.0 + +# Module is used for Speech-dispatcher, to select the speech module you want to use. +# Consult Speech-dispatcher's configuration and help Fenrir find out which modules are available. +# The default is specified in speechd.conf. +#module=espeak-ng + +# Voice selects the voice you want to use, for example, en-GB-scotland will use the Scotish English voice in Espeak, +# To find out which voices are available, consult the documentation provided with your selected synthesizer. +# This also sets the voice used in the generic driver. +# You can add a variant by adding +name onto the end. +# voice=en-us + +# Select the language you want Fenrir to use. +#language=en + +# Read new text as it happens? +autoReadIncoming=True + +# Speak individual numbers instead of whole string. +readNumbersAsDigits = False + +# genericSpeechCommand is the command that is executed for talking +# the following variables are replaced with values +# fenrirText = is the text that should be spoken +# fenrirModule = may be the speech module like used in speech-dispatcher, not every TTY need this +# fenrirLanguage = the language +# fenrirVoice = is the current voice that should be used. Set the voice variable above. +# the current volume, pitch and rate is calculated like this +# value = min + settingValue * (min - max ) +# fenrirVolume = is replaced with the current volume +# fenrirPitch = is replaced with the current pitch +# fenrirRate = is replaced with the current speed (speech rate) +genericSpeechCommand=espeak-ng -a fenrirVolume -s fenrirRate -p fenrirPitch -v fenrirVoice -- "fenrirText" + +# min and max values of the TTS system that is used in genericSpeechCommand +fenrirMinVolume=0 +fenrirMaxVolume=200 +fenrirMinPitch=0 +fenrirMaxPitch=99 +fenrirMinRate=80 +fenrirMaxRate=450 + +[screen] +driver=vcsaDriver +encoding=auto +screenUpdateDelay=0.05 +ignoreScreen= +autodetectIgnoreScreen=True + +[keyboard] +driver=evdevDriver +# filter input devices NOMICE, ALL or a DEVICE NAME +device=ALL +# gives Fenrir exclusive access to the keyboard and lets it control keystrokes. +grabDevices=True +ignoreShortcuts=False +# the current shortcut layout located in /etc/fenrirscreenreader/keyboard +keyboardLayout=laptop +# echo chars while typing. +# 0 = None +# 1 = always +# 2 = only while capslock +charEchoMode=1 +# echo deleted chars +charDeleteEcho=True +# echo word after pressing space +wordEcho=False +# interrupt speech on any keypress +interruptOnKeyPress=True +# you can filter the keys on that the speech should interrupt (empty = all keys, otherwhise the given keys) +interruptOnKeyPressFilter= +# timeout for double tap in sec +doubleTapTimeout=0.2 + +[general] +debugLevel=0 +# debugMode sets where the debug output should send to: +# debugMode=File writes to debugFile (Default:/tmp/fenrir-PID.log) +# debugMode=Print just prints on the screen +debugMode=File +debugFile= +punctuationProfile=default +punctuationLevel=some +respectPunctuationPause=True +newLinePause=True +numberOfClipboards=10 +# used path for "export_clipboard_to_file" +# $user is replaced by username +#clipboardExportPath=/home/$user/fenrirClipboard +clipboardExportPath=/tmp/fenrirClipboard +emoticons=True +# define the current Fenrir key +fenrirKeys=KEY_CAPSLOCK,KEY_KP0,KEY_META,KEY_INSERT +scriptKeys=KEY_COMPOSE +timeFormat=%H:%M:%P +dateFormat=%A, %B %d, %Y +autoSpellCheck=True +spellCheckLanguage=en_US +# path for your scripts "scriptKeys" functionality +scriptPath=/usr/share/fenrirscreenreader/scripts +# overload commands, and create new one without changing Fenrir default +commandPath= +#fenrirBGColor = the backgroundcolor +#fenrirFGColor = the foregroundcolor +#fenrirUnderline = speak the underline attribute +#fenrirBold = speak the bold attribute +#fenrirBlink = speak the blink attribute +#fenrirFont = the font +#fenrirFontSize = the fontsize +attributeFormatString=Background fenrirBGColor,Foreground fenrirFGColor,fenrirUnderline,fenrirBold,fenrirBlink, Font fenrirFont,Fontsize fenrirFontSize +# present indentation +autoPresentIndent=False +# speak is only invoked on changeing ident level, sound always +# 0 = sound and speak +# 1 = sound only +# 2 = speak only +autoPresentIndentMode=1 +# play a sound when attributes are changeing +hasAttributes=False +# shell for PTY emulatiun (empty = default shell) +shell= + +[focus] +#follow the text cursor +cursor=True +#follow highlighted text changes +highlight=False + +[remote] +enable=True +# driver +# unixDriver = unix sockets +# tcpDriver = tcp (localhost only) +driver=unixDriver +# tcp port +port=22447 +# socket filepath +socketFile= +# allow settings to overwrite +enableSettingsRemote=True +# allow commands to be executed +enableCommandRemote=True + +[barrier] +enabled=False +leftBarriers=│└┌─ +rightBarriers=│┘┐─ + +[review] +lineBreak=True +endOfScreen=True +# leave the review when pressing a key +leaveReviewOnCursorChange=True +# leave the review when changing the screen +leaveReviewOnScreenChange=True + +[promote] +enabled=True +inactiveTimeoutSec=120 +list= + +[menu] +vmenuPath= +quickMenu=speech#rate;speech#pitch;speech#volume + +[time] +# automatic time anouncement +enabled=False +# present time +presentTime=True +# present date (on change) +presentDate=True +# present time after a given period of seconds +delaySec=0 +# present time after to given minutes example every 15 minutes: 00,15,30,45 +# if delaySec is >0 onMinutes is ignored +onMinutes=00,30 +# announce via soundicon (not interrupting) +announce=True +# interrupt current speech for time announcement +interrupt=False diff --git a/home/stormux/.local/share/orca/__init__.py b/home/stormux/.local/share/orca/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/home/stormux/.local/share/orca/app-settings/gzdoom.conf b/home/stormux/.local/share/orca/app-settings/gzdoom.conf new file mode 100644 index 0000000..bc480bc --- /dev/null +++ b/home/stormux/.local/share/orca/app-settings/gzdoom.conf @@ -0,0 +1,13 @@ +{ + "profiles": { + "default": { + "general": { + "enableSpeech": false, + "textAttributesToSpeak": [], + "textAttributesToBraille": [] + }, + "pronunciations": {}, + "keybindings": {} + } + } +} \ No newline at end of file diff --git a/home/stormux/.local/share/orca/orca-customizations.py b/home/stormux/.local/share/orca/orca-customizations.py new file mode 100755 index 0000000..e69de29 diff --git a/home/stormux/.local/share/orca/orca-scripts/__init__.py b/home/stormux/.local/share/orca/orca-scripts/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/home/stormux/.local/share/orca/user-settings.conf b/home/stormux/.local/share/orca/user-settings.conf new file mode 100644 index 0000000..524f087 --- /dev/null +++ b/home/stormux/.local/share/orca/user-settings.conf @@ -0,0 +1,151 @@ +{ + "general": { + "orcaModifierKeys": [ + "Caps_Lock", + "Insert", + "KP_Insert" + ], + "enableSpeech": true, + "onlySpeakDisplayedText": false, + "speechServerFactory": "speechdispatcherfactory", + "speechServerInfo": null, + "voices": { + "default": { + "established": false + }, + "uppercase": { + "average-pitch": 7.0 + }, + "hyperlink": { + "established": false + }, + "system": { + "established": false + } + }, + "speechVerbosityLevel": 1, + "readFullRowInGUITable": true, + "readFullRowInDocumentTable": true, + "readFullRowInSpreadSheet": false, + "enableSpeechIndentation": false, + "enableEchoByCharacter": false, + "enableEchoByWord": false, + "enableEchoBySentence": false, + "enableKeyEcho": true, + "enableAlphabeticKeys": true, + "enableNumericKeys": true, + "enablePunctuationKeys": true, + "enableSpace": true, + "enableModifierKeys": true, + "enableFunctionKeys": true, + "enableActionKeys": true, + "enableNavigationKeys": false, + "enableDiacriticalKeys": false, + "enablePauseBreaks": true, + "enableTutorialMessages": true, + "enableMnemonicSpeaking": false, + "enablePositionSpeaking": false, + "enableBraille": true, + "disableBrailleEOL": false, + "brailleVerbosityLevel": 1, + "brailleRolenameStyle": 1, + "brailleSelectorIndicator": 192, + "brailleLinkIndicator": 192, + "enableSound": true, + "soundVolume": 0.5, + "playSoundForRole": false, + "playSoundForState": false, + "playSoundForPositionInSet": false, + "playSoundForValue": false, + "verbalizePunctuationStyle": 1, + "presentToolTips": false, + "sayAllStyle": 1, + "keyboardLayout": 2, + "speakBlankLines": true, + "speakNumbersAsDigits": false, + "speakMisspelledIndicator": true, + "textAttributesToSpeak": [], + "textAttributesToBraille": [], + "textAttributesBrailleIndicator": 0, + "profile": [ + "Default", + "default" + ], + "speakProgressBarUpdates": true, + "brailleProgressBarUpdates": false, + "beepProgressBarUpdates": false, + "progressBarUpdateInterval": 10, + "progressBarVerbosity": 1, + "ignoreStatusBarProgressBars": true, + "enableBrailleWordWrap": false, + "enableContractedBraille": false, + "brailleContractionTable": "", + "enableMouseReview": false, + "speakCellCoordinates": true, + "speakSpreadsheetCoordinates": true, + "alwaysSpeakSelectedSpreadsheetRange": false, + "speakCellSpan": true, + "speakCellHeaders": true, + "skipBlankCells": false, + "largeObjectTextLength": 75, + "structuralNavigationEnabled": true, + "wrappedStructuralNavigation": true, + "chatMessageVerbosity": 0, + "chatSpeakRoomName": false, + "chatAnnounceBuddyTyping": false, + "chatRoomHistories": false, + "enableFlashMessages": true, + "brailleFlashTime": 5000, + "flashIsPersistent": false, + "flashIsDetailed": true, + "messagesAreDetailed": true, + "presentDateFormat": "%x", + "presentTimeFormat": "%X", + "activeProfile": [ + "Default", + "default" + ], + "startingProfile": [ + "Default", + "default" + ], + "spellcheckSpellError": true, + "spellcheckSpellSuggestion": true, + "spellcheckPresentContext": true, + "useColorNames": true, + "capitalizationStyle": "none", + "findResultsVerbosity": 2, + "findResultsMinimumLength": 4, + "structNavTriggersFocusMode": false, + "caretNavTriggersFocusMode": false, + "layoutMode": true, + "nativeNavTriggersFocusMode": true, + "rewindAndFastForwardInSayAll": false, + "structNavInSayAll": false, + "speakDescription": true, + "speakContextBlockquote": true, + "speakContextPanel": true, + "speakContextLandmark": true, + "speakContextNonLandmarkForm": true, + "speakContextList": true, + "speakContextTable": true, + "sayAllContextBlockquote": true, + "sayAllContextPanel": true, + "sayAllContextLandmark": true, + "sayAllContextNonLandmarkForm": true, + "sayAllContextList": true, + "sayAllContextTable": true + }, + "profiles": { + "default": { + "profile": [ + "Default", + "default" + ], + "pronunciations": {}, + "keybindings": {} + } + }, + "pronunciations": {}, + "keybindings": {} +}