#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. 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 class fenrirManager(): 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.isInitialized = True self.modifierInput = False self.singleKeyCommand = False self.command = '' self.setProcessName() def proceed(self): if not self.isInitialized: return self.environment['runtime']['eventManager'].startMainEventLoop() self.shutdown() def handleInput(self, event): 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']) else: return if self.environment['runtime']['inputManager'].noKeyPressed(): self.environment['runtime']['inputManager'].clearLastDeepInput() if self.environment['runtime']['screenManager'].isSuspendingScreen(): self.environment['runtime']['inputManager'].writeEventBuffer() else: if self.environment['runtime']['helpManager'].isTutorialMode(): self.environment['runtime']['inputManager'].clearEventBuffer() self.environment['runtime']['inputManager'].keyEcho(event['Data']) if self.environment['runtime']['vmenuManager'].getActive(): self.environment['runtime']['inputManager'].clearEventBuffer() self.detectShortcutCommand() if self.modifierInput: self.environment['runtime']['inputManager'].clearEventBuffer() if self.singleKeyCommand: if self.environment['runtime']['inputManager'].noKeyPressed(): self.environment['runtime']['inputManager'].clearEventBuffer() else: self.environment['runtime']['inputManager'].writeEventBuffer() if self.environment['runtime']['inputManager'].noKeyPressed(): self.modifierInput = False self.singleKeyCommand = False self.environment['runtime']['inputManager'].writeEventBuffer() self.environment['runtime']['inputManager'].handleDeviceGrab() if self.environment['input']['keyForeward'] > 0: self.environment['input']['keyForeward'] -= 1 self.environment['runtime']['commandManager'].executeDefaultTrigger('onKeyInput') def handleByteInput(self, event): 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'] or event['Data'] == '': return currentCommand = event['Data'] # special modes if self.environment['runtime']['helpManager'].isTutorialMode(): 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(currentCommand, 'vmenu-navigation'): self.environment['runtime']['commandManager'].executeCommand(currentCommand, 'vmenu-navigation') return # default 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']['vmenuManager'].getActive(): return self.environment['runtime']['commandManager'].executeDefaultTrigger('onScreenChanged') self.environment['runtime']['screenDriver'].getCurrScreen() def handleScreenUpdate(self, event): self.environment['runtime']['screenManager'].handleScreenUpdate(event['Data']) if time.time() - self.environment['runtime']['inputManager'].getLastInputTime() >= 0.3: self.environment['runtime']['inputManager'].clearLastDeepInput() 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() def handlePlugInputDevice(self, event): try: self.environment['runtime']['inputManager'].setLastDetectedDevices(event['Data']) except: pass 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) 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 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(): if self.command != '': self.singleKeyCommand = True if not (self.singleKeyCommand or self.modifierInput): return # fire event if self.command != '': if self.modifierInput: self.environment['runtime']['eventManager'].putToEventQueue(fenrirEventType.ExecuteCommand, self.command) self.command = '' else: if self.singleKeyCommand: self.environment['runtime']['eventManager'].putToEventQueue(fenrirEventType.ExecuteCommand, self.command) self.command = '' def setProcessName(self, name='fenrir'): """Attempts to set the process name to 'fenrir'.""" try: from setproctitle import setproctitle except ImportError: pass else: setproctitle(name) return True try: from ctypes import cdll, byref, create_string_buffer libc = cdll.LoadLibrary('libc.so.6') stringBuffer = create_string_buffer(len(name) + 1) stringBuffer.value = bytes(name, 'UTF-8') libc.prctl(15, byref(stringBuffer), 0, 0, 0) return True except: pass return False def shutdownRequest(self): try: self.environment['runtime']['eventManager'].stopMainEventLoop() except: pass def captureSignal(self, sigInit, frame): self.shutdownRequest() def shutdown(self): self.environment['runtime']['inputManager'].ungrabAllDevices() self.environment['runtime']['eventManager'].stopMainEventLoop() self.environment['runtime']['outputManager'].presentText(_("Quit Fenrir"), soundIcon='ScreenReaderOff', interrupt=True) self.environment['runtime']['eventManager'].cleanEventQueue() time.sleep(0.6) 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