rules: make rule processing conditional on X11 being available
This commit is contained in:
parent
60afd53257
commit
419a7722ad
|
@ -109,7 +109,8 @@ program.
|
|||
|
||||
Solaar can process HID++ Feature Notifications from devices to, for example,
|
||||
change the speed of some thumb wheels. For more information on this capability of Solaar see
|
||||
[the rules page](https://pwr-solaar.github.io/Solaar/rules).
|
||||
[the rules page](https://pwr-solaar.github.io/Solaar/rules). As much of rule processing
|
||||
depends on X11, this capability is only when running under X11.
|
||||
|
||||
Users can edit rules using a GUI by clicking on the `Edit Rule` button in the Solaar main window.
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ title: Rule Processing of HID++ Notifications
|
|||
layout: page
|
||||
---
|
||||
|
||||
Note that rule processing is only available when running under X11.
|
||||
|
||||
Logitech devices that use HID++ version 2.0 or greater produce feature-based
|
||||
notifications that Solaar can process using a simple rule language. For
|
||||
example, using rules Solaar can emulate an `XF86_MonBrightnessDown` key tap
|
||||
|
|
|
@ -25,12 +25,7 @@ from logging import getLogger
|
|||
|
||||
import _thread
|
||||
import psutil
|
||||
import Xlib
|
||||
|
||||
from Xlib import X
|
||||
from Xlib.display import Display
|
||||
from Xlib.ext import record
|
||||
from Xlib.protocol import rq
|
||||
from yaml import add_representer as _yaml_add_representer
|
||||
from yaml import dump_all as _yaml_dump_all
|
||||
from yaml import safe_load_all as _yaml_safe_load_all
|
||||
|
@ -42,7 +37,21 @@ from .special_keys import CONTROL as _CONTROL
|
|||
_log = getLogger(__name__)
|
||||
del getLogger
|
||||
|
||||
Xlib.XK.load_keysym_group('xf86')
|
||||
# many of the rule features require X11 so turn rule processing off if X11 is not available
|
||||
try:
|
||||
import Xlib
|
||||
from Xlib import X
|
||||
from Xlib.display import Display
|
||||
from Xlib.ext import record
|
||||
from Xlib.protocol import rq
|
||||
from Xlib import XK as _XK
|
||||
_XK.load_keysym_group('xf86')
|
||||
XK_KEYS = vars(_XK)
|
||||
x11 = True
|
||||
except Exception:
|
||||
_log.warn('Xlib not available - rules will not be activated')
|
||||
XK_KEYS = {}
|
||||
x11 = False
|
||||
|
||||
# determine name of active process
|
||||
|
||||
|
@ -118,7 +127,8 @@ def key_press_handler(reply):
|
|||
current_key_modifiers = event.state & ~(1 << mod) if mod is not None else event.state
|
||||
|
||||
|
||||
_thread.start_new_thread(display.record_enable_context, (context, key_press_handler))
|
||||
if x11:
|
||||
_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
|
||||
|
@ -578,49 +588,46 @@ COMPONENTS = {
|
|||
'Execute': Execute,
|
||||
}
|
||||
|
||||
|
||||
built_in_rules = Rule([
|
||||
## Some malformed Rules for testing
|
||||
## Rule([Process(0), Feature(0), Modifiers(['XX', 0]), Modifiers('XXX'), Modifiers([0]),
|
||||
## KeyPress(['XXXXX', 0]), KeyPress(['XXXXXX']), KeyPress(0),
|
||||
## MouseScroll(0), MouseScroll([0, 0, 0]), MouseScroll(['a', 0]),
|
||||
## Rule(["XXXXXXX"])]),
|
||||
## Rule([Feature(0)]),
|
||||
## Rule([Modifiers(['XXXXXXXXX', 0])]),
|
||||
## Rule([KeyPress(['XXXXXSSSSS', 0])]),
|
||||
{'Rule': [ # Implement problematic keys for Craft and MX Master
|
||||
{'Feature': 'REPROG_CONTROLS_V4'},
|
||||
{'Report': 0x0},
|
||||
{'Rule': [{'Key': 'Brightness Down'}, {'KeyPress': 'XF86_MonBrightnessDown'}]},
|
||||
{'Rule': [{'Key': 'Brightness Up'}, {'KeyPress': 'XF86_MonBrightnessUp'}]},
|
||||
]},
|
||||
{'Rule': [ # In firefox, crown movements emits keys that move up and down if not pressed, rotate through tabs otherwise
|
||||
{'Process': 'firefox'},
|
||||
{'Feature': 'CROWN'},
|
||||
{'Report': 0x0},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_right_ratchet'}, {'KeyPress': ['Control_R', 'Tab']}]},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_left_ratchet'}, {'KeyPress': ['Control_R', 'Shift_R', 'Tab']}]},
|
||||
{'Rule': [{'Test': 'crown_right_ratchet'}, {'KeyPress': 'Down'}]},
|
||||
Rule([Test('crown_left_ratchet'), KeyPress(['Up'])]),
|
||||
]},
|
||||
{'Rule': [ # Otherwise, crown movements emit keys that modify volume if not pressed, move between tracks otherwise
|
||||
{'Feature': 'CROWN'}, {'Report': 0x0},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_right_ratchet'}, {'KeyPress': 'XF86_AudioNext'}]},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_left_ratchet'}, {'KeyPress': 'XF86_AudioPrev'}]},
|
||||
{'Rule': [{'Test': 'crown_right_ratchet'}, {'KeyPress': 'XF86_AudioRaiseVolume'}]},
|
||||
{'Rule': [{'Test': 'crown_left_ratchet'}, {'KeyPress': 'XF86_AudioLowerVolume'}]}
|
||||
]},
|
||||
{'Rule': [ # Thumb wheel does horizontal movement, doubled if control key not pressed
|
||||
{'Feature': 'THUMB WHEEL'}, # with control modifier on mouse scrolling sometimes does something different!
|
||||
{'Rule': [{'Modifiers': 'Control'}, {'Test': 'thumb_wheel_up'}, {'MouseScroll': [-1, 0]}]},
|
||||
{'Rule': [{'Modifiers': 'Control'}, {'Test': 'thumb_wheel_down'}, {'MouseScroll': [-1, 0]}]},
|
||||
{'Rule': [{'Or': [{'Test': 'thumb_wheel_up'}, {'Test': 'thumb_wheel_down'}]}, {'MouseScroll': [-2, 0]}]}
|
||||
]}
|
||||
])
|
||||
built_in_rules = Rule([])
|
||||
if x11:
|
||||
built_in_rules = Rule([
|
||||
{'Rule': [ # Implement problematic keys for Craft and MX Master
|
||||
{'Feature': 'REPROG_CONTROLS_V4'},
|
||||
{'Report': 0x0},
|
||||
{'Rule': [{'Key': 'Brightness Down'}, {'KeyPress': 'XF86_MonBrightnessDown'}]},
|
||||
{'Rule': [{'Key': 'Brightness Up'}, {'KeyPress': 'XF86_MonBrightnessUp'}]},
|
||||
]},
|
||||
{'Rule': [ # In firefox, crown emits keys that move up and down if not pressed, rotate through tabs otherwise
|
||||
{'Process': 'firefox'},
|
||||
{'Feature': 'CROWN'},
|
||||
{'Report': 0x0},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_right_ratchet'}, {'KeyPress': ['Control_R', 'Tab']}]},
|
||||
{'Rule': [{'Test': 'crown_pressed'},
|
||||
{'Test': 'crown_left_ratchet'},
|
||||
{'KeyPress': ['Control_R', 'Shift_R', 'Tab']}]},
|
||||
{'Rule': [{'Test': 'crown_right_ratchet'}, {'KeyPress': 'Down'}]},
|
||||
Rule([Test('crown_left_ratchet'), KeyPress(['Up'])]),
|
||||
]},
|
||||
{'Rule': [ # Otherwise, crown movements emit keys that modify volume if not pressed, move between tracks otherwise
|
||||
{'Feature': 'CROWN'}, {'Report': 0x0},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_right_ratchet'}, {'KeyPress': 'XF86_AudioNext'}]},
|
||||
{'Rule': [{'Test': 'crown_pressed'}, {'Test': 'crown_left_ratchet'}, {'KeyPress': 'XF86_AudioPrev'}]},
|
||||
{'Rule': [{'Test': 'crown_right_ratchet'}, {'KeyPress': 'XF86_AudioRaiseVolume'}]},
|
||||
{'Rule': [{'Test': 'crown_left_ratchet'}, {'KeyPress': 'XF86_AudioLowerVolume'}]}
|
||||
]},
|
||||
{'Rule': [ # Thumb wheel does horizontal movement, doubled if control key not pressed
|
||||
{'Feature': 'THUMB WHEEL'}, # with control modifier on mouse scrolling sometimes does something different!
|
||||
{'Rule': [{'Modifiers': 'Control'}, {'Test': 'thumb_wheel_up'}, {'MouseScroll': [-1, 0]}]},
|
||||
{'Rule': [{'Modifiers': 'Control'}, {'Test': 'thumb_wheel_down'}, {'MouseScroll': [-1, 0]}]},
|
||||
{'Rule': [{'Or': [{'Test': 'thumb_wheel_up'}, {'Test': 'thumb_wheel_down'}]}, {'MouseScroll': [-2, 0]}]}
|
||||
]}
|
||||
])
|
||||
|
||||
|
||||
# process a notification
|
||||
def process_notification(device, status, notification, feature):
|
||||
if not x11:
|
||||
return
|
||||
global keys_down, key_down
|
||||
key_down = None
|
||||
# need to keep track of keys that are down to find a new key down
|
||||
|
|
|
@ -24,10 +24,9 @@ from contextlib import contextmanager as contextlib_contextmanager
|
|||
from logging import getLogger
|
||||
from shlex import quote as shlex_quote
|
||||
|
||||
import Xlib.XK
|
||||
|
||||
from gi.repository import Gdk, GObject, Gtk
|
||||
from logitech_receiver import diversion as _DIV
|
||||
from logitech_receiver.diversion import XK_KEYS as _XK_KEYS
|
||||
from logitech_receiver.diversion import buttons as _buttons
|
||||
from logitech_receiver.hidpp20 import FEATURE as _ALL_FEATURES
|
||||
from logitech_receiver.special_keys import CONTROL as _CONTROL
|
||||
|
@ -1086,7 +1085,7 @@ class ActionUI(RuleComponentUI):
|
|||
class KeyPressUI(ActionUI):
|
||||
|
||||
CLASS = _DIV.KeyPress
|
||||
KEY_NAMES = [k[3:] if k.startswith('XK_') else k for k, v in vars(Xlib.XK).items() if isinstance(v, int)]
|
||||
KEY_NAMES = [k[3:] if k.startswith('XK_') else k for k, v in _XK_KEYS.items() if isinstance(v, int)]
|
||||
|
||||
def create_widgets(self):
|
||||
self.widgets = {}
|
||||
|
|
Loading…
Reference in New Issue