diff --git a/requirements.txt b/requirements.txt index 7c22f473..a901c88f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ daemonize +dbus-python evdev pexpect pyenchant diff --git a/setup.py b/setup.py index 9ab7b8e4..efb3b90e 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ for directory in directories: if not forceSettingsFlag: try: files = [f for f in files if not f.endswith('settings.conf')] - except: + except Exception as e: pass elif 'config/scripts' in directory: destDir = '/usr/share/fenrirscreenreader/scripts' diff --git a/src/fenrirscreenreader/commands/commands/add_word_to_spell_check.py b/src/fenrirscreenreader/commands/commands/add_word_to_spell_check.py index 80062ad0..4f62a3d0 100644 --- a/src/fenrirscreenreader/commands/commands/add_word_to_spell_check.py +++ b/src/fenrirscreenreader/commands/commands/add_word_to_spell_check.py @@ -10,7 +10,7 @@ initialized = False try: import enchant initialized = True -except: +except Exception as e: pass class command(): diff --git a/src/fenrirscreenreader/commands/commands/export_clipboard_to_file.py b/src/fenrirscreenreader/commands/commands/export_clipboard_to_file.py index d3cf7025..a818acd2 100644 --- a/src/fenrirscreenreader/commands/commands/export_clipboard_to_file.py +++ b/src/fenrirscreenreader/commands/commands/export_clipboard_to_file.py @@ -30,7 +30,7 @@ class command(): clipboard = self.env['runtime']['memoryManager'].getIndexListElement('clipboardHistory') clipboardFile.write(clipboard) clipboardFile.close() - os.chmod(clipboardFilePath, 0o666) + os.chmod(clipboardFilePath, 0o644) 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) diff --git a/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py b/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py index 28808ec4..d9583c24 100644 --- a/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py +++ b/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py @@ -19,7 +19,7 @@ class command(): # Check if progress monitoring should be enabled by default from settings try: defaultEnabled = self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'progressMonitoring') - except: + except Exception as e: # If setting doesn't exist, default to False defaultEnabled = False self.env['commandBuffer']['progressMonitoring'] = defaultEnabled @@ -201,7 +201,7 @@ class command(): return False - except Exception: + except Exception as e: # If anything fails, assume it's not a prompt to be safe return False diff --git a/src/fenrirscreenreader/commands/commands/remove_word_from_spell_check.py b/src/fenrirscreenreader/commands/commands/remove_word_from_spell_check.py index edff9217..55f01302 100644 --- a/src/fenrirscreenreader/commands/commands/remove_word_from_spell_check.py +++ b/src/fenrirscreenreader/commands/commands/remove_word_from_spell_check.py @@ -10,7 +10,7 @@ initialized = False try: import enchant initialized = True -except: +except Exception as e: pass class command(): @@ -35,7 +35,7 @@ class command(): if self.env['runtime']['settingsManager'].getSetting('general', 'spellCheckLanguage') != self.language: try: self.updateSpellLanguage() - except: + except Exception as e: return cursorPos = self.env['runtime']['cursorManager'].getReviewOrTextCursor() diff --git a/src/fenrirscreenreader/commands/commands/silence_until_prompt.py b/src/fenrirscreenreader/commands/commands/silence_until_prompt.py index e9eb7607..cca33a3d 100644 --- a/src/fenrirscreenreader/commands/commands/silence_until_prompt.py +++ b/src/fenrirscreenreader/commands/commands/silence_until_prompt.py @@ -56,7 +56,7 @@ class command(): self.env['runtime']['debug'].writeDebugOut("Found exact prompt match: " + exactMatch, debug.debugLevel.INFO) self.disableSilence() return True - except: + except Exception as e: # Prompt section doesn't exist in settings, skip custom exact matches pass @@ -68,7 +68,7 @@ class command(): if customPatterns: customList = [pattern.strip() for pattern in customPatterns.split(',') if pattern.strip()] promptPatterns.extend(customList) - except: + except Exception as e: # Prompt section doesn't exist in settings, skip custom patterns pass diff --git a/src/fenrirscreenreader/commands/commands/spell_check.py b/src/fenrirscreenreader/commands/commands/spell_check.py index df4191e7..73f1fd03 100644 --- a/src/fenrirscreenreader/commands/commands/spell_check.py +++ b/src/fenrirscreenreader/commands/commands/spell_check.py @@ -9,7 +9,7 @@ initialized = False try: import enchant initialized = True -except: +except Exception as e: pass class command(): @@ -36,7 +36,7 @@ class command(): if self.env['runtime']['settingsManager'].getSetting('general', 'spellCheckLanguage') != self.language: try: self.updateSpellLanguage() - except: + except Exception as e: return cursorPos = self.env['runtime']['cursorManager'].getReviewOrTextCursor() diff --git a/src/fenrirscreenreader/commands/onCursorChange/35000-spell_check.py b/src/fenrirscreenreader/commands/onCursorChange/35000-spell_check.py index 6f9b433a..61bccb1a 100644 --- a/src/fenrirscreenreader/commands/onCursorChange/35000-spell_check.py +++ b/src/fenrirscreenreader/commands/onCursorChange/35000-spell_check.py @@ -11,7 +11,7 @@ initialized = False try: import enchant initialized = True -except: +except Exception as e: pass class command(): @@ -40,7 +40,7 @@ class command(): if self.env['runtime']['settingsManager'].getSetting('general', 'spellCheckLanguage') != self.language: try: self.updateSpellLanguage() - except: + except Exception as e: return # just when horizontal cursor move worddetection is needed @@ -111,17 +111,17 @@ class command(): try: if os.path.exists("/bin/"+currWord): return - except: + except Exception as e: pass try: if os.path.exists("/usr/bin/"+currWord): return - except: + except Exception as e: pass try: if os.path.exists("/sbin/"+currWord): return - except: + except Exception as e: pass if not self.spellChecker.check(currWord): diff --git a/src/fenrirscreenreader/commands/onPlugInputDevice/50000-plugSound.py b/src/fenrirscreenreader/commands/onPlugInputDevice/50000-plugSound.py index 853d36d4..cdcede66 100755 --- a/src/fenrirscreenreader/commands/onPlugInputDevice/50000-plugSound.py +++ b/src/fenrirscreenreader/commands/onPlugInputDevice/50000-plugSound.py @@ -23,7 +23,7 @@ class command(): for deviceEntry in deviceList: # dont play sounds for virtual devices playSound = playSound or not deviceEntry['virtual'] - except: + except Exception as e: playSound = True if playSound: if time.time() - self.lastTime > 5: diff --git a/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py b/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py index 4944442e..d4d7db5d 100644 --- a/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py +++ b/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py @@ -31,7 +31,7 @@ class command(): self.checkForPrompt(currentLine) except Exception as e: # Silently ignore errors to avoid disrupting normal operation - pass + self.env['runtime']['debug'].writeDebugOut('prompt_detector run: Error in prompt detection: ' + str(e), debug.debugLevel.ERROR) def checkForPrompt(self, text): """Check if the current line contains a shell prompt pattern""" @@ -50,7 +50,7 @@ class command(): self.env['runtime']['debug'].writeDebugOut("Found exact prompt match: " + exactMatch, debug.debugLevel.INFO) self._restoreSpeech() return True - except: + except Exception as e: # Prompt section doesn't exist in settings, skip custom exact matches pass @@ -62,7 +62,7 @@ class command(): if customPatterns: customList = [pattern.strip() for pattern in customPatterns.split(',') if pattern.strip()] promptPatterns.extend(customList) - except: + except Exception as e: # Prompt section doesn't exist in settings, skip custom patterns pass diff --git a/src/fenrirscreenreader/commands/vmenu-navigation/search_a.py b/src/fenrirscreenreader/commands/vmenu-navigation/search_a.py index d4708630..7ef8987e 100644 --- a/src/fenrirscreenreader/commands/vmenu-navigation/search_a.py +++ b/src/fenrirscreenreader/commands/vmenu-navigation/search_a.py @@ -4,7 +4,12 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -from fenrirscreenreader.commands.vmenu_navigation.vmenu_search_base import VMenuSearchCommand +import importlib.util +import os +_spec = importlib.util.spec_from_file_location("vmenu_search_base", os.path.join(os.path.dirname(__file__), "vmenu_search_base.py")) +_module = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(_module) +VMenuSearchCommand = _module.VMenuSearchCommand class command(VMenuSearchCommand): def __init__(self): diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/config_base.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/config_base.py new file mode 100644 index 00000000..02f9d2bb --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/config_base.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Fenrir TTY screen reader +# By Chrys, Storm Dragon, and contributers. + +import os +import shutil +import tempfile +import configparser +from datetime import datetime + +class ConfigCommand(): + """Base class for configuration management commands""" + + def __init__(self): + self.env = None + self.settingsFile = None + self.config = None + + def initialize(self, environment): + self.env = environment + self.settingsFile = self.env['runtime']['settingsManager'].settingsFile + self.config = self.env['runtime']['settingsManager'].settings + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def presentText(self, text, interrupt=True, flush=True): + """Present text to user with proper speech handling""" + self.env['runtime']['outputManager'].presentText(text, interrupt=interrupt, flush=flush) + + def playSound(self, soundName): + """Play system sound""" + soundIcon = '' + if soundName == 'Accept': + soundIcon = 'Accept' + elif soundName == 'Error': + soundIcon = 'ErrorSound' + elif soundName == 'Cancel': + soundIcon = 'Cancel' + + if soundIcon: + self.env['runtime']['outputManager'].presentText('', soundIcon=soundIcon, interrupt=False) + + def backupConfig(self, announce=True): + """Create backup of current configuration file""" + try: + if not os.path.exists(self.settingsFile): + message = "Configuration file not found" + if announce: + self.presentText(message) + return False, message + + # Create backup with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_path = f"{self.settingsFile}.backup_{timestamp}" + + shutil.copy2(self.settingsFile, backup_path) + + message = f"Configuration backed up to {backup_path}" + if announce: + self.presentText("Configuration backup created successfully") + return True, message + + except Exception as e: + message = f"Failed to backup configuration: {str(e)}" + if announce: + self.presentText(message) + return False, message + + def reloadConfig(self): + """Reload configuration from file""" + try: + # Force settings manager to reload from file + self.env['runtime']['settingsManager'].loadSettings() + self.config = self.env['runtime']['settingsManager'].settings + return True + + except Exception as e: + self.presentText(f"Failed to reload configuration: {str(e)}") + return False + + def findDefaultConfig(self): + """Find default configuration file path""" + # Look for default config in multiple locations + default_paths = [ + '/etc/fenrir/settings/settings.conf.default', + '/usr/share/fenrir/settings/settings.conf', + os.path.join(os.path.dirname(self.settingsFile), 'settings.conf.default'), + os.path.join(os.path.dirname(os.path.dirname(self.settingsFile)), 'settings', 'settings.conf.default') + ] + + for path in default_paths: + if os.path.exists(path): + return path + + return None + + def createBasicDefaults(self): + """Create basic default configuration manually""" + try: + # Create a new config with basic defaults + self.config = configparser.ConfigParser() + + # Add basic sections and settings + self.config.add_section('speech') + self.config.set('speech', 'driver', 'speechdDriver') + self.config.set('speech', 'rate', '0.75') + self.config.set('speech', 'pitch', '0.5') + self.config.set('speech', 'volume', '1.0') + + self.config.add_section('sound') + self.config.set('sound', 'driver', 'genericDriver') + self.config.set('sound', 'volume', '0.7') + + self.config.add_section('keyboard') + self.config.set('keyboard', 'driver', 'evdevDriver') + self.config.set('keyboard', 'keyboardLayout', 'desktop') + + self.config.add_section('screen') + self.config.set('screen', 'driver', 'vcsaDriver') + + self.config.add_section('general') + self.config.set('general', 'debugMode', 'Off') + + return True + + except Exception as e: + self.presentText(f"Failed to create basic defaults: {str(e)}") + return False \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/revert_to_saved.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/revert_to_saved.py index 5d523f00..5629524a 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/revert_to_saved.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/revert_to_saved.py @@ -27,8 +27,8 @@ class command(): speechDriver = self.env['runtime']['speechDriver'] speechDriver.shutdown() speechDriver.initialize(self.env) - except: - pass + except Exception as e: + print(f'revert_to_saved speechDriver: Error reinitializing speech driver: {str(e)}') # Reinitialize sound system with restored settings if 'soundDriver' in self.env['runtime']: @@ -36,8 +36,8 @@ class command(): soundDriver = self.env['runtime']['soundDriver'] soundDriver.shutdown() soundDriver.initialize(self.env) - except: - pass + except Exception as e: + print(f'revert_to_saved soundDriver: Error reinitializing sound driver: {str(e)}') self.env['runtime']['outputManager'].presentText("Successfully reverted to saved settings", interrupt=False, flush=False) self.env['runtime']['outputManager'].presentText("All temporary changes have been discarded", interrupt=False, flush=False) diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/show_unsaved_changes.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/show_unsaved_changes.py index dd9e9e45..6af2aae5 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/show_unsaved_changes.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/management/show_unsaved_changes.py @@ -38,7 +38,7 @@ class command(): runtimeValue = runtimeSettings[section][option] try: fileValue = fileConfig[section][option] - except: + except Exception as e: fileValue = "" if runtimeValue != fileValue: diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_pitch.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_pitch.py index 8b13cafb..d0993a42 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_pitch.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_pitch.py @@ -17,7 +17,7 @@ class command(): try: # Get current pitch currentPitch = float(self.env['runtime']['settingsManager'].getSetting('speech', 'pitch')) - except: + except Exception as e: currentPitch = 0.5 # Present current pitch diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_rate.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_rate.py index cc2be4aa..e27b1515 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_rate.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_rate.py @@ -17,7 +17,7 @@ class command(): try: # Get current rate currentRate = float(self.env['runtime']['settingsManager'].getSetting('speech', 'rate')) - except: + except Exception as e: currentRate = 0.5 # Present current rate diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_speech_rate.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_speech_rate.py index 376ef7f3..f9cd18de 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_speech_rate.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_speech_rate.py @@ -18,7 +18,7 @@ class command(): # Get current rate from runtime settings (may be temporary) settingsManager = self.env['runtime']['settingsManager'] currentRate = float(settingsManager.getSetting('speech', 'rate')) - except: + except Exception as e: currentRate = 0.5 # Present current rate @@ -38,8 +38,8 @@ class command(): if 'speechDriver' in self.env['runtime']: try: self.env['runtime']['speechDriver'].setRate(newRate) - except: - pass + except Exception as e: + print(f'adjust_speech_rate setRate: Error setting speech rate: {str(e)}') newPercent = int(newRate * 100) self.env['runtime']['outputManager'].presentText(f"Speech rate temporarily set to {newPercent} percent", interrupt=False, flush=False) diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_volume.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_volume.py index 360bfafa..14accb4a 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_volume.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/adjust_volume.py @@ -17,7 +17,7 @@ class command(): try: # Get current volume currentVolume = float(self.env['runtime']['settingsManager'].getSetting('speech', 'volume')) - except: + except Exception as e: currentVolume = 1.0 # Present current volume diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/show_current_settings.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/show_current_settings.py index 5a8d6979..3d9c759d 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/show_current_settings.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/fenrir/speech/show_current_settings.py @@ -38,19 +38,19 @@ class command(): try: ratePercent = int(float(rate) * 100) self.env['runtime']['outputManager'].presentText(f"Rate: {ratePercent} percent", interrupt=True) - except: + except Exception as e: self.env['runtime']['outputManager'].presentText(f"Rate: {rate}", interrupt=True) try: pitchPercent = int(float(pitch) * 100) self.env['runtime']['outputManager'].presentText(f"Pitch: {pitchPercent} percent", interrupt=True) - except: + except Exception as e: self.env['runtime']['outputManager'].presentText(f"Pitch: {pitch}", interrupt=True) try: volumePercent = int(float(volume) * 100) self.env['runtime']['outputManager'].presentText(f"Volume: {volumePercent} percent", interrupt=True) - except: + except Exception as e: self.env['runtime']['outputManager'].presentText(f"Volume: {volume}", interrupt=True) self.env['runtime']['outputManager'].playSound('Accept') diff --git a/src/fenrirscreenreader/core/attributeManager.py b/src/fenrirscreenreader/core/attributeManager.py index b16992b7..43ced0d7 100644 --- a/src/fenrirscreenreader/core/attributeManager.py +++ b/src/fenrirscreenreader/core/attributeManager.py @@ -74,8 +74,8 @@ class attributeManager(): except KeyError: try: return self.defaultAttributes[0] - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('attributeManager getAttributeByXY: Error accessing default attributes: ' + str(e), debug.debugLevel.ERROR) return None def appendDefaultAttributes(self, attribute): if not attribute: @@ -214,12 +214,12 @@ class attributeManager(): try: try: attributeFormatString = attributeFormatString.replace('fenrirFontSize', int(attribute[8])) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('attributeManager formatAttributeToString: Error formatting font size as int: ' + str(e), debug.debugLevel.ERROR) try: attributeFormatString = attributeFormatString.replace('fenrirFontSize', str(attribute[8])) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('attributeManager formatAttributeToString: Error formatting font size as string: ' + str(e), debug.debugLevel.ERROR) except Exception as e: pass attributeFormatString = attributeFormatString.replace('fenrirFontSize', _('default')) diff --git a/src/fenrirscreenreader/core/byteManager.py b/src/fenrirscreenreader/core/byteManager.py index dccb3432..9d91158f 100644 --- a/src/fenrirscreenreader/core/byteManager.py +++ b/src/fenrirscreenreader/core/byteManager.py @@ -58,8 +58,8 @@ class byteManager(): return try: self.env['runtime']['debug'].writeDebugOut("handleByteInput " + eventData.decode('utf8') ,debug.debugLevel.INFO) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('byteManager handleByteInput: Error decoding byte data: ' + str(e), debug.debugLevel.ERROR) self.handleByteStream(eventData) def handleSingleByteSequence(self, eventData): convertedEscapeSequence = self.unifyEscapeSeq(eventData) @@ -140,7 +140,8 @@ class byteManager(): try: repeat = int(chr(cleanShortcut[0])) cleanShortcut = cleanShortcut[2:] - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('byteManager loadByteShortcuts: Error parsing repeat count: ' + str(e), debug.debugLevel.ERROR) repeat = 1 cleanShortcut = cleanShortcut shortcut = b'' diff --git a/src/fenrirscreenreader/core/commandManager.py b/src/fenrirscreenreader/core/commandManager.py index f126cb19..c23af755 100644 --- a/src/fenrirscreenreader/core/commandManager.py +++ b/src/fenrirscreenreader/core/commandManager.py @@ -84,11 +84,12 @@ class commandManager(): fileName = fileName.split('/')[-1] if fileName.startswith('__'): continue - try: - if self.env['commands'][section][fileName.upper()] != None: - continue - except: - pass + # Skip base classes that shouldn't be loaded as commands + if fileName.endswith('_base'): + continue + # Check if command already exists to prevent duplicate loading + if fileName.upper() in self.env['commands'][section] and self.env['commands'][section][fileName.upper()] is not None: + continue if fileExtension.lower() == '.py': command_mod = module_utils.importModule(fileName, command) self.env['commands'][section][fileName.upper()] = command_mod.command() @@ -249,7 +250,7 @@ class commandManager(): def commandExists(self, command, section = 'commands'): try: return( command in self.env['commands'][section]) - except: + except Exception as e: return False def getShortcutForCommand(self, command, formatKeys = False): shortcut = [] @@ -282,6 +283,6 @@ class commandManager(): formattedKey = formattedKey.replace('key_kp', ' keypad ') formattedKey = formattedKey.replace('key_', ' ') shortcut.append(formattedKey) - except: + except Exception as e: pass return shortcut diff --git a/src/fenrirscreenreader/core/cursorManager.py b/src/fenrirscreenreader/core/cursorManager.py index 81527188..69f10073 100644 --- a/src/fenrirscreenreader/core/cursorManager.py +++ b/src/fenrirscreenreader/core/cursorManager.py @@ -87,8 +87,8 @@ class cursorManager(): currApp = self.env['runtime']['applicationManager'].getCurrentApplication() if self.env['commandBuffer']['windowArea'][currApp]['1'] != None: return True - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('cursorManager isApplicationWindowSet: Error checking window area: ' + str(e), debug.debugLevel.ERROR) return False def setWindowForApplication(self, start = None, end = None): x1 = 0 @@ -129,6 +129,7 @@ class cursorManager(): currApp = self.env['runtime']['applicationManager'].getCurrentApplication() try: del self.env['commandBuffer']['windowArea'][currApp] - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('cursorManager clearWindowForApplication: Error clearing window area: ' + str(e), debug.debugLevel.ERROR) return False return True diff --git a/src/fenrirscreenreader/core/debugManager.py b/src/fenrirscreenreader/core/debugManager.py index 31a1aa69..42d7d417 100644 --- a/src/fenrirscreenreader/core/debugManager.py +++ b/src/fenrirscreenreader/core/debugManager.py @@ -19,8 +19,9 @@ class debugManager(): def __del__(self): try: self.shutdown() - except: - pass + except Exception as e: + # Cannot use debug manager to log its own shutdown errors + print(f'debugManager shutdown: Error during shutdown: {e}') def openDebugFile(self, fileName = ''): self._fileOpened = False diff --git a/src/fenrirscreenreader/core/dynamicVoiceMenu.py b/src/fenrirscreenreader/core/dynamicVoiceMenu.py index d23968a5..19610aa6 100644 --- a/src/fenrirscreenreader/core/dynamicVoiceMenu.py +++ b/src/fenrirscreenreader/core/dynamicVoiceMenu.py @@ -159,8 +159,8 @@ class DynamicApplyVoiceCommand: speechDriver = self.env['runtime']['speechDriver'] speechDriver.shutdown() speechDriver.initialize(self.env) - except: - pass # If this fails, at least we tried + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('dynamicVoiceMenu: Error reinitializing speech driver: ' + str(e), debug.debugLevel.ERROR) self.env['runtime']['outputManager'].presentText(f"Failed to apply voice, reverted: {str(e)}", interrupt=False, flush=False) diff --git a/src/fenrirscreenreader/core/eventManager.py b/src/fenrirscreenreader/core/eventManager.py index 2d866b3e..94ae82cc 100644 --- a/src/fenrirscreenreader/core/eventManager.py +++ b/src/fenrirscreenreader/core/eventManager.py @@ -15,7 +15,7 @@ from ctypes import c_bool class eventManager(): def __init__(self): self.running = Value(c_bool, True) - self._eventQueue = Queue() # multiprocessing.Queue() + self._eventQueue = Queue(maxsize=100) # Bounded queue to prevent memory exhaustion self.cleanEventQueue() def initialize(self, environment): self.env = environment @@ -85,8 +85,16 @@ class eventManager(): return False if event == fenrirEventType.Ignore: return False - if self.getEventQueueSize() > 50: - if not event in [fenrirEventType.ScreenUpdate, fenrirEventType.HeartBeat]: - self.cleanEventQueue() - self._eventQueue.put({"Type":event,"Data":data}) + # Use bounded queue - if full, this will block briefly or drop older events + try: + self._eventQueue.put({"Type":event,"Data":data}, timeout=0.1) + except Exception as e: + # Queue full - drop oldest event and add new one for critical events + if event in [fenrirEventType.ScreenUpdate, fenrirEventType.KeyboardInput]: + try: + self._eventQueue.get_nowait() # Remove oldest + self._eventQueue.put({"Type":event,"Data":data}, timeout=0.1) + except: + pass # If still can't add, drop the event + # For non-critical events, just drop them if queue is full return True diff --git a/src/fenrirscreenreader/core/fenrirManager.py b/src/fenrirscreenreader/core/fenrirManager.py index 6e68b64c..9f320a9c 100644 --- a/src/fenrirscreenreader/core/fenrirManager.py +++ b/src/fenrirscreenreader/core/fenrirManager.py @@ -138,8 +138,8 @@ class fenrirManager(): def handlePlugInputDevice(self, event): try: self.environment['runtime']['inputManager'].setLastDetectedDevices(event['Data']) - except: - pass + except Exception as e: + self.environment['runtime']['debug'].writeDebugOut('handlePlugInputDevice: Error setting last detected devices: ' + str(e), debug.debugLevel.ERROR) self.environment['runtime']['inputManager'].handlePlugInputDevice(event['Data']) self.environment['runtime']['commandManager'].executeDefaultTrigger('onPlugInputDevice', force=True) self.environment['runtime']['inputManager'].setLastDetectedDevices(None) @@ -200,16 +200,16 @@ class fenrirManager(): stringBuffer.value = bytes(name, 'UTF-8') libc.prctl(15, byref(stringBuffer), 0, 0, 0) return True - except: - pass + except Exception as e: + self.environment['runtime']['debug'].writeDebugOut('setProcName: Error setting process name: ' + str(e), debug.debugLevel.ERROR) return False def shutdownRequest(self): try: self.environment['runtime']['eventManager'].stopMainEventLoop() - except: - pass + except Exception as e: + self.environment['runtime']['debug'].writeDebugOut('shutdownRequest: Error stopping main event loop: ' + str(e), debug.debugLevel.ERROR) def captureSignal(self, sigInit, frame): self.shutdownRequest() diff --git a/src/fenrirscreenreader/core/helpManager.py b/src/fenrirscreenreader/core/helpManager.py index 2697a174..e28b5671 100755 --- a/src/fenrirscreenreader/core/helpManager.py +++ b/src/fenrirscreenreader/core/helpManager.py @@ -30,8 +30,8 @@ class helpManager(): else: try: self.env['bindings'] = self.env['runtime']['settingsManager'].getBindingBackup() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('helpManager setTutorialMode: Error restoring binding backup: ' + str(e), debug.debugLevel.ERROR) def isTutorialMode(self): return self.env['general']['tutorialMode'] def getFormattedShortcutForCommand(self, command): diff --git a/src/fenrirscreenreader/core/inputDriver.py b/src/fenrirscreenreader/core/inputDriver.py index 6529eec2..9f9c5460 100644 --- a/src/fenrirscreenreader/core/inputDriver.py +++ b/src/fenrirscreenreader/core/inputDriver.py @@ -76,5 +76,5 @@ class inputDriver(): return try: self.removeAllDevices() - except: - pass + except Exception as e: + print(f'inputDriver __del__: Error removing devices: {str(e)}') diff --git a/src/fenrirscreenreader/core/inputManager.py b/src/fenrirscreenreader/core/inputManager.py index ee7d6e67..fef1e300 100644 --- a/src/fenrirscreenreader/core/inputManager.py +++ b/src/fenrirscreenreader/core/inputManager.py @@ -44,8 +44,8 @@ class inputManager(): event = None try: event = self.env['runtime']['inputDriver'].getInputEvent() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('inputManager getInputEvent: Error getting input event: ' + str(e), debug.debugLevel.ERROR) return event def setExecuteDeviceGrab(self, newExecuteDeviceGrab = True): self.executeDeviceGrab = newExecuteDeviceGrab @@ -77,8 +77,8 @@ class inputManager(): # Force a release of devices if possible through alternative means try: self.env['runtime']['inputDriver'].forceUngrab() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('inputManager handleDeviceGrab: Error forcing ungrab: ' + str(e), debug.debugLevel.ERROR) break time.sleep(0.25) self.env['runtime']['debug'].writeDebugOut(f"retry ungrabAllDevices {retryCount}/{maxRetries}", debug.debugLevel.WARNING) @@ -170,8 +170,8 @@ class inputManager(): self.env['runtime']['inputDriver'].toggleLedState(1) elif mEvent['EventName'] == 'KEY_SCROLLLOCK': self.env['runtime']['inputDriver'].toggleLedState(2) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('inputManager handleLedStates: Error toggling LED state: ' + str(e), debug.debugLevel.ERROR) def grabAllDevices(self): if self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): try: @@ -192,18 +192,18 @@ class inputManager(): def updateInputDevices(self, newDevice = None): try: self.env['runtime']['inputDriver'].updateInputDevices(newDevice) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('inputManager updateInputDevices: Error updating input devices: ' + str(e), debug.debugLevel.ERROR) try: if self.env['runtime']['screenManager']: self.handleDeviceGrab(force = True) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('inputManager updateInputDevices: Error handling device grab: ' + str(e), debug.debugLevel.ERROR) def removeAllDevices(self): try: self.env['runtime']['inputDriver'].removeAllDevices() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('inputManager removeAllDevices: Error removing devices: ' + str(e), debug.debugLevel.ERROR) def convertEventName(self, eventName): if not eventName: @@ -364,7 +364,7 @@ class inputManager(): for key in keys: try: shortcutRepeat = int(key) - except: + except Exception as e: if not self.isValidKey(key.upper()): self.env['runtime']['debug'].writeDebugOut("invalid key : "+ key.upper() + ' command:' +commandName ,debug.debugLevel.WARNING) invalid = True diff --git a/src/fenrirscreenreader/core/memoryManager.py b/src/fenrirscreenreader/core/memoryManager.py index 0798539c..b6c21f5e 100644 --- a/src/fenrirscreenreader/core/memoryManager.py +++ b/src/fenrirscreenreader/core/memoryManager.py @@ -30,11 +30,12 @@ class memoryManager(): if not self.listStorageValid(name): return if self.listStorage[name]['maxLength'] == None: - self.listStorage[name]['list'] = [value] + self.listStorage[name]['list'] + # Fallback: if maxLength is still None, apply default limit of 1000 + self.listStorage[name]['list'] = [value] + self.listStorage[name]['list'][:999] else: self.listStorage[name]['list'] = [value] + self.listStorage[name]['list'][:self.listStorage[name]['maxLength'] -1] self.listStorage[name]['index'] = 0 - def addIndexList(self, name, maxLength = None, currList = [], currIndex = -1): + def addIndexList(self, name, maxLength = 1000, currList = [], currIndex = -1): if len(currList) != 0 and (currIndex == -1): currIndex = 0 self.listStorage[name] = {'list': currList, 'index': currIndex, 'maxLength': maxLength} @@ -102,7 +103,8 @@ class memoryManager(): return False try: return self.listStorage[name]['index'] - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('memoryManager getCurrentIndex: Error accessing index for ' + name + ': ' + str(e), debug.debugLevel.ERROR) return -1 def isIndexListEmpty(self, name): if not self.listStorageValid(name): @@ -119,5 +121,6 @@ class memoryManager(): return None try: return self.listStorage[name]['list'][currIndex] - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('memoryManager getIndexListElement: Error accessing element for ' + name + ': ' + str(e), debug.debugLevel.ERROR) return None diff --git a/src/fenrirscreenreader/core/outputManager.py b/src/fenrirscreenreader/core/outputManager.py index b1651aae..a5469448 100644 --- a/src/fenrirscreenreader/core/outputManager.py +++ b/src/fenrirscreenreader/core/outputManager.py @@ -111,8 +111,8 @@ class outputManager(): try: self.env['runtime']['speechDriver'].cancel() self.env['runtime']['debug'].writeDebugOut("Interrupt speech", debug.debugLevel.INFO) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('outputManager interruptOutput: Error canceling speech: ' + str(e), debug.debugLevel.ERROR) def playSoundIcon(self, soundIcon='', interrupt=True): if soundIcon == '': @@ -124,8 +124,8 @@ class outputManager(): try: e = self.env['soundIcons'][soundIcon] - except: - self.env['runtime']['debug'].writeDebugOut("SoundIcon doesnt exist: " + soundIcon, debug.debugLevel.WARNING) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('outputManager playSoundIcon: SoundIcon does not exist ' + soundIcon + ': ' + str(e), debug.debugLevel.WARNING) return False if self.env['runtime']['soundDriver'] == None: @@ -166,8 +166,8 @@ class outputManager(): adjustVolume = 0.0 try: adjustVolume = 1.0 - (frequence / 20000) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('outputManager playFrequence: Error calculating adjust volume: ' + str(e), debug.debugLevel.ERROR) if adjustVolume > 9.0: adjustVolume = 9.0 diff --git a/src/fenrirscreenreader/core/processManager.py b/src/fenrirscreenreader/core/processManager.py index 115b8f87..c75eee78 100644 --- a/src/fenrirscreenreader/core/processManager.py +++ b/src/fenrirscreenreader/core/processManager.py @@ -29,14 +29,14 @@ class processManager(): # pass #except: # pass - proc.join() + proc.join(timeout=5.0) # Timeout to prevent hanging shutdown for t in self._Threads: - t.join() + t.join(timeout=5.0) # Timeout to prevent hanging shutdown def heartBeatTimer(self, active): try: time.sleep(0.5) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('processManager heartBeatTimer: Error during sleep: ' + str(e), debug.debugLevel.ERROR) return time.time() def addCustomEventThread(self, function, pargs = None, multiprocess = False, runOnce = False): eventQueue = self.env['runtime']['eventManager'].getEventQueue() diff --git a/src/fenrirscreenreader/core/punctuationManager.py b/src/fenrirscreenreader/core/punctuationManager.py index 76806627..8f351ffe 100644 --- a/src/fenrirscreenreader/core/punctuationManager.py +++ b/src/fenrirscreenreader/core/punctuationManager.py @@ -31,7 +31,7 @@ class punctuationManager(): for char in currLevel: try: del currAllPunctNone[ord(char)] - except: + except Exception as e: pass return text.translate(currAllPunctNone) def useCustomDict(self, text, customDict, seperator=''): @@ -84,7 +84,7 @@ class punctuationManager(): punctList = list(self.env['punctuation']['LEVELDICT'].keys()) try: currIndex = punctList.index(self.env['runtime']['settingsManager'].getSetting('general', 'punctuationLevel').lower()) # curr punctuation - except: + except Exception as e: return False currIndex += 1 if currIndex >= len(punctList): diff --git a/src/fenrirscreenreader/core/quickMenuManager.py b/src/fenrirscreenreader/core/quickMenuManager.py index 349107ac..da924d9c 100644 --- a/src/fenrirscreenreader/core/quickMenuManager.py +++ b/src/fenrirscreenreader/core/quickMenuManager.py @@ -30,7 +30,8 @@ class quickMenuManager(): continue try: t = self.settings[entry[0]][entry[1]] - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('quickMenuManager loadMenu: Setting not found ' + str(entry) + ': ' + str(e), debug.debugLevel.ERROR) print(entry[0],entry[1], 'not found') continue self.quickMenu.append({'section': entry[0], 'setting': entry[1]}) @@ -56,7 +57,8 @@ class quickMenuManager(): valueString = '' try: valueString = self.env['runtime']['settingsManager'].getSetting(section, setting) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('quickMenuManager nextValue: Error getting setting value: ' + str(e), debug.debugLevel.ERROR) return False try: @@ -89,7 +91,8 @@ class quickMenuManager(): valueString = '' try: valueString = self.env['runtime']['settingsManager'].getSetting(section, setting) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('quickMenuManager prevValue: Error getting setting value: ' + str(e), debug.debugLevel.ERROR) return False try: if isinstance(self.settings[section][setting], str): @@ -120,12 +123,14 @@ class quickMenuManager(): return '' try: return _(self.quickMenu[self.position]['section']) + ' ' + _(self.quickMenu[self.position]['setting']) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('quickMenuManager getCurrentEntry: Error formatting entry: ' + str(e), debug.debugLevel.ERROR) return _('setting invalid') def getCurrentValue(self): if len(self.quickMenu) == 0: return '' try: return self.env['runtime']['settingsManager'].getSetting(self.quickMenu[self.position]['section'], self.quickMenu[self.position]['setting']) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('quickMenuManager getCurrentValue: Error getting setting value: ' + str(e), debug.debugLevel.ERROR) return _('setting value invalid') diff --git a/src/fenrirscreenreader/core/remoteManager.py b/src/fenrirscreenreader/core/remoteManager.py index 6c0ba400..af2850a2 100644 --- a/src/fenrirscreenreader/core/remoteManager.py +++ b/src/fenrirscreenreader/core/remoteManager.py @@ -252,7 +252,7 @@ class remoteManager(): else: clipboardFile.write('') clipboardFile.close() - os.chmod(clipboardFilePath, 0o666) + os.chmod(clipboardFilePath, 0o644) 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) diff --git a/src/fenrirscreenreader/core/screenManager.py b/src/fenrirscreenreader/core/screenManager.py index 1341aa98..48f004b5 100644 --- a/src/fenrirscreenreader/core/screenManager.py +++ b/src/fenrirscreenreader/core/screenManager.py @@ -41,13 +41,13 @@ class screenManager(): def getCurrScreen(self): try: self.env['runtime']['screenDriver'].getCurrScreen() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('screenManager getCurrScreen: Error getting current screen: ' + str(e), debug.debugLevel.ERROR) def getSessionInformation(self): try: self.env['runtime']['screenDriver'].getSessionInformation() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('screenManager getSessionInformation: Error getting session info: ' + str(e), debug.debugLevel.ERROR) def shutdown(self): self.env['runtime']['settingsManager'].shutdownDriver('screenDriver') def isCurrScreenIgnoredChanged(self): diff --git a/src/fenrirscreenreader/core/settingsManager.py b/src/fenrirscreenreader/core/settingsManager.py index 94801155..0cad86b5 100644 --- a/src/fenrirscreenreader/core/settingsManager.py +++ b/src/fenrirscreenreader/core/settingsManager.py @@ -94,7 +94,8 @@ class settingsManager(): self.env['settings'] = ConfigParser() try: self.env['settings'].read(settingConfigPath) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('settingsManager loadSettings: Error reading config file: ' + str(e), debug.debugLevel.ERROR) return False self.setSettingsFile(settingConfigPath) return True @@ -112,7 +113,7 @@ class settingsManager(): configFile = open(settingConfigPath, 'w') self.env['settings'].write(configFile) configFile.close() - os.chmod(settingConfigPath, 0o666) + os.chmod(settingConfigPath, 0o644) except Exception as e: self.env['runtime']['debug'].writeDebugOut('saveSettings: save settingsfile:' + settingConfigPath + 'failed. Error:' + str(e), debug.debugLevel.ERROR) def setSetting(self, section, setting, value): @@ -124,11 +125,11 @@ class settingsManager(): try: value = self.settingArgDict[section][setting] return value - except: + except Exception as e: pass try: value = self.env['settings'].get(section, setting) - except: + except Exception as e: value = str(self.settings[section][setting]) return value @@ -141,7 +142,7 @@ class settingsManager(): pass try: value = self.env['settings'].getint(section, setting) - except: + except Exception as e: value = self.settings[section][setting] return value @@ -154,7 +155,7 @@ class settingsManager(): pass try: value = self.env['settings'].getfloat(section, setting) - except: + except Exception as e: value = self.settings[section][setting] return value @@ -167,15 +168,16 @@ class settingsManager(): pass try: value = self.env['settings'].getboolean(section, setting) - except: + except Exception as e: value = self.settings[section][setting] return value def loadDriver(self, driverName, driverType): try: - self.env['runtime'][driverType].shutdown(self.env) - except: - pass + if self.env['runtime'][driverType] is not None: + self.env['runtime'][driverType].shutdown(self.env) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('settingsManager loadDriver: Error shutting down driver: ' + str(e), debug.debugLevel.ERROR) try: driver_mod = module_utils.importModule(driverName, fenrirPath + "/" + driverType + '/' + driverName + '.py') @@ -223,7 +225,7 @@ class settingsManager(): self.settingArgDict[section] = {} try: t = self.settings[section][setting] - except: + except Exception as e: print(section,setting, 'not found') return try: diff --git a/src/fenrirscreenreader/core/vmenuManager.py b/src/fenrirscreenreader/core/vmenuManager.py index e3712e74..bf7fef43 100755 --- a/src/fenrirscreenreader/core/vmenuManager.py +++ b/src/fenrirscreenreader/core/vmenuManager.py @@ -147,8 +147,8 @@ class vmenuManager(): try: self.currIndex = None self.env['bindings'] = self.env['runtime']['settingsManager'].getBindingBackup() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vmenuManager setActive: Error loading binding backup: ' + str(e), debug.debugLevel.ERROR) def createMenuTree(self, resetIndex = True): if resetIndex: self.currIndex = None @@ -169,7 +169,8 @@ class vmenuManager(): r = self.getValueByPath(self.menuDict, self.currIndex) if r == {}: self.currIndex = None - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vmenuManager createMenuTree: Error checking menu index validity: ' + str(e), debug.debugLevel.ERROR) self.currIndex = None def executeMenu(self): if self.currIndex == None: @@ -185,8 +186,8 @@ class vmenuManager(): self.incLevel() text = self.getCurrentEntry() self.env['runtime']['outputManager'].presentText(text, interrupt=True) - except: - print(e) + except Exception as ex: + self.env['runtime']['debug'].writeDebugOut('vmenuManager executeMenu: Error presenting menu text: ' + str(ex), debug.debugLevel.ERROR) def incLevel(self): if self.currIndex == None: @@ -195,7 +196,8 @@ class vmenuManager(): r = self.getValueByPath(self.menuDict, self.currIndex +[0]) if r == {}: return False - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vmenuManager incLevel: Error accessing menu path: ' + str(e), debug.debugLevel.ERROR) return False self.currIndex.append(0) return True @@ -267,6 +269,9 @@ class vmenuManager(): fileName = fileName.split('/')[-1] if fileName.startswith('__'): continue + # Skip base classes that shouldn't be loaded as commands + if fileName.endswith('_base'): + continue command = self.env['runtime']['commandManager'].loadFile(root + '/' + f) tree.update({fileName + ' ' + _('Action'): command}) except Exception as e: diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index ab765bc3..dc09f503 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.06.20" +version = "2025.06.27" codeName = "master" diff --git a/src/fenrirscreenreader/inputDriver/evdevDriver.py b/src/fenrirscreenreader/inputDriver/evdevDriver.py index 3c880cf6..21a970ca 100644 --- a/src/fenrirscreenreader/inputDriver/evdevDriver.py +++ b/src/fenrirscreenreader/inputDriver/evdevDriver.py @@ -126,7 +126,8 @@ class driver(inputDriver): try: event = device.read_one() - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver handleInputEvent: Error reading event: ' + str(e), debug.debugLevel.ERROR) self.removeDevice(fd) continue while(event): @@ -166,8 +167,8 @@ class driver(inputDriver): if uDevice: if self.gDevices[iDevice.fd]: self.writeUInput(uDevice, event) - except Exception: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver writeEventBuffer: Error writing event: ' + str(e), debug.debugLevel.ERROR) def writeUInput(self, uDevice, event): if not self._initialized: @@ -211,14 +212,16 @@ class driver(inputDriver): try: with open(deviceFile) as f: pass - except Exception: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver updateInputDevices: Error opening device file: ' + str(e), debug.debugLevel.ERROR) continue # 3 pos absolute # 2 pos relative # 1 Keys try: currDevice = evdev.InputDevice(deviceFile) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver createDeviceType: Error creating device: ' + str(e), debug.debugLevel.ERROR) continue try: # FIX: Check if attributes exist before accessing them @@ -228,8 +231,8 @@ class driver(inputDriver): continue if hasattr(currDevice, 'name') and currDevice.name and 'BRLTTY' in currDevice.name.upper(): continue - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver: Error checking device capabilities: ' + str(e), debug.debugLevel.ERROR) cap = currDevice.capabilities() if mode in ['ALL', 'NOMICE']: if eventType.EV_KEY in cap: @@ -257,8 +260,8 @@ class driver(inputDriver): try: device_name = currDevice.name if hasattr(currDevice, 'name') else "unknown" self.env['runtime']['debug'].writeDebugOut("Device Skipped (Exception): " + deviceFile + ' ' + device_name + ' ' + str(e), debug.debugLevel.INFO) - except: - self.env['runtime']['debug'].writeDebugOut("Device Skipped (Exception): " + deviceFile + ' ' + str(e), debug.debugLevel.INFO) + except Exception as ex: + self.env['runtime']['debug'].writeDebugOut("Device Skipped (Exception): " + deviceFile + ' ' + str(ex), debug.debugLevel.INFO) self.iDeviceNo = len(evdev.list_devices()) self.updateMPiDevicesFD() @@ -270,8 +273,8 @@ class driver(inputDriver): for fd in self.iDevicesFD: if not fd in self.iDevices: self.iDevicesFD.remove(fd) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver updateMPiDevicesFD: Error updating device file descriptors: ' + str(e), debug.debugLevel.ERROR) def mapEvent(self, event): if not self._initialized: @@ -294,7 +297,7 @@ class driver(inputDriver): mEvent['EventState'] = event.value mEvent['EventType'] = event.type return mEvent - except Exception: + except Exception as e: return None def getLedState(self, led=0): @@ -379,16 +382,16 @@ class driver(inputDriver): # if it doesnt work clean up try: del(self.iDevices[newDevice.fd]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error removing iDevice: ' + str(e), debug.debugLevel.ERROR) try: del(self.uDevices[newDevice.fd]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error removing uDevice: ' + str(e), debug.debugLevel.ERROR) try: del(self.gDevices[newDevice.fd]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver addDevice: Error cleaning up gDevice: ' + str(e), debug.debugLevel.ERROR) def grabDevice(self, fd): if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'): @@ -447,33 +450,33 @@ class driver(inputDriver): with self._deviceLock: try: self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: device removed: ' + str(fd) + ' ' + str(self.iDevices[fd]), debug.debugLevel.INFO) - except: - self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: device removed: ' + str(fd), debug.debugLevel.INFO) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: device removed: ' + str(fd) + ' Error: ' + str(e), debug.debugLevel.INFO) self.clearEventBuffer() try: self.ungrabDevice(fd) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error ungrabbing device ' + str(fd) + ': ' + str(e), debug.debugLevel.ERROR) try: self.iDevices[fd].close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error closing iDevice ' + str(fd) + ': ' + str(e), debug.debugLevel.ERROR) try: self.uDevices[fd].close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error closing uDevice ' + str(fd) + ': ' + str(e), debug.debugLevel.ERROR) try: del(self.iDevices[fd]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error deleting iDevice ' + str(fd) + ': ' + str(e), debug.debugLevel.ERROR) try: del(self.uDevices[fd]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error deleting uDevice ' + str(fd) + ': ' + str(e), debug.debugLevel.ERROR) try: del(self.gDevices[fd]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver removeDevice: Error deleting gDevice ' + str(fd) + ': ' + str(e), debug.debugLevel.ERROR) self.updateMPiDevicesFD() def hasIDevices(self): @@ -491,8 +494,8 @@ class driver(inputDriver): try: self.UInputinject.write(e.EV_KEY, e.ecodes[key], state) self.UInputinject.syn() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('evdevDriver sendKey: Error sending key ' + str(key) + ': ' + str(e), debug.debugLevel.ERROR) def removeAllDevices(self): if not self.hasIDevices(): diff --git a/src/fenrirscreenreader/remoteDriver/tcpDriver.py b/src/fenrirscreenreader/remoteDriver/tcpDriver.py index a65a09b1..3073c9ec 100644 --- a/src/fenrirscreenreader/remoteDriver/tcpDriver.py +++ b/src/fenrirscreenreader/remoteDriver/tcpDriver.py @@ -35,19 +35,19 @@ class driver(remoteDriver): client_sock, client_addr = self.fenrirSock.accept() try: rawdata = client_sock.recv(8129) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('tcpDriver watchDog: Error receiving data from client: ' + str(e), debug.debugLevel.ERROR) try: data = rawdata.decode("utf-8").rstrip().lstrip() eventQueue.put({"Type":fenrirEventType.RemoteIncomming, "Data": data }) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('tcpDriver watchDog: Error decoding/queuing data: ' + str(e), debug.debugLevel.ERROR) try: client_sock.close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('tcpDriver watchDog: Error closing client socket: ' + str(e), debug.debugLevel.ERROR) if self.fenrirSock: self.fenrirSock.close() self.fenrirSock = None diff --git a/src/fenrirscreenreader/remoteDriver/unixDriver.py b/src/fenrirscreenreader/remoteDriver/unixDriver.py index 89022c10..a56724ce 100644 --- a/src/fenrirscreenreader/remoteDriver/unixDriver.py +++ b/src/fenrirscreenreader/remoteDriver/unixDriver.py @@ -22,8 +22,8 @@ class driver(remoteDriver): socketFile = '' try: socketFile = self.env['runtime']['settingsManager'].getSetting('remote', 'socketFile') - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('unixDriver watchDog: Error getting socket file setting: ' + str(e), debug.debugLevel.ERROR) if socketFile == '': if self.env['runtime']['settingsManager'].getSetting('screen', 'driver') =='vcsaDriver': socketFile = '/tmp/fenrirscreenreader-deamon.sock' @@ -45,21 +45,27 @@ class driver(remoteDriver): continue if self.fenrirSock in r: client_sock, client_addr = self.fenrirSock.accept() - try: - rawdata = client_sock.recv(8129) - except: - pass - try: - data = rawdata.decode("utf-8").rstrip().lstrip() - eventQueue.put({"Type":fenrirEventType.RemoteIncomming, - "Data": data - }) - except: - pass - try: - client_sock.close() - except: - pass + # Ensure client socket is always closed to prevent resource leaks + try: + try: + rawdata = client_sock.recv(8129) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('unixDriver watchDog: Error receiving data from client: ' + str(e), debug.debugLevel.ERROR) + rawdata = b'' # Set default empty data if recv fails + + try: + data = rawdata.decode("utf-8").rstrip().lstrip() + eventQueue.put({"Type":fenrirEventType.RemoteIncomming, + "Data": data + }) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('unixDriver watchDog: Error decoding/queuing data: ' + str(e), debug.debugLevel.ERROR) + finally: + # Always close client socket, even if data processing fails + try: + client_sock.close() + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('unixDriver watchDog: Error closing client socket: ' + str(e), debug.debugLevel.ERROR) if self.fenrirSock: self.fenrirSock.close() self.fenrirSock = None diff --git a/src/fenrirscreenreader/screenDriver/ptyDriver.py b/src/fenrirscreenreader/screenDriver/ptyDriver.py index 3bd6ec2a..8bf541e5 100644 --- a/src/fenrirscreenreader/screenDriver/ptyDriver.py +++ b/src/fenrirscreenreader/screenDriver/ptyDriver.py @@ -40,7 +40,9 @@ class Terminal: for y in lines: try: t = self.attributes[y] - except: + except Exception as e: + # Terminal class doesn't have access to env, use fallback logging + print(f'ptyDriver Terminal updateAttributes: Error accessing attributes: {e}') self.attributes.append([]) self.attributes[y] = [list(attribute[1:]) + [False, 'default', 'default'] for attribute in (buffer[y].values())] @@ -135,7 +137,9 @@ class driver(screenDriver): try: if env["TERM"] == '': env["TERM"] = 'linux' - except: + except Exception as e: + # Child process doesn't have access to env, use fallback logging + print(f'ptyDriver spawnTerminal: Error checking TERM environment: {e}') env["TERM"] = 'linux' os.execvpe(argv[0], argv, env) # File-like object for I/O with the child process aka command. @@ -184,7 +188,8 @@ class driver(screenDriver): if self.shortcutType == 'KEY': try: self.injectTextToScreen(msgBytes) - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('ptyDriver getInputData: Error injecting text to screen: ' + str(e), debug.debugLevel.ERROR) eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None}) break else: diff --git a/src/fenrirscreenreader/screenDriver/vcsaDriver.py b/src/fenrirscreenreader/screenDriver/vcsaDriver.py index a0add6ed..351f293a 100644 --- a/src/fenrirscreenreader/screenDriver/vcsaDriver.py +++ b/src/fenrirscreenreader/screenDriver/vcsaDriver.py @@ -38,8 +38,8 @@ class driver(screenDriver): # set workaround for paste clipboard -> injectTextToScreen subprocess.run(['sysctl', 'dev.tty.legacy_tiocsti=1'], check=False, capture_output=True, timeout=5) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error running fgconsole: ' + str(e), debug.debugLevel.ERROR) def initialize(self, environment): self.env = environment self.env['runtime']['attributeManager'].appendDefaultAttributes([ @@ -105,7 +105,8 @@ class driver(screenDriver): file.seek(0) try: d = file.read() - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver getScreenText: Error reading file: ' + str(e), debug.debugLevel.ERROR) file.seek(0) while True: # Read from file @@ -162,19 +163,19 @@ class driver(screenDriver): if currScreen != oldScreen: try: watchdog.unregister(vcsa[oldScreen]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error unregistering watchdog: ' + str(e), debug.debugLevel.ERROR) try: watchdog.register(vcsa[currScreen], select.POLLPRI | select.POLLERR) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error registering watchdog: ' + str(e), debug.debugLevel.ERROR) self.updateCharMap(currScreen) oldScreen = currScreen try: vcsa[currScreen].seek(0) lastScreenContent = self.readFile(vcsa[currScreen]) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error reading screen content: ' + str(e), debug.debugLevel.ERROR) vcsuContent = None if useVCSU: vcsu[currScreen].seek(0) @@ -233,23 +234,23 @@ class driver(screenDriver): try: if watchdog: watchdog.close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error closing watchdog: ' + str(e), debug.debugLevel.ERROR) try: if tty: tty.close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error closing TTY: ' + str(e), debug.debugLevel.ERROR) for handle in vcsa.values(): try: handle.close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error closing VCSA handle: ' + str(e), debug.debugLevel.ERROR) for handle in vcsu.values(): try: handle.close() - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error closing VCSU handle: ' + str(e), debug.debugLevel.ERROR) def createScreenEventData(self, screen, vcsaContent, vcsuContent = None): eventData = { @@ -269,15 +270,15 @@ class driver(screenDriver): try: eventData['text'], eventData['attributes'] =\ self.autoDecodeVCSA(vcsaContent[4:], eventData['lines'], eventData['columns']) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver createScreenEventData: Error decoding VCSA content: ' + str(e), debug.debugLevel.ERROR) # VCSU seems to give b' ' instead of b'\x00\x00\x00' (tsp), deactivated until its fixed if vcsuContent != None: try: vcsuContentAsText = vcsuContent.decode('UTF-32') eventData['text'] = screen_utils.insertNewlines(vcsuContentAsText, eventData['columns']) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver createScreenEventData: Error decoding VCSU content: ' + str(e), debug.debugLevel.ERROR) return eventData.copy() def updateCharMap(self, screen): self.charmap = {} @@ -349,7 +350,8 @@ class driver(screenDriver): try: if sh & self.hichar: ch |= 0x100 - except: + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver autoDecodeVCSA: Error processing character: ' + str(e), debug.debugLevel.ERROR) ch = None if self.hichar == 0x100: attr >>= 1 @@ -364,8 +366,8 @@ class driver(screenDriver): bold = 1 #if (ink != 7) or (paper != 0): # print(ink,paper) - except: - pass + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('vcsaDriver autoDecodeVCSA: Error processing attributes: ' + str(e), debug.debugLevel.ERROR) try: lineText += self.charmap[ch] except KeyError: diff --git a/src/fenrirscreenreader/soundDriver/genericDriver.py b/src/fenrirscreenreader/soundDriver/genericDriver.py index c8badc5a..4d8e6200 100644 --- a/src/fenrirscreenreader/soundDriver/genericDriver.py +++ b/src/fenrirscreenreader/soundDriver/genericDriver.py @@ -62,6 +62,18 @@ class driver(soundDriver): return if self.soundType == 'file': self.proc.kill() + try: + self.proc.wait(timeout=1.0) # Wait for process to finish to prevent zombies + except subprocess.TimeoutExpired: + pass # Process already terminated + except Exception as e: + pass # Handle any other wait errors if self.soundType == 'frequence': self.proc.kill() + try: + self.proc.wait(timeout=1.0) # Wait for process to finish to prevent zombies + except subprocess.TimeoutExpired: + pass # Process already terminated + except Exception as e: + pass # Handle any other wait errors self.soundType = '' diff --git a/src/fenrirscreenreader/soundDriver/gstreamerDriver.py b/src/fenrirscreenreader/soundDriver/gstreamerDriver.py index 0450ff03..42926e37 100644 --- a/src/fenrirscreenreader/soundDriver/gstreamerDriver.py +++ b/src/fenrirscreenreader/soundDriver/gstreamerDriver.py @@ -57,6 +57,9 @@ class driver(soundDriver): return self.cancel() self.mainloop.quit() + # Wait for the GLib MainLoop thread to finish to prevent shutdown races + if hasattr(self, 'thread') and self.thread.is_alive(): + self.thread.join(timeout=2.0) # 2 second timeout to prevent hanging def _onPlayerMessage(self, bus, message): if not self._initialized: diff --git a/src/fenrirscreenreader/speechDriver/genericDriver.py b/src/fenrirscreenreader/speechDriver/genericDriver.py index 592aefe5..2fce657a 100644 --- a/src/fenrirscreenreader/speechDriver/genericDriver.py +++ b/src/fenrirscreenreader/speechDriver/genericDriver.py @@ -10,6 +10,7 @@ from threading import Thread, Lock from queue import Queue, Empty import shlex from subprocess import Popen +import subprocess from fenrirscreenreader.core.speechDriver import speechDriver class speakQueue(Queue): @@ -73,17 +74,28 @@ class driver(speechDriver): return self.clear_buffer() self.lock.acquire(True) - if self.proc: - try: - self.proc.terminate() - except Exception as e: - self.env['runtime']['debug'].writeDebugOut('speechDriver:Cancel:self.proc.terminate():' + str(e),debug.debugLevel.WARNING) + try: + if self.proc: try: - self.proc.kill() + self.proc.terminate() + # Wait for process to finish to prevent zombies + try: + self.proc.wait(timeout=1.0) + except subprocess.TimeoutExpired: + # If terminate didn't work, force kill + self.proc.kill() + self.proc.wait(timeout=1.0) except Exception as e: - self.env['runtime']['debug'].writeDebugOut('speechDriver:Cancel:self.proc.kill():' + str(e),debug.debugLevel.WARNING) - self.proc = None - self.lock.release() + self.env['runtime']['debug'].writeDebugOut('speechDriver:Cancel:self.proc.terminate():' + str(e),debug.debugLevel.WARNING) + try: + self.proc.kill() + self.proc.wait(timeout=1.0) # Wait after kill to prevent zombies + except Exception as e: + self.env['runtime']['debug'].writeDebugOut('speechDriver:Cancel:self.proc.kill():' + str(e),debug.debugLevel.WARNING) + self.proc = None + finally: + # Ensure lock is always released, even if process termination fails + self.lock.release() def setCallback(self, callback): print('SpeechDummyDriver: setCallback') diff --git a/src/fenrirscreenreader/utils/char_utils.py b/src/fenrirscreenreader/utils/char_utils.py index 425bad83..b812ffc2 100644 --- a/src/fenrirscreenreader/utils/char_utils.py +++ b/src/fenrirscreenreader/utils/char_utils.py @@ -117,7 +117,8 @@ def getPhonetic(currChar): if currChar.isupper(): phonChar = phonChar[0].upper() + phonChar[1:] return phonChar - except: + except Exception as e: + # Utility function, no env access - return fallback return currChar def presentCharForReview(env, char, interrupt=True, announceCapital=True, flush=False): diff --git a/src/fenrirscreenreader/utils/screen_utils.py b/src/fenrirscreenreader/utils/screen_utils.py index 715fe65f..1fb34fc3 100644 --- a/src/fenrirscreenreader/utils/screen_utils.py +++ b/src/fenrirscreenreader/utils/screen_utils.py @@ -58,8 +58,10 @@ def isValidShell(shell = ''): return False if not os.access(shell,os.X_OK): return False - except: - return False + except Exception as e: + # Utility function, no env access - use fallback logging + print(f'screen_utils isValidShell: Error checking shell {shell}: {e}') + return False return True def getShell(): @@ -67,13 +69,15 @@ def getShell(): shell = os.environ["FENRIRSHELL"] if isValidShell(shell): return shell - except: + except Exception as e: + # Utility function, no env access - continue silently pass try: shell = os.environ["SHELL"] if isValidShell(shell): return shell - except: + except Exception as e: + # Utility function, no env access - continue silently pass try: if os.access('/etc/passwd', os.R_OK): @@ -85,7 +89,8 @@ def getShell(): if username == getpass.getuser(): if isValidShell(shell): return shell - except: + except Exception as e: + # Utility function, no env access - continue silently pass if isValidShell('/bin/bash'): return '/bin/bash'