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/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/setup.py b/setup.py index eede63f0..133964da 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, @@ -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/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/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/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 4b91b01b..00000000 --- a/src/fenrirscreenreader/brailleDriver/brlapiDriver.py +++ /dev/null @@ -1,66 +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) - self._brl = None - - def initialize(self, environment): - self.env = environment - try: - import brlapi - self._brl = brlapi.Connection() - self._deviceSize = self._brl.displaySize - except Exception as e: - print(e) - 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) - return self._deviceSize - - def flush(self): - if not self._isInitialized: - return - try: - self._brl.writeText('',0) - 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) - - def connectDevice(self): - self._brl = brlapi.Connection() - - def enterScreen(self, screen): - if not self._isInitialized: - return - self._brl.enterTtyMode(int(screen)) - - def leveScreen(self): - if not self._isInitialized: - return - self._brl.leaveTtyMode() - - def shutdown(self): - if not self._isInitialized: - return - self.leveScreen() 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 8ed4c66c..00000000 --- a/src/fenrirscreenreader/core/brailleDriver.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Fenrir TTY screen reader -# By Chrys, Storm Dragon, and contributors. - -import brlapi -from fenrirscreenreader.core import debug - -class brailleDriver(): - def __init__(self): - self._isInitialized = False - self._brl = None - self.deviceSize = None - - def initialize(self, environment): - """Initialize the BRLTTY connection.""" - 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 - - def getDeviceSize(self): - """Get the size of the braille display.""" - if not self._isInitialized: - return (0, 0) - return self.deviceSize if self.deviceSize else (0, 0) - - def writeText(self, text): - """Write text to the braille 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 - - def enterScreen(self, screen): - """Enter a new screen context.""" - 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) - - def leaveScreen(self): - """Leave the current screen context.""" - 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.""" - 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) 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/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 - 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: