|
@@ -1,19 +1,13 @@
|
|
|
-import copy
|
|
|
import datetime
|
|
|
import json
|
|
|
import os
|
|
|
import ssl
|
|
|
import subprocess
|
|
|
import sys
|
|
|
-import time
|
|
|
import traceback
|
|
|
import urllib.parse
|
|
|
import urllib.request
|
|
|
-try:
|
|
|
- import Xlib.display
|
|
|
- from Xlib import X, XK
|
|
|
-except ImportError:
|
|
|
- Xlib = False
|
|
|
+import tooncher.controls
|
|
|
|
|
|
"""
|
|
|
official api documentation:
|
|
@@ -37,14 +31,6 @@ else:
|
|
|
TOONTOWN_LIBRARY_PATH = None
|
|
|
TOONTOWN_ENGINE_DEFAULT_PATH = None
|
|
|
|
|
|
-if Xlib:
|
|
|
- EXTENDED_KEYBOARD_CONTROLS_DEFAULT_MAPPING = {
|
|
|
- XK.XK_w: XK.XK_Up,
|
|
|
- XK.XK_a: XK.XK_Left,
|
|
|
- XK.XK_s: XK.XK_Down,
|
|
|
- XK.XK_d: XK.XK_Right,
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
def start_engine(engine_path, gameserver, playcookie, **kwargs):
|
|
|
env = {
|
|
@@ -137,156 +123,6 @@ def login(username=None, password=None,
|
|
|
raise Exception(repr(resp_data))
|
|
|
|
|
|
|
|
|
-def x_find_window(parent_window, filter_callback):
|
|
|
- matching = []
|
|
|
- for child_window in parent_window.query_tree().children:
|
|
|
- if filter_callback(child_window):
|
|
|
- matching.append(child_window)
|
|
|
- matching += x_find_window(child_window, filter_callback)
|
|
|
- return matching
|
|
|
-
|
|
|
-
|
|
|
-def x_find_window_by_pid(display, 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)
|
|
|
-
|
|
|
-
|
|
|
-class ExtendedControls:
|
|
|
-
|
|
|
- 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
|
|
|
-
|
|
|
- def _wait_for_engine_window(self, timeout_seconds=20, search_interval_seconds=2):
|
|
|
- start_epoch = time.time()
|
|
|
- while self._engine_process.poll() is None and (time.time() - start_epoch) <= timeout_seconds:
|
|
|
- windows = x_find_window_by_pid(
|
|
|
- self._xdisplay,
|
|
|
- self._engine_process.pid,
|
|
|
- )
|
|
|
- assert len(windows) <= 1
|
|
|
- if len(windows) == 1:
|
|
|
- return windows[0]
|
|
|
- time.sleep(search_interval_seconds)
|
|
|
- return None
|
|
|
-
|
|
|
- def run(self):
|
|
|
- self._engine_window = self._wait_for_engine_window()
|
|
|
- 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:
|
|
|
-
|
|
|
- self._handle_xevent(self._xdisplay.next_event())
|
|
|
-
|
|
|
- def _handle_xevent(self, xevent):
|
|
|
-
|
|
|
- if isinstance(xevent, Xlib.protocol.event.KeyPress) \
|
|
|
- or isinstance(xevent, Xlib.protocol.event.KeyRelease):
|
|
|
- self._handle_xkeyevent(xevent)
|
|
|
-
|
|
|
- def _handle_xkeyevent(self, xkeyevent):
|
|
|
-
|
|
|
- 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,
|
|
|
- same_screen=xkeyevent.same_screen,
|
|
|
- ))
|
|
|
-
|
|
|
- def enable(self):
|
|
|
- for keysym in self._keyboard_mapping.keys():
|
|
|
- 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():
|
|
|
- self._ungrab_key(
|
|
|
- self._xdisplay.keysym_to_keycode(keysym),
|
|
|
- )
|
|
|
- self._enabled = False
|
|
|
- print("INFO Disabled Extended Controls")
|
|
|
-
|
|
|
- @property
|
|
|
- def enabled(self):
|
|
|
- return self._enabled
|
|
|
-
|
|
|
- def toggle(self):
|
|
|
- if self.enabled:
|
|
|
- self.disable()
|
|
|
- else:
|
|
|
- self.enable()
|
|
|
-
|
|
|
- def _grab_key(self, keycode):
|
|
|
- self._engine_window.grab_key(
|
|
|
- keycode,
|
|
|
- X.AnyModifier,
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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,
|
|
|
cpu_limit_percent=None, enable_extended_keyboard_controls=False,
|
|
|
extended_keyboard_control_toggle_keysym_name=None):
|
|
@@ -315,7 +151,7 @@ def launch(engine_path, username, password, validate_ssl_certs=True,
|
|
|
])
|
|
|
if enable_extended_keyboard_controls:
|
|
|
try:
|
|
|
- ExtendedControls(
|
|
|
+ tooncher.controls.ExtendedControls(
|
|
|
engine_process=p,
|
|
|
toggle_keysym_name=extended_keyboard_control_toggle_keysym_name,
|
|
|
).run()
|