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"