diversion: Simplify and type hint

- Make static method an easy testable function.
- Fix variable name clashes
This commit is contained in:
MattHag 2024-04-21 14:31:45 +02:00 committed by Peter F. Patel-Schneider
parent 3ffa4e30f1
commit 1d5b61fbf2
1 changed files with 58 additions and 49 deletions

View File

@ -26,11 +26,15 @@ import subprocess
import sys as _sys import sys as _sys
import time as _time import time as _time
from typing import Dict
from typing import Tuple
import dbus import dbus
import gi import gi
import keysyms.keysymdef as _keysymdef
import psutil import psutil
from keysyms import keysymdef
# There is no evdev on macOS or Windows. Diversion will not work without # There is no evdev on macOS or Windows. Diversion will not work without
# it but other Solaar functionality is available. # it but other Solaar functionality is available.
if _platform.system() in ("Darwin", "Windows"): if _platform.system() in ("Darwin", "Windows"):
@ -84,7 +88,7 @@ logger = logging.getLogger(__name__)
# Xtest extension to X11 - provides input simulation, partly works under Wayland # Xtest extension to X11 - provides input simulation, partly works under Wayland
# Wayland - provides input simulation # Wayland - provides input simulation
XK_KEYS = _keysymdef.keysymdef XK_KEYS: Dict[str, int] = keysymdef.keysymdef
# Event codes - can't use Xlib.X codes because Xlib might not be available # Event codes - can't use Xlib.X codes because Xlib might not be available
_KEY_RELEASE = 0 _KEY_RELEASE = 0
@ -113,12 +117,24 @@ try:
except Exception: except Exception:
_x11 = False # X11 is not available _x11 = False # X11 is not available
# Globals
xtest_available = True # Xtest might be available xtest_available = True # Xtest might be available
xdisplay = None xdisplay = None
Xkbdisplay = None # xkb might be available Xkbdisplay = None # xkb might be available
modifier_keycodes = [] modifier_keycodes = []
XkbUseCoreKbd = 0x100 XkbUseCoreKbd = 0x100
udevice = None
key_down = None
key_up = None
keys_down = []
g_keys_down = 0
m_keys_down = 0
mr_key_down = False
thumb_wheel_displacement = 0
_dbus_interface = None _dbus_interface = None
@ -224,8 +240,6 @@ else:
key_events = [] key_events = []
devicecap = {} devicecap = {}
udevice = None
def setup_uinput(): def setup_uinput():
global udevice global udevice
@ -261,12 +275,8 @@ def modifier_code(keycode):
return m return m
key_down = None def signed(bytes_: bytes) -> int:
key_up = None return int.from_bytes(bytes_, "big", signed=True)
def signed(bytes):
return int.from_bytes(bytes, "big", signed=True)
def xy_direction(_x, _y): def xy_direction(_x, _y):
@ -432,7 +442,7 @@ def thumb_wheel_down(f, r, d, a):
return False return False
def charging(f, r, d, a): def charging(f, r, d, _a):
if ( if (
(f == _F.BATTERY_STATUS and r == 0 and 1 <= d[2] <= 4) (f == _F.BATTERY_STATUS and r == 0 and 1 <= d[2] <= 4)
or (f == _F.BATTERY_VOLTAGE and r == 0 and d[2] & (1 << 7)) or (f == _F.BATTERY_VOLTAGE and r == 0 and d[2] & (1 << 7))
@ -909,7 +919,7 @@ def bit_test(start, end, bits):
def range_test(start, end, min, max): def range_test(start, end, min, max):
def range_test_helper(f, r, d): def range_test_helper(_f, _r, d):
value = int.from_bytes(d[start:end], byteorder="big", signed=True) value = int.from_bytes(d[start:end], byteorder="big", signed=True)
return min <= value <= max and (value if value else True) return min <= value <= max and (value if value else True)
@ -965,10 +975,8 @@ class TestBytes(Condition):
isinstance(test, list) isinstance(test, list)
and 2 < len(test) <= 4 and 2 < len(test) <= 4
and all(isinstance(t, int) for t in test) and all(isinstance(t, int) for t in test)
and test[0] >= 0 and 0 <= test[0] <= 16
and test[0] <= 16 and 0 <= test[1] <= 16
and test[1] >= 0
and test[1] <= 16
and test[0] < test[1] and test[0] < test[1]
): ):
self.function = bit_test(*test) if len(test) == 3 else range_test(*test) self.function = bit_test(*test) if len(test) == 3 else range_test(*test)
@ -1113,6 +1121,32 @@ class Action(RuleComponent):
return None return None
def keysym_to_keycode(keysym, _modifiers) -> Tuple[int, int]: # maybe should take shift into account
"""Reverse the keycode to keysym mapping.
Warning:
This is an attempt to reverse the keycode to keysym mappping in XKB.
It may not be completely general.
"""
group = kbdgroup() or 0
keycodes = gkeymap.get_entries_for_keyval(keysym)
(keycode, level) = (None, None)
for k in keycodes.keys: # mappings that have the correct group
if group == k.group and k.keycode < 256 and (level is None or k.level < level):
keycode = k.keycode
level = k.level
if keycode or group == 0:
return keycode, level
for k in keycodes.keys: # mappings for group 0 where keycode only has group 0 mappings
if 0 == k.group and k.keycode < 256 and (level is None or k.level < level):
(a, m, vs) = gkeymap.get_entries_for_keycode(k.keycode)
if a and all(mk.group == 0 for mk in m):
keycode = k.keycode
level = k.level
return keycode, level
class KeyPress(Action): class KeyPress(Action):
def __init__(self, args, warn=True): def __init__(self, args, warn=True):
self.key_names, self.action = self.regularize_args(args) self.key_names, self.action = self.regularize_args(args)
@ -1137,25 +1171,6 @@ class KeyPress(Action):
action = args[1] action = args[1]
return keys, action return keys, action
# WARNING: This is an attempt to reverse the keycode to keysym mappping in XKB. It may not be completely general.
def keysym_to_keycode(self, keysym, modifiers): # maybe should take shift into account
group = kbdgroup() or 0
keycodes = gkeymap.get_entries_for_keyval(keysym)
(keycode, level) = (None, None)
for k in keycodes.keys: # mappings that have the correct group
if group == k.group and k.keycode < 256 and (level is None or k.level < level):
keycode = k.keycode
level = k.level
if keycode or group == 0:
return (keycode, level)
for k in keycodes.keys: # mappings for group 0 where keycode only has group 0 mappings
if 0 == k.group and k.keycode < 256 and (level is None or k.level < level):
(a, m, vs) = gkeymap.get_entries_for_keycode(k.keycode)
if a and all(mk.group == 0 for mk in m):
keycode = k.keycode
level = k.level
return (keycode, level)
def __str__(self): def __str__(self):
return "KeyPress: " + " ".join(self.key_names) + " " + self.action return "KeyPress: " + " ".join(self.key_names) + " " + self.action
@ -1165,26 +1180,26 @@ class KeyPress(Action):
def mods(self, level, modifiers, direction): def mods(self, level, modifiers, direction):
if level == 2 or level == 3: if level == 2 or level == 3:
(sk, _) = self.keysym_to_keycode(XK_KEYS.get("ISO_Level3_Shift", None), modifiers) (sk, _) = keysym_to_keycode(XK_KEYS.get("ISO_Level3_Shift", None), modifiers)
if sk and self.needed(sk, modifiers): if sk and self.needed(sk, modifiers):
simulate_key(sk, direction) simulate_key(sk, direction)
if level == 1 or level == 3: if level == 1 or level == 3:
(sk, _) = self.keysym_to_keycode(XK_KEYS.get("Shift_L", None), modifiers) (sk, _) = keysym_to_keycode(XK_KEYS.get("Shift_L", None), modifiers)
if sk and self.needed(sk, modifiers): if sk and self.needed(sk, modifiers):
simulate_key(sk, direction) simulate_key(sk, direction)
def keyDown(self, keysyms, modifiers): def keyDown(self, keysyms_, modifiers):
for k in keysyms: for k in keysyms_:
(keycode, level) = self.keysym_to_keycode(k, modifiers) (keycode, level) = keysym_to_keycode(k, modifiers)
if keycode is None: if keycode is None:
logger.warning("rule KeyPress key symbol not currently available %s", self) logger.warning("rule KeyPress key symbol not currently available %s", self)
elif self.action != CLICK or self.needed(keycode, modifiers): # only check needed when clicking elif self.action != CLICK or self.needed(keycode, modifiers): # only check needed when clicking
self.mods(level, modifiers, _KEY_PRESS) self.mods(level, modifiers, _KEY_PRESS)
simulate_key(keycode, _KEY_PRESS) simulate_key(keycode, _KEY_PRESS)
def keyUp(self, keysyms, modifiers): def keyUp(self, keysyms_, modifiers):
for k in keysyms: for k in keysyms_:
(keycode, level) = self.keysym_to_keycode(k, modifiers) (keycode, level) = keysym_to_keycode(k, modifiers)
if keycode and (self.action != CLICK or self.needed(keycode, modifiers)): # only check needed when clicking if keycode and (self.action != CLICK or self.needed(keycode, modifiers)): # only check needed when clicking
simulate_key(keycode, _KEY_RELEASE) simulate_key(keycode, _KEY_RELEASE)
self.mods(level, modifiers, _KEY_RELEASE) self.mods(level, modifiers, _KEY_RELEASE)
@ -1420,12 +1435,6 @@ if True:
] ]
) )
keys_down = []
g_keys_down = 0
m_keys_down = 0
mr_key_down = False
thumb_wheel_displacement = 0
def key_is_down(key): def key_is_down(key):
if key == _CONTROL.MR: if key == _CONTROL.MR: