diff --git a/README.md b/README.md index 6eb7b3fd..c9b7a4de 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,13 @@ This software is licensed under the LGPL v3 . 1. "evdevDriver" input driver for linux evdev - python-evdev >=0.6.3 - python-pyudev -- This is commonly referred to as python3-evdev by your distribution +2. This is commonly referred to as python3-evdev by your distribution - loaded uinput kernel module - ReadWrite permission - /dev/input - /dev/uinput +3. "ptyDriver" terminal emulation input driver + - python-pyte # Screen Drivers: 1. "vcsaDriver" screen driver for linux VCSA devices @@ -28,28 +30,36 @@ This software is licensed under the LGPL v3 . - /dev/tty[1-64] - /dev/vcsa[1-64] - read logind DBUS - +2. "ptyDriver" terminal emulation driver + - python-pyte +3. "dummyDriver" just a dummy + # Speech Drivers: -1. "EspeakDriver" speech driver for Espeak or Espeak-NG: +1. "genericDriver" (default) sound driver for sound as subprocess: + - espeak or espeak-ng +2. "espeakDriver" speech driver for Espeak or Espeak-NG: - python-espeak -- "speechdDriver" speech driver for Speech-dispatcher: +3. "speechdDriver" speech driver for Speech-dispatcher: - Speech-dispatcher - python-speechd -2. "dummyDriver" speech driver for debugging +4. "dummyDriver" no speech +5. "debugDriver" speech driver for debugging # Braille Drivers: 1. "BrlttyDriver" braille driver (WIP): - brltty (configured and running) - python-brlapi -2. "dummyDriver" Braille driver for debugging +2. "dummyDriver" (default) no braille +3. "debugDriver" Braille driver for debugging # Sound Drivers: -1. "genericDriver" sound driver for sound as subprocess: +1. "genericDriver" (default) sound driver for sound as subprocess: - Sox 2. "gstreamerDriver" sound driver for gstreamer - gstreamer >=1.0 - GLib -3. "dummyDriver" sound driver for debugging +3. "dummyDriver" no sound +4. "debugDriver" sound driver for debugging # Extras: 1. spellchecker @@ -72,7 +82,7 @@ Settings "settings.conf" is located in the "config" directory or after installat 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: speechdDriver +- speech driver: genericDriver (via espeak or espeak-ng, could configured in settings.conf) - braille driver: brlttyDriver (WIP) - input driver: evdevDriver diff --git a/src/fenrirscreenreader/core/inputManager.py b/src/fenrirscreenreader/core/inputManager.py index b74f4ef9..9977c8a7 100644 --- a/src/fenrirscreenreader/core/inputManager.py +++ b/src/fenrirscreenreader/core/inputManager.py @@ -110,7 +110,10 @@ class inputManager(): def grabAllDevices(self): if self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): self.env['runtime']['inputDriver'].grabAllDevices() - + def ungrabAllDevices(self): + if self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): + self.env['runtime']['inputDriver'].ungrabAllDevices() + def updateInputDevices(self): try: self.env['runtime']['inputDriver'].updateInputDevices() diff --git a/src/fenrirscreenreader/core/outputManager.py b/src/fenrirscreenreader/core/outputManager.py index c6090120..5cb82e49 100644 --- a/src/fenrirscreenreader/core/outputManager.py +++ b/src/fenrirscreenreader/core/outputManager.py @@ -228,8 +228,11 @@ class outputManager(): offsetText = offsetText[offsetStart: offsetEnd] return offsetText def interruptOutput(self): - self.env['runtime']['speechDriver'].cancel() - self.env['runtime']['debug'].writeDebugOut("Interrupt speech",debug.debugLevel.INFO) + try: + self.env['runtime']['speechDriver'].cancel() + self.env['runtime']['debug'].writeDebugOut("Interrupt speech",debug.debugLevel.INFO) + except: + pass def clearFlushTime(self): self.setFlushTime(0.0) diff --git a/src/fenrirscreenreader/core/processManager.py b/src/fenrirscreenreader/core/processManager.py index bc1af5a0..86811655 100644 --- a/src/fenrirscreenreader/core/processManager.py +++ b/src/fenrirscreenreader/core/processManager.py @@ -71,7 +71,7 @@ class processManager(): else: function(self.running, eventQueue) except Exception as e: - self.env['runtime']['debug'].writeDebugOut('processManager:customEventWorkerThread:function():' + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut('processManager:customEventWorkerThread:function('+str(function)+'):' + str(e),debug.debugLevel.ERROR) if runOnce: break @@ -88,7 +88,7 @@ class processManager(): else: Data = function(self.running) except Exception as e: - self.env['runtime']['debug'].writeDebugOut('processManager:simpleEventWorkerThread:function():' + str(e),debug.debugLevel.ERROR) + self.env['runtime']['debug'].writeDebugOut('processManager:simpleEventWorkerThread:function('+str(function)+'):' + str(e),debug.debugLevel.ERROR) self.env['runtime']['eventManager'].putToEventQueue(event, Data) if runOnce: break diff --git a/src/fenrirscreenreader/core/screenManager.py b/src/fenrirscreenreader/core/screenManager.py index 28ec2815..426e086c 100644 --- a/src/fenrirscreenreader/core/screenManager.py +++ b/src/fenrirscreenreader/core/screenManager.py @@ -10,35 +10,59 @@ import time, os, re, difflib class screenManager(): def __init__(self): - pass + self.currScreenIgnored = False + self.prevScreenIgnored = False def initialize(self, environment): self.env = environment self.env['runtime']['settingsManager'].loadDriver(\ self.env['runtime']['settingsManager'].getSetting('screen', 'driver'), 'screenDriver') - self.env['runtime']['screenDriver'].getCurrScreen() - self.env['runtime']['screenDriver'].getCurrScreen() - self.env['runtime']['screenDriver'].getSessionInformation() - + self.getCurrScreen() + self.getCurrScreen() + self.getSessionInformation() + def getCurrScreen(self): + try: + self.env['runtime']['screenDriver'].getCurrScreen() + except: + pass + def getSessionInformation(self): + try: + self.env['runtime']['screenDriver'].getSessionInformation() + except: + pass + def shutdown(self): self.env['runtime']['settingsManager'].shutdownDriver('screenDriver') def hanldeScreenChange(self, eventData): - self.env['runtime']['screenDriver'].getCurrScreen() - self.env['runtime']['screenDriver'].getSessionInformation() + self.getCurrScreen() + self.getSessionInformation() if self.isScreenChange(): self.changeBrailleScreen() if not self.isSuspendingScreen(self.env['screen']['newTTY']): self.update(eventData, 'onScreenChange') self.env['screen']['lastScreenUpdate'] = time.time() def handleScreenUpdate(self, eventData): - self.env['screen']['oldApplication'] = self.env['screen']['newApplication'] - if not self.isSuspendingScreen(self.env['screen']['newTTY']): + self.env['screen']['oldApplication'] = self.env['screen']['newApplication'] + self.updateScreenIgnored() + if self.getCurrScreenIgnored() != self.getPrevScreenIgnored(): + if self.getCurrScreenIgnored(): + self.env['runtime']['inputManager'].ungrabAllDevices() + self.env['runtime']['outputManager'].interruptOutput() + else: + self.env['runtime']['inputManager'].grabAllDevices() + 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() + def getCurrScreenIgnored(self): + return self.currScreenIgnored + def getPrevScreenIgnored(self): + return self.prevScreenIgnored + def updateScreenIgnored(self): + self.prevScreenIgnored = self.currScreenIgnored + self.currScreenIgnored = self.isSuspendingScreen(self.env['screen']['newTTY']) def update(self, eventData, trigger='onUpdate'): - # set new "old" values self.env['screen']['oldContentBytes'] = self.env['screen']['newContentBytes'] self.env['screen']['oldContentText'] = self.env['screen']['newContentText'] @@ -50,6 +74,7 @@ class screenManager(): self.env['screen']['oldAttribDelta'] = self.env['screen']['newAttribDelta'] self.env['screen']['oldNegativeDelta'] = self.env['screen']['newNegativeDelta'] self.env['screen']['newContentBytes'] = eventData['bytes'] + # get metadata like cursor or screensize self.env['screen']['lines'] = int( eventData['lines']) self.env['screen']['columns'] = int( eventData['columns']) @@ -125,7 +150,7 @@ class screenManager(): if self.env['screen']['oldContentAttrib'] != self.env['screen']['newContentAttrib']: if self.env['runtime']['settingsManager'].getSettingAsBool('focus', 'highlight'): self.env['screen']['newAttribDelta'], self.env['screen']['newCursorAttrib'] = screen_utils.trackHighlights(self.env['screen']['oldContentAttrib'], self.env['screen']['newContentAttrib'], self.env['screen']['newContentText'], self.env['screen']['columns']) - + def formatAttributes(self, attribute, attributeFormatString = None): if not attributeFormatString: attributeFormatString = self.env['runtime']['settingsManager'].getSetting('general', 'attributeFormatString') diff --git a/src/fenrirscreenreader/inputDriver/evdevDriver.py b/src/fenrirscreenreader/inputDriver/evdevDriver.py index 7b51a83d..ff317cd7 100644 --- a/src/fenrirscreenreader/inputDriver/evdevDriver.py +++ b/src/fenrirscreenreader/inputDriver/evdevDriver.py @@ -40,6 +40,7 @@ class driver(inputDriver): self.iDevices = {} self.iDevicesFD = self._manager.list() self.uDevices = {} + self.gDevices = {} self.iDeviceNo = 0 self.watchDog = Value(c_bool, True) def initialize(self, environment): @@ -75,46 +76,53 @@ class driver(inputDriver): return time.time() def inputWatchdog(self,active , eventQueue): - while active.value: - r, w, x = select(self.iDevices, [], [], 0.5) - for fd in r: - event = None - foreward = False - eventFired = False - try: - event = self.iDevices[fd].read_one() - except: - self.removeDevice(fd) - while(event): - self.env['input']['eventBuffer'].append( [self.iDevices[fd], self.uDevices[fd], event]) - if event.type == evdev.events.EV_KEY: - if event.code != 0: - currMapEvent = self.mapEvent(event) - if not currMapEvent: - foreward = True - if not isinstance(currMapEvent['EventName'], str): - foreward = True - if not foreward or eventFired: - if currMapEvent['EventState'] in [0,1,2]: - eventQueue.put({"Type":fenrirEventType.KeyboardInput,"Data":currMapEvent.copy()}) - eventFired = True - else: - if not event.type in [0,4]: - foreward = True - - event = self.iDevices[fd].read_one() - if foreward and not eventFired: - self.writeEventBuffer() - self.clearEventBuffer() - + try: + while active.value: + r, w, x = select(self.iDevices, [], [], 0.5) + for fd in r: + event = None + foreward = False + eventFired = False + try: + event = self.iDevices[fd].read_one() + except: + self.removeDevice(fd) + while(event): + self.env['input']['eventBuffer'].append( [self.iDevices[fd], self.uDevices[fd], event]) + if event.type == evdev.events.EV_KEY: + if event.code != 0: + currMapEvent = self.mapEvent(event) + if not currMapEvent: + foreward = True + if not isinstance(currMapEvent['EventName'], str): + foreward = True + if not foreward or eventFired: + if currMapEvent['EventState'] in [0,1,2]: + eventQueue.put({"Type":fenrirEventType.KeyboardInput,"Data":currMapEvent.copy()}) + eventFired = True + else: + if not event.type in [0,4]: + foreward = True + + event = self.iDevices[fd].read_one() + if foreward and not eventFired: + self.writeEventBuffer() + self.clearEventBuffer() + except Exception as e: + self.env['runtime']['debug'].writeDebugOut("INPUT WATCHDOG CRASH: "+str(e),debug.debugLevel.ERROR) + def handleInputEvent(self, event): return def writeEventBuffer(self): if not self._initialized: return - for iDevice, uDevice, event in self.env['input']['eventBuffer']: - self.writeUInput(uDevice, event) + for iDevice, uDevice, event in self.env['input']['eventBuffer']: + try: + if self.gDevices[iDevice.fd]: + self.writeUInput(uDevice, event) + except Exception as e: + pass def clearEventBuffer(self): if not self._initialized: @@ -184,7 +192,6 @@ class driver(inputDriver): self.grabDevice(currDevice.fd) self.env['runtime']['debug'].writeDebugOut('Device added (Name):' + self.iDevices[currDevice.fd].name,debug.debugLevel.INFO) except Exception as e: - print(e) self.env['runtime']['debug'].writeDebugOut("Device Skipped (Exception): " + deviceFile +' ' + currDevice.name +' '+ str(e),debug.debugLevel.INFO) self.iDeviceNo = len(evdev.list_devices()) self.updateMPiDevicesFD() @@ -238,14 +245,18 @@ class driver(inputDriver): if not self._initialized: return for fd in self.iDevices: - self.grabDevice(fd) - + self.grabDevice(fd) + def ungrabAllDevices(self): + if not self._initialized: + return + for fd in self.iDevices: + self.ungrabDevices(fd) def grabDevice(self, fd): if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): self.uDevices[fd] = None return try: - self.uDevices[fd] = UInput.from_device(self.iDevices[fd].fn) + self.uDevices[fd] = UInput.from_device(self.iDevices[fd]) except Exception as e: try: self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: compat fallback: ' + str(e),debug.debugLevel.WARNING) @@ -261,13 +272,21 @@ class driver(inputDriver): return try: self.iDevices[fd].grab() + self.gDevices[fd] = True except Exception as e: self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: grabing not possible: ' + str(e),debug.debugLevel.ERROR) - + def ungrabDevices(self,fd): + if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): + return + try: + self.iDevices[fd].ungrab() + self.gDevices[fd] = False + except: + pass def removeDevice(self,fd): self.clearEventBuffer() try: - self.iDevices[fd].ungrab() + self.ungrabDevices(fd) except: pass try: @@ -286,7 +305,11 @@ class driver(inputDriver): del(self.uDevices[fd]) except: pass - self.updateMPiDevicesFD() + try: + del(self.gDevices[fd]) + except: + pass + self.MPiDevicesFD() def hasIDevices(self): if not self._initialized: diff --git a/src/fenrirscreenreader/soundDriver/genericDriver.py b/src/fenrirscreenreader/soundDriver/genericDriver.py index d3eec5f6..c599fe9b 100644 --- a/src/fenrirscreenreader/soundDriver/genericDriver.py +++ b/src/fenrirscreenreader/soundDriver/genericDriver.py @@ -37,7 +37,7 @@ class driver(soundDriver): word = word.replace('fenrirFreqDuration', str(duration)) word = word.replace('fenrirFrequence', str(frequence)) popenFrequenceCommand[idx] = word - self.proc = subprocess.Popen(popenFrequenceCommand, shell=False) + self.proc = subprocess.Popen(popenFrequenceCommand, stdin=None, stdout=None, stderr=None, shell=False) self.soundType = 'frequence' def playSoundFile(self, filePath, interrupt = True): if not self._initialized: diff --git a/src/fenrirscreenreader/speechDriver/genericDriver.py b/src/fenrirscreenreader/speechDriver/genericDriver.py index 31f16db9..3aaecfbe 100644 --- a/src/fenrirscreenreader/speechDriver/genericDriver.py +++ b/src/fenrirscreenreader/speechDriver/genericDriver.py @@ -180,7 +180,7 @@ class driver(speechDriver): try: self.env['runtime']['debug'].writeDebugOut('speechDriver:worker:' + ' '.join(popenSpeechCommand),debug.debugLevel.INFO) self.lock.acquire(True) - self.proc = Popen(popenSpeechCommand, shell=False) + self.proc = Popen(popenSpeechCommand, stdin=None, stdout=None, stderr=None, shell=False) self.lock.release() self.proc.wait() except Exception as e: