rules: use uinput for simulating keyboard input

This commit is contained in:
Peter F. Patel-Schneider 2022-02-22 16:06:00 -05:00
parent a4afffe6c3
commit b9c17474a4
1 changed files with 21 additions and 13 deletions

View File

@ -26,6 +26,7 @@ from logging import getLogger
from math import sqrt as _sqrt from math import sqrt as _sqrt
import keysyms.keysymdef as _keysymdef import keysyms.keysymdef as _keysymdef
import evdev
import psutil import psutil
from gi.repository import Gdk, GLib from gi.repository import Gdk, GLib
@ -51,7 +52,7 @@ del getLogger
# Process condition depends on X11 from python-xlib, and is probably not possible at all in Wayland # Process condition depends on X11 from python-xlib, and is probably not possible at all in Wayland
# MouseProcess condition depends on X11 from python-xlib, and is probably not possible at all in Wayland # MouseProcess condition depends on X11 from python-xlib, and is probably not possible at all in Wayland
# Modifiers condition depends only on GDK # Modifiers condition depends only on GDK
# KeyPress action currently only works in X11, and is not currently available under Wayland # KeyPress action currently only works in X11, and is not currently available under Wayland FIXME
# KeyPress action determines whether a keysym is a currently-down modifier using get_modifier_mapping from python-xlib; # KeyPress action determines whether a keysym is a currently-down modifier using get_modifier_mapping from python-xlib;
# under Wayland no modifier keys are considered down so all modifier keys are pressed, potentially leading to problems # under Wayland no modifier keys are considered down so all modifier keys are pressed, potentially leading to problems
# KeyPress action translates key names to keysysms using the local file described for GUI keyname determination # KeyPress action translates key names to keysysms using the local file described for GUI keyname determination
@ -71,7 +72,6 @@ XK_KEYS = _keysymdef.keysymdef
try: try:
import Xlib import Xlib
from Xlib import X
from Xlib.display import Display from Xlib.display import Display
xdisplay = Display() xdisplay = Display()
modifier_keycodes = xdisplay.get_modifier_mapping() # there should be a way to do this in Gdk modifier_keycodes = xdisplay.get_modifier_mapping() # there should be a way to do this in Gdk
@ -84,10 +84,10 @@ try:
# set up to get keyboard state using ctypes interface to libx11 # set up to get keyboard state using ctypes interface to libx11
import ctypes import ctypes
class X11Display(ctypes.Structure): class XkbDisplay(ctypes.Structure):
""" opaque struct """ """ opaque struct """
class X11XkbStateRec(ctypes.Structure): class XkbStateRec(ctypes.Structure):
_fields_ = [('group', ctypes.c_ubyte), ('locked_group', ctypes.c_ubyte), ('base_group', ctypes.c_ushort), _fields_ = [('group', ctypes.c_ubyte), ('locked_group', ctypes.c_ubyte), ('base_group', ctypes.c_ushort),
('latched_group', ctypes.c_ushort), ('mods', ctypes.c_ubyte), ('base_mods', ctypes.c_ubyte), ('latched_group', ctypes.c_ushort), ('mods', ctypes.c_ubyte), ('base_mods', ctypes.c_ubyte),
('latched_mods', ctypes.c_ubyte), ('locked_mods', ctypes.c_ubyte), ('compat_state', ctypes.c_ubyte), ('latched_mods', ctypes.c_ubyte), ('locked_mods', ctypes.c_ubyte), ('compat_state', ctypes.c_ubyte),
@ -95,10 +95,11 @@ try:
('compat_lookup_mods', ctypes.c_ubyte), ('compat_lookup_mods', ctypes.c_ubyte),
('ptr_buttons', ctypes.c_ushort)] # something strange is happening here but it is not being used ('ptr_buttons', ctypes.c_ushort)] # something strange is happening here but it is not being used
XkbUseCoreKbd = 0x100
X11Lib = ctypes.cdll.LoadLibrary('libX11.so') X11Lib = ctypes.cdll.LoadLibrary('libX11.so')
X11Lib.XOpenDisplay.restype = ctypes.POINTER(X11Display) X11Lib.XOpenDisplay.restype = ctypes.POINTER(XkbDisplay)
X11Lib.XkbGetState.argtypes = [ctypes.POINTER(X11Display), ctypes.c_uint, ctypes.POINTER(X11XkbStateRec)] X11Lib.XkbGetState.argtypes = [ctypes.POINTER(XkbDisplay), ctypes.c_uint, ctypes.POINTER(XkbStateRec)]
display = X11Lib.XOpenDisplay(None) Xkbdisplay = X11Lib.XOpenDisplay(None)
except Exception: except Exception:
_log.warn( _log.warn(
'X11 not available - rules cannot access current process or keyboard group and cannot simulate input. %s', 'X11 not available - rules cannot access current process or keyboard group and cannot simulate input. %s',
@ -110,8 +111,8 @@ except Exception:
def kbdgroup(): def kbdgroup():
if x11: if x11:
state = X11XkbStateRec() state = XkbStateRec()
X11Lib.XkbGetState(display, 0x100, ctypes.pointer(state)) # 0x100 is core device FIXME X11Lib.XkbGetState(Xkbdisplay, XkbUseCoreKbd, ctypes.pointer(state))
return state.group return state.group
else: else:
return None return None
@ -645,6 +646,10 @@ class Action(RuleComponent):
return None return None
dinput = evdev.uinput.UInput()
ddevice = dinput.device
class KeyPress(Action): class KeyPress(Action):
def __init__(self, keys): def __init__(self, keys):
if isinstance(keys, str): if isinstance(keys, str):
@ -666,7 +671,7 @@ class KeyPress(Action):
return k.keycode return k.keycode
else: else:
for k in keycodes.keys: for k in keycodes.keys:
if group == k.group: if group is None or group == k.group:
return k.keycode return k.keycode
_log.warn('rule KeyPress key symbol not currently available %s', self) _log.warn('rule KeyPress key symbol not currently available %s', self)
@ -681,13 +686,15 @@ 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 self.needed(keycode, modifiers) and x11 and keycode: if self.needed(keycode, modifiers) and x11 and keycode:
Xlib.ext.xtest.fake_input(displayt, X.KeyPress, keycode) # Xlib.ext.xtest.fake_input(displayt, X.KeyPress, keycode)
ddevice.write(evdev.ecodes.EV_KEY, keycode - 8, 1) # X adds 8 to keycodes
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 self.needed(keycode, modifiers) and x11 and keycode: if self.needed(keycode, modifiers) and x11 and keycode:
Xlib.ext.xtest.fake_input(displayt, X.KeyRelease, keycode) # Xlib.ext.xtest.fake_input(displayt, X.KeyRelease, keycode)
ddevice.write(evdev.ecodes.EV_KEY, keycode - 8, 0) # X adds 8 to keycodes
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()
@ -696,7 +703,8 @@ class KeyPress(Action):
self.keyDown(self.key_symbols, current) self.keyDown(self.key_symbols, current)
self.keyUp(reversed(self.key_symbols), current) self.keyUp(reversed(self.key_symbols), current)
if x11: if x11:
displayt.sync() # displayt.sync()
dinput.syn()
return None return None
def data(self): def data(self):