diff --git a/config.c b/config.c index 77b74227..7e1f8939 100644 --- a/config.c +++ b/config.c @@ -12,6 +12,7 @@ #include #include +#include #include #define LOG_MODULE "config" @@ -448,8 +449,9 @@ parse_section_csd(const char *key, const char *value, struct config *conf, } static bool -parse_section_key_bindings(const char *key, const char *value, struct config *conf, - const char *path, unsigned lineno) +parse_section_key_bindings( + const char *key, const char *value, struct config *conf, + const char *path, unsigned lineno) { for (enum binding_action action = 0; action < BIND_ACTION_COUNT; action++) { if (strcmp(key, binding_action_map[action]) != 0) @@ -459,7 +461,8 @@ parse_section_key_bindings(const char *key, const char *value, struct config *co struct xkb_keymap *keymap = xkb_keymap_new_from_names( ctx, &(struct xkb_rule_names){0}, XKB_KEYMAP_COMPILE_NO_FLAGS); - bool valid_combo = input_parse_key_binding_for_action(keymap, action, value, NULL); + bool valid_combo = input_parse_key_binding_for_action( + keymap, action, value, NULL); xkb_keymap_unref(keymap); xkb_context_unref(ctx); @@ -479,6 +482,43 @@ parse_section_key_bindings(const char *key, const char *value, struct config *co } +static bool +parse_section_mouse_bindings( + const char *key, const char *value, struct config *conf, + const char *path, unsigned lineno) +{ + for (enum binding_action action = 0; action < BIND_ACTION_COUNT; action++) { + if (strcmp(key, binding_action_map[action]) != 0) + continue; + + const char *map[] = { + [BTN_LEFT] = "BTN_LEFT", + [BTN_RIGHT] = "BTN_RIGHT", + [BTN_MIDDLE] = "BTN_MIDDLE", + [BTN_SIDE] = "BTN_SIDE", + [BTN_EXTRA] = "BTN_EXTRA", + [BTN_FORWARD] = "BTN_FORWARD", + [BTN_BACK] = "BTN_BACK", + [BTN_TASK] = "BTN_TASK", + }; + + for (size_t i = 0; i < ALEN(map); i++) { + if (map[i] == NULL || strcmp(map[i], value) != 0) + continue; + + conf->bindings.mouse[action] = (struct mouse_binding){i, 1, action}; + return true; + } + + LOG_ERR("%s:%d: invalid mouse button: %s", path, lineno, value); + return false; + + } + + LOG_ERR("%s:%u: invalid key: %s", path, lineno, key); + return false; +} + static bool parse_config_file(FILE *f, struct config *conf, const char *path) { @@ -488,6 +528,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path) SECTION_CURSOR, SECTION_CSD, SECTION_KEY_BINDINGS, + SECTION_MOUSE_BINDINGS, SECTION_COUNT, } section = SECTION_MAIN; @@ -500,11 +541,12 @@ parse_config_file(FILE *f, struct config *conf, const char *path) parser_fun_t fun; const char *name; } section_info[] = { - [SECTION_MAIN] = {&parse_section_main, "main"}, - [SECTION_COLORS] = {&parse_section_colors, "colors"}, - [SECTION_CURSOR] = {&parse_section_cursor, "cursor"}, - [SECTION_CSD] = {&parse_section_csd, "csd"}, - [SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"}, + [SECTION_MAIN] = {&parse_section_main, "main"}, + [SECTION_COLORS] = {&parse_section_colors, "colors"}, + [SECTION_CURSOR] = {&parse_section_cursor, "cursor"}, + [SECTION_CSD] = {&parse_section_csd, "csd"}, + [SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"}, + [SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"}, }; static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch"); @@ -699,7 +741,7 @@ config_load(struct config *conf, const char *conf_path) [BIND_ACTION_SPAWN_TERMINAL] = strdup("Control+Shift+Return"), }, .mouse = { - [BIND_ACTION_PRIMARY_PASTE] = strdup("BTN_MIDDLE"), + [BIND_ACTION_PRIMARY_PASTE] = {BTN_MIDDLE, 1, BIND_ACTION_PRIMARY_PASTE}, }, .search = { }, @@ -760,7 +802,6 @@ config_free(struct config conf) for (size_t i = 0; i < BIND_ACTION_COUNT; i++) { free(conf.bindings.key[i]); - free(conf.bindings.mouse[i]); free(conf.bindings.search[i]); } } diff --git a/config.h b/config.h index af078b00..bf205305 100644 --- a/config.h +++ b/config.h @@ -39,7 +39,7 @@ struct config { struct { /* Bindings for "normal" mode */ char *key[BIND_ACTION_COUNT]; - char *mouse[BIND_ACTION_COUNT]; + struct mouse_binding mouse[BIND_ACTION_COUNT]; /* * Special modes diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 5c8becdf..dbbcc52d 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -203,6 +203,18 @@ Note that _Alt_ is usually called *Mod1*. *fullscreen* Toggles the fullscreen state. Default: _not bound_. +# SECTION: mouse-bindings + +This section lets you override the default mouse bindings. + +The general format is *action*=*BTN\_*, where *BTN\_* is +the name of the event code (e.g. *BTN\_LEFT*, *BTN\_RIGHT*). You can +find the event names using *libinput debug-events*. + +*primary-paste* + Pastes from the _primary selection_. Default: _BTN_MIDDLE_. + + # FONT FORMAT The font is specified in FontConfig syntax. That is, a colon-separated diff --git a/footrc b/footrc index 468bc67d..73512474 100644 --- a/footrc +++ b/footrc @@ -56,3 +56,6 @@ # # minimize= # # maximize= # # fullscreen= + +[mouse-bindings] +# primary-paste=BTN_MIDDLE diff --git a/input.c b/input.c index 399d9d26..8fbb02cd 100644 --- a/input.c +++ b/input.c @@ -31,6 +31,8 @@ #include "terminal.h" #include "vt.h" +#define ALEN(v) (sizeof(v) / sizeof(v[0])) + void input_execute_binding(struct terminal *term, enum binding_action action, uint32_t serial) @@ -54,8 +56,7 @@ input_execute_binding(struct terminal *term, enum binding_action action, break; case BIND_ACTION_PRIMARY_PASTE: - LOG_ERR("unimplemented"); - assert(false); + selection_from_primary(term); break; case BIND_ACTION_SEARCH_START: @@ -321,7 +322,6 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, static const struct key_data * keymap_data_for_sym(xkb_keysym_t sym, size_t *count) { -#define ALEN(a) (sizeof(a) / sizeof(a[0])) switch (sym) { case XKB_KEY_Escape: *count = ALEN(key_escape); return key_escape; case XKB_KEY_Return: *count = ALEN(key_return); return key_return; @@ -402,7 +402,6 @@ keymap_data_for_sym(xkb_keysym_t sym, size_t *count) case XKB_KEY_KP_9: *count = ALEN(key_kp_9); return key_kp_9; } - #undef ALEN return NULL; } @@ -1151,11 +1150,24 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, selection_mark_row(term, wayl->mouse.row, serial); break; } - } else { - if (wayl->mouse.count == 1 && button == BTN_MIDDLE && - selection_enabled(term)) - { - selection_from_primary(term); + } + + else { + for (size_t i = 0; i < ALEN(wayl->conf->bindings.mouse); i++) { + const struct mouse_binding *binding = + &wayl->conf->bindings.mouse[i]; + + if (binding->button != button) { + /* Wrong button */ + continue; + } + + if (binding->count != wayl->mouse.count) { + /* Not correct click count */ + continue; + } + + input_execute_binding(term, binding->action, serial); } selection_cancel(term); } diff --git a/wayland.h b/wayland.h index a824c21f..2ddfdf87 100644 --- a/wayland.h +++ b/wayland.h @@ -65,6 +65,12 @@ struct key_binding { }; typedef tll(struct key_binding) key_binding_list_t; +struct mouse_binding { + uint32_t button; + int count; + enum binding_action action; +}; + struct kbd { struct xkb_context *xkb; struct xkb_keymap *xkb_keymap;