def _loadBuffer(self): try: if log.isEnabledFor(log.DEBUG): startTime = time.time() self.VBufHandle=NVDAHelper.localLib.VBuf_createBuffer(self.rootNVDAObject.appModule.helperLocalBindingHandle,self.rootDocHandle,self.rootID,unicode(self.backendName)) if not self.VBufHandle: raise RuntimeError("Could not remotely create virtualBuffer") except: log.error("", exc_info=True) queueHandler.queueFunction(queueHandler.eventQueue, self._loadBufferDone, success=False) return if log.isEnabledFor(log.DEBUG): log.debug("Buffer load took %.3f sec, %d chars" % ( time.time() - startTime, NVDAHelper.localLib.VBuf_getTextLength(self.VBufHandle))) queueHandler.queueFunction(queueHandler.eventQueue, self._loadBufferDone)
def _loadConfig(self, fn, fileError=False): log.info(u"Loading config: {0}".format(fn)) profile = ConfigObj(fn, indent_type="\t", encoding="UTF-8", file_error=fileError) # Python converts \r\n to \n when reading files in Windows, so ConfigObj can't determine the true line ending. profile.newlines = "\r\n" profileCopy = deepcopy(profile) try: writeProfileFunc = self._writeProfileToFile if self._shouldWriteProfile else None profileUpgrader.upgrade(profile, self.validator, writeProfileFunc) except Exception as e: # Log at level info to ensure that the profile is logged. log.info(u"Config before schema update:\n%s" % profileCopy, exc_info=False) raise e # since profile settings are not yet imported we have to "peek" to see # if debug level logging is enabled. try: logLevelName = profile["general"]["loggingLevel"] except KeyError as e: logLevelName = None if log.isEnabledFor(log.DEBUG) or ( logLevelName and DEBUG >= logging.getLevelName(logLevelName)): # Log at level info to ensure that the profile is logged. log.info( u"Config loaded (after upgrade, and in the state it will be used by NVDA):\n{0}" .format(profile)) return profile
def _speechManagerDebug(msg, *args, **kwargs) -> None: """Log 'msg % args' with severity 'DEBUG' if speech manager logging is enabled. 'SpeechManager-' is prefixed to all messages to make searching the log easier. """ if not log.isEnabledFor(log.DEBUG) or not _shouldDoSpeechManagerLogging(): return log._log(log.DEBUG, f"SpeechManager- " + msg, args, **kwargs)
def _isSecureObjectWhileLockScreenActivated( obj: NVDAObjects.NVDAObject) -> bool: """ While Windows is locked, Windows 10 and 11 allow for object navigation outside of the lockscreen. @return: C{True} if the Windows 10/11 lockscreen is active and C{obj} is outside of the lockscreen. According to MS docs, "There is no function you can call to determine whether the workstation is locked." https://docs.microsoft.com/en-gb/windows/win32/api/winuser/nf-winuser-lockworkstation """ runningAppModules = appModuleHandler.runningTable.values() lockAppModule = next(filter(_isLockAppAndAlive, runningAppModules), None) if lockAppModule is None: return False # The LockApp process might be kept alive # So determine if it is active, check the foreground window foregroundHWND = winUser.getForegroundWindow() foregroundProcessId, _threadId = winUser.getWindowThreadProcessID( foregroundHWND) isLockAppForeground = foregroundProcessId == lockAppModule.processID isObjectOutsideLockApp = obj.appModule.processID != foregroundProcessId if isLockAppForeground and isObjectOutsideLockApp: if log.isEnabledFor(log.DEBUG): devInfo = '\n'.join(obj.devInfo) log.debug(f"Attempt at navigating to a secure object: {devInfo}") return True return False
def nvdaControllerInternal_logMessage(pid,tid,level,fileName,funcName,lineNo,message): if not log.isEnabledFor(level): return 0 from appModuleHandler import getAppNameFromProcessID codepath="RPC: %s, %s, %s, line %d"%(getAppNameFromProcessID(pid,includeExt=True),fileName,funcName, lineNo) log._log(level,message,[],codepath=codepath) return 0
def executeGesture(self, gesture): """Perform the action associated with a gesture. @param gesture: The gesture to execute. @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction script = gesture.script focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL or (focus.sleepMode and not getattr(script, 'allowInSleepMode', False)): raise NoInputGestureAction wasInSayAll=False if gesture.isModifier: if not self.lastModifierWasInSayAll: wasInSayAll=self.lastModifierWasInSayAll=sayAllHandler.isRunning() elif self.lastModifierWasInSayAll: wasInSayAll=True self.lastModifierWasInSayAll=False else: wasInSayAll=sayAllHandler.isRunning() if wasInSayAll: gesture.wasInSayAll=True speechEffect = gesture.speechEffectWhenExecuted if speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): queueHandler.queueFunction(queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE) if log.isEnabledFor(log.IO) and not gesture.isModifier: log.io("Input: %s" % gesture.logIdentifier) if self._captureFunc: try: if self._captureFunc(gesture) is False: return except: log.error("Error in capture function, disabling", exc_info=True) self._captureFunc = None if gesture.isModifier: raise NoInputGestureAction if config.conf["keyboard"]["speakCommandKeys"] and gesture.shouldReportAsCommand: queueHandler.queueFunction(queueHandler.eventQueue, speech.speakMessage, gesture.displayName) gesture.reportExtra() if script: scriptHandler.queueScript(script, gesture) return raise NoInputGestureAction
def louis_log(level, message): if not _isDebug(): return NVDALevel = LOUIS_TO_NVDA_LOG_LEVELS.get(level, log.DEBUG) if not log.isEnabledFor(NVDALevel): return message = message.decode("ASCII") codepath = "liblouis at internal log level %d" % level log._log(NVDALevel, message, [], codepath=codepath)
def executeGesture(self, gesture): """Perform the action associated with a gesture. @param gesture: The gesture to execute. @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction script = gesture.script focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL or ( focus.sleepMode and not getattr(script, 'allowInSleepMode', False)): raise NoInputGestureAction speechEffect = gesture.speechEffectWhenExecuted if speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): queueHandler.queueFunction( queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE) if log.isEnabledFor(log.IO) and not gesture.isModifier: log.io("Input: %s" % gesture.logIdentifier) if self.isInputHelpActive: bypass = getattr(script, "bypassInputHelp", False) queueHandler.queueFunction(queueHandler.eventQueue, self._handleInputHelp, gesture, onlyLog=bypass) if not bypass: return if gesture.isModifier: raise NoInputGestureAction if config.conf["keyboard"][ "speakCommandKeys"] and gesture.shouldReportAsCommand: queueHandler.queueFunction(queueHandler.eventQueue, speech.speakMessage, gesture.displayName) gesture.reportExtra() if script: scriptHandler.queueScript(script, gesture) return raise NoInputGestureAction
def logTimeSinceInput(): """Log the time since the last input was received. This does nothing if time since input logging is disabled. """ if (not log.isEnabledFor(log.IO) or not config.conf["debugLog"]["timeSinceInput"] or not manager or not manager._lastInputTime): return log.io("%.3f sec since input" % (time.time() - manager._lastInputTime))
def nvdaControllerInternal_logMessage(level,pid,message): if not log.isEnabledFor(level): return 0 if pid: from appModuleHandler import getAppNameFromProcessID codepath="RPC process %s (%s)"%(pid,getAppNameFromProcessID(pid,includeExt=True)) else: codepath="NVDAHelperLocal" log._log(level,message,[],codepath=codepath) return 0
def logTimeSinceInput(): """Log the time since the last input was received. This does nothing if time since input logging is disabled. """ if (not log.isEnabledFor(log.IO) or not config.conf["debugLog"]["timeSinceInput"] or not manager or not manager._lastInputTime ): return log.io("%.3f sec since input" % (time.time() - manager._lastInputTime))
def _speechManagerUnitTest(msg, *args, **kwargs) -> None: """Log 'msg % args' with severity 'DEBUG' if . 'SpeechManUnitTest-' is prefixed to all messages to make searching the log easier. When """ if not IS_UNIT_TEST_LOG_ENABLED: # Don't reuse _speechManagerDebug, it leads to incorrect function names in the log (all # SpeechManager debug logging appears to come from _speechManagerUnitTest instead of the frame # one stack higher. The codepath argument for _log could also be used to resolve this, but duplication # simpler. if log.isEnabledFor(log.DEBUG) and _shouldDoSpeechManagerLogging(): log._log(log.DEBUG, f"SpeechManager- " + msg, args, **kwargs) return log._log(log.INFO, f"SpeechManUnitTest- " + msg, args, **kwargs)
def executeGesture(self, gesture): """Perform the action associated with a gesture. @param gesture: The gesture to execute. @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction script = gesture.script focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL or (focus.sleepMode and not getattr(script, "allowInSleepMode", False)): raise NoInputGestureAction speechEffect = gesture.speechEffectWhenExecuted if speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): queueHandler.queueFunction( queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE ) if log.isEnabledFor(log.IO) and not gesture.isModifier: log.io("Input: %s" % gesture.logIdentifier) if self.isInputHelpActive: bypass = getattr(script, "bypassInputHelp", False) queueHandler.queueFunction(queueHandler.eventQueue, self._handleInputHelp, gesture, onlyLog=bypass) if not bypass: return if gesture.isModifier: raise NoInputGestureAction if config.conf["keyboard"]["speakCommandKeys"] and gesture.shouldReportAsCommand: queueHandler.queueFunction(queueHandler.eventQueue, speech.speakMessage, gesture.displayName) gesture.reportExtra() if script: scriptHandler.queueScript(script, gesture) return raise NoInputGestureAction
def event_UIA_notification(self, obj, nextHandler, notificationKind=None, notificationProcessing=None, displayString=None, activityId=None): # Introduced in Windows 10 1709 (Fall Creators Update), to be treated as a notification event. self.evtDebugLogging(obj, "UIA_notification") if isinstance(obj, UIA) and log.isEnabledFor(log.DEBUG): log.debug("EvtTracker: UIA notification: " f"sender: {obj.UIAElement}, " f"notification kind: {notificationKind}, " f"notification processing: {notificationProcessing}, " f"display string: {displayString}, " f"activity Id: {activityId}") nextHandler()
def _watcher(): global isAttemptingRecovery while isRunning: # If the watchdog is suspended, wait until it is resumed. _resumeEvent.wait() # Wait for the core to be alive. # Wait a maximum of NORMAL_CORE_ALIVE_TIMEOUT, but shorten this to a minimum of MIN_CORE_ALIVE_TIMEOUT under special circumstances. waited = 0 while True: # Wait MIN_CORE_ALIVE_TIMEOUT, unless there is less than that time remaining for NORMAL_CORE_ALIVE_TIMEOUT. timeout = min(MIN_CORE_ALIVE_TIMEOUT, NORMAL_CORE_ALIVE_TIMEOUT - waited) if timeout <= 0: # Timeout elapsed. break _coreAliveEvent.wait(timeout) waited += timeout if _coreAliveEvent.isSet() or _shouldRecoverAfterMinTimeout(): break if log.isEnabledFor(log.DEBUGWARNING) and not _coreAliveEvent.isSet(): log.debugWarning("Trying to recover from freeze, core stack:\n%s" % "".join( traceback.format_stack( sys._current_frames()[_coreThreadID]))) lastTime = time.time() while not _coreAliveEvent.isSet(): curTime = time.time() if curTime - lastTime > FROZEN_WARNING_TIMEOUT: lastTime = curTime log.warning("Core frozen in stack:\n%s" % "".join( traceback.format_stack( sys._current_frames()[_coreThreadID]))) # The core is dead, so attempt recovery. isAttemptingRecovery = True _recoverAttempt() _coreAliveEvent.wait(RECOVER_ATTEMPT_INTERVAL) isAttemptingRecovery = False # At this point, the core is alive. _coreAliveEvent.clear() # Wait a bit to avoid excessive resource consumption. time.sleep(CHECK_INTERVAL)
def _watcher(): global isAttemptingRecovery while True: # Wait for the core to die. winKernel.waitForSingleObject(_coreDeadTimer, winKernel.INFINITE) if not isRunning: return # The core hasn't reported alive for MIN_CORE_ALIVE_TIMEOUT. waited = MIN_CORE_ALIVE_TIMEOUT while not _isAlive() and not _shouldRecoverAfterMinTimeout(): # The core is still dead and fast recovery doesn't apply. # Wait up to NORMAL_ALIVE_TIMEOUT. time.sleep(MIN_CORE_ALIVE_TIMEOUT) waited += MIN_CORE_ALIVE_TIMEOUT if waited >= NORMAL_CORE_ALIVE_TIMEOUT: break if _isAlive(): continue if log.isEnabledFor(log.DEBUGWARNING): stacks = getFormattedStacksForAllThreads() log.debugWarning( f"Trying to recover from freeze. Listing stacks for Python threads:\n{stacks}" ) lastTime = time.time() isAttemptingRecovery = True # Cancel calls until the core is alive. # This event will be reset by alive(). windll.kernel32.SetEvent(_cancelCallEvent) # Some calls have to be killed individually. while True: curTime = time.time() if curTime - lastTime > FROZEN_WARNING_TIMEOUT: lastTime = curTime # Core is completely frozen. # Collect formatted stacks for all Python threads. log.error("Core frozen in stack!") stacks = getFormattedStacksForAllThreads() log.info(f"Listing stacks for Python threads:\n{stacks}") _recoverAttempt() time.sleep(RECOVER_ATTEMPT_INTERVAL) if _isAlive(): break isAttemptingRecovery = False
def speakTypedCharacters(ch): global curWordChars; if api.isTypingProtected(): realChar="*" else: realChar=ch if ch.isalnum(): curWordChars.append(realChar) elif ch=="\b": # Backspace, so remove the last character from our buffer. del curWordChars[-1:] elif len(curWordChars)>0: typedWord="".join(curWordChars) curWordChars=[] if log.isEnabledFor(log.IO): log.io("typed word: %s"%typedWord) if config.conf["keyboard"]["speakTypedWords"]: speakText(typedWord) if config.conf["keyboard"]["speakTypedCharacters"] and ord(ch)>=32: speakSpelling(realChar)
def _watcher(): global isAttemptingRecovery while True: # Wait for the core to die. winKernel.waitForSingleObject(_coreDeadTimer, winKernel.INFINITE) if not isRunning: return # The core hasn't reported alive for MIN_CORE_ALIVE_TIMEOUT. waited = MIN_CORE_ALIVE_TIMEOUT while not _isAlive() and not _shouldRecoverAfterMinTimeout(): # The core is still dead and fast recovery doesn't apply. # Wait up to NORMAL_ALIVE_TIMEOUT. time.sleep(MIN_CORE_ALIVE_TIMEOUT) waited += MIN_CORE_ALIVE_TIMEOUT if waited >= NORMAL_CORE_ALIVE_TIMEOUT: break if _isAlive(): continue if log.isEnabledFor(log.DEBUGWARNING): log.debugWarning( "Trying to recover from freeze, core stack:\n%s" % "".join( traceback.format_stack( sys._current_frames()[core.mainThreadId]))) lastTime = time.time() isAttemptingRecovery = True # Cancel calls until the core is alive. # This event will be reset by alive(). windll.kernel32.SetEvent(_cancelCallEvent) # Some calls have to be killed individually. while True: curTime = time.time() if curTime - lastTime > FROZEN_WARNING_TIMEOUT: lastTime = curTime log.warning("Core frozen in stack:\n%s" % "".join( traceback.format_stack( sys._current_frames()[core.mainThreadId]))) _recoverAttempt() time.sleep(RECOVER_ATTEMPT_INTERVAL) if _isAlive(): break isAttemptingRecovery = False
def _loadConfig(self, fn, fileError=False): log.info(u"Loading config: {0}".format(fn)) profile = ConfigObj(fn, indent_type="\t", encoding="UTF-8", file_error=fileError) # Python converts \r\n to \n when reading files in Windows, so ConfigObj can't determine the true line ending. profile.newlines = "\r\n" profileCopy = deepcopy(profile) try: profileUpgrader.upgrade(profile, self.validator) except Exception as e: # Log at level info to ensure that the profile is logged. log.info(u"Config before schema update:\n%s" % profileCopy, exc_info=False) raise e # since profile settings are not yet imported we have to "peek" to see # if debug level logging is enabled. try: logLevelName = profile["general"]["loggingLevel"] except KeyError as e: logLevelName = None if log.isEnabledFor(log.DEBUG) or (logLevelName and DEBUG >= levelNames.get(logLevelName)): # Log at level info to ensure that the profile is logged. log.info(u"Config loaded (after upgrade, and in the state it will be used by NVDA):\n{0}".format(profile)) return profile
def _watcher(): global isAttemptingRecovery while isRunning: # If the watchdog is suspended, wait until it is resumed. _resumeEvent.wait() # Wait for the core to be alive. # Wait a maximum of NORMAL_CORE_ALIVE_TIMEOUT, but shorten this to a minimum of MIN_CORE_ALIVE_TIMEOUT under special circumstances. waited = 0 while True: # Wait MIN_CORE_ALIVE_TIMEOUT, unless there is less than that time remaining for NORMAL_CORE_ALIVE_TIMEOUT. timeout = min(MIN_CORE_ALIVE_TIMEOUT, NORMAL_CORE_ALIVE_TIMEOUT - waited) if timeout <= 0: # Timeout elapsed. break _coreAliveEvent.wait(timeout) waited += timeout if _coreAliveEvent.isSet() or _shouldRecoverAfterMinTimeout(): break if log.isEnabledFor(log.DEBUGWARNING) and not _coreAliveEvent.isSet(): log.debugWarning("Trying to recover from freeze, core stack:\n%s"% "".join(traceback.format_stack(sys._current_frames()[_coreThreadID]))) lastTime=time.time() while not _coreAliveEvent.isSet(): curTime=time.time() if curTime-lastTime>FROZEN_WARNING_TIMEOUT: lastTime=curTime log.warning("Core frozen in stack:\n%s"% "".join(traceback.format_stack(sys._current_frames()[_coreThreadID]))) # The core is dead, so attempt recovery. isAttemptingRecovery = True _recoverAttempt() _coreAliveEvent.wait(RECOVER_ATTEMPT_INTERVAL) isAttemptingRecovery = False # At this point, the core is alive. _coreAliveEvent.clear() # Wait a bit to avoid excessive resource consumption. time.sleep(CHECK_INTERVAL)
def _watcher(): global isAttemptingRecovery while True: # Wait for the core to die. winKernel.waitForSingleObject(_coreDeadTimer, winKernel.INFINITE) if not isRunning: return # The core hasn't reported alive for MIN_CORE_ALIVE_TIMEOUT. waited = MIN_CORE_ALIVE_TIMEOUT while not _isAlive() and not _shouldRecoverAfterMinTimeout(): # The core is still dead and fast recovery doesn't apply. # Wait up to NORMAL_ALIVE_TIMEOUT. time.sleep(MIN_CORE_ALIVE_TIMEOUT) waited += MIN_CORE_ALIVE_TIMEOUT if waited >= NORMAL_CORE_ALIVE_TIMEOUT: break if _isAlive(): continue if log.isEnabledFor(log.DEBUGWARNING): log.debugWarning("Trying to recover from freeze, core stack:\n%s"% "".join(traceback.format_stack(sys._current_frames()[core.mainThreadId]))) lastTime=time.time() isAttemptingRecovery = True # Cancel calls until the core is alive. # This event will be reset by alive(). windll.kernel32.SetEvent(_cancelCallEvent) # Some calls have to be killed individually. while True: curTime=time.time() if curTime-lastTime>FROZEN_WARNING_TIMEOUT: lastTime=curTime log.warning("Core frozen in stack:\n%s"% "".join(traceback.format_stack(sys._current_frames()[core.mainThreadId]))) _recoverAttempt() time.sleep(RECOVER_ATTEMPT_INTERVAL) if _isAlive(): break isAttemptingRecovery = False
def executeGesture(self, gesture): """Perform the action associated with a gesture. @param gesture: The gesture to execute. @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction script = gesture.script if "brailleDisplayDrivers" in str(type(gesture)): if instanceGP.brailleKeyboardLocked and ( (hasattr(script, "__func__") and script.__func__.__name__ != "script_toggleLockBrailleKeyboard") or not hasattr(script, "__func__")): return if not config.conf["brailleExtender"][ 'stopSpeechUnknown'] and gesture.script == None: stopSpeech = False elif hasattr(script, "__func__") and (script.__func__.__name__ in [ "script_braille_dots", "script_braille_enter", "script_volumePlus", "script_volumeMinus", "script_toggleVolume", "script_hourDate", "script_ctrl", "script_alt", "script_nvda", "script_win", "script_ctrlAlt", "script_ctrlAltWin", "script_ctrlAltWinShift", "script_ctrlAltShift", "script_ctrlWin", "script_ctrlWinShift", "script_ctrlShift", "script_altWin", "script_altWinShift", "script_altShift", "script_winShift" ] or (not config.conf["brailleExtender"]['stopSpeechScroll'] and script.__func__.__name__ in ["script_braille_scrollBack", "script_braille_scrollForward"])): stopSpeech = False else: stopSpeech = True else: stopSpeech = True focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL or (focus.sleepMode and not getattr( script, 'allowInSleepMode', False)): raise NoInputGestureAction wasInSayAll = False if gesture.isModifier: if not self.lastModifierWasInSayAll: wasInSayAll = self.lastModifierWasInSayAll = sayAllHandler.isRunning( ) elif self.lastModifierWasInSayAll: wasInSayAll = True self.lastModifierWasInSayAll = False else: wasInSayAll = sayAllHandler.isRunning() if wasInSayAll: gesture.wasInSayAll = True speechEffect = gesture.speechEffectWhenExecuted if not stopSpeech: pass elif speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): queueHandler.queueFunction(queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE) if log.isEnabledFor(log.IO) and not gesture.isModifier: self._lastInputTime = time.time() log.io("Input: %s" % gesture.identifiers[0]) if self._captureFunc: try: if self._captureFunc(gesture) is False: return except BaseException: log.error("Error in capture function, disabling", exc_info=True) self._captureFunc = None if gesture.isModifier: raise NoInputGestureAction if config.conf["keyboard"][ "speakCommandKeys"] and gesture.shouldReportAsCommand: queueHandler.queueFunction(queueHandler.eventQueue, speech.speakMessage, gesture.displayName) gesture.reportExtra() # #2953: if an intercepted command Script (script that sends a gesture) is queued # then queue all following gestures (that don't have a script) with a fake script so that they remain in order. if not script and scriptHandler._numIncompleteInterceptedCommandScripts: script = lambda gesture: gesture.send() if script: scriptHandler.queueScript(script, gesture) return raise NoInputGestureAction
def __init__(self): super(GlobalPlugin, self).__init__() # Do not activate this from secure screens. # And do not activate the add-on if log level is not debug. if globalVars.appArgs.secure or not log.isEnabledFor(log.DEBUG): return
def executeGesture(self, gesture): """Perform the action associated with a gesture. @param gesture: The gesture to execute. @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction script = gesture.script focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL or ( focus.sleepMode and not getattr(script, 'allowInSleepMode', False)): raise NoInputGestureAction wasInSayAll = False if gesture.isModifier: if not self.lastModifierWasInSayAll: wasInSayAll = self.lastModifierWasInSayAll = sayAllHandler.isRunning( ) elif self.lastModifierWasInSayAll: wasInSayAll = True self.lastModifierWasInSayAll = False else: wasInSayAll = sayAllHandler.isRunning() if wasInSayAll: gesture.wasInSayAll = True speechEffect = gesture.speechEffectWhenExecuted if speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): queueHandler.queueFunction( queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE) if gesture.shouldPreventSystemIdle: winKernel.SetThreadExecutionState(winKernel.ES_SYSTEM_REQUIRED) if log.isEnabledFor(log.IO) and not gesture.isModifier: self._lastInputTime = time.time() log.io("Input: %s" % gesture.identifiers[0]) if self._captureFunc: try: if self._captureFunc(gesture) is False: return except: log.error("Error in capture function, disabling", exc_info=True) self._captureFunc = None if gesture.isModifier: raise NoInputGestureAction if config.conf["keyboard"][ "speakCommandKeys"] and gesture.shouldReportAsCommand: queueHandler.queueFunction(queueHandler.eventQueue, speech.speakMessage, gesture.displayName) gesture.reportExtra() # #2953: if an intercepted command Script (script that sends a gesture) is queued # then queue all following gestures (that don't have a script) with a fake script so that they remain in order. if not script and scriptHandler._numIncompleteInterceptedCommandScripts: script = lambda gesture: gesture.send() if script: scriptHandler.queueScript(script, gesture) return else: # Clear memorized last script to avoid getLastScriptRepeatCount detect a repeat # in case an unbound gesture is executed between two identical bound gestures. queueHandler.queueFunction(queueHandler.eventQueue, scriptHandler.clearLastScript) raise NoInputGestureAction
def _getTextWithFieldsForUIARange(self,rootElement,textRange,formatConfig,includeRoot=False,alwaysWalkAncestors=True,recurseChildren=True,_rootElementRange=None): # Edge zooms into its children at the start. # Thus you are already in the deepest first child. # Therefore get the deepest enclosing element at the start, get its content, Then do the whole thing again on the content from the end of the enclosing element to the end of its parent, and repete! # In other words, get the content while slowly zooming out from the start. log.debug("_getTextWithFieldsForUIARange (unbalanced)") if not recurseChildren: log.debug("recurseChildren is False. Falling back to super") for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(rootElement,textRange,formatConfig,includeRoot=includeRoot,alwaysWalkAncestors=True,recurseChildren=False,_rootElementRange=_rootElementRange): yield field return if log.isEnabledFor(log.DEBUG): log.debug("rootElement: %s"%rootElement.currentLocalizedControlType) log.debug("full text: %s"%textRange.getText(-1)) log.debug("includeRoot: %s"%includeRoot) startRange=textRange.clone() startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_Start) enclosingElement=startRange.getEnclosingElement() if enclosingElement: enclosingElement=enclosingElement.buildUpdatedCache(UIAHandler.handler.baseCacheRequest) else: log.debug("No enclosingElement. Returning") return enclosingRange=self.obj.getNormalizedUIATextRangeFromElement(enclosingElement) if not enclosingRange: log.debug("enclosingRange is NULL. Returning") return if log.isEnabledFor(log.DEBUG): log.debug("enclosingElement: %s"%enclosingElement.currentLocalizedControlType) startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,enclosingRange,UIAHandler.TextPatternRangeEndpoint_End) if startRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)>0: startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End) # Ensure we don't now have a collapsed range if startRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_Start)<=0: log.debug("Collapsed range. Returning") return # check for an embedded child childElements=startRange.getChildren() if childElements.length==1 and UIAHandler.handler.clientObject.compareElements(rootElement,childElements.getElement(0)): log.debug("Using single embedded child as enclosingElement") for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(rootElement,startRange,formatConfig,_rootElementRange=_rootElementRange,includeRoot=includeRoot,alwaysWalkAncestors=False,recurseChildren=False): yield field return parents=[] parentElement=enclosingElement log.debug("Generating ancestors:") hasAncestors=False while parentElement: if log.isEnabledFor(log.DEBUG): log.debug("parentElement: %s"%parentElement.currentLocalizedControlType) isRoot=UIAHandler.handler.clientObject.compareElements(parentElement,rootElement) log.debug("isRoot: %s"%isRoot) if not isRoot: hasAncestors=True if parentElement is not enclosingElement: if includeRoot or not isRoot: try: obj=UIA(windowHandle=self.obj.windowHandle,UIAElement=parentElement) field=self._getControlFieldForObject(obj) except LookupError: log.debug("Failed to fetch controlField data for parentElement. Breaking") break parents.append((parentElement,field)) else: # This is the root but it was not requested for inclusion # However we still need the root element itself for further recursion parents.append((parentElement,None)) if isRoot: log.debug("Hit root. Breaking") break log.debug("Fetching next parentElement") parentElement=UIAHandler.handler.baseTreeWalker.getParentElementBuildCache(parentElement,UIAHandler.handler.baseCacheRequest) log.debug("Done generating parents") log.debug("Yielding parents in reverse order") for parentElement,field in reversed(parents): if field: yield textInfos.FieldCommand("controlStart",field) log.debug("Done yielding parents") log.debug("Yielding balanced fields for startRange") for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(enclosingElement,startRange,formatConfig,_rootElementRange=enclosingRange,includeRoot=includeRoot or hasAncestors,alwaysWalkAncestors=False,recurseChildren=True): yield field tempRange=startRange.clone() log.debug("Walking parents to yield controlEnds and recurse unbalanced endRanges") for parentElement,field in parents: if log.isEnabledFor(log.DEBUG): log.debug("parentElement: %s"%parentElement.currentLocalizedControlType) tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_Start,tempRange,UIAHandler.TextPatternRangeEndpoint_End) parentRange=self.obj.getNormalizedUIATextRangeFromElement(parentElement) if parentRange: tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,parentRange,UIAHandler.TextPatternRangeEndpoint_End) if tempRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)>0: tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End) clippedEnd=True else: clippedEnd=False if field: clippedStart=parentRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_Start,textRange,UIAHandler.TextPatternRangeEndpoint_Start)<0 field['_startOfNode']=not clippedStart field['_endOfNode']=not clippedEnd if tempRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,tempRange,UIAHandler.TextPatternRangeEndpoint_Start)>0: log.debug("Recursing endRange") for endField in self._getTextWithFieldsForUIARange(parentElement,tempRange,formatConfig,includeRoot=False,alwaysWalkAncestors=True,recurseChildren=True): yield endField log.debug("Done recursing endRange") else: log.debug("No content after parent") if field: log.debug("Yielding controlEnd for parent") yield textInfos.FieldCommand("controlEnd",field) log.debug("Done walking parents to yield controlEnds and recurse unbalanced endRanges") log.debug("_getTextWithFieldsForUIARange (unbalanced) end")
def _removeCompletedFromQueue( self, index: int) -> Tuple[bool, bool]: # noqa: C901 """Removes completed speech sequences from the queue. @param index: The index just reached indicating a completed sequence. @return: Tuple of (valid, endOfUtterance), where valid indicates whether the index was valid and endOfUtterance indicates whether this sequence was the end of the current utterance. @rtype: (bool, bool) """ # Find the sequence that just completed speaking. if not self._curPriQueue: # No speech in progress. Probably from a previous utterance which was cancelled. return False, False for seqIndex, seq in enumerate(self._curPriQueue.pendingSequences): lastCommand = seq[-1] if isinstance(seq, list) else None if isinstance(lastCommand, IndexCommand): if self._isIndexAAfterIndexB(index, lastCommand.index): log.debugWarning( f"Reached speech index {index :d}, but index {lastCommand.index :d} never handled" ) elif index == lastCommand.index: endOfUtterance = isinstance( self._curPriQueue.pendingSequences[seqIndex + 1][0], EndUtteranceCommand) if endOfUtterance: # Remove the EndUtteranceCommand as well. seqIndex += 1 break # Found it! else: log._speechManagerDebug( "Unknown index. Probably from a previous utterance which was cancelled." ) return False, False if endOfUtterance: # These params may not apply to the next utterance if it was queued separately, # so reset the tracker. # The next utterance will include the commands again if they do still apply. self._curPriQueue.paramTracker = ParamChangeTracker() else: # Keep track of parameters changed so far. # This is necessary in case this utterance is preempted by higher priority speech. for seqIndex in range(seqIndex + 1): seq = self._curPriQueue.pendingSequences[seqIndex] for command in seq: if isinstance(command, SynthParamCommand): self._curPriQueue.paramTracker.update(command) # This sequence is done, so we don't need to track it any more. toRemove = self._curPriQueue.pendingSequences[:seqIndex + 1] log._speechManagerDebug("Removing: %r", seq) if _shouldCancelExpiredFocusEvents(): cancellables = (item for seq in toRemove for item in seq if isinstance(item, _CancellableSpeechCommand)) for item in cancellables: if log.isEnabledFor( log.DEBUG) and _shouldDoSpeechManagerLogging(): # Debug logging for cancelling expired focus events. log._speechManagerDebug( f"Item is in _cancelCommandsForUtteranceBeingSpokenBySynth: " f"{item in self._cancelCommandsForUtteranceBeingSpokenBySynth.keys()}" ) self._cancelCommandsForUtteranceBeingSpokenBySynth.pop( item, None) del self._curPriQueue.pendingSequences[:seqIndex + 1] return True, endOfUtterance
def _getTextWithFieldsForUIARange(self,rootElement,textRange,formatConfig,includeRoot=True,recurseChildren=True,alwaysWalkAncestors=True,_rootElementClipped=(True,True)): # Edge zooms into its children at the start. # Thus you are already in the deepest first child. # Therefore get the deepest enclosing element at the start, get its content, Then do the whole thing again on the content from the end of the enclosing element to the end of its parent, and repete! # In other words, get the content while slowly zooming out from the start. log.debug("_getTextWithFieldsForUIARange (unbalanced)") if not recurseChildren: log.debug("recurseChildren is False. Falling back to super") for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(rootElement,textRange,formatConfig,includeRoot=includeRoot,alwaysWalkAncestors=True,recurseChildren=False,_rootElementClipped=_rootElementClipped): yield field return if log.isEnabledFor(log.DEBUG): log.debug("rootElement: %s"%rootElement.currentLocalizedControlType) log.debug("full text: %s"%textRange.getText(-1)) log.debug("includeRoot: %s"%includeRoot) startRange=textRange.clone() startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_Start) enclosingElement=getEnclosingElementWithCacheFromUIATextRange(startRange,self._controlFieldUIACacheRequest) if not enclosingElement: log.debug("No enclosingElement. Returning") return enclosingRange=self.obj.getNormalizedUIATextRangeFromElement(enclosingElement) if not enclosingRange: log.debug("enclosingRange is NULL. Returning") return if log.isEnabledFor(log.DEBUG): log.debug("enclosingElement: %s"%enclosingElement.currentLocalizedControlType) startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,enclosingRange,UIAHandler.TextPatternRangeEndpoint_End) if startRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)>0: startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End) # Ensure we don't now have a collapsed range if startRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_Start)<=0: log.debug("Collapsed range. Returning") return # check for an embedded child childElements=getChildrenWithCacheFromUIATextRange(startRange,self._controlFieldUIACacheRequest) if childElements.length==1 and UIAHandler.handler.clientObject.compareElements(rootElement,childElements.getElement(0)): log.debug("Using single embedded child as enclosingElement") for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(rootElement,startRange,formatConfig,_rootElementClipped=_rootElementClipped,includeRoot=includeRoot,alwaysWalkAncestors=False,recurseChildren=False): yield field return parents=[] parentElement=enclosingElement log.debug("Generating ancestors:") hasAncestors=False while parentElement: if log.isEnabledFor(log.DEBUG): log.debug("parentElement: %s"%parentElement.currentLocalizedControlType) isRoot=UIAHandler.handler.clientObject.compareElements(parentElement,rootElement) log.debug("isRoot: %s"%isRoot) if not isRoot: hasAncestors=True if parentElement is not enclosingElement: if includeRoot or not isRoot: try: obj=UIA(windowHandle=self.obj.windowHandle,UIAElement=parentElement,initialUIACachedPropertyIDs=self._controlFieldUIACachedPropertyIDs) field=self._getControlFieldForObject(obj) except LookupError: log.debug("Failed to fetch controlField data for parentElement. Breaking") break parents.append((parentElement,field)) else: # This is the root but it was not requested for inclusion # However we still need the root element itself for further recursion parents.append((parentElement,None)) if isRoot: log.debug("Hit root. Breaking") break log.debug("Fetching next parentElement") parentElement=UIAHandler.handler.baseTreeWalker.getParentElementBuildCache(parentElement,self._controlFieldUIACacheRequest) log.debug("Done generating parents") log.debug("Yielding parents in reverse order") for parentElement,field in reversed(parents): if field: yield textInfos.FieldCommand("controlStart",field) log.debug("Done yielding parents") log.debug("Yielding balanced fields for startRange") clippedStart=enclosingRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_Start,startRange,UIAHandler.TextPatternRangeEndpoint_Start)<0 clippedEnd=enclosingRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_End)>0 for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(enclosingElement,startRange,formatConfig,_rootElementClipped=(clippedStart,clippedEnd),includeRoot=includeRoot or hasAncestors,alwaysWalkAncestors=False,recurseChildren=True): yield field tempRange=startRange.clone() log.debug("Walking parents to yield controlEnds and recurse unbalanced endRanges") for parentElement,field in parents: if log.isEnabledFor(log.DEBUG): log.debug("parentElement: %s"%parentElement.currentLocalizedControlType) tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_Start,tempRange,UIAHandler.TextPatternRangeEndpoint_End) parentRange=self.obj.getNormalizedUIATextRangeFromElement(parentElement) if parentRange: tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,parentRange,UIAHandler.TextPatternRangeEndpoint_End) if tempRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)>0: tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End) clippedEnd=True else: clippedEnd=False if field: clippedStart=parentRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_Start,textRange,UIAHandler.TextPatternRangeEndpoint_Start)<0 field['_startOfNode']=not clippedStart field['_endOfNode']=not clippedEnd if tempRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,tempRange,UIAHandler.TextPatternRangeEndpoint_Start)>0: log.debug("Recursing endRange") for endField in self._getTextWithFieldsForUIARange(parentElement,tempRange,formatConfig,_rootElementClipped=(clippedStart,clippedEnd),includeRoot=False,alwaysWalkAncestors=True,recurseChildren=True): yield endField log.debug("Done recursing endRange") else: log.debug("No content after parent") if field: log.debug("Yielding controlEnd for parent") yield textInfos.FieldCommand("controlEnd",field) log.debug("Done walking parents to yield controlEnds and recurse unbalanced endRanges") log.debug("_getTextWithFieldsForUIARange (unbalanced) end")
def executeGesture(self, gesture): """Perform the action associated with a gesture. @param gesture: The gesture to execute. @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction script = gesture.script focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL or ( focus.sleepMode and not getattr(script, 'allowInSleepMode', False)): raise NoInputGestureAction wasInSayAll = False if gesture.isModifier: if not self.lastModifierWasInSayAll: wasInSayAll = self.lastModifierWasInSayAll = sayAllHandler.isRunning( ) elif self.lastModifierWasInSayAll: wasInSayAll = True self.lastModifierWasInSayAll = False else: wasInSayAll = sayAllHandler.isRunning() if wasInSayAll: gesture.wasInSayAll = True speechEffect = gesture.speechEffectWhenExecuted if speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): queueHandler.queueFunction( queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE) if log.isEnabledFor(log.IO) and not gesture.isModifier: log.io("Input: %s" % gesture.logIdentifier) if self._captureFunc: try: if self._captureFunc(gesture) is False: return except: log.error("Error in capture function, disabling", exc_info=True) self._captureFunc = None if gesture.isModifier: raise NoInputGestureAction if config.conf["keyboard"][ "speakCommandKeys"] and gesture.shouldReportAsCommand: queueHandler.queueFunction(queueHandler.eventQueue, speech.speakMessage, gesture.displayName) # nvdajp begin if hasattr(gesture, "vkCode") and gesture.vkCode == winUser.VK_RETURN: dummy = winUser.getAsyncKeyState(winUser.VK_BACK) # nvdajp end gesture.reportExtra() # #2953: if an intercepted command Script (script that sends a gesture) is queued # then queue all following gestures (that don't have a script) with a fake script so that they remain in order. if not script and scriptHandler._numIncompleteInterceptedCommandScripts: script = lambda gesture: gesture.send() if script: scriptHandler.queueScript(script, gesture) return raise NoInputGestureAction
def event_UIA_layoutInvalidated(self, obj, nextHandler): self.evtDebugLogging(obj, "layoutInvalidated") if log.isEnabledFor(log.DEBUG): log.debug(f"EvtTracker: list item count: {obj.childCount}") nextHandler()
def executeKeyboardGesture(self, gesture, bypassRemanence=False): """Perform the action associated with a gesture. @param gesture: The gesture to execute @type gesture: L{InputGesture} @raise NoInputGestureAction: If there is no action to perform. """ if not hasattr(gesture, "noAction"): gesture.noAction = False if watchdog.isAttemptingRecovery: # The core is dead, so don't try to perform an action. # This lets gestures pass through unhindered where possible, # as well as stopping a flood of actions when the core revives. raise NoInputGestureAction newGesture = self.manageRemanence( gesture) if not bypassRemanence else None if newGesture is not None: queueHandler.queueFunction(queueHandler.eventQueue, self.executeNewGesture, newGesture) return newGesture = self.getNumpadKeyReplacement(gesture) if newGesture is not None: queueHandler.queueFunction(queueHandler.eventQueue, self.executeNewGesture, newGesture) return script = gesture.script focus = api.getFocusObject() if focus.sleepMode is focus.SLEEP_FULL\ or (focus.sleepMode and not getattr(script, 'allowInSleepMode', False)): raise NoInputGestureAction wasInSayAll = False if gesture.isModifier: if not _NVDA_InputManager.lastModifierWasInSayAll: wasInSayAll = _NVDA_InputManager.lastModifierWasInSayAll = sayAllHandler.isRunning( ) elif _NVDA_InputManager.lastModifierWasInSayAll: wasInSayAll = True _NVDA_InputManager.lastModifierWasInSayAll = False else: wasInSayAll = sayAllHandler.isRunning() if wasInSayAll: gesture.wasInSayAll = True speechEffect = gesture.speechEffectWhenExecuted if speechEffect == gesture.SPEECHEFFECT_CANCEL: queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) elif speechEffect in (gesture.SPEECHEFFECT_PAUSE, gesture.SPEECHEFFECT_RESUME): # noqa:E501 queueHandler.queueFunction( queueHandler.eventQueue, speech.pauseSpeech, speechEffect == gesture.SPEECHEFFECT_PAUSE) if py3 and gesture.shouldPreventSystemIdle: winKernel.SetThreadExecutionState(winKernel.ES_SYSTEM_REQUIRED | winKernel.ES_DISPLAY_REQUIRED) if log.isEnabledFor(log.IO) and not gesture.isModifier: _NVDA_InputManager._lastInputTime = time.time() log.io("Input: %s" % gesture.identifiers[0]) if _NVDA_InputManager._captureFunc: try: if _NVDA_InputManager._captureFunc(gesture) is False: return except: # noqa:E722 log.error("Error in capture function, disabling", exc_info=True) _NVDA_InputManager._captureFunc = None if gesture.isModifier: if gesture.noAction: gesture.normalizedModifiers = [] return raise NoInputGestureAction self.speakGesture(gesture) if not script: gesture.reportExtra() # then queue all following gestures # (that don't have a script # ) with a fake script so that they remain in order. if not script and ( bypassRemanence or scriptHandler._numIncompleteInterceptedCommandScripts): script = lambda gesture: gesture.send() # noqa:E731 if script: scriptHandler.queueScript(script, gesture) return raise NoInputGestureAction