Merge branch 'bindings'

This commit is contained in:
Daniel Eklöf 2020-03-12 17:26:02 +01:00
commit 47fa91e02b
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
13 changed files with 560 additions and 87 deletions

View file

@ -2,6 +2,8 @@
## Unreleased
### Added
* User configurable key- and mouse bindings. See `man 5 foot` and the
example `footrc` (https://codeberg.org/dnkl/foot/issues/1)
### Changed
* Changed icon name in `foot.desktop` and `foot-server.desktop` from

231
config.c
View file

@ -12,9 +12,16 @@
#include <assert.h>
#include <errno.h>
#include <linux/input-event-codes.h>
#include <xkbcommon/xkbcommon.h>
#define LOG_MODULE "config"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "input.h"
#include "wayland.h"
#define ALEN(v) (sizeof(v) / sizeof(v[0]))
static const uint32_t default_foreground = 0xdcdccc;
static const uint32_t default_background = 0x111111;
@ -41,6 +48,26 @@ static const uint32_t default_bright[] = {
0xffffff,
};
static const char *binding_action_map[] = {
[BIND_ACTION_NONE] = NULL,
[BIND_ACTION_SCROLLBACK_UP] = "scrollback-up",
[BIND_ACTION_SCROLLBACK_DOWN] = "scrollback-down",
[BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy",
[BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste",
[BIND_ACTION_PRIMARY_PASTE] = "primary-paste",
[BIND_ACTION_SEARCH_START] = "search-start",
[BIND_ACTION_FONT_SIZE_UP] = "font-increase",
[BIND_ACTION_FONT_SIZE_DOWN] = "font-decrease",
[BIND_ACTION_FONT_SIZE_RESET] = "font-reset",
[BIND_ACTION_SPAWN_TERMINAL] = "spawn-terminal",
[BIND_ACTION_MINIMIZE] = "minimize",
[BIND_ACTION_MAXIMIZE] = "maximize",
[BIND_ACTION_FULLSCREEN] = "fullscreen",
};
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
"binding action map size mismatch");
static char *
get_shell(void)
{
@ -238,7 +265,7 @@ parse_section_main(const char *key, const char *value, struct config *conf,
}
else {
LOG_WARN("%s:%u: invalid key: %s", path, lineno, key);
LOG_ERR("%s:%u: invalid key: %s", path, lineno, key);
return false;
}
@ -420,13 +447,143 @@ parse_section_csd(const char *key, const char *value, struct config *conf,
}
else {
LOG_WARN("%s:%u: invalid key: %s", path, lineno, key);
LOG_ERR("%s:%u: invalid key: %s", path, lineno, key);
return false;
}
return true;
}
static bool
verify_key_combo(const struct config *conf, const char *combo, const char *path, unsigned lineno)
{
for (enum binding_action action = 0; action < BIND_ACTION_COUNT; action++) {
if (conf->bindings.key[action] == NULL)
continue;
char *copy = strdup(conf->bindings.key[action]);
for (char *save = NULL, *collision = strtok_r(copy, " ", &save);
collision != NULL;
collision = strtok_r(NULL, " ", &save))
{
if (strcmp(combo, collision) == 0) {
LOG_ERR("%s:%d: %s already mapped to %s", path, lineno, combo, binding_action_map[action]);
free(copy);
return false;
}
}
free(copy);
}
struct xkb_context *ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
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, BIND_ACTION_NONE, combo, NULL);
xkb_keymap_unref(keymap);
xkb_context_unref(ctx);
if (!valid_combo) {
LOG_ERR("%s:%d: invalid key combination: %s", path, lineno, combo);
return false;
}
return true;
}
static bool
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 (binding_action_map[action] == NULL)
continue;
if (strcmp(key, binding_action_map[action]) != 0)
continue;
if (strcmp(value, "NONE") == 0) {
free(conf->bindings.key[action]);
conf->bindings.key[action] = NULL;
return true;
}
if (!verify_key_combo(conf, value, path, lineno)) {
return false;
}
free(conf->bindings.key[action]);
conf->bindings.key[action] = strdup(value);
return true;
}
LOG_ERR("%s:%u: invalid key: %s", path, lineno, key);
return false;
}
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 (binding_action_map[action] == NULL)
continue;
if (strcmp(key, binding_action_map[action]) != 0)
continue;
if (strcmp(value, "NONE") == 0) {
conf->bindings.mouse[action] = (struct mouse_binding){0, 0, BIND_ACTION_NONE};
return true;
}
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;
const int count = 1;
/* Make sure button isn't already mapped to another action */
for (enum binding_action j = 0; j < BIND_ACTION_COUNT; j++) {
const struct mouse_binding *collision = &conf->bindings.mouse[j];
if (collision->button == i && collision->count == count) {
LOG_ERR("%s:%d: %s already mapped to %s", path, lineno,
value, binding_action_map[collision->action]);
return false;
}
}
conf->bindings.mouse[action] = (struct mouse_binding){i, count, 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)
{
@ -435,6 +592,9 @@ parse_config_file(FILE *f, struct config *conf, const char *path)
SECTION_COLORS,
SECTION_CURSOR,
SECTION_CSD,
SECTION_KEY_BINDINGS,
SECTION_MOUSE_BINDINGS,
SECTION_COUNT,
} section = SECTION_MAIN;
/* Function pointer, called for each key/value line */
@ -442,22 +602,19 @@ parse_config_file(FILE *f, struct config *conf, const char *path)
const char *key, const char *value, struct config *conf,
const char *path, unsigned lineno);
/* Maps sections to line parser functions */
static const parser_fun_t section_parser_map[] = {
[SECTION_MAIN] = &parse_section_main,
[SECTION_COLORS] = &parse_section_colors,
[SECTION_CURSOR] = &parse_section_cursor,
[SECTION_CSD] = &parse_section_csd,
static const struct {
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_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
};
#if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
static const char *const section_names[] = {
[SECTION_MAIN] = "main",
[SECTION_COLORS] = "colors",
[SECTION_CURSOR] = "cursor",
[SECTION_CSD] = "csd",
};
#endif
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
unsigned lineno = 0;
@ -505,17 +662,19 @@ parse_config_file(FILE *f, struct config *conf, const char *path)
*end = '\0';
if (strcmp(&line[1], "colors") == 0)
section = SECTION_COLORS;
else if (strcmp(&line[1], "cursor") == 0)
section = SECTION_CURSOR;
else if (strcmp(&line[1], "csd") == 0)
section = SECTION_CSD;
else {
section = SECTION_COUNT;
for (enum section i = 0; i < SECTION_COUNT; i++) {
if (strcmp(&line[1], section_info[i].name) == 0) {
section = i;
}
}
if (section == SECTION_COUNT) {
LOG_ERR("%s:%d: invalid section name: %s", path, lineno, &line[1]);
goto err;
}
/* Process next line */
continue;
}
@ -554,7 +713,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path)
LOG_DBG("section=%s, key='%s', value='%s'",
section_names[section], key, value);
parser_fun_t section_parser = section_parser_map[section];
parser_fun_t section_parser = section_info[section].fun;
assert(section_parser != NULL);
if (!section_parser(key, value, conf, path, lineno))
@ -634,6 +793,25 @@ config_load(struct config *conf, const char *conf_path)
},
},
.bindings = {
.key = {
[BIND_ACTION_SCROLLBACK_UP] = strdup("Shift+Page_Up"),
[BIND_ACTION_SCROLLBACK_DOWN] = strdup("Shift+Page_Down"),
[BIND_ACTION_CLIPBOARD_COPY] = strdup("Control+Shift+C"),
[BIND_ACTION_CLIPBOARD_PASTE] = strdup("Control+Shift+V"),
[BIND_ACTION_SEARCH_START] = strdup("Control+Shift+R"),
[BIND_ACTION_FONT_SIZE_UP] = strdup("Control+plus Control+equal Control+KP_Add"),
[BIND_ACTION_FONT_SIZE_DOWN] = strdup("Control+minus Control+KP_Subtract"),
[BIND_ACTION_FONT_SIZE_RESET] = strdup("Control+0 Control+KP_0"),
[BIND_ACTION_SPAWN_TERMINAL] = strdup("Control+Shift+Return"),
},
.mouse = {
[BIND_ACTION_PRIMARY_PASTE] = {BTN_MIDDLE, 1, BIND_ACTION_PRIMARY_PASTE},
},
.search = {
},
},
.csd = {
.preferred = CONF_CSD_PREFER_SERVER,
.title_height = 26,
@ -686,4 +864,9 @@ 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.search[i]);
}
}

View file

@ -36,6 +36,20 @@ struct config {
} color;
} cursor;
struct {
/* Bindings for "normal" mode */
char *key[BIND_ACTION_COUNT];
struct mouse_binding 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;

View file

@ -98,11 +98,13 @@ arguments, use *--*:
# KEYBOARD SHORTCUTS
The following keyboard shortcuts are available. Note that they cannot
be changed.
The following keyboard shortcuts are available.
## NORMAL MODE
Note that these are just the defaults; they can be changed in the
*footrc*, see *foot*(5).
*shift*+*page up*/*page down*
Scroll up/down in history

View file

@ -147,6 +147,89 @@ component.
*button-close-color*
Close button's AARRGGBB color. Default: _ffff3030_.
# SECTION: key-bindings
This section lets you override the default key bindings.
The general format is _action_=_combo1_..._comboN_. That is, each
action may have one or more key combinations, space separated. Each
combination is on the form _mod1_+_mod2_+_key_. The names of the
modifiers and the key *must* be a valid XKB key name.
Note that if *Shift* is one of the modifiers, the _key_ *must* be in
upper case. For example, *Control*+*Shift*+*v* will never trigger -
*Control*+*Shift*+*V* is the correct way to write it.
Note that *Alt* is usually called *Mod1*.
A key combination can only be mapped to *one* action. Lets say you
want to bind *Control*+*Shift*+*R* to *fullscreen*. Since this is the
default shortcut for *search-start*, you first need to unmap the
default binding. This can be done by setting _action_=*NONE*;
e.g. *search-start*=*NONE*.
*scrollback-up*
Scrolls up/back in history. Default: _Shift+Page_Up_.
*scrollback-down*
Scroll down/forward in history. Default: _Shift+Page_Down_.
*clipboard-copy*
Copies the current selection into the _clipboard_. Default: _Control+Shift+C_.
*clipboard-paste*
Pastes from the _clipboard_. Default: _Control+Shift+V_.
*primary-paste*
Pastes from the _primary selection_. Default: not bound (to a
_key_, see *mouse-bindings*).
*search-start*
Starts a scrollback/history search. Default: _Control+Shift+R_.
*font-increase*
Increases the font size by 0.5pt. Default: _Control+plus
Control+equal Control+KP\_Add_.
*font-decrease*
Decreases the font size by 0.5pt. Default: _Control+minus
Control+KP\_Subtract_.
*font-reset*
Resets the font size to the default. Default: _Control+0 Control+KP\_0_.
*spawn-terminal*
Spawns a new terminal. Default: _Control+Shift+Return_.
*minimize*
Minimizes the window. Default: _not bound_.
*maximize*
Toggle the maximized state. Default: _not bound_.
*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\_<name>_, where _BTN\_<name>_ is
the name of the event code (e.g. *BTN\_LEFT*, *BTN\_RIGHT*). You can
find the event names using *libinput debug-events*.
A button can only be mapped to *one* action. Lets say you want to bind
*BTN\_MIDDLE* to *fullscreen*. Since *BTN\_MIDDLE* is the default
binding for *primary-paste*, you first need to unmap the default
binding. This can be done by setting _action_=*NONE*;
e.g. *primary-paste*=*NONE*.
All actions listed under *key-bindings* can be user here as well.
*primary-paste*
Pastes from the _primary selection_. Default: _BTN_MIDDLE_.
# FONT FORMAT
The font is specified in FontConfig syntax. That is, a colon-separated

17
footrc
View file

@ -42,3 +42,20 @@
# button-minimize-color=ff0000ff
# button-maximize-color=ff00ff00
# button-close-color=ffff0000
[key-bindings]
# scrollback-up=Shift+Page_Up
# scrollback-down=Shift+Page_Down
# clipboard-copy=Control+Shift+C
# clipboard-paste=Control+Shift+V
# search-start=Control+Shift+R
# font-increase=Control+plus Control+equal Control+KP_Add
# font-decrease=Control+minus Control+KP_Subtract
# font-reset=Control+0 Control+KP_0
# spawn-terminal=Control+Shift+Return
# # minimize=<not bound>
# # maximize=<not bound>
# # fullscreen=<not bound>
[mouse-bindings]
# primary-paste=BTN_MIDDLE

227
input.c
View file

@ -31,6 +31,140 @@
#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)
{
switch (action) {
case BIND_ACTION_NONE:
break;
case BIND_ACTION_SCROLLBACK_UP:
cmd_scrollback_up(term, term->rows);
break;
case BIND_ACTION_SCROLLBACK_DOWN:
cmd_scrollback_down(term, term->rows);
break;
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:
selection_from_primary(term);
break;
case BIND_ACTION_SEARCH_START:
search_begin(term);
break;
case BIND_ACTION_FONT_SIZE_UP:
term_font_size_increase(term);
break;
case BIND_ACTION_FONT_SIZE_DOWN:
term_font_size_decrease(term);
break;
case BIND_ACTION_FONT_SIZE_RESET:
term_font_size_reset(term);
break;
case BIND_ACTION_SPAWN_TERMINAL:
term_spawn_new(term);
break;
case BIND_ACTION_MINIMIZE:
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
break;
case BIND_ACTION_MAXIMIZE:
if (term->window->is_maximized)
xdg_toplevel_unset_maximized(term->window->xdg_toplevel);
else
xdg_toplevel_set_maximized(term->window->xdg_toplevel);
break;
case BIND_ACTION_FULLSCREEN:
if (term->window->is_fullscreen)
xdg_toplevel_unset_fullscreen(term->window->xdg_toplevel);
else
xdg_toplevel_set_fullscreen(term->window->xdg_toplevel, NULL);
break;
case BIND_ACTION_COUNT:
assert(false);
break;
}
}
bool
input_parse_key_binding_for_action(
struct xkb_keymap *keymap, enum binding_action action,
const char *combos, key_binding_list_t *bindings)
{
if (combos == NULL)
return true;
xkb_mod_mask_t mod_mask = 0;
xkb_keysym_t sym = 0;
char *copy = strdup(combos);
for (char *save1 = NULL, *combo = strtok_r(copy, " ", &save1);
combo != NULL;
combo = strtok_r(NULL, " ", &save1))
{
LOG_DBG("%s", combo);
for (char *save2 = NULL, *key = strtok_r(combo, "+", &save2),
*next_key = strtok_r(NULL, "+", &save2);
key != NULL;
key = next_key, next_key = strtok_r(NULL, "+", &save2))
{
if (next_key != NULL) {
/* Modifier */
xkb_mod_index_t mod = xkb_keymap_mod_get_index(keymap, key);
if (mod == -1) {
LOG_ERR("%s: not a valid modifier name", key);
free(copy);
return false;
}
mod_mask |= 1 << mod;
LOG_DBG("MOD: %d - %s", mod, key);
} else {
/* Symbol */
sym = xkb_keysym_from_name(key, 0);
}
}
LOG_DBG("action=%u: mods=0x%08x, sym=%d", action, mod_mask, sym);
assert(sym != 0);
if (bindings != NULL) {
const struct key_binding binding = {
.mods = mod_mask,
.sym = sym,
.action = action,
};
tll_push_back(*bindings, binding);
}
}
free(copy);
return true;
}
static void
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size)
@ -59,6 +193,7 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
xkb_context_unref(wayl->kbd.xkb);
wayl->kbd.xkb = NULL;
}
tll_free(wayl->kbd.bindings.key);
wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
wayl->kbd.xkb_keymap = xkb_keymap_new_from_string(
@ -81,6 +216,16 @@ 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++) {
input_parse_key_binding_for_action(
wayl->kbd.xkb_keymap, i,
wayl->conf->bindings.key[i], &wayl->kbd.bindings.key);
input_parse_key_binding_for_action(
wayl->kbd.xkb_keymap, i,
wayl->conf->bindings.search[i], &wayl->kbd.bindings.search);
}
}
static void
@ -180,7 +325,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;
@ -261,7 +405,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;
}
@ -358,7 +501,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
if (term->is_searching) {
if (should_repeat)
start_repeater(wayl, key - 8);
search_input(term, key, sym, effective_mods);
search_input(term, key, sym, effective_mods, serial);
return;
}
@ -375,57 +518,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
sym, mods, consumed, significant, effective_mods, should_repeat);
/*
* Builtin shortcuts
* User configurable bindings
*/
if (effective_mods == shift) {
if (sym == XKB_KEY_Page_Up) {
cmd_scrollback_up(term, term->rows);
goto maybe_repeat;
}
else if (sym == XKB_KEY_Page_Down) {
cmd_scrollback_down(term, term->rows);
goto maybe_repeat;
}
}
else if (effective_mods == ctrl) {
if (sym == XKB_KEY_equal || sym == XKB_KEY_plus || sym == XKB_KEY_KP_Add) {
term_font_size_increase(term);
goto maybe_repeat;
}
else if (sym == XKB_KEY_minus || sym == XKB_KEY_KP_Subtract) {
term_font_size_decrease(term);
goto maybe_repeat;
}
else if (sym == XKB_KEY_0 || sym == XKB_KEY_KP_Equal || sym == XKB_KEY_KP_0) {
term_font_size_reset(term);
goto maybe_repeat;
}
}
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) {
term_spawn_new(term);
tll_foreach(wayl->kbd.bindings.key, it) {
if (it->item.mods == effective_mods && it->item.sym == sym) {
input_execute_binding(term, it->item.action, serial);
goto maybe_repeat;
}
}
@ -1056,11 +1153,25 @@ 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);
break;
}
selection_cancel(term);
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <wayland-client.h>
#include "wayland.h"
@ -8,3 +9,10 @@ extern const struct wl_keyboard_listener keyboard_listener;
extern const struct wl_pointer_listener pointer_listener;
void input_repeat(struct wayland *wayl, uint32_t key);
bool input_parse_key_binding_for_action(
struct xkb_keymap *keymap, enum binding_action action,
const char *combos, key_binding_list_t *bindings);
void input_execute_binding(
struct terminal *term, enum binding_action action, uint32_t serial);

4
main.c
View file

@ -273,8 +273,10 @@ main(int argc, char *const *argv)
argv += optind;
struct config conf = {NULL};
if (!config_load(&conf, conf_path))
if (!config_load(&conf, conf_path)) {
config_free(conf);
return ret;
}
setlocale(LC_ALL, "");
if (!locale_is_utf8()) {

View file

@ -11,6 +11,7 @@
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "grid.h"
#include "input.h"
#include "misc.h"
#include "render.h"
#include "selection.h"
@ -412,7 +413,8 @@ distance_prev_word(const struct terminal *term)
}
void
search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods)
search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym,
xkb_mod_mask_t mods, uint32_t serial)
{
LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods);
@ -424,6 +426,16 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask
enum xkb_compose_status compose_status = xkb_compose_state_get_status(
term->wl->kbd.xkb_compose_state);
/*
* User configurable bindings
*/
tll_foreach(term->wl->kbd.bindings.search, it) {
if (it->item.mods == mods && it->item.sym == sym) {
input_execute_binding(term, it->item.action, serial);
return;
}
}
/* Cancel search */
if ((mods == 0 && sym == XKB_KEY_Escape) ||
(mods == ctrl && sym == XKB_KEY_g))

View file

@ -5,4 +5,5 @@
void search_begin(struct terminal *term);
void search_cancel(struct terminal *term);
void search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods);
void search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods,
uint32_t serial);

View file

@ -911,6 +911,8 @@ wayl_destroy(struct wayland *wayl)
if (wayl->presentation != NULL)
wp_presentation_destroy(wayl->presentation);
tll_free(wayl->kbd.bindings.key);
tll_free(wayl->kbd.bindings.search);
if (wayl->kbd.xkb_compose_state != NULL)
xkb_compose_state_unref(wayl->kbd.xkb_compose_state);
if (wayl->kbd.xkb_compose_table != NULL)

View file

@ -41,6 +41,37 @@ struct monitor {
float inch; /* e.g. 24" */
};
enum binding_action {
BIND_ACTION_NONE,
BIND_ACTION_SCROLLBACK_UP,
BIND_ACTION_SCROLLBACK_DOWN,
BIND_ACTION_CLIPBOARD_COPY,
BIND_ACTION_CLIPBOARD_PASTE,
BIND_ACTION_PRIMARY_PASTE,
BIND_ACTION_SEARCH_START,
BIND_ACTION_FONT_SIZE_UP,
BIND_ACTION_FONT_SIZE_DOWN,
BIND_ACTION_FONT_SIZE_RESET,
BIND_ACTION_SPAWN_TERMINAL,
BIND_ACTION_MINIMIZE,
BIND_ACTION_MAXIMIZE,
BIND_ACTION_FULLSCREEN,
BIND_ACTION_COUNT,
};
struct key_binding {
xkb_mod_mask_t mods;
xkb_keysym_t sym;
enum binding_action action;
};
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;
@ -66,6 +97,11 @@ struct kbd {
bool alt;
bool ctrl;
bool meta;
struct {
key_binding_list_t key;
key_binding_list_t search;
} bindings;
};
struct wl_clipboard {