Merge branch 'reduce-tllist-usage'

This commit is contained in:
Daniel Eklöf 2021-06-18 15:36:12 +02:00
commit b77dbc341b
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
5 changed files with 380 additions and 276 deletions

523
config.c
View file

@ -744,26 +744,40 @@ parse_section_main(const char *key, const char *value, struct config *conf,
strcmp(key, "font-bold") == 0 ? 1 :
strcmp(key, "font-italic") == 0 ? 2 : 3;
tll_foreach(conf->fonts[idx], it)
config_font_destroy(&it->item);
tll_free(conf->fonts[idx]);
config_font_list_destroy(&conf->fonts[idx]);
size_t count = 0;
size_t size = 0;
struct config_font *fonts = NULL;
char *copy = xstrdup(value);
for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) {
/* Trim spaces, strictly speaking not necessary, but looks nice :) */
while (*font != '\0' && isspace(*font))
font++;
if (*font != '\0') {
struct config_font font_data;
if (!config_font_parse(font, &font_data)) {
LOG_ERR("%s:%d: [default]: %s: invalid font specification",
path, lineno, key);
free(copy);
return false;
}
tll_push_back(conf->fonts[idx], font_data);
if (font[0] == '\0')
continue;
struct config_font font_data;
if (!config_font_parse(font, &font_data)) {
LOG_ERR("%s:%d: [default]: %s: invalid font specification",
path, lineno, key);
free(copy);
return false;
}
if (count + 1 > size) {
size += 4;
fonts = xrealloc(fonts, size * sizeof(fonts[0]));
}
xassert(count + 1 <= size);
fonts[count++] = font_data;
}
conf->fonts[idx].count = count;
conf->fonts[idx].arr = fonts;
free(copy);
}
@ -1356,14 +1370,20 @@ struct key_combo {
} m;
};
};
typedef tll(struct key_combo) key_combo_list_t;
static void
free_key_combo_list(key_combo_list_t *key_combos)
struct key_combo_list {
size_t count;
struct key_combo *combos;
};
static void NOINLINE
free_key_combo_list(struct key_combo_list *key_combos)
{
tll_foreach(*key_combos, it)
free(it->item.text);
tll_free(*key_combos);
for (size_t i = 0; i < key_combos->count; i++)
free(key_combos->combos[i].text);
free(key_combos->combos);
key_combos->count = 0;
key_combos->combos = NULL;
}
static bool
@ -1402,11 +1422,15 @@ out:
}
static bool
parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_combos,
parse_key_combos(struct config *conf, const char *combos,
struct key_combo_list *key_combos,
const char *section, const char *option,
const char *path, unsigned lineno)
{
xassert(tll_length(*key_combos) == 0);
xassert(key_combos != NULL);
xassert(key_combos->count == 0 && key_combos->combos == NULL);
size_t size = 0; /* Size of combos array in the key-combo list */
char *copy = xstrdup(combos);
@ -1450,18 +1474,25 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_
goto err;
}
tll_push_back(
*key_combos,
((struct key_combo){.text = xstrdup(combo), .modifiers = modifiers, .sym = sym}));
if (key_combos->count + 1 > size) {
size += 4;
key_combos->combos = xrealloc(
key_combos->combos, size * sizeof(key_combos->combos[0]));
}
xassert(key_combos->count + 1 <= size);
key_combos->combos[key_combos->count++] = (struct key_combo){
.text = xstrdup(combo),
.modifiers = modifiers,
.sym = sym,
};
}
free(copy);
return true;
err:
tll_foreach(*key_combos, it)
free(it->item.text);
tll_free(*key_combos);
free_key_combo_list(key_combos);
free(copy);
return false;
}
@ -1469,31 +1500,38 @@ err:
static bool
has_key_binding_collisions(struct config *conf,
int action, const char *const action_map[],
config_key_binding_list_t *bindings,
const key_combo_list_t *key_combos,
const struct config_key_binding_list *bindings,
const struct key_combo_list *key_combos,
const char *path, unsigned lineno)
{
tll_foreach(*bindings, it) {
if (it->item.action == action)
for (size_t j = 0; j < bindings->count; j++) {
const struct config_key_binding *combo1 = &bindings->arr[j];
if (combo1->action == BIND_ACTION_NONE)
continue;
tll_foreach(*key_combos, it2) {
const struct config_key_modifiers *mods1 = &it->item.modifiers;
const struct config_key_modifiers *mods2 = &it2->item.modifiers;
if (combo1->action == action)
continue;
for (size_t i = 0; i < key_combos->count; i++) {
const struct key_combo *combo2 = &key_combos->combos[i];
const struct config_key_modifiers *mods1 = &combo1->modifiers;
const struct config_key_modifiers *mods2 = &combo2->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;
bool sym = combo1->sym == combo2->sym;
if (shift && alt && ctrl && meta && sym) {
bool has_pipe = it->item.pipe.cmd != NULL;
bool has_pipe = combo1->pipe.cmd != NULL;
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'",
path, lineno, it2->item.text,
action_map[it->item.action],
path, lineno, combo2->text,
action_map[combo1->action],
has_pipe ? " [" : "",
has_pipe ? it->item.pipe.cmd : "",
has_pipe ? combo1->pipe.cmd : "",
has_pipe ? "]" : "");
return true;
}
@ -1587,7 +1625,7 @@ static bool NOINLINE
parse_key_binding_section(
const char *section, const char *key, const char *value,
int action_count, const char *const action_map[static action_count],
config_key_binding_list_t *bindings,
struct config_key_binding_list *bindings,
struct config *conf, const char *path, unsigned lineno)
{
char *pipe_cmd;
@ -1610,21 +1648,27 @@ parse_key_binding_section(
/* Unset binding */
if (strcasecmp(value, "none") == 0) {
tll_foreach(*bindings, it) {
if (it->item.action == action) {
if (it->item.pipe.master_copy) {
free(it->item.pipe.cmd);
free(it->item.pipe.argv);
}
tll_remove(*bindings, it);
for (size_t i = 0; i < bindings->count; i++) {
struct config_key_binding *binding = &bindings->arr[i];
if (binding->action != action)
continue;
if (binding->pipe.master_copy) {
free(binding->pipe.cmd);
free(binding->pipe.argv);
}
binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
}
free(pipe_argv);
free(pipe_cmd);
return true;
}
key_combo_list_t key_combos = tll_init();
struct key_combo_list key_combos = {0};
if (!parse_key_combos(
conf, value, &key_combos, section, key, path, lineno) ||
has_key_binding_collisions(
@ -1638,28 +1682,38 @@ parse_key_binding_section(
}
/* Remove existing bindings for this action+pipe */
tll_foreach(*bindings, it) {
if (it->item.action == action &&
((it->item.pipe.argv == NULL && pipe_argv == NULL) ||
(it->item.pipe.argv != NULL && pipe_argv != NULL &&
argv_compare(it->item.pipe.argv, pipe_argv) == 0)))
for (size_t i = 0; i < bindings->count; i++) {
struct config_key_binding *binding = &bindings->arr[i];
if (binding->action == action &&
((binding->pipe.argv == NULL && pipe_argv == NULL) ||
(binding->pipe.argv != NULL && pipe_argv != NULL &&
argv_compare(binding->pipe.argv, pipe_argv) == 0)))
{
if (it->item.pipe.master_copy) {
free(it->item.pipe.cmd);
free(it->item.pipe.argv);
if (binding->pipe.master_copy) {
free(binding->pipe.cmd);
free(binding->pipe.argv);
}
tll_remove(*bindings, it);
binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
}
}
/* Emit key bindings */
size_t ofs = bindings->count;
bindings->count += key_combos.count;
bindings->arr = xrealloc(
bindings->arr, bindings->count * sizeof(bindings->arr[0]));
bool first = true;
tll_foreach(key_combos, it) {
for (size_t i = 0; i < key_combos.count; i++) {
const struct key_combo *combo = &key_combos.combos[i];
struct config_key_binding binding = {
.action = action,
.modifiers = it->item.modifiers,
.sym = it->item.sym,
.modifiers = combo->modifiers,
.sym = combo->sym,
.pipe = {
.cmd = pipe_cmd,
.argv = pipe_argv,
@ -1667,7 +1721,8 @@ parse_key_binding_section(
},
};
tll_push_back(*bindings, binding);
/* TODO: we could re-use free:d slots */
bindings->arr[ofs + i] = binding;
first = false;
}
@ -1747,10 +1802,14 @@ parse_section_url_bindings(
}
static bool
parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *key_combos,
parse_mouse_combos(struct config *conf, const char *combos,
struct key_combo_list *key_combos,
const char *path, unsigned lineno)
{
xassert(tll_length(*key_combos) == 0);
xassert(key_combos != NULL);
xassert(key_combos->count == 0 && key_combos->combos == NULL);
size_t size = 0; /* Size of the combos array in key_combos */
char *copy = xstrdup(combos);
@ -1835,44 +1894,55 @@ parse_mouse_combos(struct config *conf, const char *combos, key_combo_list_t *ke
.count = count,
},
};
tll_push_back(*key_combos, new);
if (key_combos->count + 1 > size) {
size += 4;
key_combos->combos = xrealloc(
key_combos->combos, size * sizeof(key_combos->combos[0]));
}
xassert(key_combos->count + 1 <= size);
key_combos->combos[key_combos->count++] = new;
}
free(copy);
return true;
err:
tll_foreach(*key_combos, it) {
free(it->item.text);
tll_remove(*key_combos, it);
}
free_key_combo_list(key_combos);
free(copy);
return false;
}
static bool
has_mouse_binding_collisions(struct config *conf, const key_combo_list_t *key_combos,
has_mouse_binding_collisions(struct config *conf, const struct key_combo_list *key_combos,
const char *path, unsigned lineno)
{
tll_foreach(conf->bindings.mouse, it) {
tll_foreach(*key_combos, it2) {
const struct config_key_modifiers *mods1 = &it->item.modifiers;
const struct config_key_modifiers *mods2 = &it2->item.modifiers;
for (size_t j = 0; j < conf->bindings.mouse.count; j++) {
const struct config_mouse_binding *combo1 = &conf->bindings.mouse.arr[j];
if (combo1->action == BIND_ACTION_NONE)
continue;
for (size_t i = 0; i < key_combos->count; i++) {
const struct key_combo *combo2 = &key_combos->combos[i];
const struct config_key_modifiers *mods1 = &combo1->modifiers;
const struct config_key_modifiers *mods2 = &combo2->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 button = it->item.button == it2->item.m.button;
bool count = it->item.count == it2->item.m.count;
bool button = combo1->button == combo2->m.button;
bool count = combo1->count == combo2->m.count;
if (shift && alt && ctrl && meta && button && count) {
bool has_pipe = it->item.pipe.cmd != NULL;
bool has_pipe = combo1->pipe.cmd != NULL;
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'",
path, lineno, it2->item.text,
binding_action_map[it->item.action],
path, lineno, combo2->text,
binding_action_map[combo1->action],
has_pipe ? " [" : "",
has_pipe ? it->item.pipe.cmd : "",
has_pipe ? combo1->pipe.cmd : "",
has_pipe ? "]" : "");
return true;
}
@ -1911,13 +1981,18 @@ parse_section_mouse_bindings(
/* Unset binding */
if (strcasecmp(value, "none") == 0) {
tll_foreach(conf->bindings.mouse, it) {
if (it->item.action == action) {
if (it->item.pipe.master_copy) {
free(it->item.pipe.cmd);
free(it->item.pipe.argv);
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
struct config_mouse_binding *binding =
&conf->bindings.mouse.arr[i];
if (binding->action == action) {
if (binding->pipe.master_copy) {
free(binding->pipe.cmd);
free(binding->pipe.argv);
}
tll_remove(conf->bindings.mouse, it);
binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
}
}
free(pipe_argv);
@ -1925,7 +2000,7 @@ parse_section_mouse_bindings(
return true;
}
key_combo_list_t key_combos = tll_init();
struct key_combo_list key_combos = {0};
if (!parse_mouse_combos(conf, value, &key_combos, path, lineno) ||
has_mouse_binding_collisions(conf, &key_combos, path, lineno))
{
@ -1936,35 +2011,47 @@ parse_section_mouse_bindings(
}
/* Remove existing bindings for this action */
tll_foreach(conf->bindings.mouse, it) {
if (it->item.action == action &&
((it->item.pipe.argv == NULL && pipe_argv == NULL) ||
(it->item.pipe.argv != NULL && pipe_argv != NULL &&
argv_compare(it->item.pipe.argv, pipe_argv) == 0)))
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i];
if (binding->action == action &&
((binding->pipe.argv == NULL && pipe_argv == NULL) ||
(binding->pipe.argv != NULL && pipe_argv != NULL &&
argv_compare(binding->pipe.argv, pipe_argv) == 0)))
{
if (it->item.pipe.master_copy) {
free(it->item.pipe.cmd);
free(it->item.pipe.argv);
if (binding->pipe.master_copy) {
free(binding->pipe.cmd);
free(binding->pipe.argv);
}
tll_remove(conf->bindings.mouse, it);
binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
}
}
/* Emit mouse bindings */
size_t ofs = conf->bindings.mouse.count;
conf->bindings.mouse.count += key_combos.count;
conf->bindings.mouse.arr = xrealloc(
conf->bindings.mouse.arr,
conf->bindings.mouse.count * sizeof(conf->bindings.mouse.arr[0]));
bool first = true;
tll_foreach(key_combos, it) {
for (size_t i = 0; i < key_combos.count; i++) {
const struct key_combo *combo = &key_combos.combos[i];
struct config_mouse_binding binding = {
.action = action,
.modifiers = it->item.modifiers,
.button = it->item.m.button,
.count = it->item.m.count,
.modifiers = combo->modifiers,
.button = combo->m.button,
.count = combo->m.count,
.pipe = {
.cmd = pipe_cmd,
.argv = pipe_argv,
.master_copy = first,
},
};
tll_push_back(conf->bindings.mouse, binding);
conf->bindings.mouse.arr[ofs + i] = binding;
first = false;
}
@ -2362,137 +2449,112 @@ get_server_socket_path(void)
return xasprintf("%s/foot-%s.sock", xdg_runtime, wayland_display);
}
static void NOINLINE
add_key_binding(config_key_binding_list_t *list, int action,
const struct config_key_modifiers *mods, xkb_keysym_t sym)
{
tll_push_back(
*list,
((struct config_key_binding){action, *mods, sym}));
}
#define m_none {0}
#define m_alt {.alt = true}
#define m_ctrl {.ctrl = true}
#define m_shift {.shift = true}
#define m_ctrl_shift {.ctrl = true, .shift = true}
static void
add_default_key_bindings(struct config *conf)
{
#define add_binding(action, mods, sym) \
add_key_binding(&conf->bindings.key, action, &mods, sym)
static const struct config_key_binding bindings[] = {
{BIND_ACTION_SCROLLBACK_UP_PAGE, m_shift, XKB_KEY_Page_Up},
{BIND_ACTION_SCROLLBACK_DOWN_PAGE, m_shift, XKB_KEY_Page_Down},
{BIND_ACTION_CLIPBOARD_COPY, m_ctrl_shift, XKB_KEY_c},
{BIND_ACTION_CLIPBOARD_PASTE, m_ctrl_shift, XKB_KEY_v},
{BIND_ACTION_PRIMARY_PASTE, m_shift, XKB_KEY_Insert},
{BIND_ACTION_SEARCH_START, m_ctrl_shift, XKB_KEY_r},
{BIND_ACTION_FONT_SIZE_UP, m_ctrl, XKB_KEY_plus},
{BIND_ACTION_FONT_SIZE_UP, m_ctrl, XKB_KEY_equal},
{BIND_ACTION_FONT_SIZE_UP, m_ctrl, XKB_KEY_KP_Add},
{BIND_ACTION_FONT_SIZE_DOWN, m_ctrl, XKB_KEY_minus},
{BIND_ACTION_FONT_SIZE_DOWN, m_ctrl, XKB_KEY_KP_Subtract},
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, XKB_KEY_0},
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, XKB_KEY_KP_0},
{BIND_ACTION_SPAWN_TERMINAL, m_ctrl_shift, XKB_KEY_n},
{BIND_ACTION_SHOW_URLS_LAUNCH, m_ctrl_shift, XKB_KEY_u},
};
const struct config_key_modifiers shift = {.shift = true};
const struct config_key_modifiers ctrl = {.ctrl = true};
const struct config_key_modifiers ctrl_shift = {.ctrl = true, .shift = true};
add_binding(BIND_ACTION_SCROLLBACK_UP_PAGE, shift, XKB_KEY_Page_Up);
add_binding(BIND_ACTION_SCROLLBACK_DOWN_PAGE, shift, XKB_KEY_Page_Down);
add_binding(BIND_ACTION_CLIPBOARD_COPY, ctrl_shift, XKB_KEY_c);
add_binding(BIND_ACTION_CLIPBOARD_PASTE, ctrl_shift, XKB_KEY_v);
add_binding(BIND_ACTION_PRIMARY_PASTE, shift, XKB_KEY_Insert);
add_binding(BIND_ACTION_SEARCH_START, ctrl_shift, XKB_KEY_r);
add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_plus);
add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_equal);
add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_KP_Add);
add_binding(BIND_ACTION_FONT_SIZE_DOWN, ctrl, XKB_KEY_minus);
add_binding(BIND_ACTION_FONT_SIZE_DOWN, ctrl, XKB_KEY_KP_Subtract);
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_LAUNCH, ctrl_shift, XKB_KEY_u);
#undef add_binding
conf->bindings.key.count = ALEN(bindings);
conf->bindings.key.arr = xmalloc(sizeof(bindings));
memcpy(conf->bindings.key.arr, bindings, sizeof(bindings));
}
static void
add_default_search_bindings(struct config *conf)
{
#define add_binding(action, mods, sym) \
add_key_binding(&conf->bindings.search, action, &mods, sym)
static const struct config_key_binding bindings[] = {
{BIND_ACTION_SEARCH_CANCEL, m_ctrl, XKB_KEY_c},
{BIND_ACTION_SEARCH_CANCEL, m_ctrl, XKB_KEY_g},
{BIND_ACTION_SEARCH_CANCEL, m_none, XKB_KEY_Escape},
{BIND_ACTION_SEARCH_COMMIT, m_none, XKB_KEY_Return},
{BIND_ACTION_SEARCH_FIND_PREV, m_ctrl, XKB_KEY_r},
{BIND_ACTION_SEARCH_FIND_NEXT, m_ctrl, XKB_KEY_s},
{BIND_ACTION_SEARCH_EDIT_LEFT, m_none, XKB_KEY_Left},
{BIND_ACTION_SEARCH_EDIT_LEFT, m_ctrl, XKB_KEY_b},
{BIND_ACTION_SEARCH_EDIT_LEFT_WORD, m_ctrl, XKB_KEY_Left},
{BIND_ACTION_SEARCH_EDIT_LEFT_WORD, m_alt, XKB_KEY_b},
{BIND_ACTION_SEARCH_EDIT_RIGHT, m_none, XKB_KEY_Right},
{BIND_ACTION_SEARCH_EDIT_RIGHT, m_ctrl, XKB_KEY_f},
{BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, m_ctrl, XKB_KEY_Right},
{BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, m_alt, XKB_KEY_f},
{BIND_ACTION_SEARCH_EDIT_HOME, m_none, XKB_KEY_Home},
{BIND_ACTION_SEARCH_EDIT_HOME, m_ctrl, XKB_KEY_a},
{BIND_ACTION_SEARCH_EDIT_END, m_none, XKB_KEY_End},
{BIND_ACTION_SEARCH_EDIT_END, m_ctrl, XKB_KEY_e},
{BIND_ACTION_SEARCH_DELETE_PREV, m_none, XKB_KEY_BackSpace},
{BIND_ACTION_SEARCH_DELETE_PREV_WORD, m_ctrl, XKB_KEY_BackSpace},
{BIND_ACTION_SEARCH_DELETE_PREV_WORD, m_alt, XKB_KEY_BackSpace},
{BIND_ACTION_SEARCH_DELETE_NEXT, m_none, XKB_KEY_Delete},
{BIND_ACTION_SEARCH_DELETE_NEXT_WORD, m_ctrl, XKB_KEY_Delete},
{BIND_ACTION_SEARCH_DELETE_NEXT_WORD, m_alt, XKB_KEY_d},
{BIND_ACTION_SEARCH_EXTEND_WORD, m_ctrl, XKB_KEY_w},
{BIND_ACTION_SEARCH_EXTEND_WORD_WS, m_ctrl_shift, XKB_KEY_w},
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl, XKB_KEY_v},
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m_ctrl, XKB_KEY_y},
{BIND_ACTION_SEARCH_PRIMARY_PASTE, m_shift, XKB_KEY_Insert},
};
const struct config_key_modifiers none = {0};
const struct config_key_modifiers alt = {.alt = true};
const struct config_key_modifiers ctrl = {.ctrl = true};
const struct config_key_modifiers shift = {.shift = true};
const struct config_key_modifiers ctrl_shift = {.ctrl = true, .shift = true};
add_binding(BIND_ACTION_SEARCH_CANCEL, ctrl, XKB_KEY_c);
add_binding(BIND_ACTION_SEARCH_CANCEL, ctrl, XKB_KEY_g);
add_binding(BIND_ACTION_SEARCH_CANCEL, none, XKB_KEY_Escape);
add_binding(BIND_ACTION_SEARCH_COMMIT, none, XKB_KEY_Return);
add_binding(BIND_ACTION_SEARCH_FIND_PREV, ctrl, XKB_KEY_r);
add_binding(BIND_ACTION_SEARCH_FIND_NEXT, ctrl, XKB_KEY_s);
add_binding(BIND_ACTION_SEARCH_EDIT_LEFT, none, XKB_KEY_Left);
add_binding(BIND_ACTION_SEARCH_EDIT_LEFT, ctrl, XKB_KEY_b);
add_binding(BIND_ACTION_SEARCH_EDIT_LEFT_WORD, ctrl, XKB_KEY_Left);
add_binding(BIND_ACTION_SEARCH_EDIT_LEFT_WORD, alt, XKB_KEY_b);
add_binding(BIND_ACTION_SEARCH_EDIT_RIGHT, none, XKB_KEY_Right);
add_binding(BIND_ACTION_SEARCH_EDIT_RIGHT, ctrl, XKB_KEY_f);
add_binding(BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, ctrl, XKB_KEY_Right);
add_binding(BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, alt, XKB_KEY_f);
add_binding(BIND_ACTION_SEARCH_EDIT_HOME, none, XKB_KEY_Home);
add_binding(BIND_ACTION_SEARCH_EDIT_HOME, ctrl, XKB_KEY_a);
add_binding(BIND_ACTION_SEARCH_EDIT_END, none, XKB_KEY_End);
add_binding(BIND_ACTION_SEARCH_EDIT_END, ctrl, XKB_KEY_e);
add_binding(BIND_ACTION_SEARCH_DELETE_PREV, none, XKB_KEY_BackSpace);
add_binding(BIND_ACTION_SEARCH_DELETE_PREV_WORD, ctrl, XKB_KEY_BackSpace);
add_binding(BIND_ACTION_SEARCH_DELETE_PREV_WORD, alt, XKB_KEY_BackSpace);
add_binding(BIND_ACTION_SEARCH_DELETE_NEXT, none, XKB_KEY_Delete);
add_binding(BIND_ACTION_SEARCH_DELETE_NEXT_WORD, ctrl, XKB_KEY_Delete);
add_binding(BIND_ACTION_SEARCH_DELETE_NEXT_WORD, alt, XKB_KEY_d);
add_binding(BIND_ACTION_SEARCH_EXTEND_WORD, ctrl, XKB_KEY_w);
add_binding(BIND_ACTION_SEARCH_EXTEND_WORD_WS, ctrl_shift, XKB_KEY_w);
add_binding(BIND_ACTION_SEARCH_CLIPBOARD_PASTE, ctrl, XKB_KEY_v);
add_binding(BIND_ACTION_SEARCH_CLIPBOARD_PASTE, ctrl, XKB_KEY_y);
add_binding(BIND_ACTION_SEARCH_PRIMARY_PASTE, shift, XKB_KEY_Insert);
#undef add_binding
conf->bindings.search.count = ALEN(bindings);
conf->bindings.search.arr = xmalloc(sizeof(bindings));
memcpy(conf->bindings.search.arr, bindings, sizeof(bindings));
}
static void
add_default_url_bindings(struct config *conf)
{
#define add_binding(action, mods, sym) \
add_key_binding(&conf->bindings.url, action, &mods, sym)
static const struct config_key_binding bindings[] = {
{BIND_ACTION_URL_CANCEL, m_ctrl, XKB_KEY_c},
{BIND_ACTION_URL_CANCEL, m_ctrl, XKB_KEY_g},
{BIND_ACTION_URL_CANCEL, m_ctrl, XKB_KEY_d},
{BIND_ACTION_URL_CANCEL, m_none, XKB_KEY_Escape},
{BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL, m_none, XKB_KEY_t},
};
const struct config_key_modifiers none = {0};
const struct config_key_modifiers ctrl = {.ctrl = true};
add_binding(BIND_ACTION_URL_CANCEL, ctrl, XKB_KEY_c);
add_binding(BIND_ACTION_URL_CANCEL, ctrl, XKB_KEY_g);
add_binding(BIND_ACTION_URL_CANCEL, ctrl, XKB_KEY_d);
add_binding(BIND_ACTION_URL_CANCEL, none, XKB_KEY_Escape);
add_binding(BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL, none, XKB_KEY_t);
#undef add_binding
}
static void NOINLINE
add_mouse_binding(config_mouse_binding_list_t *list, int action,
const struct config_key_modifiers *mods,
int button, int count)
{
tll_push_back(
*list,
((struct config_mouse_binding){action, *mods, button, count, {0}}));
conf->bindings.url.count = ALEN(bindings);
conf->bindings.url.arr = xmalloc(sizeof(bindings));
memcpy(conf->bindings.url.arr, bindings, sizeof(bindings));
}
static void
add_default_mouse_bindings(struct config *conf)
{
#define add_binding(action, mods, btn, count) \
add_mouse_binding(&conf->bindings.mouse, action, &mods, btn, count)
static const struct config_mouse_binding bindings[] = {
{BIND_ACTION_PRIMARY_PASTE, m_none, BTN_MIDDLE, 1},
{BIND_ACTION_SELECT_BEGIN, m_none, BTN_LEFT, 1},
{BIND_ACTION_SELECT_BEGIN_BLOCK, m_ctrl, BTN_LEFT, 1},
{BIND_ACTION_SELECT_EXTEND, m_none, BTN_RIGHT, 1},
{BIND_ACTION_SELECT_EXTEND_CHAR_WISE, m_ctrl, BTN_RIGHT, 1},
{BIND_ACTION_SELECT_WORD, m_none, BTN_LEFT, 2},
{BIND_ACTION_SELECT_WORD_WS, m_ctrl, BTN_LEFT, 2},
{BIND_ACTION_SELECT_ROW, m_none, BTN_LEFT, 3},
};
const struct config_key_modifiers none = {0};
const struct config_key_modifiers ctrl = {.ctrl = true};
add_binding(BIND_ACTION_PRIMARY_PASTE, none, BTN_MIDDLE, 1);
add_binding(BIND_ACTION_SELECT_BEGIN, none, BTN_LEFT, 1);
add_binding(BIND_ACTION_SELECT_BEGIN_BLOCK, ctrl, BTN_LEFT, 1);
add_binding(BIND_ACTION_SELECT_EXTEND, none, BTN_RIGHT, 1);
add_binding(BIND_ACTION_SELECT_EXTEND_CHAR_WISE, ctrl, BTN_RIGHT, 1);
add_binding(BIND_ACTION_SELECT_WORD, none, BTN_LEFT, 2);
add_binding(BIND_ACTION_SELECT_WORD_WS, ctrl, BTN_LEFT, 2);
add_binding(BIND_ACTION_SELECT_ROW, none, BTN_LEFT, 3);
#undef add_binding
conf->bindings.mouse.count = ALEN(bindings);
conf->bindings.mouse.arr = xmalloc(sizeof(bindings));
memcpy(conf->bindings.mouse.arr, bindings, sizeof(bindings));
}
bool
@ -2521,7 +2583,7 @@ config_load(struct config *conf, const char *conf_path,
.palette_based = false,
},
.startup_mode = STARTUP_WINDOWED,
.fonts = {tll_init(), tll_init(), tll_init(), tll_init()},
.fonts = {{0}},
.line_height = {.pt = 0, .px = -1},
.letter_spacing = {.pt = 0, .px = 0},
.horizontal_letter_offset = {.pt = 0, .px = 0},
@ -2734,13 +2796,16 @@ config_load(struct config *conf, const char *conf_path,
conf->colors.selection_bg >> 24 == 0;
out:
if (ret && tll_length(conf->fonts[0]) == 0) {
if (ret && conf->fonts[0].count == 0) {
struct config_font font;
if (!config_font_parse("monospace", &font)) {
LOG_ERR("failed to load font 'monospace' - no fonts installed?");
ret = false;
} else
tll_push_back(conf->fonts[0], font);
} else {
conf->fonts[0].count = 1;
conf->fonts[0].arr = malloc(sizeof(font));
conf->fonts[0].arr[0] = font;
}
}
free(conf_file.path);
@ -2783,14 +2848,14 @@ config_override_apply(struct config *conf, config_override_t *overrides, bool er
return true;
}
static void
static void NOINLINE
free_spawn_template(struct config_spawn_template *template)
{
free(template->raw_cmd);
free(template->argv);
}
static void
static void NOINLINE
binding_pipe_free(struct config_binding_pipe *pipe)
{
if (pipe->master_copy) {
@ -2806,12 +2871,11 @@ key_binding_free(struct config_key_binding *binding)
}
static void
key_binding_list_free(config_key_binding_list_t *bindings)
key_binding_list_free(struct config_key_binding_list *bindings)
{
tll_foreach(*bindings, it) {
key_binding_free(&it->item);
tll_remove(*bindings, it);
}
for (size_t i = 0; i < bindings->count; i++)
key_binding_free(&bindings->arr[i]);
free(bindings->arr);
}
void
@ -2825,12 +2889,8 @@ config_free(struct config conf)
free_spawn_template(&conf.bell.command);
free(conf.scrollback.indicator.text);
free_spawn_template(&conf.notify);
for (size_t i = 0; i < ALEN(conf.fonts); i++) {
tll_foreach(conf.fonts[i], it) {
config_font_destroy(&it->item);
tll_remove(conf.fonts[i], it);
}
}
for (size_t i = 0; i < ALEN(conf.fonts); i++)
config_font_list_destroy(&conf.fonts[i]);
free(conf.server_socket_path);
free(conf.url.label_letters);
@ -2843,10 +2903,9 @@ config_free(struct config conf)
key_binding_list_free(&conf.bindings.search);
key_binding_list_free(&conf.bindings.url);
tll_foreach(conf.bindings.mouse, it) {
binding_pipe_free(&it->item.pipe);
tll_remove(conf.bindings.mouse, it);
}
for (size_t i = 0; i < conf.bindings.mouse.count; i++)
binding_pipe_free(&conf.bindings.mouse.arr[i].pipe);
free(conf.bindings.mouse.arr);
user_notifications_free(&conf.notifications);
}
@ -2881,9 +2940,11 @@ config_font_parse(const char *pattern, struct config_font *font)
}
void
config_font_destroy(struct config_font *font)
config_font_list_destroy(struct config_font_list *font_list)
{
if (font == NULL)
return;
free(font->pattern);
for (size_t i = 0; i < font_list->count; i++)
free(font_list->arr[i].pattern);
free(font_list->arr);
font_list->count = 0;
font_list->arr = NULL;
}

View file

@ -15,6 +15,12 @@
#define DEFAULT_TERM "xterm-256color"
#endif
#define DEFINE_LIST(type) \
type##_list { \
size_t count; \
type *arr; \
}
enum conf_size_type {CONF_SIZE_PX, CONF_SIZE_CELLS};
struct config_font {
@ -22,7 +28,7 @@ struct config_font {
double pt_size;
int px_size;
};
typedef tll(struct config_font) config_font_list_t;
DEFINE_LIST(struct config_font);
struct config_key_modifiers {
bool shift;
@ -43,7 +49,7 @@ struct config_key_binding {
xkb_keysym_t sym;
struct config_binding_pipe pipe;
};
typedef tll(struct config_key_binding) config_key_binding_list_t;
DEFINE_LIST(struct config_key_binding);
struct config_mouse_binding {
enum bind_action_normal action;
@ -52,7 +58,7 @@ struct config_mouse_binding {
int count;
struct config_binding_pipe pipe;
};
typedef tll(struct config_mouse_binding) config_mouse_binding_list_t;
DEFINE_LIST(struct config_mouse_binding);
typedef tll(char *) config_override_t;
@ -89,7 +95,7 @@ struct config {
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware;
config_font_list_t fonts[4];
struct config_font_list fonts[4];
/* Custom font metrics (-1 = use real font metrics) */
struct pt_or_px line_height;
@ -184,8 +190,8 @@ struct config {
struct {
/* Bindings for "normal" mode */
config_key_binding_list_t key;
config_mouse_binding_list_t mouse;
struct config_key_binding_list key;
struct config_mouse_binding_list mouse;
/*
* Special modes
@ -193,10 +199,10 @@ struct config {
/* While searching (not - action to *start* a search is in the
* 'key' bindings above */
config_key_binding_list_t search;
struct config_key_binding_list search;
/* While showing URL jump labels */
config_key_binding_list_t url;
struct config_key_binding_list url;
} bindings;
struct {
@ -257,4 +263,4 @@ bool config_load(
void config_free(struct config conf);
bool config_font_parse(const char *pattern, struct config_font *font);
void config_font_destroy(struct config_font *font);
void config_font_list_destroy(struct config_font_list *font_list);

40
input.c
View file

@ -527,22 +527,36 @@ convert_key_binding(const struct seat *seat,
static void
convert_key_bindings(const struct config *conf, struct seat *seat)
{
tll_foreach(conf->bindings.key, it)
convert_key_binding(seat, &it->item, &seat->kbd.bindings.key);
for (size_t i = 0; i < conf->bindings.key.count; i++) {
const struct config_key_binding *binding = &conf->bindings.key.arr[i];
if (binding->action == BIND_ACTION_NONE)
continue;
convert_key_binding(seat, binding, &seat->kbd.bindings.key);
}
}
static void
convert_search_bindings(const struct config *conf, struct seat *seat)
{
tll_foreach(conf->bindings.search, it)
convert_key_binding(seat, &it->item, &seat->kbd.bindings.search);
for (size_t i = 0; i < conf->bindings.search.count; i++) {
const struct config_key_binding *binding = &conf->bindings.search.arr[i];
if (binding->action == BIND_ACTION_SEARCH_NONE)
continue;
convert_key_binding(seat, binding, &seat->kbd.bindings.search);
}
}
static void
convert_url_bindings(const struct config *conf, struct seat *seat)
{
tll_foreach(conf->bindings.url, it)
convert_key_binding(seat, &it->item, &seat->kbd.bindings.url);
for (size_t i = 0; i < conf->bindings.url.count; i++) {
const struct config_key_binding *binding = &conf->bindings.url.arr[i];
#if 0
if (binding->action == BIND_ACTION_URL_NONE)
continue;
#endif
convert_key_binding(seat, binding, &seat->kbd.bindings.url);
}
}
static void
@ -562,8 +576,12 @@ convert_mouse_binding(struct seat *seat,
static void
convert_mouse_bindings(const struct config *conf, struct seat *seat)
{
tll_foreach(conf->bindings.mouse, it)
convert_mouse_binding(seat, &it->item);
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
const struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i];
if (binding->action == BIND_ACTION_NONE)
continue;
convert_mouse_binding(seat, binding);
}
}
static void
@ -1924,9 +1942,11 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
else {
/* Seat does NOT have a keyboard - use mouse bindings *without* modifiers */
const struct config_mouse_binding *match = NULL;
const struct config *conf = seat->wayl->conf;
tll_foreach(seat->wayl->conf->bindings.mouse, it) {
const struct config_mouse_binding *binding = &it->item;
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
const struct config_mouse_binding *binding =
&conf->bindings.mouse.arr[i];
if (binding->button != button) {
/* Wrong button */

17
main.c
View file

@ -441,17 +441,22 @@ main(int argc, char *const *argv)
if (login_shell)
conf.login_shell = true;
if (tll_length(conf_fonts) > 0) {
for (size_t i = 0; i < ALEN(conf.fonts); i++) {
tll_foreach(conf.fonts[i], it)
config_font_destroy(&it->item);
tll_free(conf.fonts[i]);
}
for (size_t i = 0; i < ALEN(conf.fonts); i++)
config_font_list_destroy(&conf.fonts[i]);
struct config_font_list *font_list = &conf.fonts[0];
xassert(font_list->count == 0);
xassert(font_list->arr == NULL);
font_list->arr = xmalloc(
tll_length(conf_fonts) * sizeof(font_list->arr[0]));
tll_foreach(conf_fonts, it) {
struct config_font font;
if (!config_font_parse(it->item, &font)) {
LOG_ERR("%s: invalid font specification", it->item);
} else
tll_push_back(conf.fonts[0], font);
font_list->arr[font_list->count++] = font;
}
tll_free(conf_fonts);
}

View file

@ -808,11 +808,13 @@ font_loader_thread(void *_data)
static bool
reload_fonts(struct terminal *term)
{
const struct config *conf = term->conf;
const size_t counts[4] = {
tll_length(term->conf->fonts[0]),
tll_length(term->conf->fonts[1]),
tll_length(term->conf->fonts[2]),
tll_length(term->conf->fonts[3]),
conf->fonts[0].count,
conf->fonts[1].count,
conf->fonts[2].count,
conf->fonts[3].count,
};
/* Configure size (which may have been changed run-time) */
@ -820,8 +822,10 @@ reload_fonts(struct terminal *term)
for (size_t i = 0; i < 4; i++) {
names[i] = xmalloc(counts[i] * sizeof(names[i][0]));
size_t j = 0;
tll_foreach(term->conf->fonts[i], it) {
const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; j < font_list->count; j++) {
const struct config_font *font = &font_list->arr[j];
bool use_px_size = term->font_sizes[i][j].px_size > 0;
char size[64];
@ -835,12 +839,11 @@ reload_fonts(struct terminal *term)
snprintf(size, sizeof(size), ":size=%.2f",
term->font_sizes[i][j].pt_size * (double)scale);
size_t len = strlen(it->item.pattern) + strlen(size) + 1;
size_t len = strlen(font->pattern) + strlen(size) + 1;
names[i][j] = xmalloc(len);
strcpy(names[i][j], it->item.pattern);
strcpy(names[i][j], font->pattern);
strcat(names[i][j], size);
j++;
}
}
@ -937,11 +940,15 @@ reload_fonts(struct terminal *term)
static bool
load_fonts_from_conf(struct terminal *term)
{
const struct config *conf = term->conf;
for (size_t i = 0; i < 4; i++) {
size_t j = 0;
tll_foreach(term->conf->fonts[i], it) {
const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; i < font_list->count; j++) {
const struct config_font *font = &font_list->arr[j];
term->font_sizes[i][j++] = (struct config_font){
.pt_size = it->item.pt_size, .px_size = it->item.px_size};
.pt_size = font->pt_size, .px_size = font->px_size};
}
}
@ -1039,10 +1046,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.ptmx_buffers = tll_init(),
.ptmx_paste_buffers = tll_init(),
.font_sizes = {
xmalloc(sizeof(term->font_sizes[0][0]) * tll_length(conf->fonts[0])),
xmalloc(sizeof(term->font_sizes[1][0]) * tll_length(conf->fonts[1])),
xmalloc(sizeof(term->font_sizes[2][0]) * tll_length(conf->fonts[2])),
xmalloc(sizeof(term->font_sizes[3][0]) * tll_length(conf->fonts[3])),
xmalloc(sizeof(term->font_sizes[0][0]) * conf->fonts[0].count),
xmalloc(sizeof(term->font_sizes[1][0]) * conf->fonts[1].count),
xmalloc(sizeof(term->font_sizes[2][0]) * conf->fonts[2].count),
xmalloc(sizeof(term->font_sizes[3][0]) * conf->fonts[3].count),
},
.font_dpi = 0.,
.font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */
@ -1136,10 +1143,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
term_update_ascii_printer(term);
for (size_t i = 0; i < 4; i++) {
size_t j = 0;
tll_foreach(conf->fonts[i], it) {
const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; j < font_list->count; j++) {
const struct config_font *font = &font_list->arr[j];
term->font_sizes[i][j++] = (struct config_font){
.pt_size = it->item.pt_size, .px_size = it->item.px_size};
.pt_size = font->pt_size, .px_size = font->px_size};
}
}
term->font_line_height = conf->line_height;
@ -1716,8 +1724,12 @@ term_reset(struct terminal *term, bool hard)
static bool
term_font_size_adjust(struct terminal *term, double amount)
{
const struct config *conf = term->conf;
for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < tll_length(term->conf->fonts[i]); j++) {
const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; j < font_list->count; j++) {
double old_pt_size = term->font_sizes[i][j].pt_size;
/*