diff options
author | pommicket <pommicket@gmail.com> | 2023-01-03 15:38:54 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2023-01-03 15:38:54 -0500 |
commit | baae904ed2c97ba5c701442ec5ef6900715f785d (patch) | |
tree | 6e6c10265624510dd73bb8bd0213a59fc02e3617 | |
parent | c37d493327ded5952f46cbf39aef920e47c1d7d9 (diff) |
switch from scancodes to keycodes
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | config.c | 112 | ||||
-rw-r--r-- | ds.h | 8 | ||||
-rw-r--r-- | main.c | 12 | ||||
-rw-r--r-- | ted.c | 25 | ||||
-rw-r--r-- | ted.cfg | 12 | ||||
-rw-r--r-- | ted.h | 15 | ||||
-rw-r--r-- | util.c | 4 | ||||
-rw-r--r-- | util.h | 2 |
9 files changed, 102 insertions, 89 deletions
@@ -79,6 +79,7 @@ explaining what they do. Keyboard shortcuts are of the form `key combo = action`, where `action` is an argument (number or string), followed by a command. The commands match the things in the command palette (Ctrl+Shift+p), but `:` is added to the beginning to make it clear it's a command. +A list of key names can be found [here](https://wiki.libsdl.org/SDL2/SDL_Keycode). Colors are formatted like `#rgb`, `#rgba`, `#rrggbb` or `#rrggbbaa`, where r, g, b, and a are red, green, blue, and alpha (transparency/opacity). You can use a [color picker](https://www.google.com/search?q=color+picker) to help you out. @@ -162,6 +162,7 @@ static void config_err(ConfigReader *cfg, const char *fmt, ...) { va_start(args, fmt); vsnprintf(error + strlen(error), sizeof error - strlen(error) - 1, fmt, args); va_end(args); + ted_seterr(cfg->ted, "%s", error); } static void context_copy(SettingsContext *dest, const SettingsContext *src) { @@ -221,6 +222,7 @@ static void settings_copy(Settings *dest, const Settings *src) { if (src->language_extensions[i]) dest->language_extensions[i] = str_dup(src->language_extensions[i]); } + dest->key_actions = arr_copy(src->key_actions); } static void context_free(SettingsContext *ctx) { @@ -265,83 +267,35 @@ static u32 config_parse_key_combo(ConfigReader *cfg, const char *str) { } // read key - SDL_Scancode scancode = SDL_GetScancodeFromName(str); - if (scancode == SDL_SCANCODE_UNKNOWN) { + SDL_Keycode keycode = SDL_GetKeyFromName(str); + if (keycode == SDLK_UNKNOWN) { typedef struct { const char *keyname1; const char *keyname2; // alternate key name - SDL_Scancode scancode; - bool shift; + SDL_Keycode keycode; } KeyName; static KeyName const key_names[] = { - {"Apostrophe", "Single Quote", SDL_SCANCODE_APOSTROPHE, 0}, - {"Backslash", 0, SDL_SCANCODE_BACKSLASH, 0}, - {"Comma", 0, SDL_SCANCODE_COMMA, 0}, - {"Equals", 0, SDL_SCANCODE_EQUALS, 0}, - {"Grave", "Backtick", SDL_SCANCODE_GRAVE, 0}, - {"Keypad Plus", 0, SDL_SCANCODE_KP_PLUS, 0}, - {"Keypad Minus", 0, SDL_SCANCODE_KP_MINUS, 0}, - {"Keypad Divide", 0, SDL_SCANCODE_KP_DIVIDE, 0}, - {"Keypad Multiply", 0, SDL_SCANCODE_KP_MULTIPLY, 0}, - {"Left Bracket", 0, SDL_SCANCODE_LEFTBRACKET, 0}, // [ - {"Right Bracket", 0, SDL_SCANCODE_RIGHTBRACKET, 0}, // ] - {"Dash", 0, SDL_SCANCODE_MINUS, 0}, - {"Minus", 0, SDL_SCANCODE_MINUS, 0}, - {"Period", 0, SDL_SCANCODE_PERIOD, 0}, - {"Semicolon", 0, SDL_SCANCODE_SEMICOLON, 0}, - {"Slash", 0, SDL_SCANCODE_SLASH, 0}, - {"Enter", 0, SDL_SCANCODE_RETURN, 0}, - {"Keypad Return", 0, SDL_SCANCODE_KP_ENTER, 0}, - {"Exclaim", "Exclamation Mark", SDL_SCANCODE_1, 1}, - {"!", 0, SDL_SCANCODE_1, 1}, - {"At", "@", SDL_SCANCODE_2, 1}, - {"Hash", "#", SDL_SCANCODE_3, 1}, - {"Dollar", "$", SDL_SCANCODE_4, 1}, - {"Percent", "%", SDL_SCANCODE_5, 1}, - {"Caret", "^", SDL_SCANCODE_6, 1}, - {"Ampersand", "&", SDL_SCANCODE_7, 1}, - {"Asterisk", "*", SDL_SCANCODE_8, 1}, - {"Left Paren", "(", SDL_SCANCODE_9, 1}, - {"Right Paren", ")", SDL_SCANCODE_0, 1}, - {"Underscore", "_", SDL_SCANCODE_MINUS, 1}, - {"Plus", "+", SDL_SCANCODE_EQUALS, 1}, - {"Left Brace", "{", SDL_SCANCODE_LEFTBRACKET, 1}, - {"Right Brace", "}", SDL_SCANCODE_RIGHTBRACKET, 1}, - {"Pipe", "|", SDL_SCANCODE_BACKSLASH, 1}, - {"Colon", ":", SDL_SCANCODE_SEMICOLON, 1}, - {"Double Quote", "\"", SDL_SCANCODE_APOSTROPHE, 1}, - {"Less Than", "<", SDL_SCANCODE_COMMA, 1}, - {"Greater Than", ">", SDL_SCANCODE_PERIOD, 1}, - {"Question Mark", "?", SDL_SCANCODE_SLASH, 1}, - {"Question", 0, SDL_SCANCODE_SLASH, 1}, - {"Tilde", "~", SDL_SCANCODE_GRAVE, 1}, - {"X1", "x1", SCANCODE_MOUSE_X1, 0}, - {"X2", "x2", SCANCODE_MOUSE_X2, 0} + {"X1", NULL, KEYCODE_X1}, + {"X2", NULL, KEYCODE_X2}, + {"Enter", NULL, SDLK_RETURN}, + {"Equals", "Equal", SDLK_EQUALS}, }; - // @TODO(optimize): sort key_names (and split keyname1/2); do a binary search for (size_t i = 0; i < arr_count(key_names); ++i) { KeyName const *k = &key_names[i]; - if (streq(str, k->keyname1) || (k->keyname2 && streq(str, k->keyname2))) { - scancode = k->scancode; - if (k->shift) { - if (modifier & KEY_MODIFIER_SHIFT) { - config_err(cfg, "Shift+%s is redundant.", str); - return 0; - } - modifier |= KEY_MODIFIER_SHIFT; - } + if (streq_case_insensitive(str, k->keyname1) || (k->keyname2 && streq_case_insensitive(str, k->keyname2))) { + keycode = k->keycode; break; } } - if (scancode == SDL_SCANCODE_UNKNOWN) { - if (isdigit(str[0])) { // direct scancode numbers, e.g. Ctrl+24 or Ctrl+08 + if (keycode == SDLK_UNKNOWN) { + if (isdigit(str[0])) { // direct keycode numbers, e.g. Ctrl+24 or Ctrl+08 char *endp; long n = strtol(str, &endp, 10); - if (*endp == '\0' && n > 0 && n < SCANCODE_COUNT) { - scancode = (SDL_Scancode)n; + if (*endp == '\0' && n > 0) { + keycode = (SDL_Keycode)n; } else { - config_err(cfg, "Invalid scancode number: %s", str); + config_err(cfg, "Invalid keycode number: %s", str); return 0; } } else { @@ -350,7 +304,7 @@ static u32 config_parse_key_combo(ConfigReader *cfg, const char *str) { } } } - return (u32)scancode << 3 | modifier; + return KEY_COMBO(modifier, keycode); } @@ -767,7 +721,9 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi config_err(cfg, "'%s' is not a valid color. Colors should look like #rgb, #rgba, #rrggbb, or #rrggbbaa.", value); } } else { - #if DEBUG + // don't actually produce this error. + // we have removed colors in the past and might again in the future. + #if 0 config_err(cfg, "No such color setting: %s", key); #endif } @@ -775,7 +731,18 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi case SECTION_KEYBOARD: { // lines like Ctrl+Down = 10 :down u32 key_combo = config_parse_key_combo(cfg, key); - KeyAction *action = &settings->key_actions[key_combo]; + KeyAction *action = NULL; + // check if we already have an action for this key combo + arr_foreach_ptr(settings->key_actions, KeyAction, act) { + if (act->key_combo == key_combo) { + action = act; + break; + } + } + // if this is a new key combo, add an element to the key_actions array + if (!action) + action = arr_addp(settings->key_actions); + action->key_combo = key_combo; llong argument = 1; // default argument = 1 if (isdigit(*value)) { // read the argument @@ -948,6 +915,15 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi } } +static int key_action_qsort_cmp_combo(const void *av, const void *bv) { + const KeyAction *a = av, *b = bv; + if (a->key_combo < b->key_combo) + return -1; + if (a->key_combo > b->key_combo) + return 1; + return 0; +} + void config_parse(Ted *ted, ConfigPart **pparts) { config_init_settings(); @@ -1020,6 +996,11 @@ void config_parse(Ted *ted, ConfigPart **pparts) { } arr_clear(*pparts); + + arr_foreach_ptr(ted->all_settings, Settings, s) { + // sort key_actions by key_combo. + arr_qsort(s->key_actions, key_action_qsort_cmp_combo); + } } static int gluint_cmp(const void *av, const void *bv) { @@ -1056,6 +1037,7 @@ void config_free(Ted *ted) { free(settings->language_extensions[i]); gl_rc_sab_decref(&settings->bg_shader); gl_rc_texture_decref(&settings->bg_texture); + arr_free(settings->key_actions); } @@ -192,6 +192,13 @@ static void *arr_remove_(void *arr, size_t member_size, size_t index) { } } +static void *arr_copy_(const void *arr, size_t member_size) { + void *new_arr = NULL; + arr_set_len_(&new_arr, member_size, arr_len(arr)); + memcpy(new_arr, arr, member_size * arr_len(arr)); + return new_arr; +} + #ifdef __cplusplus #define arr_cast_typeof(a) (decltype(a)) #elif defined __GNUC__ @@ -224,6 +231,7 @@ static void *arr_remove_(void *arr, size_t member_size, size_t index) { #define arr_pop_last(a) ((a)[--arr_hdr_(a)->len]) #define arr_size_in_bytes(a) (arr_len(a) * sizeof *(a)) #define arr_lastp(a) ((a) ? &(a)[arr_len(a)-1] : NULL) +#define arr_copy(a) arr_cast_typeof(a) arr_copy_((a), sizeof *(a)) #define arr_foreach_ptr_end(a, type, var, end) type *end = (a) + arr_len(a); \ for (type *var = (a); var != end; ++var) // Iterate through each element of the array, setting var to a pointer to the element. @@ -1,7 +1,6 @@ /* @TODO: - rename ted_seterr to ted_error -- use keycodes instead of scancodes (maybe that will make numlock modifier no longer necessary) - go to declaration with LSP - ted.h documentation - handle multiple symbols with same name in go-to-definition menu @@ -30,7 +29,8 @@ - bad json can give "Unexpected error: client exited without proper shutdown sequence" FUTURE FEATURES: - add numlock as a key modifier? (but make sure "Ctrl+S" handles both "No NumLock+Ctrl+S" and "NumLock+Ctrl+S") -- comment-start + comment-end settings +- font setting & support for multiple fonts to cover more characters +- comment-start & comment-end settings - robust find (results shouldn't move around when you type things) - multiple files with command line arguments - :set-build-command @@ -633,9 +633,9 @@ int main(int argc, char **argv) { float x = (float)event.button.x, y = (float)event.button.y; if (button == SDL_BUTTON_X1) { - ted_press_key(ted, SCANCODE_MOUSE_X1, key_modifier); + ted_press_key(ted, KEYCODE_X1, key_modifier); } else if (button == SDL_BUTTON_X2) { - ted_press_key(ted, SCANCODE_MOUSE_X2, key_modifier); + ted_press_key(ted, KEYCODE_X2, key_modifier); } if (button < arr_count(ted->nmouse_clicks) @@ -707,9 +707,9 @@ int main(int argc, char **argv) { ted->hover.time = 0.0; } break; case SDL_KEYDOWN: { - SDL_Scancode scancode = event.key.keysym.scancode; + SDL_Keycode keycode = event.key.keysym.sym; SDL_Keymod modifier = event.key.keysym.mod; - ted_press_key(ted, scancode, modifier); + ted_press_key(ted, keycode, modifier); } break; case SDL_TEXTINPUT: { char *text = event.text.text; @@ -549,17 +549,28 @@ void ted_load_configs(Ted *ted, bool reloading) { } } -void ted_press_key(Ted *ted, SDL_Scancode scancode, SDL_Keymod modifier) { - u32 key_combo = (u32)scancode << 3 | +void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier) { + u32 key_combo = KEY_COMBO( (u32)((modifier & (KMOD_LCTRL|KMOD_RCTRL)) != 0) << KEY_MODIFIER_CTRL_BIT | (u32)((modifier & (KMOD_LSHIFT|KMOD_RSHIFT)) != 0) << KEY_MODIFIER_SHIFT_BIT | - (u32)((modifier & (KMOD_LALT|KMOD_RALT)) != 0) << KEY_MODIFIER_ALT_BIT; - if (key_combo < KEY_COMBO_COUNT) { - KeyAction *action = &ted_active_settings(ted)->key_actions[key_combo]; - if (action->command) { - command_execute(ted, action->command, action->argument); + (u32)((modifier & (KMOD_LALT|KMOD_RALT)) != 0) << KEY_MODIFIER_ALT_BIT, + keycode); + + const KeyAction *const key_actions = ted_active_settings(ted)->key_actions; + u32 lo = 0; + u32 hi = arr_len(key_actions); + while (lo < hi) { + u32 mid = (lo + hi) / 2; + if (key_actions[mid].key_combo < key_combo) { + lo = mid + 1; + } else if (key_actions[mid].key_combo > key_combo) { + hi = mid; + } else { + command_execute(ted, key_actions[mid].command, key_actions[mid].argument); + return; } } + // nothing bound to this key } bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos) { @@ -140,10 +140,10 @@ Up = :up Shift+Up = :select-up Down = :down Shift+Down = :select-down -Ctrl+Up = 1 :up-blank-line -Ctrl+Shift+Up = 1 :select-up-blank-line -Ctrl+Down = 1 :down-blank-line -Ctrl+Shift+Down = 1 :select-down-blank-line +Ctrl+Up = :up-blank-line +Ctrl+Shift+Up = :select-up-blank-line +Ctrl+Down = :down-blank-line +Ctrl+Shift+Down = :select-down-blank-line Home = :start-of-line Shift+Home = :select-start-of-line Ctrl+Home = :start-of-file @@ -248,7 +248,7 @@ Alt+0 = 9 :tab-switch X1 = :tab-prev X2 = :tab-next -Ctrl++ = 3 :increase-text-size +Ctrl+Equals = 3 :increase-text-size Ctrl+- = 3 :decrease-text-size Ctrl+Alt+Shift+v = :view-only @@ -256,6 +256,8 @@ Ctrl+Alt+Shift+v = :view-only F4 = :build Ctrl+[ = :build-prev-error Ctrl+] = :build-next-error +# Ctrl+Shift+1 applies to QWERTY keyboards (and others), Ctrl+! for AZERTY (and others) +Ctrl+Shift+1 = :shell Ctrl+! = :shell Ctrl+t = :generate-tags @@ -95,9 +95,10 @@ ENUM_U8 { #define SYNTAX_CODE SYNTAX_PREPROCESSOR // for markdown #define SYNTAX_LINK SYNTAX_CONSTANT // for markdown -#define SCANCODE_MOUSE_X1 (SDL_NUM_SCANCODES) -#define SCANCODE_MOUSE_X2 (SDL_NUM_SCANCODES+1) -#define SCANCODE_COUNT (SDL_NUM_SCANCODES+2) +enum { + KEYCODE_X1 = 1<<20, + KEYCODE_X2 +}; // a "key combo" is some subset of {control, shift, alt} + some key. #define KEY_COMBO_COUNT (SCANCODE_COUNT << 3) #define KEY_MODIFIER_CTRL_BIT 0 @@ -106,9 +107,11 @@ ENUM_U8 { #define KEY_MODIFIER_CTRL ((u32)1<<KEY_MODIFIER_CTRL_BIT) #define KEY_MODIFIER_SHIFT ((u32)1<<KEY_MODIFIER_SHIFT_BIT) #define KEY_MODIFIER_ALT ((u32)1<<KEY_MODIFIER_ALT_BIT) -// ctrl+alt+c is encoded as SDL_SCANCODE_C << 3 | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT +// annoyingly SDL sets bit 30 for some keycodes +#define KEY_COMBO(modifier, key) ((u32)(modifier) | ((u32)(key) & ~(1u<<30)) << 3 | ((u32)(key & (1<<30)) >> 10)) typedef struct KeyAction { + u32 key_combo; u32 line_number; // config line number where this was set Command command; // this will be 0 (COMMAND_UNKNOWN) if there's no action for the key i64 argument; @@ -192,7 +195,7 @@ typedef struct { char build_default_command[256]; // [i] = comma-separated string of file extensions for language i, or NULL for none char *language_extensions[LANG_COUNT]; - KeyAction key_actions[KEY_COMBO_COUNT]; + KeyAction *key_actions; // dynamic array, sorted by KEY_COMBO(modifier, key) } Settings; // a position in the buffer @@ -1098,7 +1101,7 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer); // switch to this node void ted_node_switch(Ted *ted, Node *node); void ted_load_configs(Ted *ted, bool reloading); -void ted_press_key(Ted *ted, SDL_Scancode scancode, SDL_Keymod modifier); +void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier); bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos); void ted_flash_error_cursor(Ted *ted); void ted_go_to_position(Ted *ted, const char *path, u32 line, u32 index, bool is_lsp); @@ -298,6 +298,10 @@ int strcmp_case_insensitive(const char *a, const char *b) { #endif } +bool streq_case_insensitive(const char *a, const char *b) { + return strcmp_case_insensitive(a, b) == 0; +} + int str_qsort_case_insensitive_cmp(const void *av, const void *bv) { const char *const *a = av, *const *b = bv; return strcmp_case_insensitive(*a, *b); @@ -100,6 +100,8 @@ void print_bytes(const u8 *bytes, size_t n); char *strstr_case_insensitive(const char *haystack, const char *needle); // like strcmp, but case-insensitive int strcmp_case_insensitive(const char *a, const char *b); +// like streq, but case-insensitive +bool streq_case_insensitive(const char *a, const char *b); // function to be passed into qsort for case insensitive sorting int str_qsort_case_insensitive_cmp(const void *av, const void *bv); // the actual file name part of the path; get rid of the containing directory. |