| 
					
				 | 
			
			
				@@ -3,6 +3,7 @@ import os 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import select 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from tooncher.actions import * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     import psutil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 except ImportError: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -13,29 +14,43 @@ try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 except ImportError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     Xlib = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-EXTENDED_CONTROLS_DEFAULT_TOGGLE_KEYSYM_NAME = 'grave' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-EXTENDED_CONTROLS_PID_XPROPERTY_NAME = '_TOONCHER_EXTENDED_CONTROLS_PID' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-TOONTOWN_WINDOW_NAME = 'Toontown Rewritten' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+EXTENDED_CONTROLS_DEFAULT_TOGGLE_KEYSYM_NAME = "grave" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+EXTENDED_CONTROLS_PID_XPROPERTY_NAME = "_TOONCHER_EXTENDED_CONTROLS_PID" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+TOONTOWN_WINDOW_NAME = "Toontown Rewritten" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 if Xlib: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     EXTENDED_CONTROLS_DEFAULT_KEYSYM_MAPPINGS = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_w:         RewriteKeyEventAction(keysym=XK.XK_Up,         target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_a:         RewriteKeyEventAction(keysym=XK.XK_Left,       target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_s:         RewriteKeyEventAction(keysym=XK.XK_Down,       target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_d:         RewriteKeyEventAction(keysym=XK.XK_Right,      target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_Control_L: RewriteKeyEventAction(keysym=XK.XK_Control_L,  target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_v:         RewriteKeyEventAction(keysym=XK.XK_Delete,     target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_i:         RewriteKeyEventAction(keysym=XK.XK_Up,         target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_j:         RewriteKeyEventAction(keysym=XK.XK_Left,       target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_k:         RewriteKeyEventAction(keysym=XK.XK_Down,       target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_l:         RewriteKeyEventAction(keysym=XK.XK_Right,      target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_slash:     RewriteKeyEventAction(keysym=XK.XK_Control_L,  target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_n:         RewriteKeyEventAction(keysym=XK.XK_Delete,     target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_space:     RewriteKeyEventAction(keysym=XK.XK_Control_L,  target_engine_index=TargetEngine.All), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_e:         SelectGagAction(target_engine_index=0, column_index=4, factor_y=-0.047),  # elephant trunk 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_o:         SelectGagAction(target_engine_index=1, column_index=4, factor_y=-0.047),  # elephant trunk 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_f:         SelectGagAction(target_engine_index=0, column_index=5, factor_y=-0.047),  # foghorn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        XK.XK_semicolon: SelectGagAction(target_engine_index=1, column_index=5, factor_y=-0.047),  # foghorn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_w: RewriteKeyEventAction(keysym=XK.XK_Up, target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_a: RewriteKeyEventAction(keysym=XK.XK_Left, target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_s: RewriteKeyEventAction(keysym=XK.XK_Down, target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_d: RewriteKeyEventAction(keysym=XK.XK_Right, target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_Control_L: RewriteKeyEventAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            keysym=XK.XK_Control_L, target_engine_index=0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_v: RewriteKeyEventAction(keysym=XK.XK_Delete, target_engine_index=0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_i: RewriteKeyEventAction(keysym=XK.XK_Up, target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_j: RewriteKeyEventAction(keysym=XK.XK_Left, target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_k: RewriteKeyEventAction(keysym=XK.XK_Down, target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_l: RewriteKeyEventAction(keysym=XK.XK_Right, target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_slash: RewriteKeyEventAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            keysym=XK.XK_Control_L, target_engine_index=1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_n: RewriteKeyEventAction(keysym=XK.XK_Delete, target_engine_index=1), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_space: RewriteKeyEventAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            keysym=XK.XK_Control_L, target_engine_index=TargetEngine.All 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_e: SelectGagAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            target_engine_index=0, column_index=4, factor_y=-0.047 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ),  # elephant trunk 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_o: SelectGagAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            target_engine_index=1, column_index=4, factor_y=-0.047 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ),  # elephant trunk 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_f: SelectGagAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            target_engine_index=0, column_index=5, factor_y=-0.047 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ),  # foghorn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        XK.XK_semicolon: SelectGagAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            target_engine_index=1, column_index=5, factor_y=-0.047 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ),  # foghorn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -49,11 +64,12 @@ def x_find_window(parent_window, filter_callback): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 def x_find_window_by_pid(display, pid): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    pid_prop = display.intern_atom('_NET_WM_PID') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pid_prop = display.intern_atom("_NET_WM_PID") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def filter_callback(window): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         prop = window.get_full_property(pid_prop, X.AnyPropertyType) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return prop and prop.value.tolist() == [pid] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return x_find_window(display.screen().root, filter_callback) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -71,40 +87,54 @@ def x_wait_for_event(xdisplay, timeout_seconds): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def __init__(self, primary_engine_pid, primary_engine_window_name=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 toggle_keysym_name=EXTENDED_CONTROLS_DEFAULT_TOGGLE_KEYSYM_NAME): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        primary_engine_pid, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        primary_engine_window_name=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        toggle_keysym_name=EXTENDED_CONTROLS_DEFAULT_TOGGLE_KEYSYM_NAME, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not psutil: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            raise Exception('\n'.join([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                'Extended keyboard controls require the python lib psutil to be installed.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                'Depending on your system run', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                '\t$ sudo apt-get install python3-psutil', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                'or', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                '\t$ pip3 install --user psutil', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ])) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise Exception( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "\n".join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "Extended keyboard controls require the python lib psutil to be installed.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "Depending on your system run", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "\t$ sudo apt-get install python3-psutil", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "or", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "\t$ pip3 install --user psutil", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not Xlib: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            raise Exception('\n'.join([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                'Extended keyboard controls require xlib for python to be installed.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                'Depending on your system run', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                '\t$ sudo apt-get install python3-xlib', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                'or', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                '\t$ pip3 install --user xlib', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ])) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise Exception( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "\n".join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "Extended keyboard controls require xlib for python to be installed.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "Depending on your system run", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "\t$ sudo apt-get install python3-xlib", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "or", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "\t$ pip3 install --user xlib", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._primary_engine_pid = primary_engine_pid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._primary_engine_window_name = primary_engine_window_name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._xdisplay = Xlib.display.Display() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._toggle_keysym = XK.string_to_keysym(toggle_keysym_name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if self._toggle_keysym == X.NoSymbol: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            raise Exception("Extended keyboard controls toggle:" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            + " Unknown keysym name '{}'".format(toggle_keysym_name)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise Exception( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "Extended keyboard controls toggle:" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                + " Unknown keysym name '{}'".format(toggle_keysym_name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._keysym_mappings = copy.deepcopy( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             EXTENDED_CONTROLS_DEFAULT_KEYSYM_MAPPINGS, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if self._toggle_keysym in self._keysym_mappings: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            print("INFO Extended Controls:" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                  + " Ignoring mapping for toggle key '{}'".format(toggle_keysym_name)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self._keysym_mappings[self._toggle_keysym] \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            = ToggleExtendedControlsAction() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            print( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "INFO Extended Controls:" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                + " Ignoring mapping for toggle key '{}'".format(toggle_keysym_name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._keysym_mappings[self._toggle_keysym] = ToggleExtendedControlsAction() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._default_action = ForwardKeyEventAction() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._primary_engine_window = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._engine_windows_by_target_index = None 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -126,10 +156,7 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def _wait_for_engine_window(self, timeout_seconds=20, search_interval_seconds=2): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         start_epoch = time.time() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         while self.engine_running and (time.time() - start_epoch) <= timeout_seconds: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            windows = x_find_window_by_pid( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self._xdisplay, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self._primary_engine_pid, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            windows = x_find_window_by_pid(self._xdisplay, self._primary_engine_pid,) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             assert len(windows) <= 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if len(windows) == 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return windows[0] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -139,10 +166,8 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def run(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._primary_engine_window = self._wait_for_engine_window() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not self._primary_engine_window: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            raise Exception('Could not find the game\'s window.') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self._grab_key( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self._xdisplay.keysym_to_keycode(self._toggle_keysym), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise Exception("Could not find the game's window.") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._grab_key(self._xdisplay.keysym_to_keycode(self._toggle_keysym),) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._primary_engine_window.change_property( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.xdisplay.intern_atom(EXTENDED_CONTROLS_PID_XPROPERTY_NAME), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Xatom.CARDINAL, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -151,16 +176,18 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             mode=X.PropModeReplace, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if self._primary_engine_window_name: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self._primary_engine_window.set_wm_name( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self._primary_engine_window_name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self._primary_engine_window.set_wm_name(self._primary_engine_window_name,) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            print( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "INFO Changed engine's window name to {!r}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self._primary_engine_window_name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            print("INFO Changed engine's window name to {!r}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self._primary_engine_window_name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            )) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not self.enabled: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             keysym_name = XK.keysym_to_string(self._toggle_keysym) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            print("INFO Extended Controls are currently disabled." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                  + " Press key '{}' to enable.".format(keysym_name)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            print( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "INFO Extended Controls are currently disabled." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                + " Press key '{}' to enable.".format(keysym_name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         while self.engine_running: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             while self.xdisplay.pending_events(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 self._handle_xevent(self._xdisplay.next_event()) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -170,16 +197,14 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             x_wait_for_event(self.xdisplay, timeout_seconds=0.05) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def _handle_xevent(self, xevent): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if isinstance(xevent, Xlib.protocol.event.KeyPress) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                or isinstance(xevent, Xlib.protocol.event.KeyRelease): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if isinstance(xevent, Xlib.protocol.event.KeyPress) or isinstance( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            xevent, Xlib.protocol.event.KeyRelease 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self._handle_xkeyevent(xevent) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def _handle_xkeyevent(self, xkeyevent): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._update_active_key_registry(xkeyevent) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        keysym_in = self._xdisplay.keycode_to_keysym( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            xkeyevent.detail, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            index=0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        keysym_in = self._xdisplay.keycode_to_keysym(xkeyevent.detail, index=0,) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if keysym_in in self._keysym_mappings: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             action = self._keysym_mappings[keysym_in] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         else: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -189,9 +214,7 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def enable(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for keysym in self._keysym_mappings.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if keysym != self._toggle_keysym: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self._grab_key( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self._xdisplay.keysym_to_keycode(keysym), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                self._grab_key(self._xdisplay.keysym_to_keycode(keysym),) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._enabled = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         # reset cache 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._engine_windows_by_target_index = None 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -200,9 +223,7 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def disable(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for keysym in self._keysym_mappings.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if keysym != self._toggle_keysym: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self._ungrab_key( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self._xdisplay.keysym_to_keycode(keysym), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                self._ungrab_key(self._xdisplay.keysym_to_keycode(keysym),) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._enabled = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         print("INFO Disabled Extended Controls") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -238,7 +259,7 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return x_find_window( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.xdisplay.screen().root, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             lambda w: w.get_wm_name() == TOONTOWN_WINDOW_NAME 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                or w.get_full_property(controls_xprop, X.AnyPropertyType), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            or w.get_full_property(controls_xprop, X.AnyPropertyType), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @property 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -246,10 +267,11 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not self._engine_windows_by_target_index: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             win_by_index = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             for target_index, win in enumerate(self.find_engine_windows()): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                print('INFO Engine window {} has no target index, assuming {}'.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    win.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    target_index, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                )) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                print( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    "INFO Engine window {} has no target index, assuming {}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        win.id, target_index, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if not target_index in win_by_index: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     win_by_index[target_index] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 win_by_index[target_index].append(win) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -286,18 +308,20 @@ class ExtendedControls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             bit_index = keycode & ((1 << 3) - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if not keymap[byte_index] & (1 << bit_index): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 print("DEBUG missed release event of key {}".format(keycode)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                missed_releases.append(Xlib.protocol.event.KeyRelease( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    window=press_event.window, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    detail=press_event.detail, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    state=press_event.state, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    root_x=press_event.root_x, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    root_y=press_event.root_y, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    event_x=press_event.event_x, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    event_y=press_event.event_y, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    child=press_event.child, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    root=press_event.root, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    time=X.CurrentTime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    same_screen=press_event.same_screen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                )) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                missed_releases.append( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Xlib.protocol.event.KeyRelease( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        window=press_event.window, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        detail=press_event.detail, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        state=press_event.state, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        root_x=press_event.root_x, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        root_y=press_event.root_y, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        event_x=press_event.event_x, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        event_y=press_event.event_y, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        child=press_event.child, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        root=press_event.root, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        time=X.CurrentTime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        same_screen=press_event.same_screen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for release_event in missed_releases: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self._handle_xkeyevent(release_event) 
			 |