From 52edafe08cf904e1e7f1095736d9a7f640ac2de8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Oct 2012 17:01:13 +0200 Subject: [PATCH] conf: allow multiple grabs and keysyms per grab-option We allow users to specify grabs on the command-line or in config-options. We sometimes want multiple grabs to do the same. Therefore, this changes the grab-parser and handlers to allow this. Furthermore, this adds support for keysyms that are assembled of multiple syms. This isn't needed by any keymap, yet, but might be used in the future. Last but not least, you can also specify modifiers only now. This might be used by subsystems to use the default keysyms but let the user specify what modifiers should be used. However, be careful when specifying modifiers-only for normal grabs as this means the grab is executed everytime the modifier is reported with _any_ key. Signed-off-by: David Herrmann --- src/conf.c | 254 ++++++++++++++++++++++++++++-------------- src/conf.h | 24 +++- src/kmscon_conf.c | 24 ++-- src/kmscon_terminal.c | 16 +-- src/shl_misc.h | 103 +++++++++++++++++ src/wlt_main.c | 54 +++------ src/wlt_terminal.c | 32 +++--- src/wlt_theme.c | 4 +- 8 files changed, 347 insertions(+), 164 deletions(-) diff --git a/src/conf.c b/src/conf.c index 87cac59..44e70d4 100644 --- a/src/conf.c +++ b/src/conf.c @@ -45,8 +45,10 @@ void conf_free_value(struct conf_option *opt) { - if (*(void**)opt->mem != opt->def) + if (*(void**)opt->mem && *(void**)opt->mem != opt->def) { free(*(void**)opt->mem); + *(void**)opt->mem = NULL; + } } int conf_parse_bool(struct conf_option *opt, bool on, const char *arg) @@ -100,44 +102,12 @@ void conf_default_string(struct conf_option *opt) int conf_parse_string_list(struct conf_option *opt, bool on, const char *arg) { - unsigned int i; - unsigned int num, len, size, pos; - char **list, *off; + int ret; + char **list; - num = 0; - size = 0; - len = 0; - for (i = 0; arg[i]; ++i) { - if (arg[i] != ',') { - ++len; - continue; - } - - ++num; - size += len + 1; - len = 0; - } - - if (len > 0 || !i || (i > 0 && arg[i - 1] == ',')) { - ++num; - size += len + 1; - } - - list = malloc(sizeof(char*) * (num + 1) + size); - if (!list) - return -ENOMEM; - - off = (void*)(((char*)list) + (sizeof(char*) * (num + 1))); - i = 0; - for (pos = 0; pos < num; ++pos) { - list[pos] = off; - while (arg[i] && arg[i] != ',') - *off++ = arg[i++]; - if (arg[i]) - ++i; - *off++ = 0; - } - list[pos] = NULL; + ret = shl_split_string(arg, &list, NULL, ',', true); + if (ret) + return ret; opt->type->free(opt); *(void**)opt->mem = list; @@ -149,77 +119,193 @@ void conf_default_string_list(struct conf_option *opt) *(void**)opt->mem = opt->def; } -int conf_parse_grab(struct conf_option *opt, bool on, const char *arg) +static int parse_single_grab(char *arg, unsigned int *mods, + uint32_t *keysym, bool allow_mods) { - char *buf, *tmp, *start; - struct conf_grab grab, *gnew; + char *tmp, *start, *end; - memset(&grab, 0, sizeof(grab)); + tmp = arg; + do { + while (*tmp == ' ') + ++tmp; + if (!allow_mods) + break; + if (*tmp != '<') + break; - buf = strdup(arg); - if (!buf) - return -ENOMEM; - tmp = buf; - -next_mod: - if (*tmp == '<') { start = tmp; while (*tmp && *tmp != '>') ++tmp; if (*tmp != '>') { - log_error("missing '>' in grab '%s' near '%s'", - arg, start); - goto err_free; + log_error("missing '>' near '%s'", start); + return -EFAULT; } *tmp++ = 0; ++start; if (!strcasecmp(start, "shift")) { - grab.mods |= SHL_SHIFT_MASK; + *mods |= SHL_SHIFT_MASK; } else if (!strcasecmp(start, "lock")) { - grab.mods |= SHL_LOCK_MASK; + *mods |= SHL_LOCK_MASK; } else if (!strcasecmp(start, "control") || !strcasecmp(start, "ctrl")) { - grab.mods |= SHL_CONTROL_MASK; + *mods |= SHL_CONTROL_MASK; } else if (!strcasecmp(start, "alt")) { - grab.mods |= SHL_ALT_MASK; + *mods |= SHL_ALT_MASK; } else if (!strcasecmp(start, "logo")) { - grab.mods |= SHL_LOGO_MASK; + *mods |= SHL_LOGO_MASK; } else { - log_error("invalid modifier '%s' in grab '%s'", - start, arg); - goto err_free; + log_error("invalid modifier '%s'", start); + return -EFAULT; + } + } while (1); + + while (*tmp == ' ') + ++tmp; + + start = tmp; + end = start; + do { + while (*tmp && *tmp != ' ') + ++tmp; + end = tmp; + if (!*tmp) + break; + while (*tmp == ' ') + ++tmp; + } while (1); + + if (start == end) + return 0; + if (*end) + *end = 0; + + *keysym = xkb_keysym_from_name(start); + if (!*keysym) { + log_error("invalid key '%s'", start); + return -EFAULT; + } + + return 1; +} + +int conf_parse_grab(struct conf_option *opt, bool on, const char *arg) +{ + char **list, **keys; + unsigned int list_num, key_num, i, j, k, l; + int ret; + struct conf_grab *grab; + + ret = shl_split_string(arg, &list, &list_num, ',', false); + if (ret) + return ret; + + grab = malloc(sizeof(*grab)); + if (!grab) { + ret = -ENOMEM; + goto err_list; + } + memset(grab, 0, sizeof(*grab)); + + if (list_num) { + grab->mods = malloc(sizeof(*grab->mods) * list_num); + if (!grab->mods) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->mods, 0, sizeof(*grab->mods) * list_num); + + grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num); + if (!grab->num_syms) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num); + + grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num); + if (!grab->keysyms) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num); + } + + l = 0; + for (i = 0; i < list_num; ++i) { + ret = shl_split_string(list[i], &keys, &key_num, '+', false); + if (ret) + goto err_all; + if (!key_num) { + free(keys); + continue; } - goto next_mod; + grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l] * key_num)); + if (!grab->keysyms[l]) { + ret = -ENOMEM; + free(keys); + goto err_all; + } + + k = 0; + for (j = 0; j < key_num; ++j) { + ret = parse_single_grab(keys[j], &grab->mods[l], + &grab->keysyms[l][k], + j == 0); + if (ret < 0) { + log_error("cannot parse grab '%s' in '%s'", + list[i], arg); + free(keys); + goto err_all; + } + k += ret; + } + + free(keys); + if (!k) + continue; + grab->num_syms[l] = k; + ++l; + ++grab->num; } - if (!*tmp) { - log_error("missing key in grab '%s'", arg); - goto err_free; - } - - grab.keysym = xkb_keysym_from_name(tmp); - if (!grab.keysym) { - log_error("invalid key '%s' in grab '%s'", tmp, arg); - goto err_free; - } - - gnew = malloc(sizeof(*gnew)); - if (!gnew) - goto err_free; - memcpy(gnew, &grab, sizeof(*gnew)); - + free(list); opt->type->free(opt); - *(void**)opt->mem = gnew; - free(buf); - + *(void**)opt->mem = grab; return 0; -err_free: - free(buf); - return -EFAULT; +err_all: + for (i = 0; i < list_num; ++i) + free(grab->keysyms[i]); +err_grab: + free(grab->keysyms); + free(grab->num_syms); + free(grab->mods); + free(grab); +err_list: + free(list); + return ret; +} + +void conf_free_grab(struct conf_option *opt) +{ + struct conf_grab *grab; + unsigned int i; + + if (!*(void**)opt->mem || *(void**)opt->mem == opt->def) + return; + + grab = *(void**)opt->mem; + *(void**)opt->mem = NULL; + + for (i = 0; i < grab->num; ++i) + free(grab->keysyms[i]); + + free(grab->keysyms); + free(grab->num_syms); + free(grab->mods); + free(grab); } void conf_default_grab(struct conf_option *opt) @@ -265,7 +351,7 @@ const struct conf_type conf_string_list = { const struct conf_type conf_grab = { .flags = CONF_HAS_ARG, .parse = conf_parse_grab, - .free = conf_free_value, + .free = conf_free_grab, .set_default = conf_default_grab, }; diff --git a/src/conf.h b/src/conf.h index 3499ddf..3a65af6 100644 --- a/src/conf.h +++ b/src/conf.h @@ -35,14 +35,34 @@ #include #include +#include "shl_misc.h" /* parsed types */ struct conf_grab { - unsigned int mods; - uint32_t keysym; + unsigned int num; + unsigned int *mods; + unsigned int *num_syms; + uint32_t **keysyms; }; +static inline bool conf_grab_matches(const struct conf_grab *grab, + unsigned int ev_mods, + unsigned int ev_num_syms, + const uint32_t *ev_syms) +{ + return shl_grab_has_match(ev_mods, ev_num_syms, ev_syms, + grab->num, grab->mods, grab->num_syms, + grab->keysyms); +} + +#define CONF_SINGLE_GRAB(_mods, _sym) { \ + .num = 1, \ + .mods = (unsigned int[]) { (_mods) }, \ + .num_syms = (unsigned int[]) { 1 }, \ + .keysyms = (uint32_t*[]) { (uint32_t[]) { (_sym) } }, \ + } + /* configuration parser */ struct conf_type; diff --git a/src/kmscon_conf.c b/src/kmscon_conf.c index eda166b..5bd7e24 100644 --- a/src/kmscon_conf.c +++ b/src/kmscon_conf.c @@ -233,25 +233,17 @@ static int aftercheck_seats(struct conf_option *opt, int argc, char **argv, static char *def_seats[] = { "seat0", NULL }; -static struct conf_grab def_grab_scroll_up = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Up, -}; +static struct conf_grab def_grab_scroll_up = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Up); -static struct conf_grab def_grab_scroll_down = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Down, -}; +static struct conf_grab def_grab_scroll_down = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Down); -static struct conf_grab def_grab_page_up = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Prior, -}; +static struct conf_grab def_grab_page_up = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Prior); -static struct conf_grab def_grab_page_down = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Next, -}; +static struct conf_grab def_grab_page_down = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Next); struct conf_option options[] = { CONF_OPTION_BOOL('h', "help", aftercheck_help, &kmscon_conf.help, false), diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c index 3fc0259..a4a7a7d 100644 --- a/src/kmscon_terminal.c +++ b/src/kmscon_terminal.c @@ -322,29 +322,29 @@ static void input_event(struct uterm_input *input, if (!term->opened || !term->awake || ev->handled) return; - if (UTERM_INPUT_HAS_MODS(ev, kmscon_conf.grab_scroll_up->mods) && - ev->keysyms[0] == kmscon_conf.grab_scroll_up->keysym) { + if (conf_grab_matches(kmscon_conf.grab_scroll_up, + ev->mods, ev->num_syms, ev->keysyms)) { tsm_screen_sb_up(term->console, 1); schedule_redraw(term); ev->handled = true; return; } - if (UTERM_INPUT_HAS_MODS(ev, kmscon_conf.grab_scroll_down->mods) && - ev->keysyms[0] == kmscon_conf.grab_scroll_down->keysym) { + if (conf_grab_matches(kmscon_conf.grab_scroll_down, + ev->mods, ev->num_syms, ev->keysyms)) { tsm_screen_sb_down(term->console, 1); schedule_redraw(term); ev->handled = true; return; } - if (UTERM_INPUT_HAS_MODS(ev, kmscon_conf.grab_page_up->mods) && - ev->keysyms[0] == kmscon_conf.grab_page_up->keysym) { + if (conf_grab_matches(kmscon_conf.grab_page_up, + ev->mods, ev->num_syms, ev->keysyms)) { tsm_screen_sb_page_up(term->console, 1); schedule_redraw(term); ev->handled = true; return; } - if (UTERM_INPUT_HAS_MODS(ev, kmscon_conf.grab_page_down->mods) && - ev->keysyms[0] == kmscon_conf.grab_page_down->keysym) { + if (conf_grab_matches(kmscon_conf.grab_page_down, + ev->mods, ev->num_syms, ev->keysyms)) { tsm_screen_sb_page_down(term->console, 1); schedule_redraw(term); ev->handled = true; diff --git a/src/shl_misc.h b/src/shl_misc.h index faaf74f..803e083 100644 --- a/src/shl_misc.h +++ b/src/shl_misc.h @@ -77,6 +77,69 @@ static inline int shl_dup(void **out, const void *data, size_t size) return 0; } +/* This parses \arg and splits the string into a new allocated array. The array + * is stored in \out and is NULL terminated. Empty entries are removed from the + * array if \keep_empty is false. \out_num is the number of entries in the + * array. You can set it to NULL to not retrieve this value. + * \sep is the separator character which must be a valid ASCII character, + * otherwise this will not be UTF8 safe. */ +static inline int shl_split_string(const char *arg, char ***out, + unsigned int *out_num, char sep, + bool keep_empty) +{ + unsigned int i; + unsigned int num, len, size, pos; + char **list, *off; + + if (!arg || !out || !sep) + return -EINVAL; + + num = 0; + size = 0; + len = 0; + for (i = 0; arg[i]; ++i) { + if (arg[i] != sep) { + ++len; + continue; + } + + if (keep_empty || len) { + ++num; + size += len + 1; + len = 0; + } + } + + if (len > 0 || (keep_empty && (!i || arg[i - 1] == sep))) { + ++num; + size += len + 1; + } + + list = malloc(sizeof(char*) * (num + 1) + size); + if (!list) + return -ENOMEM; + + off = (void*)(((char*)list) + (sizeof(char*) * (num + 1))); + i = 0; + for (pos = 0; pos < num; ) { + list[pos] = off; + while (arg[i] && arg[i] != sep) + *off++ = arg[i++]; + if (arg[i]) + ++i; + if (list[pos] == off && !keep_empty) + continue; + *off++ = 0; + pos++; + } + list[pos] = NULL; + + *out = list; + if (out_num) + *out_num = num; + return 0; +} + /* TODO: xkbcommon should provide these flags! * We currently copy them into each library API we use so we need to keep * them in sync. Currently, they're used in uterm-input and tsm-vte. */ @@ -142,4 +205,44 @@ static inline uint32_t shl_get_ascii(struct xkb_state *state, uint32_t keycode, return XKB_KEY_NoSymbol; } +static inline bool shl_grab_matches(unsigned int ev_mods, + unsigned int ev_num_syms, + const uint32_t *ev_syms, + unsigned int grab_mods, + unsigned int grab_num_syms, + const uint32_t *grab_syms) +{ + if (!SHL_HAS_BITS(ev_mods, grab_mods)) + return false; + + if (grab_num_syms != 0) { + if (ev_num_syms != grab_num_syms) + return false; + if (memcmp(ev_syms, grab_syms, sizeof(uint32_t) * ev_num_syms)) + return false; + } + + return true; +} + +static inline bool shl_grab_has_match(unsigned int ev_mods, + unsigned int ev_num_syms, + const uint32_t *ev_syms, + unsigned int grab_num, + const unsigned int *grab_mods, + const unsigned int *grab_num_syms, + uint32_t **grab_syms) +{ + unsigned int i; + + for (i = 0; i < grab_num; ++i) { + if (shl_grab_matches(ev_mods, ev_num_syms, ev_syms, + grab_mods[i], grab_num_syms[i], + grab_syms[i])) + return true; + } + + return false; +} + #endif /* SHL_MISC_H */ diff --git a/src/wlt_main.c b/src/wlt_main.c index 253c409..094383b 100644 --- a/src/wlt_main.c +++ b/src/wlt_main.c @@ -318,50 +318,32 @@ static int aftercheck_login(struct conf_option *opt, int argc, char **argv, return ret; } -static struct conf_grab def_grab_scroll_up = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Up, -}; +static struct conf_grab def_grab_scroll_up = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Up); -static struct conf_grab def_grab_scroll_down = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Down, -}; +static struct conf_grab def_grab_scroll_down = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Down); -static struct conf_grab def_grab_page_up = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Prior, -}; +static struct conf_grab def_grab_page_up = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Prior); -static struct conf_grab def_grab_page_down = { - .mods = SHL_SHIFT_MASK, - .keysym = XKB_KEY_Next, -}; +static struct conf_grab def_grab_page_down = + CONF_SINGLE_GRAB(SHL_SHIFT_MASK, XKB_KEY_Next); -static struct conf_grab def_grab_fullscreen = { - .mods = 0, - .keysym = XKB_KEY_F11, -}; +static struct conf_grab def_grab_fullscreen = + CONF_SINGLE_GRAB(0, XKB_KEY_F11); -static struct conf_grab def_grab_zoom_in = { - .mods = SHL_CONTROL_MASK, - .keysym = XKB_KEY_plus, -}; +static struct conf_grab def_grab_zoom_in = + CONF_SINGLE_GRAB(SHL_CONTROL_MASK, XKB_KEY_plus); -static struct conf_grab def_grab_zoom_out = { - .mods = SHL_CONTROL_MASK, - .keysym = XKB_KEY_minus, -}; +static struct conf_grab def_grab_zoom_out = + CONF_SINGLE_GRAB(SHL_CONTROL_MASK, XKB_KEY_minus); -static struct conf_grab def_grab_copy = { - .mods = SHL_LOGO_MASK, - .keysym = XKB_KEY_c, -}; +static struct conf_grab def_grab_copy = + CONF_SINGLE_GRAB(SHL_LOGO_MASK, XKB_KEY_c); -static struct conf_grab def_grab_paste = { - .mods = SHL_LOGO_MASK, - .keysym = XKB_KEY_v, -}; +static struct conf_grab def_grab_paste = + CONF_SINGLE_GRAB(SHL_LOGO_MASK, XKB_KEY_v); struct conf_option options[] = { CONF_OPTION_BOOL('h', "help", aftercheck_help, &wlt_conf.help, false), diff --git a/src/wlt_terminal.c b/src/wlt_terminal.c index 96b5bf0..0353a89 100644 --- a/src/wlt_terminal.c +++ b/src/wlt_terminal.c @@ -411,33 +411,33 @@ static bool widget_key(struct wlt_widget *widget, unsigned int mask, ucs4 = xkb_keysym_to_utf32(sym) ? : TSM_VTE_INVALID; - if (SHL_HAS_BITS(mask, wlt_conf.grab_scroll_up->mods) && - sym == wlt_conf.grab_scroll_up->keysym) { + if (conf_grab_matches(wlt_conf.grab_scroll_up, + mask, 1, &sym)) { tsm_screen_sb_up(term->scr, 1); wlt_window_schedule_redraw(term->wnd); return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_scroll_down->mods) && - sym == wlt_conf.grab_scroll_down->keysym) { + if (conf_grab_matches(wlt_conf.grab_scroll_down, + mask, 1, &sym)) { tsm_screen_sb_down(term->scr, 1); wlt_window_schedule_redraw(term->wnd); return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_page_up->mods) && - sym == wlt_conf.grab_page_up->keysym) { + if (conf_grab_matches(wlt_conf.grab_page_up, + mask, 1, &sym)) { tsm_screen_sb_page_up(term->scr, 1); wlt_window_schedule_redraw(term->wnd); return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_page_down->mods) && - sym == wlt_conf.grab_page_down->keysym) { + if (conf_grab_matches(wlt_conf.grab_page_down, + mask, 1, &sym)) { tsm_screen_sb_page_down(term->scr, 1); wlt_window_schedule_redraw(term->wnd); return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_zoom_in->mods) && - sym == wlt_conf.grab_zoom_in->keysym) { + if (conf_grab_matches(wlt_conf.grab_zoom_in, + mask, 1, &sym)) { if (term->font_attr.points + 1 < term->font_attr.points) return true; @@ -454,8 +454,8 @@ static bool widget_key(struct wlt_widget *widget, unsigned int mask, } return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_zoom_out->mods) && - sym == wlt_conf.grab_zoom_out->keysym) { + if (conf_grab_matches(wlt_conf.grab_zoom_out, + mask, 1, &sym)) { if (term->font_attr.points - 1 < 1) return true; @@ -473,8 +473,8 @@ static bool widget_key(struct wlt_widget *widget, unsigned int mask, return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_paste->mods) && - sym == wlt_conf.grab_paste->keysym) { + if (conf_grab_matches(wlt_conf.grab_paste, + mask, 1, &sym)) { if (term->paste) { log_debug("cannot paste selection, previous paste still in progress"); return true; @@ -505,8 +505,8 @@ static bool widget_key(struct wlt_widget *widget, unsigned int mask, return true; } - if (SHL_HAS_BITS(mask, wlt_conf.grab_copy->mods) && - sym == wlt_conf.grab_copy->keysym) { + if (conf_grab_matches(wlt_conf.grab_copy, + mask, 1, &sym)) { if (term->copy) { wl_data_source_destroy(term->copy); free(term->copy_buf); diff --git a/src/wlt_theme.c b/src/wlt_theme.c index 52b7d17..4872da7 100644 --- a/src/wlt_theme.c +++ b/src/wlt_theme.c @@ -539,8 +539,8 @@ static bool widget_key(struct wlt_widget *widget, unsigned int mask, if (handled || state != WL_KEYBOARD_KEY_STATE_PRESSED) return false; - if (SHL_HAS_BITS(mask, wlt_conf.grab_fullscreen->mods) && - sym == wlt_conf.grab_fullscreen->keysym) { + if (conf_grab_matches(wlt_conf.grab_fullscreen, + mask, 1, &sym)) { wlt_window_toggle_fullscreen(theme->wnd); return true; }