#!/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 import inputData import os, inspect, time, traceback currentdir = os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) fenrirPath = os.path.dirname(currentdir) class inputManager(): def __init__(self): self.shortcutType = 'KEY' self.executeDeviceGrab = False self.lastDetectedDevices = None def setShortcutType(self, shortcutType = 'KEY'): if shortcutType in ['KEY', 'BYTE']: self.shortcutType = shortcutType def getShortcutType(self): return self.shortcutType def initialize(self, environment): self.env = environment self.env['runtime']['settingsManager'].loadDriver(\ self.env['runtime']['settingsManager'].getSetting('keyboard', 'driver'), 'inputDriver') self.updateInputDevices() # init LEDs with current state self.env['input']['newNumLock'] = self.env['runtime']['inputDriver'].getLedState() self.env['input']['oldNumLock'] = self.env['input']['newNumLock'] self.env['input']['newCapsLock'] = self.env['runtime']['inputDriver'].getLedState(1) self.env['input']['oldCapsLock'] = self.env['input']['newCapsLock'] self.env['input']['newScrollLock'] = self.env['runtime']['inputDriver'].getLedState(2) self.env['input']['oldScrollLock'] = self.env['input']['newScrollLock'] self.lastDeepestInput = [] self.lastEvent = None self.env['input']['shortcutRepeat'] = 1 self.lastInputTime = time.time() def shutdown(self): self.removeAllDevices() self.env['runtime']['settingsManager'].shutdownDriver('inputDriver') def getInputEvent(self): event = None try: event = self.env['runtime']['inputDriver'].getInputEvent() except: pass return event def setExecuteDeviceGrab(self, newExecuteDeviceGrab = True): self.executeDeviceGrab = newExecuteDeviceGrab def handleDeviceGrab(self, force = False): if force: self.setExecuteDeviceGrab() if not self.executeDeviceGrab: return if self.env['input']['eventBuffer'] != []: return if not self.noKeyPressed(): return if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): self.executeDeviceGrab = False return if self.env['runtime']['screenManager'].getCurrScreenIgnored(): while not self.ungrabAllDevices(): time.sleep(0.25) self.env['runtime']['debug'].writeDebugOut("retry ungrabAllDevices " ,debug.debugLevel.WARNING) self.env['runtime']['debug'].writeDebugOut("All devices ungrabbed" ,debug.debugLevel.INFO) else: while not self.grabAllDevices(): time.sleep(0.25) self.env['runtime']['debug'].writeDebugOut("retry grabAllDevices" ,debug.debugLevel.WARNING) self.env['runtime']['debug'].writeDebugOut("All devices grabbed" ,debug.debugLevel.INFO) self.executeDeviceGrab = False def sendKeys(self, keyMacro): for e in keyMacro: key = '' value = 0 if len(e) != 2: continue if isinstance(e[0], int) and isinstance(e[1], str): key = e[1].upper() value = e[0] elif isinstance(e[1], int) and isinstance(e[0], str): key = e[0].upper() value = e[1] else: continue if key.upper() == 'SLEEP': time.sleep(value) else: self.env['runtime']['inputDriver'].sendKey(key, value) def getLastEvent(self): return self.lastEvent def handleInputEvent(self, eventData): if not eventData: return self.lastEvent = eventData # a hang apears.. try to fix if self.env['input']['eventBuffer'] == []: if self.env['input']['currInput'] != []: self.env['input']['currInput'] = [] self.env['input']['shortcutRepeat'] = 1 self.env['input']['prevInput'] = self.env['input']['currInput'].copy() if eventData['EventState'] == 0: if eventData['EventName'] in self.env['input']['currInput']: self.env['input']['currInput'].remove(eventData['EventName']) if len(self.env['input']['currInput']) > 1: self.env['input']['currInput'] = sorted(self.env['input']['currInput']) elif len(self.env['input']['currInput']) == 0: self.env['input']['shortcutRepeat'] = 1 self.lastInputTime = time.time() elif eventData['EventState'] == 1: if not eventData['EventName'] in self.env['input']['currInput']: self.env['input']['currInput'].append(eventData['EventName']) if len(self.env['input']['currInput']) > 1: self.env['input']['currInput'] = sorted(self.env['input']['currInput']) if len(self.lastDeepestInput) < len(self.env['input']['currInput']): self.setLastDeepestInput( self.env['input']['currInput'].copy()) elif self.lastDeepestInput == self.env['input']['currInput']: if time.time() - self.lastInputTime <= self.env['runtime']['settingsManager'].getSettingAsFloat('keyboard','doubleTapTimeout'): self.env['input']['shortcutRepeat'] += 1 else: self.env['input']['shortcutRepeat'] = 1 self.handleLedStates(eventData) self.lastInputTime = time.time() elif eventData['EventState'] == 2: self.lastInputTime = time.time() self.env['input']['oldNumLock'] = self.env['input']['newNumLock'] self.env['input']['newNumLock'] = self.env['runtime']['inputDriver'].getLedState() self.env['input']['oldCapsLock'] = self.env['input']['newCapsLock'] self.env['input']['newCapsLock'] = self.env['runtime']['inputDriver'].getLedState(1) self.env['input']['oldScrollLock'] = self.env['input']['newScrollLock'] self.env['input']['newScrollLock'] = self.env['runtime']['inputDriver'].getLedState(2) self.env['runtime']['debug'].writeDebugOut("currInput " + str(self.env['input']['currInput'] ) ,debug.debugLevel.INFO) if self.noKeyPressed(): self.env['input']['prevInput'] = [] def handleLedStates(self, mEvent): if self.currKeyIsModifier(): return try: if mEvent['EventName'] == 'KEY_NUMLOCK': self.env['runtime']['inputDriver'].toggleLedState() elif mEvent['EventName'] == 'KEY_CAPSLOCK': self.env['runtime']['inputDriver'].toggleLedState(1) elif mEvent['EventName'] == 'KEY_SCROLLLOCK': self.env['runtime']['inputDriver'].toggleLedState(2) except: pass def grabAllDevices(self): if self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): try: return self.env['runtime']['inputDriver'].grabAllDevices() except Exception as e: return False return True def ungrabAllDevices(self): if self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): try: return self.env['runtime']['inputDriver'].ungrabAllDevices() except Exception as e: return False return True def handlePlugInputDevice(self, eventData): for deviceEntry in eventData: self.updateInputDevices(deviceEntry['device']) def updateInputDevices(self, newDevice = None): try: self.env['runtime']['inputDriver'].updateInputDevices(newDevice) except: pass try: if self.env['runtime']['screenManager']: self.handleDeviceGrab(force = True) except: pass def removeAllDevices(self): try: self.env['runtime']['inputDriver'].removeAllDevices() except: pass def convertEventName(self, eventName): if not eventName: return '' if eventName == '': return '' eventName = eventName.upper() if eventName == 'KEY_LEFTCTRL': eventName = 'KEY_CTRL' elif eventName == 'KEY_RIGHTCTRL': eventName = 'KEY_CTRL' elif eventName == 'KEY_LEFTSHIFT': eventName = 'KEY_SHIFT' elif eventName == 'KEY_RIGHTSHIFT': eventName = 'KEY_SHIFT' elif eventName == 'KEY_LEFTALT': eventName = 'KEY_ALT' elif eventName == 'KEY_RIGHTALT': eventName = 'KEY_ALT' elif eventName == 'KEY_LEFTMETA': eventName = 'KEY_META' elif eventName == 'KEY_RIGHTMETA': eventName = 'KEY_META' if self.isFenrirKey(eventName): eventName = 'KEY_FENRIR' if self.isScriptKey(eventName): eventName = 'KEY_SCRIPT' return eventName def clearEventBuffer(self): try: self.env['runtime']['inputDriver'].clearEventBuffer() except Exception as e: pass def setLastDeepestInput(self, currentDeepestInput): self.lastDeepestInput = currentDeepestInput def clearLastDeepInput(self): self.lastDeepestInput = [] def getLastInputTime(self): return self.lastInputTime def getLastDeepestInput(self): return self.lastDeepestInput def writeEventBuffer(self): try: if self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): self.env['runtime']['inputDriver'].writeEventBuffer() self.clearEventBuffer() except Exception as e: self.env['runtime']['debug'].writeDebugOut("Error while writeUInput",debug.debugLevel.ERROR) self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) def noKeyPressed(self): return self.env['input']['currInput'] == [] def isKeyPress(self): return (self.env['input']['prevInput'] == []) and (self.env['input']['currInput'] != []) def getPrevDeepestShortcut(self): shortcut = [] shortcut.append(self.env['input']['shortcutRepeat']) shortcut.append(self.getLastDeepestInput()) return str(shortcut) def getPrevShortcut(self): shortcut = [] shortcut.append(self.env['input']['shortcutRepeat']) shortcut.append(self.env['input']['prevInput']) return str(shortcut) def getCurrShortcut(self, inputSequence = None): shortcut = [] shortcut.append(self.env['input']['shortcutRepeat']) if inputSequence: shortcut.append(inputSequence) else: shortcut.append(self.env['input']['currInput']) if len(self.env['input']['prevInput']) < len(self.env['input']['currInput']): if self.env['input']['shortcutRepeat'] > 1 and not self.shortcutExists(str(shortcut)): shortcut = [] self.env['input']['shortcutRepeat'] = 1 shortcut.append(self.env['input']['shortcutRepeat']) shortcut.append(self.env['input']['currInput']) self.env['runtime']['debug'].writeDebugOut("currShortcut " + str(shortcut) ,debug.debugLevel.INFO) return str(shortcut) def currKeyIsModifier(self): if len(self.getLastDeepestInput()) != 1: return False return (self.env['input']['currInput'][0] =='KEY_FENRIR') or (self.env['input']['currInput'][0] == 'KEY_SCRIPT') def isFenrirKey(self, eventName): return eventName in self.env['input']['fenrirKey'] def isScriptKey(self, eventName): return eventName in self.env['input']['scriptKey'] def getCommandForShortcut(self, shortcut): if not self.shortcutExists(shortcut): return '' return self.env['bindings'][shortcut] def keyEcho(self, eventData = None): if not eventData: eventData = self.getLastEvent() if not eventData: return keyName = '' if eventData['EventState'] == 1: keyName = eventData['EventName'].lower() if keyName.startswith('key_'): keyName = keyName[4:] self.env['runtime']['outputManager'].presentText(_(keyName), interrupt=True) def shortcutExists(self, shortcut): return(shortcut in self.env['bindings']) def loadShortcuts(self, kbConfigPath=fenrirPath + '/../../config/keyboard/desktop.conf'): kbConfig = open(kbConfigPath,"r") while(True): invalid = False line = kbConfig.readline() if not line: break line = line.replace('\n','') if line.replace(" ","") == '': continue if line.replace(" ","").startswith("#"): continue if line.count("=") != 1: continue sepLine = line.split('=') commandName = sepLine[1].upper() sepLine[0] = sepLine[0].replace(" ","") sepLine[0] = sepLine[0].replace("'","") sepLine[0] = sepLine[0].replace('"',"") keys = sepLine[0].split(',') shortcutKeys = [] shortcutRepeat = 1 shortcut = [] for key in keys: try: shortcutRepeat = int(key) except: if not self.isValidKey(key.upper()): self.env['runtime']['debug'].writeDebugOut("invalid key : "+ key.upper() + ' command:' +commandName ,debug.debugLevel.WARNING) invalid = True break shortcutKeys.append(key.upper()) if invalid: continue shortcut.append(shortcutRepeat) shortcut.append(sorted(shortcutKeys)) if len(shortcutKeys) != 1 and not 'KEY_FENRIR' in shortcutKeys: self.env['runtime']['debug'].writeDebugOut("invalid shortcut (missing KEY_FENRIR): "+ str(shortcut) + ' command:' +commandName ,debug.debugLevel.ERROR) continue self.env['runtime']['debug'].writeDebugOut("Shortcut: "+ str(shortcut) + ' command:' +commandName ,debug.debugLevel.INFO, onAnyLevel=True) self.env['bindings'][str(shortcut)] = commandName self.env['rawBindings'][str(shortcut)] = shortcut kbConfig.close() # fix bindings self.env['bindings'][str([1, ['KEY_F1', 'KEY_FENRIR']])] = 'TOGGLE_TUTORIAL_MODE' def isValidKey(self, key): return key in inputData.keyNames def setLastDetectedDevices(self, devices): self.lastDetectedDevices =devices def getLastDetectedDevices(self): return self.lastDetectedDevices