>From a2918c311cb27e958740cfc9413a363f2a6cc5ad Mon Sep 17 00:00:00 2001 From: Brand Huntsman Date: Tue, 10 Dec 2019 16:45:11 -0700 Subject: [PATCH] refactor key bind parsing and support meta-shift keys Signed-off-by: Brand Huntsman --- src/global.c | 82 ++++++++++++++++++++++++++++++++++++---------------- src/nano.c | 2 ++ src/proto.h | 8 +++-- src/rcfile.c | 36 ++++++++--------------- src/winio.c | 7 +++-- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/src/global.c b/src/global.c index d0ab509b..4edf4cff 100644 --- a/src/global.c +++ b/src/global.c @@ -382,6 +382,8 @@ void add_to_sclist(int menus, const char *scstring, const int keycode, static int counter = 0; #endif keystruct *s = nmalloc(sizeof(keystruct)); + bool meta, shift, ctrl; + const char *k = scstring; /* Start the list, or tack on the next item. */ if (sclist == NULL) @@ -399,7 +401,8 @@ void add_to_sclist(int menus, const char *scstring, const int keycode, if (toggle) s->ordinal = (tailsc->toggle == toggle) ? counter : ++counter; #endif - assign_keyinfo(s, scstring, keycode); + key_modifiers_from_string(&meta, &shift, &ctrl, &k); + assign_keyinfo(s, meta, shift, ctrl, k, scstring, keycode); tailsc = s; } @@ -483,34 +486,44 @@ functionptrtype func_from_key(int *kbinput) /* Parse the given keystring and return the corresponding keycode, * or return -1 when the string is invalid. */ -int keycode_from_string(const char *keystring) +int keycode_from_string(bool meta, bool shift, bool ctrl, const char *keystring) { - if (keystring[0] == '^') { - if (strcasecmp(keystring, "^Space") == 0) + if (keystring[0] == 'F' && keystring[1] != '\0') { + int fn = atoi(&keystring[1]); + if (fn < 1 || fn > 24) + return -1; + if (meta || shift || ctrl) + return -1; + return KEY_F0 + fn; + } else if (ctrl) { + if (meta || shift) + return -1; + if (strcasecmp(keystring, "Space") == 0) return 0; #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) - if (strcasecmp(keystring, "^H") == 0) + if (strcasecmp(keystring, "H") == 0) return KEY_BACKSPACE; #endif - if (keystring[1] == '/' && strlen(keystring) == 2) + if (keystring[0] == '/' && strlen(keystring) == 1) return 31; - if (keystring[1] <= '_' && strlen(keystring) == 2) - return keystring[1] - 64; - else - return -1; - } else if (keystring[0] == 'M') { - if (strcasecmp(keystring, "M-Space") == 0) + if (keystring[0] <= '_' && strlen(keystring) == 1) + return keystring[0] - 64; + return -1; + } else if (meta) { + if (!shift && strcasecmp(keystring, "Space") == 0) return (int)' '; - if (keystring[1] == '-' && strlen(keystring) == 3) - return tolower((unsigned char)keystring[2]); - else - return -1; - } else if (keystring[0] == 'F') { - int fn = atoi(&keystring[1]); - if (fn < 1 || fn > 24) - return -1; - return KEY_F0 + fn; - } else if (!strcasecmp(keystring, "Ins")) + if (strlen(keystring) == 1) { + if (shift) { + /* Only allow keys with upper and lower representations. */ + int lower = tolower((unsigned char)keystring[0]); + return (lower == (int)keystring[0] ? -1 : (int)keystring[0]); + } + return tolower((unsigned char)keystring[0]); + } + return -1; + } else if (shift) + return -1; + else if (!strcasecmp(keystring, "Ins")) return KEY_IC; else if (!strcasecmp(keystring, "Del")) return KEY_DC; @@ -518,16 +531,35 @@ int keycode_from_string(const char *keystring) return -1; } +/* Parse key modifiers and the unmodified key. */ +void key_modifiers_from_string(bool *meta, bool *shift, bool *ctrl, + const char **keystring) +{ + const char *k = *keystring; + + if ((*meta = (k[0] != '\0' && k[1] == '-' && (k[0] | 32) == 'm'))) + k += 2; + + if ((*shift = (k[0] != '\0' && k[1] == '-' && (k[0] | 32) == 's'))) + k += 2; + + if ((*ctrl = (k[0] == '^'))) + k++; + + *keystring = k; +} + /* Set the string and its corresponding keycode for the given shortcut s. */ -void assign_keyinfo(keystruct *s, const char *keystring, const int keycode) +void assign_keyinfo(keystruct *s, bool meta, bool shift, bool ctrl, + const char *key, const char *keystring, const int keycode) { s->keystr = keystring; - s->meta = (keystring[0] == 'M' && keycode == 0); + s->meta = (meta && keycode == 0); if (keycode) s->keycode = keycode; else - s->keycode = keycode_from_string(keystring); + s->keycode = keycode_from_string(meta, shift, ctrl, key); } /* These two tags are used elsewhere too, so they are global. */ diff --git a/src/nano.c b/src/nano.c index 25a070b4..277a3af2 100644 --- a/src/nano.c +++ b/src/nano.c @@ -1540,6 +1540,8 @@ void unbound_key(int code) else if (meta_key) { if (code == '[') statusline(ALERT, _("Unbindable key: M-[")); + else if (toupper(code) == code) + statusline(ALERT, _("Unbound key: M-S-%c"), code); else statusline(ALERT, _("Unbound key: M-%c"), toupper(code)); } else if (code == ESC_CODE) diff --git a/src/proto.h b/src/proto.h index 8e5b4ec0..da4c3cb8 100644 --- a/src/proto.h +++ b/src/proto.h @@ -324,8 +324,12 @@ int the_code_for(void (*func)(void), int defaultval); size_t shown_entries_for(int menu); const keystruct *get_shortcut(int *kbinput); functionptrtype func_from_key(int *kbinput); -int keycode_from_string(const char *keystring); -void assign_keyinfo(keystruct *s, const char *keystring, const int keycode); +int keycode_from_string(bool meta, bool shift, bool ctrl, + const char *keystring); +void key_modifiers_from_string(bool *meta, bool *shift, bool *ctrl, + const char **keystring); +void assign_keyinfo(keystruct *s, bool meta, bool shift, bool ctrl, + const char *key, const char *keystring, const int keycode); void shortcut_init(void); const char *flagtostr(int flag); #ifdef ENABLE_NANORC diff --git a/src/rcfile.c b/src/rcfile.c index 469c4ce2..be376bcd 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -403,9 +403,10 @@ bool is_universal(void (*func)(void)) /* Bind or unbind a key combo, to or from a function. */ void parse_binding(char *ptr, bool dobind) { - char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL; + char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL, *k; keystruct *s, *newsc = NULL; - int menu, mask = 0; + int menu, mask = 0, keycode; + bool meta, shift, ctrl; funcstruct *f; check_for_nonempty_syntax(); @@ -417,33 +418,20 @@ void parse_binding(char *ptr, bool dobind) keyptr = ptr; ptr = parse_next_word(ptr); - keycopy = copy_of(keyptr); + k = keycopy = copy_of(keyptr); - if (strlen(keycopy) < 2) { + key_modifiers_from_string(&meta, &shift, &ctrl, (const char **)&k); + + if (k[0] == '\0') { jot_error(N_("Key name is too short")); goto free_things; } - /* Uppercase only the first two or three characters of the key name. */ - keycopy[0] = toupper((unsigned char)keycopy[0]); - keycopy[1] = toupper((unsigned char)keycopy[1]); - if (keycopy[0] == 'M' && keycopy[1] == '-') { - if (strlen(keycopy) > 2) - keycopy[2] = toupper((unsigned char)keycopy[2]); - else { - jot_error(N_("Key name is too short")); - goto free_things; - } - } + /* Uppercase the first character of the key name. */ + k[0] = toupper((unsigned char)k[0]); - /* Allow the codes for Insert and Delete to be rebound, but apart - * from those two only Control, Meta and Function sequences. */ - if (!strcasecmp(keycopy, "Ins") || !strcasecmp(keycopy, "Del")) - keycopy[1] = tolower((unsigned char)keycopy[1]); - else if (keycopy[0] != '^' && keycopy[0] != 'M' && keycopy[0] != 'F') { - jot_error(N_("Key name must begin with \"^\", \"M\", or \"F\"")); - goto free_things; - } else if (keycode_from_string(keycopy) < 0) { + keycode = keycode_from_string(meta, shift, ctrl, k); + if (keycode < 0) { jot_error(N_("Key name %s is invalid"), keycopy); goto free_things; } @@ -528,7 +516,7 @@ void parse_binding(char *ptr, bool dobind) } newsc->menus = menu; - assign_keyinfo(newsc, keycopy, 0); + assign_keyinfo(newsc, meta, shift, ctrl, k, keycopy, 0); /* Disallow rebinding ^[ and frequent escape-sequence starter "Esc [". */ if ((!newsc->meta && newsc->keycode == ESC_CODE) || diff --git a/src/winio.c b/src/winio.c index b702a30c..4caa687c 100644 --- a/src/winio.c +++ b/src/winio.c @@ -358,6 +358,7 @@ int parse_kbinput(WINDOW *win) static int escapes = 0; static bool double_esc = FALSE; int *kbinput, keycode, retval = ERR; + bool shiftable = TRUE; meta_key = FALSE; shift_held = FALSE; @@ -403,7 +404,8 @@ int parse_kbinput(WINDOW *win) * meta key sequence mode. */ if (!solitary || (keycode >= 0x20 && keycode < 0x7F)) meta_key = TRUE; - retval = tolower(keycode); + retval = keycode; + shiftable = FALSE; } else /* One escape followed by a non-escape, and there * are more codes waiting: escape sequence mode. */ @@ -611,7 +613,8 @@ int parse_kbinput(WINDOW *win) if (modifiers & 0x01) { if (retval == TAB_CODE) return SHIFT_TAB; - shift_held = TRUE; + if (shiftable) + shift_held = TRUE; } /* Is Alt being held? */ if (modifiers == 0x08) { -- 2.23.0