diff --git a/docs/rules.md b/docs/rules.md index 100cd27d..ce110d89 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -160,10 +160,20 @@ For settings that use gestures as an argument the internal name of the gesture i which can be found in the GESTURE2_GESTURES_LABELS structure in lib/logitech_receiver/settings_templates. For boolean settings '~' can be used to toggle the setting. -A `KeyPress` action takes a sequence of X11 key symbols, such as "a" or "Control+a", -and simulates a chorded keypress on the keyboard to produce these symbols. +A `KeyPress` action takes either the name of an X11 key symbol, such as "a", +a list of X11 key symbols, such as "a" or "Control+a", +or a two-element list with the first element as above +and the second element one of 'click', 'depress', or 'release' +and executes key actions on a simulated keyboard to produce these symbols. Use separate `KeyPress` actions for multiple characters, i.e., don't use a single `KeyPress` like 'a+b'. +The `KeyPress` action normally both depresses and releases (clicks) the keys, +but can also just depress the keys or just release the keys. +Use the depress or release options with extreme care, +ensuring that the depressed keys are later released. +Otherwise it may become difficult to use your system. +The keys are depressed in forward order and released in reverse order. + If a key symbol can only be produced by a shfited or level 3 keypress, e.g., "A", then Solaar will add keypresses to produce that keysymbol, e.g., simulating a left shift keypress to get "A" instead of "a". diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index ded9b4eb..a92be66d 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -820,14 +820,28 @@ class Action(RuleComponent): class KeyPress(Action): - def __init__(self, keys): - if isinstance(keys, str): - keys = [keys] - self.key_names = keys - self.key_symbols = [XK_KEYS.get(k, None) for k in keys] - if not all(self.key_symbols): - _log.warn('rule KeyPress not sequence of key names %s', keys) + CLICK, DEPRESS, RELEASE = 'click', 'depress', 'release' + + def __init__(self, args): + self.key_names, self.action = self.regularize_args(args) + if not isinstance(self.key_names, list): + _log.warn('rule KeyPress keys not key names %s', self.keys_names) self.key_symbols = [] + else: + self.key_symbols = [XK_KEYS.get(k, None) for k in self.key_names] + if not all(self.key_symbols): + _log.warn('rule KeyPress keys not key names %s', self.key_names) + self.key_symbols = [] + + def regularize_args(self, args): + action = self.CLICK + if not isinstance(args, list): + args = [args] + keys = args + if len(args) == 2 and args[1] in [self.CLICK, self.DEPRESS, self.RELEASE]: + keys = [args[0]] if isinstance(args[0], str) else args[0] + action = args[1] + return keys, action # WARNING: This is an attempt to reverse the keycode to keysym mappping in XKB. It may not be completely general. def keysym_to_keycode(self, keysym, modifiers): # maybe should take shift into account @@ -849,7 +863,7 @@ class KeyPress(Action): return (keycode, level) def __str__(self): - return 'KeyPress: ' + ' '.join(self.key_names) + return 'KeyPress: ' + ' '.join(self.key_names) + ' ' + self.action def needed(self, k, modifiers): code = modifier_code(k) @@ -885,16 +899,18 @@ class KeyPress(Action): if gkeymap: current = gkeymap.get_modifier_state() if _log.isEnabledFor(_INFO): - _log.info('KeyPress action: %s, group %s, modifiers %s', self.key_names, kbdgroup(), current) - self.keyDown(self.key_symbols, current) - self.keyUp(reversed(self.key_symbols), current) + _log.info('KeyPress action: %s %s, group %s, modifiers %s', self.key_names, self.action, kbdgroup(), current) + if self.action != self.RELEASE: + self.keyDown(self.key_symbols, current) + if self.action != self.DEPRESS: + self.keyUp(reversed(self.key_symbols), current) _time.sleep(0.01) else: _log.warn('no keymap so cannot determine which keycode to send') return None def data(self): - return {'KeyPress': [str(k) for k in self.key_names]} + return {'KeyPress': [[str(k) for k in self.key_names], self.action]} # KeyDown is dangerous as the key can auto-repeat and make your system unusable