aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hotkey.c78
-rw-r--r--src/hotkey.h42
-rw-r--r--src/parse.c178
-rw-r--r--src/parse.h15
-rw-r--r--src/skhd.c19
-rw-r--r--src/tokenize.c27
-rw-r--r--src/tokenize.h6
7 files changed, 309 insertions, 56 deletions
diff --git a/src/hotkey.c b/src/hotkey.c
index d9ebde6..af1e410 100644
--- a/src/hotkey.c
+++ b/src/hotkey.c
@@ -61,6 +61,29 @@ unsigned long hash_hotkey(struct hotkey *a)
return a->key;
}
+bool same_mode(char *a, char *b)
+{
+ while (*a && *b && *a == *b) {
+ ++a;
+ ++b;
+ }
+ return *a == '\0' && *b == '\0';
+}
+
+unsigned long hash_mode(char *key)
+{
+ unsigned long hash = 0, high;
+ while(*key) {
+ hash = (hash << 4) + *key++;
+ high = hash & 0xF0000000;
+ if(high) {
+ hash ^= (high >> 24);
+ }
+ hash &= ~high;
+ }
+ return hash;
+}
+
internal bool
fork_and_exec(char *command)
{
@@ -81,26 +104,40 @@ fork_and_exec(char *command)
return true;
}
-bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *hotkey_map)
+bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode)
{
bool result = false;
struct hotkey *hotkey;
- if ((hotkey = table_find(hotkey_map, eventkey))) {
- if (fork_and_exec(hotkey->command)) {
+ if ((hotkey = table_find(&(*current_mode)->hotkey_map, eventkey))) {
+ if (has_flags(hotkey, Hotkey_Flag_Activate)) {
+ *current_mode = table_find(mode_map, hotkey->command);
+ if ((*current_mode)->command) {
+ fork_and_exec((*current_mode)->command);
+ }
+ result = has_flags(hotkey, Hotkey_Flag_Passthrough) ? false : true;
+ } else if (fork_and_exec(hotkey->command)) {
result = has_flags(hotkey, Hotkey_Flag_Passthrough) ? false : true;
}
}
return result;
}
-void free_hotkeys(struct table *hotkey_map)
+internal void
+free_hotkeys(struct table *hotkey_map, struct table *freed_pointers)
{
int count;
void **hotkeys = table_reset(hotkey_map, &count);
+
for (int index = 0; index < count; ++index) {
struct hotkey *hotkey = (struct hotkey *) hotkeys[index];
- free(hotkey->command);
- free(hotkey);
+ if (table_find(freed_pointers, hotkey)) {
+ // we have already freed this pointer, do nothing!
+ // printf("info: %p has already been freed!\n", hotkey);
+ } else {
+ table_add(freed_pointers, hotkey, hotkey);
+ free(hotkey->command);
+ free(hotkey);
+ }
}
if (count) {
@@ -108,6 +145,35 @@ void free_hotkeys(struct table *hotkey_map)
}
}
+unsigned long hash_pointer(struct hotkey *a) { return (unsigned long)a; }
+bool same_pointer(struct hotkey *a, struct hotkey *b) { return a == b; }
+
+void free_mode_map(struct table *mode_map)
+{
+ int count;
+ struct table freed_pointers;
+ void **modes = table_reset(mode_map, &count);
+
+ if (count) {
+ table_init(&freed_pointers, 13,
+ (table_hash_func) hash_pointer,
+ (table_compare_func) same_pointer);
+ }
+
+ for (int index = 0; index < count; ++index) {
+ struct mode *mode = (struct mode *) modes[index];
+ if (mode->command) free(mode->command);
+ if (mode->name) free(mode->name);
+ free_hotkeys(&mode->hotkey_map, &freed_pointers);
+ free(mode);
+ }
+
+ if (count) {
+ free(modes);
+ table_free(&freed_pointers);
+ }
+}
+
internal void
cgevent_lrmod_flag_to_hotkey_lrmod_flag(CGEventFlags flags, struct hotkey *eventkey, int mod)
{
diff --git a/src/hotkey.h b/src/hotkey.h
index 40ea630..530520b 100644
--- a/src/hotkey.h
+++ b/src/hotkey.h
@@ -24,31 +24,45 @@ enum osx_event_mask
enum hotkey_flag
{
- Hotkey_Flag_Alt = (1 << 0),
- Hotkey_Flag_LAlt = (1 << 1),
- Hotkey_Flag_RAlt = (1 << 2),
- Hotkey_Flag_Shift = (1 << 3),
- Hotkey_Flag_LShift = (1 << 4),
- Hotkey_Flag_RShift = (1 << 5),
- Hotkey_Flag_Cmd = (1 << 6),
- Hotkey_Flag_LCmd = (1 << 7),
- Hotkey_Flag_RCmd = (1 << 8),
- Hotkey_Flag_Control = (1 << 9),
+ Hotkey_Flag_Alt = (1 << 0),
+ Hotkey_Flag_LAlt = (1 << 1),
+ Hotkey_Flag_RAlt = (1 << 2),
+ Hotkey_Flag_Shift = (1 << 3),
+ Hotkey_Flag_LShift = (1 << 4),
+ Hotkey_Flag_RShift = (1 << 5),
+ Hotkey_Flag_Cmd = (1 << 6),
+ Hotkey_Flag_LCmd = (1 << 7),
+ Hotkey_Flag_RCmd = (1 << 8),
+ Hotkey_Flag_Control = (1 << 9),
Hotkey_Flag_LControl = (1 << 10),
Hotkey_Flag_RControl = (1 << 11),
Hotkey_Flag_Fn = (1 << 12),
Hotkey_Flag_Passthrough = (1 << 13),
+ Hotkey_Flag_Activate = (1 << 14),
Hotkey_Flag_Hyper = (Hotkey_Flag_Cmd |
Hotkey_Flag_Alt |
Hotkey_Flag_Shift |
Hotkey_Flag_Control)
};
+#include "hashtable.h"
+
+struct mode
+{
+ int line;
+ int cursor;
+ char *name;
+ char *command;
+ struct table hotkey_map;
+};
+
struct hotkey
{
uint32_t flags;
uint32_t key;
char *command;
+ int mode_count;
+ struct mode *mode_list[16];
};
static inline void
@@ -70,13 +84,15 @@ clear_flags(struct hotkey *hotkey, uint32_t flag)
hotkey->flags &= ~flag;
}
+bool same_mode(char *a, char *b);
+unsigned long hash_mode(char *key);
+
bool same_hotkey(struct hotkey *a, struct hotkey *b);
unsigned long hash_hotkey(struct hotkey *a);
-bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *hotkey_map);
+bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode);
void cgeventflags_to_hotkeyflags(CGEventFlags flags, struct hotkey *eventkey);
-struct table;
-void free_hotkeys(struct table *hotkey_map);
+void free_mode_map(struct table *mode_map);
#endif
diff --git a/src/parse.c b/src/parse.c
index 14b1d14..3e60dd1 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -12,6 +12,30 @@
#define internal static
+internal struct mode *
+find_or_init_default_mode(struct parser *parser)
+{
+ struct mode *default_mode;
+
+ if ((default_mode = table_find(parser->mode_map, "default"))) {
+ return default_mode;
+ }
+
+ default_mode = malloc(sizeof(struct mode));
+ default_mode->line = -1;
+ default_mode->cursor = -1;
+ default_mode->name = copy_string("default");
+
+ table_init(&default_mode->hotkey_map, 131,
+ (table_hash_func) hash_hotkey,
+ (table_compare_func) same_hotkey);
+
+ default_mode->command = NULL;
+ table_add(parser->mode_map, default_mode->name, default_mode);
+
+ return default_mode;
+}
+
internal char *
read_file(const char *file)
{
@@ -122,8 +146,8 @@ internal enum hotkey_flag modifier_flags_value[] =
internal uint32_t
parse_modifier(struct parser *parser)
{
- uint32_t flags = 0;
struct token modifier = parser_previous(parser);
+ uint32_t flags = 0;
for (int i = 0; i < array_count(modifier_flags_str); ++i) {
if (token_equals(modifier, modifier_flags_str[i])) {
@@ -137,34 +161,76 @@ parse_modifier(struct parser *parser)
if (parser_match(parser, Token_Modifier)) {
flags |= parse_modifier(parser);
} else {
- parser_report_error(parser, "expected modifier");
+ parser_report_error(parser, Error_Unexpected_Token, "expected modifier");
}
}
return flags;
}
+internal void
+parse_mode(struct parser *parser, struct hotkey *hotkey)
+{
+ struct token identifier = parser_previous(parser);
+
+ char *name = copy_string_count(identifier.text, identifier.length);
+ struct mode *mode = table_find(parser->mode_map, name);
+ free(name);
+
+ if (!mode && token_equals(identifier, "default")) {
+ mode = find_or_init_default_mode(parser);
+ } else if (!mode) {
+ parser_report_error(parser, Error_Undeclared_Ident, "undeclared identifier");
+ return;
+ }
+
+ hotkey->mode_list[hotkey->mode_count++] = mode;
+ printf("\tmode: '%s'\n", mode->name);
+
+ if (parser_match(parser, Token_Comma)) {
+ if (parser_match(parser, Token_Identifier)) {
+ parse_mode(parser, hotkey);
+ } else {
+ parser_report_error(parser, Error_Unexpected_Token, "expected identifier");
+ }
+ }
+}
+
internal struct hotkey *
parse_hotkey(struct parser *parser)
{
struct hotkey *hotkey = malloc(sizeof(struct hotkey));
- int found_modifier;
+ memset(hotkey, 0, sizeof(struct hotkey));
+ bool found_modifier;
printf("hotkey :: #%d {\n", parser->current_token.line);
- if (parser_match(parser, Token_Modifier)) {
- hotkey->flags = parse_modifier(parser);
+ if (parser_match(parser, Token_Identifier)) {
+ parse_mode(parser, hotkey);
if (parser->error) {
goto err;
}
- found_modifier = 1;
+ }
+
+ if (hotkey->mode_count > 0) {
+ if (!parser_match(parser, Token_Insert)) {
+ parser_report_error(parser, Error_Unexpected_Token, "expected '<'");
+ goto err;
+ }
} else {
- hotkey->flags = found_modifier = 0;
+ hotkey->mode_list[hotkey->mode_count++] = find_or_init_default_mode(parser);
+ }
+
+ if ((found_modifier = parser_match(parser, Token_Modifier))) {
+ hotkey->flags = parse_modifier(parser);
+ if (parser->error) {
+ goto err;
+ }
}
if (found_modifier) {
if (!parser_match(parser, Token_Dash)) {
- parser_report_error(parser, "expected '-'");
+ parser_report_error(parser, Error_Unexpected_Token, "expected '-'");
goto err;
}
}
@@ -176,7 +242,7 @@ parse_hotkey(struct parser *parser)
} else if (parser_match(parser, Token_Literal)) {
parse_key_literal(parser, hotkey);
} else {
- parser_report_error(parser, "expected key-literal");
+ parser_report_error(parser, Error_Unexpected_Token, "expected key-literal");
goto err;
}
@@ -186,13 +252,19 @@ parse_hotkey(struct parser *parser)
if (parser_match(parser, Token_Command)) {
hotkey->command = parse_command(parser);
+ } else if (parser_match(parser, Token_Activate)) {
+ hotkey->flags |= Hotkey_Flag_Activate;
+ hotkey->command = parse_command(parser);
+ if (!table_find(parser->mode_map, hotkey->command)) {
+ parser_report_error(parser, Error_Undeclared_Ident, "undeclared identifier");
+ goto err;
+ }
} else {
- parser_report_error(parser, "expected ':' followed by command");
+ parser_report_error(parser, Error_Unexpected_Token, "expected ':' followed by command or ';' followed by mode");
goto err;
}
printf("}\n");
-
return hotkey;
err:
@@ -200,23 +272,73 @@ err:
return NULL;
}
-void parse_config(struct parser *parser, struct table *hotkey_map)
+internal struct mode *
+parse_mode_decl(struct parser *parser)
+{
+ struct mode *mode = malloc(sizeof(struct mode));
+ struct token identifier = parser_previous(parser);
+
+ mode->line = identifier.line;
+ mode->cursor = identifier.cursor;
+ mode->name = copy_string_count(identifier.text, identifier.length);
+
+ table_init(&mode->hotkey_map, 131,
+ (table_hash_func) hash_hotkey,
+ (table_compare_func) same_hotkey);
+
+ if (parser_match(parser, Token_Command)) {
+ mode->command = copy_string_count(parser->previous_token.text, parser->previous_token.length);
+ } else {
+ mode->command = NULL;
+ }
+
+ return mode;
+}
+
+void parse_declaration(struct parser *parser)
{
+ struct mode *mode;
+ parser_match(parser, Token_Decl);
+ if (parser_match(parser, Token_Identifier)) {
+ mode = parse_mode_decl(parser);
+ if (table_find(parser->mode_map, mode->name)) {
+ parser_report_error(parser, Error_Duplicate_Ident,
+ "#%d:%d duplicate declaration '%s'\n",
+ mode->line, mode->cursor, mode->name);
+ } else {
+ table_add(parser->mode_map, mode->name, mode);
+ }
+ } else {
+ parser_report_error(parser, Error_Unexpected_Token, "expected identifier");
+ }
+}
+
+void parse_config(struct parser *parser)
+{
+ struct mode *mode;
struct hotkey *hotkey;
+
while (!parser_eof(parser)) {
- if ((parser_check(parser, Token_Modifier)) ||
+ if (parser->error) {
+ free_mode_map(parser->mode_map);
+ return;
+ }
+
+ if ((parser_check(parser, Token_Identifier)) ||
+ (parser_check(parser, Token_Modifier)) ||
(parser_check(parser, Token_Literal)) ||
(parser_check(parser, Token_Key_Hex)) ||
(parser_check(parser, Token_Key))) {
if ((hotkey = parse_hotkey(parser))) {
- table_add(hotkey_map, hotkey, hotkey);
- } else if (parser->error) {
- free_hotkeys(hotkey_map);
- return;
+ for (int i = 0; i < hotkey->mode_count; ++i) {
+ mode = hotkey->mode_list[i];
+ table_add(&mode->hotkey_map, hotkey, hotkey);
+ }
}
+ } else if (parser_check(parser, Token_Decl)) {
+ parse_declaration(parser);
} else {
- parser_report_error(parser, "expected modifier or key-literal");
- return;
+ parser_report_error(parser, Error_Unexpected_Token, "expected decl, modifier or key-literal");
}
}
}
@@ -265,13 +387,23 @@ bool parser_match(struct parser *parser, enum token_type type)
return false;
}
-void parser_report_error(struct parser *parser, const char *format, ...)
+void parser_report_error(struct parser *parser, enum parse_error_type error_type, const char *format, ...)
{
va_list args;
va_start(args, format);
- fprintf(stderr, "#%d:%d error: ", parser->current_token.line, parser->current_token.cursor);
- vfprintf(stderr, format, args);
- fprintf(stderr, ", but got '%.*s'\n", parser->current_token.length, parser->current_token.text);
+
+ if (error_type == Error_Unexpected_Token) {
+ fprintf(stderr, "#%d:%d ", parser->current_token.line, parser->current_token.cursor);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, ", but got '%.*s'\n", parser->current_token.length, parser->current_token.text);
+ } else if (error_type == Error_Undeclared_Ident) {
+ fprintf(stderr, "#%d:%d ", parser->previous_token.line, parser->previous_token.cursor);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, " '%.*s'\n", parser->previous_token.length, parser->previous_token.text);
+ } else if (error_type == Error_Duplicate_Ident) {
+ vfprintf(stderr, format, args);
+ }
+
va_end(args);
parser->error = true;
}
diff --git a/src/parse.h b/src/parse.h
index 241e7ff..1c7719d 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -4,16 +4,25 @@
#include "tokenize.h"
#include <stdbool.h>
+struct table;
struct parser
{
struct token previous_token;
struct token current_token;
struct tokenizer tokenizer;
+ struct table *mode_map;
bool error;
};
-struct table;
-void parse_config(struct parser *parser, struct table *hotkey_map);
+enum parse_error_type
+{
+ Error_Unexpected_Token,
+ Error_Undeclared_Ident,
+ Error_Duplicate_Ident,
+};
+
+
+void parse_config(struct parser *parser);
struct token parser_peek(struct parser *parser);
struct token parser_previous(struct parser *parser);
@@ -23,6 +32,6 @@ bool parser_check(struct parser *parser, enum token_type type);
bool parser_match(struct parser *parser, enum token_type type);
bool parser_init(struct parser *parser, char *file);
void parser_destroy(struct parser *parser);
-void parser_report_error(struct parser *parser, const char *format, ...);
+void parser_report_error(struct parser *parser, enum parse_error_type error_type, const char *format, ...);
#endif
diff --git a/src/skhd.c b/src/skhd.c
index 3bdb5f3..2e0d6b1 100644
--- a/src/skhd.c
+++ b/src/skhd.c
@@ -43,6 +43,8 @@ internal unsigned major_version = 0;
internal unsigned minor_version = 0;
internal unsigned patch_version = 14;
internal struct table hotkey_map;
+internal struct table mode_map;
+internal struct mode *current_mode;
internal char *config_file;
internal void
@@ -69,16 +71,19 @@ parse_config_helper(char *absolutepath)
{
struct parser parser;
if (parser_init(&parser, absolutepath)) {
- parse_config(&parser, &hotkey_map);
+ parser.mode_map = &mode_map;
+ parse_config(&parser);
parser_destroy(&parser);
+ current_mode = table_find(&mode_map, "default");
} else {
+ current_mode = NULL;
warn("skhd: could not open file '%s'\n", absolutepath);
}
}
internal HOTLOADER_CALLBACK(config_handler)
{
- free_hotkeys(&hotkey_map);
+ free_mode_map(&mode_map);
parse_config_helper(absolutepath);
}
@@ -92,11 +97,12 @@ internal EVENT_TAP_CALLBACK(key_handler)
CGEventTapEnable(event_tap->handle, 1);
} break;
case kCGEventKeyDown: {
+ if (!current_mode) return event;
uint32_t flags = CGEventGetFlags(event);
uint32_t key = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
struct hotkey eventkey = { .flags = 0, .key = key };
cgeventflags_to_hotkeyflags(flags, &eventkey);
- bool result = find_and_exec_hotkey(&eventkey, &hotkey_map);
+ bool result = find_and_exec_hotkey(&eventkey, &mode_map, &current_mode);
if (result) {
return NULL;
}
@@ -190,10 +196,9 @@ int main(int argc, char **argv)
set_config_path();
}
- table_init(&hotkey_map,
- 131,
- (table_hash_func) hash_hotkey,
- (table_compare_func) same_hotkey);
+ table_init(&mode_map, 13,
+ (table_hash_func) hash_mode,
+ (table_compare_func) same_mode);
printf("skhd: using config '%s'\n", config_file);
parse_config_helper(config_file);
diff --git a/src/tokenize.c b/src/tokenize.c
index 691b9c8..82cd466 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -94,7 +94,7 @@ resolve_identifier_type(struct token token)
}
}
- return Token_Unknown;
+ return Token_Identifier;
}
struct token
@@ -121,6 +121,8 @@ get_token(struct tokenizer *tokenizer)
switch (c) {
case '\0':{ token.type = Token_EndOfStream; } break;
case '+': { token.type = Token_Plus; } break;
+ case ',': { token.type = Token_Comma; } break;
+ case '<': { token.type = Token_Insert; } break;
case '#': {
eat_comment(tokenizer);
token = get_token(tokenizer);
@@ -134,16 +136,33 @@ get_token(struct tokenizer *tokenizer)
token.type = Token_Dash;
}
} break;
- case ':': {
+ case ';': {
eat_whitespace(tokenizer);
token.text = tokenizer->at;
token.line = tokenizer->line;
token.cursor = tokenizer->cursor;
- eat_command(tokenizer);
+ eat_identifier(tokenizer);
token.length = tokenizer->at - token.text;
- token.type = Token_Command;
+ token.type = Token_Activate;
+ } break;
+ case ':': {
+ if (*tokenizer->at && *tokenizer->at == ':') {
+ advance(tokenizer);
+ token.length = tokenizer->at - token.text;
+ token.type = Token_Decl;
+ } else {
+ eat_whitespace(tokenizer);
+
+ token.text = tokenizer->at;
+ token.line = tokenizer->line;
+ token.cursor = tokenizer->cursor;
+
+ eat_command(tokenizer);
+ token.length = tokenizer->at - token.text;
+ token.type = Token_Command;
+ }
} break;
default: {
if (c == '0' && *tokenizer->at == 'x') {
diff --git a/src/tokenize.h b/src/tokenize.h
index 36b4350..114ed0d 100644
--- a/src/tokenize.h
+++ b/src/tokenize.h
@@ -28,12 +28,18 @@ static const char *literal_keycode_str[] =
enum token_type
{
+ Token_Identifier,
+ Token_Activate,
+
Token_Command,
Token_Modifier,
Token_Literal,
Token_Key_Hex,
Token_Key,
+ Token_Decl,
+ Token_Comma,
+ Token_Insert,
Token_Plus,
Token_Dash,
Token_Arrow,