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));
enum Binding::@12 release
If true, the binding should be executed upon a KeyRelease event, not a KeyPress (the default)...
bool border
If this is true for a mouse binding, the binding should be executed when the button is pressed over t...
#define SLIST_FOREACH(var, head, field)
bool parse_long(const char *str, long *out, int base)
Converts a string into a long using strtol().
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.
static struct xkb_keymap * xkb_keymap
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
#define TAILQ_EMPTY(head)
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...
xcb_connection_t * conn
XCB connection and root screen.
static Binding * get_binding(i3_event_state_mask_t state_filtered, bool is_release, uint16_t input_code, input_type_t input_type)
void switch_mode(const char *new_mode)
Switches the key bindings to the given mode, if the mode exists.
void ipc_send_binding_event(const char *event_type, Binding *bind)
For the binding events, we send the serialized binding struct.
Stores a resolved keycode (from a keysym), including the modifier mask.
const char * DEFAULT_BINDING_MODE
The name of the default mode.
static struct Mode * mode_from_name(const char *name, bool pango_markup)
struct bindings_head * bindings
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...
static struct xkb_context * xkb_context
CommandResult * run_binding(Binding *bind, Con *con)
Runs the given binding and handles parse errors.
pid_t command_error_nagbar_pid
char * symbol
Symbol the user specified in configfile, if any.
#define TAILQ_REMOVE(head, elm, field)
struct xkb_state * xkb_state_numlock
void grab_all_keys(xcb_connection_t *conn)
Grab the bound keys (tell X to send us keypress events for those keycodes)
A 'Con' represents everything from the X11 root window down to a single X11 window.
#define TAILQ_FIRST(head)
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...
char * pattern
The pattern/name used to load the font.
void reorder_bindings(void)
Reorders bindings by event_state_mask descendingly so that get_binding() correctly matches more speci...
void check_for_duplicate_bindings(struct context *context)
Checks for duplicate key bindings (the same keycode or keysym is configured more than once)...
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
bool exclude_titlebar
If this is true for a mouse binding, the binding should only be executed if the button press was not ...
void start_config_error_nagbar(const char *configpath, bool has_errors)
Launch nagbar to indicate errors in the configuration file.
char * current_configpath
static int reorder_binding_cmp(const void *a, const void *b)
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
static bool binding_in_current_group(const Binding *bind)
static char * current_mode
struct xkb_state * xkb_state
void binding_free(Binding *bind)
Frees the binding.
unsigned int xcb_numlock_mask
static Binding * binding_copy(Binding *bind)
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...
bool load_keymap(void)
Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
struct all_cons_head all_cons
static int fill_rmlvo_from_root(struct xkb_rule_names *xkb_names)
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...
bool whole_window
If this is true for a mouse binding, the binding should be executed when the button is pressed over a...
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...
The configuration file can contain multiple sets of bindings.
struct xkb_state * xkb_state_no_shift
struct xkb_state * xkb_state_numlock_no_shift
Used during the config file lexing/parsing to keep the state of the lexer in order to provide useful ...
void translate_keysyms(void)
Translates keysymbols to keycodes for all bindings which use keysyms.
#define ADD_TRANSLATED_KEY(code, mods)
i3_event_state_mask_t modifiers
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...
#define SLIST_INSERT_HEAD(head, elm, field)
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.
int * bindings_get_buttons_to_grab(void)
Returns a list of buttons that should be grabbed on a window.
void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, int *buttons)
Grab the specified buttons on a window when managing it.
char * command
Command, like in command mode.
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...
keycodes_head
Only in use if symbol != NULL.
static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
#define GRAB_KEY(modifier)
#define TAILQ_INSERT_TAIL(head, elm, field)
#define TAILQ_FOREACH(var, head, field)
struct bindings_head * bindings
input_type_t
Binding input types.
void start_nagbar(pid_t *nagbar_pid, char *argv[])
Starts an i3-nagbar instance with the given parameters.
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.