Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 047a31b4bf | |||
| 096aef9f08 | |||
| 76472b83b5 | |||
| a52bf624ec | |||
| f4e28a246f | |||
| 560ceb26c9 | |||
| 61868c94e5 | |||
| f462ca7990 | |||
| f0bbcb8a38 | |||
| aed627ec2a | |||
| e62b887e9c | |||
| bf0d134187 | |||
| c66a9ba9c2 | |||
| 2092a3e257 | |||
| d46d8de3ee | |||
| 75a8447759 | |||
| 1650eec768 | |||
| 5bb786ef4c | |||
| 7f7faa17d3 | |||
| 2766f70c5d | |||
| 8d781643bc | |||
| c184cf023a | |||
| 841c221c7b | |||
| 87553bdc38 | |||
| 77a3aae5a4 | |||
| aabc202d83 | |||
| 2f3a114790 | |||
| c797974560 | |||
| af4740d5ad | |||
| 5ef5faaebe | |||
| 7041d2567a | |||
| 2c38bcf5f4 | |||
| 96cdda99c4 | |||
| 0658d37ae8 | |||
| a6bb3e1301 | |||
| 5ff653bd00 | |||
| 356f4b01c1 | |||
| c7ad4d9200 | |||
| d274fe78f3 | |||
| f5344a7227 | |||
| 90ffc2fc08 | |||
| b635f7538b | |||
| 7f473b72a7 | |||
| e255651c28 | |||
| b3d73102fc | |||
| 78c1cbbb6b | |||
| 8c26d93001 | |||
| 98b9c56af7 | |||
| 8bada48a09 | |||
| 0a2c8472c0 | |||
| e9a0101fe7 | |||
| 69eade3327 | |||
| 73f67c2a04 | |||
| 9ef9d762f4 | |||
| 84293db6dc | |||
| 914535d12b | |||
| 94a1acbaca | |||
| 8c233e0385 | |||
| b6a9e1a692 | |||
| d41ea8388f | |||
| 2dd732dc9d | |||
| b68a28e857 | |||
| 8c668cc0cc | |||
| 579bf0f0f0 | |||
| d1bad818cd |
@@ -134,6 +134,58 @@ By default Fenrir uses:
|
||||
- **Input driver**: evdevDriver (Linux) or ptyDriver (other platforms)
|
||||
- **Screen driver**: vcsaDriver (Linux TTY) or ptyDriver (terminal emulation)
|
||||
|
||||
## Audio Configuration
|
||||
|
||||
Both PulseAudio and PipeWire require special configuration to allow console applications running as root (like Fenrir) to route audio to your regular user session. This is normal audio system behavior, not a Fenrir issue.
|
||||
|
||||
### Quick Setup - Direct Script Download
|
||||
|
||||
For non-Fenrir users or quick setup, download and run these scripts directly:
|
||||
|
||||
#### PulseAudio Configuration
|
||||
```bash
|
||||
# Download the script
|
||||
wget https://git.stormux.org/storm/fenrir/raw/branch/master/tools/configure_pulse.sh
|
||||
chmod +x configure_pulse.sh
|
||||
|
||||
# Run twice: once as user, once as root
|
||||
./configure_pulse.sh
|
||||
sudo ./configure_pulse.sh
|
||||
```
|
||||
|
||||
#### PipeWire Configuration
|
||||
```bash
|
||||
# Download the script
|
||||
wget https://git.stormux.org/storm/fenrir/raw/branch/master/tools/configure_pipewire.sh
|
||||
chmod +x configure_pipewire.sh
|
||||
|
||||
# Run twice: once as user, once as root
|
||||
./configure_pipewire.sh
|
||||
sudo ./configure_pipewire.sh
|
||||
```
|
||||
|
||||
**Direct links:**
|
||||
- [configure_pulse.sh](https://git.stormux.org/storm/fenrir/raw/branch/master/tools/configure_pulse.sh)
|
||||
- [configure_pipewire.sh](https://git.stormux.org/storm/fenrir/raw/branch/master/tools/configure_pipewire.sh)
|
||||
|
||||
### Using Installed Scripts
|
||||
|
||||
If you have Fenrir installed, the scripts are available at:
|
||||
|
||||
**PulseAudio:**
|
||||
```bash
|
||||
/usr/share/fenrirscreenreader/tools/configure_pulse.sh
|
||||
sudo /usr/share/fenrirscreenreader/tools/configure_pulse.sh
|
||||
```
|
||||
|
||||
**PipeWire:**
|
||||
```bash
|
||||
/usr/share/fenrirscreenreader/tools/configure_pipewire.sh
|
||||
sudo /usr/share/fenrirscreenreader/tools/configure_pipewire.sh
|
||||
```
|
||||
|
||||
**Note:** These scripts work for any console application that needs root audio access, not just Fenrir.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Basic Usage
|
||||
@@ -640,31 +692,6 @@ send_fenrir_command("setting set speech#rate=0.9")
|
||||
- Check Fenrir debug logs: `/var/log/fenrir.log`
|
||||
- Test with simple command: `echo "command interrupt" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock`
|
||||
|
||||
## Configure pulseaudio
|
||||
|
||||
Pulseaudio by default only plays sound for the user its currently running for. As fenrir is running as root, your local user does not hear the sound and speech produced by fenrir.
|
||||
for this fenrir provides a script to configure pulseaudio to stream the sound played as root to your local user. This is not a issue of fenrir but this is how pulseaudio works.
|
||||
|
||||
just run the configuration script twice (once as user, once as root):
|
||||
|
||||
/usr/share/fenrirscreenreader/tools/configure_pulse.sh
|
||||
sudo /usr/share/fenrirscreenreader/tools/configure_pulse.sh
|
||||
|
||||
The script is also located in the tools directory in git
|
||||
|
||||
|
||||
## Configure pipewire
|
||||
|
||||
Pipewire by default only plays sound for the user its currently running for. As fenrir is running as root, your local user does not hear the sound and speech produced by fenrir.
|
||||
for this fenrir provides a script to configure pipewire to stream the sound played as root to your local user. This is not a issue of fenrir but this is how pipewire works.
|
||||
|
||||
just run the configuration script twice (once as user, once as root):
|
||||
|
||||
/usr/share/fenrirscreenreader/tools/configure_pipewire.sh
|
||||
sudo /usr/share/fenrirscreenreader/tools/configure_pipewire.sh
|
||||
|
||||
The script is also located in the tools directory in git
|
||||
|
||||
## Command Line Options
|
||||
|
||||
Fenrir supports several command-line options for different use cases:
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
# Fenrir Release Validation Checklist
|
||||
|
||||
This checklist ensures thorough validation before releasing Fenrir packages.
|
||||
|
||||
## 🔧 Setup Tools (One-time setup)
|
||||
|
||||
### Install Pre-commit Hook
|
||||
```bash
|
||||
# Safely install composite hook (preserves existing version management)
|
||||
./tools/install_validation_hook.sh
|
||||
|
||||
# Test the hook
|
||||
./.git/hooks/pre-commit
|
||||
```
|
||||
|
||||
### Validation Scripts
|
||||
- `tools/validate_syntax.py` - Python syntax validation
|
||||
- `tools/validate_pep8.py` - PEP8 compliance checking with safe auto-fix
|
||||
- `tools/validate_release.py` - Comprehensive release validation
|
||||
- `tools/cleanup_cache.py` - Remove Python cache files and directories
|
||||
- `tools/pre-commit-hook` - Git pre-commit validation
|
||||
|
||||
## 📋 Pre-Release Checklist
|
||||
|
||||
### 1. Code Quality Validation ✅
|
||||
```bash
|
||||
# Comprehensive release validation (includes syntax, imports, structure)
|
||||
python3 tools/validate_release.py
|
||||
|
||||
# If issues found, try auto-fix
|
||||
python3 tools/validate_release.py --fix
|
||||
|
||||
# Quick validation (skips slow dependency checks)
|
||||
python3 tools/validate_release.py --quick
|
||||
```
|
||||
|
||||
**Expected Result**: All tests pass, no syntax errors
|
||||
|
||||
### 2. Dependency Validation ✅
|
||||
```bash
|
||||
# Validate all dependencies are available
|
||||
python3 check-dependencies.py
|
||||
```
|
||||
|
||||
**Expected Result**: All required dependencies reported as available
|
||||
|
||||
### 3. Core Functionality Test ✅
|
||||
```bash
|
||||
# Test core imports (safe to run without sudo)
|
||||
cd src
|
||||
python3 -c "
|
||||
import fenrirscreenreader.core.fenrirManager
|
||||
import fenrirscreenreader.core.commandManager
|
||||
import fenrirscreenreader.core.eventManager
|
||||
print('Core imports successful')
|
||||
"
|
||||
cd ..
|
||||
```
|
||||
|
||||
**Expected Result**: No import errors
|
||||
|
||||
### 4. Installation Script Validation ✅
|
||||
```bash
|
||||
# Validate setup.py syntax
|
||||
python3 -m py_compile setup.py
|
||||
|
||||
# Check setup.py can be parsed
|
||||
python3 setup.py --help-commands >/dev/null
|
||||
```
|
||||
|
||||
**Expected Result**: No syntax errors, setup.py functional
|
||||
|
||||
### 5. Configuration Validation ✅
|
||||
```bash
|
||||
# Verify config files exist and are parseable
|
||||
ls -la config/settings/settings.conf
|
||||
ls -la config/keyboard/desktop.conf
|
||||
ls -la config/punctuation/default.conf
|
||||
```
|
||||
|
||||
**Expected Result**: All core config files present
|
||||
|
||||
### 6. Manual Testing (User/Package Maintainer) ⚠️
|
||||
|
||||
**Important**: These require user interaction as they need sudo access or specific hardware.
|
||||
|
||||
```bash
|
||||
# Test basic functionality (ask user to run)
|
||||
sudo ./src/fenrir --help
|
||||
|
||||
# Test in emulation mode (safer for desktop environments)
|
||||
sudo ./src/fenrir -e --version
|
||||
|
||||
# Quick functionality test (3-5 seconds)
|
||||
sudo timeout 5 ./src/fenrir -e -f || echo "Timeout reached (expected)"
|
||||
```
|
||||
|
||||
**Expected Result**: No immediate crashes, basic help/version output works
|
||||
|
||||
### 7. Package-Specific Validation ✅
|
||||
```bash
|
||||
# Test the same compilation process used by package managers
|
||||
python3 -m compileall src/fenrirscreenreader/ -q
|
||||
|
||||
# Verify no __pycache__ permission issues
|
||||
find src/ -name "*.pyc" -delete
|
||||
find src/ -name "__pycache__" -delete
|
||||
```
|
||||
|
||||
**Expected Result**: Clean compilation, no permission errors
|
||||
|
||||
## 🚨 Known Issue Categories
|
||||
|
||||
### Critical Issues (Block Release)
|
||||
- **Python syntax errors** (SyntaxError, unterminated strings)
|
||||
- **Missing core dependencies** (dbus-python, evdev, etc.)
|
||||
- **Import failures in core modules** (fenrirManager, commandManager)
|
||||
- **Missing critical config files** (settings.conf, desktop.conf)
|
||||
|
||||
### Warning Issues (Address if Possible)
|
||||
- **PEP8 violations** (cosmetic, don't block release)
|
||||
- **Missing optional dependencies** (for specific features)
|
||||
- **Command structure issues** (missing methods in command files)
|
||||
- **Very long lines** (>120 characters)
|
||||
|
||||
## 🔍 Root Cause Analysis
|
||||
|
||||
### Why These Errors Weren't Caught Previously
|
||||
|
||||
1. **No automated syntax validation** - The codebase relied on manual testing
|
||||
2. **No pre-commit hooks** - Syntax errors could be committed
|
||||
3. **No CI/CD pipeline** - Package compilation happens only during release
|
||||
4. **Manual PEP8 cleanup** - F-string refactoring introduced syntax errors during batch cleanup
|
||||
|
||||
## 📖 Usage Instructions
|
||||
|
||||
### For Developers
|
||||
```bash
|
||||
# Before committing changes
|
||||
git add .
|
||||
git commit # Pre-commit hook will run automatically
|
||||
|
||||
# Before creating tags/releases
|
||||
python3 tools/validate_release.py
|
||||
```
|
||||
|
||||
### For Package Maintainers
|
||||
```bash
|
||||
# Before packaging
|
||||
python3 tools/validate_release.py
|
||||
|
||||
# If validation fails
|
||||
python3 tools/validate_release.py --fix
|
||||
|
||||
# Quick check (if dependencies are known good)
|
||||
python3 tools/validate_release.py --quick
|
||||
```
|
||||
|
||||
### For Release Managers
|
||||
```bash
|
||||
# Complete validation before tagging
|
||||
python3 tools/validate_release.py
|
||||
|
||||
# Manual verification (requires sudo)
|
||||
sudo ./src/fenrir --version
|
||||
|
||||
# Tag release only after all validations pass
|
||||
git tag -a v2.x.x -m "Release v2.x.x"
|
||||
```
|
||||
|
||||
## 🎯 Future Improvements
|
||||
|
||||
### Recommended Additions
|
||||
1. **GitHub Actions CI/CD** - Automated validation on every push
|
||||
2. **Automated testing** - Unit tests for core functionality
|
||||
3. **Integration testing** - Test driver interactions
|
||||
4. **Package testing** - Validate actual package installation
|
||||
|
||||
### Modern Python Packaging
|
||||
- Consider migrating to `pyproject.toml` (PEP 621)
|
||||
- Use `build` instead of `setup.py` directly
|
||||
- Add `tox.ini` for multi-environment testing
|
||||
|
||||
## 📞 Support
|
||||
|
||||
If validation fails and auto-fix doesn't resolve issues:
|
||||
|
||||
1. **Check the specific error messages** in validation output
|
||||
2. **Review recent commits** that might have introduced issues
|
||||
3. **Run individual validation steps** to isolate problems
|
||||
|
||||
Remember: **Working code is better than perfect code** - especially for accessibility software where reliability is critical.
|
||||
@@ -4,12 +4,10 @@ Wants=systemd-udev-settle.service
|
||||
After=systemd-udev-settle.service getty.target
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/var/run/fenrir.pid
|
||||
PIDFile=/run/fenrir.pid
|
||||
ExecStart=/usr/bin/fenrir
|
||||
ExecReload=/usr/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
#Group=fenrirscreenreader
|
||||
#User=fenrirscreenreader
|
||||
|
||||
[Install]
|
||||
WantedBy=getty.target
|
||||
|
||||
@@ -4,7 +4,7 @@ Wants=systemd-udev-settle.service
|
||||
After=systemd-udev-settle.service sound.target
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/var/run/fenrir.pid
|
||||
PIDFile=/run/fenrir.pid
|
||||
ExecStart=/usr/local/bin/fenrir
|
||||
ExecReload=/usr/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Please report Bugs and feature requests to:
|
||||
https://github.com/chrys87/fenrir/issues
|
||||
Please report bugs and feature requests to:
|
||||
https://git.stormux.org/storm/fenrir/issues
|
||||
|
||||
For bugs, please provide a debug file that shows the issue.
|
||||
How to create a debug file:
|
||||
|
||||
@@ -126,7 +126,8 @@ KEY_FENRIR,KEY_CTRL,KEY_S=save_settings
|
||||
# linux specific
|
||||
KEY_FENRIR,KEY_F7=import_clipboard_from_x
|
||||
KEY_FENRIR,KEY_F8=export_clipboard_to_x
|
||||
KEY_FENRIR,KEY_CTRL,KEY_UP=inc_alsa_volume
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOWN=dec_alsa_volume
|
||||
# Read-all functionality
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOWN=read_all_by_line
|
||||
KEY_FENRIR,KEY_CTRL,KEY_PAGEDOWN=read_all_by_page
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_V=announce_fenrir_version
|
||||
KEY_F4=cycle_keyboard_layout
|
||||
KEY_FENRIR,KEY_LEFTCTRL,KEY_F4=cycle_keyboard_layout
|
||||
|
||||
@@ -126,7 +126,8 @@ KEY_FENRIR,KEY_CTRL,KEY_S=save_settings
|
||||
# linux specific
|
||||
KEY_FENRIR,KEY_F7=import_clipboard_from_x
|
||||
KEY_FENRIR,KEY_F8=export_clipboard_to_x
|
||||
KEY_FENRIR,KEY_CTRL,KEY_UP=inc_alsa_volume
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOWN=dec_alsa_volume
|
||||
# Read-all functionality
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOWN=read_all_by_line
|
||||
KEY_FENRIR,KEY_CTRL,KEY_PAGEDOWN=read_all_by_page
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_V=announce_fenrir_version
|
||||
KEY_F4=cycle_keyboard_layout
|
||||
KEY_FENRIR,KEY_LEFTCTRL,KEY_F4=cycle_keyboard_layout
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
KEY_FENRIR,KEY_F1=toggle_tutorial_mode
|
||||
KEY_FENRIR,KEY_H=toggle_tutorial_mode
|
||||
KEY_CTRL=shut_up
|
||||
KEY_SHIFT,KEY_KP9=review_bottom
|
||||
KEY_SHIFT,KEY_KP7=review_top
|
||||
KEY_KP8=review_curr_line
|
||||
KEY_KP7=review_prev_line
|
||||
KEY_KP9=review_next_line
|
||||
KEY_SHIFT,KEY_KP1=review_line_begin
|
||||
KEY_SHIFT,KEY_KP3=review_line_end
|
||||
KEY_FENRIR,KEY_KP1=review_line_first_char
|
||||
KEY_FENRIR,KEY_KP3=review_line_last_char
|
||||
KEY_FENRIR,KEY_ALT,KEY_1=present_first_line
|
||||
KEY_FENRIR,KEY_ALT,KEY_2=present_last_line
|
||||
KEY_KP5=review_curr_word
|
||||
KEY_KP4=review_prev_word
|
||||
KEY_KP6=review_next_word
|
||||
2,KEY_KP5=review_curr_word_phonetic
|
||||
2,KEY_KP4=review_prev_word_phonetic
|
||||
2,KEY_KP6=review_next_word_phonetic
|
||||
KEY_KP2=review_curr_char
|
||||
KEY_KP1=review_prev_char
|
||||
KEY_KP3=review_next_char
|
||||
2,KEY_KP2=review_curr_char_phonetic
|
||||
2,KEY_KP1=review_prev_char_phonetic
|
||||
2,KEY_KP3=review_next_char_phonetic
|
||||
KEY_FENRIR,KEY_CTRL,KEY_KP8=review_up
|
||||
KEY_FENRIR,KEY_CTRL,KEY_KP2=review_down
|
||||
KEY_FENRIR,KEY_KPDOT=exit_review
|
||||
KEY_KPDOT=cursor_position
|
||||
KEY_FENRIR,KEY_I=indent_curr_line
|
||||
KEY_FENRIR,KEY_B=curr_screen
|
||||
KEY_FENRIR,KEY_KP8=curr_screen_before_cursor
|
||||
KEY_FENRIR,KEY_KP2=curr_screen_after_cursor
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_PAGEDOWN=cursor_read_to_end_of_line
|
||||
#=cursor_column
|
||||
#=cursor_lineno
|
||||
#=braille_flush
|
||||
KEY_FENRIR,KEY_CTRL,KEY_T=braille_return_to_cursor
|
||||
#=braille_pan_left
|
||||
#=braille_pan_right
|
||||
KEY_FENRIR,KEY_CTRL,KEY_1=clear_bookmark_1
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_1=set_bookmark_1
|
||||
KEY_FENRIR,KEY_1=bookmark_1
|
||||
KEY_FENRIR,KEY_K=bookmark_1
|
||||
KEY_FENRIR,KEY_CTRL,KEY_2=clear_bookmark_2
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_2=set_bookmark_2
|
||||
KEY_FENRIR,KEY_2=bookmark_2
|
||||
KEY_FENRIR,KEY_CTRL,KEY_3=clear_bookmark_3
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_3=set_bookmark_3
|
||||
KEY_FENRIR,KEY_3=bookmark_3
|
||||
KEY_FENRIR,KEY_CTRL,KEY_4=clear_bookmark_4
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_4=set_bookmark_4
|
||||
KEY_FENRIR,KEY_4=bookmark_4
|
||||
KEY_FENRIR,KEY_CTRL,KEY_5=clear_bookmark_5
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_5=set_bookmark_5
|
||||
KEY_FENRIR,KEY_5=bookmark_5
|
||||
KEY_FENRIR,KEY_CTRL,KEY_6=clear_bookmark_6
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_6=set_bookmark_6
|
||||
KEY_FENRIR,KEY_6=bookmark_6
|
||||
KEY_FENRIR,KEY_CTRL,KEY_7=clear_bookmark_7
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_7=set_bookmark_7
|
||||
KEY_FENRIR,KEY_7=bookmark_7
|
||||
KEY_FENRIR,KEY_CTRL,KEY_8=clear_bookmark_8
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_8=set_bookmark_8
|
||||
KEY_FENRIR,KEY_8=bookmark_8
|
||||
KEY_FENRIR,KEY_CTRL,KEY_9=clear_bookmark_9
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_9=set_bookmark_9
|
||||
KEY_FENRIR,KEY_9=bookmark_9
|
||||
KEY_FENRIR,KEY_CTRL,KEY_0=clear_bookmark_10
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_0=set_bookmark_10
|
||||
KEY_FENRIR,KEY_0=bookmark_10
|
||||
KEY_FENRIR,KEY_KPSLASH=set_window_application
|
||||
2,KEY_FENRIR,KEY_KPSLASH=clear_window_application
|
||||
KEY_KPPLUS=progress_bar_monitor
|
||||
KEY_FENRIR,KEY_KPPLUS=silence_until_prompt
|
||||
#=toggle_braille
|
||||
KEY_FENRIR,KEY_F3=toggle_sound
|
||||
KEY_FENRIR,KEY_F4=toggle_speech
|
||||
KEY_KPENTER=temp_disable_speech
|
||||
KEY_FENRIR,KEY_P=toggle_punctuation_level
|
||||
KEY_FENRIR,KEY_RIGHTBRACE=toggle_auto_spell_check
|
||||
KEY_FENRIR,KEY_S=toggle_output
|
||||
KEY_FENRIR,KEY_CTRL,KEY_E=toggle_emoticons
|
||||
key_FENRIR,KEY_5=toggle_auto_read
|
||||
KEY_FENRIR,KEY_CTRL,KEY_T=toggle_auto_time
|
||||
KEY_FENRIR,KEY_KPASTERISK=toggle_highlight_tracking
|
||||
KEY_FENRIR,KEY_KPMINUS=toggle_barrier
|
||||
KEY_FENRIR,KEY_Q=quit_fenrir
|
||||
KEY_FENRIR,KEY_T=time
|
||||
KEY_FENRIR,KEY_F12=time
|
||||
2,KEY_FENRIR,KEY_T=date
|
||||
2,KEY_FENRIR,KEY_F12=date
|
||||
KEY_KPSLASH=toggle_auto_indent
|
||||
KEY_FENRIR,KEY_F=attribute_cursor
|
||||
#=toggle_has_attribute
|
||||
KEY_FENRIR,KEY_F7=spell_check
|
||||
2,KEY_FENRIR,KEY_S=add_word_to_spell_check
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_S=remove_word_from_spell_check
|
||||
KEY_FENRIR,KEY_F2=forward_keypress
|
||||
KEY_FENRIR,KEY_ALT,KEY_UP=inc_sound_volume
|
||||
KEY_FENRIR,KEY_ALT,KEY_DOWN=dec_sound_volume
|
||||
#=clear_clipboard
|
||||
KEY_FENRIR,KEY_HOME=first_clipboard
|
||||
KEY_FENRIR,KEY_END=last_clipboard
|
||||
KEY_FENRIR,KEY_PAGEUP=prev_clipboard
|
||||
KEY_FENRIR,KEY_PAGEDOWN=next_clipboard
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_C=curr_clipboard
|
||||
KEY_FENRIR,KEY_C=copy_marked_to_clipboard
|
||||
KEY_FENRIR,KEY_CTRL,KEY_SHIFT,KEY_C=copy_last_echo_to_clipboard
|
||||
KEY_FENRIR,KEY_V=paste_clipboard
|
||||
KEY_FENRIR,KEY_F5=import_clipboard_from_file
|
||||
KEY_FENRIR,KEY_F6=export_clipboard_to_file
|
||||
KEY_FENRIR,KEY_CTRL,KEY_SHIFT,KEY_X=remove_marks
|
||||
KEY_FENRIR,KEY_F9=set_mark
|
||||
KEY_FENRIR,KEY_F10=marked_text
|
||||
KEY_FENRIR,KEY_F10=toggle_vmenu_mode
|
||||
KEY_FENRIR,KEY_SPACE=current_quick_menu_entry
|
||||
KEY_FENRIR,KEY_CTRL,KEY_SPACE=current_quick_menu_value
|
||||
KEY_FENRIR,KEY_CTRL,KEY_RIGHT=next_quick_menu_entry
|
||||
KEY_FENRIR,KEY_CTRL,KEY_UP=next_quick_menu_value
|
||||
KEY_FENRIR,KEY_CTRL,KEY_LEFT=prev_quick_menu_entry
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOWN=prev_quick_menu_value
|
||||
KEY_FENRIR,KEY_CTRL,KEY_C=save_settings
|
||||
# linux specific
|
||||
#=import_clipboard_from_x
|
||||
KEY_FENRIR,KEY_F8=export_clipboard_to_x
|
||||
KEY_FENRIR,KEY_ALT,KEY_UP=inc_alsa_volume
|
||||
KEY_FENRIR,KEY_ALT,KEY_DOWN=dec_alsa_volume
|
||||
KEY_F4=cycle_keyboard_layout
|
||||
@@ -1,129 +0,0 @@
|
||||
KEY_FENRIR,KEY_F1=toggle_tutorial_mode
|
||||
KEY_FENRIR,KEY_H=toggle_tutorial_mode
|
||||
KEY_CTRL=shut_up
|
||||
KEY_FENRIR,KEY_CTRL,KEY_END=review_bottom
|
||||
KEY_FENRIR,KEY_CTRL,KEY_HOME=review_top
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_DOT=review_curr_line
|
||||
KEY_FENRIR,KEY_U=review_prev_line
|
||||
KEY_FENRIR,KEY_O=review_next_line
|
||||
KEY_FENRIR,KEY_HOME=review_line_begin
|
||||
KEY_FENRIR,KEY_END=review_line_end
|
||||
KEY_FENRIR,KEY_CTRL,KEY_J=review_line_first_char
|
||||
KEY_FENRIR,KEY_CTRL,KEY_L=review_line_last_char
|
||||
KEY_FENRIR,KEY_ALT,KEY_1=present_first_line
|
||||
KEY_FENRIR,KEY_ALT,KEY_2=present_last_line
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOT=review_curr_word
|
||||
KEY_FENRIR,KEY_J=review_prev_word
|
||||
KEY_FENRIR,KEY_L=review_next_word
|
||||
2,KEY_FENRIR,KEY_CTRL,KEY_DOT=review_curr_word_phonetic
|
||||
2,KEY_FENRIR,KEY_J=review_prev_word_phonetic
|
||||
2,KEY_FENRIR,KEY_L=review_next_word_phonetic
|
||||
KEY_FENRIR,KEY_COMMA=review_curr_char
|
||||
KEY_FENRIR,KEY_M=review_prev_char
|
||||
KEY_FENRIR,KEY_DOT=review_next_char
|
||||
2,KEY_FENRIR,KEY_COMMA=curr_char_phonetic
|
||||
2,KEY_FENRIR,KEY_M=prev_char_phonetic
|
||||
2,KEY_FENRIR,KEY_DOT=next_char_phonetic
|
||||
KEY_FENRIR,KEY_CTRL,KEY_I=review_up
|
||||
KEY_FENRIR,KEY_CTRL,KEY_COMMA=review_down
|
||||
KEY_FENRIR,KEY_SLASH=exit_review
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_DOT=cursor_position
|
||||
2,KEY_FENRIR,KEY_I=indent_curr_line
|
||||
KEY_FENRIR,KEY_B=curr_screen
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_I=curr_screen_before_cursor
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_COMMA=curr_screen_after_cursor
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_PAGEDOWN=cursor_read_to_end_of_line
|
||||
#=cursor_column
|
||||
#=cursor_lineno
|
||||
#=braille_flush
|
||||
KEY_FENRIR,KEY_CTRL,KEY_T=braille_return_to_cursor
|
||||
#=braille_pan_left
|
||||
#=braille_pan_right
|
||||
KEY_FENRIR,KEY_CTRL,KEY_1=clear_bookmark_1
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_1=set_bookmark_1
|
||||
KEY_FENRIR,KEY_1=bookmark_1
|
||||
KEY_FENRIR,KEY_K=bookmark_1
|
||||
KEY_FENRIR,KEY_CTRL,KEY_2=clear_bookmark_2
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_2=set_bookmark_2
|
||||
KEY_FENRIR,KEY_2=bookmark_2
|
||||
KEY_FENRIR,KEY_CTRL,KEY_3=clear_bookmark_3
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_3=set_bookmark_3
|
||||
KEY_FENRIR,KEY_3=bookmark_3
|
||||
KEY_FENRIR,KEY_CTRL,KEY_4=clear_bookmark_4
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_4=set_bookmark_4
|
||||
KEY_FENRIR,KEY_4=bookmark_4
|
||||
KEY_FENRIR,KEY_CTRL,KEY_5=clear_bookmark_5
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_5=set_bookmark_5
|
||||
KEY_FENRIR,KEY_5=bookmark_5
|
||||
KEY_FENRIR,KEY_CTRL,KEY_6=clear_bookmark_6
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_6=set_bookmark_6
|
||||
KEY_FENRIR,KEY_6=bookmark_6
|
||||
KEY_FENRIR,KEY_CTRL,KEY_7=clear_bookmark_7
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_7=set_bookmark_7
|
||||
KEY_FENRIR,KEY_7=bookmark_7
|
||||
KEY_FENRIR,KEY_CTRL,KEY_8=clear_bookmark_8
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_8=set_bookmark_8
|
||||
KEY_FENRIR,KEY_8=bookmark_8
|
||||
KEY_FENRIR,KEY_CTRL,KEY_9=clear_bookmark_9
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_9=set_bookmark_9
|
||||
KEY_FENRIR,KEY_9=bookmark_9
|
||||
KEY_FENRIR,KEY_CTRL,KEY_0=clear_bookmark_10
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_0=set_bookmark_10
|
||||
KEY_FENRIR,KEY_0=bookmark_10
|
||||
KEY_FENRIR,KEY_CTRL,KEY_8=set_window_application
|
||||
2,KEY_FENRIR,KEY_CTRL,KEY_8=clear_window_application
|
||||
KEY_FENRIR,KEY_SEMICOLON=last_incoming
|
||||
#=toggle_braille
|
||||
KEY_FENRIR,KEY_F3=toggle_sound
|
||||
KEY_FENRIR,KEY_F4=toggle_speech
|
||||
KEY_FENRIR,KEY_ENTER=temp_disable_speech
|
||||
KEY_FENRIR,KEY_P=toggle_punctuation_level
|
||||
KEY_FENRIR,KEY_RIGHTBRACE=toggle_auto_spell_check
|
||||
KEY_FENRIR,KEY_S=toggle_output
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_E=toggle_emoticons
|
||||
KEY_FENRIR,KEY_5=toggle_auto_read
|
||||
KEY_FENRIR,KEY_CTRL,KEY_T=toggle_auto_time
|
||||
KEY_FENRIR,KEY_Y=toggle_highlight_tracking
|
||||
#=toggle_barrier
|
||||
KEY_FENRIR,KEY_Q=quit_fenrir
|
||||
KEY_FENRIR,KEY_T=time
|
||||
KEY_FENRIR,KEY_F12=time
|
||||
2,KEY_FENRIR,KEY_T=date
|
||||
2,KEY_FENRIR,KEY_F12=date
|
||||
KEY_FENRIR,KEY_BACKSLASH=toggle_auto_indent
|
||||
KEY_FENRIR,KEY_F=attribute_cursor
|
||||
#=toggle_has_attribute
|
||||
KEY_FENRIR,KEY_F7=spell_check
|
||||
2,KEY_FENRIR,KEY_S=add_word_to_spell_check
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_S=remove_word_from_spell_check
|
||||
KEY_FENRIR,KEY_F2=forward_keypress
|
||||
KEY_FENRIR,KEY_ALT,KEY_UP=inc_sound_volume
|
||||
KEY_FENRIR,KEY_ALT,KEY_DOWN=dec_sound_volume
|
||||
#=clear_clipboard
|
||||
#=first_clipboard
|
||||
#=last_clipboard
|
||||
KEY_FENRIR,KEY_PAGEUP=prev_clipboard
|
||||
KEY_FENRIR,KEY_PAGEDOWN=next_clipboard
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_C=curr_clipboard
|
||||
KEY_FENRIR,KEY_C=copy_marked_to_clipboard
|
||||
KEY_FENRIR,KEY_CTRL,KEY_SHIFT,KEY_C=copy_last_echo_to_clipboard
|
||||
KEY_FENRIR,KEY_V=paste_clipboard
|
||||
KEY_FENRIR,KEY_F5=import_clipboard_from_file
|
||||
KEY_FENRIR,KEY_F6=export_clipboard_to_file
|
||||
KEY_FENRIR,KEY_CTRL,KEY_SHIFT,KEY_X=remove_marks
|
||||
KEY_FENRIR,KEY_F9=set_mark
|
||||
KEY_FENRIR,KEY_F10=marked_text
|
||||
KEY_FENRIR,KEY_SHIFT,KEY_F10=toggle_vmenu_mode
|
||||
KEY_FENRIR,KEY_SPACE=current_quick_menu_entry
|
||||
KEY_FENRIR,KEY_CTRL,KEY_SPACE=current_quick_menu_value
|
||||
KEY_FENRIR,KEY_CTRL,KEY_RIGHT=next_quick_menu_entry
|
||||
KEY_FENRIR,KEY_CTRL,KEY_UP=next_quick_menu_value
|
||||
KEY_FENRIR,KEY_CTRL,KEY_LEFT=prev_quick_menu_entry
|
||||
KEY_FENRIR,KEY_CTRL,KEY_DOWN=prev_quick_menu_value
|
||||
KEY_FENRIR,KEY_CTRL,KEY_C=save_settings
|
||||
# linux specific
|
||||
#=import_clipboard_from_x
|
||||
KEY_FENRIR,KEY_F8=export_clipboard_to_x
|
||||
KEY_FENRIR,KEY_ALT,KEY_UP=inc_alsa_volume
|
||||
KEY_FENRIR,KEY_ALT,KEY_DOWN=dec_alsa_volume
|
||||
KEY_F4=cycle_keyboard_layout
|
||||
+151
-102
@@ -3,9 +3,9 @@
|
||||
enabled=True
|
||||
|
||||
# Select the driver used to play sounds, choices are genericDriver and gstreamerDriver.
|
||||
# Sox is the default.
|
||||
#driver=gstreamerDriver
|
||||
driver=genericDriver
|
||||
# Gstreamer 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
|
||||
@@ -24,12 +24,12 @@ volume=0.7
|
||||
# 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
|
||||
generic_play_file_command=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
|
||||
generic_frequency_command=play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence
|
||||
|
||||
# Enable progress bar monitoring with ascending tones by default
|
||||
progressMonitoring=True
|
||||
progress_monitoring=True
|
||||
|
||||
[speech]
|
||||
# Turn speech on or off:
|
||||
@@ -45,7 +45,7 @@ rate=0.5
|
||||
# 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
|
||||
capital_pitch=0.9
|
||||
|
||||
# Volume controls the loudness of the voice, select from 0, quietest, to 1.0, loudest.
|
||||
volume=1.0
|
||||
@@ -65,10 +65,10 @@ volume=1.0
|
||||
#language=en
|
||||
|
||||
# Read new text as it happens?
|
||||
autoReadIncoming=True
|
||||
auto_read_incoming=True
|
||||
|
||||
# Speak individual numbers instead of whole string.
|
||||
readNumbersAsDigits = False
|
||||
read_numbers_as_digits = False
|
||||
|
||||
# genericSpeechCommand is the command that is executed for talking
|
||||
# the following variables are replaced with values
|
||||
@@ -81,105 +81,130 @@ readNumbersAsDigits = False
|
||||
# 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"
|
||||
generic_speech_command=espeak-ng -a fenrir_volume -s fenrir_rate -p fenrir_pitch -v fenrir_voice -- "fenrir_text"
|
||||
|
||||
# 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
|
||||
# min and max values of the TTS system that is used in generic_speech_command
|
||||
fenrir_min_volume=0
|
||||
fenrir_max_volume=200
|
||||
fenrir_min_pitch=0
|
||||
fenrir_max_pitch=99
|
||||
fenrir_min_rate=80
|
||||
fenrir_max_rate=450
|
||||
|
||||
[screen]
|
||||
# Screen driver: vcsaDriver (Linux TTY/virtual console), ptyDriver (terminal emulation)
|
||||
driver=vcsaDriver
|
||||
# Text encoding: auto (auto-detect), utf-8, latin1, etc.
|
||||
encoding=auto
|
||||
screenUpdateDelay=0.05
|
||||
ignoreScreen=7
|
||||
autodetectIgnoreScreen=True
|
||||
# Delay in seconds between screen updates (lower = more responsive, higher = less CPU)
|
||||
# Recommended: 0.05 for most systems
|
||||
screen_update_delay=0.05
|
||||
# TTY number to completely ignore (e.g., 7 for X11/graphical login screen)
|
||||
ignore_screen=7
|
||||
# Automatically detect and ignore graphical TTYs (X11, Wayland sessions)
|
||||
autodetect_ignore_screen=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
|
||||
grab_devices=True
|
||||
ignore_shortcuts=False
|
||||
# the current shortcut layout located in /etc/fenrirscreenreader/keyboard
|
||||
keyboardLayout=desktop
|
||||
keyboard_layout=desktop
|
||||
# echo chars while typing.
|
||||
# 0 = None
|
||||
# 1 = always
|
||||
# 2 = only while capslock
|
||||
charEchoMode=1
|
||||
# 2 = only while capslock (not compatible with capslock as fenrir key)
|
||||
char_echo_mode=1
|
||||
# echo deleted chars
|
||||
charDeleteEcho=True
|
||||
char_delete_echo=True
|
||||
# echo word after pressing space
|
||||
wordEcho=False
|
||||
word_echo=False
|
||||
# interrupt speech on any keypress
|
||||
interruptOnKeyPress=True
|
||||
interrupt_on_key_press=True
|
||||
# you can filter the keys on that the speech should interrupt (empty = all keys, otherwhise the given keys)
|
||||
interruptOnKeyPressFilter=
|
||||
interrupt_on_key_press_filter=
|
||||
# timeout for double tap in sec
|
||||
doubleTapTimeout=0.2
|
||||
double_tap_timeout=0.2
|
||||
|
||||
[general]
|
||||
# Debug levels: 0=DEACTIVE, 1=ERROR, 2=WARNING, 3=INFO (most verbose)
|
||||
# For production use, WARNING (2) provides good balance of useful info without spam
|
||||
debugLevel=2
|
||||
debug_level=2
|
||||
# debugMode sets where the debug output should send to:
|
||||
# debugMode=File writes to debugFile (Default:/tmp/fenrir-PID.log)
|
||||
# debugMode=File writes to debug_file (Default:/tmp/fenrir-PID.log)
|
||||
# debugMode=Print just prints on the screen
|
||||
debugMode=File
|
||||
debugFile=
|
||||
punctuationProfile=default
|
||||
punctuationLevel=some
|
||||
respectPunctuationPause=True
|
||||
debug_mode=File
|
||||
debug_file=
|
||||
punctuation_profile=default
|
||||
punctuation_level=some
|
||||
respect_punctuation_pause=True
|
||||
# Replace undefined punctuation with spaces instead of removing them
|
||||
# This improves readability of text with punctuation like [X]mute, IP addresses, etc.
|
||||
replaceUndefinedPunctuationWithSpace=True
|
||||
newLinePause=True
|
||||
numberOfClipboards=50
|
||||
replace_undefined_punctuation_with_space=True
|
||||
# Pause speech briefly at newline characters for better readability
|
||||
new_line_pause=True
|
||||
number_of_clipboards=50
|
||||
# used path for "export_clipboard_to_file"
|
||||
# $user is replaced by username
|
||||
#clipboardExportPath=/home/$user/fenrirClipboard
|
||||
clipboardExportPath=/tmp/fenrirClipboard
|
||||
clipboard_export_path=/tmp/fenrirClipboard
|
||||
# Convert text emoticons like :) to descriptive text (e.g., "smiling face")
|
||||
emoticons=True
|
||||
# define the current Fenrir key
|
||||
fenrirKeys=KEY_KP0,KEY_META,KEY_INSERT
|
||||
scriptKeys=KEY_COMPOSE
|
||||
timeFormat=%%I:%%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 change
|
||||
hasAttributes=True
|
||||
# shell for PTY emulatiun (empty = default shell)
|
||||
# Define the Fenrir modifier key(s) - used to trigger Fenrir commands
|
||||
# Examples: KEY_KP0 (numpad 0), KEY_META (Super/Windows), KEY_INSERT
|
||||
# Multiple keys: KEY_KP0,KEY_META,KEY_INSERT
|
||||
fenrir_keys=KEY_KP0,KEY_META,KEY_INSERT
|
||||
# Script key - used to execute custom scripts in script_path
|
||||
script_keys=KEY_COMPOSE
|
||||
# Time format using Python strftime codes (https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior)
|
||||
# Default: %%I:%%M%%P (12-hour format, e.g., "06:38pm")
|
||||
# 24-hour format: %%H:%%M (e.g., "18:38")
|
||||
# Common codes: %%I=12hr hour, %%H=24hr hour, %%M=minute, %%S=second, %%P=am/pm lowercase, %%p=AM/PM uppercase
|
||||
time_format=%%I:%%M%%P
|
||||
# Date format using Python strftime codes
|
||||
# Default: %%A, %%B %%d, %%Y (e.g., "Tuesday, December 10, 2024")
|
||||
# ISO format: %%Y-%%m-%%d (e.g., "2024-12-10")
|
||||
# Common codes: %%A=weekday name, %%B=month name, %%d=day, %%Y=year with century, %%m=month number
|
||||
date_format=%%A, %%B %%d, %%Y
|
||||
# Automatically spell check words when reviewing character-by-character
|
||||
auto_spell_check=True
|
||||
# Language for spell checking (format: language_COUNTRY, e.g., en_US, en_GB, es_ES)
|
||||
spell_check_language=en_US
|
||||
# path for your scripts "script_keys" functionality
|
||||
script_path=/usr/share/fenrirscreenreader/scripts
|
||||
# Override default commands or add custom commands without modifying Fenrir installation
|
||||
# Leave empty to use default commands only
|
||||
command_path=
|
||||
# Format string for announcing text attributes (colors, formatting)
|
||||
# Available variables:
|
||||
# fenrirBGColor = the background color
|
||||
# fenrirFGColor = the foreground color
|
||||
# fenrirUnderline = speak the underline attribute
|
||||
# fenrirBold = speak the bold attribute
|
||||
# fenrirBlink = speak the blink attribute
|
||||
# fenrirFont = the font name
|
||||
# fenrirFontSize = the font size
|
||||
attribute_format_string=Background fenrirBGColor,Foreground fenrirFGColor,fenrirUnderline,fenrirBold,fenrirBlink, Font fenrirFont,Fontsize fenrirFontSize
|
||||
# Automatically announce indentation level changes (useful for Python, YAML, etc.)
|
||||
auto_present_indent=False
|
||||
# How to present indentation changes:
|
||||
# 0 = sound and speak (both audio feedback and voice announcement)
|
||||
# 1 = sound only (just play a tone)
|
||||
# 2 = speak only (just voice announcement)
|
||||
auto_present_indent_mode=1
|
||||
# Play a sound when text attributes (color, bold, etc.) change
|
||||
has_attributes=True
|
||||
# Shell to use for PTY emulation mode (empty = use system default shell)
|
||||
# Examples: /bin/bash, /bin/zsh, /usr/bin/fish
|
||||
shell=
|
||||
|
||||
[focus]
|
||||
#follow the text cursor
|
||||
# Follow and announce text cursor position changes
|
||||
cursor=True
|
||||
#follow highlighted text changes
|
||||
# Follow and announce highlighted/selected text changes (useful in menus)
|
||||
highlight=False
|
||||
|
||||
[remote]
|
||||
@@ -191,33 +216,52 @@ driver=unixDriver
|
||||
# tcp port
|
||||
port=22447
|
||||
# socket filepath
|
||||
socketFile=
|
||||
socket_file=
|
||||
# allow settings to overwrite
|
||||
enableSettingsRemote=True
|
||||
enable_settings_remote=True
|
||||
# allow commands to be executed
|
||||
enableCommandRemote=True
|
||||
enable_command_remote=True
|
||||
|
||||
[barrier]
|
||||
# Enable barrier detection - automatically detects table/box borders for improved navigation
|
||||
enabled=False
|
||||
leftBarriers=│└┌─
|
||||
rightBarriers=│┘┐─
|
||||
# Characters that represent left-side barriers/borders (for table/box detection)
|
||||
left_barriers=│└┌─
|
||||
# Characters that represent right-side barriers/borders (for table/box detection)
|
||||
right_barriers=│┘┐─
|
||||
|
||||
[review]
|
||||
lineBreak=True
|
||||
endOfScreen=True
|
||||
# leave the review when pressing a key
|
||||
leaveReviewOnCursorChange=True
|
||||
# leave the review when changing the screen
|
||||
leaveReviewOnScreenChange=True
|
||||
# Announce line breaks during review mode
|
||||
line_break=True
|
||||
# Announce when reaching the end of screen during review
|
||||
end_of_screen=True
|
||||
# Exit review mode when cursor position changes (e.g., when typing)
|
||||
leave_review_on_cursor_change=True
|
||||
# Exit review mode when switching to a different TTY/screen
|
||||
leave_review_on_screen_change=True
|
||||
|
||||
[promote]
|
||||
# Enable promoting (announcing) important text updates automatically
|
||||
enabled=True
|
||||
inactiveTimeoutSec=120
|
||||
# Seconds of inactivity before promoting text updates (prevents spam during active typing)
|
||||
inactive_timeout_sec=120
|
||||
# Comma-separated list of text patterns to promote when detected
|
||||
# Leave empty to disable pattern-based promotion
|
||||
list=
|
||||
|
||||
[menu]
|
||||
vmenuPath=
|
||||
quickMenu=speech#rate;speech#pitch;speech#volume
|
||||
# Custom path for VMenu (virtual menu) profiles
|
||||
# Leave empty to use default location (/etc/fenrirscreenreader/vmenu-profiles/)
|
||||
vmenu_path=
|
||||
# Quick menu: Semicolon-separated list of settings for rapid adjustment via VMenu
|
||||
# Access with Fenrir+F10, navigate to "Quick Settings"
|
||||
# Format: section#setting;section#setting;...
|
||||
# Supported settings:
|
||||
# - speech#rate, speech#pitch, speech#volume (0.0-1.0)
|
||||
# - speech#module, speech#voice (speechdDriver only, auto-added)
|
||||
# Note: speech#module and speech#voice are automatically added when
|
||||
# speechdDriver is active. Do not add them manually.
|
||||
quick_menu=speech#rate;speech#pitch;speech#volume
|
||||
|
||||
[prompt]
|
||||
# Custom prompt patterns for silence until prompt feature
|
||||
@@ -235,27 +279,32 @@ quickMenu=speech#rate;speech#pitch;speech#volume
|
||||
# For "[user@hostname ~] $" use: \[.*@.*\s.*\]\s*[$#>]\s*
|
||||
# For custom prompts ending with specific strings, use patterns like: .*your_prompt_ending$
|
||||
# For custom package manager prompts: .*your_package_manager.*\[[YyNn]/[YyNn]\].*
|
||||
customPatterns=
|
||||
custom_patterns=
|
||||
|
||||
# Specific prompt strings to match exactly (useful for very specific custom prompts)
|
||||
# Format: exactMatches=prompt1,prompt2,prompt3
|
||||
# Format: exact_matches=prompt1,prompt2,prompt3
|
||||
# Examples:
|
||||
# exactMatches=[storm@fenrir ~] $,[root@fenrir ~] #,Continue installation? [Y/n]
|
||||
exactMatches=
|
||||
# exact_matches=[storm@fenrir ~] $,[root@fenrir ~] #,Continue installation? [Y/n]
|
||||
exact_matches=
|
||||
|
||||
[time]
|
||||
# automatic time announcement
|
||||
# Enable automatic time announcements
|
||||
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 the current time
|
||||
present_time=True
|
||||
# Announce the date (only when it changes, e.g., at midnight)
|
||||
present_date=True
|
||||
# Announce time every N seconds (0 = disabled)
|
||||
# If delay_sec > 0, on_minutes is ignored
|
||||
delay_sec=0
|
||||
# Announce time at specific minutes of each hour (comma-separated)
|
||||
# Examples:
|
||||
# 00 = Only on the hour (1:00, 2:00, 3:00, etc.)
|
||||
# 00,30 = On the hour and half-hour (1:00, 1:30, 2:00, 2:30, etc.)
|
||||
# 00,15,30,45 = Every 15 minutes
|
||||
# Note: This is ignored if delay_sec > 0
|
||||
on_minutes=00,30
|
||||
# Play a sound icon before time announcement (non-interrupting)
|
||||
announce=True
|
||||
# interrupt current speech for time announcement
|
||||
# Interrupt current speech to announce time immediately
|
||||
interrupt=False
|
||||
|
||||
+41
-41
@@ -7,14 +7,14 @@ configurable and easy to customize and extend.
|
||||
=== Credit and intended audience
|
||||
|
||||
This document is just a customization for Slint of the genuine
|
||||
https://github.com/chrys87/fenrir/blob/master/docu/user.txt[Fenrir User
|
||||
https://git.stormux.org/storm/fenrir/src/branch/master/docs/user.txt[Fenrir User
|
||||
Manual] motly written by Chrys, main developer of Fenrir.
|
||||
|
||||
It has been adapted to its intended audience: end users of Fenrir on
|
||||
Slint where it is already installed, thus concentrates on its setting
|
||||
and usage. You will find more information about its features,
|
||||
installation and how customize and troubleshoot it and contribute to its
|
||||
development on https://github.com/chrys87/fenrir[the Fenrir Git
|
||||
development on https://git.stormux.org/storm/fenrir[the Fenrir Git
|
||||
repository].
|
||||
|
||||
=== Getting started with Fenrir
|
||||
@@ -1105,7 +1105,7 @@ the word "Chrys" appears after 120 Seconds of inactivity:
|
||||
....
|
||||
[promote]
|
||||
enabled=True
|
||||
inactiveTimeoutSec=120
|
||||
inactive_timeout_sec=120
|
||||
list=Chrys
|
||||
....
|
||||
|
||||
@@ -1286,8 +1286,8 @@ Enable remote control in settings.conf:
|
||||
[remote]
|
||||
enable=True
|
||||
driver=unixDriver
|
||||
enableSettingsRemote=True
|
||||
enableCommandRemote=True
|
||||
enable_settings_remote=True
|
||||
enable_command_remote=True
|
||||
....
|
||||
|
||||
==== Using socat with Unix Sockets
|
||||
@@ -1317,13 +1317,13 @@ echo "setting set focus#highlight=True" | socat - UNIX-CLIENT:/tmp/fenrirscreenr
|
||||
echo "setting set speech#rate=0.8" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Change punctuation level (none/some/most/all)
|
||||
echo "setting set general#punctuationLevel=all" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
echo "setting set general#punctuation_level=all" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Voice and TTS control
|
||||
echo "setting set speech#voice=en-us+f3" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Multiple settings at once
|
||||
echo "setting set speech#rate=0.8;sound#volume=0.7;general#punctuationLevel=most" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
echo "setting set speech#rate=0.8;sound#volume=0.7;general#punctuation_level=most" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Reset all settings to defaults
|
||||
echo "setting reset" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
@@ -1394,8 +1394,8 @@ echo "command quitapplication" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-dea
|
||||
* `+speech#module=module_name+` - TTS module (e.g., "espeak-ng")
|
||||
|
||||
*General Settings:*
|
||||
* `+general#punctuationLevel=none/some/most/all+` - Punctuation verbosity
|
||||
* `+general#autoSpellCheck=True/False+` - Automatic spell checking
|
||||
* `+general#punctuation_level=none/some/most/all+` - Punctuation verbosity
|
||||
* `+general#auto_spell_check=True/False+` - Automatic spell checking
|
||||
* `+general#emoticons=True/False+` - Enable emoticon replacement
|
||||
|
||||
*Sound Settings:*
|
||||
@@ -1407,11 +1407,11 @@ echo "command quitapplication" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-dea
|
||||
* `+focus#highlight=True/False+` - Follow text highlighting
|
||||
|
||||
*Keyboard Settings:*
|
||||
* `+keyboard#charEchoMode=0-2+` - Character echo (0=none, 1=always, 2=capslock only)
|
||||
* `+keyboard#wordEcho=True/False+` - Echo complete words
|
||||
* `+keyboard#char_echo_mode=0-2+` - Character echo (0=none, 1=always, 2=capslock only)
|
||||
* `+keyboard#word_echo=True/False+` - Echo complete words
|
||||
|
||||
*Screen Settings:*
|
||||
* `+screen#ignoreScreen=1,2,3+` - TTY screens to ignore
|
||||
* `+screen#ignore_screen=1,2,3+` - TTY screens to ignore
|
||||
|
||||
==== settings.conf syntax
|
||||
|
||||
@@ -1511,14 +1511,14 @@ frequencies.
|
||||
sound file.
|
||||
|
||||
....
|
||||
genericPlayFileCommand=<your command for playing a file>
|
||||
generic_play_file_command=<your command for playing a file>
|
||||
....
|
||||
|
||||
`+genericFrequencyCommand+` defines the command that is used playing
|
||||
frequencies.
|
||||
|
||||
....
|
||||
genericFrequencyCommand=<your command for playing a frequence>
|
||||
generic_frequency_command=<your command for playing a frequence>
|
||||
....
|
||||
|
||||
The following variables are substituted in `+genericPlayFileCommand+`
|
||||
@@ -1532,13 +1532,13 @@ and `+genericFrequencyCommand+`:
|
||||
Example genericPlayFileCommand (default)
|
||||
|
||||
....
|
||||
genericPlayFileCommand=play -q -v fenrirVolume fenrirSoundFile
|
||||
generic_play_file_command=play -q -v fenrirVolume fenrirSoundFile
|
||||
....
|
||||
|
||||
Example genericFrequencyCommand (default)
|
||||
|
||||
....
|
||||
genericFrequencyCommand=play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence
|
||||
generic_frequency_command=play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence
|
||||
....
|
||||
|
||||
==== Speech
|
||||
@@ -1579,7 +1579,7 @@ Values: Range Minimum:`+0.0+` is lowest, Maximum:`+1.0+` is highest.
|
||||
A Pitch for capital letters can be set.
|
||||
|
||||
....
|
||||
capitalPitch=0.9
|
||||
capital_pitch=0.9
|
||||
....
|
||||
|
||||
Values: Range Minimum:`+0.0+` is lowest, Maximum:`+1.0+` is highest.
|
||||
@@ -1681,7 +1681,7 @@ the pico module:
|
||||
language=de-DE
|
||||
....
|
||||
|
||||
Read new text as it occurs autoReadIncoming=True Values: on=`+True+`,
|
||||
Read new text as it occurs auto_read_incoming=True Values: on=`+True+`,
|
||||
off=`+False+`
|
||||
|
||||
==== Screen
|
||||
@@ -1711,7 +1711,7 @@ Values:`+cp850+` is used for Western languages like USA or Europe.
|
||||
The driver updates Fenrir with changes on the screen.
|
||||
|
||||
....
|
||||
screenUpdateDelay=0.05
|
||||
screen_update_delay=0.05
|
||||
....
|
||||
|
||||
Values: in Seconds
|
||||
@@ -1776,7 +1776,7 @@ Gives Fenrir exclusive access to the keyboard and lets it control
|
||||
keystrokes. This is needed to intercept Fenrir related shortcuts.
|
||||
|
||||
....
|
||||
grabDevices=True
|
||||
grab_devices=True
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1785,7 +1785,7 @@ The following makes sense if you are using a second screenreader and
|
||||
want to have some hooked events. Fenrir ignores all shortcuts then.
|
||||
|
||||
....
|
||||
ignoreShortcuts=False
|
||||
ignore_shortcuts=False
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1793,7 +1793,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
The current keyboard layout used for shortcuts.
|
||||
|
||||
....
|
||||
keyboardLayout=desktop
|
||||
keyboard_layout=desktop
|
||||
....
|
||||
|
||||
Values: An absolute Path to a Keyboard definition file or a Filename
|
||||
@@ -1810,7 +1810,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
Announce deleted characters
|
||||
|
||||
....
|
||||
charDeleteEcho=True
|
||||
char_delete_echo=True
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1818,7 +1818,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
Announce word after pressing space
|
||||
|
||||
....
|
||||
wordEcho=False
|
||||
word_echo=False
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1826,7 +1826,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
Interrupt speech on any keypress
|
||||
|
||||
....
|
||||
interruptOnKeyPress=False
|
||||
interrupt_on_key_press=False
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1834,7 +1834,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
You can filter the keys that speech should interrupt
|
||||
|
||||
....
|
||||
interruptOnKeyPressFilter=
|
||||
interrupt_on_key_press_filter=
|
||||
....
|
||||
|
||||
Values: (List) empty = all keys, otherwise interrupt with specified keys
|
||||
@@ -1842,7 +1842,7 @@ Values: (List) empty = all keys, otherwise interrupt with specified keys
|
||||
The timeout that is used for double tap shortcuts
|
||||
|
||||
....
|
||||
doubleTapTimeout=0.2
|
||||
double_tap_timeout=0.2
|
||||
....
|
||||
|
||||
Values: Seconds
|
||||
@@ -1862,7 +1862,7 @@ Values: off=0, error=1, warning=2, info=3
|
||||
the current punctuation and dict file in use:
|
||||
|
||||
....
|
||||
punctuationProfile=default
|
||||
punctuation_profile=default
|
||||
....
|
||||
|
||||
Values: Text, see available profiles in `+/etc/fenrir/punctuation+` or
|
||||
@@ -1871,7 +1871,7 @@ in `+sourceTree/config/punctuation+`
|
||||
The current punctuation level in use:
|
||||
|
||||
....
|
||||
punctuationLevel=some
|
||||
punctuation_level=some
|
||||
....
|
||||
|
||||
Values: Text, See available levels in the used punctuation file.
|
||||
@@ -1879,7 +1879,7 @@ Values: Text, See available levels in the used punctuation file.
|
||||
Respect pause for punctuations:
|
||||
|
||||
....
|
||||
respectPunctuationPause=True
|
||||
respect_punctuation_pause=True
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1887,7 +1887,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
Add a pause on Line break:
|
||||
|
||||
....
|
||||
newLinePause=True
|
||||
new_line_pause=True
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1905,7 +1905,7 @@ Values: Text, Systemfilepath
|
||||
The number of available clipboards:
|
||||
|
||||
....
|
||||
numberOfClipboards=10
|
||||
number_of_clipboards=10
|
||||
....
|
||||
|
||||
Values: Integer, 1 - 999
|
||||
@@ -1921,7 +1921,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
Define the current Fenrir keys:
|
||||
|
||||
....
|
||||
fenrirKeys=KEY_KP0,KEY_META,KEY_INSERT
|
||||
fenrir_keys=KEY_KP0,KEY_META,KEY_INSERT
|
||||
....
|
||||
|
||||
Values, Text list, separated by comma.
|
||||
@@ -1955,7 +1955,7 @@ https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior[d
|
||||
Enable or Disable spellcheck whilst typing:
|
||||
|
||||
....
|
||||
autoSpellCheck=True
|
||||
auto_spell_check=True
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -1963,7 +1963,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
The use of the dictionary with spellcheck:
|
||||
|
||||
....
|
||||
spellCheckLanguage=en_US
|
||||
spell_check_language=en_US
|
||||
....
|
||||
|
||||
Values: Text, see aspell dictionary's.
|
||||
@@ -2044,7 +2044,7 @@ Values: on=`+True+`, off=`+False+`
|
||||
Leave the review mode when changing the screen (From TTY3 to TTY4):
|
||||
|
||||
....
|
||||
leaveReviewOnScreenChange=True
|
||||
leave_review_on_screen_change=True
|
||||
....
|
||||
|
||||
Values: on=`+True+`, off=`+False+`
|
||||
@@ -2064,7 +2064,7 @@ The minimum time interval of inactivity to activate promoting. By
|
||||
default it promotes after 120 Seconds inactivity:
|
||||
|
||||
....
|
||||
inactiveTimeoutSec=120
|
||||
inactive_timeout_sec=120
|
||||
....
|
||||
|
||||
Values: in Seconds
|
||||
@@ -2193,9 +2193,9 @@ settings.conf). Commands are python files with a special scheme. You can
|
||||
assign them to a shortcut using the filename without an extension or
|
||||
place them in a hook trigger like OnInput or OnScreenChange. For further
|
||||
information see developer guide. Good Examples:
|
||||
https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/date.py["date.py"]
|
||||
https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/date.py["date.py"]
|
||||
(announce the Date),
|
||||
https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/shut_up.py["shut_up.py"]
|
||||
https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/shut_up.py["shut_up.py"]
|
||||
(interrupt output) the basic scheme for a command is as follows:
|
||||
|
||||
....
|
||||
@@ -2218,7 +2218,7 @@ class command():
|
||||
pass
|
||||
....
|
||||
|
||||
* https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/command_template.py[Template
|
||||
* https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/command_template.py[Template
|
||||
lives here]
|
||||
* The class needs to have the name "command".
|
||||
* "initialize" is running once whilst loading the command.
|
||||
@@ -2276,7 +2276,7 @@ root.
|
||||
=== Bugreports and feature requests
|
||||
|
||||
Please report Bugs and feature requests to:
|
||||
https://github.com/chrys87/fenrir/issues
|
||||
https://git.stormux.org/storm/fenrir/issues
|
||||
|
||||
for bugs please provide a link:#Howto create a debug file[debug] file
|
||||
that shows the issue.
|
||||
|
||||
+11
-11
@@ -102,17 +102,17 @@ volume=0.7
|
||||
|
||||
[keyboard]
|
||||
driver=evdevDriver
|
||||
keyboardLayout=desktop
|
||||
keyboard_layout=desktop
|
||||
|
||||
[screen]
|
||||
driver=vcsaDriver
|
||||
ignoreScreen=
|
||||
ignore_screen=
|
||||
|
||||
[remote]
|
||||
enable=True
|
||||
driver=unixDriver
|
||||
enableSettingsRemote=True
|
||||
enableCommandRemote=True
|
||||
enable_settings_remote=True
|
||||
enable_command_remote=True
|
||||
```
|
||||
|
||||
## Remote Control
|
||||
@@ -126,8 +126,8 @@ Enable remote control in settings:
|
||||
enable=True
|
||||
driver=unixDriver # or tcpDriver
|
||||
port=22447 # for TCP driver
|
||||
enableSettingsRemote=True # allow settings changes
|
||||
enableCommandRemote=True # allow command execution
|
||||
enable_settings_remote=True # allow settings changes
|
||||
enable_command_remote=True # allow command execution
|
||||
```
|
||||
|
||||
### Basic Usage with socat
|
||||
@@ -155,14 +155,14 @@ echo "setting set speech#pitch=0.6" | socat - UNIX-CLIENT:/tmp/fenrirscreenreade
|
||||
echo "setting set speech#volume=0.9" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Change punctuation level (none/some/most/all)
|
||||
echo "setting set general#punctuationLevel=all" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
echo "setting set general#punctuation_level=all" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Voice and TTS control
|
||||
echo "setting set speech#voice=en-us+f3" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
echo "setting set speech#module=espeak-ng" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Multiple settings at once
|
||||
echo "setting set speech#rate=0.8;sound#volume=0.7;general#punctuationLevel=most" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
echo "setting set speech#rate=0.8;sound#volume=0.7;general#punctuation_level=most" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
|
||||
# Reset all settings
|
||||
echo "setting reset" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
|
||||
@@ -208,11 +208,11 @@ echo "command quitapplication" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-dea
|
||||
- `command resetvmenu` - Reset virtual menu
|
||||
|
||||
**Key Settings You Can Change:**
|
||||
- Punctuation level: `setting set general#punctuationLevel=all`
|
||||
- Punctuation level: `setting set general#punctuation_level=all`
|
||||
- Speech parameters: `setting set speech#rate=0.8;speech#pitch=0.6`
|
||||
- Voice selection: `setting set speech#voice=en-us+f3`
|
||||
- Character echo: `setting set keyboard#charEchoMode=1`
|
||||
- Screen ignore: `setting set screen#ignoreScreen=1,2,3`
|
||||
- Character echo: `setting set keyboard#char_echo_mode=1`
|
||||
- Screen ignore: `setting set screen#ignore_screen=1,2,3`
|
||||
|
||||
### Scripting Integration
|
||||
|
||||
|
||||
+38
-38
@@ -160,7 +160,7 @@ For Arch there are PKGBUILDs in the AUR:
|
||||
|
||||
- Download the latest stable version from the [[https://linux-a11y.org/index.php?page=fenrir-screenreader|Fenrir-Project]] site.
|
||||
- Unpack the archive
|
||||
- Check the needed Dependencys by running [[https://github.com/chrys87/fenrir/blob/master/check-dependencies.py|check-dependencys.py]] script
|
||||
- Check the needed Dependencys by running [[https://git.stormux.org/storm/fenrir/src/branch/master/check-dependencies.py|check-dependencys.py]] script
|
||||
- install the missing dependencies an standard installation requires the following:
|
||||
* python3 >= 3.3 (and all the following is needed for python3 )
|
||||
* python3-speechd (screen)
|
||||
@@ -171,7 +171,7 @@ For Arch there are PKGBUILDs in the AUR:
|
||||
* python3-pyenchant (spellchecker)
|
||||
* your language for aspell (aspell-<lang>) (spellchecker)
|
||||
* sox (sound)
|
||||
* For an individual installation see [[#Support and Requirements|Support and Requirements]] or consult the [[https://github.com/chrys87/fenrir/blob/master/README.md|Readme]])
|
||||
* For an individual installation see [[#Support and Requirements|Support and Requirements]] or consult the [[https://git.stormux.org/storm/fenrir/src/branch/master/README.md|Readme]])
|
||||
- run "install.sh" as root
|
||||
|
||||
this installs Fenrir as the following
|
||||
@@ -185,7 +185,7 @@ to remove Fenrir just run uninstall.sh as root
|
||||
|
||||
if you want to get the latest code you can use git to get a development snapshot:
|
||||
|
||||
git clone https://github.com/chrys87/fenrir.git
|
||||
git clone https://git.stormux.org/storm/fenrir.git
|
||||
|
||||
===== Auto Start =====
|
||||
|
||||
@@ -735,7 +735,7 @@ you can define a list of words which you want to hear a sound icon for after a p
|
||||
Example if the word "Chrys" appears after 120 Seconds of inactivity:
|
||||
[promote]
|
||||
enabled=True
|
||||
inactiveTimeoutSec=120
|
||||
inactive_timeout_sec=120
|
||||
list=Chrys
|
||||
See section [[#Promote|Promote]] in ''settings.conf'' for more information.
|
||||
==== Punctuation ====
|
||||
@@ -867,9 +867,9 @@ Values: ''0.0'' is quietest, ''1.0'' is loudest.
|
||||
The generic sound driver uses shell commands for play sound and frequencies.
|
||||
|
||||
''genericPlayFileCommand'' defines the command that is used to play a sound file.
|
||||
genericPlayFileCommand=<your command for playing a file>
|
||||
generic_play_file_command=<your command for playing a file>
|
||||
''genericFrequencyCommand'' defines the command that is used playing frequencies.
|
||||
genericFrequencyCommand=<your command for playing a frequence>
|
||||
generic_frequency_command=<your command for playing a frequence>
|
||||
|
||||
The following variables are substituted in ''genericPlayFileCommand'' and ''genericFrequencyCommand'':
|
||||
* ''fenrirVolume'' = the current volume setting
|
||||
@@ -878,9 +878,9 @@ The following variables are substituted in ''genericPlayFileCommand'' and ''gene
|
||||
* ''fenrirDuration'' = the duration of the frequency
|
||||
|
||||
Example genericPlayFileCommand (default)
|
||||
genericPlayFileCommand=play -q -v fenrirVolume fenrirSoundFile
|
||||
generic_play_file_command=play -q -v fenrirVolume fenrirSoundFile
|
||||
Example genericFrequencyCommand (default)
|
||||
genericFrequencyCommand=play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence
|
||||
generic_frequency_command=play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence
|
||||
==== Speech ====
|
||||
Speech is configured in section ''[speech]''.
|
||||
Turn speech on or off:
|
||||
@@ -910,7 +910,7 @@ Pitch controls the pitch of the voice.
|
||||
Values: Range Minimum:''0.0'' is lowest, Maximum:''1.0'' is highest.
|
||||
|
||||
A Pitch for capital letters can be set.
|
||||
capitalPitch=0.9
|
||||
capital_pitch=0.9
|
||||
Values: Range Minimum:''0.0'' is lowest, Maximum:''1.0'' is highest.
|
||||
|
||||
The Volume controls the loudness of the voice.
|
||||
@@ -931,7 +931,7 @@ Select the language you want Fenrir to use.
|
||||
Values: Text, see your TTS synths documentation what is available.
|
||||
|
||||
Read new text as it occurs
|
||||
autoReadIncoming=True
|
||||
auto_read_incoming=True
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
=== Generic Driver ===
|
||||
@@ -948,16 +948,16 @@ The following variables are substituted in ''genericSpeechCommand'':
|
||||
* ''fenrirRate'' = is replaced with the current speed (speech rate)
|
||||
|
||||
Example genericSpeechCommand (default):
|
||||
genericSpeechCommand=espeak -a fenrirVolume -s fenrirRate -p fenrirPitch -v fenrirVoice "fenrirText"
|
||||
generic_speech_command=espeak -a fenrirVolume -s fenrirRate -p fenrirPitch -v fenrirVoice "fenrirText"
|
||||
|
||||
These are the minimum and maximum values of the TTS system used in genericSpeechCommand. They are needed to calculate the abstract range in volume, rate and pitch 0.0 - 1.0.
|
||||
|
||||
FenrirMinVolume=0
|
||||
fenrirMaxVolume=200
|
||||
fenrirMinPitch=0
|
||||
fenrirMaxPitch=99
|
||||
fenrirMinRate=80
|
||||
fenrirMaxRate=450
|
||||
fenrir_max_volume=200
|
||||
fenrir_min_pitch=0
|
||||
fenrir_max_pitch=99
|
||||
fenrir_min_rate=80
|
||||
fenrir_max_rate=450
|
||||
|
||||
The current volume, pitch and rate is calculated like this
|
||||
value = min + [volume,pitch,rate] * (min - max )
|
||||
@@ -1033,7 +1033,7 @@ The encoding of the screen
|
||||
Values:''cp850'' is used for Western languages like USA or Europe.
|
||||
|
||||
The driver updates Fenrir with changes on the screen.
|
||||
screenUpdateDelay=0.05
|
||||
screen_update_delay=0.05
|
||||
Values: in Seconds
|
||||
|
||||
If you want Fenrir to not be active on any screen for various reasons. Maybe an X server or Wayland is running on that screen. You can make Fenrir ignore it or multiple screens seperated by '','' with:
|
||||
@@ -1064,15 +1064,15 @@ Values:
|
||||
* ''<Device Name>'' just use the device with the given name.
|
||||
|
||||
Gives Fenrir exclusive access to the keyboard and lets it control keystrokes. This is needed to intercept Fenrir related shortcuts.
|
||||
grabDevices=True
|
||||
grab_devices=True
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
The following makes sense if you are using a second screenreader and want to have some hooked events. Fenrir ignores all shortcuts then.
|
||||
ignoreShortcuts=False
|
||||
ignore_shortcuts=False
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
The current keyboard layout used for shortcuts.
|
||||
keyboardLayout=desktop
|
||||
keyboard_layout=desktop
|
||||
Values: An absolute Path to a Keyboard definition file or a Filename without extension located in ''/etc/fenrir/keyboard''
|
||||
|
||||
Announce characters while typing.
|
||||
@@ -1080,23 +1080,23 @@ Announce characters while typing.
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Announce deleted characters
|
||||
charDeleteEcho=True
|
||||
char_delete_echo=True
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Announce word after pressing space
|
||||
wordEcho=False
|
||||
word_echo=False
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Interrupt speech on any keypress
|
||||
interruptOnKeyPress=False
|
||||
interrupt_on_key_press=False
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
You can filter the keys that speech should interrupt
|
||||
interruptOnKeyPressFilter=
|
||||
interrupt_on_key_press_filter=
|
||||
Values: (List) empty = all keys, otherwise interrupt with specified keys
|
||||
|
||||
The timeout that is used for double tap shortcuts
|
||||
doubleTapTimeout=0.2
|
||||
double_tap_timeout=0.2
|
||||
Values: Seconds
|
||||
==== General ====
|
||||
Overall settings can be configured from the section ''[general]''.
|
||||
@@ -1106,19 +1106,19 @@ Set the current debug level:
|
||||
Values: off=0, error=1, warning=2, info=3
|
||||
|
||||
the current punctuation and dict file in use:
|
||||
punctuationProfile=default
|
||||
punctuation_profile=default
|
||||
Values: Text, see available profiles in ''/etc/fenrir/punctuation'' or in ''sourceTree/config/punctuation''
|
||||
|
||||
The current punctuation level in use:
|
||||
punctuationLevel=some
|
||||
punctuation_level=some
|
||||
Values: Text, See available levels in the used punctuation file.
|
||||
|
||||
Respect pause for punctuations:
|
||||
respectPunctuationPause=True
|
||||
respect_punctuation_pause=True
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Add a pause on Line break:
|
||||
newLinePause=True
|
||||
new_line_pause=True
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Specify the path where the clipboard should be exported to.
|
||||
@@ -1128,7 +1128,7 @@ The variable ''$user'' is replaced by the current logged username.
|
||||
Values: Text, Systemfilepath
|
||||
|
||||
The number of available clipboards:
|
||||
numberOfClipboards=10
|
||||
number_of_clipboards=10
|
||||
Values: Integer, 1 - 999
|
||||
|
||||
Replace emoticons like :) or ;) with text insertions:
|
||||
@@ -1136,7 +1136,7 @@ Replace emoticons like :) or ;) with text insertions:
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Define the current Fenrir keys:
|
||||
fenrirKeys=KEY_KP0,KEY_META,KEY_INSERT
|
||||
fenrir_keys=KEY_KP0,KEY_META,KEY_INSERT
|
||||
Values, Text list, separated by comma.
|
||||
|
||||
Define the current script keys:
|
||||
@@ -1152,11 +1152,11 @@ The date format to be used for (date command) output:
|
||||
Values: see python specification for [[https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior|datetime.strftime]]
|
||||
|
||||
Enable or Disable spellcheck whilst typing:
|
||||
autoSpellCheck=True
|
||||
auto_spell_check=True
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
The use of the dictionary with spellcheck:
|
||||
spellCheckLanguage=en_US
|
||||
spell_check_language=en_US
|
||||
Values: Text, see aspell dictionary's.
|
||||
|
||||
Folder Path for your scripts "scriptKey" functionality:
|
||||
@@ -1197,7 +1197,7 @@ Leave the review mode when pressing a key:
|
||||
Values: on=''True'', off=''False''
|
||||
|
||||
Leave the review mode when changing the screen (From TTY3 to TTY4):
|
||||
leaveReviewOnScreenChange=True
|
||||
leave_review_on_screen_change=True
|
||||
Values: on=''True'', off=''False''
|
||||
==== Promote ====
|
||||
"Promoted Lists" are configured in the section ''[promote]''.
|
||||
@@ -1207,7 +1207,7 @@ Values: on=''True'', off=''False''
|
||||
|
||||
The minimum time interval of inactivity to activate promoting.
|
||||
By default it promotes after 120 Seconds inactivity:
|
||||
inactiveTimeoutSec=120
|
||||
inactive_timeout_sec=120
|
||||
Values: in Seconds
|
||||
|
||||
Define a list of promoted words comma seperated:
|
||||
@@ -1270,7 +1270,7 @@ File: ''/usr/share/fenrirscreenreader/scripts/helloWorld__-__key_h.sh'':
|
||||
===== Commands =====
|
||||
You can place your own commands in "/usr/share/fenrirscreenreader/commands" (path is configurable in settings.conf).
|
||||
Commands are python files with a special scheme. You can assign them to a shortcut using the filename without an extension or place them in a hook trigger like OnInput or OnScreenChange. For further information see developer guide.
|
||||
Good Examples: [[https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/date.py|"date.py"]] (announce the Date), [[https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/shut_up.py|"shut_up.py"]] (interrupt output)
|
||||
Good Examples: [[https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/date.py|"date.py"]] (announce the Date), [[https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/shut_up.py|"shut_up.py"]] (interrupt output)
|
||||
the basic scheme for a command is as follows:
|
||||
|
||||
from core import debug
|
||||
@@ -1289,7 +1289,7 @@ the basic scheme for a command is as follows:
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
* [[https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/command_template.py|Template lives here]]
|
||||
* [[https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/command_template.py|Template lives here]]
|
||||
* The class needs to have the name "command".
|
||||
* "initialize" is running once whilst loading the command.
|
||||
* "shutdown" is running on unload like the command (quit fenrir)
|
||||
@@ -1319,7 +1319,7 @@ the basic scheme for a command is as follows:
|
||||
- You can test if speech-dispatcher works by invoking it as root\\ ''sudo spd-say "hello world"''
|
||||
===== Bugreports and feature requests =====
|
||||
Please report Bugs and feature requests to:
|
||||
[[https://github.com/chrys87/fenrir/issues|https://github.com/chrys87/fenrir/issues]]
|
||||
[[https://git.stormux.org/storm/fenrir/issues|https://git.stormux.org/storm/fenrir/issues]]
|
||||
|
||||
for bugs please provide a [[#Howto create a debug file|debug]] file that shows the issue.
|
||||
==== How-to create a debug file ====
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ To test Fenrir:
|
||||
sudo fenrir
|
||||
|
||||
To have Fenrir start on system boot using systemd:
|
||||
download service file: https://raw.githubusercontent.com/chrys87/fenrir/master/autostart/systemd/Arch/fenrir.service
|
||||
download service file: https://git.stormux.org/storm/fenrir/raw/branch/master/autostart/systemd/Arch/fenrir.service
|
||||
move the service file to: /etc/systemd/system/fenrir.service
|
||||
sudo systemctl enable fenrir
|
||||
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
[pytest]
|
||||
# Pytest configuration for Fenrir screen reader
|
||||
|
||||
# Test discovery patterns
|
||||
python_files = test_*.py *_test.py
|
||||
python_classes = Test*
|
||||
python_functions = test_*
|
||||
|
||||
# Test paths
|
||||
testpaths = tests
|
||||
|
||||
# Minimum Python version
|
||||
minversion = 3.7
|
||||
|
||||
# Output options
|
||||
addopts =
|
||||
# Verbose output with test names
|
||||
-v
|
||||
# Show extra test summary info
|
||||
-ra
|
||||
# Enable strict markers (only registered markers allowed)
|
||||
--strict-markers
|
||||
# Show local variables in tracebacks
|
||||
--showlocals
|
||||
# Warnings configuration
|
||||
-W ignore::DeprecationWarning
|
||||
# Optional plugins (uncomment if installed):
|
||||
# --timeout=30 # Requires pytest-timeout
|
||||
# --cov-report=term-missing # Requires pytest-cov
|
||||
# -x # Stop on first failure
|
||||
|
||||
# Register custom markers
|
||||
markers =
|
||||
unit: Unit tests (fast, no mocking)
|
||||
integration: Integration tests (require mocking)
|
||||
driver: Driver tests (require root access)
|
||||
slow: Tests that take more than 1 second
|
||||
remote: Tests for remote control functionality
|
||||
settings: Tests for settings and configuration
|
||||
commands: Tests for command system
|
||||
vmenu: Tests for VMenu system
|
||||
|
||||
# Coverage configuration
|
||||
[coverage:run]
|
||||
source = src/fenrirscreenreader
|
||||
omit =
|
||||
*/tests/*
|
||||
*/vmenu-profiles/*
|
||||
*/__pycache__/*
|
||||
*/site-packages/*
|
||||
|
||||
[coverage:report]
|
||||
# Fail if coverage falls below this percentage
|
||||
# fail_under = 70
|
||||
exclude_lines =
|
||||
# Standard pragma
|
||||
pragma: no cover
|
||||
# Don't complain about missing debug code
|
||||
def __repr__
|
||||
# Don't complain if tests don't hit defensive assertion code
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
# Don't complain about abstract methods
|
||||
@abstractmethod
|
||||
# Don't complain about initialization
|
||||
if __name__ == .__main__.:
|
||||
|
||||
[coverage:html]
|
||||
directory = htmlcov
|
||||
@@ -22,7 +22,7 @@ class command:
|
||||
self.env["runtime"]["MemoryManager"].add_index_list(
|
||||
"clipboardHistory",
|
||||
self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"general", "numberOfClipboards"
|
||||
"general", "number_of_clipboards"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -36,11 +36,11 @@ class command:
|
||||
def update_spell_language(self):
|
||||
self.spellChecker = enchant.Dict(
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
)
|
||||
self.language = self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -48,7 +48,7 @@ class command:
|
||||
return
|
||||
if (
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
!= self.language
|
||||
):
|
||||
|
||||
@@ -24,9 +24,8 @@ class command:
|
||||
def run(self):
|
||||
try:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
f"Fenrir screen reader version {
|
||||
fenrirVersion.version}-{
|
||||
fenrirVersion.code_name}",
|
||||
f"Fenrir screen reader version "
|
||||
f"{fenrirVersion.version}-{fenrirVersion.code_name}",
|
||||
interrupt=True,
|
||||
)
|
||||
except Exception as e:
|
||||
|
||||
@@ -59,7 +59,7 @@ class command:
|
||||
|
||||
def run(self):
|
||||
current_layout = self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"keyboard", "keyboardLayout"
|
||||
"keyboard", "keyboard_layout"
|
||||
)
|
||||
|
||||
# Extract layout name from full path if needed
|
||||
@@ -83,7 +83,7 @@ class command:
|
||||
|
||||
# Update setting and reload shortcuts
|
||||
self.env["runtime"]["SettingsManager"].set_setting(
|
||||
"keyboard", "keyboardLayout", next_layout
|
||||
"keyboard", "keyboard_layout", next_layout
|
||||
)
|
||||
|
||||
# Reload shortcuts with new layout
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
_base_path = os.path.join(os.path.dirname(__file__), "adjustment_base.py")
|
||||
_spec = importlib.util.spec_from_file_location("adjustment_base", _base_path)
|
||||
_module = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_module)
|
||||
adjustment_command = _module.adjustment_command
|
||||
|
||||
|
||||
class command(adjustment_command):
|
||||
def __init__(self):
|
||||
super().__init__("alsa", "volume", "dec")
|
||||
@@ -26,7 +26,7 @@ class command:
|
||||
def run(self):
|
||||
clipboard_file_path = self.env["runtime"][
|
||||
"SettingsManager"
|
||||
].get_setting("general", "clipboardExportPath")
|
||||
].get_setting("general", "clipboard_export_path")
|
||||
clipboard_file_path = clipboard_file_path.replace(
|
||||
"$user", self.env["general"]["curr_user"]
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ class command:
|
||||
def run(self):
|
||||
clipboard_file_path = self.env["runtime"][
|
||||
"SettingsManager"
|
||||
].get_setting("general", "clipboardExportPath")
|
||||
].get_setting("general", "clipboard_export_path")
|
||||
clipboard_file_path = clipboard_file_path.replace(
|
||||
"$user", self.env["general"]["curr_user"]
|
||||
)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
_base_path = os.path.join(os.path.dirname(__file__), "adjustment_base.py")
|
||||
_spec = importlib.util.spec_from_file_location("adjustment_base", _base_path)
|
||||
_module = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_module)
|
||||
adjustment_command = _module.adjustment_command
|
||||
|
||||
|
||||
class command(adjustment_command):
|
||||
def __init__(self):
|
||||
super().__init__("alsa", "volume", "inc")
|
||||
@@ -18,7 +18,7 @@ class command:
|
||||
self.env["runtime"]["MemoryManager"].add_index_list(
|
||||
"clipboardHistory",
|
||||
self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"general", "numberOfClipboards"
|
||||
"general", "number_of_clipboards"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -18,17 +18,17 @@ class command:
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
# Use commandBuffer like other commands
|
||||
if "progressMonitoring" not in self.env["commandBuffer"]:
|
||||
if "progress_monitoring" not in self.env["commandBuffer"]:
|
||||
# Check if progress monitoring should be enabled by default from
|
||||
# settings
|
||||
try:
|
||||
default_enabled = self.env["runtime"][
|
||||
"SettingsManager"
|
||||
].get_setting_as_bool("sound", "progressMonitoring")
|
||||
].get_setting_as_bool("sound", "progress_monitoring")
|
||||
except Exception as e:
|
||||
# If setting doesn't exist, default to False
|
||||
default_enabled = False
|
||||
self.env["commandBuffer"]["progressMonitoring"] = default_enabled
|
||||
self.env["commandBuffer"]["progress_monitoring"] = default_enabled
|
||||
self.env["commandBuffer"]["lastProgressTime"] = 0
|
||||
self.env["commandBuffer"]["lastProgressValue"] = -1
|
||||
|
||||
@@ -40,12 +40,12 @@ class command:
|
||||
|
||||
def run(self):
|
||||
# Check if commandBuffer exists
|
||||
if "progressMonitoring" not in self.env["commandBuffer"]:
|
||||
self.env["commandBuffer"]["progressMonitoring"] = False
|
||||
if "progress_monitoring" not in self.env["commandBuffer"]:
|
||||
self.env["commandBuffer"]["progress_monitoring"] = False
|
||||
self.env["commandBuffer"]["lastProgressTime"] = 0
|
||||
self.env["commandBuffer"]["lastProgressValue"] = -1
|
||||
|
||||
if self.env["commandBuffer"]["progressMonitoring"]:
|
||||
if self.env["commandBuffer"]["progress_monitoring"]:
|
||||
self.stop_progress_monitoring()
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Progress monitoring disabled"), interrupt=True
|
||||
@@ -57,17 +57,17 @@ class command:
|
||||
)
|
||||
|
||||
def start_progress_monitoring(self):
|
||||
self.env["commandBuffer"]["progressMonitoring"] = True
|
||||
self.env["commandBuffer"]["progress_monitoring"] = True
|
||||
self.env["commandBuffer"]["lastProgressTime"] = time.time()
|
||||
self.env["commandBuffer"]["lastProgressValue"] = -1
|
||||
# Don't control speech - let user decide with silence_until_prompt
|
||||
|
||||
def stop_progress_monitoring(self):
|
||||
self.env["commandBuffer"]["progressMonitoring"] = False
|
||||
self.env["commandBuffer"]["progress_monitoring"] = False
|
||||
# Don't control speech - progress monitor is beep-only
|
||||
|
||||
def detect_progress(self, text):
|
||||
if not self.env["commandBuffer"]["progressMonitoring"]:
|
||||
if not self.env["commandBuffer"]["progress_monitoring"]:
|
||||
return
|
||||
|
||||
# Skip progress detection if current screen looks like a prompt
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
from fenrirscreenreader.core import debug
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("Read all text line by line from current position")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Read-all functionality temporarily disabled",
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
# Check if speechd is available for callbacks
|
||||
try:
|
||||
speech_driver = self.env['runtime']['SpeechDriver']
|
||||
if not (hasattr(speech_driver, '_sd') and speech_driver._sd):
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Read all requires speech-dispatcher - not available with current speech driver"),
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||
f"ReadAllByLine run: Error checking speech driver: {e}",
|
||||
debug.DebugLevel.ERROR
|
||||
)
|
||||
return
|
||||
|
||||
# Check if ReadAllManager is available
|
||||
if "ReadAllManager" not in self.env["runtime"]:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Read all manager not in runtime"),
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
elif not self.env["runtime"]["ReadAllManager"]:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Read all manager is None"),
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
|
||||
# Start continuous line-by-line reading
|
||||
if self.env["runtime"]["ReadAllManager"].is_active():
|
||||
# Stop if already active
|
||||
self.env["runtime"]["ReadAllManager"].stop_read_all()
|
||||
else:
|
||||
# Start line-by-line reading
|
||||
self.env["runtime"]["ReadAllManager"].start_read_all('line')
|
||||
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
from fenrirscreenreader.core import debug
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("Read all text page by page from current position")
|
||||
|
||||
def run(self):
|
||||
# Check if speechd is available for callbacks
|
||||
try:
|
||||
speech_driver = self.env['runtime']['SpeechDriver']
|
||||
if not (hasattr(speech_driver, '_sd') and speech_driver._sd):
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Read all requires speech-dispatcher - not available with current speech driver"),
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||
f"ReadAllByPage run: Error checking speech driver: {e}",
|
||||
debug.DebugLevel.ERROR
|
||||
)
|
||||
return
|
||||
|
||||
# Check if ReadAllManager is available
|
||||
if "ReadAllManager" not in self.env["runtime"]:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Read all manager not in runtime"),
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
elif not self.env["runtime"]["ReadAllManager"]:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Read all manager is None"),
|
||||
interrupt=True
|
||||
)
|
||||
return
|
||||
|
||||
# Start continuous page-by-page reading
|
||||
if self.env["runtime"]["ReadAllManager"].is_active():
|
||||
# Stop if already active
|
||||
self.env["runtime"]["ReadAllManager"].stop_read_all()
|
||||
else:
|
||||
# Start page-by-page reading
|
||||
self.env["runtime"]["ReadAllManager"].start_read_all('page')
|
||||
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -36,11 +36,11 @@ class command:
|
||||
def update_spell_language(self):
|
||||
self.spellChecker = enchant.Dict(
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
)
|
||||
self.language = self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -51,7 +51,7 @@ class command:
|
||||
return
|
||||
if (
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
!= self.language
|
||||
):
|
||||
|
||||
@@ -36,11 +36,11 @@ class command:
|
||||
return
|
||||
self.spellChecker = enchant.Dict(
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
)
|
||||
self.language = self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -51,7 +51,7 @@ class command:
|
||||
return
|
||||
if (
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
!= self.language
|
||||
):
|
||||
|
||||
@@ -25,15 +25,15 @@ class command:
|
||||
def run(self):
|
||||
self.env["runtime"]["SettingsManager"].set_setting(
|
||||
"general",
|
||||
"autoPresentIndent",
|
||||
"auto_present_indent",
|
||||
str(
|
||||
not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoPresentIndent"
|
||||
"general", "auto_present_indent"
|
||||
)
|
||||
),
|
||||
)
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoPresentIndent"
|
||||
"general", "auto_present_indent"
|
||||
):
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("autoindent enabled"), sound_icon="", interrupt=True
|
||||
|
||||
@@ -25,15 +25,15 @@ class command:
|
||||
def run(self):
|
||||
self.env["runtime"]["SettingsManager"].set_setting(
|
||||
"speech",
|
||||
"autoReadIncoming",
|
||||
"auto_read_incoming",
|
||||
str(
|
||||
not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"speech", "autoReadIncoming"
|
||||
"speech", "auto_read_incoming"
|
||||
)
|
||||
),
|
||||
)
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"speech", "autoReadIncoming"
|
||||
"speech", "auto_read_incoming"
|
||||
):
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("autoread enabled"), sound_icon="", interrupt=True
|
||||
|
||||
@@ -24,15 +24,15 @@ class command:
|
||||
def run(self):
|
||||
self.env["runtime"]["SettingsManager"].set_setting(
|
||||
"general",
|
||||
"autoSpellCheck",
|
||||
"auto_spell_check",
|
||||
str(
|
||||
not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoSpellCheck"
|
||||
"general", "auto_spell_check"
|
||||
)
|
||||
),
|
||||
)
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoSpellCheck"
|
||||
"general", "auto_spell_check"
|
||||
):
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("auto spellcheck enabled"), sound_icon="", interrupt=True
|
||||
|
||||
@@ -25,7 +25,7 @@ class command:
|
||||
if self.env["runtime"]["PunctuationManager"].cycle_punctuation():
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "punctuationLevel"
|
||||
"general", "punctuation_level"
|
||||
),
|
||||
interrupt=True,
|
||||
ignore_punctuation=True,
|
||||
|
||||
@@ -23,7 +23,7 @@ class command:
|
||||
|
||||
def run(self):
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"keyboard", "interruptOnKeyPress"
|
||||
"keyboard", "interrupt_on_key_press"
|
||||
):
|
||||
return
|
||||
if self.env["runtime"]["InputManager"].no_key_pressed():
|
||||
@@ -37,13 +37,13 @@ class command:
|
||||
# if the filter is set
|
||||
if (
|
||||
self.env["runtime"]["SettingsManager"]
|
||||
.get_setting("keyboard", "interruptOnKeyPressFilter")
|
||||
.get_setting("keyboard", "interrupt_on_key_press_filter")
|
||||
.strip()
|
||||
!= ""
|
||||
):
|
||||
filter_list = (
|
||||
self.env["runtime"]["SettingsManager"]
|
||||
.get_setting("keyboard", "interruptOnKeyPressFilter")
|
||||
.get_setting("keyboard", "interrupt_on_key_press_filter")
|
||||
.split(",")
|
||||
)
|
||||
for currInput in self.env["input"]["currInput"]:
|
||||
|
||||
@@ -24,7 +24,7 @@ class command:
|
||||
def run(self):
|
||||
# enabled?
|
||||
active = self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"keyboard", "charEchoMode"
|
||||
"keyboard", "char_echo_mode"
|
||||
)
|
||||
# 0 = off
|
||||
if active == 0:
|
||||
|
||||
@@ -26,7 +26,7 @@ class command:
|
||||
def run(self):
|
||||
# is it enabled?
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"keyboard", "wordEcho"
|
||||
"keyboard", "word_echo"
|
||||
):
|
||||
return
|
||||
# is navigation?
|
||||
|
||||
@@ -39,23 +39,23 @@ class command:
|
||||
return
|
||||
self.spellChecker = enchant.Dict(
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
)
|
||||
self.language = self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
if not initialized:
|
||||
return
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoSpellCheck"
|
||||
"general", "auto_spell_check"
|
||||
):
|
||||
return
|
||||
if (
|
||||
self.env["runtime"]["SettingsManager"].get_setting(
|
||||
"general", "spellCheckLanguage"
|
||||
"general", "spell_check_language"
|
||||
)
|
||||
!= self.language
|
||||
):
|
||||
|
||||
@@ -23,7 +23,7 @@ class command:
|
||||
|
||||
def run(self):
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"keyboard", "charDeleteEcho"
|
||||
"keyboard", "char_delete_echo"
|
||||
):
|
||||
return
|
||||
# detect typing or chilling
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ class command:
|
||||
|
||||
# echo word insteed of char
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"keyboard", "wordEcho"
|
||||
"keyboard", "word_echo"
|
||||
):
|
||||
if (
|
||||
abs(
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
|
||||
import time
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
@@ -14,55 +14,150 @@ class command:
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
# Initialize tab completion state tracking
|
||||
if "tabCompletion" not in self.env["commandBuffer"]:
|
||||
self.env["commandBuffer"]["tabCompletion"] = {
|
||||
"lastTabTime": 0,
|
||||
"pendingCompletion": None,
|
||||
"retryCount": 0
|
||||
}
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return "No Description found"
|
||||
return _("Announces tab completions when detected")
|
||||
|
||||
def _is_recent_tab_input(self):
|
||||
"""Check if TAB was pressed recently (within 200ms window)"""
|
||||
current_time = time.time()
|
||||
tab_detected = False
|
||||
|
||||
# Check KEY mode
|
||||
if self.env["runtime"]["InputManager"].get_shortcut_type() in ["KEY"]:
|
||||
if (self.env["runtime"]["InputManager"].get_last_deepest_input()
|
||||
in [["KEY_TAB"]]):
|
||||
tab_detected = True
|
||||
self.env["commandBuffer"]["tabCompletion"]["lastTabTime"] = current_time
|
||||
|
||||
# Check BYTE mode
|
||||
elif self.env["runtime"]["InputManager"].get_shortcut_type() in ["BYTE"]:
|
||||
for currByte in self.env["runtime"]["ByteManager"].get_last_byte_key():
|
||||
if currByte == 9: # Tab character
|
||||
tab_detected = True
|
||||
self.env["commandBuffer"]["tabCompletion"]["lastTabTime"] = current_time
|
||||
|
||||
# Check if tab was pressed recently (200ms window)
|
||||
if not tab_detected:
|
||||
time_since_tab = current_time - self.env["commandBuffer"]["tabCompletion"]["lastTabTime"]
|
||||
if time_since_tab <= 0.2: # 200ms window
|
||||
tab_detected = True
|
||||
|
||||
return tab_detected
|
||||
|
||||
def _is_flexible_completion_match(self, x_move, delta_text):
|
||||
"""Use flexible matching instead of strict equality"""
|
||||
if not delta_text:
|
||||
return False
|
||||
|
||||
delta_len = len(delta_text)
|
||||
|
||||
# Exact match (preserve original behavior)
|
||||
if x_move == delta_len:
|
||||
return True
|
||||
|
||||
# Flexible range: allow ±2 characters difference
|
||||
# Handles spacing adjustments and unicode width variations
|
||||
if abs(x_move - delta_len) <= 2 and delta_len > 0:
|
||||
return True
|
||||
|
||||
# For longer completions, allow proportional variance
|
||||
if delta_len > 10 and abs(x_move - delta_len) <= (delta_len * 0.2):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _detect_completion_patterns(self, delta_text):
|
||||
"""Detect common tab completion patterns for improved accuracy"""
|
||||
if not delta_text:
|
||||
return False
|
||||
|
||||
delta_stripped = delta_text.strip()
|
||||
|
||||
# File extension completion
|
||||
if '.' in delta_stripped and delta_stripped.count('.') <= 2:
|
||||
return True
|
||||
|
||||
# Path completion (contains / or \)
|
||||
if '/' in delta_stripped or '\\' in delta_stripped:
|
||||
return True
|
||||
|
||||
# Command parameter completion (starts with -)
|
||||
if delta_stripped.startswith('-') and len(delta_stripped) > 1:
|
||||
return True
|
||||
|
||||
# Word boundary completion (alphanumeric content)
|
||||
if delta_stripped.isalnum() and len(delta_stripped) >= 2:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def run(self):
|
||||
# try to detect the tab completion by cursor change
|
||||
"""Enhanced tab completion detection with improved reliability"""
|
||||
# Basic cursor movement check (preserve original logic)
|
||||
x_move = (
|
||||
self.env["screen"]["new_cursor"]["x"]
|
||||
- self.env["screen"]["old_cursor"]["x"]
|
||||
)
|
||||
if x_move <= 0:
|
||||
return
|
||||
if self.env["runtime"]["InputManager"].get_shortcut_type() in ["KEY"]:
|
||||
if not (
|
||||
self.env["runtime"]["InputManager"].get_last_deepest_input()
|
||||
in [["KEY_TAB"]]
|
||||
):
|
||||
if x_move < 5:
|
||||
return
|
||||
elif self.env["runtime"]["InputManager"].get_shortcut_type() in [
|
||||
"BYTE"
|
||||
]:
|
||||
found = False
|
||||
for currByte in self.env["runtime"][
|
||||
"ByteManager"
|
||||
].get_last_byte_key():
|
||||
if currByte == 9:
|
||||
found = True
|
||||
if not found:
|
||||
if x_move < 5:
|
||||
return
|
||||
# is there any change?
|
||||
|
||||
# Enhanced tab input detection with persistence
|
||||
tab_detected = self._is_recent_tab_input()
|
||||
|
||||
# Fallback for non-tab movements (preserve original thresholds)
|
||||
if not tab_detected:
|
||||
if x_move < 5:
|
||||
return
|
||||
|
||||
# Screen delta availability check
|
||||
if not self.env["runtime"]["ScreenManager"].is_delta():
|
||||
# If tab was detected but no delta yet, store for potential retry
|
||||
if tab_detected and self.env["commandBuffer"]["tabCompletion"]["retryCount"] < 2:
|
||||
self.env["commandBuffer"]["tabCompletion"]["pendingCompletion"] = {
|
||||
"x_move": x_move,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
self.env["commandBuffer"]["tabCompletion"]["retryCount"] += 1
|
||||
return
|
||||
if not x_move == len(self.env["screen"]["new_delta"]):
|
||||
return
|
||||
# filter unneded space on word begin
|
||||
curr_delta = self.env["screen"]["new_delta"]
|
||||
if (
|
||||
len(curr_delta.strip()) != len(curr_delta)
|
||||
and curr_delta.strip() != ""
|
||||
):
|
||||
|
||||
delta_text = self.env["screen"]["new_delta"]
|
||||
|
||||
# Enhanced correlation checking with flexible matching
|
||||
if not self._is_flexible_completion_match(x_move, delta_text):
|
||||
# Additional pattern-based validation for edge cases
|
||||
if not (tab_detected and self._detect_completion_patterns(delta_text)):
|
||||
return
|
||||
|
||||
# Reset retry counter on successful detection
|
||||
self.env["commandBuffer"]["tabCompletion"]["retryCount"] = 0
|
||||
self.env["commandBuffer"]["tabCompletion"]["pendingCompletion"] = None
|
||||
|
||||
# Mark that we've handled this delta to prevent duplicate announcements
|
||||
# This prevents the incoming text handler from also announcing the same content
|
||||
self.env["commandBuffer"]["tabCompletion"]["lastProcessedDelta"] = delta_text
|
||||
self.env["commandBuffer"]["tabCompletion"]["lastProcessedTime"] = time.time()
|
||||
|
||||
# Text filtering and announcement (preserve original behavior)
|
||||
curr_delta = delta_text
|
||||
if (len(curr_delta.strip()) != len(curr_delta) and curr_delta.strip() != ""):
|
||||
curr_delta = curr_delta.strip()
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
curr_delta, interrupt=True, announce_capital=True, flush=False
|
||||
)
|
||||
|
||||
# Enhanced announcement with better handling of empty completions
|
||||
if curr_delta:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
curr_delta, interrupt=True, announce_capital=True, flush=False
|
||||
)
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
|
||||
+7
-6
@@ -55,16 +55,17 @@ class command:
|
||||
self.lastIdent = curr_ident
|
||||
do_interrupt = True
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoPresentIndent"
|
||||
"general", "auto_present_indent"
|
||||
):
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"general", "autoPresentIndentMode"
|
||||
"general", "auto_present_indent_mode"
|
||||
) in [0, 1]:
|
||||
self.env["runtime"]["OutputManager"].play_frequence(
|
||||
curr_ident * 50, 0.1, interrupt=do_interrupt
|
||||
)
|
||||
if self.lastIdent != curr_ident:
|
||||
self.env["runtime"]["OutputManager"].play_frequence(
|
||||
curr_ident * 50, 0.1, interrupt=do_interrupt
|
||||
)
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"general", "autoPresentIndentMode"
|
||||
"general", "auto_present_indent_mode"
|
||||
) in [0, 2]:
|
||||
if self.lastIdent != curr_ident:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
|
||||
+37
-30
@@ -31,10 +31,9 @@ class command:
|
||||
self.lastIdent = 0
|
||||
return
|
||||
|
||||
# is a vertical change?
|
||||
if not self.env["runtime"][
|
||||
"CursorManager"
|
||||
].is_cursor_horizontal_move():
|
||||
# Skip if no cursor movement at all
|
||||
if (not self.env["runtime"]["CursorManager"].is_cursor_horizontal_move() and
|
||||
not self.env["runtime"]["CursorManager"].is_cursor_vertical_move()):
|
||||
return
|
||||
x, y, curr_line = line_utils.get_current_line(
|
||||
self.env["screen"]["new_cursor"]["x"],
|
||||
@@ -43,42 +42,50 @@ class command:
|
||||
)
|
||||
curr_ident = self.env["screen"]["new_cursor"]["x"]
|
||||
|
||||
if not curr_line.isspace():
|
||||
# ident
|
||||
lastIdent, lastY, last_line = line_utils.get_current_line(
|
||||
self.env["screen"]["new_cursor"]["x"],
|
||||
self.env["screen"]["new_cursor"]["y"],
|
||||
self.env["screen"]["old_content_text"],
|
||||
)
|
||||
if curr_line.strip() != last_line.strip():
|
||||
return
|
||||
if len(curr_line.lstrip()) == len(last_line.lstrip()):
|
||||
return
|
||||
if curr_line.isspace():
|
||||
# Don't beep for lines with only spaces - no meaningful indentation
|
||||
return
|
||||
|
||||
# Lines with actual content - calculate proper indentation
|
||||
lastIdent, lastY, last_line = line_utils.get_current_line(
|
||||
self.env["screen"]["new_cursor"]["x"],
|
||||
self.env["screen"]["new_cursor"]["y"],
|
||||
self.env["screen"]["old_content_text"],
|
||||
)
|
||||
if curr_line.strip() != last_line.strip():
|
||||
return
|
||||
if len(curr_line.lstrip()) == len(last_line.lstrip()):
|
||||
return
|
||||
|
||||
curr_ident = len(curr_line) - len(curr_line.lstrip())
|
||||
curr_ident = len(curr_line) - len(curr_line.lstrip())
|
||||
|
||||
if self.lastIdent == -1:
|
||||
self.lastIdent = curr_ident
|
||||
if curr_ident <= 0:
|
||||
return
|
||||
if curr_ident <= 0:
|
||||
return
|
||||
|
||||
# Initialize lastIdent if needed
|
||||
if self.lastIdent == -1:
|
||||
self.lastIdent = curr_ident
|
||||
|
||||
# Only beep/announce if indentation level has changed
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"general", "autoPresentIndent"
|
||||
):
|
||||
"general", "auto_present_indent"
|
||||
) and self.lastIdent != curr_ident:
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"general", "autoPresentIndentMode"
|
||||
"general", "auto_present_indent_mode"
|
||||
) in [0, 1]:
|
||||
self.env["runtime"]["OutputManager"].play_frequence(
|
||||
curr_ident * 50, 0.1, interrupt=False
|
||||
)
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"general", "autoPresentIndentMode"
|
||||
"general", "autePresentIndentMode"
|
||||
) in [0, 2]:
|
||||
if self.lastIdent != curr_ident:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("indented ") + str(curr_ident) + " ",
|
||||
interrupt=False,
|
||||
flush=False,
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("indented ") + str(curr_ident) + " ",
|
||||
interrupt=False,
|
||||
flush=False,
|
||||
)
|
||||
|
||||
# Always update lastIdent for next comparison
|
||||
self.lastIdent = curr_ident
|
||||
|
||||
def set_callback(self, callback):
|
||||
|
||||
@@ -23,7 +23,7 @@ class command:
|
||||
|
||||
def run(self):
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"review", "leaveReviewOnCursorChange"
|
||||
"review", "leave_review_on_cursor_change"
|
||||
):
|
||||
return
|
||||
if self.env["runtime"]["CursorManager"].is_review_mode():
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
import time
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("Handles delayed retry for tab completion detection")
|
||||
|
||||
def run(self):
|
||||
"""Check for and process pending tab completions with slight delay"""
|
||||
# Only process if we have tab completion state
|
||||
if "tabCompletion" not in self.env["commandBuffer"]:
|
||||
return
|
||||
|
||||
tab_state = self.env["commandBuffer"]["tabCompletion"]
|
||||
pending = tab_state.get("pendingCompletion")
|
||||
|
||||
if not pending:
|
||||
return
|
||||
|
||||
current_time = time.time()
|
||||
|
||||
# Process pending completion after 50ms delay
|
||||
if current_time - pending["timestamp"] < 0.05:
|
||||
return
|
||||
|
||||
# Check if screen delta is now available
|
||||
if not self.env["runtime"]["ScreenManager"].is_delta():
|
||||
# Give up after 200ms total
|
||||
if current_time - pending["timestamp"] > 0.2:
|
||||
tab_state["pendingCompletion"] = None
|
||||
tab_state["retryCount"] = 0
|
||||
return
|
||||
|
||||
# Process the delayed completion
|
||||
delta_text = self.env["screen"]["new_delta"]
|
||||
x_move = pending["x_move"]
|
||||
|
||||
# Use the same flexible matching logic as main tab completion
|
||||
match_found = self._is_flexible_completion_match(x_move, delta_text)
|
||||
|
||||
if not match_found:
|
||||
# Try pattern-based detection as final fallback
|
||||
match_found = self._detect_completion_patterns(delta_text)
|
||||
|
||||
if match_found and delta_text:
|
||||
# Mark that we've handled this delta to prevent duplicate announcements
|
||||
tab_state["lastProcessedDelta"] = delta_text
|
||||
tab_state["lastProcessedTime"] = current_time
|
||||
|
||||
# Filter and announce the completion
|
||||
curr_delta = delta_text
|
||||
if (len(curr_delta.strip()) != len(curr_delta) and
|
||||
curr_delta.strip() != ""):
|
||||
curr_delta = curr_delta.strip()
|
||||
|
||||
if curr_delta:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
curr_delta, interrupt=True, announce_capital=True, flush=False
|
||||
)
|
||||
|
||||
# Clear pending completion
|
||||
tab_state["pendingCompletion"] = None
|
||||
tab_state["retryCount"] = 0
|
||||
|
||||
def _is_flexible_completion_match(self, x_move, delta_text):
|
||||
"""Use flexible matching (duplicated from main command for heartbeat use)"""
|
||||
if not delta_text:
|
||||
return False
|
||||
|
||||
delta_len = len(delta_text)
|
||||
|
||||
# Exact match
|
||||
if x_move == delta_len:
|
||||
return True
|
||||
|
||||
# Flexible range: allow ±2 characters difference
|
||||
if abs(x_move - delta_len) <= 2 and delta_len > 0:
|
||||
return True
|
||||
|
||||
# For longer completions, allow proportional variance
|
||||
if delta_len > 10 and abs(x_move - delta_len) <= (delta_len * 0.2):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _detect_completion_patterns(self, delta_text):
|
||||
"""Detect common tab completion patterns (duplicated from main command)"""
|
||||
if not delta_text:
|
||||
return False
|
||||
|
||||
delta_stripped = delta_text.strip()
|
||||
|
||||
# File extension completion
|
||||
if '.' in delta_stripped and delta_stripped.count('.') <= 2:
|
||||
return True
|
||||
|
||||
# Path completion
|
||||
if '/' in delta_stripped or '\\' in delta_stripped:
|
||||
return True
|
||||
|
||||
# Command parameter completion
|
||||
if delta_stripped.startswith('-') and len(delta_stripped) > 1:
|
||||
return True
|
||||
|
||||
# Word boundary completion
|
||||
if delta_stripped.isalnum() and len(delta_stripped) >= 2:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -22,7 +22,7 @@ class command:
|
||||
|
||||
def run(self):
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"keyboard", "interruptOnKeyPress"
|
||||
"keyboard", "interrupt_on_key_press"
|
||||
):
|
||||
return
|
||||
if self.env["runtime"]["InputManager"].no_key_pressed():
|
||||
@@ -36,13 +36,13 @@ class command:
|
||||
# if the filter is set
|
||||
if (
|
||||
self.env["runtime"]["SettingsManager"]
|
||||
.get_setting("keyboard", "interruptOnKeyPressFilter")
|
||||
.get_setting("keyboard", "interrupt_on_key_press_filter")
|
||||
.strip()
|
||||
!= ""
|
||||
):
|
||||
filter_list = (
|
||||
self.env["runtime"]["SettingsManager"]
|
||||
.get_setting("keyboard", "interruptOnKeyPressFilter")
|
||||
.get_setting("keyboard", "interrupt_on_key_press_filter")
|
||||
.split(",")
|
||||
)
|
||||
for currInput in self.env["input"]["currInput"]:
|
||||
|
||||
@@ -24,14 +24,30 @@ class command:
|
||||
def run(self):
|
||||
if self.env["input"]["oldNumLock"] == self.env["input"]["newNumLock"]:
|
||||
return
|
||||
if self.env["input"]["newNumLock"]:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Numlock on"), interrupt=True
|
||||
)
|
||||
else:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Numlock off"), interrupt=True
|
||||
)
|
||||
|
||||
# Only announce numlock changes if an actual numlock key was pressed
|
||||
# AND the LED state actually changed (some numpads send spurious NUMLOCK events)
|
||||
current_input = self.env["input"]["currInput"]
|
||||
|
||||
# Check if this is a genuine numlock key press by verifying:
|
||||
# 1. KEY_NUMLOCK is in the current input sequence
|
||||
# 2. The LED state has actually changed
|
||||
# 3. This isn't just a side effect from a KP_ key (which some buggy numpads do)
|
||||
is_genuine_numlock = (
|
||||
current_input and
|
||||
"KEY_NUMLOCK" in current_input and
|
||||
not any(key.startswith("KEY_KP") for key in current_input if isinstance(key, str))
|
||||
)
|
||||
|
||||
if is_genuine_numlock:
|
||||
if self.env["input"]["newNumLock"]:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Numlock on"), interrupt=True
|
||||
)
|
||||
else:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Numlock off"), interrupt=True
|
||||
)
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
|
||||
@@ -23,7 +23,7 @@ class command:
|
||||
|
||||
def run(self):
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"review", "leaveReviewOnScreenChange"
|
||||
"review", "leave_review_on_screen_change"
|
||||
):
|
||||
return
|
||||
self.env["runtime"]["CursorManager"].clear_review_cursor()
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
from fenrirscreenreader.core import debug
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _(
|
||||
"TUI focus mode handler - suppresses screen update spam "
|
||||
"for interactive TUI applications"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
# Check if TUI mode is enabled
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"focus", "tui"
|
||||
):
|
||||
return
|
||||
|
||||
# TUI mode is active - set suppression flag for incoming handler
|
||||
# This prevents the 70000-incoming.py command from announcing
|
||||
# screen updates
|
||||
self.env["commandBuffer"]["tuiSuppressIncoming"] = True
|
||||
|
||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||
"tui_focus_handler: TUI mode active, suppressing incoming text",
|
||||
debug.DebugLevel.INFO
|
||||
)
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -25,8 +25,8 @@ class command:
|
||||
# Only run if progress monitoring is enabled
|
||||
try:
|
||||
if (
|
||||
"progressMonitoring" in self.env["commandBuffer"]
|
||||
and self.env["commandBuffer"]["progressMonitoring"]
|
||||
"progress_monitoring" in self.env["commandBuffer"]
|
||||
and self.env["commandBuffer"]["progress_monitoring"]
|
||||
):
|
||||
# Check if current line is a prompt - if so, reset progress
|
||||
# state
|
||||
@@ -66,12 +66,18 @@ class command:
|
||||
|
||||
# Check if delta is too large (screen change) vs small incremental
|
||||
# updates
|
||||
delta_length = len(self.env["screen"]["new_delta"])
|
||||
delta_text = self.env["screen"]["new_delta"]
|
||||
delta_length = len(delta_text)
|
||||
if (
|
||||
delta_length > 200
|
||||
): # Allow longer progress lines like Claude Code's status
|
||||
return False
|
||||
|
||||
# If delta contains newlines and is substantial, let incoming handler
|
||||
# deal with it to avoid interfering with multi-line text output
|
||||
if '\n' in delta_text and delta_length > 50:
|
||||
return False
|
||||
|
||||
# Check if current line looks like a prompt - progress unlikely during
|
||||
# prompts
|
||||
if self.is_current_line_prompt():
|
||||
@@ -144,13 +150,43 @@ class command:
|
||||
] = current_time
|
||||
return
|
||||
|
||||
# Pattern 1a2: Curl classic progress format (percentage without % symbol)
|
||||
# Extract percentage from curl's classic format
|
||||
curl_classic_match = re.search(
|
||||
r"^\s*(\d+)\s+\d+[kMGT]?\s+(\d+)\s+\d+[kMGT]?\s+\d+\s+\d+\s+\d+[kMGT]?\s+\d+\s+\d+:\d+:\d+\s+\d+:\d+:\d+\s+\d+:\d+:\d+\s+\d+[kMGT]?\s*$", text
|
||||
)
|
||||
if curl_classic_match:
|
||||
# Use the first percentage (total progress)
|
||||
percentage = float(curl_classic_match.group(1))
|
||||
if 0 <= percentage <= 100:
|
||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||
"found curl classic percentage: " + str(percentage),
|
||||
debug.DebugLevel.INFO,
|
||||
)
|
||||
if (
|
||||
percentage
|
||||
!= self.env["commandBuffer"]["lastProgressValue"]
|
||||
):
|
||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||
"Playing tone for curl: " + str(percentage),
|
||||
debug.DebugLevel.INFO,
|
||||
)
|
||||
self.play_progress_tone(percentage)
|
||||
self.env["commandBuffer"][
|
||||
"lastProgressValue"
|
||||
] = percentage
|
||||
self.env["commandBuffer"][
|
||||
"lastProgressTime"
|
||||
] = current_time
|
||||
return
|
||||
|
||||
# Pattern 1b: Time/token activity (not percentage-based, so use single
|
||||
# beep)
|
||||
time_match = re.search(r"(\d+)s\s", text)
|
||||
token_match = re.search(r"(\d+)\s+tokens", text)
|
||||
time_match = re.search(r"(?:(?:remaining|elapsed|left|ETA|eta)[:;\s]*(\d+)s|(\d+)s\s+(?:remaining|elapsed|left))", text, re.IGNORECASE)
|
||||
token_match = re.search(r"(?:processing|generating|used|consumed)\s+(\d+)\s+tokens", text, re.IGNORECASE)
|
||||
# Pattern 1c: dd command output (bytes copied with transfer rate)
|
||||
dd_match = re.search(r"\d+\s+bytes.*copied.*\d+\s+s.*[kMGT]?B/s", text)
|
||||
# Pattern 1d: Curl-style transfer data (bytes, speed indicators)
|
||||
# Pattern 1d: Curl-style transfer data (bytes, speed indicators - legacy)
|
||||
curl_match = re.search(
|
||||
r"(\d+\s+\d+\s+\d+\s+\d+.*?(?:k|M|G)?.*?--:--:--|Speed)", text
|
||||
)
|
||||
@@ -183,7 +219,10 @@ class command:
|
||||
if fraction_match:
|
||||
current = int(fraction_match.group(1))
|
||||
total = int(fraction_match.group(2))
|
||||
if total > 0:
|
||||
# Filter out dates, page numbers, and other non-progress fractions
|
||||
if (total > 0 and total <= 1000 and current <= total and
|
||||
not re.search(r"\b(?:page|chapter|section|line|row|column|year|month|day)\b", text, re.IGNORECASE) and
|
||||
not re.search(r"\d{1,2}/\d{1,2}/\d{2,4}", text)): # Date pattern
|
||||
percentage = (current / total) * 100
|
||||
if (
|
||||
percentage
|
||||
@@ -235,6 +274,62 @@ class command:
|
||||
):
|
||||
self.play_activity_beep()
|
||||
self.env["commandBuffer"]["lastProgressTime"] = current_time
|
||||
return
|
||||
|
||||
# Pattern 5: Braille spinner indicators
|
||||
braille_match = re.search(r'[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⡿⣟⣯⣷⣾⣽⣻⢿]', text)
|
||||
if braille_match:
|
||||
if current_time - self.env["commandBuffer"]["lastProgressTime"] >= 1.0:
|
||||
self.play_activity_beep()
|
||||
self.env["commandBuffer"]["lastProgressTime"] = current_time
|
||||
return
|
||||
|
||||
# Pattern 6: Claude/Codex working indicators (bullets + "esc to interrupt")
|
||||
claude_progress_match = re.search(
|
||||
r'^[\s]*[•◦][^\n]*\(\s*(?:\d+m\s+)?\d+s?\s+•\s+esc to interrupt[^)]*\)',
|
||||
text,
|
||||
re.IGNORECASE,
|
||||
)
|
||||
if claude_progress_match:
|
||||
if current_time - self.env["commandBuffer"]["lastProgressTime"] >= 1.0:
|
||||
self.play_activity_beep()
|
||||
self.env["commandBuffer"]["lastProgressTime"] = current_time
|
||||
return
|
||||
|
||||
# Pattern 7: Moon phase spinner indicators
|
||||
moon_match = re.search(r'[🌑🌒🌓🌔🌕🌖🌗🌘]', text)
|
||||
if moon_match:
|
||||
if current_time - self.env["commandBuffer"]["lastProgressTime"] >= 1.0:
|
||||
self.play_activity_beep()
|
||||
self.env["commandBuffer"]["lastProgressTime"] = current_time
|
||||
return
|
||||
|
||||
# Pattern 8: Thinking/processing with timing (🔄 Thinking... 23s)
|
||||
thinking_match = re.search(r'🔄[^\w]*(?:thinking|processing|working|analyzing)[^\d]*(\d+)s?\b', text, re.IGNORECASE)
|
||||
if thinking_match:
|
||||
# Extract timing value for activity beep frequency adjustment
|
||||
seconds = int(thinking_match.group(1))
|
||||
# Use slightly longer interval for thinking patterns to avoid spam
|
||||
thinking_interval = 1.5 if seconds < 10 else 2.0
|
||||
if (
|
||||
current_time - self.env["commandBuffer"]["lastProgressTime"]
|
||||
>= thinking_interval
|
||||
):
|
||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||
f"Playing thinking activity beep (timing: {seconds}s)",
|
||||
debug.DebugLevel.INFO,
|
||||
)
|
||||
self.play_activity_beep()
|
||||
self.env["commandBuffer"]["lastProgressTime"] = current_time
|
||||
return
|
||||
|
||||
# Pattern 9: Half-circle/circle progress indicators (◐ ◓ ◒ ◑)
|
||||
circle_match = re.search(r'[◐◓◒◑]', text)
|
||||
if circle_match:
|
||||
if current_time - self.env["commandBuffer"]["lastProgressTime"] >= 1.0:
|
||||
self.play_activity_beep()
|
||||
self.env["commandBuffer"]["lastProgressTime"] = current_time
|
||||
return
|
||||
|
||||
def play_progress_tone(self, percentage):
|
||||
# Map 0-100% to 400-1200Hz frequency range
|
||||
@@ -250,9 +345,21 @@ class command:
|
||||
self.play_quiet_tone(800, 0.08)
|
||||
|
||||
def play_quiet_tone(self, frequency, duration):
|
||||
"""Play a quiet tone using Sox directly"""
|
||||
"""Play a quiet tone using Sox directly with flood protection"""
|
||||
import shlex
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
# Flood protection: prevent beeps closer than 0.1 seconds apart
|
||||
current_time = time.time()
|
||||
if not hasattr(self, '_last_beep_time'):
|
||||
self._last_beep_time = 0
|
||||
|
||||
if current_time - self._last_beep_time < 0.1:
|
||||
# Skip this beep to prevent audio crackling on low-resource systems
|
||||
return
|
||||
|
||||
self._last_beep_time = current_time
|
||||
|
||||
# Build the Sox command: play -qn synth <duration> tri <frequency> gain
|
||||
# -8
|
||||
@@ -369,12 +476,10 @@ class command:
|
||||
"""Check if text contains URLs that might cause false progress detection"""
|
||||
import re
|
||||
|
||||
# Common URL patterns that might contain progress-like patterns
|
||||
# Specific URL patterns - only match actual URLs, not filenames
|
||||
url_patterns = [
|
||||
r"https?://[^\s]+", # http:// or https:// URLs
|
||||
r"ftp://[^\s]+", # ftp:// URLs
|
||||
r"www\.[^\s]+", # www. domains
|
||||
r"[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}[/\w.-]*", # domain.com/path patterns
|
||||
r"\S+://\S+\.\S{2,}", # Any protocol:// with domain.ext
|
||||
r"www\.[^\s]+\.[a-zA-Z]{2,}", # www.domain.ext patterns
|
||||
]
|
||||
|
||||
for pattern in url_patterns:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
|
||||
import time
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
@@ -19,17 +19,42 @@ class command:
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return "No Description found"
|
||||
return _("Announces incoming text changes")
|
||||
|
||||
def _was_handled_by_tab_completion(self, delta_text):
|
||||
"""Check if this delta was already handled by tab completion to avoid duplicates"""
|
||||
if "tabCompletion" not in self.env["commandBuffer"]:
|
||||
return False
|
||||
|
||||
tab_state = self.env["commandBuffer"]["tabCompletion"]
|
||||
|
||||
# Check if this exact delta was processed recently by tab completion
|
||||
if (tab_state.get("lastProcessedDelta") == delta_text and
|
||||
tab_state.get("lastProcessedTime")):
|
||||
|
||||
# Only suppress if processed within the last 50ms to avoid stale suppression
|
||||
# Reduced from 100ms to minimize false positives with rapid multi-line updates
|
||||
time_since_processed = time.time() - tab_state["lastProcessedTime"]
|
||||
if time_since_processed <= 0.05:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def run(self):
|
||||
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"speech", "autoReadIncoming"
|
||||
"speech", "auto_read_incoming"
|
||||
):
|
||||
return
|
||||
# is there something to read?
|
||||
if not self.env["runtime"]["ScreenManager"].is_delta(ignoreSpace=True):
|
||||
return
|
||||
|
||||
delta_text = self.env["screen"]["new_delta"]
|
||||
|
||||
# Skip if tab completion already handled this delta
|
||||
if self._was_handled_by_tab_completion(delta_text):
|
||||
return
|
||||
|
||||
# this must be a keyecho or something
|
||||
# if len(self.env['screen']['new_delta'].strip(' \n\t')) <= 1:
|
||||
x_move = abs(
|
||||
@@ -41,14 +66,14 @@ class command:
|
||||
- self.env["screen"]["old_cursor"]["y"]
|
||||
)
|
||||
|
||||
if (x_move >= 1) and x_move == len(self.env["screen"]["new_delta"]):
|
||||
if (x_move >= 1) and x_move == len(delta_text):
|
||||
# if len(self.env['screen']['new_delta'].strip(' \n\t0123456789'))
|
||||
# <= 2:
|
||||
if "\n" not in self.env["screen"]["new_delta"]:
|
||||
if "\n" not in delta_text:
|
||||
return
|
||||
# print(x_move, y_move, len(self.env['screen']['new_delta']), len(self.env['screen']['newNegativeDelta']))
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
self.env["screen"]["new_delta"], interrupt=False, flush=False
|
||||
delta_text, interrupt=False, flush=False
|
||||
)
|
||||
|
||||
def set_callback(self, callback):
|
||||
|
||||
@@ -38,7 +38,7 @@ class command:
|
||||
if int(time.time() - self.env["input"]["lastInputTime"]) < self.env[
|
||||
"runtime"
|
||||
]["SettingsManager"].get_setting_as_int(
|
||||
"promote", "inactiveTimeoutSec"
|
||||
"promote", "inactive_timeout_sec"
|
||||
):
|
||||
return
|
||||
if (
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Emoji VMenu category
|
||||
@@ -0,0 +1 @@
|
||||
# Flags emoji subcategory
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇦🇷"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Argentina flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Argentina flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇦🇺"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Australia flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Australia flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇧🇪"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Belgium flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Belgium flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇧🇷"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Brazil flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Brazil flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇨🇦"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Canada flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Canada flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇨🇳"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add China flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added China flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇩🇰"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Denmark flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Denmark flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇫🇮"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Finland flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Finland flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇫🇷"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add France flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added France flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇩🇪"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Germany flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Germany flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇬🇷"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Greece flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Greece flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇮🇳"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add India flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added India flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇮🇪"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Ireland flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Ireland flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇮🇱"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Israel flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Israel flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇮🇹"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Italy flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Italy flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇯🇵"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Japan flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Japan flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇲🇽"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Mexico flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Mexico flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇳🇱"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Netherlands flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Netherlands flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇳🇴"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Norway flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Norway flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇵🇱"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Poland flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Poland flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇵🇹"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Portugal flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Portugal flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇷🇺"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Russia flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Russia flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇿🇦"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add South Africa flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added South Africa flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇰🇷"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add South Korea flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added South Korea flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇪🇸"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Spain flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Spain flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇸🇪"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Sweden flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Sweden flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇨🇭"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Switzerland flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Switzerland flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇹🇷"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Turkey flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Turkey flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇬🇧"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add UK flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added UK flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇺🇦"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add Ukraine flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added Ukraine flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🇺🇸"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add USA flag emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added USA flag to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
# Food emoji subcategory
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🍎"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Red apple emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added red apple to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🥑"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Avocado emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added avocado to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🍺"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Beer emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added beer to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🎂"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Birthday cake emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added birthday cake to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "☕"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add coffee emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added coffee to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🍩"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Donut emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added donut to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🍔"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add hamburger emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added hamburger to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🍕"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add pizza emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added pizza to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🍓"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Strawberry emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added strawberry to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🌮"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Taco emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added taco to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
# Holidays emoji subcategory
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "👾"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Alien monster emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added alien monster to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🦇"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Add bat emoji to clipboard"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added bat to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
class command():
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
self.emoji = "🐈⬛"
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
|
||||
def getDescription(self):
|
||||
return "Black cat emoji"
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", self.emoji
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
"Added black cat to clipboard",
|
||||
interrupt=False, flush=False
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user