rules: use relative scroll events for scrolling in uinput

This commit is contained in:
Peter F. Patel-Schneider 2022-03-04 13:43:39 -05:00
parent b5c6cf8d63
commit 3d82075773
1 changed files with 57 additions and 25 deletions

View File

@ -644,12 +644,17 @@ buttons = {
'button8': (8, evdev.ecodes.ecodes['BTN_8']), 'button8': (8, evdev.ecodes.ecodes['BTN_8']),
'button9': (9, evdev.ecodes.ecodes['BTN_9']), 'button9': (9, evdev.ecodes.ecodes['BTN_9']),
} }
mousecap = {evdev.ecodes.EV_KEY: [evcode for (_, evcode) in buttons.values() if evcode]} mousecap = {
evdev.ecodes.EV_KEY: [evcode for (_, evcode) in buttons.values() if evcode],
evdev.ecodes.EV_REL:
[evdev.ecodes.REL_WHEEL, evdev.ecodes.REL_HWHEEL, evdev.ecodes.REL_WHEEL_HI_RES, evdev.ecodes.REL_HWHEEL_HI_RES]
}
if x11: if x11:
displayt = Display() displayt = Display()
else: else:
displayt = None displayt = None
displayt = None
try: try:
ukeyboard = evdev.uinput.UInput() ukeyboard = evdev.uinput.UInput()
@ -663,33 +668,69 @@ except Exception as e:
umouse = None umouse = None
def simulate_input(code, event): # X11 keycode/buttoncode and event def simulate_xtest(code, event):
global displayt, ukeyboard, umouse global displayt
if isinstance(code, int): # evdev keycode is 8 less than X11 keycodes
code = (code, code - 8)
if displayt: if displayt:
try: try:
if code[0]: Xlib.ext.xtest.fake_input(displayt, event, code)
Xlib.ext.xtest.fake_input(displayt, event, code[0])
displayt.sync() displayt.sync()
return True return True
except Exception as e: except Exception as e:
displayt = None displayt = None
_log.warn('xtest fake input failed: %s', e) _log.warn('xtest fake input failed: %s', e)
if ukeyboard:
direction = 1 if event == Xlib.X.KeyPress or event == Xlib.X.ButtonPress else 0
device = ukeyboard if event == Xlib.X.KeyPress or event == Xlib.X.KeyRelease else umouse def simulate_uinput(device, what, code, arg):
global ukeyboard, umouse
if device:
try: try:
if code[1]: device.write(what, code, arg)
device.write(evdev.ecodes.EV_KEY, code[1], direction)
device.syn() device.syn()
return True return True
except Exception as e: except Exception as e:
ukeyboard = umouse = None ukeyboard = umouse = None
_log.warn('uinput write failed: %s', e) _log.warn('uinput write key failed: %s', e)
def simulate_key(code, event): # X11 keycode and event
if simulate_xtest(code, event):
return True
direction = 1 if event == Xlib.X.KeyPress or event == Xlib.X.ButtonPress else 0
device = ukeyboard if event == Xlib.X.KeyPress or event == Xlib.X.KeyRelease else umouse
if simulate_uinput(device, evdev.ecodes.EV_KEY, code - 8, direction):
return True
_log.warn('no way to simulate input') _log.warn('no way to simulate input')
def click(button, count):
for _ in range(count):
if not simulate_xtest(button, Xlib.X.ButtonPress):
return False
if not simulate_xtest(button, Xlib.X.ButtonRelease):
return False
return True
def simulate_scroll(dx, dy):
if displayt:
success = True
if dx:
success = click(7 if dx > 0 else 6, count=abs(dx))
if dy and success:
success = click(4 if dy > 0 else 5, count=abs(dy))
if success:
return True
if umouse:
success = True
if dx:
success = simulate_uinput(umouse, evdev.ecodes.EV_REL, evdev.ecodes.REL_HWHEEL, dx)
if dy and success:
success = simulate_uinput(umouse, evdev.ecodes.EV_REL, evdev.ecodes.REL_WHEEL, dy)
if success:
return True
_log.warn('no way to simulate scrolling')
class Action(RuleComponent): class Action(RuleComponent):
def __init__(self, *args): def __init__(self, *args):
pass pass
@ -731,13 +772,13 @@ class KeyPress(Action):
for k in keysyms: for k in keysyms:
keycode = self.keysym_to_keycode(k, modifiers) keycode = self.keysym_to_keycode(k, modifiers)
if keycode and self.needed(keycode, modifiers): if keycode and self.needed(keycode, modifiers):
simulate_input(keycode, Xlib.X.KeyPress) simulate_key(keycode, Xlib.X.KeyPress)
def keyUp(self, keysyms, modifiers): def keyUp(self, keysyms, modifiers):
for k in keysyms: for k in keysyms:
keycode = self.keysym_to_keycode(k, modifiers) keycode = self.keysym_to_keycode(k, modifiers)
if keycode and self.needed(keycode, modifiers): if keycode and self.needed(keycode, modifiers):
simulate_input(keycode, Xlib.X.KeyRelease) simulate_key(keycode, Xlib.X.KeyRelease)
def evaluate(self, feature, notification, device, status, last_result): def evaluate(self, feature, notification, device, status, last_result):
current = gkeymap.get_modifier_state() current = gkeymap.get_modifier_state()
@ -760,12 +801,6 @@ class KeyPress(Action):
# super().keyUp(self.keys, current_key_modifiers) # super().keyUp(self.keys, current_key_modifiers)
def click(button, count):
for _ in range(count):
simulate_input(button, Xlib.X.ButtonPress)
simulate_input(button, Xlib.X.ButtonRelease)
class MouseScroll(Action): class MouseScroll(Action):
def __init__(self, amounts): def __init__(self, amounts):
import numbers import numbers
@ -788,10 +823,7 @@ class MouseScroll(Action):
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info('MouseScroll action: %s %s %s', self.amounts, last_result, amounts) _log.info('MouseScroll action: %s %s %s', self.amounts, last_result, amounts)
dx, dy = amounts dx, dy = amounts
if dx: simulate_scroll(dx, dy)
click(button=buttons['scroll_right'] if dx > 0 else buttons['scroll_left'], count=abs(dx))
if dy:
click(button=buttons['scroll_up'] if dy > 0 else buttons['scroll_down'], count=abs(dy))
return None return None
def data(self): def data(self):