From 5ab66f6978cdab783ed9b657d4600a30ed51bacf Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 20 Mar 2025 02:55:39 -0400 Subject: [PATCH 1/4] Attempt to fix the bug where Fenrir freezes the computer solid if it manages to start before sound is ready. It shuld at least release the keyboard now. --- src/fenrirscreenreader/core/inputDriver.py | 19 +++++++++++++ src/fenrirscreenreader/core/inputManager.py | 30 ++++++++++++++++++--- src/fenrirscreenreader/fenrirVersion.py | 2 +- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/fenrirscreenreader/core/inputDriver.py b/src/fenrirscreenreader/core/inputDriver.py index 16efa0b1..6529eec2 100644 --- a/src/fenrirscreenreader/core/inputDriver.py +++ b/src/fenrirscreenreader/core/inputDriver.py @@ -42,6 +42,25 @@ class inputDriver(): if not self._initialized: return True return True + + def forceUngrab(self): + """Emergency method to release grabbed devices in case of failure""" + if not self._initialized: + return True + try: + # Try standard ungrab first + return self.ungrabAllDevices() + except Exception as e: + # Just log the failure and inform the user + if hasattr(self, 'env') and 'runtime' in self.env and 'debug' in self.env['runtime']: + self.env['runtime']['debug'].writeDebugOut( + f"Emergency device release failed: {str(e)}", + debug.debugLevel.ERROR + ) + else: + print(f"Emergency device release failed: {str(e)}") + return False + def hasIDevices(self): if not self._initialized: return False diff --git a/src/fenrirscreenreader/core/inputManager.py b/src/fenrirscreenreader/core/inputManager.py index be5f9520..d3550fa0 100644 --- a/src/fenrirscreenreader/core/inputManager.py +++ b/src/fenrirscreenreader/core/inputManager.py @@ -49,6 +49,7 @@ class inputManager(): return event def setExecuteDeviceGrab(self, newExecuteDeviceGrab = True): self.executeDeviceGrab = newExecuteDeviceGrab + def handleDeviceGrab(self, force = False): if force: self.setExecuteDeviceGrab() @@ -61,17 +62,38 @@ class inputManager(): if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): self.executeDeviceGrab = False return + + # Add maximum retries to prevent infinite loops + maxRetries = 5 + retryCount = 0 + grabTimeout = 3 # Timeout in seconds + startTime = time.time() + if self.env['runtime']['screenManager'].getCurrScreenIgnored(): while not self.ungrabAllDevices(): + retryCount += 1 + if retryCount >= maxRetries or (time.time() - startTime) > grabTimeout: + self.env['runtime']['debug'].writeDebugOut("Failed to ungrab devices after multiple attempts", debug.debugLevel.ERROR) + # Force a release of devices if possible through alternative means + try: + self.env['runtime']['inputDriver'].forceUngrab() + except: + pass + break 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) + self.env['runtime']['debug'].writeDebugOut(f"retry ungrabAllDevices {retryCount}/{maxRetries}", debug.debugLevel.WARNING) else: while not self.grabAllDevices(): + retryCount += 1 + if retryCount >= maxRetries or (time.time() - startTime) > grabTimeout: + self.env['runtime']['debug'].writeDebugOut("Failed to grab devices after multiple attempts", debug.debugLevel.ERROR) + # Continue without grabbing input - limited functionality but not locked + break 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.env['runtime']['debug'].writeDebugOut(f"retry grabAllDevices {retryCount}/{maxRetries}", debug.debugLevel.WARNING) + self.executeDeviceGrab = False + def sendKeys(self, keyMacro): for e in keyMacro: key = '' diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index 3af6d94a..e0cebdeb 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -version = "2025.03.02" +version = "2025.03.20" codeName = "testing" From f68a1af2238de4e9ea5626d44c9cdd6b938a11b8 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 10 Apr 2025 05:46:22 -0400 Subject: [PATCH 2/4] Fixed a typo in requirements.txt. --- requirements.txt | 2 +- src/fenrirscreenreader/fenrirVersion.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e50d47f2..8a08ff62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,6 @@ daemonize>=2.5.0 dbus-python>=1.2.8 pyudev>=0.21.0 pexpect -ppyperclip +pyperclip pyte>=0.7.0 rapidfuzz>=2.0.0 diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index e0cebdeb..faef15b1 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -version = "2025.03.20" +version = "2025.04.10" codeName = "testing" From 2dda73ac874b25624d407d176203182a5112e074 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 14 Apr 2025 18:57:10 -0400 Subject: [PATCH 3/4] Request to be able to use the numpad if numlock is on and only process fenrir commands if numlock is off. This should work, let me know if anything breaks. --- .../core/attributeManager.py | 24 +++++++------- src/fenrirscreenreader/core/barrierManager.py | 4 +-- src/fenrirscreenreader/core/commandManager.py | 12 +++---- src/fenrirscreenreader/core/cursorManager.py | 16 +++++++--- src/fenrirscreenreader/core/debugManager.py | 4 +-- src/fenrirscreenreader/core/eventManager.py | 2 +- src/fenrirscreenreader/core/fenrirManager.py | 28 ++++++++-------- src/fenrirscreenreader/core/inputManager.py | 32 ++++++++++++++++--- src/fenrirscreenreader/core/outputManager.py | 12 +++---- src/fenrirscreenreader/core/processManager.py | 2 +- src/fenrirscreenreader/core/remoteManager.py | 2 +- src/fenrirscreenreader/core/screenManager.py | 12 +++---- src/fenrirscreenreader/core/speechDriver.py | 2 +- src/fenrirscreenreader/core/tableManager.py | 2 +- src/fenrirscreenreader/fenrirVersion.py | 2 +- 15 files changed, 93 insertions(+), 63 deletions(-) diff --git a/src/fenrirscreenreader/core/attributeManager.py b/src/fenrirscreenreader/core/attributeManager.py index f5f202b2..7578a080 100644 --- a/src/fenrirscreenreader/core/attributeManager.py +++ b/src/fenrirscreenreader/core/attributeManager.py @@ -105,7 +105,7 @@ class attributeManager(): cursorPos = cursor.copy() try: attribute = self.getAttributeByXY( cursorPos['x'], cursorPos['y']) - + if update: self.setLastCursorAttribute(attribute) if not self.isLastCursorAttributeChange(): @@ -155,13 +155,13 @@ class attributeManager(): attributeFormatString = attributeFormatString.replace('fenrirFGColor', _(attribute[0])) except Exception as e: attributeFormatString = attributeFormatString.replace('fenrirFGColor', '') - + # 1 BG color (name) try: attributeFormatString = attributeFormatString.replace('fenrirBGColor', _(attribute[1])) except Exception as e: attributeFormatString = attributeFormatString.replace('fenrirBGColor', '') - + # 2 bold (True/ False) try: if attribute[2]: @@ -169,7 +169,7 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirBold', '') - + # 3 italics (True/ False) try: if attribute[3]: @@ -177,7 +177,7 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirItalics', '') - + # 4 underline (True/ False) try: if attribute[4]: @@ -185,7 +185,7 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirUnderline', '') - + # 5 strikethrough (True/ False) try: if attribute[5]: @@ -193,7 +193,7 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirStrikethrough', '') - + # 6 reverse (True/ False) try: if attribute[6]: @@ -201,7 +201,7 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirReverse', '') - + # 7 blink (True/ False) try: if attribute[7]: @@ -209,7 +209,7 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirBlink', '') - + # 8 font size (int/ string) try: try: @@ -223,14 +223,14 @@ class attributeManager(): except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirFontSize', _('default')) - + # 9 font family (string) try: attributeFormatString = attributeFormatString.replace('fenrirFont', attribute[9]) except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirFont', _('default')) - + return attributeFormatString def trackHighlights(self): result = '' @@ -287,4 +287,4 @@ class attributeManager(): useful = True return useful - + diff --git a/src/fenrirscreenreader/core/barrierManager.py b/src/fenrirscreenreader/core/barrierManager.py index b3b4de17..6e8b4585 100644 --- a/src/fenrirscreenreader/core/barrierManager.py +++ b/src/fenrirscreenreader/core/barrierManager.py @@ -18,7 +18,7 @@ class barrierManager(): def updateBarrierChange(self, isBarrier): self.prefIsBarrier = self.currIsBarrier self.currIsBarrier = isBarrier - + def resetBarrierChange(self): self.currIsBarrier = False self.prefIsBarrier = False @@ -38,7 +38,7 @@ class barrierManager(): self.env['runtime']['outputManager'].playSoundIcon(soundIcon='BarrierStart', interrupt=doInterrupt) else: self.env['runtime']['outputManager'].playSoundIcon(soundIcon='BarrierEnd', interrupt=doInterrupt) - + if not isBarrier: sayLine = '' return isBarrier, sayLine diff --git a/src/fenrirscreenreader/core/commandManager.py b/src/fenrirscreenreader/core/commandManager.py index 163eab33..f64f3d3a 100644 --- a/src/fenrirscreenreader/core/commandManager.py +++ b/src/fenrirscreenreader/core/commandManager.py @@ -27,7 +27,7 @@ class commandManager(): # scripts for scriptKey self.env['runtime']['commandManager'].loadScriptCommands() - + def shutdown(self): for commandFolder in self.env['general']['commandFolderList']: self.env['runtime']['commandManager'].shutdownCommands(commandFolder) @@ -99,7 +99,7 @@ class commandManager(): self.env['runtime']['debug'].writeDebugOut("loadCommands: Loading command:" + command ,debug.debugLevel.ERROR) self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR) continue - + def loadScriptCommands(self, section='commands', scriptPath=''): if scriptPath =='': scriptPath = self.env['runtime']['settingsManager'].getSetting('general', 'scriptPath') @@ -165,7 +165,7 @@ class commandManager(): if section not in self.env['commands']: self.env['runtime']['debug'].writeDebugOut("shutdownCommands: section not found:" + section, debug.debugLevel.WARNING) return - + for command in sorted(self.env['commands'][section]): try: self.env['commands'][section][command].shutdown() @@ -228,7 +228,7 @@ class commandManager(): except Exception as e: self.env['runtime']['debug'].writeDebugOut("Executing command:" + section + "." + command +' ' + str(e),debug.debugLevel.ERROR) - + def runCommand(self, command, section = 'commands'): if self.commandExists(command, section): try: @@ -237,7 +237,7 @@ class commandManager(): except Exception as e: self.env['runtime']['debug'].writeDebugOut("runCommand command:" + section + "." + command +' ' + str(e),debug.debugLevel.ERROR) self.env['commandInfo']['lastCommandExecutionTime'] = time.time() - + def getCommandDescription(self, command, section = 'commands'): if self.commandExists(command, section): try: @@ -245,7 +245,7 @@ class commandManager(): except Exception as e: self.env['runtime']['debug'].writeDebugOut('commandManager.getCommandDescription:' + str(e),debug.debugLevel.ERROR) self.env['commandInfo']['lastCommandExecutionTime'] = time.time() - + def commandExists(self, command, section = 'commands'): try: return( command in self.env['commands'][section]) diff --git a/src/fenrirscreenreader/core/cursorManager.py b/src/fenrirscreenreader/core/cursorManager.py index d14dbe71..81527188 100644 --- a/src/fenrirscreenreader/core/cursorManager.py +++ b/src/fenrirscreenreader/core/cursorManager.py @@ -11,6 +11,14 @@ class cursorManager(): pass def initialize(self, environment): self.env = environment + def shouldProcessNumpadCommands(self): + """ + Check if numpad commands should be processed based on numlock state + Return True if numlock is OFF (commands should work) + Return False if numlock is ON (let keys type numbers) + """ + # Return False if numlock is ON + return not self.env['input']['newNumLock'] def shutdown(self): pass def clearMarks(self): @@ -47,7 +55,7 @@ class cursorManager(): return self.env['screen']['oldCursorReview'] = None self.env['screen']['newCursorReview'] = None - + def isCursorHorizontalMove(self): return self.env['screen']['newCursor']['x'] != self.env['screen']['oldCursor']['x'] @@ -56,7 +64,7 @@ class cursorManager(): def isReviewMode(self): return self.env['screen']['newCursorReview'] != None - + def enterReviewModeCurrTextCursor(self, overwrite=False): if self.isReviewMode() and not overwrite: return @@ -73,7 +81,7 @@ class cursorManager(): self.env['screen']['oldCursorReview'] = self.env['screen']['newCursorReview'] self.env['screen']['newCursorReview']['x'] = x self.env['screen']['newCursorReview']['y'] = y - + def isApplicationWindowSet(self): try: currApp = self.env['runtime']['applicationManager'].getCurrentApplication() @@ -108,7 +116,7 @@ class cursorManager(): currApp = self.env['runtime']['applicationManager'].getCurrentApplication() self.env['commandBuffer']['windowArea'][currApp] = {} - + if x1 * y1 <= \ x2 * y2: self.env['commandBuffer']['windowArea'][currApp]['1'] = {'x':x1, 'y':y1} diff --git a/src/fenrirscreenreader/core/debugManager.py b/src/fenrirscreenreader/core/debugManager.py index ffe9ffb9..1325b756 100644 --- a/src/fenrirscreenreader/core/debugManager.py +++ b/src/fenrirscreenreader/core/debugManager.py @@ -36,14 +36,14 @@ class debugManager(): except Exception as e: print(e) def writeDebugOut(self, text, level = debug.debugLevel.DEACTIVE, onAnyLevel=False): - + mode = self.env['runtime']['settingsManager'].getSetting('general','debugMode') if mode == '': mode = 'FILE' mode = mode.upper().split(',') fileMode = 'FILE' in mode printMode = 'PRINT' in mode - + if (self.env['runtime']['settingsManager'].getSettingAsInt('general','debugLevel') < int(level)) and \ not (onAnyLevel and self.env['runtime']['settingsManager'].getSettingAsInt('general','debugLevel') > int(debug.debugLevel.DEACTIVE)) : if self._fileOpened: diff --git a/src/fenrirscreenreader/core/eventManager.py b/src/fenrirscreenreader/core/eventManager.py index 1de24235..2d866b3e 100644 --- a/src/fenrirscreenreader/core/eventManager.py +++ b/src/fenrirscreenreader/core/eventManager.py @@ -21,7 +21,7 @@ class eventManager(): self.env = environment def shutdown(self): self.cleanEventQueue() - + def proceedEventLoop(self): event = self._eventQueue.get() st = time.time() diff --git a/src/fenrirscreenreader/core/fenrirManager.py b/src/fenrirscreenreader/core/fenrirManager.py index f88ee62c..fedbe4ba 100644 --- a/src/fenrirscreenreader/core/fenrirManager.py +++ b/src/fenrirscreenreader/core/fenrirManager.py @@ -23,11 +23,11 @@ class fenrirManager(): 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 @@ -42,10 +42,10 @@ class fenrirManager(): 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']) @@ -54,7 +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: @@ -74,7 +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 @@ -83,7 +83,7 @@ class fenrirManager(): if self.environment['input']['keyForeward'] > 0: self.environment['input']['keyForeward'] -= 1 - + self.environment['runtime']['commandManager'].executeDefaultTrigger('onKeyInput') def handleByteInput(self, event): @@ -124,14 +124,14 @@ class fenrirManager(): 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() @@ -150,17 +150,17 @@ class fenrirManager(): 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) @@ -220,7 +220,7 @@ class fenrirManager(): 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() diff --git a/src/fenrirscreenreader/core/inputManager.py b/src/fenrirscreenreader/core/inputManager.py index d3550fa0..ac993f11 100644 --- a/src/fenrirscreenreader/core/inputManager.py +++ b/src/fenrirscreenreader/core/inputManager.py @@ -62,13 +62,13 @@ class inputManager(): if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): self.executeDeviceGrab = False return - + # Add maximum retries to prevent infinite loops maxRetries = 5 retryCount = 0 grabTimeout = 3 # Timeout in seconds startTime = time.time() - + if self.env['runtime']['screenManager'].getCurrScreenIgnored(): while not self.ungrabAllDevices(): retryCount += 1 @@ -91,7 +91,7 @@ class inputManager(): break time.sleep(0.25) self.env['runtime']['debug'].writeDebugOut(f"retry grabAllDevices {retryCount}/{maxRetries}", debug.debugLevel.WARNING) - + self.executeDeviceGrab = False def sendKeys(self, keyMacro): @@ -274,17 +274,39 @@ class inputManager(): def getCurrShortcut(self, inputSequence = None): shortcut = [] shortcut.append(self.env['input']['shortcutRepeat']) + + numpadKeys = ['KEY_KP0', 'KEY_KP1', 'KEY_KP2', 'KEY_KP3', 'KEY_KP4', + 'KEY_KP5', 'KEY_KP6', 'KEY_KP7', 'KEY_KP8', 'KEY_KP9', + 'KEY_KPDOT', 'KEY_KPPLUS', 'KEY_KPMINUS', 'KEY_KPASTERISK', + 'KEY_KPSLASH', 'KEY_KPENTER', 'KEY_KPEQUAL'] if inputSequence: + # Check if any key in the sequence is a numpad key and numlock is ON + # If numlock is ON and any key in the sequence is a numpad key, return an empty shortcut + if not self.env['runtime']['cursorManager'].shouldProcessNumpadCommands(): + for key in inputSequence: + if key in numpadKeys: + # Return an empty/invalid shortcut that won't match any command + return "[]" + shortcut.append(inputSequence) else: + # Same check for current input + + if not self.env['runtime']['cursorManager'].shouldProcessNumpadCommands(): + for key in self.env['input']['currInput']: + if key in numpadKeys: + # Return an empty/invalid shortcut that won't match any command + return "[]" + 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)): + 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) + self.env['runtime']['debug'].writeDebugOut("currShortcut " + str(shortcut), debug.debugLevel.INFO) return str(shortcut) def currKeyIsModifier(self): diff --git a/src/fenrirscreenreader/core/outputManager.py b/src/fenrirscreenreader/core/outputManager.py index 1314e14d..3967b19f 100644 --- a/src/fenrirscreenreader/core/outputManager.py +++ b/src/fenrirscreenreader/core/outputManager.py @@ -22,7 +22,7 @@ class outputManager(): def shutdown(self): self.env['runtime']['settingsManager'].shutdownDriver('soundDriver') self.env['runtime']['settingsManager'].shutdownDriver('speechDriver') - + def presentText(self, text, interrupt=True, soundIcon='', ignorePunctuation=False, announceCapital=False, flush=True): if text == '': return @@ -58,13 +58,13 @@ class outputManager(): 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) - + 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) - + try: if announceCapital: self.env['runtime']['speechDriver'].setPitch(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'capitalPitch')) @@ -73,13 +73,13 @@ class outputManager(): 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) - + 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) - + try: self.env['runtime']['speechDriver'].setModule(self.env['runtime']['settingsManager'].getSetting('speech', 'module')) except Exception as e: @@ -91,7 +91,7 @@ class outputManager(): 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) - + try: if self.env['runtime']['settingsManager'].getSettingAsBool('general', 'newLinePause'): cleanText = text.replace('\n', ' , ') diff --git a/src/fenrirscreenreader/core/processManager.py b/src/fenrirscreenreader/core/processManager.py index 7f45f448..115b8f87 100644 --- a/src/fenrirscreenreader/core/processManager.py +++ b/src/fenrirscreenreader/core/processManager.py @@ -20,7 +20,7 @@ class processManager(): self.addSimpleEventThread(fenrirEventType.HeartBeat, self.heartBeatTimer, multiprocess=True) def shutdown(self): self.terminateAllProcesses() - + def terminateAllProcesses(self): for proc in self._Processes: #try: diff --git a/src/fenrirscreenreader/core/remoteManager.py b/src/fenrirscreenreader/core/remoteManager.py index 0dfb0417..3dc16327 100644 --- a/src/fenrirscreenreader/core/remoteManager.py +++ b/src/fenrirscreenreader/core/remoteManager.py @@ -172,7 +172,7 @@ class remoteManager(): self.env['runtime']['outputManager'].presentText(_('clipboard exported to file'), interrupt=True) except Exception as e: self.env['runtime']['debug'].writeDebugOut('export_clipboard_to_file:run: Filepath:'+ clipboardFile +' trace:' + str(e),debug.debugLevel.ERROR) - + def saveSettings(self, settingConfigPath = None): if not settingConfigPath: settingConfigPath = self.env['runtime']['settingsManager'].getSettingsFile() diff --git a/src/fenrirscreenreader/core/screenManager.py b/src/fenrirscreenreader/core/screenManager.py index 77b24676..a8fc669c 100644 --- a/src/fenrirscreenreader/core/screenManager.py +++ b/src/fenrirscreenreader/core/screenManager.py @@ -83,7 +83,7 @@ class screenManager(): 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'] @@ -146,11 +146,11 @@ class screenManager(): cursorLineEndOffset = cursorLineStart + self.env['screen']['newCursor']['x'] + 3 oldScreenText = self.env['screen']['oldContentText'][cursorLineStartOffset:cursorLineEndOffset] newScreenText = self.env['screen']['newContentText'][cursorLineStartOffset:cursorLineEndOffset] - + # Use the original differ for typing mode to preserve behavior diff = self.differ.compare(oldScreenText, newScreenText) diffList = list(diff) - + typing = True tempNewDelta = ''.join(x[2:] for x in diffList if x[0] == '+') if tempNewDelta.strip() != '': @@ -169,11 +169,11 @@ class screenManager(): # Process line by line using rapidfuzz old_lines = oldScreenText.split('\n') new_lines = newScreenText.split('\n') - + # Use standard differ for better word grouping diff = self.differ.compare(old_lines, new_lines) diffList = list(diff) - + except Exception as e: # Fall back to standard differ if there's any issue self.env['runtime']['debug'].writeDebugOut('screenManager:update:rapidfuzz: ' + str(e), debug.debugLevel.ERROR) @@ -209,7 +209,7 @@ class screenManager(): ignoreScreens.extend(self.env['screen']['autoIgnoreScreens']) self.env['runtime']['debug'].writeDebugOut('screenManager:isSuspendingScreen ignore:' + str(ignoreScreens) + ' current:'+ str(screen ), debug.debugLevel.INFO) return (screen in ignoreScreens) - + def isScreenChange(self): if not self.env['screen']['oldTTY']: return False diff --git a/src/fenrirscreenreader/core/speechDriver.py b/src/fenrirscreenreader/core/speechDriver.py index faf5e813..82b62d8d 100644 --- a/src/fenrirscreenreader/core/speechDriver.py +++ b/src/fenrirscreenreader/core/speechDriver.py @@ -29,7 +29,7 @@ class speechDriver(): return if not queueable: self.cancel() - + def cancel(self): if not self._isInitialized: return diff --git a/src/fenrirscreenreader/core/tableManager.py b/src/fenrirscreenreader/core/tableManager.py index b4466b2d..b27dac0f 100644 --- a/src/fenrirscreenreader/core/tableManager.py +++ b/src/fenrirscreenreader/core/tableManager.py @@ -34,7 +34,7 @@ class tableManager(): return '' def setRowColumnSep(self, columnSep = ''): self.rowColumnSep = columnSep - + def setHeadLine(self, headLine = ''): self.setHeadColumnSep() self.setRowColumnSep() diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index faef15b1..480faf91 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -version = "2025.04.10" +version = "2025.04.14" codeName = "testing" From 676c2b07a97e1c95e2572de66a13aa61aa598805 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 14 Apr 2025 20:02:00 -0400 Subject: [PATCH 4/4] A couple of small improvements to install.sh. --- install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install.sh b/install.sh index db529973..46a721e6 100755 --- a/install.sh +++ b/install.sh @@ -1,12 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash #Basic install script for Fenrir. -read -p "This will install Fenrir. Press ctrl+C to cancel, or enter to continue." continue +read -rp "This will install Fenrir. Press ctrl+C to cancel, or enter to continue." # Fenrir main application install -m755 -d /opt/fenrirscreenreader cp -af src/* /opt/fenrirscreenreader -ln -fs /opt/fenrirscreenreader/fenrir-daemon /usr/bin/fenrir-daemon ln -fs /opt/fenrirscreenreader/fenrir /usr/bin/fenrir # tools install -m755 -d /usr/share/fenrirscreenreader/tools @@ -33,8 +32,9 @@ cp -af config/sound/template /usr/share/sounds/fenrirscreenreader/template # config if [ -f "/etc/fenrirscreenreader/settings/settings.conf" ]; then echo "Do you want to overwrite your current global settings? (y/n)" - read yn - if [ $yn = "Y" -o $yn = "y" ]; then + read -r yn + yn="${yn:0:1}" + if [[ "${yn^}" == "Y" ]]; then mv /etc/fenrirscreenreader/settings/settings.conf /etc/fenrirscreenreader/settings/settings.conf.bak echo "Your old settings.conf has been backed up to settings.conf.bak." install -m644 -D "config/settings/settings.conf" /etc/fenrirscreenreader/settings/settings.conf