From 9cdf80b313f88212e238cf92031dc2a9dec5292a Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 7 Dec 2024 23:36:21 -0500 Subject: [PATCH 1/7] Moved flag parsing to launcher. Got read of fenrir-daemon and put everything into the fenrir launcher. --- src/fenrir | 109 ++++++++++- src/fenrir-daemon | 28 --- src/fenrir-daemon-pypy | 28 --- src/fenrirscreenreader/core/fenrirManager.py | 185 ++++++------------- 4 files changed, 161 insertions(+), 189 deletions(-) delete mode 100755 src/fenrir-daemon delete mode 100755 src/fenrir-daemon-pypy diff --git a/src/fenrir b/src/fenrir index 735e09e6..71c89edd 100755 --- a/src/fenrir +++ b/src/fenrir @@ -2,20 +2,115 @@ # -*- coding: utf-8 -*- # Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. +# By Chrys, Storm Dragon, and contributors. -import os, sys, inspect +import os +import sys +import inspect +import argparse +from daemonize import Daemonize + +# Get the fenrir installation path fenrirPath = os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) - if not fenrirPath in sys.path: sys.path.append(fenrirPath) from fenrirscreenreader.core import fenrirManager +from fenrirscreenreader import fenrirVersion + +def create_argument_parser(): + """Create and return the argument parser for Fenrir""" + argumentParser = argparse.ArgumentParser( + description="Fenrir - A console screen reader for Linux", + formatter_class=argparse.RawDescriptionHelpFormatter + ) + argumentParser.add_argument( + '-v', '--version', + action='version', + version=f'Fenrir screen reader version {fenrirVersion.version}-{fenrirVersion.codeName}', + help='Show version information and exit' + ) + argumentParser.add_argument( + '-f', '--foreground', + action='store_true', + help='Run Fenrir in the foreground (default: run as daemon)' + ) + argumentParser.add_argument( + '-s', '--setting', + metavar='SETTING-FILE', + default='/etc/fenrir/settings/settings.conf', + help='Path to custom settings file' + ) + argumentParser.add_argument( + '-o', '--options', + metavar='SECTION#SETTING=VALUE;..', + default='', + help='Override settings file options. Format: SECTION#SETTING=VALUE;... (case sensitive)' + ) + argumentParser.add_argument( + '-d', '--debug', + action='store_true', + help='Enable debug mode' + ) + argumentParser.add_argument( + '-p', '--print', + action='store_true', + help='Print debug messages to screen' + ) + argumentParser.add_argument( + '-e', '--emulated-pty', + action='store_true', + help='Use PTY emulation with escape sequences for input (enables desktop/X/Wayland usage)' + ) + argumentParser.add_argument( + '-E', '--emulated-evdev', + action='store_true', + help='Use PTY emulation with evdev for input (single instance)' + ) + return argumentParser + +def validate_arguments(cliArgs): + """Validate command line arguments""" + if cliArgs.options: + for option in cliArgs.options.split(';'): + if option and ('#' not in option or '=' not in option): + return False, f"Invalid option format: {option}\nExpected format: SECTION#SETTING=VALUE" + + if cliArgs.emulated_pty and cliArgs.emulated_evdev: + return False, "Cannot use both --emulated-pty and --emulated-evdev simultaneously" + + return True, None + +def run_fenrir(): + """Main function that runs Fenrir""" + fenrirApp = fenrirManager.fenrirManager(cliArgs) + fenrirApp.proceed() + del fenrirApp def main(): - app = fenrirManager.fenrirManager() - app.proceed() - del app + global cliArgs + argumentParser = create_argument_parser() + cliArgs = argumentParser.parse_args() + + # Validate arguments + isValid, errorMsg = validate_arguments(cliArgs) + if not isValid: + argumentParser.error(errorMsg) + sys.exit(1) + + if cliArgs.foreground: + # Run directly in foreground + run_fenrir() + else: + # Run as daemon + pidFile = "/run/fenrir.pid" + daemonProcess = Daemonize( + app="fenrir", + pid=pidFile, + action=run_fenrir, + chdir=fenrirPath + ) + daemonProcess.start() if __name__ == "__main__": - main() + main() diff --git a/src/fenrir-daemon b/src/fenrir-daemon deleted file mode 100755 index e5abb41c..00000000 --- a/src/fenrir-daemon +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -import os, sys, inspect -fenrirPath = os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) - -if not fenrirPath in sys.path: - sys.path.append(fenrirPath) - -from fenrirscreenreader.core import fenrirManager -from daemonize import Daemonize - -pidFile = "/run/fenrir.pid" - -def main(): - app = fenrirManager.fenrirManager() - app.proceed() - del app - -if __name__ == "__main__": - # for debug in foreground - #daemon = Daemonize(app="fenrir-daemon", pid=pidFile, action=main, foreground=True,chdir=fenrirPath) - daemon = Daemonize(app="fenrir-daemon", pid=pidFile, action=main, chdir=fenrirPath) - daemon.start() - diff --git a/src/fenrir-daemon-pypy b/src/fenrir-daemon-pypy deleted file mode 100755 index 363b2b6c..00000000 --- a/src/fenrir-daemon-pypy +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env pypy3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -import os, sys, inspect -fenrirPath = os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) - -if not fenrirPath in sys.path: - sys.path.append(fenrirPath) - -from fenrirscreenreader.core import fenrirManager -from daemonize import Daemonize - -pidFile = "/run/fenrir.pid" - -def main(): - app = fenrirManager.fenrirManager() - app.proceed() - del app - -if __name__ == "__main__": - # for debug in foreground - #daemon = Daemonize(app="fenrir-daemon", pid=pidFile, action=main, foreground=True,chdir=os.path.dirname(os.path.realpath(fenrirVersion.__file__))) - daemon = Daemonize(app="fenrir-daemon", pid=pidFile, action=main, chdir=fenrirPath) - daemon.start() - diff --git a/src/fenrirscreenreader/core/fenrirManager.py b/src/fenrirscreenreader/core/fenrirManager.py index d7bf2ff8..f88ee62c 100644 --- a/src/fenrirscreenreader/core/fenrirManager.py +++ b/src/fenrirscreenreader/core/fenrirManager.py @@ -2,109 +2,50 @@ # -*- coding: utf-8 -*- # Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. +# By Chrys, Storm Dragon, and contributors. -import signal, time, argparse, os, sys +import signal +import time +import os +import sys from fenrirscreenreader.core import i18n from fenrirscreenreader.core import settingsManager from fenrirscreenreader.core import debug from fenrirscreenreader.core.eventData import fenrirEventType -from fenrirscreenreader import fenrirVersion class fenrirManager(): - def __init__(self): - self.initialized = False - cliArgs = self.handleArgs() - if not cliArgs: - return + def __init__(self, cliArgs): + self.isInitialized = False try: self.environment = settingsManager.settingsManager().initFenrirConfig(cliArgs, self) if not self.environment: raise RuntimeError('Cannot Initialize. Maybe the configfile is not available or not parseable') except RuntimeError: raise + self.environment['runtime']['outputManager'].presentText(_("Start Fenrir"), soundIcon='ScreenReaderOn', interrupt=True) signal.signal(signal.SIGINT, self.captureSignal) signal.signal(signal.SIGTERM, self.captureSignal) - self.initialized = True + + self.isInitialized = True self.modifierInput = False self.singleKeyCommand = False self.command = '' self.setProcessName() - def handleArgs(self): - """ - Parse and handle command line arguments for Fenrir. - - Returns: - argparse.Namespace: Parsed command line arguments - None: If argument parsing fails - """ - parser = argparse.ArgumentParser( - description="Fenrir - A console screen reader for Linux", - formatter_class=argparse.RawDescriptionHelpFormatter - ) - parser.add_argument( - '-v', '--version', - action='version', - version=f'Fenrir screen reader version {fenrirVersion.version}-{fenrirVersion.codeName}', - help='Show version information and exit' - ) - parser.add_argument( - '-s', '--setting', - metavar='SETTING-FILE', - default='/etc/fenrir/settings/settings.conf', - help='Path to custom settings file' - ) - parser.add_argument( - '-o', '--options', - metavar='SECTION#SETTING=VALUE;..', - default='', - help='Override settings file options. Format: SECTION#SETTING=VALUE;... (case sensitive)' - ) - parser.add_argument( - '-d', '--debug', - action='store_true', - help='Enable debug mode' - ) - parser.add_argument( - '-p', '--print', - action='store_true', - help='Print debug messages to screen' - ) - parser.add_argument( - '-e', '--emulated-pty', - action='store_true', - help='Use PTY emulation with escape sequences for input (enables desktop/X/Wayland usage)' - ) - parser.add_argument( - '-E', '--emulated-evdev', - action='store_true', - help='Use PTY emulation with evdev for input (single instance)' - ) - try: - args = parser.parse_args() - # Only do format validation, let file existence be handled by the config initialization - if args.options: - for option in args.options.split(';'): - if option and ('#' not in option or '=' not in option): - parser.error(f"Invalid option format: {option}\nExpected format: SECTION#SETTING=VALUE") - if args.emulated_pty and args.emulated_evdev: - parser.error("Cannot use both --emulated-pty and --emulated-evdev simultaneously") - return args - except Exception as e: - print(f"Error parsing arguments: {str(e)}", file=sys.stderr) - return None + def proceed(self): - if not self.initialized: + if not self.isInitialized: return self.environment['runtime']['eventManager'].startMainEventLoop() self.shutdown() + def handleInput(self, event): - #startTime = time.time() - self.environment['runtime']['debug'].writeDebugOut('DEBUG INPUT fenrirMan:' + str(event),debug.debugLevel.INFO) + self.environment['runtime']['debug'].writeDebugOut('DEBUG INPUT fenrirMan:' + str(event), debug.debugLevel.INFO) + if not event['Data']: event['Data'] = self.environment['runtime']['inputManager'].getInputEvent() + if event['Data']: event['Data']['EventName'] = self.environment['runtime']['inputManager'].convertEventName(event['Data']['EventName']) self.environment['runtime']['inputManager'].handleInputEvent(event['Data']) @@ -113,6 +54,7 @@ class fenrirManager(): if self.environment['runtime']['inputManager'].noKeyPressed(): self.environment['runtime']['inputManager'].clearLastDeepInput() + if self.environment['runtime']['screenManager'].isSuspendingScreen(): self.environment['runtime']['inputManager'].writeEventBuffer() else: @@ -132,6 +74,7 @@ class fenrirManager(): self.environment['runtime']['inputManager'].clearEventBuffer() else: self.environment['runtime']['inputManager'].writeEventBuffer() + if self.environment['runtime']['inputManager'].noKeyPressed(): self.modifierInput = False self.singleKeyCommand = False @@ -139,73 +82,59 @@ class fenrirManager(): self.environment['runtime']['inputManager'].handleDeviceGrab() if self.environment['input']['keyForeward'] > 0: - self.environment['input']['keyForeward'] -=1 + self.environment['input']['keyForeward'] -= 1 + self.environment['runtime']['commandManager'].executeDefaultTrigger('onKeyInput') - #print('handleInput:',time.time() - startTime) + def handleByteInput(self, event): - if not event['Data']: - return - if event['Data'] == b'': + if not event['Data'] or event['Data'] == b'': return self.environment['runtime']['byteManager'].handleByteInput(event['Data']) self.environment['runtime']['commandManager'].executeDefaultTrigger('onByteInput') - def handleExecuteCommand(self, event): - if not event['Data']: + + def handleExecuteCommand(self, event): + if not event['Data'] or event['Data'] == '': return - if event['Data'] == '': - return - command = event['Data'] + currentCommand = event['Data'] # special modes if self.environment['runtime']['helpManager'].isTutorialMode(): - if self.environment['runtime']['commandManager'].commandExists( command, 'help'): - self.environment['runtime']['commandManager'].executeCommand( command, 'help') + if self.environment['runtime']['commandManager'].commandExists(currentCommand, 'help'): + self.environment['runtime']['commandManager'].executeCommand(currentCommand, 'help') return elif self.environment['runtime']['vmenuManager'].getActive(): - if self.environment['runtime']['commandManager'].commandExists( command, 'vmenu-navigation'): - self.environment['runtime']['commandManager'].executeCommand( command, 'vmenu-navigation') + if self.environment['runtime']['commandManager'].commandExists(currentCommand, 'vmenu-navigation'): + self.environment['runtime']['commandManager'].executeCommand(currentCommand, 'vmenu-navigation') return # default - self.environment['runtime']['commandManager'].executeCommand( command, 'commands') + self.environment['runtime']['commandManager'].executeCommand(currentCommand, 'commands') + def handleRemoteIncomming(self, event): if not event['Data']: return self.environment['runtime']['remoteManager'].handleRemoteIncomming(event['Data']) + def handleScreenChange(self, event): self.environment['runtime']['screenManager'].hanldeScreenChange(event['Data']) - ''' - if self.environment['runtime']['applicationManager'].isApplicationChange(): - self.environment['runtime']['commandManager'].executeDefaultTrigger('onApplicationChange') - self.environment['runtime']['commandManager'].executeSwitchTrigger('onSwitchApplicationProfile', \ - self.environment['runtime']['applicationManager'].getPrevApplication(), \ - self.environment['runtime']['applicationManager'].getCurrentApplication()) - ''' if self.environment['runtime']['vmenuManager'].getActive(): return - self.environment['runtime']['commandManager'].executeDefaultTrigger('onScreenChanged') self.environment['runtime']['screenDriver'].getCurrScreen() + def handleScreenUpdate(self, event): - #startTime = time.time() self.environment['runtime']['screenManager'].handleScreenUpdate(event['Data']) - ''' - if self.environment['runtime']['applicationManager'].isApplicationChange(): - self.environment['runtime']['commandManager'].executeDefaultTrigger('onApplicationChange') - self.environment['runtime']['commandManager'].executeSwitchTrigger('onSwitchApplicationProfile', \ - self.environment['runtime']['applicationManager'].getPrevApplication(), \ - self.environment['runtime']['applicationManager'].getCurrentApplication()) - ''' - # timout for the last keypress + if time.time() - self.environment['runtime']['inputManager'].getLastInputTime() >= 0.3: self.environment['runtime']['inputManager'].clearLastDeepInput() - # has cursor changed? - if self.environment['runtime']['cursorManager'].isCursorVerticalMove() or \ - self.environment['runtime']['cursorManager'].isCursorHorizontalMove(): + + if (self.environment['runtime']['cursorManager'].isCursorVerticalMove() or + self.environment['runtime']['cursorManager'].isCursorHorizontalMove()): self.environment['runtime']['commandManager'].executeDefaultTrigger('onCursorChange') + self.environment['runtime']['commandManager'].executeDefaultTrigger('onScreenUpdate') self.environment['runtime']['inputManager'].clearLastDeepInput() - #print('handleScreenUpdate:',time.time() - startTime) + def handlePlugInputDevice(self, event): try: self.environment['runtime']['inputManager'].setLastDetectedDevices(event['Data']) @@ -214,26 +143,27 @@ class fenrirManager(): self.environment['runtime']['inputManager'].handlePlugInputDevice(event['Data']) self.environment['runtime']['commandManager'].executeDefaultTrigger('onPlugInputDevice', force=True) self.environment['runtime']['inputManager'].setLastDetectedDevices(None) - def handleHeartBeat(self, event): - self.environment['runtime']['commandManager'].executeDefaultTrigger('onHeartBeat',force=True) - #self.environment['runtime']['outputManager'].brailleText(flush=False) + def handleHeartBeat(self, event): + self.environment['runtime']['commandManager'].executeDefaultTrigger('onHeartBeat', force=True) def detectShortcutCommand(self): if self.environment['input']['keyForeward'] > 0: return + if len(self.environment['input']['prevInput']) > len(self.environment['input']['currInput']): return + if self.environment['runtime']['inputManager'].isKeyPress(): self.modifierInput = self.environment['runtime']['inputManager'].currKeyIsModifier() else: if not self.environment['runtime']['inputManager'].noKeyPressed(): if self.singleKeyCommand: - self.singleKeyCommand = len(self.environment['input']['currInput'])== 1 - # key is already released. we need the old one - if not( self.singleKeyCommand and self.environment['runtime']['inputManager'].noKeyPressed()): - shortcut = self.environment['runtime']['inputManager'].getCurrShortcut() - self.command = self.environment['runtime']['inputManager'].getCommandForShortcut(shortcut) + self.singleKeyCommand = len(self.environment['input']['currInput']) == 1 + + if not(self.singleKeyCommand and self.environment['runtime']['inputManager'].noKeyPressed()): + currentShortcut = self.environment['runtime']['inputManager'].getCurrShortcut() + self.command = self.environment['runtime']['inputManager'].getCommandForShortcut(currentShortcut) if not self.modifierInput: if self.environment['runtime']['inputManager'].isKeyPress(): @@ -252,7 +182,8 @@ class fenrirManager(): if self.singleKeyCommand: self.environment['runtime']['eventManager'].putToEventQueue(fenrirEventType.ExecuteCommand, self.command) self.command = '' - def setProcessName(self, name = 'fenrir'): + + def setProcessName(self, name='fenrir'): """Attempts to set the process name to 'fenrir'.""" try: from setproctitle import setproctitle @@ -273,12 +204,14 @@ class fenrirManager(): pass return False + def shutdownRequest(self): try: self.environment['runtime']['eventManager'].stopMainEventLoop() except: pass - def captureSignal(self, siginit, frame): + + def captureSignal(self, sigInit, frame): self.shutdownRequest() def shutdown(self): @@ -287,10 +220,10 @@ class fenrirManager(): self.environment['runtime']['outputManager'].presentText(_("Quit Fenrir"), soundIcon='ScreenReaderOff', interrupt=True) self.environment['runtime']['eventManager'].cleanEventQueue() time.sleep(0.6) - for currManager in self.environment['general']['managerList']: - if self.environment['runtime'][currManager]: - self.environment['runtime'][currManager].shutdown() - del self.environment['runtime'][currManager] + + for currentManager in self.environment['general']['managerList']: + if self.environment['runtime'][currentManager]: + self.environment['runtime'][currentManager].shutdown() + del self.environment['runtime'][currentManager] self.environment = None - From 0e787c21ab6a8be5ccc008db16a781142e85567d Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 7 Dec 2024 23:39:22 -0500 Subject: [PATCH 2/7] Removed fenrir-daemon from setup.py. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eede63f0..d70316f7 100755 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ setup( # Packages packages=find_packages('src/'), package_dir={'': 'src/'}, - scripts=['src/fenrir','src/fenrir-daemon'], + scripts=['src/fenrir'], # Include additional files into the package include_package_data=True, From 84514edc96f7c8fd7b479e3d8206195ec9f81c7c Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 8 Dec 2024 00:02:45 -0500 Subject: [PATCH 3/7] Removed the pypy version of the old launcher file. --- src/fenrir-pypy | 21 --------------------- src/fenrirscreenreader/fenrirVersion.py | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100755 src/fenrir-pypy diff --git a/src/fenrir-pypy b/src/fenrir-pypy deleted file mode 100755 index b2d50a1c..00000000 --- a/src/fenrir-pypy +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env pypy3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -import os, sys, inspect -fenrirPath = os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) - -if not fenrirPath in sys.path: - sys.path.append(fenrirPath) - -from fenrirscreenreader.core import fenrirManager - -def main(): - app = fenrirManager.fenrirManager() - app.proceed() - del app - -if __name__ == "__main__": - main() diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index db064326..64b77140 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -version = "2024.12.07" +version = "2024.12.08" codeName = "testing" From 1696d62526a81a28cfa2549821f346964bdae602 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 8 Dec 2024 04:37:53 -0500 Subject: [PATCH 4/7] Updated Braille support now that I'm more familiar with how it should work. --- .../brailleDriver/brlapiDriver.py | 148 ++++++++++++--- src/fenrirscreenreader/core/brailleDriver.py | 174 +++++++++++++----- 2 files changed, 249 insertions(+), 73 deletions(-) diff --git a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py b/src/fenrirscreenreader/brailleDriver/brlapiDriver.py index 4b91b01b..f2df2a50 100644 --- a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py +++ b/src/fenrirscreenreader/brailleDriver/brlapiDriver.py @@ -2,65 +2,167 @@ # -*- coding: utf-8 -*- # Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. +# By Chrys, Storm Dragon, and contributors. from fenrirscreenreader.core import debug from fenrirscreenreader.core.brailleDriver import brailleDriver class driver(brailleDriver): + """BRLAPI implementation of the braille driver interface. + Supports all braille displays compatible with BRLTTY.""" + def __init__(self): + """Initialize the BRLAPI driver.""" brailleDriver.__init__(self) self._brl = None + self._last_written_text = "" def initialize(self, environment): + """Initialize the braille driver with BRLAPI connection.""" self.env = environment try: import brlapi self._brl = brlapi.Connection() self._deviceSize = self._brl.displaySize + # Accept all key types from any braille display + self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX]) + self._current_cursor_pos = 0 except Exception as e: - print(e) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) return self._isInitialized = True def getDeviceSize(self): - if not self._isInitialized: - return (0,0) - if not self._deviceSize: - return (0,0) + """Get the size of the connected braille display.""" + if not self._isInitialized or not self._deviceSize: + return (0, 0) return self._deviceSize - def flush(self): + def writeText(self, text): + """Write text to the braille display.""" if not self._isInitialized: return try: - self._brl.writeText('',0) + self._last_written_text = text + # Handle unicode properly for international braille + if isinstance(text, str): + self._brl.writeText(text) + else: + self._brl.writeText(text.decode('utf-8', 'replace')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.flush '+str(e),debug.debugLevel.ERROR) - - def writeText(self,text): - if not self._isInitialized: - return - try: - self._brl.writeText(text) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.writeText '+str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut('BRAILLE.writeText ' + str(e), debug.debugLevel.ERROR) def connectDevice(self): - self._brl = brlapi.Connection() + """Establish connection with BRLAPI.""" + try: + import brlapi + self._brl = brlapi.Connection() + # Accept all key types from the connected display + self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX]) + return True + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.connectDevice ' + str(e), debug.debugLevel.ERROR) + return False def enterScreen(self, screen): + """Enter a specific TTY mode.""" if not self._isInitialized: return - self._brl.enterTtyMode(int(screen)) + try: + self._brl.enterTtyMode(int(screen)) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.enterScreen ' + str(e), debug.debugLevel.ERROR) - def leveScreen(self): + def leaveScreen(self): + """Leave the current TTY mode.""" if not self._isInitialized: return - self._brl.leaveTtyMode() + try: + self._brl.leaveTtyMode() + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.leaveScreen ' + str(e), debug.debugLevel.ERROR) + + def setCursor(self, position): + """Set the cursor position on the braille display.""" + if not self._isInitialized: + return + try: + max_pos = self.getDeviceSize()[0] - 1 + self._current_cursor_pos = max(0, min(position, max_pos)) + # Use BRLAPI's cursor command which works across different displays + self._brl.writeDots(1 << self._current_cursor_pos) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.setCursor ' + str(e), debug.debugLevel.ERROR) + + def getCursorPosition(self): + """Get the current cursor position.""" + if not self._isInitialized: + return 0 + return self._current_cursor_pos + + def clear(self): + """Clear the braille display.""" + if not self._isInitialized: + return + try: + self._brl.writeText('') + self._last_written_text = "" + self._current_cursor_pos = 0 + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.clear ' + str(e), debug.debugLevel.ERROR) + + def flush(self): + """Force an update of the physical display.""" + if not self._isInitialized: + return + try: + if self._last_written_text: + self._brl.writeText(self._last_written_text) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.flush ' + str(e), debug.debugLevel.ERROR) + + def handleKeyInput(self, key): + """Handle input from any BRLTTY-compatible braille display. + Returns the key event for processing by Fenrir's input system.""" + if not self._isInitialized: + return False + try: + key_code = self._brl.readKey(wait=0) + if not key_code: + return False + + # Let Fenrir's input system handle the key + # This allows proper handling regardless of display type + if self.env['runtime']['debug'].debugLevel >= debug.debugLevel.INFO: + self.env['runtime']['debug'].writeDebugOut( + 'BRAILLE.keyPressed: ' + str(key_code), + debug.debugLevel.INFO + ) + return True + except brlapi.ConnectionError: + self.env['runtime']['debug'].writeDebugOut( + 'BRAILLE: Connection lost. Attempting to reconnect...', + debug.debugLevel.ERROR + ) + self.connectDevice() + return False + except Exception as e: + self.env['runtime']['debug'].writeDebugOut( + 'BRAILLE.handleKeyInput ' + str(e), + debug.debugLevel.ERROR + ) + return False def shutdown(self): + """Shut down the BRLAPI driver.""" if not self._isInitialized: return - self.leveScreen() + try: + self.clear() + self.leaveScreen() + if self._brl: + self._brl.closeConnection() + self._brl = None + self._isInitialized = False + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('BRAILLE.shutdown ' + str(e), debug.debugLevel.ERROR) diff --git a/src/fenrirscreenreader/core/brailleDriver.py b/src/fenrirscreenreader/core/brailleDriver.py index 8ed4c66c..bdd6f3e3 100644 --- a/src/fenrirscreenreader/core/brailleDriver.py +++ b/src/fenrirscreenreader/core/brailleDriver.py @@ -1,82 +1,156 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- + # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. -import brlapi +from abc import ABC, abstractmethod from fenrirscreenreader.core import debug -class brailleDriver(): +class brailleDriver(ABC): + """Base class for Fenrir braille display drivers. + + This abstract base class defines the interface that all braille drivers must implement. + It provides basic initialization state tracking and defines the required methods + for interacting with braille displays. + """ + def __init__(self): + """Initialize the driver with default state.""" self._isInitialized = False - self._brl = None self.deviceSize = None - + self.env = None + self._current_cursor_pos = 0 + self._display_content = "" + + @abstractmethod def initialize(self, environment): - """Initialize the BRLTTY connection.""" + """Initialize the braille driver with the given environment. + + Args: + environment (dict): The Fenrir environment dictionary containing runtime settings + and helper objects. + """ self.env = environment - try: - self._brl = brlapi.Connection() - self._brl.enterTtyMode() - self.deviceSize = self._brl.displaySize - self._isInitialized = True - return True - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('ERROR: Initializing braille failed:' + str(e),debug.debugLevel.ERROR) - return False + self._isInitialized = True + @abstractmethod def getDeviceSize(self): - """Get the size of the braille display.""" + """Get the size of the connected braille display. + + Returns: + tuple: A (columns, rows) tuple indicating the display dimensions. + Returns (0, 0) if no display is connected or not initialized. + """ if not self._isInitialized: return (0, 0) - return self.deviceSize if self.deviceSize else (0, 0) + return (0, 0) + @abstractmethod def writeText(self, text): - """Write text to the braille display.""" + """Write text to the braille display. + + Args: + text (str): The text to be written to the display. + """ if not self._isInitialized: return - try: - self._brl.writeText(text) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('ERROR: Writing braille failed:' + str(e),debug.debugLevel.ERROR) - def readKeypress(self): - """Read a keypress from the braille display.""" - if not self._isInitialized: - return None - try: - return self._brl.readKey(wait=0) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('ERROR: Reading key failed:' + str(e),debug.debugLevel.ERROR) - return None + @abstractmethod + def connectDevice(self): + """Establish connection with the braille display. + + This method should handle the initial connection setup with the display. + It should be called before any other display operations. + """ + pass + @abstractmethod def enterScreen(self, screen): - """Enter a new screen context.""" + """Enter a specific screen/TTY mode. + + Args: + screen (int): The screen/TTY number to enter. + """ if not self._isInitialized: return - try: - self._brl.enterTtyModeWithPath(screen) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('ERROR: Entering screen failed:' + str(e),debug.debugLevel.ERROR) + @abstractmethod def leaveScreen(self): - """Leave the current screen context.""" + """Leave the current screen/TTY mode. + + This method should clean up any screen-specific state and + prepare for potential screen switches. + """ + if not self._isInitialized: + return + + @abstractmethod + def setCursor(self, position): + """Set the cursor position on the braille display. + + Args: + position (int): The position where the cursor should be placed. + """ + if not self._isInitialized: + return + self._current_cursor_pos = max(0, min(position, self.getDeviceSize()[0] - 1)) + + @abstractmethod + def getCursorPosition(self): + """Get the current cursor position on the braille display. + + Returns: + int: The current cursor position. + """ + if not self._isInitialized: + return 0 + return self._current_cursor_pos + + @abstractmethod + def clear(self): + """Clear the braille display.""" + if not self._isInitialized: + return + self._display_content = "" + self._current_cursor_pos = 0 + + @abstractmethod + def flush(self): + """Force an update of the physical display with current content.""" if not self._isInitialized: return - try: - self._brl.leaveTtyMode() - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('ERROR: Leaving screen failed:' + str(e),debug.debugLevel.ERROR) def shutdown(self): - """Shutdown the braille driver.""" + """Shut down the braille driver and clean up resources. + + This method ensures proper cleanup of resources and + disconnection from the braille display. + """ if not self._isInitialized: return - try: - self.leaveScreen() - if self._brl: - self._brl.closeConnection() - self._brl = None - self._isInitialized = False - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('ERROR: Shutting down braille failed:' + str(e),debug.debugLevel.ERROR) + self.clear() + self.leaveScreen() + self._isInitialized = False + + @property + def is_initialized(self): + """Check if the driver is initialized. + + Returns: + bool: True if the driver is initialized, False otherwise. + """ + return self._isInitialized + + @abstractmethod + def handleKeyInput(self, key): + """Handle input from the braille display's keys. + + Args: + key: The key event from the braille display. + Returns: + bool: True if the key was handled, False otherwise. + """ + if not self._isInitialized: + return False + return False From 3757a1ceeb3986dffb1a052c418e019f809809f8 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 8 Dec 2024 05:02:29 -0500 Subject: [PATCH 5/7] Updated brlapi driver. Hopefully will now actually work. --- .../brailleDriver/brlapiDriver.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py b/src/fenrirscreenreader/brailleDriver/brlapiDriver.py index f2df2a50..acab6f76 100644 --- a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py +++ b/src/fenrirscreenreader/brailleDriver/brlapiDriver.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/bin/python # -*- coding: utf-8 -*- # Fenrir TTY screen reader @@ -8,8 +8,7 @@ from fenrirscreenreader.core import debug from fenrirscreenreader.core.brailleDriver import brailleDriver class driver(brailleDriver): - """BRLAPI implementation of the braille driver interface. - Supports all braille displays compatible with BRLTTY.""" + """BRLAPI implementation of the braille driver interface.""" def __init__(self): """Initialize the BRLAPI driver.""" @@ -24,7 +23,6 @@ class driver(brailleDriver): import brlapi self._brl = brlapi.Connection() self._deviceSize = self._brl.displaySize - # Accept all key types from any braille display self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX]) self._current_cursor_pos = 0 except Exception as e: @@ -39,16 +37,31 @@ class driver(brailleDriver): return self._deviceSize def writeText(self, text): - """Write text to the braille display.""" + """Write text to the braille display. + Handles both flush and non-flush text from the output manager.""" if not self._isInitialized: return try: + # Remove the 'flush ' or 'notflush ' prefix + if text.startswith('flush '): + text = text[6:] # Remove 'flush ' prefix + elif text.startswith('notflush '): + text = text[9:] # Remove 'notflush ' prefix + self._last_written_text = text + # Handle unicode properly for international braille if isinstance(text, str): self._brl.writeText(text) else: self._brl.writeText(text.decode('utf-8', 'replace')) + + # Update cursor if needed + if 'screen' in self.env and 'newCursorReview' in self.env['screen']: + new_pos = self.env['screen']['newCursorReview']['x'] + if new_pos != self._current_cursor_pos: + self.setCursor(new_pos) + except Exception as e: self.env['runtime']['debug'].writeDebugOut('BRAILLE.writeText ' + str(e), debug.debugLevel.ERROR) @@ -57,7 +70,6 @@ class driver(brailleDriver): try: import brlapi self._brl = brlapi.Connection() - # Accept all key types from the connected display self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX]) return True except Exception as e: @@ -89,7 +101,6 @@ class driver(brailleDriver): try: max_pos = self.getDeviceSize()[0] - 1 self._current_cursor_pos = max(0, min(position, max_pos)) - # Use BRLAPI's cursor command which works across different displays self._brl.writeDots(1 << self._current_cursor_pos) except Exception as e: self.env['runtime']['debug'].writeDebugOut('BRAILLE.setCursor ' + str(e), debug.debugLevel.ERROR) @@ -122,8 +133,7 @@ class driver(brailleDriver): self.env['runtime']['debug'].writeDebugOut('BRAILLE.flush ' + str(e), debug.debugLevel.ERROR) def handleKeyInput(self, key): - """Handle input from any BRLTTY-compatible braille display. - Returns the key event for processing by Fenrir's input system.""" + """Handle input from any BRLTTY-compatible braille display.""" if not self._isInitialized: return False try: @@ -131,8 +141,6 @@ class driver(brailleDriver): if not key_code: return False - # Let Fenrir's input system handle the key - # This allows proper handling regardless of display type if self.env['runtime']['debug'].debugLevel >= debug.debugLevel.INFO: self.env['runtime']['debug'].writeDebugOut( 'BRAILLE.keyPressed: ' + str(key_code), From baa4c9a937c70f35a568a0ee3751f7a1c56c18c6 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 8 Dec 2024 06:43:22 -0500 Subject: [PATCH 6/7] Removed Braille. BrlTTY does a great job already in the console. Also, Fenrir's implementation was only partially done and not working. --- README.md | 10 +- check-dependencies.py | 11 +- config/settings/settings.conf | 31 --- setup.py | 1 - .../brailleDriver/__init__.py | 0 .../brailleDriver/brlapiDriver.py | 176 ------------- .../brailleDriver/debugDriver.py | 49 ---- .../brailleDriver/dummyDriver.py | 12 - .../commands/commands/braille_flush.py | 21 -- .../commands/commands/braille_pan_left.py | 21 -- .../commands/commands/braille_pan_right.py | 21 -- .../commands/braille_return_to_cursor.py | 21 -- .../commands/commands/toggle_braille.py | 27 -- .../commands/commands/toggle_output.py | 5 +- src/fenrirscreenreader/core/brailleDriver.py | 156 ------------ src/fenrirscreenreader/core/eventData.py | 14 +- src/fenrirscreenreader/core/eventManager.py | 4 - src/fenrirscreenreader/core/outputManager.py | 241 ++++-------------- src/fenrirscreenreader/core/runtimeData.py | 1 - src/fenrirscreenreader/core/screenManager.py | 28 +- src/fenrirscreenreader/core/settingsData.py | 11 - tools/configure_fenrir.py | 7 +- 22 files changed, 58 insertions(+), 810 deletions(-) delete mode 100755 src/fenrirscreenreader/brailleDriver/__init__.py delete mode 100644 src/fenrirscreenreader/brailleDriver/brlapiDriver.py delete mode 100644 src/fenrirscreenreader/brailleDriver/debugDriver.py delete mode 100644 src/fenrirscreenreader/brailleDriver/dummyDriver.py delete mode 100644 src/fenrirscreenreader/commands/commands/braille_flush.py delete mode 100644 src/fenrirscreenreader/commands/commands/braille_pan_left.py delete mode 100644 src/fenrirscreenreader/commands/commands/braille_pan_right.py delete mode 100644 src/fenrirscreenreader/commands/commands/braille_return_to_cursor.py delete mode 100644 src/fenrirscreenreader/commands/commands/toggle_braille.py delete mode 100644 src/fenrirscreenreader/core/brailleDriver.py diff --git a/README.md b/README.md index d4e3942f..e2652000 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This software is licensed under the LGPL v3. # Core Requirements - python3 >= 3.3 -- screen, input, speech, sound or braille drivers dependencies see "Features, Drivers, Extras". +- screen, input, speech, sound drivers dependencies see "Features, Drivers, Extras". # Features, Drivers, Extras, Dependencies @@ -59,13 +59,6 @@ This software is licensed under the LGPL v3. - emacspeak -# Braille Drivers: - -1. "BrlttyDriver" braille driver (WIP): - - brltty (configured and running) - - python-brlapi - - # Sound Drivers: 1. "genericDriver" (default) sound driver for sound as subprocess: @@ -113,7 +106,6 @@ Take care to use drivers from the config matching your installed drivers. By default it uses: - sound driver: genericDriver (via sox, could configured in settings.conf) - speech driver: genericDriver (via espeak or espeak-ng, could configured in settings.conf) -- braille driver: brlttyDriver (WIP) - input driver: evdevDriver diff --git a/check-dependencies.py b/check-dependencies.py index 66ba282c..c92c9903 100755 --- a/check-dependencies.py +++ b/check-dependencies.py @@ -8,7 +8,7 @@ from typing import List, Optional @dataclass class Dependency: name: str - depType: str # screen, braille, input, sound, speech, core + depType: str # screen, input, sound, speech, core moduleName: str checkCommands: Optional[List[str]] = None # Command-line tools to check pythonImports: Optional[List[str]] = None # Python packages to check @@ -60,12 +60,6 @@ dependencyList = [ Dependency('PTY', 'screen', 'ptyDriver', pythonImports=['pyte']), - # Braille drivers - Dependency('DummyBraille', 'braille', 'dummyDriver'), - Dependency('DebugBraille', 'braille', 'debugDriver'), - Dependency('BRLAPI', 'braille', 'brlapiDriver', - pythonImports=['brlapi']), - # Input drivers Dependency('DummyInput', 'input', 'dummyDriver'), Dependency('DebugInput', 'input', 'debugDriver'), @@ -94,7 +88,6 @@ dependencyList = [ defaultModules = { 'FenrirCore', 'VCSA', - 'DummyBraille', 'Evdev', 'GenericSpeech', 'GenericSound' @@ -105,7 +98,7 @@ def check_all_dependencies(): availableModules = [] # Group dependencies by type for organized output - for depType in ['core', 'screen', 'braille', 'input', 'sound', 'speech']: + for depType in ['core', 'screen', 'input', 'sound', 'speech']: print(f'{depType.upper()} DRIVERS') print('-' * 20) diff --git a/config/settings/settings.conf b/config/settings/settings.conf index 642e5086..9bdf97e0 100644 --- a/config/settings/settings.conf +++ b/config/settings/settings.conf @@ -91,37 +91,6 @@ fenrirMaxPitch=99 fenrirMinRate=80 fenrirMaxRate=450 -[braille] -enabled=False -driver=dummyDriver -layout=en -# to what should the flush timeout relate to -# word = flush after (number of words to display) * seconds -# char = flush after (number of chars to display) * seconds -# fix = flush after X seconds -# none = no automatic flush (manual via shortcut) -flushMode=word -# seconds to flush or -# -1 = no automatic flush (manual via shortcut) -flushTimeout=3 -# how should the cursor be focused? -# page = if cursor cross the border move to next page and start at beginn -# fixCell = ajust the cursor on an special cell where it is always placed. the display scroll here more smooth. -cursorFocusMode=page -# define the cell on the Braille device where fenrir should scroll and keep the cursor -# 0 = first cell on device -# -1 = last cell on device -# >0 = fix cell number -fixCursorOnCell=-1 -#How should the braille follow the focus -# none = no automatic toggle command used -# review = priority to review -# last = follow last used cursor -cursorFollowMode=review -# number of cells in panning (horizontal) -# 0 = display size, >0 number of cells -panSizeHorizontal=0 - [screen] driver=vcsaDriver encoding=auto diff --git a/setup.py b/setup.py index d70316f7..133964da 100755 --- a/setup.py +++ b/setup.py @@ -119,5 +119,4 @@ print('once as their user account and once as root to configure Pulseaudio.') print('Please install the following packages manually:') print('- Speech-dispatcher: for the default speech driver') print('- Espeak: as basic TTS engine') -print('- BrlTTY: for Braille') print('- sox: is a player for the generic sound driver') diff --git a/src/fenrirscreenreader/brailleDriver/__init__.py b/src/fenrirscreenreader/brailleDriver/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py b/src/fenrirscreenreader/brailleDriver/brlapiDriver.py deleted file mode 100644 index acab6f76..00000000 --- a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py +++ /dev/null @@ -1,176 +0,0 @@ -#!/bin/python -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributors. - -from fenrirscreenreader.core import debug -from fenrirscreenreader.core.brailleDriver import brailleDriver - -class driver(brailleDriver): - """BRLAPI implementation of the braille driver interface.""" - - def __init__(self): - """Initialize the BRLAPI driver.""" - brailleDriver.__init__(self) - self._brl = None - self._last_written_text = "" - - def initialize(self, environment): - """Initialize the braille driver with BRLAPI connection.""" - self.env = environment - try: - import brlapi - self._brl = brlapi.Connection() - self._deviceSize = self._brl.displaySize - self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX]) - self._current_cursor_pos = 0 - except Exception as e: - self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) - return - self._isInitialized = True - - def getDeviceSize(self): - """Get the size of the connected braille display.""" - if not self._isInitialized or not self._deviceSize: - return (0, 0) - return self._deviceSize - - def writeText(self, text): - """Write text to the braille display. - Handles both flush and non-flush text from the output manager.""" - if not self._isInitialized: - return - try: - # Remove the 'flush ' or 'notflush ' prefix - if text.startswith('flush '): - text = text[6:] # Remove 'flush ' prefix - elif text.startswith('notflush '): - text = text[9:] # Remove 'notflush ' prefix - - self._last_written_text = text - - # Handle unicode properly for international braille - if isinstance(text, str): - self._brl.writeText(text) - else: - self._brl.writeText(text.decode('utf-8', 'replace')) - - # Update cursor if needed - if 'screen' in self.env and 'newCursorReview' in self.env['screen']: - new_pos = self.env['screen']['newCursorReview']['x'] - if new_pos != self._current_cursor_pos: - self.setCursor(new_pos) - - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.writeText ' + str(e), debug.debugLevel.ERROR) - - def connectDevice(self): - """Establish connection with BRLAPI.""" - try: - import brlapi - self._brl = brlapi.Connection() - self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX]) - return True - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.connectDevice ' + str(e), debug.debugLevel.ERROR) - return False - - def enterScreen(self, screen): - """Enter a specific TTY mode.""" - if not self._isInitialized: - return - try: - self._brl.enterTtyMode(int(screen)) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.enterScreen ' + str(e), debug.debugLevel.ERROR) - - def leaveScreen(self): - """Leave the current TTY mode.""" - if not self._isInitialized: - return - try: - self._brl.leaveTtyMode() - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.leaveScreen ' + str(e), debug.debugLevel.ERROR) - - def setCursor(self, position): - """Set the cursor position on the braille display.""" - if not self._isInitialized: - return - try: - max_pos = self.getDeviceSize()[0] - 1 - self._current_cursor_pos = max(0, min(position, max_pos)) - self._brl.writeDots(1 << self._current_cursor_pos) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.setCursor ' + str(e), debug.debugLevel.ERROR) - - def getCursorPosition(self): - """Get the current cursor position.""" - if not self._isInitialized: - return 0 - return self._current_cursor_pos - - def clear(self): - """Clear the braille display.""" - if not self._isInitialized: - return - try: - self._brl.writeText('') - self._last_written_text = "" - self._current_cursor_pos = 0 - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.clear ' + str(e), debug.debugLevel.ERROR) - - def flush(self): - """Force an update of the physical display.""" - if not self._isInitialized: - return - try: - if self._last_written_text: - self._brl.writeText(self._last_written_text) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.flush ' + str(e), debug.debugLevel.ERROR) - - def handleKeyInput(self, key): - """Handle input from any BRLTTY-compatible braille display.""" - if not self._isInitialized: - return False - try: - key_code = self._brl.readKey(wait=0) - if not key_code: - return False - - if self.env['runtime']['debug'].debugLevel >= debug.debugLevel.INFO: - self.env['runtime']['debug'].writeDebugOut( - 'BRAILLE.keyPressed: ' + str(key_code), - debug.debugLevel.INFO - ) - return True - except brlapi.ConnectionError: - self.env['runtime']['debug'].writeDebugOut( - 'BRAILLE: Connection lost. Attempting to reconnect...', - debug.debugLevel.ERROR - ) - self.connectDevice() - return False - except Exception as e: - self.env['runtime']['debug'].writeDebugOut( - 'BRAILLE.handleKeyInput ' + str(e), - debug.debugLevel.ERROR - ) - return False - - def shutdown(self): - """Shut down the BRLAPI driver.""" - if not self._isInitialized: - return - try: - self.clear() - self.leaveScreen() - if self._brl: - self._brl.closeConnection() - self._brl = None - self._isInitialized = False - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('BRAILLE.shutdown ' + str(e), debug.debugLevel.ERROR) diff --git a/src/fenrirscreenreader/brailleDriver/debugDriver.py b/src/fenrirscreenreader/brailleDriver/debugDriver.py deleted file mode 100644 index 15642a69..00000000 --- a/src/fenrirscreenreader/brailleDriver/debugDriver.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -from fenrirscreenreader.core import debug -from fenrirscreenreader.core.brailleDriver import brailleDriver - -class driver(brailleDriver): - def __init__(self): - brailleDriver.__init__(self) - - def initialize(self, environment): - self.env = environment - self._isInitialized = True - self.deviceSize = (40,0) - print('Braille Debug Driver: Initialized') - - def getDeviceSize(self): - if not self._isInitialized: - return (0,0) - print('Braille Debug Driver: getDeviceSize ' + str(self.deviceSize)) - return self.deviceSize - - def writeText(self,text): - if not self._isInitialized: - return - print('Braille Debug Driver: writeText:' + str(text)) - print('Braille Debug Driver: -----------------------------------') - - def connectDevice(self): - print('Braille Debug Driver: connectDevice') - - def enterScreen(self, screen): - if not self._isInitialized: - return - print('Braille Debug Driver: enterScreen') - - def leveScreen(self): - if not self._isInitialized: - return - print('Braille Debug Driver: leveScreen') - - def shutdown(self): - if self._isInitialized: - self.leveScreen() - self._isInitialized = False - print('Braille Debug Driver: Shutdown') diff --git a/src/fenrirscreenreader/brailleDriver/dummyDriver.py b/src/fenrirscreenreader/brailleDriver/dummyDriver.py deleted file mode 100644 index 8cc5a71f..00000000 --- a/src/fenrirscreenreader/brailleDriver/dummyDriver.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -from fenrirscreenreader.core import debug -from fenrirscreenreader.core.brailleDriver import brailleDriver - -class driver(brailleDriver): - def __init__(self): - brailleDriver.__init__(self) diff --git a/src/fenrirscreenreader/commands/commands/braille_flush.py b/src/fenrirscreenreader/commands/commands/braille_flush.py deleted file mode 100644 index 12cecde4..00000000 --- a/src/fenrirscreenreader/commands/commands/braille_flush.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -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 _('Clear the Braille device if it is displaying a message') - def run(self): - self.env['runtime']['outputManager'].clearFlushTime() - def setCallback(self, callback): - pass diff --git a/src/fenrirscreenreader/commands/commands/braille_pan_left.py b/src/fenrirscreenreader/commands/commands/braille_pan_left.py deleted file mode 100644 index 1f535392..00000000 --- a/src/fenrirscreenreader/commands/commands/braille_pan_left.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -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 _('Move braille view to the left.') - def run(self): - panned = self.env['runtime']['outputManager'].setPanLeft() - def setCallback(self, callback): - pass diff --git a/src/fenrirscreenreader/commands/commands/braille_pan_right.py b/src/fenrirscreenreader/commands/commands/braille_pan_right.py deleted file mode 100644 index 344386bd..00000000 --- a/src/fenrirscreenreader/commands/commands/braille_pan_right.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -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 _('Move braille view to the right.') - def run(self): - panned = self.env['runtime']['outputManager'].setPanRight() - def setCallback(self, callback): - pass diff --git a/src/fenrirscreenreader/commands/commands/braille_return_to_cursor.py b/src/fenrirscreenreader/commands/commands/braille_return_to_cursor.py deleted file mode 100644 index 04a0def0..00000000 --- a/src/fenrirscreenreader/commands/commands/braille_return_to_cursor.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -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 _('Set the braille view back to cursor.') - def run(self): - self.env['runtime']['outputManager'].removePanning() - def setCallback(self, callback): - pass diff --git a/src/fenrirscreenreader/commands/commands/toggle_braille.py b/src/fenrirscreenreader/commands/commands/toggle_braille.py deleted file mode 100644 index 738020ae..00000000 --- a/src/fenrirscreenreader/commands/commands/toggle_braille.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. - -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 _('Enables and disables Braille output') - - def run(self): - if self.env['runtime']['settingsManager'].getSettingAsBool('braille', 'enabled'): - self.env['runtime']['outputManager'].presentText(_('braille disabled'), soundIcon='BrailleOff', interrupt=True) - self.env['runtime']['settingsManager'].setSetting('braille', 'enabled', str(not self.env['runtime']['settingsManager'].getSettingAsBool('braille', 'enabled'))) - if self.env['runtime']['settingsManager'].getSettingAsBool('braille', 'enabled'): - self.env['runtime']['outputManager'].presentText(_('braille enabled'), soundIcon='BrailleOn', interrupt=True) - - def setCallback(self, callback): - pass diff --git a/src/fenrirscreenreader/commands/commands/toggle_output.py b/src/fenrirscreenreader/commands/commands/toggle_output.py index b5f31cc2..7ed8f5e6 100644 --- a/src/fenrirscreenreader/commands/commands/toggle_output.py +++ b/src/fenrirscreenreader/commands/commands/toggle_output.py @@ -18,16 +18,13 @@ class command(): def run(self): if self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'enabled') or \ - self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'enabled') or \ - self.env['runtime']['settingsManager'].getSettingAsBool('braille', 'enabled'): + self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'enabled'): self.env['runtime']['outputManager'].presentText(_('Fenrir muted'), soundIcon='Accept', interrupt=True) self.env['runtime']['settingsManager'].setSetting('speech', 'enabled','False') self.env['runtime']['settingsManager'].setSetting('sound', 'enabled','False') - self.env['runtime']['settingsManager'].setSetting('braille', 'enabled','False') else: self.env['runtime']['settingsManager'].setSetting('speech', 'enabled','True') self.env['runtime']['settingsManager'].setSetting('sound', 'enabled','True') - self.env['runtime']['settingsManager'].setSetting('braille', 'enabled','True') self.env['runtime']['outputManager'].presentText(_('Fenrir unmuted'), soundIcon='Cancel', interrupt=True) def setCallback(self, callback): diff --git a/src/fenrirscreenreader/core/brailleDriver.py b/src/fenrirscreenreader/core/brailleDriver.py deleted file mode 100644 index bdd6f3e3..00000000 --- a/src/fenrirscreenreader/core/brailleDriver.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributors. - -from abc import ABC, abstractmethod -from fenrirscreenreader.core import debug - -class brailleDriver(ABC): - """Base class for Fenrir braille display drivers. - - This abstract base class defines the interface that all braille drivers must implement. - It provides basic initialization state tracking and defines the required methods - for interacting with braille displays. - """ - - def __init__(self): - """Initialize the driver with default state.""" - self._isInitialized = False - self.deviceSize = None - self.env = None - self._current_cursor_pos = 0 - self._display_content = "" - - @abstractmethod - def initialize(self, environment): - """Initialize the braille driver with the given environment. - - Args: - environment (dict): The Fenrir environment dictionary containing runtime settings - and helper objects. - """ - self.env = environment - self._isInitialized = True - - @abstractmethod - def getDeviceSize(self): - """Get the size of the connected braille display. - - Returns: - tuple: A (columns, rows) tuple indicating the display dimensions. - Returns (0, 0) if no display is connected or not initialized. - """ - if not self._isInitialized: - return (0, 0) - return (0, 0) - - @abstractmethod - def writeText(self, text): - """Write text to the braille display. - - Args: - text (str): The text to be written to the display. - """ - if not self._isInitialized: - return - - @abstractmethod - def connectDevice(self): - """Establish connection with the braille display. - - This method should handle the initial connection setup with the display. - It should be called before any other display operations. - """ - pass - - @abstractmethod - def enterScreen(self, screen): - """Enter a specific screen/TTY mode. - - Args: - screen (int): The screen/TTY number to enter. - """ - if not self._isInitialized: - return - - @abstractmethod - def leaveScreen(self): - """Leave the current screen/TTY mode. - - This method should clean up any screen-specific state and - prepare for potential screen switches. - """ - if not self._isInitialized: - return - - @abstractmethod - def setCursor(self, position): - """Set the cursor position on the braille display. - - Args: - position (int): The position where the cursor should be placed. - """ - if not self._isInitialized: - return - self._current_cursor_pos = max(0, min(position, self.getDeviceSize()[0] - 1)) - - @abstractmethod - def getCursorPosition(self): - """Get the current cursor position on the braille display. - - Returns: - int: The current cursor position. - """ - if not self._isInitialized: - return 0 - return self._current_cursor_pos - - @abstractmethod - def clear(self): - """Clear the braille display.""" - if not self._isInitialized: - return - self._display_content = "" - self._current_cursor_pos = 0 - - @abstractmethod - def flush(self): - """Force an update of the physical display with current content.""" - if not self._isInitialized: - return - - def shutdown(self): - """Shut down the braille driver and clean up resources. - - This method ensures proper cleanup of resources and - disconnection from the braille display. - """ - if not self._isInitialized: - return - self.clear() - self.leaveScreen() - self._isInitialized = False - - @property - def is_initialized(self): - """Check if the driver is initialized. - - Returns: - bool: True if the driver is initialized, False otherwise. - """ - return self._isInitialized - - @abstractmethod - def handleKeyInput(self, key): - """Handle input from the braille display's keys. - - Args: - key: The key event from the braille display. - Returns: - bool: True if the key was handled, False otherwise. - """ - if not self._isInitialized: - return False - return False diff --git a/src/fenrirscreenreader/core/eventData.py b/src/fenrirscreenreader/core/eventData.py index 0f50e0ba..0dafeeca 100644 --- a/src/fenrirscreenreader/core/eventData.py +++ b/src/fenrirscreenreader/core/eventData.py @@ -12,14 +12,12 @@ class fenrirEventType(Enum): StopMainLoop = 1 ScreenUpdate = 2 KeyboardInput = 3 - BrailleInput = 4 - PlugInputDevice = 5 - BrailleFlush = 6 - ScreenChanged = 7 - HeartBeat = 8 # for time based scheduling - ExecuteCommand = 9 - ByteInput = 10 - RemoteIncomming = 11 + PlugInputDevice = 4 + ScreenChanged = 5 + HeartBeat = 6 + ExecuteCommand = 7 + ByteInput = 8 + RemoteIncomming = 9 def __int__(self): return self.value def __str__(self): diff --git a/src/fenrirscreenreader/core/eventManager.py b/src/fenrirscreenreader/core/eventManager.py index 96bff615..1de24235 100644 --- a/src/fenrirscreenreader/core/eventManager.py +++ b/src/fenrirscreenreader/core/eventManager.py @@ -42,12 +42,8 @@ class eventManager(): self.env['runtime']['fenrirManager'].handleScreenUpdate(event) elif event['Type'] == fenrirEventType.KeyboardInput: self.env['runtime']['fenrirManager'].handleInput(event) - elif event['Type'] == fenrirEventType.BrailleInput: - pass elif event['Type'] == fenrirEventType.PlugInputDevice: self.env['runtime']['fenrirManager'].handlePlugInputDevice(event) - elif event['Type'] == fenrirEventType.BrailleFlush: - pass elif event['Type'] == fenrirEventType.ScreenChanged: self.env['runtime']['fenrirManager'].handleScreenChange(event) elif event['Type'] == fenrirEventType.HeartBeat: diff --git a/src/fenrirscreenreader/core/outputManager.py b/src/fenrirscreenreader/core/outputManager.py index 6f8a1960..1314e14d 100644 --- a/src/fenrirscreenreader/core/outputManager.py +++ b/src/fenrirscreenreader/core/outputManager.py @@ -1,8 +1,8 @@ -#!/usr/bin/env python3 +#!/bin/python # -*- coding: utf-8 -*- # Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributers. +# By Chrys, Storm Dragon, and contributors. from fenrirscreenreader.core import debug from fenrirscreenreader.utils import line_utils @@ -11,27 +11,26 @@ import string, time, re class outputManager(): def __init__(self): self.lastEcho = '' + def initialize(self, environment): self.env = environment - self.env['runtime']['settingsManager'].loadDriver(\ - self.env['runtime']['settingsManager'].getSetting('speech', 'driver'), 'speechDriver') - self.env['runtime']['settingsManager'].loadDriver(\ - self.env['runtime']['settingsManager'].getSetting('sound', 'driver'), 'soundDriver') - self.env['runtime']['settingsManager'].loadDriver(\ - self.env['runtime']['settingsManager'].getSetting('braille', 'driver'), 'brailleDriver') + self.env['runtime']['settingsManager'].loadDriver( + self.env['runtime']['settingsManager'].getSetting('speech', 'driver'), 'speechDriver') + self.env['runtime']['settingsManager'].loadDriver( + self.env['runtime']['settingsManager'].getSetting('sound', 'driver'), 'soundDriver') + def shutdown(self): self.env['runtime']['settingsManager'].shutdownDriver('soundDriver') self.env['runtime']['settingsManager'].shutdownDriver('speechDriver') - self.env['runtime']['settingsManager'].shutdownDriver('brailleDriver') - def presentText(self, text, interrupt=True, soundIcon = '', ignorePunctuation=False, announceCapital=False, flush=True, brailleAlternative = ''): + def presentText(self, text, interrupt=True, soundIcon='', ignorePunctuation=False, announceCapital=False, flush=True): if text == '': return if self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'readNumbersAsDigits') and len(text.strip()) > 1: text = re.sub(r"(\d)", r"\1 ", text).rstrip() - self.env['runtime']['debug'].writeDebugOut("presentText:\nsoundIcon:'"+soundIcon+"'\nText:\n" + text ,debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("presentText:\nsoundIcon:'"+soundIcon+"'\nText:\n" + text, debug.debugLevel.INFO) if self.playSoundIcon(soundIcon, interrupt): - self.env['runtime']['debug'].writeDebugOut("soundIcon found" ,debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("soundIcon found", debug.debugLevel.INFO) return if (len(text) > 1) and (text.strip(string.whitespace) == ''): return @@ -40,35 +39,31 @@ class outputManager(): if self.playSoundIcon('capital', False): toAnnounceCapital = False self.lastEcho = text - self.speakText(text, interrupt, ignorePunctuation,toAnnounceCapital) - if flush: - if brailleAlternative != '': - brlText = brailleAlternative - else: - brlText = text - self.brailleText(brlText, flush) + self.speakText(text, interrupt, ignorePunctuation, toAnnounceCapital) + def getLastEcho(self): return self.lastEcho + def speakText(self, text, interrupt=True, ignorePunctuation=False, announceCapital=False): if not self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'enabled'): - self.env['runtime']['debug'].writeDebugOut("Speech disabled in outputManager.speakText",debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("Speech disabled in outputManager.speakText", debug.debugLevel.INFO) return if self.env['runtime']['speechDriver'] == None: - self.env['runtime']['debug'].writeDebugOut("No speechDriver in outputManager.speakText",debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("No speechDriver in outputManager.speakText", debug.debugLevel.ERROR) return if interrupt: self.interruptOutput() try: self.env['runtime']['speechDriver'].setLanguage(self.env['runtime']['settingsManager'].getSetting('speech', 'language')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("setting speech language in outputManager.speakText",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("setting speech language in outputManager.speakText", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) try: self.env['runtime']['speechDriver'].setVoice(self.env['runtime']['settingsManager'].getSetting('speech', 'voice')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("Error while setting speech voice in outputManager.speakText",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("Error while setting speech voice in outputManager.speakText", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) try: if announceCapital: @@ -76,199 +71,55 @@ class outputManager(): else: self.env['runtime']['speechDriver'].setPitch(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'pitch')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("setting speech pitch in outputManager.speakText",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("setting speech pitch in outputManager.speakText", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) try: self.env['runtime']['speechDriver'].setRate(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'rate')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("setting speech rate in outputManager.speakText",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("setting speech rate in outputManager.speakText", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) try: self.env['runtime']['speechDriver'].setModule(self.env['runtime']['settingsManager'].getSetting('speech', 'module')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("setting speech module in outputManager.speakText",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("setting speech module in outputManager.speakText", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) try: self.env['runtime']['speechDriver'].setVolume(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'volume')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("setting speech volume in outputManager.speakText ",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("setting speech volume in outputManager.speakText ", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) try: if self.env['runtime']['settingsManager'].getSettingAsBool('general', 'newLinePause'): - cleanText = text.replace('\n',' , ') + cleanText = text.replace('\n', ' , ') else: - cleanText = text.replace('\n',' ') + cleanText = text.replace('\n', ' ') cleanText = self.env['runtime']['textManager'].replaceHeadLines(cleanText) cleanText = self.env['runtime']['punctuationManager'].proceedPunctuation(cleanText, ignorePunctuation) - cleanText = re.sub(' +$',' ', cleanText) + cleanText = re.sub(' +$', ' ', cleanText) self.env['runtime']['speechDriver'].speak(cleanText) - self.env['runtime']['debug'].writeDebugOut("Speak: "+ cleanText,debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("Speak: "+ cleanText, debug.debugLevel.INFO) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("\"speak\" in outputManager.speakText ",debug.debugLevel.ERROR) - self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("\"speak\" in outputManager.speakText ", debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR) - def brailleText(self, text='', flush=True): - if not self.env['runtime']['settingsManager'].getSettingAsBool('braille', 'enabled'): - return - if self.env['runtime']['brailleDriver'] == None: - return - if flush: - self.env['output']['nextFlush'] = time.time() + self.getFlushTime(text) - self.env['output']['messageOffset'] = {'x':0,'y':0} - self.env['output']['messageText'] = text - displayText = self.getBrailleTextWithOffset(self.env['output']['messageText'], self.env['output']['messageOffset']) - self.env['runtime']['brailleDriver'].writeText('flush '+ displayText) - else: - if self.env['output']['nextFlush'] < time.time(): - if self.env['output']['messageText'] != '': - self.env['output']['messageText'] = '' - if self.env['output']['messageOffset'] != None: - self.env['output']['messageOffset'] = None - cursor = self.getBrailleCursor() - x, y, self.env['output']['brlText'] = \ - line_utils.getCurrentLine(cursor['x'], cursor['y'], self.env['screen']['newContentText']) - displayText = self.getBrailleTextWithOffset(self.env['screen']['newContentText'], self.env['output']['cursorOffset'], cursor) - self.env['runtime']['brailleDriver'].writeText('notflush ' + displayText) - else: - displayText = self.getBrailleTextWithOffset(self.env['output']['messageText'], self.env['output']['messageOffset']) - self.env['runtime']['brailleDriver'].writeText('flush'+displayText) - def resetSpeechDriver(self): - try: - self.env['runtime']['speechDriver'].reset() - except Exception as e: - self.env['runtime']['debug'].writeDebugOut("reset " + str(e),debug.debugLevel.ERROR) - - def getBrailleCursor(self): - if self.env['runtime']['settingsManager'].getSetting('braille', 'cursorFollowMode').upper() == 'REVIEW': - return self.env['runtime']['cursorManager'].getReviewOrTextCursor() - if self.env['runtime']['settingsManager'].getSetting('braille', 'cursorFollowMode').upper() == 'MANUAL': - return self.env['runtime']['cursorManager'].getReviewOrTextCursor() - if self.env['runtime']['settingsManager'].getSetting('braille', 'cursorFollowMode').upper() == 'LAST': - return self.env['runtime']['cursorManager'].getReviewOrTextCursor() - return self.env['runtime']['cursorManager'].getReviewOrTextCursor() - - def getFixCursorCell(self): - size = self.env['runtime']['brailleDriver'].getDeviceSize()[0] - fixCell = self.env['runtime']['settingsManager'].getSettingAsInt('braille', 'fixCursorOnCell') - if fixCell <= -1: - return size[0] - if fixCell >= size[0]: - return size[0] - return fixCell - def getActiveOffsetAndText(self): - if self.env['output']['messageOffset']: - return self.env['output']['messageOffset'], self.env['output']['messageText'] - if not self.env['output']['cursorOffset']: - return self.getBrailleCursor(), self.env['screen']['newContentText'] - return self.env['output']['cursorOffset'], self.env['screen']['newContentText'] - def getHorizontalPanSize(self): - size = self.env['runtime']['brailleDriver'].getDeviceSize() - if self.env['runtime']['settingsManager'].getSettingAsInt('braille', 'panSizeHorizontal') <= 0: - return size[0] - if self.env['runtime']['settingsManager'].getSettingAsInt('braille', 'panSizeHorizontal') >= size[0]: - return size[0] - return self.env['runtime']['settingsManager'].getSettingAsInt('braille', 'panSizeHorizontal') - def getHorizontalPanLevel(self,offsetChange = 0): - panned = True - panSize = self.getHorizontalPanSize() - offset, text = self.getActiveOffsetAndText() - currline = text.split('\n')[offset['y']] - newOffsetStart = (int(offset['x'] / panSize) + offsetChange) * panSize - if newOffsetStart < 0: - newOffsetStart = 0 - panned = False - if newOffsetStart >= len(text): - newOffsetStart = int((len(text) - panSize - 1) / panSize) - panned = False - return newOffsetStart, panned - def setPanLeft(self): - newPan, panned = self.getHorizontalPanLevel(-1) - if self.env['output']['messageOffset']: - self.env['output']['messageOffset'] = newPan.copy() - else: - self.env['output']['cursorOffset'] = newPan.copy() - return panned - def setPanRight(self): - newPan, panned = self.getHorizontalPanLevel(1) - if self.env['output']['messageOffset']: - self.env['output']['messageOffset'] = newPan.copy() - else: - self.env['output']['cursorOffset'] = newPan.copy() - return panned - def removePanning(self): - if self.env['output']['messageOffset']: - self.env['output']['messageOffset'] = None - else: - self.env['output']['cursorOffset'] = None - def getBrailleTextWithOffset(self, text, offset = None, cursor = None): - if text == '': - return '' - size = self.env['runtime']['brailleDriver'].getDeviceSize() - offsetText = text - - if cursor and not offset: - if self.env['runtime']['settingsManager'].getSetting('braille', 'cursorFollowMode').upper() == 'FIXCELL': - #fix cell - cursorCell = self.getFixCursorCell() - offsetStart = cursor['x'] - if offsetStart < size[0]: - if offsetStart <= cursorCell: - return offsetText[0: size[0]] - - offsetStart -= cursorCell - if offsetStart >= len(offsetText): - offsetStart = len(offsetText) - 1 - else: - # page and fallback - offsetStart = int(cursor['x'] / size[0]) * size[0] - else: - if not offset: - offset = {'x':0,'y':0} - offsetStart = offset['x'] - if offsetStart >= len(offsetText): - offsetStart = len(offsetText) - size[0] - - if offsetStart < 0: - offsetStart = 0 - offsetEnd = offsetStart + size[0] - offsetText = offsetText[offsetStart: offsetEnd] - return offsetText def interruptOutput(self): try: self.env['runtime']['speechDriver'].cancel() - self.env['runtime']['debug'].writeDebugOut("Interrupt speech",debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("Interrupt speech", debug.debugLevel.INFO) except: pass - def clearFlushTime(self): - self.setFlushTime(0.0) - - def setFlushTime(self,newTime): - self.env['output']['nextFlush'] = newTime - - def getFlushTime(self,text=''): - if self.env['runtime']['settingsManager'].getSettingAsFloat('braille', 'flushTimeout') < 0 or \ - self.env['runtime']['settingsManager'].getSetting('braille', 'flushMode').upper() == 'NONE': - return 999999999999 - if self.env['runtime']['settingsManager'].getSetting('braille', 'flushMode').upper() == 'FIX': - return self.env['runtime']['settingsManager'].getSettingAsFloat('braille', 'flushTimeout') - if self.env['runtime']['settingsManager'].getSetting('braille', 'flushMode').upper() == 'CHAR': - return self.env['runtime']['settingsManager'].getSettingAsFloat('braille', 'flushTimeout') * len(text) - if self.env['runtime']['settingsManager'].getSetting('braille', 'flushMode').upper() == 'WORD': - wordsList = text.split(' ') - return self.env['runtime']['settingsManager'].getSettingAsFloat('braille', 'flushTimeout') * len( list( filter(None, wordsList) ) ) - - def playSoundIcon(self, soundIcon = '', interrupt=True): + def playSoundIcon(self, soundIcon='', interrupt=True): if soundIcon == '': return False soundIcon = soundIcon.upper() if not self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'enabled'): - self.env['runtime']['debug'].writeDebugOut("Sound disabled in outputManager.playSoundIcon",debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("Sound disabled in outputManager.playSoundIcon", debug.debugLevel.INFO) return False try: @@ -278,40 +129,40 @@ class outputManager(): return False if self.env['runtime']['soundDriver'] == None: - self.env['runtime']['debug'].writeDebugOut("No soundDriver in outputManager.playSoundIcon: soundDriver not loaded",debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("No soundDriver in outputManager.playSoundIcon: soundDriver not loaded", debug.debugLevel.ERROR) return False try: self.env['runtime']['soundDriver'].setVolume(self.env['runtime']['settingsManager'].getSettingAsFloat('sound', 'volume')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::setVolume: " + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::setVolume: " + str(e), debug.debugLevel.ERROR) try: self.env['runtime']['soundDriver'].playSoundFile(self.env['soundIcons'][soundIcon], interrupt) return True except Exception as e: - self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::playSoundFile: " + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::playSoundFile: " + str(e), debug.debugLevel.ERROR) return False return False def playFrequence(self, frequence, duration, interrupt=True): if not self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'enabled'): - self.env['runtime']['debug'].writeDebugOut("Sound disabled in outputManager.playFrequence",debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("Sound disabled in outputManager.playFrequence", debug.debugLevel.INFO) return False if frequence < 1 or frequence > 20000: - self.env['runtime']['debug'].writeDebugOut("outputManager.playFrequence::Filefrequence is out of range:" + str(frequence),debug.debugLevel.INFO) + self.env['runtime']['debug'].writeDebugOut("outputManager.playFrequence::Filefrequence is out of range:" + str(frequence), debug.debugLevel.INFO) return False if self.env['runtime']['soundDriver'] == None: - self.env['runtime']['debug'].writeDebugOut("No soundDriver in outputManager.playFrequence: soundDriver not loaded",debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("No soundDriver in outputManager.playFrequence: soundDriver not loaded", debug.debugLevel.ERROR) return False try: self.env['runtime']['soundDriver'].setVolume(self.env['runtime']['settingsManager'].getSettingAsFloat('sound', 'volume')) except Exception as e: - self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::setVolume: " + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::setVolume: " + str(e), debug.debugLevel.ERROR) adjustVolume = 0.0 try: adjustVolume = 1.0 - (frequence / 20000) @@ -324,7 +175,7 @@ class outputManager(): self.env['runtime']['soundDriver'].playFrequence(frequence, duration, adjustVolume, interrupt) return True except Exception as e: - self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::playSoundFile: " + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::playSoundFile: " + str(e), debug.debugLevel.ERROR) return False return False @@ -335,9 +186,9 @@ class outputManager(): self.env['commandBuffer']['enableSpeechOnKeypress'] = True self.env['runtime']['settingsManager'].setSetting('speech', 'enabled', str(not self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'enabled'))) self.interruptOutput() + def announceActiveCursor(self, interrupt_p=False): if self.env['runtime']['cursorManager'].isReviewMode(): self.presentText(' review cursor ', interrupt=interrupt_p) else: self.presentText(' text cursor ', interrupt=interrupt_p) - diff --git a/src/fenrirscreenreader/core/runtimeData.py b/src/fenrirscreenreader/core/runtimeData.py index bc34a90c..c2677f1d 100644 --- a/src/fenrirscreenreader/core/runtimeData.py +++ b/src/fenrirscreenreader/core/runtimeData.py @@ -12,7 +12,6 @@ runtimeData = { 'soundDriver': None, 'inputDriver': None, 'remoteDriver': None, -'brailleDriver': None, 'inputManager': None, 'commandManager': None, 'screenManager': None, diff --git a/src/fenrirscreenreader/core/screenManager.py b/src/fenrirscreenreader/core/screenManager.py index 8f448eeb..13203162 100644 --- a/src/fenrirscreenreader/core/screenManager.py +++ b/src/fenrirscreenreader/core/screenManager.py @@ -59,8 +59,6 @@ class screenManager(): if self.isCurrScreenIgnoredChanged(): self.env['runtime']['inputManager'].setExecuteDeviceGrab() self.env['runtime']['inputManager'].handleDeviceGrab() - if self.isScreenChange(): - self.changeBrailleScreen() if not self.isSuspendingScreen(self.env['screen']['newTTY']): self.update(eventData, 'onScreenChange') self.env['screen']['lastScreenUpdate'] = time.time() @@ -74,9 +72,6 @@ class screenManager(): self.env['runtime']['inputManager'].handleDeviceGrab() if not self.getCurrScreenIgnored(): self.update(eventData, 'onScreenUpdate') - #if trigger == 'onUpdate' or self.isScreenChange() \ - # or len(self.env['screen']['newDelta']) > 6: - # self.env['runtime']['screenDriver'].getCurrApplication() self.env['screen']['lastScreenUpdate'] = time.time() elif self.isCurrScreenIgnoredChanged(): self.env['runtime']['outputManager'].interruptOutput() @@ -145,14 +140,10 @@ class screenManager(): self.env['screen']['newContentText'][cursorLineEnd:] == self.env['screen']['oldContentText'][cursorLineEnd:]: cursorLineStartOffset = cursorLineStart cursorLineEndOffset = cursorLineEnd - #if cursorLineStart < cursorLineStart + self.env['screen']['newCursor']['x'] - 4: - # cursorLineStartOffset = cursorLineStart + self.env['screen']['newCursor']['x'] - 4 if cursorLineEnd > cursorLineStart + self.env['screen']['newCursor']['x'] + 3: cursorLineEndOffset = cursorLineStart + self.env['screen']['newCursor']['x'] + 3 oldScreenText = self.env['screen']['oldContentText'][cursorLineStartOffset:cursorLineEndOffset] - # oldScreenText = re.sub(' +',' ',oldScreenText) newScreenText = self.env['screen']['newContentText'][cursorLineStartOffset:cursorLineEndOffset] - #newScreenText = re.sub(' +',' ',newScreenText) diff = self.differ.compare(oldScreenText, newScreenText) diffList = list(diff) typing = True @@ -221,21 +212,4 @@ class screenManager(): try: self.env['runtime']['screenDriver'].injectTextToScreen(text, screen) except Exception as e: - self.env['runtime']['debug'].writeDebugOut('screenManager:injectTextToScreen ' + str(e),debug.debugLevel.ERROR) - - def changeBrailleScreen(self): - if not self.env['runtime']['settingsManager'].getSettingAsBool('braille', 'enabled'): - return - if not self.env['runtime']['brailleDriver']: - return - if self.env['screen']['oldTTY']: - if not self.isSuspendingScreen(self.env['screen']['oldTTY']): - try: - self.env['runtime']['brailleDriver'].leveScreen() - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('screenManager:changeBrailleScreen:leveScreen ' + str(e),debug.debugLevel.ERROR) - if not self.isSuspendingScreen(): - try: - self.env['runtime']['brailleDriver'].enterScreen(self.env['screen']['newTTY']) - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('screenManager:changeBrailleScreen:enterScreen ' + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut('screenManager:injectTextToScreen ' + str(e),debug.debugLevel.ERROR) diff --git a/src/fenrirscreenreader/core/settingsData.py b/src/fenrirscreenreader/core/settingsData.py index f3b5c4ff..15f9c702 100644 --- a/src/fenrirscreenreader/core/settingsData.py +++ b/src/fenrirscreenreader/core/settingsData.py @@ -36,17 +36,6 @@ settingsData = { 'fenrirMinRate':80, 'fenrirMaxRate':450, }, -'braille':{ - 'enabled': False, - 'driver':'brlapiDriver', - 'layout': 'en', - 'flushMode': 'word', #NONE,FIX,CHAR,WORD - 'flushTimeout': 3, - 'cursorFocusMode':'page', # page,fixCell - 'fixCursorOnCell': -1, - 'cursorFollowMode': 'review', # none, review, last, text - 'panSizeHorizontal': 0 # 0 = display size -}, 'screen':{ 'driver': 'vcsaDriver', 'encoding': 'auto', diff --git a/tools/configure_fenrir.py b/tools/configure_fenrir.py index 3141a7a9..fdebd3cd 100755 --- a/tools/configure_fenrir.py +++ b/tools/configure_fenrir.py @@ -31,13 +31,9 @@ class FenrirConfigTool: self.presetOptions = { 'sound.driver': ['genericDriver', 'gstreamerDriver'], 'speech.driver': ['speechdDriver', 'genericDriver'], - 'braille.driver': ['dummyDriver', 'brailttyDriver', 'brlapiDriver'], 'screen.driver': ['vcsaDriver', 'dummyDriver', 'ptyDriver', 'debugDriver'], 'keyboard.driver': ['evdevDriver', 'dummyDriver'], 'remote.driver': ['unixDriver', 'tcpDriver'], - 'braille.flushMode': ['word', 'char', 'fix', 'none'], - 'braille.cursorFocusMode': ['page', 'fixCell'], - 'braille.cursorFollowMode': ['review', 'last', 'none'], 'keyboard.charEchoMode': ['0', '1', '2'], 'general.punctuationLevel': ['none', 'some', 'most', 'all'], 'general.debugMode': ['File', 'Print'] @@ -47,8 +43,7 @@ class FenrirConfigTool: 'sound.volume': 'Volume level from 0 (quietest) to 1.0 (loudest)', 'speech.rate': 'Speech rate from 0 (slowest) to 1.0 (fastest)', 'speech.pitch': 'Voice pitch from 0 (lowest) to 1.0 (highest)', - 'keyboard.charEchoMode': '0 = None, 1 = always, 2 = only while capslock', - 'braille.flushMode': 'word = flush after words, char = flush after chars, fix = flush after time, none = manual flush' + 'keyboard.charEchoMode': '0 = None, 1 = always, 2 = only while capslock' } def check_root(self) -> bool: From 5a59ef6325c2c1fea39a05686ee80af8e9819c54 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 9 Dec 2024 12:58:14 -0500 Subject: [PATCH 7/7] Updated the man page creation script. --- docs/create_manpage.sh | 2 +- src/fenrirscreenreader/fenrirVersion.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/create_manpage.sh b/docs/create_manpage.sh index 30bb538f..cf775a3a 100755 --- a/docs/create_manpage.sh +++ b/docs/create_manpage.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # needs pandoc and php installed # remove old files diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index 64b77140..0c56ba87 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -version = "2024.12.08" +version = "2024.12.09" codeName = "testing"