@@ -241,6 +241,12 @@ def __init__(self):
241241 self ._startup_ok = False
242242 self ._prev_raw_buttons = {}
243243 self ._last_rehook_time = 0
244+ # Serializes gesture begin/end transitions across the LL hook
245+ # thread (XBUTTON), the Raw Input window proc thread, and the HID
246+ # listener thread. Held only around the `_gesture_active` /
247+ # `_gesture_triggered` flips -- dispatch happens outside so an
248+ # engine callback that re-enters the hook never deadlocks.
249+ self ._gesture_lock = threading .Lock ()
244250 self ._init_dispatch_queue (maxsize = 512 )
245251 self ._dispatch_worker_thread = None
246252
@@ -562,15 +568,20 @@ def _check_raw_mouse_gesture(self, hDevice, buffer):
562568 if extra_now == extra_prev :
563569 return
564570 if extra_now and not extra_prev :
565- if not self ._gesture_active :
571+ with self ._gesture_lock :
572+ if self ._gesture_active :
573+ return
566574 self ._gesture_active = True
567575 self ._gesture_triggered = False
568- print (f"[MouseHook] Gesture DOWN (rawBtns extra: 0x{ extra_now :X} )" )
569- elif not extra_now and extra_prev :
570- if self ._gesture_active :
576+ print (f"[MouseHook] Gesture DOWN (rawBtns extra: 0x{ extra_now :X} )" )
577+ return
578+ if not extra_now and extra_prev :
579+ with self ._gesture_lock :
580+ if not self ._gesture_active :
581+ return
571582 self ._gesture_active = False
572- print ("[MouseHook] Gesture UP" )
573- self ._dispatch (MouseEvent (MouseEvent .GESTURE_CLICK ))
583+ print ("[MouseHook] Gesture UP" )
584+ self ._dispatch (MouseEvent (MouseEvent .GESTURE_CLICK ))
574585
575586 def _setup_raw_input (self ):
576587 instance = GetModuleHandleW (None )
@@ -709,34 +720,42 @@ def _reinstall_hook(self):
709720 print ("[MouseHook] Failed to reinstall hook!" )
710721
711722 def _on_hid_gesture_down (self ):
712- if not self ._gesture_active :
723+ with self ._gesture_lock :
724+ if self ._gesture_active :
725+ return
713726 self ._gesture_active = True
714727 self ._gesture_triggered = False
715- self ._emit_debug ("HID gesture button down" )
716- self ._emit_gesture_event ({"type" : "button_down" })
717- if self ._gesture_direction_enabled and not self ._gesture_cooldown_active ():
728+ tracking = (
729+ self ._gesture_direction_enabled
730+ and not self ._gesture_cooldown_active ()
731+ )
732+ if tracking :
718733 self ._start_gesture_tracking ()
719734 else :
720735 self ._gesture_tracking = False
721- self ._gesture_triggered = False
736+ self ._emit_debug ("HID gesture button down" )
737+ self ._emit_gesture_event ({"type" : "button_down" })
722738
723739 def _on_hid_gesture_up (self ):
724- if self ._gesture_active :
740+ should_click = False
741+ with self ._gesture_lock :
742+ if not self ._gesture_active :
743+ return
725744 should_click = not self ._gesture_triggered
726745 self ._gesture_active = False
727746 self ._finish_gesture_tracking ()
728747 self ._gesture_triggered = False
729- self ._emit_debug (
730- f"HID gesture button up click_candidate={ str (should_click ).lower ()} "
731- )
732- self ._emit_gesture_event (
733- {
734- "type" : "button_up" ,
735- "click_candidate" : should_click ,
736- }
737- )
738- if should_click :
739- self ._dispatch (MouseEvent (MouseEvent .GESTURE_CLICK ))
748+ self ._emit_debug (
749+ f"HID gesture button up click_candidate={ str (should_click ).lower ()} "
750+ )
751+ self ._emit_gesture_event (
752+ {
753+ "type" : "button_up" ,
754+ "click_candidate" : should_click ,
755+ }
756+ )
757+ if should_click :
758+ self ._dispatch (MouseEvent (MouseEvent .GESTURE_CLICK ))
740759
741760 def _on_hid_mode_shift_down (self ):
742761 self ._emit_debug ("HID mode shift button down" )
0 commit comments