mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
config: add infrastructure to handle URL mode specific key bindings
* Add “show-urls” action to regular key bindings * Add url-bindings section to foot.ini * Add “cancel” action to URL mode key bindings
This commit is contained in:
parent
8f4a8cd8ce
commit
ee39966ded
6 changed files with 190 additions and 1 deletions
120
config.c
120
config.c
|
|
@ -75,6 +75,7 @@ static const char *const binding_action_map[] = {
|
|||
[BIND_ACTION_PIPE_SCROLLBACK] = "pipe-scrollback",
|
||||
[BIND_ACTION_PIPE_VIEW] = "pipe-visible",
|
||||
[BIND_ACTION_PIPE_SELECTED] = "pipe-selected",
|
||||
[BIND_ACTION_SHOW_URLS] = "show-urls",
|
||||
|
||||
/* Mouse-specific actions */
|
||||
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
|
||||
|
|
@ -114,6 +115,14 @@ static const char *const search_binding_action_map[] = {
|
|||
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
|
||||
"search binding action map size mismatch");
|
||||
|
||||
static const char *const url_binding_action_map[] = {
|
||||
[BIND_ACTION_URL_NONE] = NULL,
|
||||
[BIND_ACTION_URL_CANCEL] = "cancel",
|
||||
};
|
||||
|
||||
static_assert(ALEN(url_binding_action_map) == BIND_ACTION_URL_COUNT,
|
||||
"URL binding action map size mismatch");
|
||||
|
||||
#define LOG_AND_NOTIFY_ERR(...) \
|
||||
do { \
|
||||
LOG_ERR(__VA_ARGS__); \
|
||||
|
|
@ -1168,6 +1177,37 @@ has_search_binding_collisions(struct config *conf, enum bind_action_search actio
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
has_url_binding_collisions(struct config *conf, enum bind_action_url action,
|
||||
const key_combo_list_t *key_combos,
|
||||
const char *path, unsigned lineno)
|
||||
{
|
||||
tll_foreach(conf->bindings.url, it) {
|
||||
if (it->item.action == action)
|
||||
continue;
|
||||
|
||||
tll_foreach(*key_combos, it2) {
|
||||
const struct config_key_modifiers *mods1 = &it->item.modifiers;
|
||||
const struct config_key_modifiers *mods2 = &it2->item.modifiers;
|
||||
|
||||
bool shift = mods1->shift == mods2->shift;
|
||||
bool alt = mods1->alt == mods2->alt;
|
||||
bool ctrl = mods1->ctrl == mods2->ctrl;
|
||||
bool meta = mods1->meta == mods2->meta;
|
||||
bool sym = it->item.sym == it2->item.sym;
|
||||
|
||||
if (shift && alt && ctrl && meta && sym) {
|
||||
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s'",
|
||||
path, lineno, it2->item.text,
|
||||
url_binding_action_map[it->item.action]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
argv_compare(char *const *argv1, char *const *argv2)
|
||||
{
|
||||
|
|
@ -1403,6 +1443,63 @@ parse_section_search_bindings(
|
|||
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_section_url_bindings(
|
||||
const char *key, const char *value, struct config *conf,
|
||||
const char *path, unsigned lineno)
|
||||
{
|
||||
for (enum bind_action_url action = 0;
|
||||
action < BIND_ACTION_URL_COUNT;
|
||||
action++)
|
||||
{
|
||||
if (url_binding_action_map[action] == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(key, url_binding_action_map[action]) != 0)
|
||||
continue;
|
||||
|
||||
/* Unset binding */
|
||||
if (strcasecmp(value, "none") == 0) {
|
||||
tll_foreach(conf->bindings.url, it) {
|
||||
if (it->item.action == action)
|
||||
tll_remove(conf->bindings.url, it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
key_combo_list_t key_combos = tll_init();
|
||||
if (!parse_key_combos(conf, value, &key_combos, path, lineno) ||
|
||||
has_url_binding_collisions(conf, action, &key_combos, path, lineno))
|
||||
{
|
||||
free_key_combo_list(&key_combos);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Remove existing bindings for this action */
|
||||
tll_foreach(conf->bindings.url, it) {
|
||||
if (it->item.action == action)
|
||||
tll_remove(conf->bindings.url, it);
|
||||
}
|
||||
|
||||
/* Emit key bindings */
|
||||
tll_foreach(key_combos, it) {
|
||||
struct config_key_binding_url binding = {
|
||||
.action = action,
|
||||
.modifiers = it->item.modifiers,
|
||||
.sym = it->item.sym,
|
||||
};
|
||||
tll_push_back(conf->bindings.url, binding);
|
||||
}
|
||||
|
||||
free_key_combo_list(&key_combos);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_AND_NOTIFY_ERR("%s:%u: [url-bindings]: %s: invalid key", path, lineno, key);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *key_combos,
|
||||
const char *path, unsigned lineno)
|
||||
|
|
@ -1778,6 +1875,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar
|
|||
SECTION_CSD,
|
||||
SECTION_KEY_BINDINGS,
|
||||
SECTION_SEARCH_BINDINGS,
|
||||
SECTION_URL_BINDINGS,
|
||||
SECTION_MOUSE_BINDINGS,
|
||||
SECTION_TWEAK,
|
||||
SECTION_COUNT,
|
||||
|
|
@ -1800,6 +1898,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar
|
|||
[SECTION_CSD] = {&parse_section_csd, "csd"},
|
||||
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
|
||||
[SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"},
|
||||
[SECTION_URL_BINDINGS] = {&parse_section_url_bindings, "url-bindings"},
|
||||
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
|
||||
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
||||
};
|
||||
|
|
@ -1993,6 +2092,7 @@ add_default_key_bindings(struct config *conf)
|
|||
add_binding(BIND_ACTION_FONT_SIZE_RESET, ctrl, XKB_KEY_0);
|
||||
add_binding(BIND_ACTION_FONT_SIZE_RESET, ctrl, XKB_KEY_KP_0);
|
||||
add_binding(BIND_ACTION_SPAWN_TERMINAL, ctrl_shift, XKB_KEY_N);
|
||||
add_binding(BIND_ACTION_SHOW_URLS, ctrl_shift, XKB_KEY_F);
|
||||
|
||||
#undef add_binding
|
||||
}
|
||||
|
|
@ -2045,6 +2145,25 @@ add_default_search_bindings(struct config *conf)
|
|||
#undef add_binding
|
||||
}
|
||||
|
||||
static void
|
||||
add_default_url_bindings(struct config *conf)
|
||||
{
|
||||
#define add_binding(action, mods, sym) \
|
||||
do { \
|
||||
tll_push_back( \
|
||||
conf->bindings.url, \
|
||||
((struct config_key_binding_url){action, mods, sym})); \
|
||||
} while (0)
|
||||
|
||||
const struct config_key_modifiers none = {0};
|
||||
const struct config_key_modifiers ctrl = {.ctrl = true};
|
||||
|
||||
add_binding(BIND_ACTION_URL_CANCEL, ctrl, XKB_KEY_g);
|
||||
add_binding(BIND_ACTION_URL_CANCEL, none, XKB_KEY_Escape);
|
||||
|
||||
#undef add_binding
|
||||
}
|
||||
|
||||
static void
|
||||
add_default_mouse_bindings(struct config *conf)
|
||||
{
|
||||
|
|
@ -2195,6 +2314,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
|
||||
add_default_key_bindings(conf);
|
||||
add_default_search_bindings(conf);
|
||||
add_default_url_bindings(conf);
|
||||
add_default_mouse_bindings(conf);
|
||||
|
||||
struct config_file conf_file = {.path = NULL, .fd = -1};
|
||||
|
|
|
|||
9
config.h
9
config.h
|
|
@ -42,6 +42,12 @@ struct config_key_binding_search {
|
|||
xkb_keysym_t sym;
|
||||
};
|
||||
|
||||
struct config_key_binding_url {
|
||||
enum bind_action_url action;
|
||||
struct config_key_modifiers modifiers;
|
||||
xkb_keysym_t sym;
|
||||
};
|
||||
|
||||
struct config_mouse_binding {
|
||||
enum bind_action_normal action;
|
||||
struct config_key_modifiers modifiers;
|
||||
|
|
@ -157,6 +163,9 @@ struct config {
|
|||
/* While searching (not - action to *start* a search is in the
|
||||
* 'key' bindings above */
|
||||
tll(struct config_key_binding_search) search;
|
||||
|
||||
/* While showing URL jump labels */
|
||||
tll(struct config_key_binding_url) url;
|
||||
} bindings;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -491,6 +491,11 @@ e.g. *search-start=none*.
|
|||
|
||||
Default: _not bound_
|
||||
|
||||
*show-urls*
|
||||
Enters URL mode, where all currently visible URLs are tagged with
|
||||
a jump label with a key sequence that will open the URL. Default:
|
||||
_Control+Shift+F_.
|
||||
|
||||
|
||||
# SECTION: search-bindings
|
||||
|
||||
|
|
@ -569,6 +574,17 @@ scrollback search mode. The syntax is exactly the same as the regular
|
|||
Paste from the _primary selection_ into the search
|
||||
buffer. Default: _Shift+Insert_.
|
||||
|
||||
|
||||
# SECTION: url-bindings
|
||||
|
||||
This section lets you override the default key bindings used in URL
|
||||
mode. The syntax is exactly the same as the regular **key-bindings**.
|
||||
|
||||
*cancel*
|
||||
Exits URL mode without opening an URL. Default: _Control+g
|
||||
Escape_.
|
||||
|
||||
|
||||
# SECTION: mouse-bindings
|
||||
|
||||
This section lets you override the default mouse bindings.
|
||||
|
|
|
|||
3
foot.ini
3
foot.ini
|
|
@ -116,6 +116,9 @@
|
|||
# clipboard-paste=Control+v Control+y
|
||||
# primary-paste=Shift+Insert
|
||||
|
||||
[url-bindings]
|
||||
# cancel=Control+g Escape
|
||||
|
||||
[mouse-bindings]
|
||||
# primary-paste=BTN_MIDDLE
|
||||
# select-begin=BTN_LEFT
|
||||
|
|
|
|||
28
input.c
28
input.c
|
|
@ -271,6 +271,10 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
return true;
|
||||
}
|
||||
|
||||
case BIND_ACTION_SHOW_URLS:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SELECT_BEGIN:
|
||||
selection_start(
|
||||
term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false);
|
||||
|
|
@ -403,6 +407,29 @@ convert_search_bindings(const struct config *conf, struct seat *seat)
|
|||
convert_search_binding(seat, &it->item);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_url_binding(struct seat *seat,
|
||||
const struct config_key_binding_url *conf_binding)
|
||||
{
|
||||
struct key_binding_url binding = {
|
||||
.action = conf_binding->action,
|
||||
.bind = {
|
||||
.mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers),
|
||||
.sym = conf_binding->sym,
|
||||
.key_codes = key_codes_for_xkb_sym(
|
||||
seat->kbd.xkb_keymap, conf_binding->sym),
|
||||
},
|
||||
};
|
||||
tll_push_back(seat->kbd.bindings.url, binding);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_url_bindings(const struct config *conf, struct seat *seat)
|
||||
{
|
||||
tll_foreach(conf->bindings.url, it)
|
||||
convert_url_binding(seat, &it->item);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_mouse_binding(struct seat *seat,
|
||||
const struct config_mouse_binding *conf_binding)
|
||||
|
|
@ -528,6 +555,7 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|||
|
||||
convert_key_bindings(wayl->conf, seat);
|
||||
convert_search_bindings(wayl->conf, seat);
|
||||
convert_url_bindings(wayl->conf, seat);
|
||||
convert_mouse_bindings(wayl->conf, seat);
|
||||
}
|
||||
|
||||
|
|
|
|||
15
wayland.h
15
wayland.h
|
|
@ -49,6 +49,7 @@ enum bind_action_normal {
|
|||
BIND_ACTION_PIPE_SCROLLBACK,
|
||||
BIND_ACTION_PIPE_VIEW,
|
||||
BIND_ACTION_PIPE_SELECTED,
|
||||
BIND_ACTION_SHOW_URLS,
|
||||
|
||||
/* Mouse specific actions - i.e. they require a mouse coordinate */
|
||||
BIND_ACTION_SELECT_BEGIN,
|
||||
|
|
@ -59,7 +60,7 @@ enum bind_action_normal {
|
|||
BIND_ACTION_SELECT_WORD_WS,
|
||||
BIND_ACTION_SELECT_ROW,
|
||||
|
||||
BIND_ACTION_KEY_COUNT = BIND_ACTION_PIPE_SELECTED + 1,
|
||||
BIND_ACTION_KEY_COUNT = BIND_ACTION_SHOW_URLS + 1,
|
||||
BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1,
|
||||
};
|
||||
|
||||
|
|
@ -106,6 +107,17 @@ struct key_binding_search {
|
|||
enum bind_action_search action;
|
||||
};
|
||||
|
||||
enum bind_action_url {
|
||||
BIND_ACTION_URL_NONE,
|
||||
BIND_ACTION_URL_CANCEL,
|
||||
BIND_ACTION_URL_COUNT,
|
||||
};
|
||||
|
||||
struct key_binding_url {
|
||||
struct key_binding bind;
|
||||
enum bind_action_url action;
|
||||
};
|
||||
|
||||
/* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */
|
||||
enum data_offer_mime_type {
|
||||
DATA_OFFER_MIME_UNSET,
|
||||
|
|
@ -192,6 +204,7 @@ struct seat {
|
|||
struct {
|
||||
tll(struct key_binding_normal) key;
|
||||
tll(struct key_binding_search) search;
|
||||
tll(struct key_binding_url) url;
|
||||
} bindings;
|
||||
} kbd;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue