diff --git a/README.md b/README.md index b819fc0..9f6a726 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ list of features | blacklist applications | [x] | | use media-keys as hotkey | [x] | | synthesize a key-press | [x] | +| noremap / key forwarding | [x] | ### Install @@ -96,6 +97,7 @@ mode = 'name of mode' | ',' action = '[' ']' | '->' '[' ']' ':' | '->' ':' ';' | '->' ';' + '|' keysym = '-' | @@ -172,3 +174,11 @@ General options that configure the behaviour of **skhd**: "google chrome" ] ``` + +Key forwarding (noremap like functionality): + +``` +# specify source key (left) and target key (right) to synthesize +cmd - 1 : yabai -m space --focus 1 +ctrl - 1 | cmd - 1 # press `ctrl - 1` will be `cmd - 1`, bypassing `cmd - 1` command above +``` diff --git a/src/hotkey.c b/src/hotkey.c index 780aa3a..95c35c4 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -163,6 +163,34 @@ find_process_command_mapping(struct hotkey *hotkey, uint32_t *capture, struct ca return result; } +bool find_and_forward_hotkey(struct hotkey *k, struct mode *m, CGEventRef event) +{ + struct hotkey *found = table_find(&m->hotkey_map, k); + if (!found || !found->forwarded_hotkey) return false; + debug("forwarding hotkey\n"); + struct hotkey *forwarded = found->forwarded_hotkey; + + int flags = 0; + if (has_flags(forwarded, Hotkey_Flag_Alt)) { + flags |= kCGEventFlagMaskAlternate; + } + if (has_flags(forwarded, Hotkey_Flag_Shift)) { + flags |= kCGEventFlagMaskShift; + } + if (has_flags(forwarded, Hotkey_Flag_Cmd)) { + flags |= kCGEventFlagMaskCommand; + } + if (has_flags(forwarded, Hotkey_Flag_Control)) { + flags |= kCGEventFlagMaskControl; + } + if (has_flags(forwarded, Hotkey_Flag_Fn)) { + flags |= kCGEventFlagMaskSecondaryFn; + } + CGEventSetFlags(event, flags); + CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, forwarded->key); + return true; +} + bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m, struct carbon_event *carbon) { uint32_t c = MODE_CAPTURE((int)(*m)->capture); @@ -212,6 +240,7 @@ void free_mode_map(struct table *mode_map) } buf_free(hotkey->command); + if (hotkey->forwarded_hotkey) free(hotkey->forwarded_hotkey); free(hotkey); next:; } diff --git a/src/hotkey.h b/src/hotkey.h index 6b6e92a..de62cad 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -76,6 +76,7 @@ struct hotkey char **command; char *wildcard_command; struct mode **mode_list; + struct hotkey *forwarded_hotkey; }; #define internal static diff --git a/src/parse.c b/src/parse.c index 42feca2..a88398f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -317,7 +317,16 @@ parse_hotkey(struct parser *parser) hotkey->flags |= Hotkey_Flag_Passthrough; } - if (parser_match(parser, Token_Command)) { + if (parser_match(parser, Token_Forward)) { + debug(" forward key stroke: {\n"); + struct hotkey *forwarded = parse_keypress(parser); + if (!forwarded) { + parser_report_error(parser, parser_peek(parser), "expect keysym\n"); + goto err; + } + hotkey->forwarded_hotkey = forwarded; + debug(" }\n"); + } else if (parser_match(parser, Token_Command)) { parse_command(parser, hotkey); } else if (parser_match(parser, Token_BeginList)) { parse_process_command_list(parser, hotkey); diff --git a/src/skhd.c b/src/skhd.c index 3525269..20047a7 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -165,8 +165,11 @@ internal EVENT_TAP_CALLBACK(key_handler) if (table_find(&blacklst, carbon.process_name)) return event; if (!current_mode) return event; - BEGIN_TIMED_BLOCK("handle_keypress"); struct hotkey eventkey = create_eventkey(event); + if (find_and_forward_hotkey(&eventkey, current_mode, event)) { + return event; + } + BEGIN_TIMED_BLOCK("handle_keypress"); bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode, &carbon); END_TIMED_BLOCK(); diff --git a/src/tokenize.c b/src/tokenize.c index eaf0e3a..2c73f0a 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -219,6 +219,15 @@ get_token(struct tokenizer *tokenizer) token.type = Token_Command; } } break; + case '|': { + eat_whitespace(tokenizer); + + token.text = tokenizer->at; + token.line = tokenizer->line; + token.cursor = tokenizer->cursor; + + token.type = Token_Forward; + } break; default: { if (c == '0' && *tokenizer->at == 'x') { advance(tokenizer); diff --git a/src/tokenize.h b/src/tokenize.h index 52d76fc..aa372d0 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -47,6 +47,7 @@ enum token_type Token_Key, Token_Decl, + Token_Forward, Token_Comma, Token_Insert, Token_Plus,