summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base.h1
-rw-r--r--config.c158
-rw-r--r--ted-base.c4
-rw-r--r--util.c4
4 files changed, 162 insertions, 5 deletions
diff --git a/base.h b/base.h
index c6e6de4..c32c149 100644
--- a/base.h
+++ b/base.h
@@ -67,6 +67,7 @@ typedef unsigned long long ullong;
#define Status bool WarnUnusedResult // false = error, true = success
+#define arr_count(a) (sizeof (a) / sizeof *(a))
#ifdef __GNUC__
#define no_warn_start _Pragma("GCC diagnostic push") \
diff --git a/config.c b/config.c
index 8218399..1bf6d70 100644
--- a/config.c
+++ b/config.c
@@ -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;
}
}
diff --git a/ted-base.c b/ted-base.c
index 5ed6469..28e7d4e 100644
--- a/ted-base.c
+++ b/ted-base.c
@@ -6,6 +6,10 @@ typedef struct {
#define SCANCODE_COUNT 0x120 // SDL scancodes should be less than this value.
// a "key combo" is some subset of {control, shift, alt} + some key.
#define KEY_COMBO_COUNT (SCANCODE_COUNT << 3)
+#define KEY_MODIFIER_CTRL 1
+#define KEY_MODIFIER_SHIFT 2
+#define KEY_MODIFIER_ALT 4
+// ctrl+alt+c is encoded as SDL_SCANCODE_C << 3 | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT
typedef struct {
TextBuffer *active_buffer;
diff --git a/util.c b/util.c
index 26a8d8e..06606e3 100644
--- a/util.c
+++ b/util.c
@@ -42,3 +42,7 @@ static char32_t const *util_mem32chr_const(char32_t const *s, char32_t c, size_t
static bool streq(char const *a, char const *b) {
return strcmp(a, b) == 0;
}
+
+static bool util_is_prefix(char const *str, char const *prefix) {
+ return strncmp(str, prefix, strlen(prefix)) == 0;
+}