1c72fcc34Sopenharmony_ci#include <stdlib.h> 2c72fcc34Sopenharmony_ci#include <string.h> 3c72fcc34Sopenharmony_ci#include <stdint.h> 4c72fcc34Sopenharmony_ci#include "curskey.h" 5c72fcc34Sopenharmony_ci#include "utils.h" 6c72fcc34Sopenharmony_ci#include "mem.h" 7c72fcc34Sopenharmony_ci 8c72fcc34Sopenharmony_cistruct curskey_key { 9c72fcc34Sopenharmony_ci char *keyname; 10c72fcc34Sopenharmony_ci int keycode; 11c72fcc34Sopenharmony_ci}; 12c72fcc34Sopenharmony_ci 13c72fcc34Sopenharmony_cistatic struct curskey_key *keynames; 14c72fcc34Sopenharmony_cistatic unsigned int keynames_count; 15c72fcc34Sopenharmony_ci 16c72fcc34Sopenharmony_ciunsigned int meta_keycode_start; 17c72fcc34Sopenharmony_cistatic uint64_t invalid_meta_char_mask[2]; 18c72fcc34Sopenharmony_ci 19c72fcc34Sopenharmony_ci// Names for non-printable/whitespace characters and aliases for existing keys 20c72fcc34Sopenharmony_cistatic const struct curskey_key keyname_aliases[] = { 21c72fcc34Sopenharmony_ci // Sorted by `keyname` 22c72fcc34Sopenharmony_ci { "DEL", KEY_DEL }, 23c72fcc34Sopenharmony_ci { "DELETE", KEY_DC }, 24c72fcc34Sopenharmony_ci { "ENTER", '\n' }, 25c72fcc34Sopenharmony_ci { "ENTER", '\r' }, 26c72fcc34Sopenharmony_ci { "ESCAPE", KEY_ESCAPE }, 27c72fcc34Sopenharmony_ci { "INSERT", KEY_IC }, 28c72fcc34Sopenharmony_ci { "PAGEDOWN", KEY_NPAGE }, 29c72fcc34Sopenharmony_ci { "PAGEUP", KEY_PPAGE }, 30c72fcc34Sopenharmony_ci { "SPACE", KEY_SPACE }, 31c72fcc34Sopenharmony_ci { "TAB", KEY_TAB } 32c72fcc34Sopenharmony_ci}; 33c72fcc34Sopenharmony_ci 34c72fcc34Sopenharmony_ci#define STARTSWITH_KEY(S) \ 35c72fcc34Sopenharmony_ci ((name[0] == 'K' || name[0] == 'k') && \ 36c72fcc34Sopenharmony_ci (name[1] == 'E' || name[1] == 'e') && \ 37c72fcc34Sopenharmony_ci (name[2] == 'Y' || name[2] == 'y') && \ 38c72fcc34Sopenharmony_ci (name[3] == '_')) 39c72fcc34Sopenharmony_ci 40c72fcc34Sopenharmony_ci#define IS_META(S) \ 41c72fcc34Sopenharmony_ci ((S[0] == 'M' || S[0] == 'm' || S[0] == 'A' || S[0] == 'a') && S[1] == '-') 42c72fcc34Sopenharmony_ci 43c72fcc34Sopenharmony_cistatic int curskey_key_cmp(const void *a, const void *b) { 44c72fcc34Sopenharmony_ci return strcmp(((struct curskey_key*) a)->keyname, 45c72fcc34Sopenharmony_ci ((struct curskey_key*) b)->keyname); 46c72fcc34Sopenharmony_ci} 47c72fcc34Sopenharmony_ci 48c72fcc34Sopenharmony_cistatic int curskey_find(const struct curskey_key *table, unsigned int size, const char *name) { 49c72fcc34Sopenharmony_ci unsigned int start = 0; 50c72fcc34Sopenharmony_ci unsigned int end = size; 51c72fcc34Sopenharmony_ci unsigned int i; 52c72fcc34Sopenharmony_ci int cmp; 53c72fcc34Sopenharmony_ci 54c72fcc34Sopenharmony_ci while (1) { 55c72fcc34Sopenharmony_ci i = (start+end) / 2; 56c72fcc34Sopenharmony_ci cmp = strcasecmp(name, table[i].keyname); 57c72fcc34Sopenharmony_ci 58c72fcc34Sopenharmony_ci if (cmp == 0) 59c72fcc34Sopenharmony_ci return table[i].keycode; 60c72fcc34Sopenharmony_ci else if (end == start + 1) 61c72fcc34Sopenharmony_ci return ERR; 62c72fcc34Sopenharmony_ci else if (cmp > 0) 63c72fcc34Sopenharmony_ci start = i; 64c72fcc34Sopenharmony_ci else 65c72fcc34Sopenharmony_ci end = i; 66c72fcc34Sopenharmony_ci } 67c72fcc34Sopenharmony_ci} 68c72fcc34Sopenharmony_ci 69c72fcc34Sopenharmony_ci/* Translate the name of a ncurses KEY_ constant to its value. 70c72fcc34Sopenharmony_ci * "KEY_DOWN" -> 258 71c72fcc34Sopenharmony_ci * 72c72fcc34Sopenharmony_ci * Return ERR on failure. 73c72fcc34Sopenharmony_ci */ 74c72fcc34Sopenharmony_ciint curskey_keycode(const char *name) 75c72fcc34Sopenharmony_ci{ 76c72fcc34Sopenharmony_ci int i; 77c72fcc34Sopenharmony_ci 78c72fcc34Sopenharmony_ci if (! name) 79c72fcc34Sopenharmony_ci return ERR; 80c72fcc34Sopenharmony_ci 81c72fcc34Sopenharmony_ci if (STARTSWITH_KEY(name)) 82c72fcc34Sopenharmony_ci name += 4; 83c72fcc34Sopenharmony_ci 84c72fcc34Sopenharmony_ci if (name[0] == 'F' || name[0] == 'f') { 85c72fcc34Sopenharmony_ci i = (name[1] == '(' ? 2 : 1); 86c72fcc34Sopenharmony_ci 87c72fcc34Sopenharmony_ci if (name[i] >= '0' && name[i] <= '9') { 88c72fcc34Sopenharmony_ci i = atoi(name + i); 89c72fcc34Sopenharmony_ci if (i >= 1 && i <= 63) 90c72fcc34Sopenharmony_ci return KEY_F(i); 91c72fcc34Sopenharmony_ci } 92c72fcc34Sopenharmony_ci } 93c72fcc34Sopenharmony_ci 94c72fcc34Sopenharmony_ci i = curskey_find(keyname_aliases, ARRAY_SIZE(keyname_aliases), name); 95c72fcc34Sopenharmony_ci if (i != ERR) 96c72fcc34Sopenharmony_ci return i; 97c72fcc34Sopenharmony_ci 98c72fcc34Sopenharmony_ci return curskey_find(keynames, keynames_count, name); 99c72fcc34Sopenharmony_ci} 100c72fcc34Sopenharmony_ci 101c72fcc34Sopenharmony_cistatic void free_ncurses_keynames() { 102c72fcc34Sopenharmony_ci if (keynames) { 103c72fcc34Sopenharmony_ci while (keynames_count) 104c72fcc34Sopenharmony_ci free(keynames[--keynames_count].keyname); 105c72fcc34Sopenharmony_ci free(keynames); 106c72fcc34Sopenharmony_ci keynames = NULL; 107c72fcc34Sopenharmony_ci } 108c72fcc34Sopenharmony_ci} 109c72fcc34Sopenharmony_ci 110c72fcc34Sopenharmony_ci/* Create the list of ncurses KEY_ constants and their names. 111c72fcc34Sopenharmony_ci * Returns OK on success, ERR on failure. 112c72fcc34Sopenharmony_ci */ 113c72fcc34Sopenharmony_ciint create_ncurses_keynames() { 114c72fcc34Sopenharmony_ci int key; 115c72fcc34Sopenharmony_ci char *name; 116c72fcc34Sopenharmony_ci 117c72fcc34Sopenharmony_ci free_ncurses_keynames(); 118c72fcc34Sopenharmony_ci keynames = ccalloc(sizeof(struct curskey_key), (KEY_MAX - KEY_MIN)); 119c72fcc34Sopenharmony_ci 120c72fcc34Sopenharmony_ci for (key = KEY_MIN; key != KEY_MAX; ++key) { 121c72fcc34Sopenharmony_ci name = (char*) keyname(key); 122c72fcc34Sopenharmony_ci 123c72fcc34Sopenharmony_ci if (!name || !STARTSWITH_KEY(name)) 124c72fcc34Sopenharmony_ci continue; 125c72fcc34Sopenharmony_ci 126c72fcc34Sopenharmony_ci name += 4; 127c72fcc34Sopenharmony_ci if (name[0] == 'F' && name[1] == '(') 128c72fcc34Sopenharmony_ci continue; // ignore KEY_F(1),... 129c72fcc34Sopenharmony_ci 130c72fcc34Sopenharmony_ci keynames[keynames_count].keycode = key; 131c72fcc34Sopenharmony_ci keynames[keynames_count].keyname = cstrdup(name); 132c72fcc34Sopenharmony_ci ++keynames_count; 133c72fcc34Sopenharmony_ci } 134c72fcc34Sopenharmony_ci 135c72fcc34Sopenharmony_ci keynames = crealloc(keynames, keynames_count * sizeof(struct curskey_key)); 136c72fcc34Sopenharmony_ci qsort(keynames, keynames_count, sizeof(struct curskey_key), curskey_key_cmp); 137c72fcc34Sopenharmony_ci 138c72fcc34Sopenharmony_ci return OK; 139c72fcc34Sopenharmony_ci} 140c72fcc34Sopenharmony_ci 141c72fcc34Sopenharmony_ci/* Defines meta escape sequences in ncurses. 142c72fcc34Sopenharmony_ci * 143c72fcc34Sopenharmony_ci * Some combinations with meta/alt may not be available since they collide 144c72fcc34Sopenharmony_ci * with the prefix of a pre-defined key. 145c72fcc34Sopenharmony_ci * For example, keys F1 - F4 begin with "\eO", so ALT-O cannot be defined. 146c72fcc34Sopenharmony_ci * 147c72fcc34Sopenharmony_ci * Returns OK if meta keys are available, ERR otherwise. 148c72fcc34Sopenharmony_ci */ 149c72fcc34Sopenharmony_ciint curskey_define_meta_keys(unsigned int keycode_start) { 150c72fcc34Sopenharmony_ci#ifdef NCURSES_VERSION 151c72fcc34Sopenharmony_ci int ch; 152c72fcc34Sopenharmony_ci int keycode; 153c72fcc34Sopenharmony_ci int new_keycode = keycode_start; 154c72fcc34Sopenharmony_ci char key_sequence[3] = "\e "; 155c72fcc34Sopenharmony_ci 156c72fcc34Sopenharmony_ci invalid_meta_char_mask[0] = 0; 157c72fcc34Sopenharmony_ci invalid_meta_char_mask[1] = 0; 158c72fcc34Sopenharmony_ci 159c72fcc34Sopenharmony_ci for (ch = 0; ch <= CURSKEY_MAX_META_CHAR; ++ch) { 160c72fcc34Sopenharmony_ci key_sequence[1] = ch; 161c72fcc34Sopenharmony_ci keycode = key_defined(key_sequence); 162c72fcc34Sopenharmony_ci if (! keycode) { 163c72fcc34Sopenharmony_ci define_key(key_sequence, new_keycode); 164c72fcc34Sopenharmony_ci } 165c72fcc34Sopenharmony_ci else if (keycode == new_keycode) 166c72fcc34Sopenharmony_ci ; 167c72fcc34Sopenharmony_ci else 168c72fcc34Sopenharmony_ci invalid_meta_char_mask[ch/65] |= (1UL << (ch % 64)); 169c72fcc34Sopenharmony_ci 170c72fcc34Sopenharmony_ci ++new_keycode; 171c72fcc34Sopenharmony_ci } 172c72fcc34Sopenharmony_ci 173c72fcc34Sopenharmony_ci meta_keycode_start = keycode_start; 174c72fcc34Sopenharmony_ci return OK; 175c72fcc34Sopenharmony_ci#endif 176c72fcc34Sopenharmony_ci return ERR; 177c72fcc34Sopenharmony_ci} 178c72fcc34Sopenharmony_ci 179c72fcc34Sopenharmony_ci/* Return the keycode for a key with modifiers applied. 180c72fcc34Sopenharmony_ci * 181c72fcc34Sopenharmony_ci * Available modifiers are: 182c72fcc34Sopenharmony_ci * - CURSKEY_MOD_META / CURSKEY_MOD_ALT 183c72fcc34Sopenharmony_ci * - CURSKEY_MOD_CNTRL 184c72fcc34Sopenharmony_ci * 185c72fcc34Sopenharmony_ci * See also the macros curskey_meta_key(), curskey_cntrl_key(). 186c72fcc34Sopenharmony_ci * 187c72fcc34Sopenharmony_ci * Returns ERR if the modifiers cannot be applied to this key. 188c72fcc34Sopenharmony_ci */ 189c72fcc34Sopenharmony_ciint curskey_mod_key(int key, unsigned int modifiers) { 190c72fcc34Sopenharmony_ci if (modifiers & CURSKEY_MOD_CNTRL) { 191c72fcc34Sopenharmony_ci if ((key >= 'A' && key <= '_') || (key >= 'a' && key <= 'z') || key == ' ') 192c72fcc34Sopenharmony_ci key = key % 32; 193c72fcc34Sopenharmony_ci else 194c72fcc34Sopenharmony_ci return ERR; 195c72fcc34Sopenharmony_ci } 196c72fcc34Sopenharmony_ci 197c72fcc34Sopenharmony_ci if (modifiers & CURSKEY_MOD_META) { 198c72fcc34Sopenharmony_ci if (meta_keycode_start && 199c72fcc34Sopenharmony_ci (key >= 0 && key <= CURSKEY_MAX_META_CHAR) && 200c72fcc34Sopenharmony_ci ! (invalid_meta_char_mask[key/65] & (1UL << (key % 64)))) { 201c72fcc34Sopenharmony_ci key = meta_keycode_start + key; 202c72fcc34Sopenharmony_ci } 203c72fcc34Sopenharmony_ci else 204c72fcc34Sopenharmony_ci return ERR; 205c72fcc34Sopenharmony_ci } 206c72fcc34Sopenharmony_ci 207c72fcc34Sopenharmony_ci return key; 208c72fcc34Sopenharmony_ci} 209c72fcc34Sopenharmony_ci 210c72fcc34Sopenharmony_ci/* Return the ncurses keycode for a key definition. 211c72fcc34Sopenharmony_ci * 212c72fcc34Sopenharmony_ci * Key definition may be: 213c72fcc34Sopenharmony_ci * - Single character (a, z, ...) 214c72fcc34Sopenharmony_ci * - Character with control-modifier (^x, C-x, c-x, ...) 215c72fcc34Sopenharmony_ci * - Character with meta/alt-modifier (M-x, m-x, A-x, a-x, ...) 216c72fcc34Sopenharmony_ci * - Character with both modifiers (C-M-x, M-C-x, M-^x, ...) 217c72fcc34Sopenharmony_ci * - Curses keyname, no modifiers allowed (KEY_HOME, HOME, F1, F(1), ...) 218c72fcc34Sopenharmony_ci * 219c72fcc34Sopenharmony_ci * Returns ERR if either 220c72fcc34Sopenharmony_ci * - The key definition is NULL or empty 221c72fcc34Sopenharmony_ci * - The key could not be found ("KEY_FOO") 222c72fcc34Sopenharmony_ci * - The key combination is invalid in general ("C-TAB", "C-RETURN") 223c72fcc34Sopenharmony_ci * - The key is invalid because of compile time options (the 224c72fcc34Sopenharmony_ci * `define_key()` function was not available.) 225c72fcc34Sopenharmony_ci * - The key is invalid because it could not be defined by 226c72fcc34Sopenharmony_ci * curskey_define_meta_keys() 227c72fcc34Sopenharmony_ci */ 228c72fcc34Sopenharmony_ciint curskey_parse(const char *def) { 229c72fcc34Sopenharmony_ci int c; 230c72fcc34Sopenharmony_ci unsigned int mod = 0; 231c72fcc34Sopenharmony_ci 232c72fcc34Sopenharmony_ci if (! def) 233c72fcc34Sopenharmony_ci return ERR; 234c72fcc34Sopenharmony_ci 235c72fcc34Sopenharmony_ci for (;;) { 236c72fcc34Sopenharmony_ci if (def[0] == '^' && def[1] != '\0') { 237c72fcc34Sopenharmony_ci ++def; 238c72fcc34Sopenharmony_ci mod |= CURSKEY_MOD_CNTRL; 239c72fcc34Sopenharmony_ci } 240c72fcc34Sopenharmony_ci else if ((def[0] == 'C' || def[0] == 'c') && def[1] == '-') { 241c72fcc34Sopenharmony_ci def += 2; 242c72fcc34Sopenharmony_ci mod |= CURSKEY_MOD_CNTRL; 243c72fcc34Sopenharmony_ci } 244c72fcc34Sopenharmony_ci else if (IS_META(def)) { 245c72fcc34Sopenharmony_ci if (! meta_keycode_start) 246c72fcc34Sopenharmony_ci return ERR; 247c72fcc34Sopenharmony_ci def += 2; 248c72fcc34Sopenharmony_ci mod |= CURSKEY_MOD_ALT; 249c72fcc34Sopenharmony_ci } 250c72fcc34Sopenharmony_ci else 251c72fcc34Sopenharmony_ci break; 252c72fcc34Sopenharmony_ci } 253c72fcc34Sopenharmony_ci 254c72fcc34Sopenharmony_ci if (*def == '\0') 255c72fcc34Sopenharmony_ci return ERR; 256c72fcc34Sopenharmony_ci else if (*(def+1) == '\0') 257c72fcc34Sopenharmony_ci c = *def; 258c72fcc34Sopenharmony_ci else 259c72fcc34Sopenharmony_ci c = curskey_keycode(def); 260c72fcc34Sopenharmony_ci 261c72fcc34Sopenharmony_ci return curskey_mod_key(c, mod); 262c72fcc34Sopenharmony_ci} 263c72fcc34Sopenharmony_ci 264c72fcc34Sopenharmony_ci/* Initialize curskey. 265c72fcc34Sopenharmony_ci * Returns OK on success, ERR on failure. */ 266c72fcc34Sopenharmony_ciint curskey_init() { 267c72fcc34Sopenharmony_ci keypad(stdscr, TRUE); 268c72fcc34Sopenharmony_ci return create_ncurses_keynames(); 269c72fcc34Sopenharmony_ci} 270c72fcc34Sopenharmony_ci 271c72fcc34Sopenharmony_ci/* Destroy curskey. */ 272c72fcc34Sopenharmony_civoid curskey_destroy() { 273c72fcc34Sopenharmony_ci free_ncurses_keynames(); 274c72fcc34Sopenharmony_ci} 275