Преглед изворни кода

extended controls: refactor

Fabian Peter Hammerle пре 7 година
родитељ
комит
4036c5ebd1
1 измењених фајлова са 97 додато и 103 уклоњено
  1. 97 103
      tooncher/__init__.py

+ 97 - 103
tooncher/__init__.py

@@ -155,38 +155,6 @@ def x_find_window_by_pid(display, pid):
     return x_find_window(display.screen().root, filter_callback)
 
 
-def x_grab_key(grab_window, keycode, modifiers=None):
-    if modifiers is None:
-        modifiers = X.AnyModifier
-    grab_window.grab_key(
-        keycode,
-        modifiers,
-        # owner_events
-        # https://stackoverflow.com/questions/32122360/x11-will-xgrabpointer-prevent-other-apps-from-any-mouse-event
-        # False,
-        True,
-        X.GrabModeAsync,
-        X.GrabModeAsync,
-    )
-
-
-def x_grab_keysym(xdisplay, grab_window, keysym, modifiers=None):
-    x_grab_key(
-        grab_window=grab_window,
-        keycode=xdisplay.keysym_to_keycode(keysym),
-        modifiers=modifiers,
-    )
-
-
-def x_ungrab_keysym(xdisplay, grab_window, keysym, modifiers=None):
-    if modifiers is None:
-        modifiers = X.AnyModifier
-    grab_window.ungrab_key(
-        xdisplay.keysym_to_keycode(keysym),
-        modifiers,
-    )
-
-
 def wait_for_engine_window(xdisplay, engine_process, timeout_seconds=20, search_interval_seconds=2):
     start_epoch = time.time()
     while engine_process.poll() is None and (time.time() - start_epoch) <= timeout_seconds:
@@ -200,23 +168,97 @@ def wait_for_engine_window(xdisplay, engine_process, timeout_seconds=20, search_
 
 class ExtendedControls:
 
-    def __init__(self, xdisplay, engine_window, keyboard_mapping, toggle_keysym_name):
-        self._xdisplay = xdisplay
-        self._engine_window = engine_window
+    def __init__(self, engine_process, toggle_keysym_name):
+        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',
+            ]))
+        self._engine_process = engine_process
+        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))
+        keyboard_mapping = copy.deepcopy(
+            EXTENDED_KEYBOARD_CONTROLS_DEFAULT_MAPPING
+        )
+        if self._toggle_keysym in keyboard_mapping:
+            del keyboard_mapping[self._toggle_keysym]
+            print("INFO Extended Controls:"
+                  + " Ignoring mapping for toggle key '{}'".format(toggle_keysym_name))
         self._keyboard_mapping = keyboard_mapping
+        self._engine_window = None
         self._enabled = False
-        print("INFO Extended Controls are currently off."
-              + " Press key '{}' to enable.".format(toggle_keysym_name))
+
+    def run(self):
+        self._engine_window = wait_for_engine_window(
+            self._xdisplay,
+            self._engine_process,
+        )
+        if not self._engine_window:
+            raise Exception('Could not find the game\'s window.')
+        self._grab_key(
+            self._xdisplay.keysym_to_keycode(self._toggle_keysym),
+        )
+        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))
+        while self._engine_process.poll() is None:
+            # TODO don't block here, engine might have already been stopped
+            self._handle_xevent(self._xdisplay.next_event())
+
+    def _handle_xevent(self, xevent):
+        # TODO investigate why some release events get lost
+        if isinstance(xevent, Xlib.protocol.event.KeyPress) \
+                or isinstance(xevent, Xlib.protocol.event.KeyRelease):
+            self._handle_xkeyevent(xevent)
+
+    def _handle_xkeyevent(self, xkeyevent):
+        # TODO map keycodes instead of keysyms
+        keysym_in = self._xdisplay.keycode_to_keysym(
+            xkeyevent.detail,
+            index=0,
+        )
+        if keysym_in == self._toggle_keysym:
+            if isinstance(xkeyevent, Xlib.protocol.event.KeyPress):
+                self.toggle()
+        else:
+            if self.enabled and keysym_in in self._keyboard_mapping:
+                keysym_out = self._keyboard_mapping[keysym_in]
+            else:
+                keysym_out = keysym_in
+            self._engine_window.send_event(type(xkeyevent)(
+                window=self._engine_window,
+                detail=self._xdisplay.keysym_to_keycode(keysym_out),
+                state=0,
+                root_x=xkeyevent.root_x,
+                root_y=xkeyevent.root_y,
+                event_x=xkeyevent.event_x,
+                event_y=xkeyevent.event_y,
+                child=xkeyevent.child,
+                root=xkeyevent.root,
+                time=xkeyevent.time,  # X.CurrentTime
+                same_screen=xkeyevent.same_screen,
+            ))
 
     def enable(self):
         for keysym in self._keyboard_mapping.keys():
-            x_grab_keysym(self._xdisplay, self._engine_window, keysym)
+            self._grab_key(
+                self._xdisplay.keysym_to_keycode(keysym),
+            )
         self._enabled = True
         print("INFO Enabled Extended Controls")
 
     def disable(self):
         for keysym in self._keyboard_mapping.keys():
-            x_ungrab_keysym(self._xdisplay, self._engine_window, keysym)
+            self._ungrab_key(
+                self._xdisplay.keysym_to_keycode(keysym),
+            )
         self._enabled = False
         print("INFO Disabled Extended Controls")
 
@@ -230,68 +272,20 @@ class ExtendedControls:
         else:
             self.enable()
 
-def run_extended_keyboard_controls(engine_process, toggle_keysym_name):
-    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',
-        ]))
-    xdisplay = Xlib.display.Display()
-    engine_window = wait_for_engine_window(xdisplay, engine_process)
-    if not engine_window:
-        raise Exception('Could not find the game\'s window.')
-    toggle_keysym = XK.string_to_keysym(toggle_keysym_name)
-    if toggle_keysym == X.NoSymbol:
-        raise Exception("Extended keyboard controls toggle:"
-                        + " Unknown keysym name '{}'".format(toggle_keysym_name))
-    x_grab_keysym(xdisplay, engine_window, toggle_keysym)
-    keyboard_mapping = copy.deepcopy(
-        EXTENDED_KEYBOARD_CONTROLS_DEFAULT_MAPPING
-    )
-    if toggle_keysym in keyboard_mapping:
-        del keyboard_mapping[toggle_keysym]
-        print("INFO Extended Controls:"
-              + " Ignoring mapping for toggle key '{}'".format(toggle_keysym_name))
-    ec = ExtendedControls(
-        xdisplay,
-        engine_window,
-        keyboard_mapping,
-        toggle_keysym_name,
-    )
-    while engine_process.poll() is None:
-        # TODO don't block here, engine might have already been stopped
-        xevent = xdisplay.next_event()
-        # TODO investigate why some release events get lost
-        if isinstance(xevent, Xlib.protocol.event.KeyPress) \
-                or isinstance(xevent, Xlib.protocol.event.KeyRelease):
-            keysym_in = xdisplay.keycode_to_keysym(
-                xevent.detail,
-                index=0,
-            )
-            if keysym_in == toggle_keysym:
-                if isinstance(xevent, Xlib.protocol.event.KeyPress):
-                    ec.toggle()
-            else:
-                if ec.enabled and keysym_in in keyboard_mapping:
-                    keysym_out = keyboard_mapping[keysym_in]
-                else:
-                    keysym_out = keysym_in
-                engine_window.send_event(type(xevent)(
-                    window=engine_window,
-                    detail=xdisplay.keysym_to_keycode(keysym_out),
-                    state=0,
-                    root_x=xevent.root_x,
-                    root_y=xevent.root_y,
-                    event_x=xevent.event_x,
-                    event_y=xevent.event_y,
-                    child=xevent.child,
-                    root=xevent.root,
-                    time=xevent.time,  # X.CurrentTime
-                    same_screen=xevent.same_screen,
-                ))
+    def _grab_key(self, keycode):
+        self._engine_window.grab_key(
+            keycode,
+            X.AnyModifier,
+            # owner_events
+            # https://stackoverflow.com/questions/32122360/x11-will-xgrabpointer-prevent-other-apps-from-any-mouse-event
+            # False,
+            True,
+            X.GrabModeAsync,
+            X.GrabModeAsync,
+        )
+
+    def _ungrab_key(self, keycode):
+        self._engine_window.ungrab_key(keycode, X.AnyModifier)
 
 
 def launch(engine_path, username, password, validate_ssl_certs=True,
@@ -322,10 +316,10 @@ def launch(engine_path, username, password, validate_ssl_certs=True,
             ])
         if enable_extended_keyboard_controls:
             try:
-                run_extended_keyboard_controls(
+                ExtendedControls(
                     engine_process=p,
                     toggle_keysym_name=extended_keyboard_control_toggle_keysym_name,
-                )
+                ).run()
             except Exception as e:
                 if isinstance(e, KeyboardInterrupt):
                     raise e