rules: partial implementation of rules under Wayland

This commit is contained in:
Peter F. Patel-Schneider 2022-01-25 16:12:22 -05:00
parent cb7845471c
commit 3e2be09cb5
1 changed files with 55 additions and 26 deletions

View File

@ -41,7 +41,12 @@ from .special_keys import CONTROL as _CONTROL
_log = getLogger(__name__)
del getLogger
#
# See docs/rules.md for documentation
#
# many of the rule features require X11 so turn rule processing off if X11 is not available
XK_KEYS = {}
try:
import Xlib
from Xlib import X
@ -57,13 +62,13 @@ try:
NET_WM_PID = disp_prog.intern_atom('_NET_WM_PID')
WM_CLASS = disp_prog.intern_atom('WM_CLASS')
except Exception:
_log.warn('X11 not available - rules will not be activated', exc_info=_sys.exc_info())
XK_KEYS = {}
_log.warn(
'X11 not available - rules cannot access current process or modifier key state nor can they simulate input',
exc_info=_sys.exc_info()
)
x11 = False
# determine current key modifiers
# there must be a better way to do this
# determine current key modifiers - there must be a better way to do this
if x11:
display = Display()
try:
@ -81,10 +86,10 @@ if x11:
}]
)
except Exception:
_log.warn('X11 xtest not available - Modifiers and KeyPress will not work correctly', exc_info=_sys.exc_info())
_log.warn('X11 xtest not available - rules cannot access modifier key state', exc_info=_sys.exc_info())
context = None
modifier_keycodes = display.get_modifier_mapping()
current_key_modifiers = 0
current_key_modifiers = 0
def modifier_code(keycode):
@ -112,8 +117,6 @@ if x11 and context is not None:
_thread.start_new_thread(display.record_enable_context, (context, key_press_handler))
# display.record_free_context(context) when should this be done??
# See docs/rules.md for documentation
key_down = None
key_up = None
@ -180,6 +183,8 @@ COMPONENTS = {}
if x11:
displayt = Display()
else:
displayt = None
class RuleComponent:
@ -320,6 +325,8 @@ def x11_pointer_prog():
class Process(Condition):
def __init__(self, process):
self.process = process
if not x11:
_log.warn('X11 not available - rules cannot access current process - %s', self)
if not isinstance(process, str):
_log.warn('rule Process argument not a string: %s', process)
self.process = str(process)
@ -330,7 +337,7 @@ class Process(Condition):
def evaluate(self, feature, notification, device, status, last_result):
if not isinstance(self.process, str):
return False
focus = x11_focus_prog()
focus = x11_focus_prog() if x11 else None
result = any(bool(s and s.startswith(self.process)) for s in focus) if focus else None
return result
@ -341,6 +348,8 @@ class Process(Condition):
class MouseProcess(Condition):
def __init__(self, process):
self.process = process
if not x11:
_log.warn('X11 not available - rules cannot access current mouse process - %s', self)
if not isinstance(process, str):
_log.warn('rule MouseProcess argument not a string: %s', process)
self.process = str(process)
@ -351,7 +360,7 @@ class MouseProcess(Condition):
def evaluate(self, feature, notification, device, status, last_result):
if not isinstance(self.process, str):
return False
result = any(bool(s and s.startswith(self.process)) for s in x11_pointer_prog())
result = any(bool(s and s.startswith(self.process)) for s in x11_pointer_prog()) if x11 else None
return result
def data(self):
@ -407,11 +416,15 @@ class Modifiers(Condition):
self.modifiers.append(k)
else:
_log.warn('unknown rule Modifier value: %s', k)
if not x11:
_log.warn('X11 not available - rules cannot access keyboard modifier state - %s', self)
def __str__(self):
return 'Modifiers: ' + str(self.desired)
def evaluate(self, feature, notification, device, status, last_result):
if not context:
_log.warn('X11 xtest not available - rules cannot access modifier key state - %s', self)
return self.desired == (current_key_modifiers & MODIFIER_MASK)
def data(self):
@ -567,27 +580,33 @@ class KeyPress(Action):
if isinstance(keys, str):
keys = [keys]
self.key_symbols = keys
key_from_string = lambda s: displayt.keysym_to_keycode(Xlib.XK.string_to_keysym(s))
if x11:
key_from_string = lambda s: (displayt.keysym_to_keycode(Xlib.XK.string_to_keysym(s)))
self.keys = [isinstance(k, str) and key_from_string(k) for k in keys]
if not all(self.keys):
_log.warn('rule KeyPress argument not sequence of key names %s', keys)
self.keys = []
else:
self.keys = []
_log.warn('X11 not available - rules cannot simulate keyboard input - %s', self)
def __str__(self):
return 'KeyPress: ' + ' '.join(self.key_symbols)
def needed(self, k, current_key_modifiers):
if not context:
_log.warn('X11 xtest not available - rules cannot access modifier key state - %s', self)
code = modifier_code(k)
return not (code and current_key_modifiers & (1 << code))
def keyDown(self, keys, modifiers):
for k in keys:
if self.needed(k, modifiers):
if self.needed(k, modifiers) and x11:
Xlib.ext.xtest.fake_input(displayt, X.KeyPress, k)
def keyUp(self, keys, modifiers):
for k in keys:
if self.needed(k, modifiers):
if self.needed(k, modifiers) and x11:
Xlib.ext.xtest.fake_input(displayt, X.KeyRelease, k)
def evaluate(self, feature, notification, device, status, last_result):
@ -596,6 +615,7 @@ class KeyPress(Action):
_log.info('KeyPress action: %s, modifiers %s %s', self.key_symbols, current, [hex(k) for k in self.keys])
self.keyDown(self.keys, current)
self.keyUp(reversed(self.keys), current)
if x11:
displayt.sync()
return None
@ -626,9 +646,12 @@ for i in range(8, 31):
def click(button, count):
if x11:
for _ in range(count):
Xlib.ext.xtest.fake_input(displayt, Xlib.X.ButtonPress, button)
Xlib.ext.xtest.fake_input(displayt, Xlib.X.ButtonRelease, button)
else:
_log.warn('X11 not available - rules cannot simulate mouse clicks')
class MouseScroll(Action):
@ -640,6 +663,8 @@ class MouseScroll(Action):
_log.warn('rule MouseScroll argument not two numbers %s', amounts)
amounts = [0, 0]
self.amounts = amounts
if not x11:
_log.warn('X11 not available - rules cannot simulate mouse scrolling - %s', self)
def __str__(self):
return 'MouseScroll: ' + ' '.join([str(a) for a in self.amounts])
@ -657,6 +682,7 @@ class MouseScroll(Action):
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))
if x11:
displayt.sync()
return None
@ -680,6 +706,8 @@ class MouseClick(Action):
except (ValueError, TypeError):
_log.warn('rule MouseClick action: count %s should be an integer', count)
self.count = 1
if not x11:
_log.warn('X11 not available - rules cannot simulate mouse clicks - %s', self)
def __str__(self):
return 'MouseClick: %s (%d)' % (self.button, self.count)
@ -689,6 +717,7 @@ class MouseClick(Action):
_log.info('MouseClick action: %d %s' % (self.count, self.button))
if self.button and self.count:
click(buttons[self.button], self.count)
if x11:
displayt.sync()
return None
@ -779,7 +808,7 @@ COMPONENTS = {
}
built_in_rules = Rule([])
if x11:
if True: # x11
built_in_rules = Rule([
{'Rule': [ # Implement problematic keys for Craft and MX Master
{'Rule': [{'Key': 'Brightness Down'}, {'KeyPress': 'XF86_MonBrightnessDown'}]},
@ -817,7 +846,7 @@ mr_key_down = False
# process a notification
def process_notification(device, status, notification, feature):
if not x11:
if False: # not x11
return
global keys_down, g_keys_down, m_keys_down, mr_key_down, key_down, key_up
key_down, key_up = None, None
@ -935,5 +964,5 @@ def _load_config_rule_file():
rules = Rule([Rule(loaded_rules, source=_file_path), built_in_rules])
if x11:
if True: # x11
_load_config_rule_file()