def IUIAutomationEventHandler_HandleAutomationEvent(self, sender, eventID): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. return if eventID == UIA_MenuOpenedEventId and eventHandler.isPendingEvents( "gainFocus"): # We don't need the menuOpened event if focus has been fired, # as focus should be more correct. return NVDAEventName = UIAEventIdsToNVDAEventNames.get(eventID, None) if not NVDAEventName: return if not self.isNativeUIAElement(sender): return window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent(NVDAEventName, windowHandle=window): return import NVDAObjects.UIA obj = NVDAObjects.UIA.UIA(UIAElement=sender) if (not obj or (NVDAEventName == "gainFocus" and not obj.shouldAllowUIAFocusEvent) or (NVDAEventName == "liveRegionChange" and not obj._shouldAllowUIALiveRegionChangeEvent)): return focus = api.getFocusObject() if obj == focus: obj = focus eventHandler.queueEvent(NVDAEventName, obj)
def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent( self, sender, propertyId, newValue): # #3867: For now manually force this VARIANT type to empty to get around a nasty double free in comtypes/ctypes. # We also don't use the value in this callback. newValue.vt = VT_EMPTY if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. return NVDAEventName = UIAPropertyIdsToNVDAEventNames.get(propertyId, None) if not NVDAEventName: return if not self.isNativeUIAElement(sender): return window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent(NVDAEventName, windowHandle=window): return import NVDAObjects.UIA obj = NVDAObjects.UIA.UIA(UIAElement=sender) if not obj: return focus = api.getFocusObject() if obj == focus: obj = focus eventHandler.queueEvent(NVDAEventName, obj)
def IUIAutomationEventHandler_HandleAutomationEvent(self,sender,eventID): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. if _isDebug(): log.debug("HandleAutomationEvent: event received while not fully initialized") return if eventID==UIA_MenuOpenedEventId and eventHandler.isPendingEvents("gainFocus"): # We don't need the menuOpened event if focus has been fired, # as focus should be more correct. if _isDebug(): log.debug("HandleAutomationEvent: Ignored MenuOpenedEvent while focus event pending") return NVDAEventName=UIAEventIdsToNVDAEventNames.get(eventID,None) if not NVDAEventName: if _isDebug(): log.debugWarning(f"HandleAutomationEvent: Don't know how to handle event {eventID}") return focus = api.getFocusObject() import NVDAObjects.UIA if ( isinstance(focus, NVDAObjects.UIA.UIA) and self.clientObject.compareElements(focus.UIAElement, sender) ): pass elif not self.isNativeUIAElement(sender): if _isDebug(): log.debug( f"HandleAutomationEvent: Ignoring event {NVDAEventName} for non native element" ) return window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent(NVDAEventName, windowHandle=window): if _isDebug(): log.debug( f"HandleAutomationEvent: Ignoring event {NVDAEventName} for shouldAcceptEvent=False" ) return try: obj = NVDAObjects.UIA.UIA(UIAElement=sender) except Exception: if _isDebug(): log.debugWarning( f"HandleAutomationEvent: Exception while creating object for event {NVDAEventName}", exc_info=True ) return if ( not obj or (NVDAEventName=="gainFocus" and not obj.shouldAllowUIAFocusEvent) or (NVDAEventName=="liveRegionChange" and not obj._shouldAllowUIALiveRegionChangeEvent) ): if _isDebug(): log.debug( "HandleAutomationEvent: " f"Ignoring event {NVDAEventName} because no object or ignored by object itself" ) return if obj==focus: obj=focus eventHandler.queueEvent(NVDAEventName,obj)
def IUIAutomationEventHandler_HandleAutomationEvent(self,sender,eventID): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. return if eventID==UIA_MenuOpenedEventId and eventHandler.isPendingEvents("gainFocus"): # We don't need the menuOpened event if focus has been fired, # as focus should be more correct. return NVDAEventName=UIAEventIdsToNVDAEventNames.get(eventID,None) if not NVDAEventName: return if not self.isNativeUIAElement(sender): return window=self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent(NVDAEventName,windowHandle=window): return import NVDAObjects.UIA obj=NVDAObjects.UIA.UIA(UIAElement=sender) if ( not obj or (NVDAEventName=="gainFocus" and not obj.shouldAllowUIAFocusEvent) or (NVDAEventName=="liveRegionChange" and not obj._shouldAllowUIALiveRegionChangeEvent) ): return focus=api.getFocusObject() if obj==focus: obj=focus eventHandler.queueEvent(NVDAEventName,obj)
def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent( self, sender): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. return self.lastFocusedUIAElement = sender if not self.isNativeUIAElement(sender): return import NVDAObjects.UIA if isinstance(eventHandler.lastQueuedFocusObject, NVDAObjects.UIA.UIA): lastFocus = eventHandler.lastQueuedFocusObject.UIAElement # Ignore duplicate focus events. # It seems that it is possible for compareElements to return True, even though the objects are different. # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state. if self.clientObject.compareElements( sender, lastFocus) and lastFocus.currentHasKeyboardFocus: return window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent("gainFocus", windowHandle=window): return obj = NVDAObjects.UIA.UIA(UIAElement=sender) if not obj or not obj.shouldAllowUIAFocusEvent: return eventHandler.queueEvent("gainFocus", obj)
def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(self,sender,propertyId,newValue): # #3867: For now manually force this VARIANT type to empty to get around a nasty double free in comtypes/ctypes. # We also don't use the value in this callback. newValue.vt=VT_EMPTY if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. return NVDAEventName=UIAPropertyIdsToNVDAEventNames.get(propertyId,None) if not NVDAEventName: return if not self.isNativeUIAElement(sender): return try: window=sender.cachedNativeWindowHandle except COMError: window=None if window and not eventHandler.shouldAcceptEvent(NVDAEventName,windowHandle=window): return import NVDAObjects.UIA obj=NVDAObjects.UIA.UIA(UIAElement=sender) if not obj: return focus=api.getFocusObject() if obj==focus: obj=focus eventHandler.queueEvent(NVDAEventName,obj)
def pumpAll(): global _deferUntilForegroundWindow,_foregroundDefers if _deferUntilForegroundWindow: # #3831: Sometimes, a foreground event is fired, # but GetForegroundWindow() takes a short while to return this new foreground. if _foregroundDefers<MAX_FOREGROUND_DEFERS and winUser.getForegroundWindow()!=_deferUntilForegroundWindow: # Wait a core cycle before handling events to give the foreground window time to update. core.requestPump() _foregroundDefers+=1 return else: # Either the foreground window is now correct # or we've already had the maximum number of defers. # (Sometimes, foreground events are fired even when the foreground hasn't actually changed.) _deferUntilForegroundWindow=None #Receive all the winEvents from the limiter for this cycle winEvents=winEventLimiter.flushEvents() focusWinEvents=[] validFocus=False fakeFocusEvent=None for winEvent in winEvents[0-MAX_WINEVENTS:]: # #4001: Ideally, we'd call shouldAcceptEvent in winEventCallback, # but this causes focus issues when starting applications. if not eventHandler.shouldAcceptEvent(winEventIDsToNVDAEventNames[winEvent[0]], windowHandle=winEvent[1]): continue #We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS,winUser.EVENT_SYSTEM_FOREGROUND): focusWinEvents.append(winEvent) continue else: for focusWinEvent in reversed(focusWinEvents): procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent if procFunc(*(focusWinEvent[1:])): validFocus=True break focusWinEvents=[] if winEvent[0]==winUser.EVENT_SYSTEM_DESKTOPSWITCH: processDesktopSwitchWinEvent(*winEvent[1:]) elif winEvent[0]==winUser.EVENT_OBJECT_SHOW: processShowWinEvent(*winEvent[1:]) elif winEvent[0] in MENU_EVENTIDS+(winUser.EVENT_SYSTEM_SWITCHEND,): # If there is no valid focus event, we may need to use this to fake the focus later. fakeFocusEvent=winEvent else: processGenericWinEvent(*winEvent) for focusWinEvent in reversed(focusWinEvents): procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent if procFunc(*(focusWinEvent[1:])): validFocus=True break if fakeFocusEvent: # Try this as a last resort. if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUPOPUPSTART): # menuStart needs to be handled specially and might act even if there was a valid focus event. processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus) elif not validFocus: # Other fake focus events only need to be handled if there was no valid focus event. processFakeFocusWinEvent(*fakeFocusEvent)
def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent( self, sender): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. if _isDebug(): log.debug( "HandleFocusChangedEvent: event received while not fully initialized" ) return self.lastFocusedUIAElement = sender if not self.isNativeUIAElement(sender): if _isDebug(): log.debug( "HandleFocusChangedEvent: Ignoring for non native element") return import NVDAObjects.UIA if isinstance(eventHandler.lastQueuedFocusObject, NVDAObjects.UIA.UIA): lastFocusObj = eventHandler.lastQueuedFocusObject # Ignore duplicate focus events. # It seems that it is possible for compareElements to return True, even though the objects are different. # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state. try: if (not lastFocusObj.shouldAllowDuplicateUIAFocusEvent and self.clientObject.compareElements( sender, lastFocusObj.UIAElement) and lastFocusObj.UIAElement.currentHasKeyboardFocus): if _isDebug(): log.debugWarning( "HandleFocusChangedEvent: Ignoring duplicate focus event" ) return except COMError: if _isDebug(): log.debugWarning( "HandleFocusChangedEvent: Couldn't check for duplicate focus event", exc_info=True) window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent("gainFocus", windowHandle=window): if _isDebug(): log.debug( "HandleFocusChangedEvent: Ignoring for shouldAcceptEvent=False" ) return try: obj = NVDAObjects.UIA.UIA(UIAElement=sender) except Exception: if _isDebug(): log.debugWarning( "HandleFocusChangedEvent: Exception while creating object", exc_info=True) return if not obj or not obj.shouldAllowUIAFocusEvent: if _isDebug(): log.debug( "HandleFocusChangedEvent: Ignoring because no object or ignored by object itself" ) return eventHandler.queueEvent("gainFocus", obj)
def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(self,sender,propertyId,newValue): # #3867: For now manually force this VARIANT type to empty to get around a nasty double free in comtypes/ctypes. # We also don't use the value in this callback. newValue.vt=VT_EMPTY if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. if _isDebug(): log.debug("HandlePropertyChangedEvent: event received while not fully initialized") return try: processId = sender.CachedProcessID except COMError: pass else: appMod = appModuleHandler.getAppModuleFromProcessID(processId) if not appMod.shouldProcessUIAPropertyChangedEvent(sender, propertyId): return NVDAEventName=UIAPropertyIdsToNVDAEventNames.get(propertyId,None) if not NVDAEventName: if _isDebug(): log.debugWarning(f"HandlePropertyChangedEvent: Don't know how to handle property {propertyId}") return focus = api.getFocusObject() import NVDAObjects.UIA if ( isinstance(focus, NVDAObjects.UIA.UIA) and self.clientObject.compareElements(focus.UIAElement, sender) ): pass elif not self.isNativeUIAElement(sender): if _isDebug(): log.debug( f"HandlePropertyChangedEvent: Ignoring event {NVDAEventName} for non native element" ) return window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent(NVDAEventName, windowHandle=window): if _isDebug(): log.debug( f"HandlePropertyChangedEvent: Ignoring event {NVDAEventName} for shouldAcceptEvent=False" ) return try: obj = NVDAObjects.UIA.UIA(UIAElement=sender) except Exception: if _isDebug(): log.debugWarning( f"HandlePropertyChangedEvent: Exception while creating object for event {NVDAEventName}", exc_info=True ) return if not obj: if _isDebug(): log.debug(f"HandlePropertyChangedEvent: Ignoring event {NVDAEventName} because no object") return if obj==focus: obj=focus eventHandler.queueEvent(NVDAEventName,obj)
def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(self,sender): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. return if not self.isNativeUIAElement(sender): return import NVDAObjects.UIA if isinstance(eventHandler.lastQueuedFocusObject,NVDAObjects.UIA.UIA): lastFocus=eventHandler.lastQueuedFocusObject.UIAElement # Ignore duplicate focus events. # It seems that it is possible for compareElements to return True, even though the objects are different. # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state. if self.clientObject.compareElements(sender,lastFocus) and lastFocus.currentHasKeyboardFocus: return window=self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent("gainFocus",windowHandle=window): return obj=NVDAObjects.UIA.UIA(UIAElement=sender) if not obj or not obj.shouldAllowUIAFocusEvent: return eventHandler.queueEvent("gainFocus",obj)
def pumpAll(): # noqa: C901 if not _shouldGetEvents(): return focusWinEvents = [] validFocus = False fakeFocusEvent = None focus = eventHandler.lastQueuedFocusObject # Receive all the winEvents from the limiter for this cycle winEvents = winEventLimiter.flushEvents() winEvents = winEvents[0 - MAX_WINEVENTS:] for winEvent in winEvents: isEventOnCaret = winEvent[2] == winUser.OBJID_CARET showHideCaretEvent = focus and isEventOnCaret and winEvent[0] in [ winUser.EVENT_OBJECT_SHOW, winUser.EVENT_OBJECT_HIDE ] # #4001: Ideally, we'd call shouldAcceptEvent in winEventCallback, but this causes focus issues when # starting applications. #7332: If this is a show event, which would normally be dropped by # `shouldAcceptEvent` and this event is for the caret, later it will be mapped to a caret event, # so skip `shouldAcceptEvent` if showHideCaretEvent: if not focus.shouldAcceptShowHideCaretEvent: continue elif not eventHandler.shouldAcceptEvent( winEventIDsToNVDAEventNames[winEvent[0]], windowHandle=winEvent[1]): continue # We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS, winUser.EVENT_SYSTEM_FOREGROUND): focusWinEvents.append(winEvent) continue else: for focusWinEvent in reversed(focusWinEvents): isForeground = focusWinEvent[ 0] == winUser.EVENT_SYSTEM_FOREGROUND procFunc = processForegroundWinEvent if isForeground else processFocusWinEvent if procFunc(*(focusWinEvent[1:])): validFocus = True break focusWinEvents = [] if winEvent[0] == winUser.EVENT_SYSTEM_DESKTOPSWITCH: processDesktopSwitchWinEvent(*winEvent[1:]) # we dont want show caret events to be processed by `processShowWinEvent`, instead they should be # handled by `processGenericWinEvent` elif winEvent[ 0] == winUser.EVENT_OBJECT_SHOW and not isEventOnCaret: processShowWinEvent(*winEvent[1:]) elif winEvent[0] in MENU_EVENTIDS + ( winUser.EVENT_SYSTEM_SWITCHEND, ): # If there is no valid focus event, we may need to use this to fake the focus later. fakeFocusEvent = winEvent else: processGenericWinEvent(*winEvent) for focusWinEvent in reversed(focusWinEvents): isForeground = focusWinEvent[0] == winUser.EVENT_SYSTEM_FOREGROUND procFunc = processForegroundWinEvent if isForeground else processFocusWinEvent if procFunc(*(focusWinEvent[1:])): validFocus = True break if fakeFocusEvent: # Try this as a last resort. if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUPOPUPSTART): # menuStart needs to be handled specially and might act even if there was a valid focus event. processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus) elif not validFocus: # Other fake focus events only need to be handled if there was no valid focus event. processFakeFocusWinEvent(*fakeFocusEvent)
def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent( self, sender): if not self.MTAThreadInitEvent.isSet(): # UIAHandler hasn't finished initialising yet, so just ignore this event. if _isDebug(): log.debug( "HandleFocusChangedEvent: event received while not fully initialized" ) return self.lastFocusedUIAElement = sender if not self.isNativeUIAElement(sender): # #12982: This element may be the root of an MS Word document # for which we may be refusing to use UIA as its implementation may be incomplete. # However, there are some controls embedded in the MS Word document window # such as the Modern comments side track pane # for which we do have to use UIA. # But, if focus jumps from one of these controls back to the document (E.g. the user presses escape), # we receive no MSAA focus event, only a UIA focus event. # As we are not treating the Word doc as UIA, we need to manually fire an MSAA focus event on the document. self._emitMSAAFocusForWordDocIfNecessary(sender) if _isDebug(): log.debug( "HandleFocusChangedEvent: Ignoring for non native element") return import NVDAObjects.UIA if isinstance(eventHandler.lastQueuedFocusObject, NVDAObjects.UIA.UIA): lastFocusObj = eventHandler.lastQueuedFocusObject # Ignore duplicate focus events. # It seems that it is possible for compareElements to return True, even though the objects are different. # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state. try: if (not lastFocusObj.shouldAllowDuplicateUIAFocusEvent and self.clientObject.compareElements( sender, lastFocusObj.UIAElement) and lastFocusObj.UIAElement.currentHasKeyboardFocus): if _isDebug(): log.debugWarning( "HandleFocusChangedEvent: Ignoring duplicate focus event" ) return except COMError: if _isDebug(): log.debugWarning( "HandleFocusChangedEvent: Couldn't check for duplicate focus event", exc_info=True) window = self.getNearestWindowHandle(sender) if window and not eventHandler.shouldAcceptEvent("gainFocus", windowHandle=window): if _isDebug(): log.debug( "HandleFocusChangedEvent: Ignoring for shouldAcceptEvent=False" ) return try: obj = NVDAObjects.UIA.UIA(UIAElement=sender) except Exception: if _isDebug(): log.debugWarning( "HandleFocusChangedEvent: Exception while creating object", exc_info=True) return if not obj or not obj.shouldAllowUIAFocusEvent: if _isDebug(): log.debug( "HandleFocusChangedEvent: Ignoring because no object or ignored by object itself" ) return eventHandler.queueEvent("gainFocus", obj)