From 97344c4660fd3c4b961be23da2f5d39b0696a75c Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Wed, 4 May 2022 11:37:05 -0400 Subject: [PATCH] rules: better determination of keycodes and handle keysym level --- docs/rules.md | 19 +++++++++++++------ lib/logitech_receiver/diversion.py | 30 +++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/docs/rules.md b/docs/rules.md index 00999f57..1cda70f5 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -155,14 +155,21 @@ 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 and simulates a chorded keypress on the keyboard, -such as "A", "Shift+A", or "Control+A". -Use separate `KeyPress` actions for multiple characters. -If Solaar can determine the current modifier keys, -any key symbols that correspond to these modifier keys are not pressed. +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. +Use separate `KeyPress` actions for multiple characters, +i.e., don't use a single `KeyPress` like 'a+b'. +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". +If Solaar can determine the current key modifiers (shift, control, etc.) +any key symbols that correspond to these modifier keys are not pressed, +so if the shift key is currently down on a keyboard Solaar will not bother to simulate a shift key. + Simulating input in Linux is complex. Solaar has to try to determine which keyboard key corresponds to which input character as it cannot directly -simulate inputting a character and, unfortunately, this determination can go wrong in multiple ways and is more likely +simulate inputting a key symbol. +Unfortunately, this determination can go wrong in several ways and is more likely to go wrong under Wayland than under X11. A `MouseScroll` action takes a sequence of two numbers and simulates a horizontal and vertical mouse scroll of these amounts. diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index 61ae8d96..e721dbaa 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -836,14 +836,14 @@ class KeyPress(Action): def keysym_to_keycode(self, keysym, modifiers): # maybe should take shift into account group = kbdgroup() or 0 keycodes = gkeymap.get_entries_for_keyval(keysym) - if len(keycodes.keys) == 1: - k = keycodes.keys[0] - return k.keycode - else: - for k in keycodes.keys: - if group is None or group == k.group: - return k.keycode + (keycode, level) = (None, None) + for k in keycodes.keys: + if (group == k.group or len(keycodes.keys) == 1) and k.keycode < 256 and (level is None or k.level < level): + keycode = k.keycode + level = k.level + if keycode is None: _log.warn('rule KeyPress key symbol not currently available %s', self) + return (keycode, level) def __str__(self): return 'KeyPress: ' + ' '.join(self.key_names) @@ -852,17 +852,29 @@ class KeyPress(Action): code = modifier_code(k) return not (code is not None and modifiers & (1 << code)) + def mods(self, level, modifiers, direction): + if level == 2 or level == 3: + (sk, _) = self.keysym_to_keycode(XK_KEYS.get('ISO_Level3_Shift', None), modifiers) + if sk and self.needed(sk, modifiers): + simulate_key(sk, direction) + if level == 1 or level == 3: + (sk, _) = self.keysym_to_keycode(XK_KEYS.get('Shift_L', None), modifiers) + if sk and self.needed(sk, modifiers): + simulate_key(sk, direction) + def keyDown(self, keysyms, modifiers): for k in keysyms: - keycode = self.keysym_to_keycode(k, modifiers) + (keycode, level) = self.keysym_to_keycode(k, modifiers) if keycode and self.needed(keycode, modifiers): + self.mods(level, modifiers, _KEY_PRESS) simulate_key(keycode, _KEY_PRESS) def keyUp(self, keysyms, modifiers): for k in keysyms: - keycode = self.keysym_to_keycode(k, modifiers) + (keycode, level) = self.keysym_to_keycode(k, modifiers) if keycode and self.needed(keycode, modifiers): simulate_key(keycode, _KEY_RELEASE) + self.mods(level, modifiers, _KEY_RELEASE) def evaluate(self, feature, notification, device, status, last_result): if gkeymap: