2121import os
2222import time
2323import ctypes
24+ from enum import Enum
2425import logHandler
2526import languageHandler
2627import globalVars
@@ -54,7 +55,16 @@ def __getattr__(attrName: str) -> Any:
5455mainThreadId = threading .get_ident ()
5556
5657_pump = None
57- _isPumpPending = False
58+
59+
60+ class _PumpPending (Enum ):
61+ NONE = 0
62+ DELAYED = 1
63+ IMMEDIATE = 2
64+
65+ def __bool__ (self ):
66+ return self is not self .NONE
67+
5868
5969_hasShutdownBeenTriggered = False
6070_shuttingDownFlagLock = threading .Lock ()
@@ -705,11 +715,29 @@ def onEndSession(evt):
705715 # Doing this here is a bit ugly, but we don't want these modules imported
706716 # at module level, including wx.
707717 log .debug ("Initializing core pump" )
708- class CorePump (gui .NonReEntrantTimer ):
718+
719+ class CorePump (wx .Timer ):
709720 "Checks the queues and executes functions."
710- def run (self ):
711- global _isPumpPending
712- _isPumpPending = False
721+ pending = _PumpPending .NONE
722+ isPumping = False
723+
724+ def request (self ):
725+ if self .isPumping :
726+ return # Prevent re-entry.
727+ if self .pending == _PumpPending .IMMEDIATE :
728+ # A delayed pump might have been scheduled. If so, cancel it.
729+ self .Stop ()
730+ self .Notify ()
731+ elif self .pending == _PumpPending .DELAYED :
732+ self .Start (PUMP_MAX_DELAY , True )
733+
734+ def Notify (self ):
735+ if self .isPumping :
736+ log .error ("Pumping while already pumping" , stack_info = True )
737+ if not self .pending :
738+ log .error ("Pumping but pump wasn't pending" , stack_info = True )
739+ self .isPumping = True
740+ self .pending = _PumpPending .NONE
713741 watchdog .alive ()
714742 try :
715743 if touchHandler .handler :
@@ -725,10 +753,11 @@ def run(self):
725753 log .exception ("errors in this core pump cycle" )
726754 baseObject .AutoPropertyObject .invalidateCaches ()
727755 watchdog .asleep ()
728- if _isPumpPending and not _pump .IsRunning ():
756+ self .isPumping = False
757+ if self .pending :
729758 # #3803: Another pump was requested during this pump execution.
730759 # As our pump is not re-entrant, schedule another pump.
731- _pump . Start ( PUMP_MAX_DELAY , True )
760+ self . request ( )
732761 global _pump
733762 _pump = CorePump ()
734763 requestPump ()
@@ -834,23 +863,28 @@ def _terminate(module, name=None):
834863 except :
835864 log .exception ("Error terminating %s" % name )
836865
837- def requestPump ():
866+
867+ def requestPump (immediate : bool = False ):
838868 """Request a core pump.
839869 This will perform any queued activity.
840- It is delayed slightly so that queues can implement rate limiting,
841- filter extraneous events, etc.
870+ @param immediate: If True, the pump will happen as soon as possible. This
871+ should be used where response time is most important; e.g. user input or
872+ focus events.
873+ If False, it is delayed slightly so that queues can implement rate limiting,
874+ filter extraneous events, etc.
842875 """
843- global _isPumpPending
844- if not _pump or _isPumpPending :
845- return
846- _isPumpPending = True
847- if threading .get_ident () == mainThreadId :
848- _pump .Start (PUMP_MAX_DELAY , True )
876+ if not _pump :
849877 return
850- # This isn't the main thread. wx timers cannot be run outside the main thread.
851- # Therefore, Have wx start it in the main thread with a CallAfter.
852- import wx
853- wx .CallAfter (_pump .Start ,PUMP_MAX_DELAY , True )
878+ # We only need to do something if:
879+ if (
880+ # There is no pending pump.
881+ _pump .pending == _PumpPending .NONE
882+ # There is a pending delayed pump but an immediate pump was just requested.
883+ or (immediate and _pump .pending == _PumpPending .DELAYED )
884+ ):
885+ _pump .pending = _PumpPending .IMMEDIATE if immediate else _PumpPending .DELAYED
886+ import wx
887+ wx .CallAfter (_pump .request )
854888
855889
856890class NVDANotInitializedError (Exception ):
0 commit comments