rules: fix problems when X11 is not available
This commit is contained in:
parent
371027c690
commit
5aa02aa01d
|
@ -16,9 +16,11 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import ctypes as _ctypes
|
||||||
import os as _os
|
import os as _os
|
||||||
import os.path as _path
|
import os.path as _path
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
|
import time as _time
|
||||||
|
|
||||||
from logging import DEBUG as _DEBUG
|
from logging import DEBUG as _DEBUG
|
||||||
from logging import INFO as _INFO
|
from logging import INFO as _INFO
|
||||||
|
@ -67,74 +69,153 @@ del getLogger
|
||||||
# and http://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h
|
# and http://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h
|
||||||
# because there does not seem to be a non-X11 file for this set of key names
|
# because there does not seem to be a non-X11 file for this set of key names
|
||||||
|
|
||||||
|
# Setting up is complex because there are several systems that each provide partial facilities:
|
||||||
|
# GDK - always available (when running with a window system) but only provides access to keymap
|
||||||
|
# X11 - provides access to active process and process with window under mouse and current modifier keys
|
||||||
|
# Xtest extension to X11 - provides input simulation, partly works under Wayland
|
||||||
|
# Wayland - provides input simulation
|
||||||
|
|
||||||
XK_KEYS = _keysymdef.keysymdef
|
XK_KEYS = _keysymdef.keysymdef
|
||||||
|
|
||||||
|
# Event codes - can't use Xlib.X codes because Xlib might not be available
|
||||||
|
_KEY_RELEASE = 0
|
||||||
|
_KEY_PRESS = 1
|
||||||
|
_BUTTON_RELEASE = 2
|
||||||
|
_BUTTON_PRESS = 3
|
||||||
|
|
||||||
|
gdisplay = Gdk.Display.get_default() # can be None if Solaar is run without a full window system
|
||||||
|
gkeymap = Gdk.Keymap.get_for_display(gdisplay) if gdisplay else None
|
||||||
|
if _log.isEnabledFor(_INFO):
|
||||||
|
_log.info('GDK Keymap %sset up', '' if gkeymap else 'not ')
|
||||||
|
|
||||||
|
wayland = _os.getenv('WAYLAND_DISPLAY') # is this Wayland?
|
||||||
|
if wayland:
|
||||||
|
_log.warn('rules cannot access active process or modifier keys in Wayland')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import Xlib
|
import Xlib
|
||||||
from Xlib.display import Display
|
_x11 = None # X11 might be available
|
||||||
xdisplay = Display()
|
|
||||||
modifier_keycodes = xdisplay.get_modifier_mapping() # there should be a way to do this in Gdk
|
|
||||||
x11 = True
|
|
||||||
|
|
||||||
NET_ACTIVE_WINDOW = xdisplay.intern_atom('_NET_ACTIVE_WINDOW')
|
|
||||||
NET_WM_PID = xdisplay.intern_atom('_NET_WM_PID')
|
|
||||||
WM_CLASS = xdisplay.intern_atom('WM_CLASS')
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
_log.warn(
|
_x11 = False # X11 is not available
|
||||||
'X11 not available - rules cannot access current process, modifier keys, or keyboard group. %s',
|
|
||||||
exc_info=_sys.exc_info()
|
|
||||||
)
|
|
||||||
modifier_keycodes = []
|
|
||||||
x11 = False
|
|
||||||
|
|
||||||
if x11:
|
xtest_available = True # Xtest might be available
|
||||||
|
xdisplay = None
|
||||||
|
Xkbdisplay = None # xkb might be avilable
|
||||||
|
modifier_keycodes = []
|
||||||
|
XkbUseCoreKbd = 0x100
|
||||||
|
|
||||||
|
|
||||||
|
class XkbDisplay(_ctypes.Structure):
|
||||||
|
""" opaque struct """
|
||||||
|
|
||||||
|
|
||||||
|
class XkbStateRec(_ctypes.Structure):
|
||||||
|
_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_mods', _ctypes.c_ubyte), ('locked_mods', _ctypes.c_ubyte), ('compat_state', _ctypes.c_ubyte),
|
||||||
|
('grab_mods', _ctypes.c_ubyte), ('compat_grab_mods', _ctypes.c_ubyte), ('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
|
||||||
|
|
||||||
|
|
||||||
|
def x11_setup():
|
||||||
|
global _x11, xdisplay, modifier_keycodes, NET_ACTIVE_WINDOW, NET_WM_PID, WM_CLASS, xtest_available
|
||||||
|
if _x11 is not None:
|
||||||
|
return _x11
|
||||||
try:
|
try:
|
||||||
# set up to get keyboard state using ctypes interface to libx11
|
from Xlib.display import Display
|
||||||
import ctypes
|
xdisplay = Display()
|
||||||
|
modifier_keycodes = xdisplay.get_modifier_mapping() # there should be a way to do this in Gdk
|
||||||
class XkbDisplay(ctypes.Structure):
|
NET_ACTIVE_WINDOW = xdisplay.intern_atom('_NET_ACTIVE_WINDOW')
|
||||||
""" opaque struct """
|
NET_WM_PID = xdisplay.intern_atom('_NET_WM_PID')
|
||||||
|
WM_CLASS = xdisplay.intern_atom('WM_CLASS')
|
||||||
class XkbStateRec(ctypes.Structure):
|
_x11 = True # X11 available
|
||||||
_fields_ = [('group', ctypes.c_ubyte), ('locked_group', ctypes.c_ubyte), ('base_group', ctypes.c_ushort),
|
if _log.isEnabledFor(_INFO):
|
||||||
('latched_group', ctypes.c_ushort), ('mods', ctypes.c_ubyte), ('base_mods', ctypes.c_ubyte),
|
_log.info('X11 library loaded and display set up')
|
||||||
('latched_mods', ctypes.c_ubyte), ('locked_mods', ctypes.c_ubyte), ('compat_state', ctypes.c_ubyte),
|
|
||||||
('grab_mods', ctypes.c_ubyte), ('compat_grab_mods', ctypes.c_ubyte), ('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
|
|
||||||
|
|
||||||
XkbUseCoreKbd = 0x100
|
|
||||||
X11Lib = ctypes.cdll.LoadLibrary('libX11.so')
|
|
||||||
X11Lib.XOpenDisplay.restype = ctypes.POINTER(XkbDisplay)
|
|
||||||
X11Lib.XkbGetState.argtypes = [ctypes.POINTER(XkbDisplay), ctypes.c_uint, ctypes.POINTER(XkbStateRec)]
|
|
||||||
Xkbdisplay = X11Lib.XOpenDisplay(None)
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
_log.warn('X11 library not available - rules cannot access keyboard group. %s', exc_info=_sys.exc_info())
|
_log.warn('X11 not available - some rule capabilities inoperable: %s', exc_info=_sys.exc_info())
|
||||||
Xkbdisplay = None
|
_x11 = False
|
||||||
|
xtest_available = False
|
||||||
|
return _x11
|
||||||
|
|
||||||
|
|
||||||
|
def xkb_setup():
|
||||||
|
global X11Lib, Xkbdisplay
|
||||||
|
if Xkbdisplay is not None:
|
||||||
|
return Xkbdisplay
|
||||||
|
try: # set up to get keyboard state using ctypes interface to libx11
|
||||||
|
X11Lib = _ctypes.cdll.LoadLibrary('libX11.so')
|
||||||
|
X11Lib.XOpenDisplay.restype = _ctypes.POINTER(XkbDisplay)
|
||||||
|
X11Lib.XkbGetState.argtypes = [_ctypes.POINTER(XkbDisplay), _ctypes.c_uint, _ctypes.POINTER(XkbStateRec)]
|
||||||
|
Xkbdisplay = X11Lib.XOpenDisplay(None)
|
||||||
|
if _log.isEnabledFor(_INFO):
|
||||||
|
_log.info('XKB display set up')
|
||||||
|
except Exception:
|
||||||
|
_log.warn('XKB display not available - rules cannot access keyboard group: %s', exc_info=_sys.exc_info())
|
||||||
|
Xkbdisplay = False
|
||||||
|
return Xkbdisplay
|
||||||
|
|
||||||
|
|
||||||
|
buttons = {
|
||||||
|
'unknown': (None, None),
|
||||||
|
'left': (1, evdev.ecodes.ecodes['BTN_LEFT']),
|
||||||
|
'middle': (2, evdev.ecodes.ecodes['BTN_MIDDLE']),
|
||||||
|
'right': (3, evdev.ecodes.ecodes['BTN_RIGHT']),
|
||||||
|
'scroll_up': (4, evdev.ecodes.ecodes['BTN_4']),
|
||||||
|
'scroll_down': (5, evdev.ecodes.ecodes['BTN_5']),
|
||||||
|
'scroll_left': (6, evdev.ecodes.ecodes['BTN_6']),
|
||||||
|
'scroll_right': (7, evdev.ecodes.ecodes['BTN_7']),
|
||||||
|
'button8': (8, evdev.ecodes.ecodes['BTN_8']),
|
||||||
|
'button9': (9, evdev.ecodes.ecodes['BTN_9']),
|
||||||
|
}
|
||||||
|
|
||||||
|
# uinput capability for keyboard keys, mouse buttons, and scrolling
|
||||||
|
key_events = [c for n, c in evdev.ecodes.ecodes.items() if n.startswith('KEY') and n != 'KEY_CNT']
|
||||||
|
for (_, evcode) in buttons.values():
|
||||||
|
if evcode:
|
||||||
|
key_events.append(evcode)
|
||||||
|
devicecap = {
|
||||||
|
evdev.ecodes.EV_KEY:
|
||||||
|
key_events,
|
||||||
|
evdev.ecodes.EV_REL:
|
||||||
|
[evdev.ecodes.REL_WHEEL, evdev.ecodes.REL_HWHEEL, evdev.ecodes.REL_WHEEL_HI_RES, evdev.ecodes.REL_HWHEEL_HI_RES]
|
||||||
|
}
|
||||||
|
udevice = None
|
||||||
|
|
||||||
|
|
||||||
|
def setup_uinput():
|
||||||
|
global udevice
|
||||||
|
if udevice is not None:
|
||||||
|
return udevice
|
||||||
|
try:
|
||||||
|
udevice = evdev.uinput.UInput(events=devicecap, name='solaar-keyboard')
|
||||||
|
if _log.isEnabledFor(_INFO):
|
||||||
|
_log.info('uinput device set up')
|
||||||
|
except Exception as e:
|
||||||
|
_log.warn('cannot create uinput device: %s', e)
|
||||||
|
|
||||||
|
|
||||||
|
if wayland: # wayland can't use xtest so may as well set up uinput now
|
||||||
|
setup_uinput()
|
||||||
|
|
||||||
|
|
||||||
def kbdgroup():
|
def kbdgroup():
|
||||||
if Xkbdisplay:
|
if xkb_setup():
|
||||||
state = XkbStateRec()
|
state = XkbStateRec()
|
||||||
X11Lib.XkbGetState(Xkbdisplay, XkbUseCoreKbd, ctypes.pointer(state))
|
X11Lib.XkbGetState(Xkbdisplay, XkbUseCoreKbd, _ctypes.pointer(state))
|
||||||
return state.group
|
return state.group
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def modifier_code(keycode):
|
def modifier_code(keycode):
|
||||||
if keycode == 0:
|
if wayland or not x11_setup() or keycode == 0:
|
||||||
return None
|
return None
|
||||||
for m in range(0, len(modifier_keycodes)):
|
for m in range(0, len(modifier_keycodes)):
|
||||||
if keycode in modifier_keycodes[m]:
|
if keycode in modifier_keycodes[m]:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
gdisplay = Gdk.Display.get_default() # can be None if Solaar is run without a full window system
|
|
||||||
gkeymap = Gdk.Keymap.get_for_display(gdisplay) if gdisplay else None
|
|
||||||
|
|
||||||
key_down = None
|
key_down = None
|
||||||
key_up = None
|
key_up = None
|
||||||
|
|
||||||
|
@ -170,85 +251,75 @@ def xy_direction(_x, _y):
|
||||||
return 'noop'
|
return 'noop'
|
||||||
|
|
||||||
|
|
||||||
buttons = {
|
|
||||||
'unknown': (None, None),
|
|
||||||
'left': (1, evdev.ecodes.ecodes['BTN_LEFT']),
|
|
||||||
'middle': (2, evdev.ecodes.ecodes['BTN_MIDDLE']),
|
|
||||||
'right': (3, evdev.ecodes.ecodes['BTN_RIGHT']),
|
|
||||||
'scroll_up': (4, evdev.ecodes.ecodes['BTN_4']),
|
|
||||||
'scroll_down': (5, evdev.ecodes.ecodes['BTN_5']),
|
|
||||||
'scroll_left': (6, evdev.ecodes.ecodes['BTN_6']),
|
|
||||||
'scroll_right': (7, evdev.ecodes.ecodes['BTN_7']),
|
|
||||||
'button8': (8, evdev.ecodes.ecodes['BTN_8']),
|
|
||||||
'button9': (9, evdev.ecodes.ecodes['BTN_9']),
|
|
||||||
}
|
|
||||||
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:
|
|
||||||
displayt = Display()
|
|
||||||
else:
|
|
||||||
displayt = None
|
|
||||||
try:
|
|
||||||
ukeyboard = evdev.uinput.UInput()
|
|
||||||
umouse = evdev.uinput.UInput(mousecap)
|
|
||||||
except Exception as e:
|
|
||||||
if not x11:
|
|
||||||
_log.warn('cannot create uinput device: %s', e)
|
|
||||||
else:
|
|
||||||
_log.info('cannot create uinput device: %s', e)
|
|
||||||
ukeyboard = None
|
|
||||||
umouse = None
|
|
||||||
|
|
||||||
|
|
||||||
def simulate_xtest(code, event):
|
def simulate_xtest(code, event):
|
||||||
global displayt
|
global xtest_available
|
||||||
if displayt:
|
if x11_setup() and xtest_available:
|
||||||
try:
|
try:
|
||||||
Xlib.ext.xtest.fake_input(displayt, event, code)
|
event = (
|
||||||
displayt.sync()
|
Xlib.X.KeyPress if event == _KEY_PRESS else Xlib.X.KeyRelease if event == _KEY_RELEASE else
|
||||||
|
Xlib.X.ButtonPress if event == _BUTTON_PRESS else Xlib.X.ButtonRelease if event == _BUTTON_RELEASE else None
|
||||||
|
)
|
||||||
|
Xlib.ext.xtest.fake_input(xdisplay, event, code)
|
||||||
|
xdisplay.sync()
|
||||||
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
_log.debug('xtest simulated input %s %s %s', xdisplay, event, code)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
displayt = None
|
xtest_available = False
|
||||||
_log.warn('xtest fake input failed: %s', e)
|
_log.warn('xtest fake input failed: %s', e)
|
||||||
|
|
||||||
|
|
||||||
def simulate_uinput(device, what, code, arg):
|
def simulate_uinput(what, code, arg):
|
||||||
global ukeyboard, umouse
|
global udevice
|
||||||
if device:
|
if setup_uinput():
|
||||||
try:
|
try:
|
||||||
device.write(what, code, arg)
|
udevice.write(what, code, arg)
|
||||||
device.syn()
|
udevice.syn()
|
||||||
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
_log.debug('uinput simulated input %s %s %s', what, code, arg)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ukeyboard = umouse = None
|
udevice = None
|
||||||
_log.warn('uinput write key failed: %s', e)
|
_log.warn('uinput write failed: %s', e)
|
||||||
|
|
||||||
|
|
||||||
def simulate_key(code, event): # X11 keycode and event
|
def simulate_key(code, event): # X11 keycode but Solaar event code
|
||||||
if simulate_xtest(code, event):
|
if not wayland and simulate_xtest(code, event):
|
||||||
return True
|
return True
|
||||||
direction = 1 if event == Xlib.X.KeyPress or event == Xlib.X.ButtonPress else 0
|
if simulate_uinput(evdev.ecodes.EV_KEY, code - 8, event):
|
||||||
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
|
return True
|
||||||
_log.warn('no way to simulate input')
|
_log.warn('no way to simulate key input')
|
||||||
|
|
||||||
|
|
||||||
def click(button, count):
|
def click_xtest(button, count):
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
if not simulate_xtest(button, Xlib.X.ButtonPress):
|
if not simulate_xtest(button[0], _BUTTON_PRESS):
|
||||||
return False
|
return False
|
||||||
if not simulate_xtest(button, Xlib.X.ButtonRelease):
|
if not simulate_xtest(button[0], _BUTTON_RELEASE):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def click_uinput(button, count):
|
||||||
|
for _ in range(count):
|
||||||
|
if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 1):
|
||||||
|
return False
|
||||||
|
if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 0):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def click(button, count):
|
||||||
|
if not wayland and click_xtest(button, count):
|
||||||
|
return True
|
||||||
|
if click_uinput(button, count):
|
||||||
|
return True
|
||||||
|
_log.warn('no way to simulate mouse click')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def simulate_scroll(dx, dy):
|
def simulate_scroll(dx, dy):
|
||||||
if displayt:
|
if not wayland and xtest_available:
|
||||||
success = True
|
success = True
|
||||||
if dx:
|
if dx:
|
||||||
success = click(7 if dx > 0 else 6, count=abs(dx))
|
success = click(7 if dx > 0 else 6, count=abs(dx))
|
||||||
|
@ -256,12 +327,12 @@ def simulate_scroll(dx, dy):
|
||||||
success = click(4 if dy > 0 else 5, count=abs(dy))
|
success = click(4 if dy > 0 else 5, count=abs(dy))
|
||||||
if success:
|
if success:
|
||||||
return True
|
return True
|
||||||
if umouse:
|
if setup_uinput():
|
||||||
success = True
|
success = True
|
||||||
if dx:
|
if dx:
|
||||||
success = simulate_uinput(umouse, evdev.ecodes.EV_REL, evdev.ecodes.REL_HWHEEL, dx)
|
success = simulate_uinput(evdev.ecodes.EV_REL, evdev.ecodes.REL_HWHEEL, dx)
|
||||||
if dy and success:
|
if dy and success:
|
||||||
success = simulate_uinput(umouse, evdev.ecodes.EV_REL, evdev.ecodes.REL_WHEEL, dy)
|
success = simulate_uinput(evdev.ecodes.EV_REL, evdev.ecodes.REL_WHEEL, dy)
|
||||||
if success:
|
if success:
|
||||||
return True
|
return True
|
||||||
_log.warn('no way to simulate scrolling')
|
_log.warn('no way to simulate scrolling')
|
||||||
|
@ -404,6 +475,8 @@ class And(Condition):
|
||||||
|
|
||||||
|
|
||||||
def x11_focus_prog():
|
def x11_focus_prog():
|
||||||
|
if not x11_setup():
|
||||||
|
return None
|
||||||
pid = wm_class = None
|
pid = wm_class = None
|
||||||
window = xdisplay.get_input_focus().focus
|
window = xdisplay.get_input_focus().focus
|
||||||
while window:
|
while window:
|
||||||
|
@ -420,6 +493,8 @@ def x11_focus_prog():
|
||||||
|
|
||||||
|
|
||||||
def x11_pointer_prog():
|
def x11_pointer_prog():
|
||||||
|
if not x11_setup():
|
||||||
|
return None
|
||||||
pid = wm_class = None
|
pid = wm_class = None
|
||||||
window = xdisplay.screen().root.query_pointer().child
|
window = xdisplay.screen().root.query_pointer().child
|
||||||
for child in reversed(window.query_tree().children):
|
for child in reversed(window.query_tree().children):
|
||||||
|
@ -434,8 +509,8 @@ def x11_pointer_prog():
|
||||||
class Process(Condition):
|
class Process(Condition):
|
||||||
def __init__(self, process):
|
def __init__(self, process):
|
||||||
self.process = process
|
self.process = process
|
||||||
if not x11:
|
if wayland or not x11_setup():
|
||||||
_log.warn('X11 not available - rules cannot access current process - %s', self)
|
_log.warn('rules can only access active process in X11 - %s', self)
|
||||||
if not isinstance(process, str):
|
if not isinstance(process, str):
|
||||||
_log.warn('rule Process argument not a string: %s', process)
|
_log.warn('rule Process argument not a string: %s', process)
|
||||||
self.process = str(process)
|
self.process = str(process)
|
||||||
|
@ -446,7 +521,7 @@ class Process(Condition):
|
||||||
def evaluate(self, feature, notification, device, status, last_result):
|
def evaluate(self, feature, notification, device, status, last_result):
|
||||||
if not isinstance(self.process, str):
|
if not isinstance(self.process, str):
|
||||||
return False
|
return False
|
||||||
focus = x11_focus_prog() if x11 else None
|
focus = x11_focus_prog()
|
||||||
result = any(bool(s and s.startswith(self.process)) for s in focus) if focus else None
|
result = any(bool(s and s.startswith(self.process)) for s in focus) if focus else None
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -457,8 +532,8 @@ class Process(Condition):
|
||||||
class MouseProcess(Condition):
|
class MouseProcess(Condition):
|
||||||
def __init__(self, process):
|
def __init__(self, process):
|
||||||
self.process = process
|
self.process = process
|
||||||
if not x11:
|
if wayland or not x11_setup():
|
||||||
_log.warn('X11 not available - rules cannot access current mouse process - %s', self)
|
_log.warn('rules cannot access active mouse process in X11 - %s', self)
|
||||||
if not isinstance(process, str):
|
if not isinstance(process, str):
|
||||||
_log.warn('rule MouseProcess argument not a string: %s', process)
|
_log.warn('rule MouseProcess argument not a string: %s', process)
|
||||||
self.process = str(process)
|
self.process = str(process)
|
||||||
|
@ -469,7 +544,8 @@ class MouseProcess(Condition):
|
||||||
def evaluate(self, feature, notification, device, status, last_result):
|
def evaluate(self, feature, notification, device, status, last_result):
|
||||||
if not isinstance(self.process, str):
|
if not isinstance(self.process, str):
|
||||||
return False
|
return False
|
||||||
result = any(bool(s and s.startswith(self.process)) for s in x11_pointer_prog()) if x11 else None
|
pointer_focus = x11_pointer_prog()
|
||||||
|
result = any(bool(s and s.startswith(self.process)) for s in pointer_focus) if pointer_focus else None
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def data(self):
|
def data(self):
|
||||||
|
@ -780,13 +856,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_key(keycode, Xlib.X.KeyPress)
|
simulate_key(keycode, _KEY_PRESS)
|
||||||
|
|
||||||
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_key(keycode, Xlib.X.KeyRelease)
|
simulate_key(keycode, _KEY_RELEASE)
|
||||||
|
|
||||||
def evaluate(self, feature, notification, device, status, last_result):
|
def evaluate(self, feature, notification, device, status, last_result):
|
||||||
if gkeymap:
|
if gkeymap:
|
||||||
|
@ -795,6 +871,7 @@ class KeyPress(Action):
|
||||||
_log.info('KeyPress action: %s, modifiers %s %s', self.key_names, current, [hex(k) for k in self.key_symbols])
|
_log.info('KeyPress action: %s, modifiers %s %s', self.key_names, current, [hex(k) for k in self.key_symbols])
|
||||||
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)
|
||||||
|
_time.sleep(0.01)
|
||||||
else:
|
else:
|
||||||
_log.warn('no keymap so cannot determine which keycode to send')
|
_log.warn('no keymap so cannot determine which keycode to send')
|
||||||
return None
|
return None
|
||||||
|
@ -835,6 +912,7 @@ class MouseScroll(Action):
|
||||||
_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
|
||||||
simulate_scroll(dx, dy)
|
simulate_scroll(dx, dy)
|
||||||
|
_time.sleep(0.01)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def data(self):
|
def data(self):
|
||||||
|
@ -866,6 +944,7 @@ class MouseClick(Action):
|
||||||
_log.info('MouseClick action: %d %s' % (self.count, self.button))
|
_log.info('MouseClick action: %d %s' % (self.count, self.button))
|
||||||
if self.button and self.count:
|
if self.button and self.count:
|
||||||
click(buttons[self.button], self.count)
|
click(buttons[self.button], self.count)
|
||||||
|
_time.sleep(0.01)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def data(self):
|
def data(self):
|
||||||
|
|
Loading…
Reference in New Issue