11 #include <xkbcommon/xkbcommon.h> 12 #include <xkbcommon/xkbcommon-x11.h> 58 const char *release,
const char *border,
const char *whole_window,
59 const char *exclude_titlebar,
const char *command,
const char *modename,
62 DLOG(
"Binding %p bindtype %s, modifiers %s, input code %s, release %s\n", new_binding, bindtype, modifiers, input_code, release);
63 new_binding->
release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
64 new_binding->
border = (border != NULL);
67 if (strcmp(bindtype,
"bindsym") == 0) {
68 new_binding->
input_type = (strncasecmp(input_code,
"button", (
sizeof(
"button") - 1)) == 0
76 ELOG(
"Could not parse \"%s\" as an input code, ignoring this binding.\n", input_code);
86 int group_bits_set = 0;
95 if (group_bits_set > 1)
96 ELOG(
"Keybinding has more than one Group specified, but your X server is always in precisely one group. The keybinding can never trigger.\n");
111 case XCB_XKB_GROUP_1:
113 case XCB_XKB_GROUP_2:
115 case XCB_XKB_GROUP_3:
117 case XCB_XKB_GROUP_4:
120 ELOG(
"BUG: xkb_current_group (= %d) outside of [XCB_XKB_GROUP_1..XCB_XKB_GROUP_4]\n",
xkb_current_group);
127 #define GRAB_KEY(modifier) \ 129 xcb_grab_key(conn, 0, root, modifier, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \ 132 DLOG(
"Binding %p Grabbing keycode %d with event state mask 0x%x (mods 0x%x)\n",
165 const int mods = (binding_keycode->
modifiers & 0xFFFF);
166 DLOG(
"Binding %p Grabbing keycode %d with mods %d\n", bind,
keycode, mods);
167 xcb_grab_key(
conn, 0,
root, mods,
keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
179 xcb_grab_server(
conn);
186 xcb_ungrab_button(
conn, XCB_BUTTON_INDEX_ANY, con->
window->
id, XCB_BUTTON_MASK_ANY);
191 xcb_ungrab_server(
conn);
209 if (bind->
release == B_UPON_KEYRELEASE_IGNORE_MODS)
210 bind->
release = B_UPON_KEYRELEASE;
214 const uint32_t xkb_group_state = (state_filtered & 0xFFFF0000);
215 const uint32_t modifiers_state = (state_filtered & 0x0000FFFF);
222 const bool groups_match = ((xkb_group_state & xkb_group_mask) == xkb_group_mask);
224 DLOG(
"skipping binding %p because XKB groups do not match\n", bind);
231 bool found_keycode =
false;
233 xcb_keycode_t input_keycode = (xcb_keycode_t)input_code;
236 const uint32_t modifiers_mask = (binding_keycode->
modifiers & 0x0000FFFF);
237 const bool mods_match = (modifiers_mask == modifiers_state);
238 DLOG(
"binding_keycode->modifiers = %d, modifiers_mask = %d, modifiers_state = %d, mods_match = %s\n",
239 binding_keycode->
modifiers, modifiers_mask, modifiers_state, (mods_match ?
"yes" :
"no"));
240 if (binding_keycode->
keycode == input_keycode &&
241 (mods_match || (bind->
release == B_UPON_KEYRELEASE_IGNORE_MODS && is_release))) {
242 found_keycode =
true;
248 if (bind->
keycode != input_code) {
254 const uint32_t modifiers_mask = (binding_keycode->
modifiers & 0x0000FFFF);
255 const bool mods_match = (modifiers_mask == modifiers_state);
256 DLOG(
"binding_keycode->modifiers = %d, modifiers_mask = %d, modifiers_state = %d, mods_match = %s\n",
257 binding_keycode->
modifiers, modifiers_mask, modifiers_state, (mods_match ?
"yes" :
"no"));
258 if (mods_match || (bind->
release == B_UPON_KEYRELEASE_IGNORE_MODS && is_release)) {
259 found_keycode =
true;
264 if (!found_keycode) {
272 if (bind->
release == B_UPON_KEYRELEASE && !is_release) {
273 bind->
release = B_UPON_KEYRELEASE_IGNORE_MODS;
274 DLOG(
"marked bind %p as B_UPON_KEYRELEASE_IGNORE_MODS\n", bind);
282 if ((bind->
release == B_UPON_KEYPRESS && is_release)) {
288 }
else if (!result) {
303 const bool is_release = (
event->response_type == XCB_KEY_RELEASE ||
304 event->response_type == XCB_BUTTON_RELEASE);
306 const input_type_t input_type = ((
event->response_type == XCB_BUTTON_RELEASE ||
307 event->response_type == XCB_BUTTON_PRESS)
311 const uint16_t event_state = ((xcb_key_press_event_t *)event)->state;
312 const uint16_t event_detail = ((xcb_key_press_event_t *)event)->detail;
316 DLOG(
"(removed capslock, state = 0x%x)\n", state_filtered);
326 switch ((event_state & 0x6000) >> 13) {
327 case XCB_XKB_GROUP_1:
330 case XCB_XKB_GROUP_2:
333 case XCB_XKB_GROUP_3:
336 case XCB_XKB_GROUP_4:
340 state_filtered &= ~0x6000;
341 DLOG(
"(transformed keyboard group, state = 0x%x)\n", state_filtered);
342 return get_binding(state_filtered, is_release, event_detail, input_type);
365 #define ADD_TRANSLATED_KEY(code, mods) \ 367 struct Binding_Keycode *binding_keycode = smalloc(sizeof(struct Binding_Keycode)); \ 368 binding_keycode->modifiers = (mods); \ 369 binding_keycode->keycode = (code); \ 370 TAILQ_INSERT_TAIL(&(bind->keycodes_head), binding_keycode, keycodes); \ 380 const struct resolve *resolving = data;
382 xkb_keysym_t sym = xkb_state_key_get_one_sym(resolving->
xkb_state, key);
383 if (sym != resolving->
keysym) {
386 const xkb_layout_index_t layout = xkb_state_key_get_layout(resolving->
xkb_state, key);
387 if (layout == XKB_LAYOUT_INVALID)
389 if (xkb_state_key_get_level(resolving->
xkb_state, key, layout) > 1)
393 if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_Equal)
397 if (sym != resolving->
keysym)
414 xkb_keysym_t sym_numlock = xkb_state_key_get_one_sym(numlock_state, key);
415 if (sym_numlock == resolving->
keysym) {
422 DLOG(
"Skipping automatic numlock fallback, key %d resolves to 0x%x with numlock\n",
433 struct xkb_state *dummy_state = NULL;
434 struct xkb_state *dummy_state_no_shift = NULL;
435 struct xkb_state *dummy_state_numlock = NULL;
436 struct xkb_state *dummy_state_numlock_no_shift = NULL;
437 bool has_errors =
false;
439 if ((dummy_state = xkb_state_new(
xkb_keymap)) == NULL ||
440 (dummy_state_no_shift = xkb_state_new(
xkb_keymap)) == NULL ||
441 (dummy_state_numlock = xkb_state_new(
xkb_keymap)) == NULL ||
442 (dummy_state_numlock_no_shift = xkb_state_new(
xkb_keymap)) == NULL) {
443 ELOG(
"Could not create XKB state, cannot translate keysyms.\n");
452 ELOG(
"Could not translate string to button: \"%s\"\n", bind->
symbol);
455 xcb_keycode_t key = button;
457 DLOG(
"Binding Mouse button, Keycode = %d\n", key);
460 xkb_layout_index_t group = XCB_XKB_GROUP_1;
462 group = XCB_XKB_GROUP_2;
464 group = XCB_XKB_GROUP_3;
466 group = XCB_XKB_GROUP_4;
468 DLOG(
"Binding %p group = %d, event_state_mask = %d, &2 = %s, &3 = %s, &4 = %s\n",
475 (void)xkb_state_update_mask(
484 (void)xkb_state_update_mask(
485 dummy_state_no_shift,
493 (void)xkb_state_update_mask(
502 (void)xkb_state_update_mask(
503 dummy_state_numlock_no_shift,
532 xkb_keysym_t sym = xkb_state_key_get_one_sym(dummy_state, bind->
keycode);
533 xkb_keysym_t sym_numlock = xkb_state_key_get_one_sym(dummy_state_numlock, bind->
keycode);
534 if (sym == sym_numlock) {
541 DLOG(
"Skipping automatic numlock fallback, key %d resolves to 0x%x with numlock\n",
550 const xkb_keysym_t keysym = xkb_keysym_from_name(bind->
symbol, XKB_KEYSYM_NO_FLAGS);
551 if (keysym == XKB_KEY_NoSymbol) {
552 ELOG(
"Could not translate string to key symbol: \"%s\"\n",
560 .xkb_state = dummy_state,
561 .xkb_state_no_shift = dummy_state_no_shift,
562 .xkb_state_numlock = dummy_state_numlock,
563 .xkb_state_numlock_no_shift = dummy_state_numlock_no_shift,
572 int num_keycodes = 0;
586 if (check->
symbol != NULL)
596 DLOG(
"state=0x%x, cfg=\"%s\", sym=0x%x → keycodes%s (%d)\n",
602 xkb_state_unref(dummy_state);
603 xkb_state_unref(dummy_state_no_shift);
604 xkb_state_unref(dummy_state_numlock);
605 xkb_state_unref(dummy_state_numlock_no_shift);
612 #undef ADD_TRANSLATED_KEY 621 DLOG(
"Switching to mode %s\n", new_mode);
624 if (strcmp(mode->
name, new_mode) != 0)
636 if (bind->
release == B_UPON_KEYRELEASE_IGNORE_MODS)
637 bind->
release = B_UPON_KEYRELEASE;
641 sasprintf(&event_msg,
"{\"change\":\"%s\", \"pango_markup\":%s}",
650 ELOG(
"Mode not found\n");
680 struct bindings_head *reordered =
scalloc(1,
sizeof(
struct bindings_head));
682 for (
int i = 0; i < n; i++) {
739 if ((bind->
symbol == NULL && current->
symbol != NULL) ||
745 if (bind->
symbol != NULL &&
758 ELOG(
"Duplicate keybinding in config file:\n state mask 0x%x with keycode %d, command \"%s\"\n",
761 ELOG(
"Duplicate keybinding in config file:\n state mask 0x%x with keysym %s, command \"%s\"\n",
782 *ret_binding_keycode = *binding_keycode;
843 "The configured command for this shortcut could not be run successfully.",
859 xcb_intern_atom_reply_t *atom_reply;
860 size_t content_max_words = 256;
862 atom_reply = xcb_intern_atom_reply(
863 conn, xcb_intern_atom(
conn, 0, strlen(
"_XKB_RULES_NAMES"),
"_XKB_RULES_NAMES"), NULL);
864 if (atom_reply == NULL)
867 xcb_get_property_cookie_t prop_cookie;
868 xcb_get_property_reply_t *prop_reply;
869 prop_cookie = xcb_get_property_unchecked(
conn,
false,
root, atom_reply->atom,
870 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
871 prop_reply = xcb_get_property_reply(
conn, prop_cookie, NULL);
872 if (prop_reply == NULL) {
876 if (xcb_get_property_value_length(prop_reply) > 0 && prop_reply->bytes_after > 0) {
879 content_max_words += ceil(prop_reply->bytes_after / 4.0);
882 prop_cookie = xcb_get_property_unchecked(
conn,
false,
root, atom_reply->atom,
883 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
884 prop_reply = xcb_get_property_reply(
conn, prop_cookie, NULL);
885 if (prop_reply == NULL) {
890 if (xcb_get_property_value_length(prop_reply) == 0) {
896 const char *walk = (
const char *)xcb_get_property_value(prop_reply);
897 int remaining = xcb_get_property_value_length(prop_reply);
898 for (
int i = 0; i < 5 && remaining > 0; i++) {
899 const int len = strnlen(walk, remaining);
902 sasprintf((
char **)&(xkb_names->rules),
"%.*s", len, walk);
905 sasprintf((
char **)&(xkb_names->model),
"%.*s", len, walk);
908 sasprintf((
char **)&(xkb_names->layout),
"%.*s", len, walk);
911 sasprintf((
char **)&(xkb_names->variant),
"%.*s", len, walk);
914 sasprintf((
char **)&(xkb_names->options),
"%.*s", len, walk);
917 DLOG(
"component %d of _XKB_RULES_NAMES is \"%.*s\"\n", i, len, walk);
919 remaining -= (len + 1);
934 ELOG(
"Could not create xkbcommon context\n");
941 if (
xkb_supported && (device_id = xkb_x11_get_core_keyboard_device_id(
conn)) > -1) {
942 if ((new_keymap = xkb_x11_keymap_new_from_device(
xkb_context,
conn, device_id, 0)) == NULL) {
943 ELOG(
"xkb_x11_keymap_new_from_device failed\n");
949 LOG(
"No XKB / core keyboard device? Assembling keymap from local RMLVO.\n");
950 struct xkb_rule_names names = {
957 ELOG(
"Could not get _XKB_RULES_NAMES atom from root window, falling back to defaults.\n");
960 new_keymap = xkb_keymap_new_from_names(
xkb_context, &names, 0);
961 free((
char *)names.rules);
962 free((
char *)names.model);
963 free((
char *)names.layout);
964 free((
char *)names.variant);
965 free((
char *)names.options);
966 if (new_keymap == NULL) {
967 ELOG(
"xkb_keymap_new_from_names failed\n");
999 if (num + 1 == num_max)
1008 ELOG(
"Could not parse button number, skipping this binding. Please report this bug in i3.\n");
1013 for (
int i = 0; i < num; i++) {
1014 if (buffer[i] == button)
1018 buffer[num++] = button;
1022 int *buttons =
scalloc(num,
sizeof(
int));
1023 memcpy(buttons, buffer, num *
sizeof(
int));
unsigned int xcb_numlock_mask
#define TAILQ_FOREACH(var, head, field)
Stores a resolved keycode (from a keysym), including the modifier mask.
static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode)
CommandResult * parse_command(const char *input, yajl_gen gen, ipc_client *client)
Parses and executes the given command.
bool exclude_titlebar
If this is true for a mouse binding, the binding should only be executed if the button press was not ...
void ipc_send_event(const char *event, uint32_t message_type, const char *payload)
Sends the specified event to all IPC clients which are currently connected and subscribed to this kin...
#define SLIST_FOREACH(var, head, field)
static struct xkb_keymap * xkb_keymap
static Binding * get_binding(i3_event_state_mask_t state_filtered, bool is_release, uint16_t input_code, input_type_t input_type)
struct bindings_head * bindings
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
void switch_mode(const char *new_mode)
Switches the key bindings to the given mode, if the mode exists.
struct bindings_head * bindings
const char * DEFAULT_BINDING_MODE
The name of the default mode.
static struct Mode * mode_from_name(const char *name, bool pango_markup)
A 'Con' represents everything from the X11 root window down to a single X11 window.
keycodes_head
Only in use if symbol != NULL.
input_type_t
Binding input types.
static void reorder_bindings_of_mode(struct Mode *mode)
Binding * get_binding_from_xcb_event(xcb_generic_event_t *event)
Returns a pointer to the Binding that matches the given xcb event or NULL if no such binding exists...
i3_event_state_mask_t modifiers
static struct xkb_context * xkb_context
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
CommandResult * run_binding(Binding *bind, Con *con)
Runs the given binding and handles parse errors.
pid_t command_error_nagbar_pid
bool whole_window
If this is true for a mouse binding, the binding should be executed when the button is pressed over a...
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
enum Binding::@12 release
If true, the binding should be executed upon a KeyRelease event, not a KeyPress (the default)...
struct xkb_state * xkb_state
void grab_all_keys(xcb_connection_t *conn)
Grab the bound keys (tell X to send us keypress events for those keycodes)
xcb_connection_t * conn
XCB connection and root screen.
#define TAILQ_EMPTY(head)
void start_nagbar(pid_t *nagbar_pid, char *argv[])
Starts an i3-nagbar instance with the given parameters.
void reorder_bindings(void)
Reorders bindings by event_state_mask descendingly so that get_binding() correctly matches more speci...
struct xkb_state * xkb_state_no_shift
void check_for_duplicate_bindings(struct context *context)
Checks for duplicate key bindings (the same keycode or keysym is configured more than once)...
void ipc_send_binding_event(const char *event_type, Binding *bind)
For the binding events, we send the serialized binding struct.
bool parse_long(const char *str, long *out, int base)
Converts a string into a long using strtol().
char * command
Command, like in command mode.
void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, int *buttons)
Grab the specified buttons on a window when managing it.
void start_config_error_nagbar(const char *configpath, bool has_errors)
Launch nagbar to indicate errors in the configuration file.
#define SLIST_INSERT_HEAD(head, elm, field)
char * current_configpath
static int reorder_binding_cmp(const void *a, const void *b)
static bool binding_in_current_group(const Binding *bind)
static char * current_mode
void binding_free(Binding *bind)
Frees the binding.
static Binding * binding_copy(Binding *bind)
i3_event_state_mask_t event_state_mask
Bitmask which is applied against event->state for KeyPress and KeyRelease events to determine whether...
uint32_t keycode
Keycode to bind.
bool load_keymap(void)
Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
static int fill_rmlvo_from_root(struct xkb_rule_names *xkb_names)
bool border
If this is true for a mouse binding, the binding should be executed when the button is pressed over t...
i3_event_state_mask_t event_state_from_str(const char *str)
A utility function to convert a string containing the group and modifiers to the corresponding bit ma...
char * symbol
Symbol the user specified in configfile, if any.
void * scalloc(size_t num, size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
struct xkb_state * xkb_state_numlock_no_shift
#define TAILQ_INSERT_TAIL(head, elm, field)
void translate_keysyms(void)
Translates keysymbols to keycodes for all bindings which use keysyms.
#define ADD_TRANSLATED_KEY(code, mods)
A struct that contains useful information about the result of a command as a whole (e...
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
void regrab_all_buttons(xcb_connection_t *conn)
Release the button grabs on all managed windows and regrab them, reevaluating which buttons need to b...
int * bindings_get_buttons_to_grab(void)
Returns a list of buttons that should be grabbed on a window.
struct all_cons_head all_cons
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
The configuration file can contain multiple sets of bindings.
Used during the config file lexing/parsing to keep the state of the lexer in order to provide useful ...
#define TAILQ_FIRST(head)
Binding * configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command, const char *modename, bool pango_markup)
Adds a binding from config parameters given as strings and returns a pointer to the binding structure...
void ungrab_all_keys(xcb_connection_t *conn)
Ungrabs all keys, to be called before re-grabbing the keys because of a mapping_notify event or a con...
struct xkb_state * xkb_state_numlock
static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
#define TAILQ_REMOVE(head, elm, field)
#define GRAB_KEY(modifier)
uint32_t i3_event_state_mask_t
The lower 16 bits contain a xcb_key_but_mask_t, the higher 16 bits contain an i3_xkb_group_mask_t.
char * pattern
The pattern/name used to load the font.