def _shouldGetEvents(): global _deferUntilForegroundWindow, _foregroundDefers if _deferUntilForegroundWindow: # #3831: Sometimes, a foreground event is fired, # but GetForegroundWindow() takes a short while to return this new foreground. curForegroundWindow = winUser.getForegroundWindow() curForegroundClassName = winUser.getClassName(curForegroundWindow) futureForegroundClassName = winUser.getClassName( _deferUntilForegroundWindow) if (_foregroundDefers < MAX_FOREGROUND_DEFERS and curForegroundWindow != _deferUntilForegroundWindow): # Wait a core cycle before handling events to give the foreground window time to update. core.requestPump() _foregroundDefers += 1 if isMSAADebugLoggingEnabled(): log.debugWarning( f"Foreground still {curForegroundWindow} ({curForegroundClassName}). " f"Deferring until foreground is {_deferUntilForegroundWindow} ({futureForegroundClassName}), " f"defer count {_foregroundDefers}") return False 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.) if curForegroundWindow != _deferUntilForegroundWindow: log.debugWarning( "Foreground took too long to change. " f"Foreground still {curForegroundWindow} ({curForegroundClassName}). " f"Should be {_deferUntilForegroundWindow} ({futureForegroundClassName})" ) _deferUntilForegroundWindow = None return True
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 winEventCallback(handle,eventID,window,objectID,childID,threadID,timestamp): try: #Ignore all object IDs from alert onwards (sound, nativeom etc) as we don't support them if objectID<=winUser.OBJID_ALERT: return #Ignore all locationChange events except ones for the caret if eventID==winUser.EVENT_OBJECT_LOCATIONCHANGE and objectID!=winUser.OBJID_CARET: return if eventID==winUser.EVENT_OBJECT_DESTROY: processDestroyWinEvent(window,objectID,childID) return #Change window objIDs to client objIDs for better reporting of objects if (objectID==0) and (childID==0): objectID=winUser.OBJID_CLIENT #Ignore events with invalid window handles isWindow = winUser.isWindow(window) if window else 0 if window==0 or (not isWindow and eventID in (winUser.EVENT_SYSTEM_SWITCHSTART,winUser.EVENT_SYSTEM_SWITCHEND,winUser.EVENT_SYSTEM_MENUEND,winUser.EVENT_SYSTEM_MENUPOPUPEND)): window=winUser.getDesktopWindow() elif not isWindow: return if childID<0: tempWindow=window while tempWindow and not winUser.getWindowStyle(tempWindow)&winUser.WS_POPUP and winUser.getClassName(tempWindow)=="MozillaWindowClass": tempWindow=winUser.getAncestor(tempWindow,winUser.GA_PARENT) if tempWindow and winUser.getClassName(tempWindow).startswith('Mozilla'): window=tempWindow windowClassName=winUser.getClassName(window) # Modern IME candidate list windows fire menu events which confuse us # and can't be used properly in conjunction with input composition support. if windowClassName=="Microsoft.IME.UIManager.CandidateWindow.Host" and eventID in MENU_EVENTIDS: return #At the moment we can't handle show, hide or reorder events on Mozilla Firefox Location bar,as there are just too many of them #Ignore show, hide and reorder on MozillaDropShadowWindowClass windows. if windowClassName.startswith('Mozilla') and eventID in (winUser.EVENT_OBJECT_SHOW,winUser.EVENT_OBJECT_HIDE,winUser.EVENT_OBJECT_REORDER) and childID<0: #Mozilla Gecko can sometimes fire win events on a catch-all window which isn't really the real window #Move up the ancestry to find the real mozilla Window and use that if winUser.getClassName(window)=='MozillaDropShadowWindowClass': return if eventID==winUser.EVENT_SYSTEM_FOREGROUND: #We never want to see foreground events for the Program Manager or Shell (task bar) if windowClassName in ("Progman","Shell_TrayWnd"): return # #3831: Event handling can be deferred if Windows takes a while to change the foreground window. # See pumpAll for details. global _deferUntilForegroundWindow,_foregroundDefers _deferUntilForegroundWindow=window _foregroundDefers=0 if windowClassName=="MSNHiddenWindowClass": # HACK: Events get fired by this window in Windows Live Messenger 2009 when it starts. # If we send a WM_NULL to this window at this point (which happens in accessibleObjectFromEvent), Messenger will silently exit (#677). # Therefore, completely ignore these events, which is useless to us anyway. return if winEventLimiter.addEvent(eventID,window,objectID,childID,threadID): core.requestPump() except: log.error("winEventCallback", exc_info=True)
def registerGeneratorObject(generatorObj): global generators, lastGeneratorObjID if not isinstance(generatorObj, types.GeneratorType): raise TypeError("Arg 2 must be a generator object, not %s" % type(generatorObj)) lastGeneratorObjID += 1 log.debug("Adding generator %d" % lastGeneratorObjID) generators[lastGeneratorObjID] = generatorObj core.requestPump() return lastGeneratorObjID
def registerGeneratorObject(generatorObj): global generators,lastGeneratorObjID if not isinstance(generatorObj,types.GeneratorType): raise TypeError('Arg 2 must be a generator object, not %s'%type(generatorObj)) lastGeneratorObjID+=1 log.debug("Adding generator %d"%lastGeneratorObjID) generators[lastGeneratorObjID]=generatorObj core.requestPump() return lastGeneratorObjID
def winEventCallback(handle,eventID,window,objectID,childID,threadID,timestamp): try: #Ignore all object IDs from alert onwards (sound, nativeom etc) as we don't support them if objectID<=winUser.OBJID_ALERT: return #Ignore all locationChange events except ones for the caret if eventID==winUser.EVENT_OBJECT_LOCATIONCHANGE and objectID!=winUser.OBJID_CARET: return if eventID==winUser.EVENT_OBJECT_DESTROY: processDestroyWinEvent(window,objectID,childID) return #Change window objIDs to client objIDs for better reporting of objects if (objectID==0) and (childID==0): objectID=winUser.OBJID_CLIENT #Ignore events with invalid window handles isWindow = winUser.isWindow(window) if window else 0 if window==0 or (not isWindow and eventID in (winUser.EVENT_SYSTEM_SWITCHSTART,winUser.EVENT_SYSTEM_SWITCHEND,winUser.EVENT_SYSTEM_MENUEND,winUser.EVENT_SYSTEM_MENUPOPUPEND)): window=winUser.getDesktopWindow() elif not isWindow: return if childID<0: tempWindow=window while tempWindow and not winUser.getWindowStyle(tempWindow)&winUser.WS_POPUP and winUser.getClassName(tempWindow)=="MozillaWindowClass": tempWindow=winUser.getAncestor(tempWindow,winUser.GA_PARENT) if tempWindow and winUser.getClassName(tempWindow).startswith('Mozilla'): window=tempWindow windowClassName=winUser.getClassName(window) #At the moment we can't handle show, hide or reorder events on Mozilla Firefox Location bar,as there are just too many of them #Ignore show, hide and reorder on MozillaDropShadowWindowClass windows. if windowClassName.startswith('Mozilla') and eventID in (winUser.EVENT_OBJECT_SHOW,winUser.EVENT_OBJECT_HIDE,winUser.EVENT_OBJECT_REORDER) and childID<0: #Mozilla Gecko can sometimes fire win events on a catch-all window which isn't really the real window #Move up the ancestry to find the real mozilla Window and use that if winUser.getClassName(window)=='MozillaDropShadowWindowClass': return if eventID==winUser.EVENT_SYSTEM_FOREGROUND: #We never want to see foreground events for the Program Manager or Shell (task bar) if windowClassName in ("Progman","Shell_TrayWnd"): return # #3831: Event handling can be deferred if Windows takes a while to change the foreground window. # See pumpAll for details. global _deferUntilForegroundWindow,_foregroundDefers _deferUntilForegroundWindow=window _foregroundDefers=0 if windowClassName=="MSNHiddenWindowClass": # HACK: Events get fired by this window in Windows Live Messenger 2009 when it starts. # If we send a WM_NULL to this window at this point (which happens in accessibleObjectFromEvent), Messenger will silently exit (#677). # Therefore, completely ignore these events, which is useless to us anyway. return if winEventLimiter.addEvent(eventID,window,objectID,childID,threadID): core.requestPump() except: log.error("winEventCallback", exc_info=True)
def inputTouchWndProc(self,hwnd,msg,wParam,lParam): if msg>=_WM_POINTER_FIRST and msg<=_WM_POINTER_LAST: flags=winUser.HIWORD(wParam) touching=(flags&POINTER_MESSAGE_FLAG_INRANGE) and (flags&POINTER_MESSAGE_FLAG_FIRSTBUTTON) x=winUser.LOWORD(lParam) y=winUser.HIWORD(lParam) ID=winUser.LOWORD(wParam) if touching: self.trackerManager.update(ID,x,y,False) core.requestPump() elif not flags&POINTER_MESSAGE_FLAG_FIRSTBUTTON: self.trackerManager.update(ID,x,y,True) core.requestPump() return 0 return windll.user32.DefWindowProcW(hwnd,msg,wParam,lParam)
def inputTouchWndProc(self,hwnd,msg,wParam,lParam): if msg>=_WM_POINTER_FIRST and msg<=_WM_POINTER_LAST: flags=winUser.HIWORD(wParam) touching=(flags&POINTER_MESSAGE_FLAG_INRANGE) and (flags&POINTER_MESSAGE_FLAG_FIRSTBUTTON) x=winUser.GET_X_LPARAM(lParam) y=winUser.GET_Y_LPARAM(lParam) ID=winUser.LOWORD(wParam) if touching: self.trackerManager.update(ID,x,y,False) core.requestPump() elif not flags&POINTER_MESSAGE_FLAG_FIRSTBUTTON: self.trackerManager.update(ID,x,y,True) core.requestPump() return 0 return windll.user32.DefWindowProcW(hwnd,msg,wParam,lParam)
def internal_mouseEvent(msg,x,y,injected): global mouseMoved, curMousePos, lastMouseEventTime lastMouseEventTime=time.time() if injected: return True if not config.conf['mouse']['enableMouseTracking']: return True try: curMousePos=(x,y) if msg==WM_MOUSEMOVE: mouseMoved=True core.requestPump() elif msg in (WM_LBUTTONDOWN,WM_RBUTTONDOWN): queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech) except: log.error("", exc_info=True) return True
def internal_mouseEvent(msg,x,y,injected): """Event called by winInputHook when it receives a mouse event. """ global mouseMoved, curMousePos, lastMouseEventTime lastMouseEventTime=time.time() if injected and (ignoreInjected or config.conf['mouse']['ignoreInjectedMouseInput']): return True if not config.conf['mouse']['enableMouseTracking']: return True try: curMousePos=(x,y) if msg==WM_MOUSEMOVE: mouseMoved=True core.requestPump() elif msg in (WM_LBUTTONDOWN,WM_RBUTTONDOWN): queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech) except: log.error("", exc_info=True) return True
def _shouldGetEvents(): 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 False 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 return True
def pumpAll(): # This dict can mutate during iteration, so use keys(). for ID in generators.keys(): # KeyError could occur within the generator itself, so retrieve the generator first. try: gen = generators[ID] except KeyError: # Generator was cancelled. This is fine. continue watchdog.alive() try: next(gen) except StopIteration: log.debug("generator %s finished" % ID) del generators[ID] except: log.exception("error in generator %d" % ID) del generators[ID] # Lose our reference so Python can destroy the generator if appropriate. del gen if generators: core.requestPump() flushQueue(eventQueue)
def pumpAll(): # This dict can mutate during iteration, so wrap the keys in a list. for ID in list(generators): # KeyError could occur within the generator itself, so retrieve the generator first. try: gen = generators[ID] except KeyError: # Generator was cancelled. This is fine. continue watchdog.alive() try: next(gen) except StopIteration: log.debug("generator %s finished"%ID) del generators[ID] except: log.exception("error in generator %d"%ID) del generators[ID] # Lose our reference so Python can destroy the generator if appropriate. del gen if generators: core.requestPump() flushQueue(eventQueue)
def queueFunction(queue, func, *args, **kwargs): queue.put_nowait((func, args, kwargs)) core.requestPump()
def winEventCallback(handle, eventID, window, objectID, childID, threadID, timestamp): # noqa: C901 if isMSAADebugLoggingEnabled(): log.debug( f"Hook received winEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) try: # Ignore all object IDs from alert onwards (sound, nativeom etc) as we don't support them if objectID <= winUser.OBJID_ALERT: if isMSAADebugLoggingEnabled(): log.debug( f"objectID not supported. " f"Dropping winEvent {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) return # Ignore all locationChange events except ones for the caret if eventID == winUser.EVENT_OBJECT_LOCATIONCHANGE and objectID != winUser.OBJID_CARET: if isMSAADebugLoggingEnabled(): log.debug( f"locationChange for something other than the caret. " f"Dropping winEvent {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) return if eventID == winUser.EVENT_OBJECT_DESTROY: _processDestroyWinEvent(window, objectID, childID) return # Change window objIDs to client objIDs for better reporting of objects if (objectID == 0) and (childID == 0): objectID = winUser.OBJID_CLIENT if isMSAADebugLoggingEnabled(): log.debug( f"Changing OBJID_WINDOW to OBJID_CLIENT " f"for winEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) # Ignore events with invalid window handles isWindow = winUser.isWindow(window) if window else 0 if window == 0 or (not isWindow and eventID in ( winUser.EVENT_SYSTEM_SWITCHSTART, winUser.EVENT_SYSTEM_SWITCHEND, winUser.EVENT_SYSTEM_MENUEND, winUser.EVENT_SYSTEM_MENUPOPUPEND, )): if isMSAADebugLoggingEnabled(): log.debug( f"Changing NULL or invalid window to desktop window " f"for winEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) window = winUser.getDesktopWindow() elif not isWindow: if isMSAADebugLoggingEnabled(): log.debug( f"Invalid window. " f"Dropping winEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) return windowClassName = winUser.getClassName(window) if windowClassName == "ConsoleWindowClass": # #10113: we need to use winEvents to track the real thread for console windows. consoleWindowsToThreadIDs[window] = threadID # Modern IME candidate list windows fire menu events which confuse us # and can't be used properly in conjunction with input composition support. if windowClassName == "Microsoft.IME.UIManager.CandidateWindow.Host" and eventID in MENU_EVENTIDS: if isMSAADebugLoggingEnabled(): log.debug( f"Dropping menu event for IME window. " f"WinEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) return if eventID == winUser.EVENT_SYSTEM_FOREGROUND: # We never want to see foreground events for the Program Manager or Shell (task bar) if windowClassName in ("Progman", "Shell_TrayWnd"): if isMSAADebugLoggingEnabled(): log.debug( f"Progman or shell_trayWnd window. " f"Dropping winEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) return # #3831: Event handling can be deferred if Windows takes a while to change the foreground window. # See pumpAll for details. global _deferUntilForegroundWindow, _foregroundDefers _deferUntilForegroundWindow = window _foregroundDefers = 0 if isMSAADebugLoggingEnabled(): log.debug( f"Recording foreground defer " f"for WinEvent: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) if windowClassName == "MSNHiddenWindowClass": # HACK: Events get fired by this window in Windows Live Messenger 2009 when it starts. If we send a # WM_NULL to this window at this point (which happens in accessibleObjectFromEvent), Messenger will # silently exit (#677). Therefore, completely ignore these events, which is useless to us anyway. return if isMSAADebugLoggingEnabled(): log.debug( f"Adding winEvent to limiter: {getWinEventLogInfo(window, objectID, childID, eventID, threadID)}" ) if winEventLimiter.addEvent(eventID, window, objectID, childID, threadID): core.requestPump() except Exception: log.error("winEventCallback", exc_info=True)
def internalQueueFunction(func,*args,**kwargs): internalFunctionQueue.put_nowait((func,args,kwargs)) core.requestPump()
def queueFunction(queue,func,*args,**kwargs): queue.put_nowait((func,args,kwargs)) core.requestPump()