|
@@ -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):
|