diff options
author | koekeishiya <aasvi93@hotmail.com> | 2019-07-10 00:11:50 +0200 |
---|---|---|
committer | koekeishiya <aasvi93@hotmail.com> | 2019-07-10 00:11:50 +0200 |
commit | 32f669eab3dea1908a83133e402411fa2e377ac3 (patch) | |
tree | f6ace98d121b9fac8598e6b52e266b082920671c /src/skhd.c | |
parent | aae0e4f2a5d90a812343acf3d5b3f2f20f5c2dc0 (diff) | |
parent | 22071ddd738ad591f42500ad8dbe818ffd1eb93d (diff) | |
download | skhd-32f669eab3dea1908a83133e402411fa2e377ac3.tar.gz skhd-32f669eab3dea1908a83133e402411fa2e377ac3.zip |
merge mastermt
Diffstat (limited to 'src/skhd.c')
-rw-r--r-- | src/skhd.c | 364 |
1 files changed, 294 insertions, 70 deletions
@@ -5,88 +5,145 @@ #include <getopt.h> #include <signal.h> #include <string.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/uio.h> #include <unistd.h> #include <Carbon/Carbon.h> +#include "timing.h" +#include "log.h" #define HASHTABLE_IMPLEMENTATION #include "hashtable.h" #include "sbuffer.h" #include "hotload.h" #include "event_tap.h" #include "locale.h" +#include "carbon.h" #include "tokenize.h" #include "parse.h" #include "hotkey.h" +#include "synthesize.h" #include "mtouch.h" #include "hotload.c" #include "event_tap.c" #include "locale.c" +#include "carbon.c" #include "tokenize.c" #include "parse.c" #include "hotkey.c" +#include "synthesize.c" #include "mtouch.c" -#define internal static -extern bool CGSIsSecureEventInputSet(); +extern CFDictionaryRef CGSCopyCurrentSessionDictionary(void); +extern bool CGSIsSecureEventInputSet(void); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet -#if 0 -#define BEGIN_TIMED_BLOCK() \ - clock_t timed_block_begin = clock() -#define END_TIMED_BLOCK() \ - clock_t timed_block_end = clock(); \ - double timed_block_elapsed = ((timed_block_end - timed_block_begin) / (double)CLOCKS_PER_SEC) * 1000.0f; \ - printf("elapsed time: %.4fms\n", timed_block_elapsed) -#else -#define BEGIN_TIMED_BLOCK() -#define END_TIMED_BLOCK() -#endif - -internal unsigned major_version = 0; -internal unsigned minor_version = 2; -internal unsigned patch_version = 1; - -internal struct mode *current_mode; -internal struct table mode_map; -internal char *config_file; +#define internal static +#define global static -internal void -error(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - exit(EXIT_FAILURE); -} +#define SKHD_CONFIG_FILE ".skhdrc" +#define SKHD_PIDFILE_FMT "/tmp/skhd_%s.pid" -internal void -warn(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -} +global unsigned major_version = 0; +global unsigned minor_version = 3; +global unsigned patch_version = 4; + +global struct carbon_event carbon; +global struct event_tap event_tap; +global struct hotloader hotloader; +global struct mode *current_mode; +global struct table mode_map; +global struct table blacklst; +global bool thwart_hotloader; +global char *config_file; + +internal HOTLOADER_CALLBACK(config_handler); internal void parse_config_helper(char *absolutepath) { struct parser parser; - if (parser_init(&parser, &mode_map, absolutepath)) { - parse_config(&parser); + if (parser_init(&parser, &mode_map, &blacklst, absolutepath)) { + if (!thwart_hotloader) { + hotloader_end(&hotloader); + hotloader_add_file(&hotloader, absolutepath); + } + + if (parse_config(&parser)) { + parser_do_directives(&parser, &hotloader, thwart_hotloader); + } parser_destroy(&parser); + + if (!thwart_hotloader) { + if (hotloader_begin(&hotloader, config_handler)) { + debug("skhd: watching files for changes:\n", absolutepath); + for (int i = 0; i < hotloader.watch_count; ++i) { + debug("\t%s\n", hotloader.watch_list[i].file_info.absolutepath); + } + } else { + warn("skhd: could not start watcher.. hotloading is not enabled\n"); + } + } } else { warn("skhd: could not open file '%s'\n", absolutepath); } + current_mode = table_find(&mode_map, "default"); } internal HOTLOADER_CALLBACK(config_handler) { + BEGIN_TIMED_BLOCK("hotload_config"); + debug("skhd: config-file has been modified.. reloading config\n"); free_mode_map(&mode_map); - parse_config_helper(absolutepath); + free_blacklist(&blacklst); + parse_config_helper(config_file); + END_TIMED_BLOCK(); +} + +internal CF_NOTIFICATION_CALLBACK(keymap_handler) +{ + BEGIN_TIMED_BLOCK("keymap_changed"); + if (initialize_keycode_map()) { + debug("skhd: input source changed.. reloading config\n"); + free_mode_map(&mode_map); + free_blacklist(&blacklst); + parse_config_helper(config_file); + } + END_TIMED_BLOCK(); +} + +internal EVENT_TAP_CALLBACK(key_observer_handler) +{ + switch (type) { + case kCGEventTapDisabledByTimeout: + case kCGEventTapDisabledByUserInput: { + debug("skhd: restarting event-tap\n"); + struct event_tap *event_tap = (struct event_tap *) reference; + CGEventTapEnable(event_tap->handle, 1); + } break; + case kCGEventKeyDown: + case kCGEventFlagsChanged: { + uint32_t flags = CGEventGetFlags(event); + uint32_t keycode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); + + if (keycode == kVK_ANSI_C && flags & 0x40000) { + exit(0); + } + + printf("\rkeycode: 0x%.2X\tflags: ", keycode); + for (int i = 31; i >= 0; --i) { + printf("%c", (flags & (1 << i)) ? '1' : '0'); + } + fflush(stdout); + + return NULL; + } break; + } + return event; } internal EVENT_TAP_CALLBACK(key_handler) @@ -94,23 +151,28 @@ internal EVENT_TAP_CALLBACK(key_handler) switch (type) { case kCGEventTapDisabledByTimeout: case kCGEventTapDisabledByUserInput: { - printf("skhd: restarting event-tap\n"); + debug("skhd: restarting event-tap\n"); struct event_tap *event_tap = (struct event_tap *) reference; CGEventTapEnable(event_tap->handle, 1); } break; case kCGEventKeyDown: { + 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); - bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode); + bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode, &carbon); + END_TIMED_BLOCK(); + if (result) return NULL; } break; case NX_SYSDEFINED: { + if (table_find(&blacklst, carbon.process_name)) return event; if (!current_mode) return event; - struct systemkey systemkey = create_systemkey(event); - if (systemkey.intercept) { - bool result = find_and_exec_hotkey(&systemkey.eventkey, &mode_map, ¤t_mode); + struct hotkey eventkey; + if (intercept_systemkey(event, &eventkey)) { + bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode, &carbon); if (result) return NULL; } } break; @@ -146,25 +208,137 @@ internal MULTITOUCH_CALLBACK(touch_handler) return 0; } +internal void +sigusr1_handler(int signal) +{ + BEGIN_TIMED_BLOCK("sigusr1"); + debug("skhd: SIGUSR1 received.. reloading config\n"); + free_mode_map(&mode_map); + free_blacklist(&blacklst); + parse_config_helper(config_file); + END_TIMED_BLOCK(); +} + +internal pid_t +read_pid_file(void) +{ + char pid_file[255] = {}; + pid_t pid = 0; + + char *user = getenv("USER"); + if (user) { + snprintf(pid_file, sizeof(pid_file), SKHD_PIDFILE_FMT, user); + } else { + error("skhd: could not create path to pid-file because 'env USER' was not set! abort..\n"); + } + + int handle = open(pid_file, O_RDWR); + if (handle == -1) { + error("skhd: could not open pid-file..\n"); + } + + if (flock(handle, LOCK_EX | LOCK_NB) == 0) { + error("skhd: could not locate existing instance..\n"); + } else if (read(handle, &pid, sizeof(pid_t)) == -1) { + error("skhd: could not read pid-file..\n"); + } + + close(handle); + return pid; +} + +internal void +create_pid_file(void) +{ + char pid_file[255] = {}; + pid_t pid = getpid(); + + char *user = getenv("USER"); + if (user) { + snprintf(pid_file, sizeof(pid_file), SKHD_PIDFILE_FMT, user); + } else { + error("skhd: could not create path to pid-file because 'env USER' was not set! abort..\n"); + } + + int handle = open(pid_file, O_CREAT | O_RDWR, 0644); + if (handle == -1) { + error("skhd: could not create pid-file! abort..\n"); + } + + struct flock lockfd = { + .l_start = 0, + .l_len = 0, + .l_pid = pid, + .l_type = F_WRLCK, + .l_whence = SEEK_SET + }; + + if (fcntl(handle, F_SETLK, &lockfd) == -1) { + error("skhd: could not lock pid-file! abort..\n"); + } else if (write(handle, &pid, sizeof(pid_t)) == -1) { + error("skhd: could not write pid-file! abort..\n"); + } + + // NOTE(koekeishiya): we intentionally leave the handle open, + // as calling close(..) will release the lock we just acquired. + + debug("skhd: successfully created pid-file..\n"); +} + internal bool parse_arguments(int argc, char **argv) { int option; - const char *short_option = "vc:"; + const char *short_option = "VPvc:k:t:rho"; struct option long_option[] = { + { "verbose", no_argument, NULL, 'V' }, + { "profile", no_argument, NULL, 'P' }, { "version", no_argument, NULL, 'v' }, { "config", required_argument, NULL, 'c' }, + { "no-hotload", no_argument, NULL, 'h' }, + { "key", required_argument, NULL, 'k' }, + { "text", required_argument, NULL, 't' }, + { "reload", no_argument, NULL, 'r' }, + { "observe", no_argument, NULL, 'o' }, { NULL, 0, NULL, 0 } }; while ((option = getopt_long(argc, argv, short_option, long_option, NULL)) != -1) { switch (option) { + case 'V': { + verbose = true; + } break; + case 'P': { + profile = true; + } break; case 'v': { printf("skhd version %d.%d.%d\n", major_version, minor_version, patch_version); return true; } break; case 'c': { - config_file = strdup(optarg); + config_file = copy_string(optarg); + } break; + case 'h': { + thwart_hotloader = true; + } break; + case 'k': { + synthesize_key(optarg); + return true; + } break; + case 't': { + synthesize_text(optarg); + return true; + } break; + case 'r': { + pid_t pid = read_pid_file(); + if (pid) kill(pid, SIGUSR1); + return true; + } break; + case 'o': { + event_tap.mask = (1 << kCGEventKeyDown) | + (1 << kCGEventFlagsChanged); + event_tap_begin(&event_tap, key_observer_handler); + CFRunLoopRun(); } break; } } @@ -173,7 +347,7 @@ parse_arguments(int argc, char **argv) } internal bool -check_privileges() +check_privileges(void) { bool result; const void *keys[] = { kAXTrustedCheckOptionPrompt }; @@ -192,31 +366,63 @@ check_privileges() } internal void -set_config_path() +use_default_config_path(void) { char *home = getenv("HOME"); - if (home) { - int length = strlen(home) + strlen("/.skhdrc"); - config_file = (char *) malloc(length + 1); - strcpy(config_file, home); - strcat(config_file, "/.skhdrc"); - } else { - config_file = strdup(".skhdrc"); + if (!home) { + error("skhd: could not locate config because 'env HOME' was not set! abort..\n"); } + + int home_len = strlen(home); + int config_len = strlen("/"SKHD_CONFIG_FILE); + int length = home_len + config_len; + config_file = malloc(length + 1); + memcpy(config_file, home, home_len); + memcpy(config_file + home_len, "/"SKHD_CONFIG_FILE, config_len); + config_file[length] = '\0'; } -int main(int argc, char **argv) +internal void +dump_secure_keyboard_entry_process_info(void) { - if (parse_arguments(argc, argv)) { - return EXIT_SUCCESS; + CFDictionaryRef session; + CFNumberRef pid_ref; + char *process_name; + pid_t pid; + + session = CGSCopyCurrentSessionDictionary(); + if (!session) goto err; + + pid_ref = (CFNumberRef) CFDictionaryGetValue(session, CFSTR("kCGSSessionSecureInputPID")); + if (!pid_ref) goto err; + + CFNumberGetValue(pid_ref, CFNumberGetType(pid_ref), &pid); + process_name = find_process_name_for_pid(pid); + + if (process_name) { + error("skhd: secure keyboard entry is enabled by (%lld) '%s'! abort..\n", pid, process_name); } +err: + error("skhd: secure keyboard entry is enabled! abort..\n"); +} + +int main(int argc, char **argv) +{ if (getuid() == 0 || geteuid() == 0) { error("skhd: running as root is not allowed! abort..\n"); } + if (parse_arguments(argc, argv)) { + return EXIT_SUCCESS; + } + + BEGIN_SCOPED_TIMED_BLOCK("total_time"); + BEGIN_SCOPED_TIMED_BLOCK("init"); + create_pid_file(); + if (secure_keyboard_entry_enabled()) { - error("skhd: secure keyboard entry is enabled! abort..\n"); + dump_secure_keyboard_entry_process_info(); } if (!check_privileges()) { @@ -227,24 +433,42 @@ int main(int argc, char **argv) error("skhd: could not initialize keycode map! abort..\n"); } + if (!carbon_event_init(&carbon)) { + error("skhd: could not initialize carbon events! abort..\n"); + } + if (!config_file) { - set_config_path(); + use_default_config_path(); } - printf("skhd: using config '%s'\n", config_file); - table_init(&mode_map, 13, (table_hash_func) hash_mode, (table_compare_func) same_mode); - parse_config_helper(config_file); + CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), + NULL, + &keymap_handler, + kTISNotifySelectedKeyboardInputSourceChanged, + NULL, + CFNotificationSuspensionBehaviorCoalesce); + signal(SIGCHLD, SIG_IGN); + signal(SIGUSR1, sigusr1_handler); + + init_shell(); + table_init(&mode_map, 13, (table_hash_func) hash_string, (table_compare_func) compare_string); + table_init(&blacklst, 13, (table_hash_func) hash_string, (table_compare_func) compare_string); + END_SCOPED_TIMED_BLOCK(); - struct event_tap event_tap = { .mask = (1 << kCGEventKeyDown) | (1 << NX_SYSDEFINED) }; + BEGIN_SCOPED_TIMED_BLOCK("parse_config"); + debug("skhd: using config '%s'\n", config_file); + parse_config_helper(config_file); + END_SCOPED_TIMED_BLOCK(); + + BEGIN_SCOPED_TIMED_BLOCK("begin_eventtap"); + event_tap.mask = (1 << kCGEventKeyDown) | (1 << NX_SYSDEFINED); event_tap_begin(&event_tap, key_handler); + END_SCOPED_TIMED_BLOCK(); struct multitouch multitouch; multitouch_begin(&multitouch, touch_handler); - - struct hotloader hotloader = {}; - hotloader_add_file(&hotloader, config_file); - hotloader_begin(&hotloader, config_handler); + END_SCOPED_TIMED_BLOCK(); CFRunLoopRun(); return EXIT_SUCCESS; |