diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 158 |
1 files changed, 153 insertions, 5 deletions
@@ -12,10 +12,124 @@ typedef enum { SECTION_KEYBOARD } Section; +// Returns the key combination described by str. filename and line_number should be passed inm to generate nice errors. +static u32 config_parse_key_combo(Ted *ted, char const *str, char const *filename, uint line_number) { + u32 modifier = 0; + // read modifier + while (true) { + if (util_is_prefix(str, "Ctrl+")) { + if (modifier & KEY_MODIFIER_CTRL) { + ted_seterr(ted, "%s:%u: Ctrl+ written twice", filename, line_number); + return 0; + } + modifier |= KEY_MODIFIER_CTRL; + str += strlen("Ctrl+"); + } else if (util_is_prefix(str, "Shift+")) { + if (modifier & KEY_MODIFIER_SHIFT) { + ted_seterr(ted, "%s:%u: Shift+ written twice", filename, line_number); + return 0; + } + modifier |= KEY_MODIFIER_SHIFT; + str += strlen("Shift+"); + } else if (util_is_prefix(str, "Alt+")) { + if (modifier & KEY_MODIFIER_ALT) { + ted_seterr(ted, "%s:%u: Alt+ written twice", filename, line_number); + return 0; + } + modifier |= KEY_MODIFIER_ALT; + str += strlen("Alt+"); + } else break; + } + + // read key + SDL_Scancode scancode = SDL_GetScancodeFromName(str); + if (scancode == SDL_SCANCODE_UNKNOWN) { + typedef struct { + char const *keyname1; + char const *keyname2; // alternate key name + SDL_Scancode scancode; + bool shift; + } 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}, + {"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} + }; + + // @OPTIMIZE: sort key_names; 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) { + ted_seterr(ted, "%s:%u: Shift+%s is redundant.", filename, line_number, str); + return 0; + } + modifier |= KEY_MODIFIER_SHIFT; + } + break; + } + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + if (isdigit(str[0])) { // direct scancode 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; + } else { + ted_seterr(ted, "%s:%u: Invalid scancode number: %s", filename, line_number, str); + return 0; + } + } else { + ted_seterr(ted, "%s:%u: Unrecognized key name: %s.", filename, line_number, str); + return 0; + } + } + } + return (u32)scancode << 3 | modifier; +} + void config_read(Ted *ted, char const *filename) { FILE *fp = fopen(filename, "rb"); if (fp) { - u32 line_number = 1; + uint line_number = 1; int line_cap = 4096; char *line = ted_malloc(ted, (size_t)line_cap); if (line) { @@ -37,10 +151,10 @@ void config_read(Ted *ted, char const *filename) { #define SECTION_HEADER_HELP "Section headers should look like this: [section-name]" char *closing = strchr(line, ']'); if (!closing) { - ted_seterr(ted, "%s:" U32_FMT ": Unmatched [. " SECTION_HEADER_HELP, filename, line_number); + ted_seterr(ted, "%s:%u: Unmatched [. " SECTION_HEADER_HELP, filename, line_number); error = true; } else if (closing[1] != '\0') { - ted_seterr(ted, "%s:" U32_FMT ": Text after section. " SECTION_HEADER_HELP, filename, line_number); + ted_seterr(ted, "%s:%u: Text after section. " SECTION_HEADER_HELP, filename, line_number); error = true; } else { *closing = '\0'; @@ -48,18 +162,52 @@ void config_read(Ted *ted, char const *filename) { if (streq(section_name, "keyboard")) { section = SECTION_KEYBOARD; } else { - ted_seterr(ted, "%s:" U32_FMT ": Unrecognized section: [%s].", filename, line_number, section_name); + ted_seterr(ted, "%s:%u: Unrecognized section: [%s].", filename, line_number, section_name); error = true; } } } break; + default: { + char *equals = strchr(line, '='); + if (equals) { + char *key = line; + *equals = '\0'; + char *value = key + 1; + while (isspace(*key)) ++key; + while (isspace(*value)) ++value; + if (equals != line) { + for (char *p = equals - 1; p > line; --p) { + // remove trailing spaces after key + if (isspace(*p)) *p = '\0'; + else break; + } + } + if (key[0] == '\0') { + ted_seterr(ted, "%s:%u: Empty property name. This line should look like: key = value", filename, line_number); + } else { + switch (section) { + case SECTION_NONE: + ted_seterr(ted, "%s:%u: Line outside of any section." + "Try putting a section header, e.g. [keyboard] before this line?", filename, line_number); + break; + case SECTION_KEYBOARD: { + u32 key_combo = config_parse_key_combo(ted, key, filename, line_number); + (void)key_combo; // @TODO + } break; + } + } + } else { + ted_seterr(ted, "%s:%u: Invalid line syntax." + "Lines should either look like [section-name] or key = value", filename, line_number); + } + } break; } if (error) break; ++line_number; } else { - ted_seterr(ted, "%s:" U32_FMT ": Line is too long.", filename, line_number); + ted_seterr(ted, "%s:%u: Line is too long.", filename, line_number); break; } } |