Преглед на файлове

Action is being defined, added Xlib for mouse

It appears the mouse module will work for platforms such as Windows, but
not Linux... and pyautogui was simple enough, I just copied it without
their extra mess of moving the mouse (which I never wanted it to do)

> python3-xlib is added for X11 mouse support, now done by us
apollo преди 5 дни
родител
ревизия
d5c18a66ba
променени са 2 файла, в които са добавени 114 реда и са изтрити 22 реда
  1. 3 2
      requirements.txt
  2. 111 20
      tyrell.py

+ 3 - 2
requirements.txt

@@ -1,7 +1,6 @@
 click==8.1.8
 colorama==0.4.6
 keyboard==0.13.5
-mouse==0.7.1
 MouseInfo==0.1.3
 pillow==10.4.0
 PyAutoGUI==0.9.54
@@ -9,7 +8,9 @@ PyGetWindow==0.0.9
 PyMsgBox==1.0.9
 pyperclip==1.9.0
 PyRect==0.2.0
-pyscreeze==1.0.1
+PyScreeze==1.0.1
+python-xlib==0.33
 python3-xlib==0.15
 pytweening==1.2.0
+six==1.17.0
 toml==0.10.2

+ 111 - 20
tyrell.py

@@ -7,7 +7,7 @@ from os.path import exists as _exists, join as _join, isdir as _isdir
 
 from time import sleep as _sleep
 
-from typing import Dict as _Dict, Tuple as _Tuple, List as _List
+from typing import Dict as _Dict, Tuple as _Tuple, List as _List, Optional as _Option, Any as _Any
 
 try:
     from click import echo as _echo, style as _style
@@ -15,16 +15,6 @@ except ImportError:
     print("ERROR Please activate a virtual environment and install requirements.txt")
     exit()
 
-from pyautogui import mouseDown as _mouse_down, mouseUp as _mouse_up
-from keyboard import press as _key_down, release as _key_up, KeyboardEvent as _KeyboardEvent, add_hotkey as _add_hotkey, hook as _key_hook, write as _key_write
-# Not entirely sure why this doesn't work
-#from mouse import press as mouse_down, release as mouse_up, click as mouse_press, move as mouse_move
-
-from toml import load as _toml_load
-
-from asyncio import run as _run, sleep as _asleep
-from asyncio.tasks import gather as _task_group
-
 def _print_err(msg: str):
     _echo(_style("ERROR", fg="bright_red") + " " + msg)
 
@@ -37,16 +27,10 @@ def _print_ok(msg: str):
 def _print_info(msg: str):
     print("      " + msg)
 
-def _keyboard_output(msg: str, delay: int=50, hold: int=20):
-    _key_write(msg, delay=hold)
-    _sleep(delay/1000)
+from pyautogui import LEFT as _LEFT, RIGHT as _RIGHT, MIDDLE as _MIDDLE
+from keyboard import press as _key_down, release as _key_up, KeyboardEvent as _KeyboardEvent, add_hotkey as _add_hotkey, hook as _key_hook, write as _key_write
 
-def _mouse_output(button: str, delay: int=50, hold: int=20):
-    _mouse_down(button=button)
-    _sleep(hold / 1000)
-    _mouse_up(button=button)
-    _sleep(hold / 1000)
-    _sleep(delay / 1000)
+_BUTTON_MAP: _Dict[str | int, int] = {_LEFT: 1, _MIDDLE:3, _RIGHT: 3, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}
 
 platform: str = ""
 os_name = _OS_NAME.lower()
@@ -61,6 +45,46 @@ else:
     _print_info(f"Platform: {_OS_NAME}")
     exit()
 
+if platform == 'LINUX':
+    # It appears those under LINUX, either don't work or work but play with mouse position
+    from Xlib.display import Display as _Display
+    _display = _Display(_env['DISPLAY'])
+    from Xlib.X import ButtonPress as _ButtonPress, ButtonRelease as _ButtonRelease
+    from Xlib.ext.xtest import fake_input as _fake_input
+    def _mouse_down(button: str | int=_LEFT):
+        assert button in _BUTTON_MAP.keys(), "button argument not in ('left', 'middle', 'right', 1, 2, 3, 4, 5, 6, 7)"
+        btn = _BUTTON_MAP[button]
+        _fake_input(_display, _ButtonPress, btn)
+        _display.sync()
+    def _mouse_up(button: str | int=_LEFT):
+        assert button in _BUTTON_MAP.keys(), "button argument not in ('left', 'middle', 'right', 1, 2, 3, 4, 5, 6, 7)"
+        btn = _BUTTON_MAP[button]
+        _fake_input(_display, _ButtonRelease, btn)
+        _display.sync()
+    def _click(button: str|int=_LEFT):
+        assert button in _BUTTON_MAP.keys(), "button argument not in ('left', 'middle', 'right', 1, 2, 3, 4, 5, 6, 7)"
+        _mouse_down(button)
+        _mouse_up(button)
+else:
+    # We can simply use the mouse module under WINDOWS and MACOS
+    from mouse import press as _mouse_down, release as _mouse_up, click as _click
+
+from toml import load as _toml_load
+
+from asyncio import run as _run, sleep as _asleep
+from asyncio.tasks import gather as _task_group
+
+def _keyboard_output(msg: str, delay: int=50, hold: int=20):
+    _key_write(msg, delay=hold)
+    _sleep(delay/1000)
+
+def _mouse_output(button: str, delay: int=50, hold: int=20):
+    _mouse_down(button=button)
+    _sleep(hold / 1000)
+    _mouse_up(button=button)
+    _sleep(hold / 1000)
+    _sleep(delay / 1000)
+
 def _profile_pre_check(profile_name: str):
     if profile_name.endswith(".toml"):
         profile_name = profile_name.removesuffix(".toml")
@@ -133,6 +157,10 @@ def _write_example(profile_name: str):
             '',
             "# When 'h' is pressed, write 'Hello World!'",
             '',
+            '# Do we want this key-bind active on start-up',
+            "# This is a simple example, it won't use this",
+            '#startup = true',
+            '',
             '# Key to trigger on',
             "# This hotkey must be in the format, 'ctrl+shift+a' (user holds ctrl, shift and 'a' at once)",
             "key = 'h'",
@@ -203,6 +231,10 @@ def _write_example_mirror(profile_name: str):
             "# Press alt when 'w' is down, and release when 'w' is up",
             "# Use 'z' to toggle on/off",
             '',
+            '# Do we want this key-bind active on start-up',
+            "# This is a simple example, it won't use this",
+            '#startup = true',
+            '',
             '# Key to toggle',
             "key = 'z'",
             '',
@@ -226,6 +258,63 @@ def _write_example_mirror(profile_name: str):
     if "SUDO_USER" in _env:
         _chown(_join(profile_name, "keys", "example_mirror.toml"), int(_env['SUDO_UID']), int(_env['SUDO_GID']))
 
+class Action:
+    # Toggle to determine if this action is 'on' or 'off'
+    state: bool
+    # Key to trigger on (toggle on mirror)
+    key: str
+    # Kind of action to perform ('click', 'click down', 'key', 'key down', 'mirror')
+    kind: str
+    # Optional, keys to send
+    write: _Option[str]
+    # Optional, Repeat X number of ticks
+    duration: _Option[int]
+    # Optional, mouse button to send
+    button: _Option[str]
+    # Optional, Key to mirror
+    mirror: _Option[str]
+    # Optional, Override, Delay between each key (in milliseconds)
+    delay: _Option[int]
+    # Optional, Override, Hold delay for each key (in milliseconds)
+    hold: _Option[int]
+    def __init__(self, data: _Dict[str, _Option[_Any]]):
+        # Validate data!
+        assert 'key' in data, "Action missing 'key'"
+        assert 'kind' in data, "Action missing 'kind'"
+        assert data['kind'] in ('click', 'click down', 'key', 'key down', 'mirror'), f"Action 'kind' is unsupported, got '{data['kind']}'"
+        assert 'write' in data or 'button' in data, "Action missing 'write' or 'button' (one needed)"
+        # Assign class data
+        if 'startup' in data:
+            self.state = True
+        else:
+            self.state = False
+        self.key = str(data['key'])
+        self.kind = str(data['kind'])
+        if 'write' in data:
+            self.write = str(data['write'])
+        else:
+            self.write = None
+        if 'button' in data:
+            self.button = str(data['button'])
+        else:
+            self.button = None
+        if 'mirror' in data:
+            self.mirror = str(data['mirror'])
+        else:
+            self.mirror = None
+        if 'duration' in data:
+            self.duration = int(data['duration'])
+        else:
+            self.duration = None
+        if 'delay' in data:
+            self.delay = int(data['delay'])
+        else:
+            self.delay = None
+        if 'hold' in data:
+            self.hold = int(data['hold'])
+        else:
+            self.hold = None
+
 class Profile:
     name: str
     placeholders: _Dict[str, bool]
@@ -289,6 +378,8 @@ class Profile:
 
 if __name__ == "__main__":
     if not _exists("_example"):
+        _print_warn("Missing '_example' profile, creating...")
         p = Profile("_example")
+        _print_ok("")
         exit()
     _print_ok("All okay")