Browse Source

write key bindings to stdout; fix RewriteKeyEventAction if keysym rewritting disabled

Fabian Peter Hammerle 4 years ago
parent
commit
badbcf6289

+ 21 - 12
rescriptoon/__init__.py

@@ -14,11 +14,12 @@ from rescriptoon._actions import (
     SelectGagAction,
     ToggleOverlayAction,
 )
+from rescriptoon._keys import keysym_to_label
 
 EXTENDED_CONTROLS_DEFAULT_TOGGLE_KEYSYM_NAME = "grave"
 TOONTOWN_WINDOW_NAME = "Toontown Rewritten"
 
-EXTENDED_CONTROLS_DEFAULT_KEYSYM_MAPPINGS = {
+_KEYSYM_ACTION_MAPPINGS = {
     # pylint: disable=no-member; false positive for XK.*
     XK.XK_w: RewriteKeyEventAction(keysym=XK.XK_Up, target_engine_index=0),
     XK.XK_a: RewriteKeyEventAction(keysym=XK.XK_Left, target_engine_index=0),
@@ -35,18 +36,25 @@ EXTENDED_CONTROLS_DEFAULT_KEYSYM_MAPPINGS = {
     XK.XK_slash: RewriteKeyEventAction(keysym=XK.XK_Control_L, target_engine_index=1),
     XK.XK_n: LowThrowAction(target_engine_index=1),
     XK.XK_space: RewriteKeyEventAction(keysym=XK.XK_Control_L),
+    # TODO replace gag_name with enum
     XK.XK_e: SelectGagAction(
-        target_engine_index=0, column_index=4, factor_y=-0.047
-    ),  # elephant trunk
+        gag_name="elephant trunk",
+        target_engine_index=0,
+        column_index=4,
+        factor_y=-0.047,
+    ),
     XK.XK_i: SelectGagAction(
-        target_engine_index=1, column_index=4, factor_y=-0.047
-    ),  # elephant trunk
+        gag_name="elephant trunk",
+        target_engine_index=1,
+        column_index=4,
+        factor_y=-0.047,
+    ),
     XK.XK_f: SelectGagAction(
-        target_engine_index=0, column_index=5, factor_y=-0.047
-    ),  # foghorn
+        gag_name="foghorn", target_engine_index=0, column_index=5, factor_y=-0.047
+    ),
     XK.XK_j: SelectGagAction(
-        target_engine_index=1, column_index=5, factor_y=-0.047
-    ),  # foghorn
+        gag_name="foghorn", target_engine_index=1, column_index=5, factor_y=-0.047
+    ),
 }
 
 
@@ -83,9 +91,7 @@ class Overlay:
                 "rescriptoon controls toggle:"
                 + " unknown keysym name '{}'".format(toggle_keysym_name)
             )
-        self._keysym_mappings = copy.deepcopy(
-            EXTENDED_CONTROLS_DEFAULT_KEYSYM_MAPPINGS,
-        )
+        self._keysym_mappings = copy.deepcopy(_KEYSYM_ACTION_MAPPINGS)
         if self._toggle_keysym in self._keysym_mappings:
             logging.warning("ignoring mapping for toggle key %s", toggle_keysym_name)
         self._keysym_mappings[self._toggle_keysym] = ToggleOverlayAction()
@@ -117,6 +123,9 @@ class Overlay:
         if not self._engine_windows:
             raise Exception("no toontown window found")
         self._grab_key(self.xdisplay.keysym_to_keycode(self._toggle_keysym),)
+        print("key bindings:")
+        for keysym, action in self._keysym_mappings.items():
+            print("{}: {}".format(keysym_to_label(keysym), action.description))
         self.enable()
         while self._engine_windows_open:
             while self.xdisplay.pending_events():

+ 55 - 6
rescriptoon/_actions.py

@@ -8,12 +8,24 @@ import Xlib.protocol.event
 import Xlib.X
 import Xlib.XK
 
+from rescriptoon._keys import keysym_to_label
+
 _XKEYEVENT_TYPE = typing.Union[
     Xlib.protocol.event.KeyPress, Xlib.protocol.event.KeyRelease
 ]
 
 
-class EngineAction:
+class _Action:
+    @abc.abstractmethod
+    def execute(self, overlay: "rescriptoon.Overlay", xkeyevent: _XKEYEVENT_TYPE):
+        raise NotImplementedError()
+
+    @abc.abstractproperty
+    def description(self) -> str:
+        raise NotImplementedError()
+
+
+class EngineAction(_Action):
     def __init__(self, target_engine_index: typing.Optional[int] = None):
         self._target_engine_index = target_engine_index
 
@@ -39,6 +51,12 @@ class EngineAction:
                 overlay, xkeyevent, overlay.engine_windows[self._target_engine_index]
             )
 
+    @property
+    def _engine_label(self) -> str:
+        if self._target_engine_index is None:
+            return "all engines"
+        return "engine #{}".format(self._target_engine_index)
+
 
 class CenterClickAction(EngineAction):
     def __init__(self, target_engine_index: int, factor_x: float, factor_y: float):
@@ -84,12 +102,23 @@ class SelectGagAction(CenterClickAction):
     X_OFFSET = -0.286
     X_FACTOR = 0.081
 
-    def __init__(self, target_engine_index: int, column_index: int, factor_y: float):
+    def __init__(
+        self,
+        target_engine_index: int,
+        column_index: int,
+        factor_y: float,
+        gag_name: str,
+    ):
         super().__init__(
             target_engine_index=target_engine_index,
             factor_x=self.X_OFFSET + self.X_FACTOR * column_index,
             factor_y=factor_y,
         )
+        self._gag_name = gag_name
+
+    @property
+    def description(self) -> str:
+        return "select {} in battle @ {}".format(self._gag_name, self._engine_label)
 
 
 def _send_rewritten_xkeyevent(
@@ -118,7 +147,11 @@ def _send_rewritten_xkeyevent(
 
 
 class RewriteKeyEventAction(EngineAction):
-    def __init__(self, target_engine_index: typing.Optional[int] = None, keysym=None):
+    def __init__(
+        self,
+        target_engine_index: typing.Optional[int] = None,
+        keysym: typing.Optional[int] = None,
+    ):
         super().__init__(target_engine_index=target_engine_index,)
         self._keysym = keysym
 
@@ -131,9 +164,17 @@ class RewriteKeyEventAction(EngineAction):
         _send_rewritten_xkeyevent(
             event_template=xkeyevent,
             window=engine_window,
-            keycode=overlay.xdisplay.keysym_to_keycode(self._keysym),
+            keycode=overlay.xdisplay.keysym_to_keycode(self._keysym)
+            if self._keysym is not None
+            else xkeyevent.detail,
         )
 
+    @property
+    def description(self) -> str:
+        if self._keysym is None:
+            return "forward to {}".format(self._engine_label)
+        return "send {} to {}".format(keysym_to_label(self._keysym), self._engine_label)
+
 
 class LowThrowAction(EngineAction):
 
@@ -162,8 +203,16 @@ class LowThrowAction(EngineAction):
             event_type=Xlib.protocol.event.KeyRelease,
         )
 
+    @property
+    def description(self) -> str:
+        return "low throw @ {}".format(self._engine_label)
 
-class ToggleOverlayAction:
-    def execute(self, overlay: "rescriptoon.Overlay", xkeyevent: "Xlib.display.Window"):
+
+class ToggleOverlayAction(_Action):
+    def execute(self, overlay: "rescriptoon.Overlay", xkeyevent: _XKEYEVENT_TYPE):
         if isinstance(xkeyevent, Xlib.protocol.event.KeyPress):
             overlay.toggle()
+
+    @property
+    def description(self) -> str:
+        return "enable/disable rescriptoon"

+ 1 - 0
rescriptoon/_keys.py

@@ -6,6 +6,7 @@ _KEYSYM_LABELS = {
     # pylint: disable=no-member; false positive
     XK.XK_Control_L: "left ctrl",
     XK.XK_Control_R: "right ctrl",
+    XK.XK_Delete: "delete",
     XK.XK_Down: "↓",
     XK.XK_Left: "→",
     XK.XK_Right: "←",

+ 5 - 1
tests/_keys_test.py

@@ -1,7 +1,10 @@
 import pytest
-import rescriptoon._keys
+
 from Xlib import XK
 
+# pylint: disable=protected-access
+import rescriptoon._keys
+
 
 @pytest.mark.parametrize(
     ("keysym", "label"),
@@ -9,6 +12,7 @@ from Xlib import XK
         # pylint: disable=no-member; false positive
         (XK.XK_Control_L, "left ctrl"),
         (XK.XK_Control_R, "right ctrl"),
+        (XK.XK_Delete, "delete"),
         (XK.XK_Down, "↓"),
         (XK.XK_Left, "→"),
         (XK.XK_Right, "←"),

+ 14 - 0
tests/actions/engine_test.py

@@ -0,0 +1,14 @@
+import pytest
+
+from rescriptoon._actions import EngineAction
+
+# pylint: disable=protected-access
+
+
+@pytest.mark.parametrize(
+    ("target_engine_index", "engine_label"),
+    [(None, "all engines"), (0, "engine #0"), (1, "engine #1"),],
+)
+def test__engine_label(target_engine_index, engine_label):
+    action = EngineAction(target_engine_index=target_engine_index)
+    assert action._engine_label == engine_label

+ 16 - 0
tests/actions/low_throw_test.py

@@ -0,0 +1,16 @@
+import pytest
+
+from rescriptoon._actions import LowThrowAction
+
+
+@pytest.mark.parametrize(
+    ("target_engine_index", "description"),
+    [
+        (None, "low throw @ all engines"),
+        (0, "low throw @ engine #0"),
+        (1, "low throw @ engine #1"),
+    ],
+)
+def test_description(target_engine_index, description):
+    action = LowThrowAction(target_engine_index=target_engine_index)
+    assert action.description == description

+ 23 - 0
tests/actions/rewrite_key_event_test.py

@@ -0,0 +1,23 @@
+import pytest
+from Xlib import XK
+
+from rescriptoon._actions import RewriteKeyEventAction
+
+
+@pytest.mark.parametrize(
+    ("keysym", "target_engine_index", "description"),
+    [
+        (None, None, "forward to all engines"),
+        (None, 0, "forward to engine #0"),
+        (None, 1, "forward to engine #1"),
+        # pylint: disable=no-member; false positive
+        (XK.XK_Up, None, "send ↑ to all engines"),
+        (XK.XK_Delete, 0, "send delete to engine #0"),
+        (XK.XK_Control_L, 1, "send left ctrl to engine #1"),
+    ],
+)
+def test_description(keysym, target_engine_index, description):
+    action = RewriteKeyEventAction(
+        keysym=keysym, target_engine_index=target_engine_index
+    )
+    assert action.description == description

+ 30 - 0
tests/actions/select_gag_test.py

@@ -0,0 +1,30 @@
+import pytest
+
+from rescriptoon._actions import SelectGagAction
+
+
+@pytest.mark.parametrize(
+    ("column_index", "factor_y", "gag_name", "target_engine_index", "description"),
+    [
+        (
+            4,
+            -0.047,
+            "elephant trunk",
+            None,
+            "select elephant trunk in battle @ all engines",
+        ),
+        (4, -0.047, "elephant trunk", 0, "select elephant trunk in battle @ engine #0"),
+        (4, -0.047, "elephant trunk", 1, "select elephant trunk in battle @ engine #1"),
+        (5, -0.047, "foghorn", 0, "select foghorn in battle @ engine #0"),
+    ],
+)
+def test_description(
+    column_index, factor_y, gag_name, target_engine_index, description
+):
+    action = SelectGagAction(
+        column_index=column_index,
+        factor_y=factor_y,
+        gag_name=gag_name,
+        target_engine_index=target_engine_index,
+    )
+    assert action.description == description

+ 5 - 0
tests/actions/toggle_overlay_test.py

@@ -0,0 +1,5 @@
+from rescriptoon._actions import ToggleOverlayAction
+
+
+def test_description():
+    assert ToggleOverlayAction().description == "enable/disable rescriptoon"