فهرست منبع

extended controls: added toggle to turn on / off

Fabian Peter Hammerle 6 سال پیش
والد
کامیت
60f406ee01
3فایلهای تغییر یافته به همراه106 افزوده شده و 26 حذف شده
  1. 10 0
      README.md
  2. 13 1
      scripts/tooncher
  3. 83 25
      tooncher/__init__.py

+ 10 - 0
README.md

@@ -52,6 +52,16 @@ launch tooncher with the `--extended-controls` option:
 $ tooncher --extended-controls username
 ```
 
+While in the game press `` ` `` (grave) to enable extended controls.
+Press `` ` `` again to disable.
+
+The command line parameter `--extended-controls-toggle` may be used
+to change the toggling key:
+
+```{sh}
+$ tooncher --extended-controls --extended-controls-toggle slash username 
+```
+
 Extended controls require the X Window System (X11).
 
 ### Examples

+ 13 - 1
scripts/tooncher

@@ -8,7 +8,8 @@ import yaml
 
 
 def run(username, config_path, engine_path=None, validate_ssl_certs=True,
-        cpu_limit_percent=None, enable_extended_keyboard_controls=False):
+        cpu_limit_percent=None, enable_extended_keyboard_controls=False,
+        extended_keyboard_control_toggle_keysym_name=None):
 
     if os.path.exists(config_path):
         with open(config_path) as f:
@@ -33,6 +34,7 @@ def run(username, config_path, engine_path=None, validate_ssl_certs=True,
                 validate_ssl_certs=validate_ssl_certs,
                 cpu_limit_percent=cpu_limit_percent,
                 enable_extended_keyboard_controls=enable_extended_keyboard_controls,
+                extended_keyboard_control_toggle_keysym_name=extended_keyboard_control_toggle_keysym_name,
             )
 
 
@@ -84,6 +86,16 @@ def _init_argparser():
             + ', e.g. walk with WASD'
             + ' (requires xlib for python, default: %(default)s)',
     )
+    argparser.add_argument(
+        '--extended-controls-toggle',
+        metavar='KEYSYM_NAME',
+        dest='extended_keyboard_control_toggle_keysym_name',
+        default='grave',
+        help='key to turn extended keyboard controls on / off.'
+            + ' any keysym name may be used'
+            + ' (see XStringToKeysym & X11/keysymdef.h, '
+            + ' default: %(default)s)',
+    )
     return argparser
 
 

+ 83 - 25
tooncher/__init__.py

@@ -1,3 +1,4 @@
+import copy
 import datetime
 import json
 import os
@@ -37,7 +38,7 @@ else:
     TOONTOWN_ENGINE_DEFAULT_PATH = None
 
 if Xlib:
-    EXTENDED_KEYBOARD_CONTROLS_MAPPING = {
+    EXTENDED_KEYBOARD_CONTROLS_DEFAULT_MAPPING = {
         XK.XK_w: XK.XK_Up,
         XK.XK_a: XK.XK_Left,
         XK.XK_s: XK.XK_Down,
@@ -169,6 +170,23 @@ def x_grab_key(grab_window, keycode, modifiers=None):
     )
 
 
+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:
@@ -180,7 +198,17 @@ def wait_for_engine_window(xdisplay, engine_process, timeout_seconds=20, search_
     return None
 
 
-def run_extended_keyboard_controls(engine_process):
+def _enable_extended_controls(xdisplay, engine_window, keyboard_mapping):
+    for keysym in keyboard_mapping.keys():
+        x_grab_keysym(xdisplay, engine_window, keysym)
+
+
+def _disable_extended_controls(xdisplay, engine_window, keyboard_mapping):
+    for keysym in keyboard_mapping.keys():
+        x_ungrab_keysym(xdisplay, engine_window, keysym)
+
+
+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.',
@@ -193,12 +221,21 @@ def run_extended_keyboard_controls(engine_process):
     engine_window = wait_for_engine_window(xdisplay, engine_process)
     if not engine_window:
         raise Exception('Could not find the game\'s window.')
-    # TODO add toggle to switch on / off
-    for keysym in EXTENDED_KEYBOARD_CONTROLS_MAPPING.keys():
-        x_grab_key(
-            engine_window,
-            xdisplay.keysym_to_keycode(keysym),
-        )
+    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))
+    print("INFO Extended Controls are currently off."
+          + " Press key '{}' to enable.".format(toggle_keysym_name))
+    controls_enabled = False
     while engine_process.poll() is None:
         # TODO don't block here, engine might have already been stopped
         xevent = xdisplay.next_event()
@@ -209,27 +246,47 @@ def run_extended_keyboard_controls(engine_process):
                 xevent.detail,
                 index=0,
             )
-            if keysym_in in EXTENDED_KEYBOARD_CONTROLS_MAPPING:
-                keysym_out = EXTENDED_KEYBOARD_CONTROLS_MAPPING[keysym_in]
+            if keysym_in == toggle_keysym:
+                if isinstance(xevent, Xlib.protocol.event.KeyPress):
+                    if controls_enabled:
+                        _disable_extended_controls(
+                            xdisplay,
+                            engine_window,
+                            keyboard_mapping,
+                        )
+                    else:
+                        _enable_extended_controls(
+                            xdisplay,
+                            engine_window,
+                            keyboard_mapping,
+                        )
+                    controls_enabled = not controls_enabled
+                    print("INFO {} Extended Controls".format(
+                        "Enabled" if controls_enabled else "Disabled",
+                    ))
             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,
-            ))
+                if controls_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 launch(engine_path, username, password, validate_ssl_certs=True,
-           cpu_limit_percent=None, enable_extended_keyboard_controls=False):
+           cpu_limit_percent=None, enable_extended_keyboard_controls=False,
+           extended_keyboard_control_toggle_keysym_name=None):
     result = login(
         username=username,
         password=password,
@@ -257,6 +314,7 @@ def launch(engine_path, username, password, validate_ssl_certs=True,
             try:
                 run_extended_keyboard_controls(
                     engine_process=p,
+                    toggle_keysym_name=extended_keyboard_control_toggle_keysym_name,
                 )
             except Exception as e:
                 if isinstance(e, KeyboardInterrupt):