389 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			389 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| # Fenrir Development Guide
 | |
| 
 | |
| This document provides information for developers who want to contribute to Fenrir or understand its architecture.
 | |
| 
 | |
| ## Project Structure
 | |
| 
 | |
| Fenrir follows a modular, driver-based architecture:
 | |
| 
 | |
| ```
 | |
| src/fenrirscreenreader/
 | |
| ├── core/                    # Core system modules
 | |
| │   ├── fenrirManager.py    # Main application manager
 | |
| │   ├── screenManager.py   # Screen handling
 | |
| │   ├── inputManager.py    # Input handling
 | |
| │   ├── outputManager.py   # Speech/sound output
 | |
| │   ├── commandManager.py  # Command system
 | |
| │   └── settingsManager.py # Configuration management
 | |
| ├── commands/               # Command implementations
 | |
| │   ├── commands/          # User-invoked commands
 | |
| │   ├── onCursorChange/   # Cursor movement hooks
 | |
| │   ├── onScreenUpdate/   # Screen update hooks
 | |
| │   ├── onKeyInput/       # Key input hooks
 | |
| │   └── help/             # Tutorial system
 | |
| ├── drivers/              # Driver implementations
 | |
| │   ├── inputDriver/     # Input drivers (evdev, pty, atspi)
 | |
| │   ├── screenDriver/    # Screen drivers (vcsa, pty)
 | |
| │   ├── speechDriver/    # Speech drivers (speechd, generic)
 | |
| │   └── soundDriver/     # Sound drivers (generic, gstreamer)
 | |
| └── utils/               # Utility modules
 | |
| ```
 | |
| 
 | |
| ## Core Architecture
 | |
| 
 | |
| ### Driver System
 | |
| Fenrir uses a pluggable driver architecture:
 | |
| 
 | |
| 1. **Input Drivers**: Capture keyboard input
 | |
|    - evdevDriver: Linux evdev (recommended)
 | |
|    - ptyDriver: Terminal emulation
 | |
|    - atspiDriver: AT-SPI for desktop
 | |
| 
 | |
| 2. **Screen Drivers**: Read screen content
 | |
|    - vcsaDriver: Linux VCSA devices
 | |
|    - ptyDriver: Terminal emulation
 | |
| 
 | |
| 3. **Speech Drivers**: Text-to-speech output
 | |
|    - speechdDriver: Speech-dispatcher
 | |
|    - genericDriver: Command-line TTS
 | |
| 
 | |
| 4. **Sound Drivers**: Audio output
 | |
|    - genericDriver: Sox-based
 | |
|    - gstreamerDriver: GStreamer
 | |
| 
 | |
| 5. **Remote Drivers**: Remote control interfaces
 | |
|    - unixDriver: Unix socket control
 | |
|    - tcpDriver: TCP socket control
 | |
| 
 | |
| ### Command System
 | |
| Commands are Python modules that implement specific functionality:
 | |
| 
 | |
| ```python
 | |
| class command():
 | |
|     def __init__(self):
 | |
|         pass
 | |
|     
 | |
|     def initialize(self, environment):
 | |
|         self.env = environment
 | |
|     
 | |
|     def shutdown(self):
 | |
|         pass
 | |
|     
 | |
|     def getDescription(self):
 | |
|         return _('Command description')
 | |
|     
 | |
|     def run(self):
 | |
|         # Command implementation
 | |
|         pass
 | |
| ```
 | |
| 
 | |
| ### Event Hooks
 | |
| Fenrir supports various event hooks:
 | |
| 
 | |
| - **onCursorChange**: Triggered when cursor moves
 | |
| - **onScreenUpdate**: Triggered on screen content changes
 | |
| - **onKeyInput**: Triggered on key presses
 | |
| - **onByteInput**: Triggered on byte-level input
 | |
| - **onScreenChanged**: Triggered when switching screens
 | |
| 
 | |
| ## Development Setup
 | |
| 
 | |
| ### Requirements
 | |
| - Python 3.6+
 | |
| - python3-evdev
 | |
| - python3-pyudev
 | |
| - speech-dispatcher
 | |
| - sox
 | |
| 
 | |
| ### Getting Started
 | |
| ```bash
 | |
| # Clone repository
 | |
| git clone https://git.stormux.org/storm/fenrir.git
 | |
| cd fenrir
 | |
| 
 | |
| # Install dependencies
 | |
| sudo pip3 install -r requirements.txt
 | |
| 
 | |
| # Run from source
 | |
| cd src/
 | |
| sudo ./fenrir -f -d
 | |
| ```
 | |
| 
 | |
| ### Testing
 | |
| ```bash
 | |
| # Run in debug mode
 | |
| sudo ./fenrir -f -d -p
 | |
| 
 | |
| # Debug output goes to:
 | |
| # - Console (with -p flag)
 | |
| # - /var/log/fenrir.log
 | |
| ```
 | |
| 
 | |
| ## Creating Commands
 | |
| 
 | |
| ### Basic Command
 | |
| Create a file in `src/fenrirscreenreader/commands/commands/`:
 | |
| 
 | |
| ```python
 | |
| from fenrirscreenreader.core import debug
 | |
| 
 | |
| class command():
 | |
|     def __init__(self):
 | |
|         pass
 | |
|         
 | |
|     def initialize(self, environment):
 | |
|         self.env = environment
 | |
|         
 | |
|     def shutdown(self):
 | |
|         pass
 | |
|         
 | |
|     def getDescription(self):
 | |
|         return _('My custom command')
 | |
|         
 | |
|     def run(self):
 | |
|         # Get current text
 | |
|         text = self.env['screen']['newContentText']
 | |
|         
 | |
|         # Speak something
 | |
|         self.env['runtime']['outputManager'].presentText('Hello World')
 | |
|         
 | |
|         # Play sound
 | |
|         self.env['runtime']['outputManager'].playSoundIcon('Accept')
 | |
| ```
 | |
| 
 | |
| ### Key Bindings
 | |
| Add key bindings in keyboard layout files:
 | |
| `config/keyboard/desktop.conf` or `config/keyboard/laptop.conf`
 | |
| 
 | |
| ```ini
 | |
| [KEY_CTRL]#[KEY_ALT]#[KEY_H]=my_command
 | |
| ```
 | |
| 
 | |
| ### Event Hooks
 | |
| Create event handlers in appropriate directories:
 | |
| 
 | |
| ```python
 | |
| # onCursorChange/my_hook.py
 | |
| class command():
 | |
|     def __init__(self):
 | |
|         pass
 | |
|         
 | |
|     def initialize(self, environment):
 | |
|         self.env = environment
 | |
|         
 | |
|     def shutdown(self):
 | |
|         pass
 | |
|         
 | |
|     def getDescription(self):
 | |
|         return _('My cursor change handler')
 | |
|         
 | |
|     def run(self):
 | |
|         if self.env['runtime']['cursorManager'].isCursorHorizontalMove():
 | |
|             # Handle horizontal cursor movement
 | |
|             pass
 | |
| ```
 | |
| 
 | |
| ## Creating Drivers
 | |
| 
 | |
| ### Driver Template
 | |
| ```python
 | |
| class driver():
 | |
|     def __init__(self):
 | |
|         pass
 | |
|         
 | |
|     def initialize(self, environment):
 | |
|         self.env = environment
 | |
|         
 | |
|     def shutdown(self):
 | |
|         pass
 | |
|         
 | |
|     # Driver-specific methods...
 | |
| ```
 | |
| 
 | |
| ### Input Driver
 | |
| Implement these methods:
 | |
| - `getInputEvent()`: Return input events
 | |
| - `writeEventBuffer()`: Handle output events
 | |
| - `grabDevices()`: Take exclusive control
 | |
| - `releaseDevices()`: Release control
 | |
| 
 | |
| ### Screen Driver
 | |
| Implement these methods:
 | |
| - `getCurrScreen()`: Get current screen content
 | |
| - `getSessionInformation()`: Get session info
 | |
| 
 | |
| ### Speech Driver
 | |
| Implement these methods:
 | |
| - `speak()`: Speak text
 | |
| - `cancel()`: Stop speech
 | |
| - `setCallback()`: Set callback functions
 | |
| 
 | |
| ### Remote Driver
 | |
| Implement these methods:
 | |
| - `initialize()`: Setup socket/connection
 | |
| - `watchDog()`: Listen for incoming commands
 | |
| - `shutdown()`: Clean up connections
 | |
| 
 | |
| #### Remote Driver Example
 | |
| ```python
 | |
| class driver(remoteDriver):
 | |
|     def initialize(self, environment):
 | |
|         self.env = environment
 | |
|         # Start watchdog thread
 | |
|         self.env['runtime']['processManager'].addCustomEventThread(
 | |
|             self.watchDog, multiprocess=True
 | |
|         )
 | |
|     
 | |
|     def watchDog(self, active, eventQueue):
 | |
|         # Listen for connections and process commands
 | |
|         while active.value:
 | |
|             # Accept connections
 | |
|             # Parse incoming data
 | |
|             # Send to event queue
 | |
|             eventQueue.put({
 | |
|                 "Type": fenrirEventType.RemoteIncomming,
 | |
|                 "Data": command_text
 | |
|             })
 | |
| ```
 | |
| 
 | |
| ## Configuration
 | |
| 
 | |
| ### Settings System
 | |
| Settings are hierarchical:
 | |
| 1. Command-line options (`-o`)
 | |
| 2. Configuration file
 | |
| 3. Hard-coded defaults
 | |
| 
 | |
| ### Adding Settings
 | |
| 1. Add default value to `core/settingsData.py`
 | |
| 2. Access via `self.env['runtime']['settingsManager'].getSetting(section, key)`
 | |
| 
 | |
| ## Debugging
 | |
| 
 | |
| ### Debug Levels
 | |
| - 0: DEACTIVE
 | |
| - 1: ERROR
 | |
| - 2: WARNING  
 | |
| - 3: INFO
 | |
| 
 | |
| ### Debug Output
 | |
| ```python
 | |
| self.env['runtime']['debug'].writeDebugOut(
 | |
|     'Debug message', 
 | |
|     debug.debugLevel.INFO
 | |
| )
 | |
| ```
 | |
| 
 | |
| ### Testing Commands
 | |
| ```bash
 | |
| # Test specific functionality
 | |
| sudo fenrir -f -d -o "general#debugLevel=3"
 | |
| 
 | |
| # Test with custom config
 | |
| sudo fenrir -f -s /path/to/test.conf
 | |
| ```
 | |
| 
 | |
| ## Contributing
 | |
| 
 | |
| ### Code Style
 | |
| - Follow PEP 8
 | |
| - Use descriptive variable names
 | |
| - Add docstrings for complex functions
 | |
| - Handle exceptions gracefully
 | |
| 
 | |
| ### Testing
 | |
| - Test with different drivers
 | |
| - Test keyboard layouts
 | |
| - Test on different terminals
 | |
| - Verify accessibility features
 | |
| 
 | |
| ### Submitting Changes
 | |
| 1. Fork the repository
 | |
| 2. Create feature branch
 | |
| 3. Make changes with clear commit messages
 | |
| 4. Test thoroughly
 | |
| 5. Submit pull request
 | |
| 
 | |
| ## API Reference
 | |
| 
 | |
| ### Environment Structure
 | |
| The `environment` dict contains all runtime data:
 | |
| 
 | |
| ```python
 | |
| environment = {
 | |
|     'runtime': {
 | |
|         'settingsManager': settingsManager,
 | |
|         'commandManager': commandManager,
 | |
|         'screenManager': screenManager,
 | |
|         'inputManager': inputManager,
 | |
|         'outputManager': outputManager,
 | |
|         'debug': debugManager,
 | |
|         # ... other managers
 | |
|     },
 | |
|     'screen': {
 | |
|         'newContentText': '',
 | |
|         'oldContentText': '',
 | |
|         'newCursor': {'x': 0, 'y': 0},
 | |
|         'oldCursor': {'x': 0, 'y': 0},
 | |
|         # ... screen data
 | |
|     },
 | |
|     'general': {
 | |
|         'prevCommand': '',
 | |
|         'currCommand': '',
 | |
|         # ... general data
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Common Operations
 | |
| 
 | |
| #### Speaking Text
 | |
| ```python
 | |
| self.env['runtime']['outputManager'].presentText('Hello')
 | |
| ```
 | |
| 
 | |
| #### Playing Sounds
 | |
| ```python
 | |
| self.env['runtime']['outputManager'].playSoundIcon('Accept')
 | |
| ```
 | |
| 
 | |
| #### Getting Settings
 | |
| ```python
 | |
| rate = self.env['runtime']['settingsManager'].getSetting('speech', 'rate')
 | |
| ```
 | |
| 
 | |
| #### Cursor Information
 | |
| ```python
 | |
| x = self.env['screen']['newCursor']['x']
 | |
| y = self.env['screen']['newCursor']['y']
 | |
| ```
 | |
| 
 | |
| #### Screen Content
 | |
| ```python
 | |
| text = self.env['screen']['newContentText']
 | |
| lines = text.split('\n')
 | |
| current_line = lines[self.env['screen']['newCursor']['y']]
 | |
| ```
 | |
| 
 | |
| ## Maintenance
 | |
| 
 | |
| ### Release Process
 | |
| 1. Update version in `fenrirVersion.py`
 | |
| 2. Update changelog
 | |
| 3. Test on multiple systems
 | |
| 4. Tag release
 | |
| 5. Update documentation
 | |
| 
 | |
| ### Compatibility
 | |
| - Maintain Python 3.6+ compatibility
 | |
| - Test on multiple Linux distributions
 | |
| - Ensure driver compatibility
 | |
| - Check dependencies
 | |
| 
 | |
| ## Resources
 | |
| 
 | |
| - **Repository**: https://git.stormux.org/storm/fenrir
 | |
| - **Wiki**: https://git.stormux.org/storm/fenrir/wiki
 | |
| - **Issues**: Use repository issue tracker
 | |
| - **Community**: IRC irc.stormux.org #stormux
 | |
| - **Email**: stormux+subscribe@groups.io |