aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hotkey.h6
-rw-r--r--src/parse.c55
-rw-r--r--src/parse.h2
-rw-r--r--src/skhd.c16
-rw-r--r--src/synthesize.c92
-rw-r--r--src/synthesize.h7
6 files changed, 176 insertions, 2 deletions
diff --git a/src/hotkey.h b/src/hotkey.h
index c2caed5..e616303 100644
--- a/src/hotkey.h
+++ b/src/hotkey.h
@@ -5,6 +5,12 @@
#include <stdint.h>
#include <stdbool.h>
+#define Modifier_Keycode_Alt 0x3A
+#define Modifier_Keycode_Shift 0x38
+#define Modifier_Keycode_Cmd 0x37
+#define Modifier_Keycode_Ctrl 0x3B
+#define Modifier_Keycode_Fn 0x3F
+
enum osx_event_mask
{
Event_Mask_Alt = 0x00080000,
diff --git a/src/parse.c b/src/parse.c
index 79f4aba..a79d8a3 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -367,6 +367,53 @@ void parse_config(struct parser *parser)
}
}
+struct hotkey *
+parse_keypress(struct parser *parser)
+{
+ if ((parser_check(parser, Token_Modifier)) ||
+ (parser_check(parser, Token_Literal)) ||
+ (parser_check(parser, Token_Key_Hex)) ||
+ (parser_check(parser, Token_Key))) {
+ struct hotkey *hotkey = malloc(sizeof(struct hotkey));
+ memset(hotkey, 0, sizeof(struct hotkey));
+ bool found_modifier;
+
+ 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, Error_Unexpected_Token, "expected '-'");
+ goto err;
+ }
+ }
+
+ if (parser_match(parser, Token_Key)) {
+ hotkey->key = parse_key(parser);
+ } else if (parser_match(parser, Token_Key_Hex)) {
+ hotkey->key = parse_key_hex(parser);
+ } else if (parser_match(parser, Token_Literal)) {
+ parse_key_literal(parser, hotkey);
+ } else {
+ parser_report_error(parser, Error_Unexpected_Token, "expected key-literal");
+ goto err;
+ }
+
+ return hotkey;
+
+ err:
+ free(hotkey);
+ return NULL;
+ } else {
+ parser_report_error(parser, Error_Unexpected_Token, "expected modifier or key-literal");
+ }
+ return NULL;
+}
+
struct token
parser_peek(struct parser *parser)
{
@@ -445,6 +492,14 @@ bool parser_init(struct parser *parser, struct table *mode_map, char *file)
return false;
}
+bool parser_init_text(struct parser *parser, char *text)
+{
+ memset(parser, 0, sizeof(struct parser));
+ tokenizer_init(&parser->tokenizer, text);
+ parser_advance(parser);
+ return true;
+}
+
void parser_destroy(struct parser *parser)
{
free(parser->tokenizer.buffer);
diff --git a/src/parse.h b/src/parse.h
index d3543b9..93fcfc6 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -23,6 +23,7 @@ enum parse_error_type
void parse_config(struct parser *parser);
+struct hotkey *parse_keypress(struct parser *parser);
struct token parser_peek(struct parser *parser);
struct token parser_previous(struct parser *parser);
@@ -31,6 +32,7 @@ struct token parser_advance(struct parser *parser);
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, struct table *mode_map, char *file);
+bool parser_init_text(struct parser *parser, char *text);
void parser_destroy(struct parser *parser);
void parser_report_error(struct parser *parser, enum parse_error_type error_type, const char *format, ...);
diff --git a/src/skhd.c b/src/skhd.c
index 4d1ce63..972d75e 100644
--- a/src/skhd.c
+++ b/src/skhd.c
@@ -17,6 +17,7 @@
#include "tokenize.h"
#include "parse.h"
#include "hotkey.h"
+#include "synthesize.h"
#include "hotload.c"
#include "event_tap.c"
@@ -24,6 +25,7 @@
#include "tokenize.c"
#include "parse.c"
#include "hotkey.c"
+#include "synthesize.c"
#define internal static
extern bool CGSIsSecureEventInputSet();
@@ -123,10 +125,12 @@ internal bool
parse_arguments(int argc, char **argv)
{
int option;
- const char *short_option = "vc:";
+ const char *short_option = "vc:k:t:";
struct option long_option[] = {
{ "version", no_argument, NULL, 'v' },
{ "config", required_argument, NULL, 'c' },
+ { "key", required_argument, NULL, 'k' },
+ { "text", required_argument, NULL, 't' },
{ NULL, 0, NULL, 0 }
};
@@ -137,7 +141,15 @@ parse_arguments(int argc, char **argv)
return true;
} break;
case 'c': {
- config_file = strdup(optarg);
+ config_file = copy_string(optarg);
+ } break;
+ case 'k': {
+ synthesize_key(optarg);
+ return true;
+ } break;
+ case 't': {
+ synthesize_text(optarg);
+ return true;
} break;
}
}
diff --git a/src/synthesize.c b/src/synthesize.c
new file mode 100644
index 0000000..0084604
--- /dev/null
+++ b/src/synthesize.c
@@ -0,0 +1,92 @@
+#include <Carbon/Carbon.h>
+
+#include "synthesize.h"
+#include "locale.h"
+#include "parse.h"
+#include "hotkey.h"
+
+#define internal static
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+
+internal inline void
+create_and_post_keyevent(uint16_t key, bool pressed)
+{
+ CGPostKeyboardEvent((CGCharCode)0, (CGKeyCode)key, pressed);
+}
+
+internal inline void
+synthesize_modifiers(struct hotkey *hotkey, bool pressed)
+{
+ if (has_flags(hotkey, Hotkey_Flag_Alt)) {
+ create_and_post_keyevent(Modifier_Keycode_Alt, pressed);
+ }
+
+ if (has_flags(hotkey, Hotkey_Flag_Shift)) {
+ create_and_post_keyevent(Modifier_Keycode_Shift, pressed);
+ }
+
+ if (has_flags(hotkey, Hotkey_Flag_Cmd)) {
+ create_and_post_keyevent(Modifier_Keycode_Cmd, pressed);
+ }
+
+ if (has_flags(hotkey, Hotkey_Flag_Control)) {
+ create_and_post_keyevent(Modifier_Keycode_Ctrl, pressed);
+ }
+
+ if (has_flags(hotkey, Hotkey_Flag_Fn)) {
+ create_and_post_keyevent(Modifier_Keycode_Fn, pressed);
+ }
+}
+
+void synthesize_key(char *key_string)
+{
+ if (!initialize_keycode_map()) return;
+ struct parser parser;
+ parser_init_text(&parser, key_string);
+
+ close(1);
+ close(2);
+
+ struct hotkey *hotkey = parse_keypress(&parser);
+ if (!hotkey) return;
+
+ CGSetLocalEventsSuppressionInterval(0.0f);
+ CGEnableEventStateCombining(false);
+
+ synthesize_modifiers(hotkey, true);
+ create_and_post_keyevent(hotkey->key, true);
+
+ create_and_post_keyevent(hotkey->key, false);
+ synthesize_modifiers(hotkey, false);
+}
+
+void synthesize_text(char *text)
+{
+ CFStringRef text_ref = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
+ CFIndex text_length = CFStringGetLength(text_ref);
+
+ CGEventRef de = CGEventCreateKeyboardEvent(NULL, 0, true);
+ CGEventRef ue = CGEventCreateKeyboardEvent(NULL, 0, false);
+
+ CGEventSetFlags(de, 0);
+ CGEventSetFlags(ue, 0);
+
+ UniChar c;
+ for (CFIndex i = 0; i < text_length; ++i)
+ {
+ c = CFStringGetCharacterAtIndex(text_ref, i);
+ CGEventKeyboardSetUnicodeString(de, 1, &c);
+ CGEventPost(kCGAnnotatedSessionEventTap, de);
+ usleep(1000);
+ CGEventKeyboardSetUnicodeString(ue, 1, &c);
+ CGEventPost(kCGAnnotatedSessionEventTap, ue);
+ }
+
+ CFRelease(ue);
+ CFRelease(de);
+ CFRelease(text_ref);
+}
+
+#pragma clang diagnostic pop
diff --git a/src/synthesize.h b/src/synthesize.h
new file mode 100644
index 0000000..4a2b262
--- /dev/null
+++ b/src/synthesize.h
@@ -0,0 +1,7 @@
+#ifndef SKHD_SYNTHESIZE_H
+#define SKHD_SYNTHESIZE_H
+
+void synthesize_key(char *key_string);
+void synthesize_text(char *text);
+
+#endif