diff --git a/config.c b/config.c index 46056567..0baa5eb5 100644 --- a/config.c +++ b/config.c @@ -619,6 +619,19 @@ config_load(struct config *conf, const char *conf_path) }, }, + .bindings = { + .key = { + [BIND_ACTION_CLIPBOARD_COPY] = strdup("Control+Shift+C"), + [BIND_ACTION_CLIPBOARD_PASTE] = strdup("Control+Shift+V"), + [BIND_ACTION_SEARCH_START] = strdup("Control+Shift+R"), + }, + .mouse = { + [BIND_ACTION_PRIMARY_PASTE] = strdup("BTN_MIDDLE"), + }, + .search = { + }, + }, + .csd = { .preferred = CONF_CSD_PREFER_SERVER, .title_height = 26, @@ -671,4 +684,10 @@ config_free(struct config conf) free(conf.shell); tll_free_and_free(conf.fonts, free); free(conf.server_socket_path); + + 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 a89637e1..af078b00 100644 --- a/config.h +++ b/config.h @@ -36,6 +36,20 @@ struct config { } color; } cursor; + struct { + /* Bindings for "normal" mode */ + char *key[BIND_ACTION_COUNT]; + char *mouse[BIND_ACTION_COUNT]; + + /* + * Special modes + */ + + /* While searching (not - action to *start* a search is in the + * 'key' bindings above */ + char *search[BIND_ACTION_COUNT]; + } bindings; + struct { enum { CONF_CSD_PREFER_SERVER, CONF_CSD_PREFER_CLIENT } preferred; diff --git a/input.c b/input.c index efa58c9d..0e6b3c92 100644 --- a/input.c +++ b/input.c @@ -31,6 +31,35 @@ #include "terminal.h" #include "vt.h" +static void +execute_binding(struct terminal *term, enum binding_action action, + uint32_t serial) +{ + switch (action) { + case BIND_ACTION_CLIPBOARD_COPY: + selection_to_clipboard(term, serial); + break; + + case BIND_ACTION_CLIPBOARD_PASTE: + selection_from_clipboard(term, serial); + term_reset_view(term); + break; + + case BIND_ACTION_PRIMARY_PASTE: + LOG_ERR("unimplemented"); + assert(false); + break; + + case BIND_ACTION_SEARCH_START: + search_begin(term); + break; + + case BIND_ACTION_COUNT: + assert(false); + break; + } +} + static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) @@ -59,6 +88,7 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, xkb_context_unref(wayl->kbd.xkb); wayl->kbd.xkb = NULL; } + tll_free(wayl->kbd.key_bindings); wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); wayl->kbd.xkb_keymap = xkb_keymap_new_from_string( @@ -81,6 +111,48 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, munmap(map_str, size); close(fd); + + for (size_t i = 0; i < BIND_ACTION_COUNT; i++) { + const char *combo = wayl->conf->bindings.key[i]; + + if (combo == NULL) + continue; + + xkb_mod_mask_t mod_mask = 0; + xkb_keysym_t sym = 0; + + char *copy = strdup(combo); + + for (char *p = strtok(copy, "+"), *n = strtok(NULL, "+"); + p != NULL; + p = n, n = strtok(NULL, "+")) + { + if (n != NULL) { + /* Modifier */ + xkb_mod_index_t mod = xkb_keymap_mod_get_index( + wayl->kbd.xkb_keymap, p); + + if (mod == -1) { + LOG_ERR("%s: not a valid modifier name", p); + continue; + } + + mod_mask |= 1 << mod; + LOG_DBG("MOD: %d - %s", mod, p); + } else { + /* Symbol */ + sym = xkb_keysym_from_name(p, 0); + } + } + + free(copy); + + assert(sym != 0); + + /* TODO: convert to DBG */ + LOG_INFO("binding: %s -> mods=0x%08x, sym=%d", combo, mod_mask, sym); + tll_push_back(wayl->kbd.key_bindings, ((struct key_binding){mod_mask, sym, i})); + } } static void @@ -374,6 +446,13 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, "effective=0x%08x, repeats=%d", sym, mods, consumed, significant, effective_mods, should_repeat); + tll_foreach(wayl->kbd.key_bindings, it) { + if (it->item.mods == effective_mods && it->item.sym == sym) { + execute_binding(term, it->item.action, serial); + goto maybe_repeat; + } + } + /* * Builtin shortcuts */ @@ -408,23 +487,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, } else if (effective_mods == (shift | ctrl)) { - if (sym == XKB_KEY_C) { - selection_to_clipboard(term, serial); - goto maybe_repeat; - } - - else if (sym == XKB_KEY_V) { - selection_from_clipboard(term, serial); - term_reset_view(term); - goto maybe_repeat; - } - - else if (sym == XKB_KEY_R) { - search_begin(term); - goto maybe_repeat; - } - - else if (sym == XKB_KEY_Return) { + if (sym == XKB_KEY_Return) { term_spawn_new(term); goto maybe_repeat; } diff --git a/wayland.c b/wayland.c index 4bf77562..c508c710 100644 --- a/wayland.c +++ b/wayland.c @@ -911,6 +911,7 @@ wayl_destroy(struct wayland *wayl) if (wayl->presentation != NULL) wp_presentation_destroy(wayl->presentation); + tll_free(wayl->kbd.key_bindings); if (wayl->kbd.xkb_compose_state != NULL) xkb_compose_state_unref(wayl->kbd.xkb_compose_state); if (wayl->kbd.xkb_compose_table != NULL) diff --git a/wayland.h b/wayland.h index ed53591d..775aee0f 100644 --- a/wayland.h +++ b/wayland.h @@ -41,6 +41,20 @@ struct monitor { float inch; /* e.g. 24" */ }; +enum binding_action { + BIND_ACTION_CLIPBOARD_COPY, + BIND_ACTION_CLIPBOARD_PASTE, + BIND_ACTION_PRIMARY_PASTE, + BIND_ACTION_SEARCH_START, + BIND_ACTION_COUNT, +}; + +struct key_binding { + xkb_mod_mask_t mods; + xkb_keysym_t sym; + enum binding_action action; +}; + struct kbd { struct xkb_context *xkb; struct xkb_keymap *xkb_keymap; @@ -66,6 +80,8 @@ struct kbd { bool alt; bool ctrl; bool meta; + + tll(struct key_binding) key_bindings; }; struct wl_clipboard {