From 4a8b50603ede4a3735f426d5426134de051c9ef7 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sun, 29 Jan 2023 04:06:46 +0100 Subject: [PATCH] src/config/rcxml.c: allow clearing key/mouse bindings Fixes #567 --- docs/rc.xml | 7 ++++ include/config/keybind.h | 2 + include/config/mousebind.h | 1 + src/action.c | 10 +++-- src/config/keybind.c | 18 ++++++++- src/config/mousebind.c | 11 ++++++ src/config/rcxml.c | 77 ++++++++++++++++++++++++++++++++------ src/keyboard.c | 2 +- src/menu/menu.c | 5 ++- 9 files changed, 115 insertions(+), 18 deletions(-) diff --git a/docs/rc.xml b/docs/rc.xml index fe95e2f7..3bf2d116 100644 --- a/docs/rc.xml +++ b/docs/rc.xml @@ -23,6 +23,13 @@ + + + + diff --git a/include/config/keybind.h b/include/config/keybind.h index b11914ed..e6c2f324 100644 --- a/include/config/keybind.h +++ b/include/config/keybind.h @@ -28,4 +28,6 @@ struct keybind *keybind_create(const char *keybind); */ uint32_t parse_modifier(const char *symname); +bool keybind_the_same(struct keybind *a, struct keybind *b); + #endif /* __LABWC_KEYBIND_H */ diff --git a/include/config/mousebind.h b/include/config/mousebind.h index f7dec7de..b6335bdf 100644 --- a/include/config/mousebind.h +++ b/include/config/mousebind.h @@ -48,5 +48,6 @@ enum mouse_event mousebind_event_from_str(const char *str); uint32_t mousebind_button_from_str(const char *str, uint32_t *modifiers); enum direction mousebind_direction_from_str(const char *str, uint32_t *modifiers); struct mousebind *mousebind_create(const char *context); +bool mousebind_the_same(struct mousebind *a, struct mousebind *b); #endif /* __LABWC_MOUSEBIND_H */ diff --git a/src/action.c b/src/action.c index 09992ef1..5668a628 100644 --- a/src/action.c +++ b/src/action.c @@ -157,8 +157,14 @@ action_create(const char *action_name) wlr_log(WLR_ERROR, "action name not specified"); return NULL; } + + enum action_type action_type = action_type_from_str(action_name); + if (action_type == ACTION_TYPE_NONE) { + return NULL; + } + struct action *action = znew(*action); - action->type = action_type_from_str(action_name); + action->type = action_type; wl_list_init(&action->args); return action; } @@ -445,8 +451,6 @@ actions_run(struct view *activator, struct server *server, wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name); } break; - case ACTION_TYPE_NONE: - break; case ACTION_TYPE_INVALID: wlr_log(WLR_ERROR, "Not executing unknown action"); break; diff --git a/src/config/keybind.c b/src/config/keybind.c index 858e0207..5700c54c 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include #include #include #include @@ -26,13 +27,28 @@ parse_modifier(const char *symname) } } +bool +keybind_the_same(struct keybind *a, struct keybind *b) +{ + assert(a && b); + if (a->modifiers != b->modifiers || a->keysyms_len != b->keysyms_len) { + return false; + } + for (size_t i = 0; i < a->keysyms_len; i++) { + if (a->keysyms[i] != b->keysyms[i]) { + return false; + } + } + return true; +} + struct keybind * keybind_create(const char *keybind) { struct keybind *k = znew(*k); xkb_keysym_t keysyms[MAX_KEYSYMS]; gchar **symnames = g_strsplit(keybind, "-", -1); - for (int i = 0; symnames[i]; i++) { + for (size_t i = 0; symnames[i]; i++) { char *symname = symnames[i]; uint32_t modifier = parse_modifier(symname); if (modifier != 0) { diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 4c37ad5f..c578c46b 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -137,6 +137,17 @@ context_from_str(const char *str) return LAB_SSD_NONE; } +bool +mousebind_the_same(struct mousebind *a, struct mousebind *b) +{ + assert(a && b); + return a->context == b->context + && a->button == b->button + && a->direction == b->direction + && a->mouse_event == b->mouse_event + && a->modifiers == b->modifiers; +} + struct mousebind * mousebind_create(const char *context) { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index cbb0ca83..dd33750e 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -111,8 +111,10 @@ fill_keybind(char *nodename, char *content) "nodename: '%s' content: '%s'", nodename, content); } else if (!strcmp(nodename, "name.action")) { current_keybind_action = action_create(content); - wl_list_append(¤t_keybind->actions, - ¤t_keybind_action->link); + if (current_keybind_action) { + wl_list_append(¤t_keybind->actions, + ¤t_keybind_action->link); + } } else if (!current_keybind_action) { wlr_log(WLR_ERROR, "expect element first. " "nodename: '%s' content: '%s'", nodename, content); @@ -164,8 +166,10 @@ fill_mousebind(char *nodename, char *content) mousebind_event_from_str(content); } else if (!strcmp(nodename, "name.action")) { current_mousebind_action = action_create(content); - wl_list_append(¤t_mousebind->actions, - ¤t_mousebind_action->link); + if (current_mousebind_action) { + wl_list_append(¤t_mousebind->actions, + ¤t_mousebind_action->link); + } } else if (!current_mousebind_action) { wlr_log(WLR_ERROR, "expect element first. " "nodename: '%s' content: '%s'", nodename, content); @@ -683,20 +687,17 @@ load_default_mouse_bindings(void) } static void -merge_mouse_bindings(void) +deduplicate_mouse_bindings(void) { uint32_t replaced = 0; + uint32_t cleared = 0; struct mousebind *current, *tmp, *existing; wl_list_for_each_safe(existing, tmp, &rc.mousebinds, link) { wl_list_for_each_reverse(current, &rc.mousebinds, link) { if (existing == current) { break; } - if (existing->context == current->context - && existing->button == current->button - && existing->direction == current->direction - && existing->mouse_event == current->mouse_event - && existing->modifiers == current->modifiers) { + if (mousebind_the_same(existing, current)) { wl_list_remove(&existing->link); action_list_free(&existing->actions); free(existing); @@ -705,9 +706,54 @@ merge_mouse_bindings(void) } } } + wl_list_for_each_safe(current, tmp, &rc.mousebinds, link) { + if (wl_list_empty(¤t->actions)) { + wl_list_remove(¤t->link); + free(current); + cleared++; + } + } if (replaced) { wlr_log(WLR_DEBUG, "Replaced %u mousebinds", replaced); } + if (cleared) { + wlr_log(WLR_DEBUG, "Cleared %u mousebinds", cleared); + } +} + +static void +deduplicate_key_bindings(void) +{ + uint32_t replaced = 0; + uint32_t cleared = 0; + struct keybind *current, *tmp, *existing; + wl_list_for_each_safe(existing, tmp, &rc.keybinds, link) { + wl_list_for_each_reverse(current, &rc.keybinds, link) { + if (existing == current) { + break; + } + if (keybind_the_same(existing, current)) { + wl_list_remove(&existing->link); + action_list_free(&existing->actions); + free(existing); + replaced++; + break; + } + } + } + wl_list_for_each_safe(current, tmp, &rc.keybinds, link) { + if (wl_list_empty(¤t->actions)) { + wl_list_remove(¤t->link); + free(current); + cleared++; + } + } + if (replaced) { + wlr_log(WLR_DEBUG, "Replaced %u keybinds", replaced); + } + if (cleared) { + wlr_log(WLR_DEBUG, "Cleared %u keybinds", cleared); + } } static void @@ -723,8 +769,15 @@ post_processing(void) load_default_mouse_bindings(); } - /* Replace all earlier mousebindings by later ones */ - merge_mouse_bindings(); + /* + * Replace all earlier bindings by later ones + * and clear the ones with an empty action list. + * + * This is required so users are able to remove + * a default binding by using the "None" action. + */ + deduplicate_key_bindings(); + deduplicate_mouse_bindings(); if (!rc.font_activewindow.name) { rc.font_activewindow.name = xstrdup("sans"); diff --git a/src/keyboard.c b/src/keyboard.c index 1749aaf3..894ae639 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -81,7 +81,7 @@ static bool handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym) { struct keybind *keybind; - wl_list_for_each_reverse(keybind, &rc.keybinds, link) { + wl_list_for_each(keybind, &rc.keybinds, link) { if (modifiers ^ keybind->modifiers) { continue; } diff --git a/src/menu/menu.c b/src/menu/menu.c index cd463429..ecbeb50b 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -279,7 +279,10 @@ fill_item(char *nodename, char *content) */ } else if (!strcmp(nodename, "name.action")) { current_item_action = action_create(content); - wl_list_append(¤t_item->actions, ¤t_item_action->link); + if (current_item_action) { + wl_list_append(¤t_item->actions, + ¤t_item_action->link); + } } else if (!current_item_action) { wlr_log(WLR_ERROR, "expect element first. " "nodename: '%s' content: '%s'", nodename, content);