Use libxkbcommon for translating keysyms, support all XKB groups.
fixes #1835 This commit improves the translation of keysyms to keycodes by loading keymaps using libxkbcommon-x11 and using libxkbcommon for figuring out the keymap, depending on each keybinding’s modifiers. This way, the upper layers of complex layouts are now usable with i3’s bindsym directive, such as de_neo’s layer 3 and higher. Furthermore, the commit generalizes the handling of different XKB groups. We formerly had support only for two separate groups, the default group 1, and group 2. While Mode_switch is only one way to switch to group 2, we called the binding option Mode_switch. With this commit, the new names Group1, Group2 (an alias for Mode_switch), Group3 and Group4 are introduced for configuring bindings. This is only useful for advanced keyboard layouts, such as people loading two keyboard layouts and switching between them (us, ru seems to be a popular combination). When grabbing keys, one can only specify the modifier mask, but not an XKB state mask (or value), so we still dynamically unbind and re-bind keys whenever the XKB group changes. The commit was manually tested using the following i3 config: bindsym Group4+n nop heya from group 4 bindsym Group3+n nop heya from group 3 bindsym Group2+n nop heya from group 2 bindsym n nop heya bindsym shift+N nop explicit shift binding bindsym shift+r nop implicit shift binding bindcode Group2+38 nop fallback overwritten in group 2 only bindcode 38 nop fallback …with the following layout: setxkbmap -layout "us,ua,ru,de" -variant ",winkeys,,neo" \ -option "grp:shift_caps_toggle,grp_led:scroll" \ -model pc104 -rules evdev By default (xkb group 1, us layout), pressing “n” will result in the “heya” message appearing. Pressing “a” will result in the “fallback” message appearing. “j” is not triggered. By pressing Shift+CapsLock you switch to the next group (xkb group 2, ua layout). Pressing “a” will result in the “fallback overwritten in group 2 only” message, pressing “n” will still result in “heya”. “j” is not triggered. In the next group (xkb group 3, ru layout), pressing “a” will result in the “fallback” message again, pressing “n” will result in “heya”, “j” is not triggered. In the last group (xkb group 4, de_neo layout), pressing “a” will still result in “fallback”, pressing “n” will result in “heya”, pressing “j” will result in “heya from group 4”. Pressing shift+n results in “explicit shift binding”, pressing shift+r results in “implicit shift binding”. This ensures that keysym translation falls back to looking at non-shift keys (“r” can be used instead of ”R”) and that the order of keybindings doesn’t play a role (“bindsym n” does not override “bindsym shift+n”, even though it’s specified earlier in the config). The fallback behavior ensures use-cases such as ticket #1775 are still covered. Only binding keys when the X server is in the corresponding XKB group ensures use-cases such as ticket #585 are still covered.
This commit is contained in:
@ -31,7 +31,7 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch
|
||||
* Grab the bound keys (tell X to send us keypress events for those keycodes)
|
||||
*
|
||||
*/
|
||||
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch);
|
||||
void grab_all_keys(xcb_connection_t *conn);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the Binding that matches the given xcb event or NULL if
|
||||
@ -52,6 +52,21 @@ void translate_keysyms(void);
|
||||
*/
|
||||
void switch_mode(const char *new_mode);
|
||||
|
||||
/**
|
||||
* Reorders bindings by event_state_mask descendingly so that get_binding()
|
||||
* correctly matches more specific bindings before more generic bindings. Take
|
||||
* the following binding configuration as an example:
|
||||
*
|
||||
* bindsym n nop lower-case n pressed
|
||||
* bindsym Shift+n nop upper-case n pressed
|
||||
*
|
||||
* Without reordering, the first binding’s event_state_mask of 0x0 would match
|
||||
* the actual event_stat_mask of 0x1 and hence trigger instead of the second
|
||||
* keybinding.
|
||||
*
|
||||
*/
|
||||
void reorder_bindings(void);
|
||||
|
||||
/**
|
||||
* Checks for duplicate key bindings (the same keycode or keysym is configured
|
||||
* more than once). If a duplicate binding is found, a message is printed to
|
||||
@ -74,3 +89,9 @@ void binding_free(Binding *bind);
|
||||
*
|
||||
*/
|
||||
CommandResult *run_binding(Binding *bind, Con *con);
|
||||
|
||||
/**
|
||||
* Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
|
||||
*
|
||||
*/
|
||||
bool load_keymap(void);
|
||||
|
@ -12,10 +12,10 @@
|
||||
#include "config_parser.h"
|
||||
|
||||
/**
|
||||
* A utility function to convert a string of modifiers to the corresponding bit
|
||||
* mask.
|
||||
* A utility function to convert a string containing the group and modifiers to
|
||||
* the corresponding bit mask.
|
||||
*/
|
||||
uint32_t modifiers_from_str(const char *str);
|
||||
i3_event_state_mask_t event_state_from_str(const char *str);
|
||||
|
||||
/** The beginning of the prototype for every cfg_ function. */
|
||||
#define I3_CFG Match *current_match, struct ConfigResultIR *result
|
||||
|
@ -74,18 +74,6 @@ typedef enum { ADJ_NONE = 0,
|
||||
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
|
||||
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
|
||||
|
||||
enum {
|
||||
BIND_NONE = 0,
|
||||
BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */
|
||||
BIND_CONTROL = XCB_MOD_MASK_CONTROL, /* (1 << 2) */
|
||||
BIND_MOD1 = XCB_MOD_MASK_1, /* (1 << 3) */
|
||||
BIND_MOD2 = XCB_MOD_MASK_2, /* (1 << 4) */
|
||||
BIND_MOD3 = XCB_MOD_MASK_3, /* (1 << 5) */
|
||||
BIND_MOD4 = XCB_MOD_MASK_4, /* (1 << 6) */
|
||||
BIND_MOD5 = XCB_MOD_MASK_5, /* (1 << 7) */
|
||||
BIND_MODE_SWITCH = (1 << 8)
|
||||
};
|
||||
|
||||
/**
|
||||
* Container layouts. See Con::layout.
|
||||
*/
|
||||
@ -107,6 +95,25 @@ typedef enum {
|
||||
B_MOUSE = 1
|
||||
} input_type_t;
|
||||
|
||||
/**
|
||||
* Bitmask for matching XCB_XKB_GROUP_1 to XCB_XKB_GROUP_4.
|
||||
*/
|
||||
typedef enum {
|
||||
I3_XKB_GROUP_MASK_ANY = 0,
|
||||
I3_XKB_GROUP_MASK_1 = (1 << 0),
|
||||
I3_XKB_GROUP_MASK_2 = (1 << 1),
|
||||
I3_XKB_GROUP_MASK_3 = (1 << 2),
|
||||
I3_XKB_GROUP_MASK_4 = (1 << 3)
|
||||
} i3_xkb_group_mask_t;
|
||||
|
||||
/**
|
||||
* The lower 16 bits contain a xcb_key_but_mask_t, the higher 16 bits contain
|
||||
* an i3_xkb_group_mask_t. This type is necessary for the fallback logic to
|
||||
* work when handling XKB groups (see ticket #1775) and makes the code which
|
||||
* locates keybindings upon KeyPress/KeyRelease events simpler.
|
||||
*/
|
||||
typedef uint32_t i3_event_state_mask_t;
|
||||
|
||||
/**
|
||||
* Mouse pointer warping modes.
|
||||
*/
|
||||
@ -269,8 +276,10 @@ struct Binding {
|
||||
/** Keycode to bind */
|
||||
uint32_t keycode;
|
||||
|
||||
/** Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */
|
||||
uint32_t mods;
|
||||
/** Bitmask which is applied against event->state for KeyPress and
|
||||
* KeyRelease events to determine whether this binding applies to the
|
||||
* current state. */
|
||||
i3_event_state_mask_t event_state_mask;
|
||||
|
||||
/** Symbol the user specified in configfile, if any. This needs to be
|
||||
* stored with the binding to be able to re-convert it into a keycode
|
||||
|
Reference in New Issue
Block a user