From 1f639be61318987b6b1a2f7adea6085ee584dcac Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 22 May 2018 18:13:58 +0200 Subject: code cleanup --- src/hotkey.c | 25 +++++++++++++------------ src/hotkey.h | 8 +------- src/skhd.c | 6 +++--- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/hotkey.c b/src/hotkey.c index a2c383a..a37e27c 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -216,21 +216,22 @@ struct hotkey create_eventkey(CGEventRef event) return eventkey; } -struct systemkey create_systemkey(CGEventRef event) +bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey) { CFDataRef event_data = CGEventCreateData(kCFAllocatorDefault, event); - const uint8_t *data = CFDataGetBytePtr(event_data); - uint8_t event_subtype = data[123]; - uint8_t key_code = data[129]; + const uint8_t *data = CFDataGetBytePtr(event_data); + uint8_t key_code = data[129]; uint8_t key_state = data[130]; + uint8_t key_stype = data[123]; CFRelease(event_data); - struct systemkey systemkey = { - .eventkey = { - .key = key_code, - .flags = cgevent_flags_to_hotkey_flags(CGEventGetFlags(event)) | Hotkey_Flag_NX - }, - .intercept = key_state == NX_KEYDOWN && event_subtype == NX_SUBTYPE_AUX_CONTROL_BUTTONS - }; - return systemkey; + bool result = ((key_state == NX_KEYDOWN) && + (key_stype == NX_SUBTYPE_AUX_CONTROL_BUTTONS)); + + if (result) { + eventkey->key = key_code; + eventkey->flags = cgevent_flags_to_hotkey_flags(CGEventGetFlags(event)) | Hotkey_Flag_NX; + } + + return result; } diff --git a/src/hotkey.h b/src/hotkey.h index b0ca504..59ee243 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -66,12 +66,6 @@ struct hotkey struct mode **mode_list; }; -struct systemkey -{ - struct hotkey eventkey; - bool intercept; -}; - static inline void add_flags(struct hotkey *hotkey, uint32_t flag) { @@ -98,7 +92,7 @@ bool same_hotkey(struct hotkey *a, struct hotkey *b); unsigned long hash_hotkey(struct hotkey *a); struct hotkey create_eventkey(CGEventRef event); -struct systemkey create_systemkey(CGEventRef event); +bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey); bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode); void free_mode_map(struct table *mode_map); diff --git a/src/skhd.c b/src/skhd.c index fc220fe..4bfae62 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -106,9 +106,9 @@ internal EVENT_TAP_CALLBACK(key_handler) case NX_SYSDEFINED: { 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); if (result) return NULL; } } break; -- cgit v1.2.3 From 8b0eb94b4331434a8ccd7d367bc8eb5c9ac64aaa Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 26 May 2018 13:30:39 +0200 Subject: update sample --- examples/skhdrc | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/examples/skhdrc b/examples/skhdrc index 2b0e6d8..94c40ca 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -111,6 +111,17 @@ shift + alt - left : chunkc tiling::window --grid-layout 1:2:0:0:1:1 # make floating window fill right-half of screen shift + alt - right : chunkc tiling::window --grid-layout 1:2:1:0:1:1 +# fast focus desktop +cmd + alt - z : chunkc tiling::desktop --focus prev +cmd + alt - c : chunkc tiling::desktop --focus next +cmd + alt - 1 : chunkc tiling::desktop --focus 1 +cmd + alt - 2 : chunkc tiling::desktop --focus 2 +cmd + alt - 3 : chunkc tiling::desktop --focus 3 +cmd + alt - 4 : chunkc tiling::desktop --focus 4 +cmd + alt - 5 : chunkc tiling::desktop --focus 5 +cmd + alt - 6 : chunkc tiling::desktop --focus 6 +cmd + alt - 7 : chunkc tiling::desktop --focus 7 + # send window to desktop shift + alt - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop) shift + alt - z : chunkc tiling::window --send-to-desktop prev @@ -121,17 +132,19 @@ shift + alt - 3 : chunkc tiling::window --send-to-desktop 3 shift + alt - 4 : chunkc tiling::window --send-to-desktop 4 shift + alt - 5 : chunkc tiling::window --send-to-desktop 5 shift + alt - 6 : chunkc tiling::window --send-to-desktop 6 +shift + alt - 7 : chunkc tiling::window --send-to-desktop 7 # send window to desktop and follow focus -shift + cmd - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop); qes -k "cmd + alt - $(chunkc get _last_active_desktop)" -shift + cmd - z : chunkc tiling::window --send-to-desktop prev; qes -k "cmd + alt - z" -shift + cmd - c : chunkc tiling::window --send-to-desktop next; qes -k "cmd + alt - c" -shift + cmd - 1 : chunkc tiling::window --send-to-desktop 1; qes -k "cmd + alt - 1" -shift + cmd - 2 : chunkc tiling::window --send-to-desktop 2; qes -k "cmd + alt - 2" -shift + cmd - 3 : chunkc tiling::window --send-to-desktop 3; qes -k "cmd + alt - 3" -shift + cmd - 4 : chunkc tiling::window --send-to-desktop 4; qes -k "cmd + alt - 4" -shift + cmd - 5 : chunkc tiling::window --send-to-desktop 5; qes -k "cmd + alt - 5" -shift + cmd - 6 : chunkc tiling::window --send-to-desktop 6; qes -k "cmd + alt - 6" +shift + cmd - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop); chunkc tiling::desktop --focus $(chunkc get _last_active_desktop) +shift + cmd - z : chunkc tiling::window --send-to-desktop prev; chunkc tiling::desktop --focus prev +shift + cmd - c : chunkc tiling::window --send-to-desktop next; chunkc tiling::desktop --focus next +shift + cmd - 1 : chunkc tiling::window --send-to-desktop 1; chunkc tiling::desktop --focus 1 +shift + cmd - 2 : chunkc tiling::window --send-to-desktop 2; chunkc tiling::desktop --focus 2 +shift + cmd - 3 : chunkc tiling::window --send-to-desktop 3; chunkc tiling::desktop --focus 3 +shift + cmd - 4 : chunkc tiling::window --send-to-desktop 4; chunkc tiling::desktop --focus 4 +shift + cmd - 5 : chunkc tiling::window --send-to-desktop 5; chunkc tiling::desktop --focus 5 +shift + cmd - 6 : chunkc tiling::window --send-to-desktop 6; chunkc tiling::desktop --focus 6 +shift + cmd - 7 : chunkc tiling::window --send-to-desktop 7; chunkc tiling::desktop --focus 7 # focus monitor ctrl + alt - z : chunkc tiling::monitor -f prev @@ -210,7 +223,3 @@ ctrl + alt - s : chunkc tiling::desktop --layout monocle ctrl + alt - d : chunkc tiling::desktop --layout float ctrl + alt - w : chunkc tiling::desktop --deserialize ~/.chunkwm_layouts/dev_1 - -cmd - 7 : qes -k "shift + alt - 7" -cmd - 8 : qes -k "shift + alt - 8" -cmd - 9 : qes -k "shift + alt - 9" -- cgit v1.2.3 From d110e8451c00005aa90fb3da617309d9bcf06b94 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 26 May 2018 13:48:57 +0200 Subject: update sample binds --- examples/skhdrc | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/skhdrc b/examples/skhdrc index 94c40ca..81b2c8c 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -112,6 +112,7 @@ shift + alt - left : chunkc tiling::window --grid-layout 1:2:0:0:1:1 shift + alt - right : chunkc tiling::window --grid-layout 1:2:1:0:1:1 # fast focus desktop +cmd + alt - x : chunkc tiling::desktop --focus $(chunkc get _last_active_desktop) cmd + alt - z : chunkc tiling::desktop --focus prev cmd + alt - c : chunkc tiling::desktop --focus next cmd + alt - 1 : chunkc tiling::desktop --focus 1 -- cgit v1.2.3 From 7892d89487e410a450de198a7bbdfa535321fdc6 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 27 May 2018 21:57:28 +0200 Subject: fix annoying issue; setsid --- examples/skhdrc | 8 ++++---- src/hotkey.c | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/skhdrc b/examples/skhdrc index 81b2c8c..c531b6f 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -70,7 +70,7 @@ # default, test < cmd - return : open -na /Applications/Terminal.app # open terminal, blazingly fast compared to iTerm/Hyper -cmd - return : open -na /Applications/Kitty.app +cmd - return : /Applications/Kitty.app/Contents/MacOS/kitty --single-instance # open qutebrowser cmd + shift - return : ~/Scripts/qtb.sh @@ -121,7 +121,7 @@ cmd + alt - 3 : chunkc tiling::desktop --focus 3 cmd + alt - 4 : chunkc tiling::desktop --focus 4 cmd + alt - 5 : chunkc tiling::desktop --focus 5 cmd + alt - 6 : chunkc tiling::desktop --focus 6 -cmd + alt - 7 : chunkc tiling::desktop --focus 7 +# cmd + alt - 7 : chunkc tiling::desktop --focus 7 # send window to desktop shift + alt - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop) @@ -133,7 +133,7 @@ shift + alt - 3 : chunkc tiling::window --send-to-desktop 3 shift + alt - 4 : chunkc tiling::window --send-to-desktop 4 shift + alt - 5 : chunkc tiling::window --send-to-desktop 5 shift + alt - 6 : chunkc tiling::window --send-to-desktop 6 -shift + alt - 7 : chunkc tiling::window --send-to-desktop 7 +# shift + alt - 7 : chunkc tiling::window --send-to-desktop 7 # send window to desktop and follow focus shift + cmd - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop); chunkc tiling::desktop --focus $(chunkc get _last_active_desktop) @@ -145,7 +145,7 @@ shift + cmd - 3 : chunkc tiling::window --send-to-desktop 3; chunkc tiling::desk shift + cmd - 4 : chunkc tiling::window --send-to-desktop 4; chunkc tiling::desktop --focus 4 shift + cmd - 5 : chunkc tiling::window --send-to-desktop 5; chunkc tiling::desktop --focus 5 shift + cmd - 6 : chunkc tiling::window --send-to-desktop 6; chunkc tiling::desktop --focus 6 -shift + cmd - 7 : chunkc tiling::window --send-to-desktop 7; chunkc tiling::desktop --focus 7 +# shift + cmd - 7 : chunkc tiling::window --send-to-desktop 7; chunkc tiling::desktop --focus 7 # focus monitor ctrl + alt - z : chunkc tiling::monitor -f prev diff --git a/src/hotkey.c b/src/hotkey.c index a37e27c..0677ae9 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -102,6 +102,7 @@ fork_and_exec(char *command) int cpid = fork(); if (cpid == 0) { + setsid(); char *exec[] = { shell, arg, command, NULL}; int status_code = execvp(exec[0], exec); exit(status_code); -- cgit v1.2.3 From 330f23c284f0dbeaef1e97dd5dfdf6798ab1bc8a Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 27 May 2018 21:57:53 +0200 Subject: v0.2.2 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index 4bfae62..d987594 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -43,7 +43,7 @@ extern bool CGSIsSecureEventInputSet(); internal unsigned major_version = 0; internal unsigned minor_version = 2; -internal unsigned patch_version = 1; +internal unsigned patch_version = 2; internal struct mode *current_mode; internal struct table mode_map; -- cgit v1.2.3 From 54b7f52ec0bd932e66c7f31de0899c79ea463124 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 27 May 2018 22:45:18 +0200 Subject: fix terminal launch bind --- examples/skhdrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/skhdrc b/examples/skhdrc index c531b6f..83b4ef0 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -70,7 +70,7 @@ # default, test < cmd - return : open -na /Applications/Terminal.app # open terminal, blazingly fast compared to iTerm/Hyper -cmd - return : /Applications/Kitty.app/Contents/MacOS/kitty --single-instance +cmd - return : /Applications/Kitty.app/Contents/MacOS/kitty --single-instance -d ~ # open qutebrowser cmd + shift - return : ~/Scripts/qtb.sh -- cgit v1.2.3 From 2804aa714970975632ffaab9eb9a58a6f1ecaeae Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 10 Jul 2018 14:43:13 +0200 Subject: update sample --- examples/skhdrc | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/examples/skhdrc b/examples/skhdrc index 83b4ef0..c7658c2 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -75,6 +75,9 @@ cmd - return : /Applications/Kitty.app/Contents/MacOS/kitty --single-instance -d # open qutebrowser cmd + shift - return : ~/Scripts/qtb.sh +# open mpv +cmd - m : open -na /Applications/mpv.app $(pbpaste) + # close focused window alt - w : chunkc tiling::window --close @@ -111,6 +114,19 @@ shift + alt - left : chunkc tiling::window --grid-layout 1:2:0:0:1:1 # make floating window fill right-half of screen shift + alt - right : chunkc tiling::window --grid-layout 1:2:1:0:1:1 +# create desktop, move window and follow focus +shift + cmd - n : chunkc tiling::desktop --create;\ + id=$(chunkc tiling::query --desktops-for-monitor $(chunkc tiling::query --monitor-for-desktop $(chunkc tiling::query --desktop id)));\ + chunkc tiling::window --send-to-desktop $(echo ${id##* });\ + chunkc tiling::desktop --focus $(echo ${id##* }) + +# create desktop and follow focus +cmd + alt - n : chunkc tiling::desktop --create;\ + id=$(chunkc tiling::query --desktops-for-monitor $(chunkc tiling::query --monitor-for-desktop $(chunkc tiling::query --desktop id)));\ + chunkc tiling::desktop --focus $(echo ${id##* }) +# destroy desktop +cmd + alt - w : chunkc tiling::desktop --annihilate + # fast focus desktop cmd + alt - x : chunkc tiling::desktop --focus $(chunkc get _last_active_desktop) cmd + alt - z : chunkc tiling::desktop --focus prev @@ -211,8 +227,11 @@ alt - q : chunkc tiling::window --toggle fade alt - t : chunkc tiling::window --toggle float;\ chunkc tiling::window --grid-layout 4:4:1:1:2:2 +# toggle sticky +alt - s : chunkc tiling::window --toggle sticky + # toggle sticky, float and resize to picture-in-picture size -alt - s : chunkc tiling::window --toggle sticky;\ +alt - p : chunkc tiling::window --toggle sticky;\ chunkc tiling::window --grid-layout 5:5:4:0:1:1 # float next window to be tiled -- cgit v1.2.3 From a6546422134855457efc0fc020f3d17f5369d709 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 10 Jul 2018 14:45:26 +0200 Subject: #43 passthrough should correctly bypass mode keypress capture setting --- src/hotkey.c | 62 ++++++++++++++++++++++++++++++++++++++++++++---------------- src/hotkey.h | 2 ++ src/skhd.c | 6 +++++- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/hotkey.c b/src/hotkey.c index 0677ae9..75a3647 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -3,6 +3,10 @@ #define internal static #define local_persist static +#define HOTKEY_FOUND ((1) << 0) +#define MODE_CAPTURE(a) ((a) << 1) +#define HOTKEY_PASSTHROUGH(a) ((a) << 2) + #define LRMOD_ALT 0 #define LRMOD_CMD 6 #define LRMOD_CTRL 9 @@ -10,6 +14,9 @@ #define LMOD_OFFS 1 #define RMOD_OFFS 2 +internal char arg[] = "-c"; +internal char *shell = NULL; + internal uint32_t cgevent_lrmod_flag[] = { Event_Mask_Alt, Event_Mask_LAlt, Event_Mask_RAlt, @@ -90,16 +97,9 @@ unsigned long hash_mode(char *key) return hash; } -internal void +internal inline void fork_and_exec(char *command) { - local_persist char arg[] = "-c"; - local_persist char *shell = NULL; - if (!shell) { - char *env_shell = getenv("SHELL"); - shell = env_shell ? env_shell : "/bin/bash"; - } - int cpid = fork(); if (cpid == 0) { setsid(); @@ -109,22 +109,44 @@ fork_and_exec(char *command) } } -internal inline bool -passthrough(struct hotkey *hotkey) +internal inline void +passthrough(struct hotkey *hotkey, uint32_t *capture) { - return !has_flags(hotkey, Hotkey_Flag_Passthrough); + *capture |= HOTKEY_PASSTHROUGH((int)has_flags(hotkey, Hotkey_Flag_Passthrough)); } internal inline struct hotkey * -find_hotkey(struct mode *mode, struct hotkey *hotkey) +find_hotkey(struct mode *mode, struct hotkey *hotkey, uint32_t *capture) { - return table_find(&mode->hotkey_map, hotkey); + struct hotkey *result = table_find(&mode->hotkey_map, hotkey); + if (result) *capture |= HOTKEY_FOUND; + return result; +} + +internal inline bool +should_capture_hotkey(uint32_t capture) +{ + if ((capture & HOTKEY_FOUND)) { + if (!(capture & MODE_CAPTURE(1)) && + !(capture & HOTKEY_PASSTHROUGH(1))) { + return true; + } + + if (!(capture & HOTKEY_PASSTHROUGH(1)) && + (capture & MODE_CAPTURE(1))) { + return true; + } + + return false; + } + + return (capture & MODE_CAPTURE(1)); } bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m) { - bool c = (*m)->capture; - for (struct hotkey *h = find_hotkey(*m, k); h; c |= passthrough(h), h = 0) { + uint32_t c = MODE_CAPTURE((int)(*m)->capture); + for (struct hotkey *h = find_hotkey(*m, k, &c); h; passthrough(h, &c), h = 0) { char *cmd = h->command; if (has_flags(h, Hotkey_Flag_Activate)) { *m = table_find(t, cmd); @@ -132,7 +154,7 @@ bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m) } if (cmd) fork_and_exec(cmd); } - return c; + return should_capture_hotkey(c); } void free_mode_map(struct table *mode_map) @@ -236,3 +258,11 @@ bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey) return result; } + +void init_shell() +{ + if (!shell) { + char *env_shell = getenv("SHELL"); + shell = env_shell ? env_shell : "/bin/bash"; + } +} diff --git a/src/hotkey.h b/src/hotkey.h index 59ee243..c2caed5 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -97,4 +97,6 @@ bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey); bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode); void free_mode_map(struct table *mode_map); +void init_shell(); + #endif diff --git a/src/skhd.c b/src/skhd.c index d987594..34d4377 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -29,7 +29,7 @@ extern bool CGSIsSecureEventInputSet(); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet -#if 0 +#if 1 #define BEGIN_TIMED_BLOCK() \ clock_t timed_block_begin = clock() #define END_TIMED_BLOCK() \ @@ -99,8 +99,11 @@ internal EVENT_TAP_CALLBACK(key_handler) case kCGEventKeyDown: { if (!current_mode) return event; + BEGIN_TIMED_BLOCK(); struct hotkey eventkey = create_eventkey(event); bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode); + END_TIMED_BLOCK(); + if (result) return NULL; } break; case NX_SYSDEFINED: { @@ -205,6 +208,7 @@ int main(int argc, char **argv) table_init(&mode_map, 13, (table_hash_func) hash_mode, (table_compare_func) same_mode); parse_config_helper(config_file); signal(SIGCHLD, SIG_IGN); + init_shell(); struct event_tap event_tap; event_tap.mask = (1 << kCGEventKeyDown) | (1 << NX_SYSDEFINED); -- cgit v1.2.3 From 22147c7516a06e8c2d6db42fe453119a7a49ded2 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 10 Jul 2018 14:46:43 +0200 Subject: disable keypres profiling --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index 34d4377..977a3b2 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -29,7 +29,7 @@ extern bool CGSIsSecureEventInputSet(); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet -#if 1 +#ifdef SKHD_PROFILE #define BEGIN_TIMED_BLOCK() \ clock_t timed_block_begin = clock() #define END_TIMED_BLOCK() \ -- cgit v1.2.3 From 2f370cc12adb6e4050ee1766a5348576083415b1 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 19 Jul 2018 21:02:35 +0200 Subject: simplify hotloader; get rid of calls to malloc in callback --- src/hotload.c | 31 ++++++++++--------------------- src/hotload.h | 1 + 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index ad02d21..6e8ae02 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -50,7 +50,7 @@ resolve_symlink(char *file) } if (!S_ISLNK(buffer.st_mode)) { - return file; + return copy_string(file); } ssize_t size = buffer.st_size + 1; @@ -72,18 +72,9 @@ hotloader_watched_file(struct hotloader *hotloader, char *absolutepath) struct watched_file *result = NULL; for (unsigned index = 0; result == NULL && index < hotloader->watch_count; ++index) { struct watched_file *watch_info = hotloader->watch_list + index; - - char *directory = file_directory(absolutepath); - char *filename = file_name(absolutepath); - - if (strcmp(watch_info->directory, directory) == 0) { - if (strcmp(watch_info->filename, filename) == 0) { - result = watch_info; - } + if (strcmp(watch_info->absolutepath, absolutepath) == 0) { + result = watch_info; } - - free(filename); - free(directory); } return result; @@ -99,7 +90,7 @@ internal FSEVENT_CALLBACK(hotloader_handler) for (unsigned index = 0; index < count; ++index) { char *absolutepath = files[index]; if ((watch_info = hotloader_watched_file(hotloader, absolutepath))) { - hotloader->callback(absolutepath, watch_info->directory, watch_info->filename); + hotloader->callback(watch_info->absolutepath, watch_info->directory, watch_info->filename); } } } @@ -109,14 +100,11 @@ void hotloader_add_file(struct hotloader *hotloader, char *file) if (!hotloader->enabled) { char *real_path = resolve_symlink(file); if (real_path) { - struct watched_file watch_info; - watch_info.directory = file_directory(real_path); - watch_info.filename = file_name(real_path); - - if (real_path != file) { - free(real_path); - } - + struct watched_file watch_info = { + .absolutepath = real_path, + .directory = file_directory(real_path), + .filename = file_name(real_path) + }; hotloader->watch_list[hotloader->watch_count++] = watch_info; printf("hotload: watching file '%s' in directory '%s'\n", watch_info.filename, watch_info.directory); } else { @@ -168,6 +156,7 @@ void hotloader_end(struct hotloader *hotloader) CFIndex count = CFArrayGetCount(hotloader->path); for (unsigned index = 0; index < count; ++index) { CFStringRef string_ref = (CFStringRef) CFArrayGetValueAtIndex(hotloader->path, index); + free(hotloader->watch_list[index].absolutepath); free(hotloader->watch_list[index].directory); free(hotloader->watch_list[index].filename); CFRelease(string_ref); diff --git a/src/hotload.h b/src/hotload.h index ad782fb..78ed07b 100644 --- a/src/hotload.h +++ b/src/hotload.h @@ -9,6 +9,7 @@ typedef HOTLOADER_CALLBACK(hotloader_callback); struct watched_file { + char *absolutepath; char *directory; char *filename; }; -- cgit v1.2.3 From 66f90b7ac685d9e1c79ca4c1075bbe69239f4681 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 19 Jul 2018 21:14:07 +0200 Subject: simplify hotloader; get rid of local var and mark function inline (For readability reasons) --- src/hotload.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index 6e8ae02..854fd8e 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -66,7 +66,7 @@ resolve_symlink(char *file) return NULL; } -internal struct watched_file * +internal inline struct watched_file * hotloader_watched_file(struct hotloader *hotloader, char *absolutepath) { struct watched_file *result = NULL; @@ -88,8 +88,7 @@ internal FSEVENT_CALLBACK(hotloader_handler) struct watched_file *watch_info; for (unsigned index = 0; index < count; ++index) { - char *absolutepath = files[index]; - if ((watch_info = hotloader_watched_file(hotloader, absolutepath))) { + if ((watch_info = hotloader_watched_file(hotloader, files[index]))) { hotloader->callback(watch_info->absolutepath, watch_info->directory, watch_info->filename); } } -- cgit v1.2.3 From 14e1c4237d8bb8e6716952c2a7e29e7232c6df9d Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 19 Jul 2018 21:38:56 +0200 Subject: simplify hotloader; manually inline function --- src/hotload.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index 854fd8e..e5f633f 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -66,30 +66,21 @@ resolve_symlink(char *file) return NULL; } -internal inline struct watched_file * -hotloader_watched_file(struct hotloader *hotloader, char *absolutepath) -{ - struct watched_file *result = NULL; - for (unsigned index = 0; result == NULL && index < hotloader->watch_count; ++index) { - struct watched_file *watch_info = hotloader->watch_list + index; - if (strcmp(watch_info->absolutepath, absolutepath) == 0) { - result = watch_info; - } - } - - return result; -} - internal FSEVENT_CALLBACK(hotloader_handler) { /* NOTE(koekeishiya): We sometimes get two events upon file save. */ struct hotloader *hotloader = (struct hotloader *) context; char **files = (char **) paths; - struct watched_file *watch_info; - for (unsigned index = 0; index < count; ++index) { - if ((watch_info = hotloader_watched_file(hotloader, files[index]))) { - hotloader->callback(watch_info->absolutepath, watch_info->directory, watch_info->filename); + for (unsigned file_index = 0; file_index < count; ++file_index) { + for (unsigned watch_index = 0; watch_index < hotloader->watch_count; ++watch_index) { + struct watched_file *watch_info = hotloader->watch_list + watch_index; + if (strcmp(watch_info->absolutepath, files[file_index]) == 0) { + hotloader->callback(watch_info->absolutepath, + watch_info->directory, + watch_info->filename); + break; + } } } } -- cgit v1.2.3 From e2b0e15880f903f8efe9b6b458450eafa44c99c6 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 19 Jul 2018 22:00:05 +0200 Subject: hotloader: code cleanup --- src/hotload.c | 66 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index e5f633f..23774ff 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -96,7 +96,8 @@ void hotloader_add_file(struct hotloader *hotloader, char *file) .filename = file_name(real_path) }; hotloader->watch_list[hotloader->watch_count++] = watch_info; - printf("hotload: watching file '%s' in directory '%s'\n", watch_info.filename, watch_info.directory); + printf("hotload: watching file '%s' in directory '%s'\n", + watch_info.filename, watch_info.directory); } else { fprintf(stderr, "hotload: could not watch file '%s'\n", file); } @@ -105,10 +106,7 @@ void hotloader_add_file(struct hotloader *hotloader, char *file) bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback) { - if ((hotloader->enabled) || - (!hotloader->watch_count)) { - return false; - } + if (hotloader->enabled || !hotloader->watch_count) return false; CFStringRef string_refs[hotloader->watch_count]; for (unsigned index = 0; index < hotloader->watch_count; ++index) { @@ -117,13 +115,18 @@ bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback) kCFStringEncodingUTF8); } - FSEventStreamContext context = {}; - context.info = (void *) hotloader; + FSEventStreamContext context = { + .info = hotloader + }; + + hotloader->path = (CFArrayRef) CFArrayCreate(NULL, + (const void **) string_refs, + hotloader->watch_count, + &kCFTypeArrayCallBacks); + + hotloader->flags = kFSEventStreamCreateFlagNoDefer | + kFSEventStreamCreateFlagFileEvents; - hotloader->enabled = true; - hotloader->callback = callback; - hotloader->path = (CFArrayRef) CFArrayCreate(NULL, (const void **) string_refs, hotloader->watch_count, &kCFTypeArrayCallBacks); - hotloader->flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents; hotloader->stream = FSEventStreamCreate(NULL, hotloader_handler, &context, @@ -131,28 +134,35 @@ bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback) kFSEventStreamEventIdSinceNow, 0.5, hotloader->flags); - FSEventStreamScheduleWithRunLoop(hotloader->stream, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + + FSEventStreamScheduleWithRunLoop(hotloader->stream, + CFRunLoopGetMain(), + kCFRunLoopDefaultMode); + + hotloader->callback = callback; FSEventStreamStart(hotloader->stream); + hotloader->enabled = true; + return true; } void hotloader_end(struct hotloader *hotloader) { - if (hotloader->enabled) { - FSEventStreamStop(hotloader->stream); - FSEventStreamInvalidate(hotloader->stream); - FSEventStreamRelease(hotloader->stream); - - CFIndex count = CFArrayGetCount(hotloader->path); - for (unsigned index = 0; index < count; ++index) { - CFStringRef string_ref = (CFStringRef) CFArrayGetValueAtIndex(hotloader->path, index); - free(hotloader->watch_list[index].absolutepath); - free(hotloader->watch_list[index].directory); - free(hotloader->watch_list[index].filename); - CFRelease(string_ref); - } - - CFRelease(hotloader->path); - memset(hotloader, 0, sizeof(struct hotloader)); + if (!hotloader->enabled) return; + + FSEventStreamStop(hotloader->stream); + FSEventStreamInvalidate(hotloader->stream); + FSEventStreamRelease(hotloader->stream); + + CFIndex count = CFArrayGetCount(hotloader->path); + for (unsigned index = 0; index < count; ++index) { + CFTypeRef string_ref = CFArrayGetValueAtIndex(hotloader->path, index); + free(hotloader->watch_list[index].absolutepath); + free(hotloader->watch_list[index].directory); + free(hotloader->watch_list[index].filename); + CFRelease(string_ref); } + + CFRelease(hotloader->path); + memset(hotloader, 0, sizeof(struct hotloader)); } -- cgit v1.2.3 From 0b38f59cf5ec2c6de20697959e8c1760f074aef8 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 19 Jul 2018 22:06:40 +0200 Subject: fix function name --- src/locale.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locale.c b/src/locale.c index 464c3ba..be09954 100644 --- a/src/locale.c +++ b/src/locale.c @@ -9,7 +9,7 @@ internal struct table keymap_table; internal char * -copy_cf_string_to_c(CFStringRef string) +copy_cfstring(CFStringRef string) { CFStringEncoding encoding = kCFStringEncodingUTF8; CFIndex length = CFStringGetLength(string); @@ -104,7 +104,7 @@ bool initialize_keycode_map() CFStringRef key_string = cfstring_from_keycode(keyboard_layout, index); if (!key_string) continue; - char *c_key_string = copy_cf_string_to_c(key_string); + char *c_key_string = copy_cfstring(key_string); CFRelease(key_string); if (!c_key_string) continue; -- cgit v1.2.3 From 55a930709a909cca7d518bd838366104df341ae3 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 11:20:18 +0200 Subject: hotloader: support for watching all files of given extension (or all, if not specified) in target directory --- src/hotload.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++----------- src/hotload.h | 25 ++++++++++- 2 files changed, 130 insertions(+), 26 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index 23774ff..ff66b01 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -8,11 +8,18 @@ #define FSEVENT_CALLBACK(name) void name(ConstFSEventStreamRef stream,\ void *context,\ - size_t count,\ - void *paths,\ + size_t file_count,\ + void *file_paths,\ const FSEventStreamEventFlags *flags,\ const FSEventStreamEventId *ids) +internal inline bool +same_string(const char *a, const char *b) +{ + bool result = a && b && strcmp(a, b) == 0; + return result; +} + internal char * copy_string(const char *s) { @@ -42,14 +49,21 @@ file_name(const char *file) } internal char * -resolve_symlink(char *file) +resolve_symlink(char *file, enum watch_kind *kind) { struct stat buffer; if (lstat(file, &buffer) != 0) { + *kind = WATCH_KIND_INVALID; return NULL; } + if (S_ISDIR(buffer.st_mode)) { + *kind = WATCH_KIND_CATALOG; + return copy_string(file); + } + if (!S_ISLNK(buffer.st_mode)) { + *kind = WATCH_KIND_FILE; return copy_string(file); } @@ -59,49 +73,116 @@ resolve_symlink(char *file) if (read != -1) { result[read] = '\0'; + *kind = WATCH_KIND_FILE; return result; } free(result); + *kind = WATCH_KIND_INVALID; return NULL; } +internal char * +same_catalog(char *absolutepath, struct watched_catalog *catalog_info) +{ + char *last_slash = strrchr(absolutepath, '/'); + if (!last_slash) return NULL; + + char *filename = NULL; + + // NOTE(koekeisihya): null terminate '/' to cut off filename + *last_slash = '\0'; + + if (same_string(absolutepath, catalog_info->directory)) { + filename = !catalog_info->extension + ? last_slash + 1 + : same_string(catalog_info->extension, strrchr(last_slash + 1, '.')) + ? last_slash + 1 + : NULL + } + + // NOTE(koekeisihya): revert '/' to restore filename + *last_slash = '/'; + + return filename; +} + +internal inline bool +same_file(char *absolutepath, struct watched_file *file_info) +{ + bool result = same_string(absolutepath, file_info->absolutepath); + return result; +} + internal FSEVENT_CALLBACK(hotloader_handler) { /* NOTE(koekeishiya): We sometimes get two events upon file save. */ struct hotloader *hotloader = (struct hotloader *) context; - char **files = (char **) paths; + char **files = (char **) file_paths; - for (unsigned file_index = 0; file_index < count; ++file_index) { + for (unsigned file_index = 0; file_index < file_count; ++file_index) { for (unsigned watch_index = 0; watch_index < hotloader->watch_count; ++watch_index) { - struct watched_file *watch_info = hotloader->watch_list + watch_index; - if (strcmp(watch_info->absolutepath, files[file_index]) == 0) { - hotloader->callback(watch_info->absolutepath, - watch_info->directory, - watch_info->filename); + struct watched_entry *watch_info = hotloader->watch_list + watch_index; + if (watch_info->kind == WATCH_KIND_CATALOG) { + char *filename = same_catalog(files[file_index], &watch_info->catalog_info); + if (!filename) continue; + + hotloader->callback(files[file_index], + watch_info->catalog_info->directory, + filename); + break; + } else if (watch_info->kind == WATCH_KIND_FILE) { + bool match = same_file(files[file_index], &watch_info->file_info); + if (!match) continue; + + hotloader->callback(watch_info->file_info->absolutepath, + watch_info->file_info->directory, + watch_info->file_info->filename); break; } } } } -void hotloader_add_file(struct hotloader *hotloader, char *file) +bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *extension) { - if (!hotloader->enabled) { - char *real_path = resolve_symlink(file); - if (real_path) { - struct watched_file watch_info = { - .absolutepath = real_path, - .directory = file_directory(real_path), - .filename = file_name(real_path) - }; - hotloader->watch_list[hotloader->watch_count++] = watch_info; - printf("hotload: watching file '%s' in directory '%s'\n", - watch_info.filename, watch_info.directory); - } else { - fprintf(stderr, "hotload: could not watch file '%s'\n", file); + if (hotloader->enabled) return false; + + enum watch_kind kind; + char *real_path = resolve_symlink(directory, &kind); + if (kind != WATCH_KIND_CATALOG) return false; + + hotloader->watch_list[hotloader->watch_count++] = { + .kind = WATCH_KIND_CATALOG, + .catalog_info = { + .directory = real_path, + .extension = extension + ? copy_string(extension) + : NULL } - } + }; + + return true; +} + +bool hotloader_add_file(struct hotloader *hotloader, char *file) +{ + if (hotloader->enabled) return false; + + enum watch_kind kind; + char *real_path = resolve_symlink(file, &kind); + if (kind != WATCH_KIND_FILE) return false; + + hotloader->watch_list[hotloader->watch_count++] = { + .kind = WATCH_KIND_FILE, + .file_info = { + .absolutepath = real_path, + .directory = file_directory(real_path), + .filename = file_name(real_path) + } + }; + + return true; } bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback) diff --git a/src/hotload.h b/src/hotload.h index 78ed07b..7f11f6d 100644 --- a/src/hotload.h +++ b/src/hotload.h @@ -7,6 +7,19 @@ #define HOTLOADER_CALLBACK(name) void name(char *absolutepath, char *directory, char *filename) typedef HOTLOADER_CALLBACK(hotloader_callback); +enum watch_kind +{ + WATCH_KIND_INVALID, + WATCH_KIND_CATALOG, + WATCH_KIND_FILE +}; + +struct watched_catalog +{ + char *directory; + char *extension; +} + struct watched_file { char *absolutepath; @@ -14,6 +27,15 @@ struct watched_file char *filename; }; +struct watched_entry +{ + enum watch_kind kind; + union { + struct watched_file file_info; + struct watched_catalog catalog_info; + }; +}; + struct hotloader { FSEventStreamEventFlags flags; @@ -22,12 +44,13 @@ struct hotloader bool enabled; hotloader_callback *callback; - struct watched_file watch_list[32]; + struct watched_entry watch_list[32]; unsigned watch_count; }; bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback); void hotloader_end(struct hotloader *hotloader); +void hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *extension); void hotloader_add_file(struct hotloader *hotloader, char *file); #endif -- cgit v1.2.3 From e374b97695ab52d4c9036ceae207135e98807e6d Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 11:34:34 +0200 Subject: hotloader: support for watching all files of given extension (or all, if not specified) in target directory --- src/hotload.c | 39 ++++++++++++++++++++++++++------------- src/hotload.h | 6 +++--- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index ff66b01..4f24f15 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -98,7 +98,7 @@ same_catalog(char *absolutepath, struct watched_catalog *catalog_info) ? last_slash + 1 : same_string(catalog_info->extension, strrchr(last_slash + 1, '.')) ? last_slash + 1 - : NULL + : NULL; } // NOTE(koekeisihya): revert '/' to restore filename @@ -128,16 +128,16 @@ internal FSEVENT_CALLBACK(hotloader_handler) if (!filename) continue; hotloader->callback(files[file_index], - watch_info->catalog_info->directory, + watch_info->catalog_info.directory, filename); break; } else if (watch_info->kind == WATCH_KIND_FILE) { bool match = same_file(files[file_index], &watch_info->file_info); if (!match) continue; - hotloader->callback(watch_info->file_info->absolutepath, - watch_info->file_info->directory, - watch_info->file_info->filename); + hotloader->callback(watch_info->file_info.absolutepath, + watch_info->file_info.directory, + watch_info->file_info.filename); break; } } @@ -152,7 +152,7 @@ bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *e char *real_path = resolve_symlink(directory, &kind); if (kind != WATCH_KIND_CATALOG) return false; - hotloader->watch_list[hotloader->watch_count++] = { + struct watched_entry entry = { .kind = WATCH_KIND_CATALOG, .catalog_info = { .directory = real_path, @@ -161,6 +161,7 @@ bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *e : NULL } }; + hotloader->watch_list[hotloader->watch_count++] = entry; return true; } @@ -173,7 +174,7 @@ bool hotloader_add_file(struct hotloader *hotloader, char *file) char *real_path = resolve_symlink(file, &kind); if (kind != WATCH_KIND_FILE) return false; - hotloader->watch_list[hotloader->watch_count++] = { + struct watched_entry entry = { .kind = WATCH_KIND_FILE, .file_info = { .absolutepath = real_path, @@ -181,6 +182,7 @@ bool hotloader_add_file(struct hotloader *hotloader, char *file) .filename = file_name(real_path) } }; + hotloader->watch_list[hotloader->watch_count++] = entry; return true; } @@ -191,8 +193,12 @@ bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback) CFStringRef string_refs[hotloader->watch_count]; for (unsigned index = 0; index < hotloader->watch_count; ++index) { + struct watched_entry *watch_info = hotloader->watch_list + watch_index; + char *directory = watch_info->kind == WATCH_KIND_FILE + ? watch_info->file_info.directory + : watch_info->catalog_info.directory; string_refs[index] = CFStringCreateWithCString(kCFAllocatorDefault, - hotloader->watch_list[index].directory, + directory, kCFStringEncodingUTF8); } @@ -237,11 +243,18 @@ void hotloader_end(struct hotloader *hotloader) CFIndex count = CFArrayGetCount(hotloader->path); for (unsigned index = 0; index < count; ++index) { - CFTypeRef string_ref = CFArrayGetValueAtIndex(hotloader->path, index); - free(hotloader->watch_list[index].absolutepath); - free(hotloader->watch_list[index].directory); - free(hotloader->watch_list[index].filename); - CFRelease(string_ref); + struct watched_entry *watch_info = hotloader->watch_list + index; + if (watch_info->kind == WATCH_KIND_FILE) { + free(watch_info->file_info.absolutepath); + free(watch_info->file_info.directory); + free(watch_info->file_info.filename); + } else if (watch_info->kind == WATCH_KIND_CATALOG) { + free(watch_info->catalog_info.directory); + if (watch_info->catalog_info.extension) { + free(watch_info->catalog_info.extension); + } + } + CFRelease(CFArrayGetValueAtIndex(hotloader->path, index)); } CFRelease(hotloader->path); diff --git a/src/hotload.h b/src/hotload.h index 7f11f6d..f90eb67 100644 --- a/src/hotload.h +++ b/src/hotload.h @@ -18,7 +18,7 @@ struct watched_catalog { char *directory; char *extension; -} +}; struct watched_file { @@ -50,7 +50,7 @@ struct hotloader bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback); void hotloader_end(struct hotloader *hotloader); -void hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *extension); -void hotloader_add_file(struct hotloader *hotloader, char *file); +bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *extension); +bool hotloader_add_file(struct hotloader *hotloader, char *file); #endif -- cgit v1.2.3 From 3bbbd37c796eb7039247cb1625608062dcc898e7 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 11:35:52 +0200 Subject: fix typo --- src/hotload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotload.c b/src/hotload.c index 4f24f15..6610609 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -193,7 +193,7 @@ bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback) CFStringRef string_refs[hotloader->watch_count]; for (unsigned index = 0; index < hotloader->watch_count; ++index) { - struct watched_entry *watch_info = hotloader->watch_list + watch_index; + struct watched_entry *watch_info = hotloader->watch_list + index; char *directory = watch_info->kind == WATCH_KIND_FILE ? watch_info->file_info.directory : watch_info->catalog_info.directory; -- cgit v1.2.3 From b64de269a73c2e3cc820d29f5dd9c10c42a1ee36 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 11:48:50 +0200 Subject: hotloader: properly resolve file kind --- src/hotload.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index 6610609..f925d53 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -49,21 +49,18 @@ file_name(const char *file) } internal char * -resolve_symlink(char *file, enum watch_kind *kind) +resolve_symlink(char *file) { struct stat buffer; if (lstat(file, &buffer) != 0) { - *kind = WATCH_KIND_INVALID; return NULL; } if (S_ISDIR(buffer.st_mode)) { - *kind = WATCH_KIND_CATALOG; return copy_string(file); } if (!S_ISLNK(buffer.st_mode)) { - *kind = WATCH_KIND_FILE; return copy_string(file); } @@ -73,15 +70,32 @@ resolve_symlink(char *file, enum watch_kind *kind) if (read != -1) { result[read] = '\0'; - *kind = WATCH_KIND_FILE; return result; } free(result); - *kind = WATCH_KIND_INVALID; return NULL; } +internal enum watch_kind +resolve_watch_kind(char *file) +{ + struct stat buffer; + if (lstat(file, &buffer) != 0) { + return WATCH_KIND_INVALID; + } + + if (S_ISDIR(buffer.st_mode)) { + return WATCH_KIND_CATALOG; + } + + if (!S_ISLNK(buffer.st_mode)) { + return WATCH_KIND_FILE; + } + + return WATCH_KIND_INVALID; +} + internal char * same_catalog(char *absolutepath, struct watched_catalog *catalog_info) { @@ -148,8 +162,10 @@ bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *e { if (hotloader->enabled) return false; - enum watch_kind kind; - char *real_path = resolve_symlink(directory, &kind); + char *real_path = resolve_symlink(directory); + if (!real_path) return false; + + enum watch_kind kind = resolve_watch_kind(real_path); if (kind != WATCH_KIND_CATALOG) return false; struct watched_entry entry = { @@ -170,8 +186,10 @@ bool hotloader_add_file(struct hotloader *hotloader, char *file) { if (hotloader->enabled) return false; - enum watch_kind kind; - char *real_path = resolve_symlink(file, &kind); + char *real_path = resolve_symlink(file); + if (!real_path) return false; + + enum watch_kind kind = resolve_watch_kind(real_path); if (kind != WATCH_KIND_FILE) return false; struct watched_entry entry = { -- cgit v1.2.3 From d63960e23ac6108d13f4ffd53b5ecd2d9e7a7655 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 13:04:01 +0200 Subject: hotloader: check if init succeeded --- src/skhd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/skhd.c b/src/skhd.c index 977a3b2..b3fa43c 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -215,8 +215,12 @@ int main(int argc, char **argv) event_tap_begin(&event_tap, key_handler); struct hotloader hotloader = {}; - hotloader_add_file(&hotloader, config_file); - hotloader_begin(&hotloader, config_handler); + if (hotloader_add_file(&hotloader, config_file) && + hotloader_begin(&hotloader, config_handler)) { + printf("skhd: watching '%s' for changes\n", config_file); + } else { + printf("skhd: could not watch '%s'\n", config_file); + } CFRunLoopRun(); return EXIT_SUCCESS; -- cgit v1.2.3 From 5b70ba7d55a56d9ed41d41aa992e6d806d3fa85f Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 13:04:19 +0200 Subject: hotloader: check if init succeeded --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index b3fa43c..4d1ce63 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -219,7 +219,7 @@ int main(int argc, char **argv) hotloader_begin(&hotloader, config_handler)) { printf("skhd: watching '%s' for changes\n", config_file); } else { - printf("skhd: could not watch '%s'\n", config_file); + fprintf(stderr, "skhd: could not watch '%s'\n", config_file); } CFRunLoopRun(); -- cgit v1.2.3 From 37a0efa99eaa2ce4a6e7d0a1bda1a64d849e8a31 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 14:23:21 +0200 Subject: update hotloader --- src/hotload.c | 6 +++--- src/hotload.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index f925d53..5836aa9 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -49,7 +49,7 @@ file_name(const char *file) } internal char * -resolve_symlink(char *file) +resolve_symlink(const char *file) { struct stat buffer; if (lstat(file, &buffer) != 0) { @@ -158,7 +158,7 @@ internal FSEVENT_CALLBACK(hotloader_handler) } } -bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *extension) +bool hotloader_add_catalog(struct hotloader *hotloader, const char *directory, const char *extension) { if (hotloader->enabled) return false; @@ -182,7 +182,7 @@ bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *e return true; } -bool hotloader_add_file(struct hotloader *hotloader, char *file) +bool hotloader_add_file(struct hotloader *hotloader, const char *file) { if (hotloader->enabled) return false; diff --git a/src/hotload.h b/src/hotload.h index f90eb67..4b2068f 100644 --- a/src/hotload.h +++ b/src/hotload.h @@ -50,7 +50,7 @@ struct hotloader bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback); void hotloader_end(struct hotloader *hotloader); -bool hotloader_add_catalog(struct hotloader *hotloader, char *directory, char *extension); -bool hotloader_add_file(struct hotloader *hotloader, char *file); +bool hotloader_add_catalog(struct hotloader *hotloader, const char *directory, const char *extension); +bool hotloader_add_file(struct hotloader *hotloader, const char *file); #endif -- cgit v1.2.3 From bee00428694c093baa805858e093815bd59ff112 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 17:17:24 +0200 Subject: hotload: remove incorrectly applied const --- src/hotload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index 5836aa9..1073bfe 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -31,7 +31,7 @@ copy_string(const char *s) } internal char * -file_directory(const char *file) +file_directory(char *file) { char *last_slash = strrchr(file, '/'); *last_slash = '\0'; @@ -41,7 +41,7 @@ file_directory(const char *file) } internal char * -file_name(const char *file) +file_name(char *file) { char *last_slash = strrchr(file, '/'); char *name = copy_string(last_slash + 1); -- cgit v1.2.3 From 0b03bdb56667fc3efcbf2624145409436cac9b78 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 19:26:15 +0200 Subject: #44 synthesize keypress; skhd -k "alt - h" --- src/hotkey.h | 6 ++++ src/parse.c | 55 +++++++++++++++++++++++++++++++++ src/parse.h | 2 ++ src/skhd.c | 16 ++++++++-- src/synthesize.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/synthesize.h | 7 +++++ 6 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 src/synthesize.c create mode 100644 src/synthesize.h 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 #include +#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 + +#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 -- cgit v1.2.3 From 6e9823e29b5c268545bddffee1d0cc095388f3df Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 19:28:21 +0200 Subject: #44 don't spend time reporting errors --- src/parse.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/parse.c b/src/parse.c index a79d8a3..5439a0f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -387,7 +387,6 @@ parse_keypress(struct parser *parser) if (found_modifier) { if (!parser_match(parser, Token_Dash)) { - parser_report_error(parser, Error_Unexpected_Token, "expected '-'"); goto err; } } @@ -399,7 +398,6 @@ parse_keypress(struct parser *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; } @@ -408,9 +406,8 @@ parse_keypress(struct parser *parser) err: free(hotkey); return NULL; - } else { - parser_report_error(parser, Error_Unexpected_Token, "expected modifier or key-literal"); } + return NULL; } -- cgit v1.2.3 From 98e18bcfdf95f98ed129fcb43427c2616f92ec3c Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 20 Jul 2018 19:33:13 +0200 Subject: #44 ocd --- src/synthesize.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/synthesize.c b/src/synthesize.c index 0084604..6eddd6f 100644 --- a/src/synthesize.c +++ b/src/synthesize.c @@ -43,6 +43,7 @@ synthesize_modifiers(struct hotkey *hotkey, bool pressed) void synthesize_key(char *key_string) { if (!initialize_keycode_map()) return; + struct parser parser; parser_init_text(&parser, key_string); -- cgit v1.2.3 From 18c5ce87a15bc12262277fbb2a30245d2d1f8ab9 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 26 Jul 2018 16:37:15 +0200 Subject: update hotloader --- src/hotload.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- src/hotload.h | 36 ++++++----------------------------- 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index 1073bfe..f4d7b44 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -13,6 +13,35 @@ const FSEventStreamEventFlags *flags,\ const FSEventStreamEventId *ids) +enum watch_kind +{ + WATCH_KIND_INVALID, + WATCH_KIND_CATALOG, + WATCH_KIND_FILE +}; + +struct watched_catalog +{ + char *directory; + char *extension; +}; + +struct watched_file +{ + char *absolutepath; + char *directory; + char *filename; +}; + +struct watched_entry +{ + enum watch_kind kind; + union { + struct watched_file file_info; + struct watched_catalog catalog_info; + }; +}; + internal inline bool same_string(const char *a, const char *b) { @@ -24,7 +53,7 @@ internal char * copy_string(const char *s) { unsigned length = strlen(s); - char *result = malloc(length + 1); + char *result = (char *) malloc(length + 1); memcpy(result, s, length); result[length] = '\0'; return result; @@ -65,7 +94,7 @@ resolve_symlink(const char *file) } ssize_t size = buffer.st_size + 1; - char *result = malloc(size); + char *result = (char *) malloc(size); ssize_t read = readlink(file, result, size); if (read != -1) { @@ -158,6 +187,22 @@ internal FSEVENT_CALLBACK(hotloader_handler) } } +internal inline void +hotloader_add_watched_entry(struct hotloader *hotloader, struct watched_entry entry) +{ + if (!hotloader->watch_list) { + hotloader->watch_capacity = 32; + hotloader->watch_list = (struct watched_entry *) malloc(hotloader->watch_capacity * sizeof(struct watched_entry)); + } + + if (hotloader->watch_count >= hotloader->watch_capacity) { + hotloader->watch_capacity = (unsigned) ceil(hotloader->watch_capacity * 1.5f); + hotloader->watch_list = (struct watched_entry *) realloc(hotloader->watch_list, hotloader->watch_capacity * sizeof(struct watched_entry)); + } + + hotloader->watch_list[hotloader->watch_count++] = entry; +} + bool hotloader_add_catalog(struct hotloader *hotloader, const char *directory, const char *extension) { if (hotloader->enabled) return false; @@ -168,7 +213,7 @@ bool hotloader_add_catalog(struct hotloader *hotloader, const char *directory, c enum watch_kind kind = resolve_watch_kind(real_path); if (kind != WATCH_KIND_CATALOG) return false; - struct watched_entry entry = { + hotloader_add_watched_entry(hotloader, (struct watched_entry) { .kind = WATCH_KIND_CATALOG, .catalog_info = { .directory = real_path, @@ -176,8 +221,7 @@ bool hotloader_add_catalog(struct hotloader *hotloader, const char *directory, c ? copy_string(extension) : NULL } - }; - hotloader->watch_list[hotloader->watch_count++] = entry; + }); return true; } @@ -192,15 +236,14 @@ bool hotloader_add_file(struct hotloader *hotloader, const char *file) enum watch_kind kind = resolve_watch_kind(real_path); if (kind != WATCH_KIND_FILE) return false; - struct watched_entry entry = { + hotloader_add_watched_entry(hotloader, (struct watched_entry) { .kind = WATCH_KIND_FILE, .file_info = { .absolutepath = real_path, .directory = file_directory(real_path), .filename = file_name(real_path) } - }; - hotloader->watch_list[hotloader->watch_count++] = entry; + }); return true; } @@ -276,5 +319,6 @@ void hotloader_end(struct hotloader *hotloader) } CFRelease(hotloader->path); + free(hotloader->watch_list); memset(hotloader, 0, sizeof(struct hotloader)); } diff --git a/src/hotload.h b/src/hotload.h index 4b2068f..1d3921b 100644 --- a/src/hotload.h +++ b/src/hotload.h @@ -1,41 +1,16 @@ #ifndef SKHD_HOTLOAD_H #define SKHD_HOTLOAD_H +#ifndef __cplusplus #include +#endif + #include #define HOTLOADER_CALLBACK(name) void name(char *absolutepath, char *directory, char *filename) typedef HOTLOADER_CALLBACK(hotloader_callback); -enum watch_kind -{ - WATCH_KIND_INVALID, - WATCH_KIND_CATALOG, - WATCH_KIND_FILE -}; - -struct watched_catalog -{ - char *directory; - char *extension; -}; - -struct watched_file -{ - char *absolutepath; - char *directory; - char *filename; -}; - -struct watched_entry -{ - enum watch_kind kind; - union { - struct watched_file file_info; - struct watched_catalog catalog_info; - }; -}; - +struct watched_entry; struct hotloader { FSEventStreamEventFlags flags; @@ -44,7 +19,8 @@ struct hotloader bool enabled; hotloader_callback *callback; - struct watched_entry watch_list[32]; + struct watched_entry *watch_list; + unsigned watch_capacity; unsigned watch_count; }; -- cgit v1.2.3 From 3e9a26cda27a0c85be349ec61bffdea91cbf762b Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 26 Jul 2018 19:30:33 +0200 Subject: update readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1446a28..9a25320 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build Status](https://travis-ci.org/koekeishiya/skhd.svg?branch=master)](https://travis-ci.org/koekeishiya/skhd) -**skhd** is a simple hotkey daemon for macOS. It is a stripped version of [**khd**](https://github.com/koekeishiya/khd) +**skhd** is a simple hotkey daemon for macOS. It is a stripped version of [**khd** (*no longer maintained*)](https://github.com/koekeishiya/khd) (although rewritten from scratch), that sacrifices the more advanced features in favour of increased responsiveness and performance. **skhd** is able to hotload its config file, meaning that hotkeys can be edited and updated live while **skhd** is running. @@ -51,6 +51,12 @@ Requires xcode-8 command-line tools. -c | --config: Specify location of config file skhd -c ~/.skhdrc + +-k | --key: Synthesize a keypress (same syntax as when defining a hotkey) + skhd -k "shift + alt - 7" + +-t | --text: Synthesize a line of text + skhd -t "ello, worldã‚·" ``` ### Configuration -- cgit v1.2.3 From dd564609eabfce89ba715d35f8f286e0699f289b Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 26 Jul 2018 19:31:13 +0200 Subject: v0.2.3 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index 972d75e..f4bced7 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -45,7 +45,7 @@ extern bool CGSIsSecureEventInputSet(); internal unsigned major_version = 0; internal unsigned minor_version = 2; -internal unsigned patch_version = 2; +internal unsigned patch_version = 3; internal struct mode *current_mode; internal struct table mode_map; -- cgit v1.2.3 From a1da10d6d393f0a3f1f56f7a9a14d1e868bfcbe7 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 26 Jul 2018 19:35:38 +0200 Subject: update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a25320..58ab70a 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Requires xcode-8 command-line tools. skhd -k "shift + alt - 7" -t | --text: Synthesize a line of text - skhd -t "ello, worldã‚·" + skhd -t "hello, worldã‚·" ``` ### Configuration -- cgit v1.2.3 From 93917e6609a709cdcff4f23ed6c788cfe6bcc838 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Mon, 30 Jul 2018 19:22:08 +0100 Subject: Add meh keyword --- src/hotkey.h | 5 ++++- src/parse.c | 2 +- src/tokenize.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hotkey.h b/src/hotkey.h index e616303..f06ce90 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -49,7 +49,10 @@ enum hotkey_flag Hotkey_Flag_Hyper = (Hotkey_Flag_Cmd | Hotkey_Flag_Alt | Hotkey_Flag_Shift | - Hotkey_Flag_Control) + Hotkey_Flag_Control), + Hotkey_Flag_Meh = (Hotkey_Flag_Control | + Hotkey_Flag_Shift | + Hotkey_Flag_Alt) }; #include "hashtable.h" diff --git a/src/parse.c b/src/parse.c index 5439a0f..0b09f02 100644 --- a/src/parse.c +++ b/src/parse.c @@ -158,7 +158,7 @@ internal enum hotkey_flag modifier_flags_value[] = Hotkey_Flag_Shift, Hotkey_Flag_LShift, Hotkey_Flag_RShift, Hotkey_Flag_Cmd, Hotkey_Flag_LCmd, Hotkey_Flag_RCmd, Hotkey_Flag_Control, Hotkey_Flag_LControl, Hotkey_Flag_RControl, - Hotkey_Flag_Fn, Hotkey_Flag_Hyper, + Hotkey_Flag_Fn, Hotkey_Flag_Hyper, Hotkey_Flag_Meh, }; internal uint32_t diff --git a/src/tokenize.h b/src/tokenize.h index d6bd4bf..3c1bd31 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -7,7 +7,7 @@ static const char *modifier_flags_str[] = "shift", "lshift", "rshift", "cmd", "lcmd", "rcmd", "ctrl", "lctrl", "rctrl", - "fn", "hyper", + "fn", "hyper", "meh", }; static const char *literal_keycode_str[] = -- cgit v1.2.3 From b38b848eb1ad44c73c8e07f2367fdc7cf7bff172 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 31 Jul 2018 16:53:12 +0200 Subject: v0.2.4 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index f4bced7..3f9ead8 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -45,7 +45,7 @@ extern bool CGSIsSecureEventInputSet(); internal unsigned major_version = 0; internal unsigned minor_version = 2; -internal unsigned patch_version = 3; +internal unsigned patch_version = 4; internal struct mode *current_mode; internal struct table mode_map; -- cgit v1.2.3 From 9d07ac498f5c515f9665888f0c814ba71d1782a1 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 00:40:14 +0200 Subject: add profile flag to debug build --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index a70a05e..9042061 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ FRAMEWORKS = -framework Carbon BUILD_PATH = ./bin -BUILD_FLAGS = -std=c99 -Wall -g -O0 +BUILD_FLAGS = -std=c99 -Wall -g -O0 -DSKHD_PROFILE SKHD_SRC = ./src/skhd.c BINS = $(BUILD_PATH)/skhd -- cgit v1.2.3 From 9add5f1470c20d196d71aed73ee907de974237c2 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 00:42:01 +0200 Subject: optimize generation of config-path --- src/skhd.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/skhd.c b/src/skhd.c index 3f9ead8..76eb664 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -43,6 +43,8 @@ extern bool CGSIsSecureEventInputSet(); #define END_TIMED_BLOCK() #endif +#define SKHD_CONFIG_FILE ".skhdrc" + internal unsigned major_version = 0; internal unsigned minor_version = 2; internal unsigned patch_version = 4; @@ -177,17 +179,20 @@ check_privileges() } internal void -set_config_path() +use_default_config_path() { 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) @@ -213,7 +218,7 @@ int main(int argc, char **argv) } if (!config_file) { - set_config_path(); + use_default_config_path(); } printf("skhd: using config '%s'\n", config_file); -- cgit v1.2.3 From c22aecba94046adad69f88b06cf1f4f7d3c35d3e Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 00:58:41 +0200 Subject: update targets --- makefile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/makefile b/makefile index 9042061..50c5ee8 100644 --- a/makefile +++ b/makefile @@ -1,18 +1,21 @@ FRAMEWORKS = -framework Carbon BUILD_PATH = ./bin -BUILD_FLAGS = -std=c99 -Wall -g -O0 -DSKHD_PROFILE +BUILD_FLAGS = -std=c99 -Wall -g -O0 SKHD_SRC = ./src/skhd.c BINS = $(BUILD_PATH)/skhd -.PHONY: all clean install +.PHONY: all clean install profile fast_profile all: clean $(BINS) install: BUILD_FLAGS=-std=c99 -O3 install: clean $(BINS) -segfault: BUILD_FLAGS=-O0 -g -Wall -std=c99 -D_FORTIFY_SOURCE=2 -fstack-protector-strong --param ssp-buffer-size=4 -fPIC -fno-strict-overflow -Wformat -Wformat-security -Werror=format-security -segfault: clean $(BINS) +profile: BUILD_FLAGS=-std=c99 -Wall -g -O0 -DSKHD_PROFILE +profile: clean $(BINS) + +fast_profile: BUILD_FLAGS=-std=c99 -O3 -DSKHD_PROFILE +fast_profile: clean $(BINS) clean: rm -rf $(BUILD_PATH) -- cgit v1.2.3 From ca6057596b43b2a5cfb104c35a84d521599bc919 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 01:14:49 +0200 Subject: remove unused define --- src/hotkey.c | 1 - src/locale.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/hotkey.c b/src/hotkey.c index 75a3647..6448328 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -1,7 +1,6 @@ #include "hotkey.h" #define internal static -#define local_persist static #define HOTKEY_FOUND ((1) << 0) #define MODE_CAPTURE(a) ((a) << 1) diff --git a/src/locale.c b/src/locale.c index be09954..50cf930 100644 --- a/src/locale.c +++ b/src/locale.c @@ -4,7 +4,6 @@ #include #define internal static -#define local_persist static internal struct table keymap_table; -- cgit v1.2.3 From 74915cd6c87dd88f093e8a8819dd8cfca0880a44 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 02:04:03 +0200 Subject: gate debug information behind '--verbose' flag; add profiling support (must be enabled at compile-time) --- README.md | 3 +++ src/log.h | 36 +++++++++++++++++++++++++++++ src/parse.c | 16 ++++++------- src/skhd.c | 77 +++++++++++++++++++++++++++++++++++-------------------------- 4 files changed, 91 insertions(+), 41 deletions(-) create mode 100644 src/log.h diff --git a/README.md b/README.md index 58ab70a..5b60c28 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,9 @@ Requires xcode-8 command-line tools. ### Usage ``` +-V | --verbose: Output debug information + skhd -V + -v | --version: Print version number to stdout skhd -v diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..2e8167b --- /dev/null +++ b/src/log.h @@ -0,0 +1,36 @@ +#ifndef SKHD_LOG_H +#define SKHD_LOG_H + +static bool verbose; + +static inline void +debug(const char *format, ...) +{ + if (!verbose) return; + + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); +} + +static inline void +warn(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +static inline void +error(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + exit(EXIT_FAILURE); +} + +#endif diff --git a/src/parse.c b/src/parse.c index 0b09f02..8dc90dd 100644 --- a/src/parse.c +++ b/src/parse.c @@ -79,7 +79,7 @@ parse_command(struct parser *parser) { struct token command = parser_previous(parser); char *result = copy_string_count(command.text, command.length); - printf("\tcmd: '%s'\n", result); + debug("\tcmd: '%s'\n", result); return result; } @@ -90,7 +90,7 @@ parse_key_hex(struct parser *parser) char *hex = copy_string_count(key.text, key.length); uint32_t keycode = keycode_from_hex(hex); free(hex); - printf("\tkey: '%.*s' (0x%02x)\n", key.length, key.text, keycode); + debug("\tkey: '%.*s' (0x%02x)\n", key.length, key.text, keycode); return keycode; } @@ -100,7 +100,7 @@ parse_key(struct parser *parser) uint32_t keycode; struct token key = parser_previous(parser); keycode = keycode_from_char(*key.text); - printf("\tkey: '%c' (0x%02x)\n", *key.text, keycode); + debug("\tkey: '%c' (0x%02x)\n", *key.text, keycode); return keycode; } @@ -146,7 +146,7 @@ parse_key_literal(struct parser *parser, struct hotkey *hotkey) if (token_equals(key, literal_keycode_str[i])) { handle_implicit_literal_flags(hotkey, i); hotkey->key = literal_keycode_value[i]; - printf("\tkey: '%.*s' (0x%02x)\n", key.length, key.text, hotkey->key); + debug("\tkey: '%.*s' (0x%02x)\n", key.length, key.text, hotkey->key); break; } } @@ -170,7 +170,7 @@ parse_modifier(struct parser *parser) for (int i = 0; i < array_count(modifier_flags_str); ++i) { if (token_equals(modifier, modifier_flags_str[i])) { flags |= modifier_flags_value[i]; - printf("\tmod: '%s'\n", modifier_flags_str[i]); + debug("\tmod: '%s'\n", modifier_flags_str[i]); break; } } @@ -203,7 +203,7 @@ parse_mode(struct parser *parser, struct hotkey *hotkey) } buf_push(hotkey->mode_list, mode); - printf("\tmode: '%s'\n", mode->name); + debug("\tmode: '%s'\n", mode->name); if (parser_match(parser, Token_Comma)) { if (parser_match(parser, Token_Identifier)) { @@ -221,7 +221,7 @@ parse_hotkey(struct parser *parser) memset(hotkey, 0, sizeof(struct hotkey)); bool found_modifier; - printf("hotkey :: #%d {\n", parser->current_token.line); + debug("hotkey :: #%d {\n", parser->current_token.line); if (parser_match(parser, Token_Identifier)) { parse_mode(parser, hotkey); @@ -282,7 +282,7 @@ parse_hotkey(struct parser *parser) goto err; } - printf("}\n"); + debug("}\n"); return hotkey; err: diff --git a/src/skhd.c b/src/skhd.c index 76eb664..7071965 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -8,6 +8,7 @@ #include #include +#include "log.h" #define HASHTABLE_IMPLEMENTATION #include "hashtable.h" #include "sbuffer.h" @@ -32,14 +33,26 @@ extern bool CGSIsSecureEventInputSet(); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet #ifdef SKHD_PROFILE -#define BEGIN_TIMED_BLOCK() \ - clock_t timed_block_begin = clock() +#define BEGIN_SCOPED_TIMED_BLOCK(note) \ + do { \ + char *timed_note = note; \ + clock_t timed_block_begin = clock() +#define END_SCOPED_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("%.4fms (%s)\n", timed_block_elapsed, timed_note); \ + } while (0) +#define BEGIN_TIMED_BLOCK(note) \ + char *timed_note = note; \ + 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) + clock_t timed_block_end = clock(); \ + double timed_block_elapsed = ((timed_block_end - timed_block_begin) / (double)CLOCKS_PER_SEC) * 1000.0f; \ + printf("%.4fms (%s)\n", timed_block_elapsed, timed_note) #else -#define BEGIN_TIMED_BLOCK() +#define BEGIN_SCOPED_TIMED_BLOCK(note) +#define END_SCOPED_TIMED_BLOCK() +#define BEGIN_TIMED_BLOCK(note) #define END_TIMED_BLOCK() #endif @@ -53,25 +66,6 @@ internal struct mode *current_mode; internal struct table mode_map; internal char *config_file; -internal void -error(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - exit(EXIT_FAILURE); -} - -internal void -warn(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -} - internal void parse_config_helper(char *absolutepath) { @@ -87,8 +81,10 @@ parse_config_helper(char *absolutepath) internal HOTLOADER_CALLBACK(config_handler) { + BEGIN_TIMED_BLOCK("hotload_config"); free_mode_map(&mode_map); parse_config_helper(absolutepath); + END_TIMED_BLOCK(); } internal EVENT_TAP_CALLBACK(key_handler) @@ -96,14 +92,14 @@ 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 (!current_mode) return event; - BEGIN_TIMED_BLOCK(); + BEGIN_TIMED_BLOCK("handle_keypress"); struct hotkey eventkey = create_eventkey(event); bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode); END_TIMED_BLOCK(); @@ -127,8 +123,9 @@ internal bool parse_arguments(int argc, char **argv) { int option; - const char *short_option = "vc:k:t:"; + const char *short_option = "Vvc:k:t:"; struct option long_option[] = { + { "verbose", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'v' }, { "config", required_argument, NULL, 'c' }, { "key", required_argument, NULL, 'k' }, @@ -138,6 +135,9 @@ parse_arguments(int argc, char **argv) while ((option = getopt_long(argc, argv, short_option, long_option, NULL)) != -1) { switch (option) { + case 'V': { + verbose = true; + } break; case 'v': { printf("skhd version %d.%d.%d\n", major_version, minor_version, patch_version); return true; @@ -197,6 +197,8 @@ use_default_config_path() int main(int argc, char **argv) { + BEGIN_TIMED_BLOCK("startup"); + BEGIN_SCOPED_TIMED_BLOCK("initialization"); if (parse_arguments(argc, argv)) { return EXIT_SUCCESS; } @@ -221,23 +223,32 @@ int main(int argc, char **argv) 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); signal(SIGCHLD, SIG_IGN); init_shell(); + table_init(&mode_map, 13, (table_hash_func) hash_mode, (table_compare_func) same_mode); + END_SCOPED_TIMED_BLOCK(); + + 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"); struct event_tap event_tap; event_tap.mask = (1 << kCGEventKeyDown) | (1 << NX_SYSDEFINED); event_tap_begin(&event_tap, key_handler); + END_SCOPED_TIMED_BLOCK(); + BEGIN_SCOPED_TIMED_BLOCK("begin_hotloader"); struct hotloader hotloader = {}; if (hotloader_add_file(&hotloader, config_file) && hotloader_begin(&hotloader, config_handler)) { - printf("skhd: watching '%s' for changes\n", config_file); + debug("skhd: watching '%s' for changes\n", config_file); } else { - fprintf(stderr, "skhd: could not watch '%s'\n", config_file); + warn("skhd: could not watch '%s'\n", config_file); } + END_SCOPED_TIMED_BLOCK(); + END_TIMED_BLOCK(); CFRunLoopRun(); return EXIT_SUCCESS; -- cgit v1.2.3 From 3033b1e9ee22102178aee43ebdbed3856bfa38fc Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 02:05:34 +0200 Subject: v0.2.5 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index 7071965..b33b6c7 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -60,7 +60,7 @@ extern bool CGSIsSecureEventInputSet(); internal unsigned major_version = 0; internal unsigned minor_version = 2; -internal unsigned patch_version = 4; +internal unsigned patch_version = 5; internal struct mode *current_mode; internal struct table mode_map; -- cgit v1.2.3 From 187d3507a6a76fb01bbeb21441906eb846c96f18 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 02:28:21 +0200 Subject: update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5b60c28..7591be4 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,9 @@ Requires xcode-8 command-line tools. make install # release version make # debug version + make fast_profile # release version with profiling information + make profile # debug version with profiling information + ### Usage ``` -- cgit v1.2.3 From 28c9b4da0d26c0fff2c1566f4879caa7303f7a8c Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 5 Aug 2018 02:34:17 +0200 Subject: update readme --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 7591be4..4d3197a 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,6 @@ Requires xcode-8 command-line tools. brew install koekeishiya/formulae/skhd brew services start skhd - stdout -> /tmp/skhd.out - stderr -> /tmp/skhd.err - **Source**: Requires xcode-8 command-line tools. -- cgit v1.2.3 From e616840f72bc0c2c18c1011a3d333fc15adabbfd Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 24 Aug 2018 19:55:59 +0200 Subject: #19 properly hotload relative symlinks --- src/hotload.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/hotload.c b/src/hotload.c index f4d7b44..88d286a 100644 --- a/src/hotload.c +++ b/src/hotload.c @@ -93,17 +93,8 @@ resolve_symlink(const char *file) return copy_string(file); } - ssize_t size = buffer.st_size + 1; - char *result = (char *) malloc(size); - ssize_t read = readlink(file, result, size); - - if (read != -1) { - result[read] = '\0'; - return result; - } - - free(result); - return NULL; + char *result = realpath(file, NULL); + return result; } internal enum watch_kind -- cgit v1.2.3 From 822c4603547aa927b62efb6f77dd0c6b56ec7f33 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 28 Aug 2018 19:05:40 +0200 Subject: initial code required for #47 --- src/carbon.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/skhd.c | 14 ++++++++++++-- 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/carbon.c diff --git a/src/carbon.c b/src/carbon.c new file mode 100644 index 0000000..98e2793 --- /dev/null +++ b/src/carbon.c @@ -0,0 +1,56 @@ +#include + +struct carbon_event +{ + EventTargetRef target; + EventHandlerUPP handler; + EventTypeSpec type; + EventHandlerRef handler_ref; + char * volatile process_name; +}; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +static OSStatus +carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) +{ + struct carbon_event *carbon = (struct carbon_event *) context; + + ProcessSerialNumber psn; + if (GetEventParameter(event, + kEventParamProcessID, + typeProcessSerialNumber, + NULL, + sizeof(psn), + NULL, + &psn) != noErr) { + return -1; + } + + CFStringRef process_name_ref; + if (CopyProcessName(&psn, &process_name_ref) == noErr) { + if (carbon->process_name) free(carbon->process_name); + carbon->process_name = copy_cfstring(process_name_ref); + printf("front app changed: %s\n", carbon->process_name); + CFRelease(process_name_ref); + } + + return noErr; +} +#pragma clang diagnostic pop + +bool carbon_event_init(struct carbon_event *carbon) +{ + carbon->target = GetApplicationEventTarget(); + carbon->handler = NewEventHandlerUPP(carbon_event_handler); + carbon->type.eventClass = kEventClassApplication; + carbon->type.eventKind = kEventAppFrontSwitched; + carbon->process_name = NULL; + + return InstallEventHandler(carbon->target, + carbon->handler, + 1, + &carbon->type, + carbon, + &carbon->handler_ref) == noErr; +} diff --git a/src/skhd.c b/src/skhd.c index b33b6c7..1391125 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -27,6 +27,7 @@ #include "parse.c" #include "hotkey.c" #include "synthesize.c" +// #include "carbon.c" #define internal static extern bool CGSIsSecureEventInputSet(); @@ -62,6 +63,9 @@ internal unsigned major_version = 0; internal unsigned minor_version = 2; internal unsigned patch_version = 5; +// internal struct carbon_event carbon; +internal struct event_tap event_tap; +internal struct hotloader hotloader; internal struct mode *current_mode; internal struct table mode_map; internal char *config_file; @@ -219,6 +223,14 @@ int main(int argc, char **argv) error("skhd: could not initialize keycode map! abort..\n"); } + /* + * NOTE(koekeishiya: hooks up event for tracking name of focused process/application + * + * if (!carbon_event_init(&carbon)) { + * error("skhd: could not initialize carbon events! abort..\n"); + * } + */ + if (!config_file) { use_default_config_path(); } @@ -234,13 +246,11 @@ int main(int argc, char **argv) END_SCOPED_TIMED_BLOCK(); BEGIN_SCOPED_TIMED_BLOCK("begin_eventtap"); - struct event_tap event_tap; event_tap.mask = (1 << kCGEventKeyDown) | (1 << NX_SYSDEFINED); event_tap_begin(&event_tap, key_handler); END_SCOPED_TIMED_BLOCK(); BEGIN_SCOPED_TIMED_BLOCK("begin_hotloader"); - struct hotloader hotloader = {}; if (hotloader_add_file(&hotloader, config_file) && hotloader_begin(&hotloader, config_handler)) { debug("skhd: watching '%s' for changes\n", config_file); -- cgit v1.2.3 From 3d90dbeaa9cf13058ed349ba7beb2532c571c7fb Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 14:09:56 +0200 Subject: first iteration --- src/carbon.c | 20 ++++++++------------ src/carbon.h | 17 +++++++++++++++++ src/hotkey.c | 29 ++++++++++++++++++++++++++--- src/hotkey.h | 7 +++++-- src/parse.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-------- src/parse.h | 1 + src/skhd.c | 19 ++++++++----------- src/tokenize.c | 29 +++++++++++++++++++++++++++++ src/tokenize.h | 4 ++++ 9 files changed, 146 insertions(+), 36 deletions(-) create mode 100644 src/carbon.h diff --git a/src/carbon.c b/src/carbon.c index 98e2793..9af606a 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -1,17 +1,10 @@ -#include +#include "carbon.h" -struct carbon_event -{ - EventTargetRef target; - EventHandlerUPP handler; - EventTypeSpec type; - EventHandlerRef handler_ref; - char * volatile process_name; -}; +#define internal static #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" -static OSStatus +internal OSStatus carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) { struct carbon_event *carbon = (struct carbon_event *) context; @@ -29,9 +22,12 @@ carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) CFStringRef process_name_ref; if (CopyProcessName(&psn, &process_name_ref) == noErr) { - if (carbon->process_name) free(carbon->process_name); + if (carbon->process_name) { + free(carbon->process_name); + carbon->process_name = NULL; + } + carbon->process_name = copy_cfstring(process_name_ref); - printf("front app changed: %s\n", carbon->process_name); CFRelease(process_name_ref); } diff --git a/src/carbon.h b/src/carbon.h new file mode 100644 index 0000000..d98792a --- /dev/null +++ b/src/carbon.h @@ -0,0 +1,17 @@ +#ifndef SKHD_CARBON_H +#define SKHD_CARBON_H + +#include + +struct carbon_event +{ + EventTargetRef target; + EventHandlerUPP handler; + EventTypeSpec type; + EventHandlerRef handler_ref; + char * volatile process_name; +}; + +bool carbon_event_init(struct carbon_event *carbon); + +#endif diff --git a/src/hotkey.c b/src/hotkey.c index 6448328..1cb4669 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -96,6 +96,13 @@ unsigned long hash_mode(char *key) return hash; } +internal inline bool +same_string(char *a, char *b) +{ + if (!a || !b) return false; + return same_mode(a, b); +} + internal inline void fork_and_exec(char *command) { @@ -142,14 +149,29 @@ should_capture_hotkey(uint32_t capture) return (capture & MODE_CAPTURE(1)); } -bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m) +internal inline char * +find_process_command_mapping(struct hotkey *hotkey, uint32_t *capture, struct carbon_event *carbon) +{ + for (int i = 0; i < buf_len(hotkey->process_name); ++i) { + if (same_string(carbon->process_name, hotkey->process_name[i])) { + return hotkey->command[i]; + } + } + + *capture &= ~FOUND_HOTKEY; + return NULL; +} + +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); for (struct hotkey *h = find_hotkey(*m, k, &c); h; passthrough(h, &c), h = 0) { - char *cmd = h->command; + char *cmd = h->command[0]; if (has_flags(h, Hotkey_Flag_Activate)) { *m = table_find(t, cmd); cmd = (*m)->command; + } else if (buf_len(h->process_name) > 0) { + cmd = find_process_command_mapping(h, &c, carbon); } if (cmd) fork_and_exec(cmd); } @@ -178,7 +200,8 @@ void free_mode_map(struct table *mode_map) buf_push(freed_pointers, hotkey); buf_free(hotkey->mode_list); - free(hotkey->command); + buf_free(hotkey->process_name); + buf_free(hotkey->command); free(hotkey); next:; } diff --git a/src/hotkey.h b/src/hotkey.h index f06ce90..f96fcea 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -57,6 +57,8 @@ enum hotkey_flag #include "hashtable.h" +struct carbon_event; + struct mode { int line; @@ -71,7 +73,8 @@ struct hotkey { uint32_t flags; uint32_t key; - char *command; + char **process_name; + char **command; struct mode **mode_list; }; @@ -103,7 +106,7 @@ unsigned long hash_hotkey(struct hotkey *a); struct hotkey create_eventkey(CGEventRef event); bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey); -bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode); +bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m, struct carbon_event *carbon); void free_mode_map(struct table *mode_map); void init_shell(); diff --git a/src/parse.c b/src/parse.c index 8dc90dd..46378b2 100644 --- a/src/parse.c +++ b/src/parse.c @@ -74,13 +74,46 @@ keycode_from_hex(char *hex) return result; } -internal char * -parse_command(struct parser *parser) +internal void +parse_command(struct parser *parser, struct hotkey *hotkey) { struct token command = parser_previous(parser); char *result = copy_string_count(command.text, command.length); debug("\tcmd: '%s'\n", result); - return result; + buf_push(hotkey->command, result); +} + +internal void +parse_process_command_list(struct parser *parser, struct hotkey *hotkey) +{ + if (parser_match(parser, Token_String)) { + struct token name_token = parser_previous(parser); + char *name = copy_string_count(name_token.text, name_token.length); + buf_push(hotkey->process_name, name); + if (parser_match(parser, Token_Command)) { + parse_command(parser, hotkey); + parse_process_command_list(parser, hotkey); + } else { + parser_report_error(parser, Error_Unexpected_Token, "expected ':' followed by command"); + } + } else if (parser_match(parser, Token_EndList)) { + if (!buf_len(hotkey->process_name)) { + parser_report_error(parser, Error_Missing_Value, "list must contain at least one value"); + } + } else { + parser_report_error(parser, Error_Unexpected_Token, "expected process command mapping or ']'"); + } +} + +internal void +parse_activate(struct parser *parser, struct hotkey *hotkey) +{ + parse_command(parser, hotkey); + hotkey->flags |= Hotkey_Flag_Activate; + + if (!table_find(parser->mode_map, hotkey->command[0])) { + parser_report_error(parser, Error_Undeclared_Ident, "undeclared identifier"); + } } internal uint32_t @@ -269,12 +302,15 @@ parse_hotkey(struct parser *parser) } if (parser_match(parser, Token_Command)) { - hotkey->command = parse_command(parser); + parse_command(parser, hotkey); + } else if (parser_match(parser, Token_BeginList)) { + parse_process_command_list(parser); + if (parser->error) { + goto err; + } } 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"); + parse_activate(parser, hotkey); + if (parser->error) { goto err; } } else { @@ -468,6 +504,10 @@ void parser_report_error(struct parser *parser, enum parse_error_type error_type 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_Missing_Value) { + fprintf(stderr, "#%d:%d ", parser->previous_token.line, parser->previous_token.cursor); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); } else if (error_type == Error_Duplicate_Ident) { vfprintf(stderr, format, args); } diff --git a/src/parse.h b/src/parse.h index 93fcfc6..9bd11e5 100644 --- a/src/parse.h +++ b/src/parse.h @@ -19,6 +19,7 @@ enum parse_error_type Error_Unexpected_Token, Error_Undeclared_Ident, Error_Duplicate_Ident, + Error_Missing_Value, }; diff --git a/src/skhd.c b/src/skhd.c index 1391125..3725641 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -15,6 +15,7 @@ #include "hotload.h" #include "event_tap.h" #include "locale.h" +#include "carbon.h" #include "tokenize.h" #include "parse.h" #include "hotkey.h" @@ -23,11 +24,11 @@ #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 "carbon.c" #define internal static extern bool CGSIsSecureEventInputSet(); @@ -63,7 +64,7 @@ internal unsigned major_version = 0; internal unsigned minor_version = 2; internal unsigned patch_version = 5; -// internal struct carbon_event carbon; +internal struct carbon_event carbon; internal struct event_tap event_tap; internal struct hotloader hotloader; internal struct mode *current_mode; @@ -105,7 +106,7 @@ internal EVENT_TAP_CALLBACK(key_handler) 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; @@ -115,7 +116,7 @@ internal EVENT_TAP_CALLBACK(key_handler) struct hotkey eventkey; if (intercept_systemkey(event, &eventkey)) { - bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode); + bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode, &carbon); if (result) return NULL; } } break; @@ -223,13 +224,9 @@ int main(int argc, char **argv) error("skhd: could not initialize keycode map! abort..\n"); } - /* - * NOTE(koekeishiya: hooks up event for tracking name of focused process/application - * - * if (!carbon_event_init(&carbon)) { - * error("skhd: could not initialize carbon events! abort..\n"); - * } - */ + if (!carbon_event_init(&carbon)) { + error("skhd: could not initialize carbon events! abort..\n"); + } if (!config_file) { use_default_config_path(); diff --git a/src/tokenize.c b/src/tokenize.c index 5040a7c..8445a71 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -63,6 +63,22 @@ eat_hex(struct tokenizer *tokenizer) } } +internal void +eat_string(struct tokenizer *tokenizer) +{ + /* + * NOTE(koekeishiya): This is NOT proper string parsing code, as we do + * not check for escaped '"' here. At the time of writing, this is only + * supposed to be used for parsing names of processes, and such names + * should not contain escaped quotes at all. We are lazy and simply do + * the most basic implementation that fulfills our current requirement. + */ + + while (*tokenizer->at && *tokenizer->at != '"') { + advance(tokenizer); + } +} + internal inline bool isidentifier(char c) { @@ -130,6 +146,19 @@ get_token(struct tokenizer *tokenizer) case ',': { token.type = Token_Comma; } break; case '<': { token.type = Token_Insert; } break; case '@': { token.type = Token_Capture; } break; + case '[': { token.type = Token_BeginList; } break; + case ']': { token.type = Token_EndList; } break; + case '"': { + token.text = tokenizer->at; + token.line = tokenizer->line; + token.cursor = tokenizer->cursor; + + eat_string(tokenizer); + token.length = tokenizer->at - token.text; + token.type = Token_String; + + advance(tokenizer); + } break; case '#': { eat_comment(tokenizer); token = get_token(tokenizer); diff --git a/src/tokenize.h b/src/tokenize.h index 3c1bd31..73cdb30 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -49,6 +49,10 @@ enum token_type Token_Dash, Token_Arrow, Token_Capture, + Token_String, + + Token_BeginList, + Token_EndList, Token_Unknown, Token_EndOfStream, -- cgit v1.2.3 From 3bdb843b9da0ff4b986c1d117f06b1de4d0f857a Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 14:20:23 +0200 Subject: minor fixes --- src/hotkey.c | 9 +-------- src/parse.c | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/hotkey.c b/src/hotkey.c index 1cb4669..f1d2ab6 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -96,13 +96,6 @@ unsigned long hash_mode(char *key) return hash; } -internal inline bool -same_string(char *a, char *b) -{ - if (!a || !b) return false; - return same_mode(a, b); -} - internal inline void fork_and_exec(char *command) { @@ -158,7 +151,7 @@ find_process_command_mapping(struct hotkey *hotkey, uint32_t *capture, struct ca } } - *capture &= ~FOUND_HOTKEY; + *capture &= ~HOTKEY_FOUND; return NULL; } diff --git a/src/parse.c b/src/parse.c index 46378b2..399f6c6 100644 --- a/src/parse.c +++ b/src/parse.c @@ -304,7 +304,7 @@ parse_hotkey(struct parser *parser) if (parser_match(parser, Token_Command)) { parse_command(parser, hotkey); } else if (parser_match(parser, Token_BeginList)) { - parse_process_command_list(parser); + parse_process_command_list(parser, hotkey); if (parser->error) { goto err; } -- cgit v1.2.3 From 91b00e95831bb02e9ce339f14727b99d8bd528d8 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 14:52:12 +0200 Subject: lowercase process name --- src/carbon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/carbon.c b/src/carbon.c index 9af606a..bd3775b 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -27,6 +27,7 @@ carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) carbon->process_name = NULL; } + CFStringLowercase(process_name_ref, CFLocaleGetSystem()); carbon->process_name = copy_cfstring(process_name_ref); CFRelease(process_name_ref); } -- cgit v1.2.3 From 7fc64890b29d4553c8f7a5067978ade6019c7666 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 15:37:38 +0200 Subject: cleanup error reporting --- src/hotkey.h | 2 -- src/parse.c | 59 +++++++++++++++++++---------------------------------------- src/parse.h | 11 +---------- 3 files changed, 20 insertions(+), 52 deletions(-) diff --git a/src/hotkey.h b/src/hotkey.h index f96fcea..3b26ac3 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -61,8 +61,6 @@ struct carbon_event; struct mode { - int line; - int cursor; char *name; char *command; bool capture; diff --git a/src/parse.c b/src/parse.c index 399f6c6..d9202c5 100644 --- a/src/parse.c +++ b/src/parse.c @@ -22,8 +22,6 @@ find_or_init_default_mode(struct parser *parser) } 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, @@ -94,14 +92,14 @@ parse_process_command_list(struct parser *parser, struct hotkey *hotkey) parse_command(parser, hotkey); parse_process_command_list(parser, hotkey); } else { - parser_report_error(parser, Error_Unexpected_Token, "expected ':' followed by command"); + parser_report_error(parser, parser_peek(parser), "expected ':' followed by command\n"); } } else if (parser_match(parser, Token_EndList)) { if (!buf_len(hotkey->process_name)) { - parser_report_error(parser, Error_Missing_Value, "list must contain at least one value"); + parser_report_error(parser, parser_previous(parser), "list must contain at least one value\n"); } } else { - parser_report_error(parser, Error_Unexpected_Token, "expected process command mapping or ']'"); + parser_report_error(parser, parser_peek(parser), "expected process command mapping or ']'\n"); } } @@ -112,7 +110,7 @@ parse_activate(struct parser *parser, struct hotkey *hotkey) hotkey->flags |= Hotkey_Flag_Activate; if (!table_find(parser->mode_map, hotkey->command[0])) { - parser_report_error(parser, Error_Undeclared_Ident, "undeclared identifier"); + parser_report_error(parser, parser_previous(parser), "undeclared identifier\n"); } } @@ -212,7 +210,7 @@ parse_modifier(struct parser *parser) if (parser_match(parser, Token_Modifier)) { flags |= parse_modifier(parser); } else { - parser_report_error(parser, Error_Unexpected_Token, "expected modifier"); + parser_report_error(parser, parser_peek(parser), "expected modifier\n"); } } @@ -231,7 +229,7 @@ parse_mode(struct parser *parser, struct hotkey *hotkey) 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"); + parser_report_error(parser, identifier, "undeclared identifier\n"); return; } @@ -242,7 +240,7 @@ parse_mode(struct parser *parser, struct hotkey *hotkey) if (parser_match(parser, Token_Identifier)) { parse_mode(parser, hotkey); } else { - parser_report_error(parser, Error_Unexpected_Token, "expected identifier"); + parser_report_error(parser, parser_peek(parser), "expected identifier\n"); } } } @@ -265,7 +263,7 @@ parse_hotkey(struct parser *parser) if (buf_len(hotkey->mode_list) > 0) { if (!parser_match(parser, Token_Insert)) { - parser_report_error(parser, Error_Unexpected_Token, "expected '<'"); + parser_report_error(parser, parser_peek(parser), "expected '<'\n"); goto err; } } else { @@ -281,7 +279,7 @@ parse_hotkey(struct parser *parser) if (found_modifier) { if (!parser_match(parser, Token_Dash)) { - parser_report_error(parser, Error_Unexpected_Token, "expected '-'"); + parser_report_error(parser, parser_peek(parser), "expected '-'\n"); goto err; } } @@ -293,7 +291,7 @@ parse_hotkey(struct parser *parser) } else if (parser_match(parser, Token_Literal)) { parse_key_literal(parser, hotkey); } else { - parser_report_error(parser, Error_Unexpected_Token, "expected key-literal"); + parser_report_error(parser, parser_peek(parser), "expected key-literal\n"); goto err; } @@ -314,7 +312,7 @@ parse_hotkey(struct parser *parser) goto err; } } else { - parser_report_error(parser, Error_Unexpected_Token, "expected ':' followed by command or ';' followed by mode"); + parser_report_error(parser, parser_peek(parser), "expected ':' followed by command or ';' followed by mode\n"); goto err; } @@ -332,8 +330,6 @@ 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, @@ -357,19 +353,17 @@ parse_mode_decl(struct parser *parser) 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); + struct token identifier = parser_previous(parser); + struct mode *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); + parser_report_error(parser, identifier, "duplicate declaration '%s'\n", mode->name); } else { table_add(parser->mode_map, mode->name, mode); } } else { - parser_report_error(parser, Error_Unexpected_Token, "expected identifier"); + parser_report_error(parser, parser_peek(parser), "expected identifier\n"); } } @@ -398,7 +392,7 @@ void parse_config(struct parser *parser) } else if (parser_check(parser, Token_Decl)) { parse_declaration(parser); } else { - parser_report_error(parser, Error_Unexpected_Token, "expected decl, modifier or key-literal"); + parser_report_error(parser, parser_peek(parser), "expected decl, modifier or key-literal\n"); } } } @@ -491,27 +485,12 @@ bool parser_match(struct parser *parser, enum token_type type) return false; } -void parser_report_error(struct parser *parser, enum parse_error_type error_type, const char *format, ...) +void parser_report_error(struct parser *parser, struct token token, const char *format, ...) { va_list args; va_start(args, format); - - 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_Missing_Value) { - fprintf(stderr, "#%d:%d ", parser->previous_token.line, parser->previous_token.cursor); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - } else if (error_type == Error_Duplicate_Ident) { - vfprintf(stderr, format, args); - } - + fprintf(stderr, "#%d:%d ", token.line, token.cursor); + vfprintf(stderr, format, args); va_end(args); parser->error = true; } diff --git a/src/parse.h b/src/parse.h index 9bd11e5..35bc62e 100644 --- a/src/parse.h +++ b/src/parse.h @@ -14,15 +14,6 @@ struct parser bool error; }; -enum parse_error_type -{ - Error_Unexpected_Token, - Error_Undeclared_Ident, - Error_Duplicate_Ident, - Error_Missing_Value, -}; - - void parse_config(struct parser *parser); struct hotkey *parse_keypress(struct parser *parser); @@ -35,6 +26,6 @@ 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, ...); +void parser_report_error(struct parser *parser, struct token token, const char *format, ...); #endif -- cgit v1.2.3 From f8e4925d683530f04f432fe9998d0e53b907dbec Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 15:47:16 +0200 Subject: properly free memory allocated for process_names and commands --- src/hotkey.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/hotkey.c b/src/hotkey.c index f1d2ab6..7b3008a 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -193,8 +193,15 @@ void free_mode_map(struct table *mode_map) buf_push(freed_pointers, hotkey); buf_free(hotkey->mode_list); - buf_free(hotkey->process_name); - buf_free(hotkey->command); + + for (int i = 0; i < buf_len(hotkey->process_name); ++i) { + free(hotkey->process_name[i]); + } + + for (int i = 0; i < buf_len(hotkey->command); ++i) { + free(hotkey->command[i]); + } + free(hotkey); next:; } -- cgit v1.2.3 From 40bc9b361d655348b182af061adf2cc374f737b5 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 15:48:41 +0200 Subject: properly free memory allocated for process_names and commands --- src/hotkey.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotkey.c b/src/hotkey.c index 7b3008a..10d5f5e 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -197,10 +197,12 @@ void free_mode_map(struct table *mode_map) for (int i = 0; i < buf_len(hotkey->process_name); ++i) { free(hotkey->process_name[i]); } + buf_free(hotkey->process_name); for (int i = 0; i < buf_len(hotkey->command); ++i) { free(hotkey->command[i]); } + buf_free(hotkey->command); free(hotkey); next:; -- cgit v1.2.3 From a86558bf86a52ba7a8d5de24abc782b990711afe Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 16:01:02 +0200 Subject: cast --- src/carbon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/carbon.c b/src/carbon.c index bd3775b..285468b 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -27,7 +27,9 @@ carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) carbon->process_name = NULL; } - CFStringLowercase(process_name_ref, CFLocaleGetSystem()); + // NOTE(koekeishiya): Might want to call ProcessInformationCopyDictionary instead. + // http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/Process_Manager/prmref_main/function_group_1.html#//apple_ref/c/func/GetProcessInformationCopyDictionary + CFStringLowercase((CFMutableStringRef)process_name_ref, CFLocaleGetSystem()); carbon->process_name = copy_cfstring(process_name_ref); CFRelease(process_name_ref); } -- cgit v1.2.3 From 6f55631142457b6865e955bc2f204525027d4d4f Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 16:45:46 +0200 Subject: automatically convert to lowercase --- src/carbon.c | 5 ++--- src/parse.c | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/carbon.c b/src/carbon.c index 285468b..d722104 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -27,10 +27,9 @@ carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) carbon->process_name = NULL; } - // NOTE(koekeishiya): Might want to call ProcessInformationCopyDictionary instead. - // http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/Process_Manager/prmref_main/function_group_1.html#//apple_ref/c/func/GetProcessInformationCopyDictionary - CFStringLowercase((CFMutableStringRef)process_name_ref, CFLocaleGetSystem()); carbon->process_name = copy_cfstring(process_name_ref); + for (char *s = carbon->process_name; *s; ++s) *s = tolower(*s); + CFRelease(process_name_ref); } diff --git a/src/parse.c b/src/parse.c index d9202c5..a5760b7 100644 --- a/src/parse.c +++ b/src/parse.c @@ -87,6 +87,7 @@ parse_process_command_list(struct parser *parser, struct hotkey *hotkey) if (parser_match(parser, Token_String)) { struct token name_token = parser_previous(parser); char *name = copy_string_count(name_token.text, name_token.length); + for (char *s = name; *s; ++s) *s = tolower(*s); buf_push(hotkey->process_name, name); if (parser_match(parser, Token_Command)) { parse_command(parser, hotkey); -- cgit v1.2.3 From b5a35a9bcbf237cb86105c556b535fb689d4834c Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 17:04:22 +0200 Subject: cleanup and set active app on init --- src/carbon.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/carbon.c b/src/carbon.c index d722104..043685b 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -4,6 +4,28 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" +internal inline char * +find_process_name_for_psn(ProcessSerialNumber *psn) +{ + CFStringRef process_name_ref; + if (CopyProcessName(psn, &process_name_ref) == noErr) { + char *process_name = copy_cfstring(process_name_ref); + for (char *s = process_name; *s; ++s) *s = tolower(*s); + CFRelease(process_name_ref); + return process_name; + } + return NULL; +} + +internal inline char * +find_active_process_name(void) +{ + ProcessSerialNumber psn; + GetFrontProcess(&psn); + return find_process_name_for_psn(&psn); +} +#pragma clang diagnostic pop + internal OSStatus carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) { @@ -20,22 +42,15 @@ carbon_event_handler(EventHandlerCallRef ref, EventRef event, void *context) return -1; } - CFStringRef process_name_ref; - if (CopyProcessName(&psn, &process_name_ref) == noErr) { - if (carbon->process_name) { - free(carbon->process_name); - carbon->process_name = NULL; - } - - carbon->process_name = copy_cfstring(process_name_ref); - for (char *s = carbon->process_name; *s; ++s) *s = tolower(*s); - - CFRelease(process_name_ref); + if (carbon->process_name) { + free(carbon->process_name); + carbon->process_name = NULL; } + carbon->process_name = find_process_name_for_psn(&psn); + return noErr; } -#pragma clang diagnostic pop bool carbon_event_init(struct carbon_event *carbon) { @@ -43,7 +58,8 @@ bool carbon_event_init(struct carbon_event *carbon) carbon->handler = NewEventHandlerUPP(carbon_event_handler); carbon->type.eventClass = kEventClassApplication; carbon->type.eventKind = kEventAppFrontSwitched; - carbon->process_name = NULL; + carbon->process_name = find_active_process_name(); + printf("active %s\n", carbon->process_name); return InstallEventHandler(carbon->target, carbon->handler, -- cgit v1.2.3 From 109802090a08b4792e14c0eabece44fb3a6d6729 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 17:07:28 +0200 Subject: #47 remove debug print --- src/carbon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/carbon.c b/src/carbon.c index 043685b..c6fddef 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -59,7 +59,6 @@ bool carbon_event_init(struct carbon_event *carbon) carbon->type.eventClass = kEventClassApplication; carbon->type.eventKind = kEventAppFrontSwitched; carbon->process_name = find_active_process_name(); - printf("active %s\n", carbon->process_name); return InstallEventHandler(carbon->target, carbon->handler, -- cgit v1.2.3 From 8c1a567dc93697f60b33cc60066a234df7e0e1ca Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 17:27:55 +0200 Subject: update readme and sample config --- README.md | 45 ++++++++++++++++++++++----------------- examples/skhdrc | 66 ++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 4d3197a..b645b3b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ feature comparison between **skhd** and **khd** | hotkey passthrough | [x] | [x] | | modal hotkey-system | [x] | [x] | | use media-keys as hotkey | [x] | [ ] | -| application specific hotkey| [ ] | [x] | +| application specific hotkey| [x] | [x] | | modifier only hotkey | [ ] | [x] | | caps-lock as hotkey | [ ] | [x] | | mouse-buttons as hotkey | [ ] | [x] | @@ -72,35 +72,42 @@ A list of all built-in modifier and literal keywords can be found [here](https:/ A hotkey is written according to the following rules: ``` -hotkey = '<' | +hotkey = '<' | -mode = 'name of mode' | ',' +mode = 'name of mode' | ',' -action = ':' | '->' ':' - ';' | '->' ';' +action = '[' ']' | '->' '[' ']' + ':' | '->' ':' + ';' | '->' ';' -keysym = '-' | +keysym = '-' | -mod = 'modifier keyword' | '+' +mod = 'modifier keyword' | '+' -key = | +key = | -literal = 'single letter or built-in keyword' +literal = 'single letter or built-in keyword' -keycode = 'apple keyboard kVK_ values (0x3C)' +keycode = 'apple keyboard kVK_ values (0x3C)' --> = keypress is not consumed by skhd +proc_map_lst = * -command = command is executed through '$SHELL -c' and - follows valid shell syntax. if the $SHELL environment - variable is not set, it will default to '/bin/bash'. - when bash is used, the ';' delimeter can be specified - to chain commands. +proc_map = ':' - to allow a command to extend into multiple lines, - prepend '\' at the end of the previous line. +string = '"' 'sequence of characters' '"' - an EOL character signifies the end of the bind. +command = command is executed through '$SHELL -c' and + follows valid shell syntax. if the $SHELL environment + variable is not set, it will default to '/bin/bash'. + when bash is used, the ';' delimeter can be specified + to chain commands. + + to allow a command to extend into multiple lines, + prepend '\' at the end of the previous line. + + an EOL character signifies the end of the bind. + +-> = keypress is not consumed by skhd ``` A mode is declared according to the following rules: diff --git a/examples/skhdrc b/examples/skhdrc index c7658c2..5336e71 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -3,36 +3,42 @@ # # A hotkey is written according to the following rules: # -# hotkey = '<' | +# hotkey = '<' | # -# mode = 'name of mode' | ',' +# mode = 'name of mode' | ',' # -# action = ':' | '->' ':' -# ';' | '->' ';' +# action = '[' ']' | '->' '[' ']' +# ':' | '->' ':' +# ';' | '->' ';' # -# keysym = '-' | +# keysym = '-' | # -# mod = 'built-in mod keyword' | '+' +# mod = 'modifier keyword' | '+' # -# key = | +# key = | # -# literal = 'single letter or built-in keyword' +# literal = 'single letter or built-in keyword' # -# keycode = 'apple keyboard kVK_ values (0x3C)' +# keycode = 'apple keyboard kVK_ values (0x3C)' # -# -> = keypress is not consumed by skhd +# proc_map_lst = * # -# command = command is executed through '$SHELL -c' and -# follows valid shell syntax. if the $SHELL environment -# variable is not set, it will default to '/bin/bash'. -# when bash is used, the ';' delimeter can be specified -# to chain commands. +# proc_map = ':' # -# to allow a command to extend into multiple lines, -# prepend '\' at the end of the previous line. +# string = '"' 'sequence of characters' '"' # -# an EOL character signifies the end of the bind. +# command = command is executed through '$SHELL -c' and +# follows valid shell syntax. if the $SHELL environment +# variable is not set, it will default to '/bin/bash'. +# when bash is used, the ';' delimeter can be specified +# to chain commands. # +# to allow a command to extend into multiple lines, +# prepend '\' at the end of the previous line. +# +# an EOL character signifies the end of the bind. +# +# -> = keypress is not consumed by skhd # # NOTE(koekeishiya): A mode is declared according to the following rules: # @@ -43,16 +49,16 @@ # # @ = capture keypresses regardless of being bound to an action # -# command = command is executed through '$SHELL -c' and -# follows valid shell syntax. if the $SHELL environment -# variable is not set, it will default to '/bin/bash'. -# when bash is used, the ';' delimeter can be specified -# to chain commands. +# command = command is executed through '$SHELL -c' and +# follows valid shell syntax. if the $SHELL environment +# variable is not set, it will default to '/bin/bash'. +# when bash is used, the ';' delimeter can be specified +# to chain commands. # -# to allow a command to extend into multiple lines, -# prepend '\' at the end of the previous line. +# to allow a command to extend into multiple lines, +# prepend '\' at the end of the previous line. # -# an EOL character signifies the end of the bind. +# an EOL character signifies the end of the bind. # add an on_enter command to the default mode # :: default : chunkc border::color 0xff775759 @@ -69,6 +75,14 @@ # launch a new terminal instance when in either 'default' or 'test' mode # default, test < cmd - return : open -na /Applications/Terminal.app +# application specific bindings +# +# cmd - n [ +# "kitty" : echo "hello kitty" +# "qutebrowser" : echo "hello qutebrowser" +# "finder" : false +# ] + # open terminal, blazingly fast compared to iTerm/Hyper cmd - return : /Applications/Kitty.app/Contents/MacOS/kitty --single-instance -d ~ -- cgit v1.2.3 From d9712b3b72db49d8afd214f95d308a1f1261aead Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 30 Aug 2018 17:32:17 +0200 Subject: v0.3.0 --- src/skhd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/skhd.c b/src/skhd.c index 3725641..e8a59f3 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -61,8 +61,8 @@ extern bool CGSIsSecureEventInputSet(); #define SKHD_CONFIG_FILE ".skhdrc" internal unsigned major_version = 0; -internal unsigned minor_version = 2; -internal unsigned patch_version = 5; +internal unsigned minor_version = 3; +internal unsigned patch_version = 0; internal struct carbon_event carbon; internal struct event_tap event_tap; -- cgit v1.2.3 From e66bfd7ae9c1ccbb3cd88f3ad253f9e2cafaeb8e Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 8 Sep 2018 12:10:35 +0200 Subject: . --- src/carbon.h.tu | Bin 0 -> 14977496 bytes src/event_tap.h.tu | Bin 0 -> 14977324 bytes src/hotkey.c | 2 +- src/hotkey.h | 2 +- src/locale.c | 2 +- src/locale.h | 2 +- src/skhd.c | 4 ++-- 7 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 src/carbon.h.tu create mode 100644 src/event_tap.h.tu diff --git a/src/carbon.h.tu b/src/carbon.h.tu new file mode 100644 index 0000000..69daf6b Binary files /dev/null and b/src/carbon.h.tu differ diff --git a/src/event_tap.h.tu b/src/event_tap.h.tu new file mode 100644 index 0000000..19867fb Binary files /dev/null and b/src/event_tap.h.tu differ diff --git a/src/hotkey.c b/src/hotkey.c index 10d5f5e..0c8b9f0 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -283,7 +283,7 @@ bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey) return result; } -void init_shell() +void init_shell(void) { if (!shell) { char *env_shell = getenv("SHELL"); diff --git a/src/hotkey.h b/src/hotkey.h index 3b26ac3..71d2e1d 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -107,6 +107,6 @@ bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey); bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m, struct carbon_event *carbon); void free_mode_map(struct table *mode_map); -void init_shell(); +void init_shell(void); #endif diff --git a/src/locale.c b/src/locale.c index 50cf930..b4c61dc 100644 --- a/src/locale.c +++ b/src/locale.c @@ -85,7 +85,7 @@ cfstring_from_keycode(UCKeyboardLayout *keyboard_layout, CGKeyCode keycode) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" -bool initialize_keycode_map() +bool initialize_keycode_map(void) { TISInputSourceRef keyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); CFDataRef uchr = (CFDataRef) TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); diff --git a/src/locale.h b/src/locale.h index 653bcfd..d00a72e 100644 --- a/src/locale.h +++ b/src/locale.h @@ -3,7 +3,7 @@ #include -bool initialize_keycode_map(); +bool initialize_keycode_map(void); uint32_t keycode_from_char(char key); #endif diff --git a/src/skhd.c b/src/skhd.c index e8a59f3..de73ccd 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -165,7 +165,7 @@ parse_arguments(int argc, char **argv) } internal bool -check_privileges() +check_privileges(void) { bool result; const void *keys[] = { kAXTrustedCheckOptionPrompt }; @@ -184,7 +184,7 @@ check_privileges() } internal void -use_default_config_path() +use_default_config_path(void) { char *home = getenv("HOME"); if (!home) { -- cgit v1.2.3 From eeab7a37a2e017ea36d2385fd6b03b9572f181aa Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 8 Sep 2018 12:10:59 +0200 Subject: . --- src/carbon.h.tu | Bin 14977496 -> 0 bytes src/event_tap.h.tu | Bin 14977324 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/carbon.h.tu delete mode 100644 src/event_tap.h.tu diff --git a/src/carbon.h.tu b/src/carbon.h.tu deleted file mode 100644 index 69daf6b..0000000 Binary files a/src/carbon.h.tu and /dev/null differ diff --git a/src/event_tap.h.tu b/src/event_tap.h.tu deleted file mode 100644 index 19867fb..0000000 Binary files a/src/event_tap.h.tu and /dev/null differ -- cgit v1.2.3 From 0e703c3f275f6d4477f452acfb9cf9c73b9d9f1e Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 23 Feb 2019 14:49:56 +0100 Subject: #59 reload config when input source changes --- src/hashtable.h | 5 ++++- src/locale.c | 15 ++++++++------- src/locale.h | 8 ++++++++ src/skhd.c | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/hashtable.h b/src/hashtable.h index 42d0166..f593432 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -76,7 +76,10 @@ void table_free(struct table *table) bucket = next; } } - free(table->buckets); + if (table->buckets) { + free(table->buckets); + table->buckets = NULL; + } } void *table_find(struct table *table, void *key) diff --git a/src/locale.c b/src/locale.c index b4c61dc..3c03f6a 100644 --- a/src/locale.c +++ b/src/locale.c @@ -83,6 +83,13 @@ cfstring_from_keycode(UCKeyboardLayout *keyboard_layout, CGKeyCode keycode) return NULL; } +uint32_t keycode_from_char(char key) +{ + char lookup_key[] = { key, '\0' }; + uint32_t keycode = (uint32_t) table_find(&keymap_table, &lookup_key); + return keycode; +} + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" bool initialize_keycode_map(void) @@ -94,6 +101,7 @@ bool initialize_keycode_map(void) UCKeyboardLayout *keyboard_layout = (UCKeyboardLayout *) CFDataGetBytePtr(uchr); if (!keyboard_layout) return false; + table_free(&keymap_table); table_init(&keymap_table, 131, (table_hash_func) hash_keymap, @@ -113,10 +121,3 @@ bool initialize_keycode_map(void) return true; } #pragma clang diagnostic pop - -uint32_t keycode_from_char(char key) -{ - char lookup_key[] = { key, '\0' }; - uint32_t keycode = (uint32_t) table_find(&keymap_table, &lookup_key); - return keycode; -} diff --git a/src/locale.h b/src/locale.h index d00a72e..7a1ec6e 100644 --- a/src/locale.h +++ b/src/locale.h @@ -3,6 +3,14 @@ #include +#define CF_NOTIFICATION_CALLBACK(name) \ + void name(CFNotificationCenterRef center, \ + void *observer, \ + CFNotificationName name, \ + const void *object, \ + CFDictionaryRef userInfo) +typedef CF_NOTIFICATION_CALLBACK(cf_notification_callback); + bool initialize_keycode_map(void); uint32_t keycode_from_char(char key); diff --git a/src/skhd.c b/src/skhd.c index de73ccd..77168c8 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -87,11 +87,23 @@ parse_config_helper(char *absolutepath) 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); 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); + parse_config_helper(config_file); + } + END_TIMED_BLOCK(); +} + internal EVENT_TAP_CALLBACK(key_handler) { switch (type) { @@ -232,6 +244,13 @@ int main(int argc, char **argv) use_default_config_path(); } + CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), + NULL, + &keymap_handler, + kTISNotifySelectedKeyboardInputSourceChanged, + NULL, + CFNotificationSuspensionBehaviorCoalesce); + signal(SIGCHLD, SIG_IGN); init_shell(); table_init(&mode_map, 13, (table_hash_func) hash_mode, (table_compare_func) same_mode); -- cgit v1.2.3 From 5287a3192799d8f9bbdca01fa457cd6cb3e7eba5 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 23 Feb 2019 17:18:49 +0100 Subject: #62 implement option to unbind certain applications etc. --- README.md | 7 ++++++- examples/skhdrc | 6 +++++- src/hotkey.c | 13 ++++++++++--- src/hotkey.h | 1 + src/parse.c | 18 +++++++++++++++++- src/tokenize.c | 2 ++ src/tokenize.h | 2 ++ 7 files changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b645b3b..a4ddf41 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,8 @@ keycode = 'apple keyboard kVK_ values (0x3C)' proc_map_lst = * -proc_map = ':' +proc_map = ':' | ':' '~' | + '*' ':' | '*' '~' string = '"' 'sequence of characters' '"' @@ -108,6 +109,10 @@ command = command is executed through '$SHELL -c' and an EOL character signifies the end of the bind. -> = keypress is not consumed by skhd + +* = matches every application not specified in + +~ = application is unbound and keypress is forwarded per usual, when specified in a ``` A mode is declared according to the following rules: diff --git a/examples/skhdrc b/examples/skhdrc index 5336e71..e68433c 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -23,7 +23,8 @@ # # proc_map_lst = * # -# proc_map = ':' +# proc_map = ':' | ':' '~' | +# '*' ':' | '*' '~' # # string = '"' 'sequence of characters' '"' # @@ -79,7 +80,9 @@ # # cmd - n [ # "kitty" : echo "hello kitty" +# * : echo "hello everyone" # "qutebrowser" : echo "hello qutebrowser" +# "terminal" ~ # "finder" : false # ] @@ -257,3 +260,4 @@ ctrl + alt - s : chunkc tiling::desktop --layout monocle ctrl + alt - d : chunkc tiling::desktop --layout float ctrl + alt - w : chunkc tiling::desktop --deserialize ~/.chunkwm_layouts/dev_1 + diff --git a/src/hotkey.c b/src/hotkey.c index 0c8b9f0..2f0df3a 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -145,14 +145,21 @@ should_capture_hotkey(uint32_t capture) internal inline char * find_process_command_mapping(struct hotkey *hotkey, uint32_t *capture, struct carbon_event *carbon) { + char *result = NULL; + bool found = false; + for (int i = 0; i < buf_len(hotkey->process_name); ++i) { if (same_string(carbon->process_name, hotkey->process_name[i])) { - return hotkey->command[i]; + result = hotkey->command[i]; + found = true; + break; } } - *capture &= ~HOTKEY_FOUND; - return NULL; + if (!found) result = hotkey->wildcard_command; + if (!result) *capture &= ~HOTKEY_FOUND; + + return result; } bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m, struct carbon_event *carbon) diff --git a/src/hotkey.h b/src/hotkey.h index 71d2e1d..641875d 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -73,6 +73,7 @@ struct hotkey uint32_t key; char **process_name; char **command; + char *wildcard_command; struct mode **mode_list; }; diff --git a/src/parse.c b/src/parse.c index a5760b7..760485d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -92,8 +92,24 @@ parse_process_command_list(struct parser *parser, struct hotkey *hotkey) if (parser_match(parser, Token_Command)) { parse_command(parser, hotkey); parse_process_command_list(parser, hotkey); + } else if (parser_match(parser, Token_Unbound)) { + buf_push(hotkey->command, NULL); + parse_process_command_list(parser, hotkey); + } else { + parser_report_error(parser, parser_peek(parser), "expected '~' or ':' followed by command\n"); + } + } else if (parser_match(parser, Token_Wildcard)) { + if (parser_match(parser, Token_Command)) { + struct token command = parser_previous(parser); + char *result = copy_string_count(command.text, command.length); + debug("\tcmd: '%s'\n", result); + hotkey->wildcard_command = result; + parse_process_command_list(parser, hotkey); + } else if (parser_match(parser, Token_Unbound)) { + hotkey->wildcard_command = NULL; + parse_process_command_list(parser, hotkey); } else { - parser_report_error(parser, parser_peek(parser), "expected ':' followed by command\n"); + parser_report_error(parser, parser_peek(parser), "expected '~' or ':' followed by command\n"); } } else if (parser_match(parser, Token_EndList)) { if (!buf_len(hotkey->process_name)) { diff --git a/src/tokenize.c b/src/tokenize.c index 8445a71..a0fd93d 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -146,6 +146,8 @@ get_token(struct tokenizer *tokenizer) case ',': { token.type = Token_Comma; } break; case '<': { token.type = Token_Insert; } break; case '@': { token.type = Token_Capture; } break; + case '~': { token.type = Token_Unbound; } break; + case '*': { token.type = Token_Wildcard; } break; case '[': { token.type = Token_BeginList; } break; case ']': { token.type = Token_EndList; } break; case '"': { diff --git a/src/tokenize.h b/src/tokenize.h index 73cdb30..ff82dee 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -49,6 +49,8 @@ enum token_type Token_Dash, Token_Arrow, Token_Capture, + Token_Unbound, + Token_Wildcard, Token_String, Token_BeginList, -- cgit v1.2.3 From 8a952517b0ec62eb87e8053e26eb845a21b3978c Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 23 Feb 2019 17:34:15 +0100 Subject: #62 implement option to unbind certain applications etc. --- README.md | 2 +- examples/skhdrc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a4ddf41..4f916dc 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ keycode = 'apple keyboard kVK_ values (0x3C)' proc_map_lst = * -proc_map = ':' | ':' '~' | +proc_map = ':' | '~' | '*' ':' | '*' '~' string = '"' 'sequence of characters' '"' diff --git a/examples/skhdrc b/examples/skhdrc index e68433c..747ccf7 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -23,7 +23,7 @@ # # proc_map_lst = * # -# proc_map = ':' | ':' '~' | +# proc_map = ':' | '~' | # '*' ':' | '*' '~' # # string = '"' 'sequence of characters' '"' -- cgit v1.2.3 From 600cf250cf6e00efb71117070b7b28074997e8cb Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 24 Feb 2019 13:28:59 +0100 Subject: #54 more accurate profiling information --- README.md | 6 ++--- examples/skhdrc | 1 - makefile | 10 ++------- src/skhd.c | 37 ++++++++---------------------- src/timing.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 src/timing.h diff --git a/README.md b/README.md index 4f916dc..e0b9ded 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,15 @@ Requires xcode-8 command-line tools. make install # release version make # debug version - make fast_profile # release version with profiling information - make profile # debug version with profiling information - ### Usage ``` -V | --verbose: Output debug information skhd -V +-P | --profile: Output profiling information + skhd -P + -v | --version: Print version number to stdout skhd -v diff --git a/examples/skhdrc b/examples/skhdrc index 747ccf7..ffd0613 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -260,4 +260,3 @@ ctrl + alt - s : chunkc tiling::desktop --layout monocle ctrl + alt - d : chunkc tiling::desktop --layout float ctrl + alt - w : chunkc tiling::desktop --deserialize ~/.chunkwm_layouts/dev_1 - diff --git a/makefile b/makefile index 50c5ee8..bbf993f 100644 --- a/makefile +++ b/makefile @@ -1,22 +1,16 @@ -FRAMEWORKS = -framework Carbon +FRAMEWORKS = -framework Carbon -framework CoreAudio BUILD_PATH = ./bin BUILD_FLAGS = -std=c99 -Wall -g -O0 SKHD_SRC = ./src/skhd.c BINS = $(BUILD_PATH)/skhd -.PHONY: all clean install profile fast_profile +.PHONY: all clean install all: clean $(BINS) install: BUILD_FLAGS=-std=c99 -O3 install: clean $(BINS) -profile: BUILD_FLAGS=-std=c99 -Wall -g -O0 -DSKHD_PROFILE -profile: clean $(BINS) - -fast_profile: BUILD_FLAGS=-std=c99 -O3 -DSKHD_PROFILE -fast_profile: clean $(BINS) - clean: rm -rf $(BUILD_PATH) diff --git a/src/skhd.c b/src/skhd.c index 77168c8..d586c9e 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -8,6 +8,7 @@ #include #include +#include "timing.h" #include "log.h" #define HASHTABLE_IMPLEMENTATION #include "hashtable.h" @@ -34,30 +35,6 @@ extern bool CGSIsSecureEventInputSet(); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet -#ifdef SKHD_PROFILE -#define BEGIN_SCOPED_TIMED_BLOCK(note) \ - do { \ - char *timed_note = note; \ - clock_t timed_block_begin = clock() -#define END_SCOPED_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("%.4fms (%s)\n", timed_block_elapsed, timed_note); \ - } while (0) -#define BEGIN_TIMED_BLOCK(note) \ - char *timed_note = note; \ - 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("%.4fms (%s)\n", timed_block_elapsed, timed_note) -#else -#define BEGIN_SCOPED_TIMED_BLOCK(note) -#define END_SCOPED_TIMED_BLOCK() -#define BEGIN_TIMED_BLOCK(note) -#define END_TIMED_BLOCK() -#endif - #define SKHD_CONFIG_FILE ".skhdrc" internal unsigned major_version = 0; @@ -140,9 +117,10 @@ internal bool parse_arguments(int argc, char **argv) { int option; - const char *short_option = "Vvc:k:t:"; + const char *short_option = "VPvc:k:t:"; struct option long_option[] = { { "verbose", no_argument, NULL, 'V' }, + { "profile", no_argument, NULL, 'P' }, { "version", no_argument, NULL, 'v' }, { "config", required_argument, NULL, 'c' }, { "key", required_argument, NULL, 'k' }, @@ -155,6 +133,9 @@ parse_arguments(int argc, char **argv) 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; @@ -214,12 +195,12 @@ use_default_config_path(void) int main(int argc, char **argv) { - BEGIN_TIMED_BLOCK("startup"); - BEGIN_SCOPED_TIMED_BLOCK("initialization"); if (parse_arguments(argc, argv)) { return EXIT_SUCCESS; } + BEGIN_SCOPED_TIMED_BLOCK("total_time"); + BEGIN_SCOPED_TIMED_BLOCK("init"); if (getuid() == 0 || geteuid() == 0) { error("skhd: running as root is not allowed! abort..\n"); } @@ -274,7 +255,7 @@ int main(int argc, char **argv) warn("skhd: could not watch '%s'\n", config_file); } END_SCOPED_TIMED_BLOCK(); - END_TIMED_BLOCK(); + END_SCOPED_TIMED_BLOCK(); CFRunLoopRun(); return EXIT_SUCCESS; diff --git a/src/timing.h b/src/timing.h new file mode 100644 index 0000000..44e91a8 --- /dev/null +++ b/src/timing.h @@ -0,0 +1,70 @@ +#ifndef MACOS_TIMING_H +#define MACOS_TIMING_H + +#include +#include + +#define BEGIN_SCOPED_TIMED_BLOCK(note) \ + do { \ + struct timing_info timing; \ + if (profile) begin_timing(&timing, note) +#define END_SCOPED_TIMED_BLOCK() \ + if (profile) end_timing(&timing); \ + } while (0) + +#define BEGIN_TIMED_BLOCK(note) \ + struct timing_info timing; \ + if (profile) begin_timing(&timing, note) +#define END_TIMED_BLOCK() \ + if (profile) end_timing(&timing) + +static bool profile; + +struct timing_info +{ + char *note; + uint64_t start; + uint64_t end; + float ms; +}; + +void begin_timing(struct timing_info *timing, char *note); +void end_timing(struct timing_info *timing); + +static inline uint64_t +macos_get_wall_clock(void) +{ + uint64_t result = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); + return result; +} + +static inline float +macos_get_seconds_elapsed(uint64_t start, uint64_t end) +{ + float result = ((float)(end - start) / 1000.0f) / 1000000.0f; + return result; +} + +static inline float +macos_get_milliseconds_elapsed(uint64_t start, uint64_t end) +{ + float result = 1000.0f * macos_get_seconds_elapsed(start, end); + return result; +} + +void begin_timing(struct timing_info *timing, char *note) { + timing->note = note; + timing->start = macos_get_wall_clock(); +} + +void end_timing(struct timing_info *timing) { + timing->end = macos_get_wall_clock(); + timing->ms = macos_get_milliseconds_elapsed(timing->start, timing->end); + if (timing->note) { + printf("%6.4fms (%s)\n", timing->ms, timing->note); + } else { + printf("%6.4fms\n", timing->ms); + } +} + +#endif -- cgit v1.2.3 From b55ce42591ac45a82d2339844631f7a50cedef90 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 24 Feb 2019 18:03:59 +0100 Subject: bump v0.3.1 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index d586c9e..7aecdc1 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -39,7 +39,7 @@ extern bool CGSIsSecureEventInputSet(); internal unsigned major_version = 0; internal unsigned minor_version = 3; -internal unsigned patch_version = 0; +internal unsigned patch_version = 1; internal struct carbon_event carbon; internal struct event_tap event_tap; -- cgit v1.2.3 From e23811d884b9dae356c84e4515ef52184aa3432c Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 2 Mar 2019 10:33:53 +0100 Subject: statics.. --- src/hashtable.h | 8 ++++++-- src/hotkey.c | 5 +++-- src/hotkey.h | 10 +++++++--- src/locale.c | 3 ++- src/log.h | 14 ++++++++++---- src/sbuffer.h | 6 +++++- src/skhd.c | 22 ++++++++++++---------- src/timing.h | 14 ++++++++++---- src/tokenize.h | 8 ++++++-- 9 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/hashtable.h b/src/hashtable.h index f593432..8f73066 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -33,7 +33,9 @@ void *table_reset(struct table *table, int *count); #include #include -static struct bucket * +#define internal static + +internal struct bucket * table_new_bucket(void *key, void *value) { struct bucket *bucket = malloc(sizeof(struct bucket)); @@ -43,7 +45,7 @@ table_new_bucket(void *key, void *value) return bucket; } -static struct bucket ** +internal struct bucket ** table_get_bucket(struct table *table, void *key) { struct bucket **bucket = table->buckets + (table->hash(key) % table->capacity); @@ -56,6 +58,8 @@ table_get_bucket(struct table *table, void *key) return bucket; } +#undef internal + void table_init(struct table *table, int capacity, table_hash_func hash, table_compare_func compare) { table->count = 0; diff --git a/src/hotkey.c b/src/hotkey.c index 2f0df3a..9b6b85a 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -1,6 +1,7 @@ #include "hotkey.h" #define internal static +#define global static #define HOTKEY_FOUND ((1) << 0) #define MODE_CAPTURE(a) ((a) << 1) @@ -13,8 +14,8 @@ #define LMOD_OFFS 1 #define RMOD_OFFS 2 -internal char arg[] = "-c"; -internal char *shell = NULL; +global char arg[] = "-c"; +global char *shell = NULL; internal uint32_t cgevent_lrmod_flag[] = { diff --git a/src/hotkey.h b/src/hotkey.h index 641875d..372715d 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -77,25 +77,29 @@ struct hotkey struct mode **mode_list; }; -static inline void +#define internal static + +internal inline void add_flags(struct hotkey *hotkey, uint32_t flag) { hotkey->flags |= flag; } -static inline bool +internal inline bool has_flags(struct hotkey *hotkey, uint32_t flag) { bool result = hotkey->flags & flag; return result; } -static inline void +internal inline void clear_flags(struct hotkey *hotkey, uint32_t flag) { hotkey->flags &= ~flag; } +#undef internal + bool same_mode(char *a, char *b); unsigned long hash_mode(char *key); diff --git a/src/locale.c b/src/locale.c index 3c03f6a..f7aebe3 100644 --- a/src/locale.c +++ b/src/locale.c @@ -4,8 +4,9 @@ #include #define internal static +#define global static -internal struct table keymap_table; +global struct table keymap_table; internal char * copy_cfstring(CFStringRef string) diff --git a/src/log.h b/src/log.h index 2e8167b..88c12a2 100644 --- a/src/log.h +++ b/src/log.h @@ -1,9 +1,12 @@ #ifndef SKHD_LOG_H #define SKHD_LOG_H -static bool verbose; +#define internal static +#define global static -static inline void +global bool verbose; + +internal inline void debug(const char *format, ...) { if (!verbose) return; @@ -14,7 +17,7 @@ debug(const char *format, ...) va_end(args); } -static inline void +internal inline void warn(const char *format, ...) { va_list args; @@ -23,7 +26,7 @@ warn(const char *format, ...) va_end(args); } -static inline void +internal inline void error(const char *format, ...) { va_list args; @@ -33,4 +36,7 @@ error(const char *format, ...) exit(EXIT_FAILURE); } +#undef internal +#undef global + #endif diff --git a/src/sbuffer.h b/src/sbuffer.h index 92ac811..3558bbc 100644 --- a/src/sbuffer.h +++ b/src/sbuffer.h @@ -24,7 +24,9 @@ struct buf_hdr #define buf_last(b) ((b)[buf_len(b)-1]) #define buf_free(b) ((b) ? free(buf__hdr(b)) : 0) -static void *buf__grow_f(const void *buf, size_t new_len, size_t elem_size) +#define internal static + +internal void *buf__grow_f(const void *buf, size_t new_len, size_t elem_size) { size_t new_cap = MAX(1 + 2*buf_cap(buf), new_len); size_t new_size = OFFSETOF(struct buf_hdr, buf) + new_cap*elem_size; @@ -36,4 +38,6 @@ static void *buf__grow_f(const void *buf, size_t new_len, size_t elem_size) return new_hdr->buf; } +#undef internal + #endif diff --git a/src/skhd.c b/src/skhd.c index 7aecdc1..05e955f 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -31,22 +31,24 @@ #include "hotkey.c" #include "synthesize.c" -#define internal static extern bool CGSIsSecureEventInputSet(); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet +#define internal static +#define global static + #define SKHD_CONFIG_FILE ".skhdrc" -internal unsigned major_version = 0; -internal unsigned minor_version = 3; -internal unsigned patch_version = 1; +global unsigned major_version = 0; +global unsigned minor_version = 3; +global unsigned patch_version = 1; -internal struct carbon_event carbon; -internal struct event_tap event_tap; -internal struct hotloader hotloader; -internal struct mode *current_mode; -internal struct table mode_map; -internal char *config_file; +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 char *config_file; internal void parse_config_helper(char *absolutepath) diff --git a/src/timing.h b/src/timing.h index 44e91a8..44cde56 100644 --- a/src/timing.h +++ b/src/timing.h @@ -18,7 +18,10 @@ #define END_TIMED_BLOCK() \ if (profile) end_timing(&timing) -static bool profile; +#define internal static +#define global static + +global bool profile; struct timing_info { @@ -31,21 +34,21 @@ struct timing_info void begin_timing(struct timing_info *timing, char *note); void end_timing(struct timing_info *timing); -static inline uint64_t +internal inline uint64_t macos_get_wall_clock(void) { uint64_t result = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); return result; } -static inline float +internal inline float macos_get_seconds_elapsed(uint64_t start, uint64_t end) { float result = ((float)(end - start) / 1000.0f) / 1000000.0f; return result; } -static inline float +internal inline float macos_get_milliseconds_elapsed(uint64_t start, uint64_t end) { float result = 1000.0f * macos_get_seconds_elapsed(start, end); @@ -67,4 +70,7 @@ void end_timing(struct timing_info *timing) { } } +#undef internal +#undef global + #endif diff --git a/src/tokenize.h b/src/tokenize.h index ff82dee..4592ac9 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -1,7 +1,9 @@ #ifndef SKHD_TOKENIZE_H #define SKHD_TOKENIZE_H -static const char *modifier_flags_str[] = +#define global static + +global const char *modifier_flags_str[] = { "alt", "lalt", "ralt", "shift", "lshift", "rshift", @@ -10,7 +12,7 @@ static const char *modifier_flags_str[] = "fn", "hyper", "meh", }; -static const char *literal_keycode_str[] = +global const char *literal_keycode_str[] = { "return", "tab", "space", "backspace", "escape", "delete", @@ -31,6 +33,8 @@ static const char *literal_keycode_str[] = "brightness_down", "illumination_up", "illumination_down" }; +#undef global + enum token_type { Token_Identifier, -- cgit v1.2.3 From 56b94a6eed9fe4642382f533666dc19901fad3fc Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 3 Mar 2019 18:39:34 +0100 Subject: #53 allow opt-out for applications --- README.md | 14 +++++++++++++- examples/skhdrc | 8 ++++++++ src/hotkey.c | 14 ++++++++++++-- src/hotkey.h | 5 +++-- src/parse.c | 41 ++++++++++++++++++++++++++++++++++++++++- src/parse.h | 3 ++- src/skhd.c | 10 ++++++++-- src/tokenize.c | 17 +++++++++++++++++ src/tokenize.h | 1 + 9 files changed, 104 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e0b9ded..3ab4481 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ feature comparison between **skhd** and **khd** | hotload config file | [x] | [ ] | | hotkey passthrough | [x] | [x] | | modal hotkey-system | [x] | [x] | -| use media-keys as hotkey | [x] | [ ] | | application specific hotkey| [x] | [x] | +| blacklist applications | [x] | [ ] | +| use media-keys as hotkey | [x] | [ ] | | modifier only hotkey | [ ] | [x] | | caps-lock as hotkey | [ ] | [x] | | mouse-buttons as hotkey | [ ] | [x] | @@ -136,3 +137,14 @@ command = command is executed through '$SHELL -c' and an EOL character signifies the end of the bind. ``` + +General options that configure the behaviour of **skhd**: +``` +# prevents skhd from monitoring events for listed processes +.blacklist [ + "terminal" + "qutebrowser" + "kitty" + "google chrome" +] +``` diff --git a/examples/skhdrc b/examples/skhdrc index ffd0613..bf0f7a3 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -86,6 +86,14 @@ # "finder" : false # ] +# prevent skhd from monitoring events for specific applications +# +# .blacklist [ +# "kitty" +# "terminal" +# "qutebrowser" +# ] + # open terminal, blazingly fast compared to iTerm/Hyper cmd - return : /Applications/Kitty.app/Contents/MacOS/kitty --single-instance -d ~ diff --git a/src/hotkey.c b/src/hotkey.c index 9b6b85a..780aa3a 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -74,7 +74,7 @@ unsigned long hash_hotkey(struct hotkey *a) return a->key; } -bool same_mode(char *a, char *b) +bool compare_string(char *a, char *b) { while (*a && *b && *a == *b) { ++a; @@ -83,7 +83,7 @@ bool same_mode(char *a, char *b) return *a == '\0' && *b == '\0'; } -unsigned long hash_mode(char *key) +unsigned long hash_string(char *key) { unsigned long hash = 0, high; while(*key) { @@ -228,6 +228,16 @@ next:; } } +void free_blacklist(struct table *blacklst) +{ + int count; + void **items = table_reset(blacklst, &count); + for (int index = 0; index < count; ++index) { + char *item = (char *) items[index]; + free(item); + } +} + internal void cgevent_lrmod_flag_to_hotkey_lrmod_flag(CGEventFlags eventflags, uint32_t *flags, int mod) { diff --git a/src/hotkey.h b/src/hotkey.h index 372715d..4611b09 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -100,8 +100,8 @@ clear_flags(struct hotkey *hotkey, uint32_t flag) #undef internal -bool same_mode(char *a, char *b); -unsigned long hash_mode(char *key); +bool compare_string(char *a, char *b); +unsigned long hash_string(char *key); bool same_hotkey(struct hotkey *a, struct hotkey *b); unsigned long hash_hotkey(struct hotkey *a); @@ -111,6 +111,7 @@ bool intercept_systemkey(CGEventRef event, struct hotkey *eventkey); bool find_and_exec_hotkey(struct hotkey *k, struct table *t, struct mode **m, struct carbon_event *carbon); void free_mode_map(struct table *mode_map); +void free_blacklist(struct table *blacklst); void init_shell(void); diff --git a/src/parse.c b/src/parse.c index 760485d..4e673f4 100644 --- a/src/parse.c +++ b/src/parse.c @@ -384,6 +384,41 @@ void parse_declaration(struct parser *parser) } } +void parse_option_blacklist(struct parser *parser) +{ + if (parser_match(parser, Token_String)) { + struct token name_token = parser_previous(parser); + char *name = copy_string_count(name_token.text, name_token.length); + for (char *s = name; *s; ++s) *s = tolower(*s); + debug("\t%s\n", name); + table_add(parser->blacklst, name, name); + parse_option_blacklist(parser); + } else if (parser_match(parser, Token_EndList)) { + if (parser->blacklst->count == 0) { + parser_report_error(parser, parser_previous(parser), "list must contain at least one value\n"); + } + } else { + parser_report_error(parser, parser_peek(parser), "expected process name or ']'\n"); + } +} + +void parse_option(struct parser *parser) +{ + parser_match(parser, Token_Option); + struct token option = parser_previous(parser); + if (token_equals(option, "blacklist")) { + if (parser_match(parser, Token_BeginList)) { + debug("blacklist :: #%d {\n", option.line); + parse_option_blacklist(parser); + debug("}\n"); + } else { + parser_report_error(parser, option, "expected '[' followed by list of process names\n"); + } + } else { + parser_report_error(parser, option, "invalid option specified\n"); + } +} + void parse_config(struct parser *parser) { struct mode *mode; @@ -392,6 +427,7 @@ void parse_config(struct parser *parser) while (!parser_eof(parser)) { if (parser->error) { free_mode_map(parser->mode_map); + free_blacklist(parser->blacklst); return; } @@ -408,6 +444,8 @@ void parse_config(struct parser *parser) } } else if (parser_check(parser, Token_Decl)) { parse_declaration(parser); + } else if (parser_check(parser, Token_Option)) { + parse_option(parser); } else { parser_report_error(parser, parser_peek(parser), "expected decl, modifier or key-literal\n"); } @@ -512,12 +550,13 @@ void parser_report_error(struct parser *parser, struct token token, const char * parser->error = true; } -bool parser_init(struct parser *parser, struct table *mode_map, char *file) +bool parser_init(struct parser *parser, struct table *mode_map, struct table *blacklst, char *file) { memset(parser, 0, sizeof(struct parser)); char *buffer = read_file(file); if (buffer) { parser->mode_map = mode_map; + parser->blacklst = blacklst; tokenizer_init(&parser->tokenizer, buffer); parser_advance(parser); return true; diff --git a/src/parse.h b/src/parse.h index 35bc62e..95813c2 100644 --- a/src/parse.h +++ b/src/parse.h @@ -11,6 +11,7 @@ struct parser struct token current_token; struct tokenizer tokenizer; struct table *mode_map; + struct table *blacklst; bool error; }; @@ -23,7 +24,7 @@ bool parser_eof(struct parser *parser); 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(struct parser *parser, struct table *mode_map, struct table *blacklst, char *file); bool parser_init_text(struct parser *parser, char *text); void parser_destroy(struct parser *parser); void parser_report_error(struct parser *parser, struct token token, const char *format, ...); diff --git a/src/skhd.c b/src/skhd.c index 05e955f..ceea857 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -48,13 +48,14 @@ 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 char *config_file; internal void parse_config_helper(char *absolutepath) { struct parser parser; - if (parser_init(&parser, &mode_map, absolutepath)) { + if (parser_init(&parser, &mode_map, &blacklst, absolutepath)) { parse_config(&parser); parser_destroy(&parser); } else { @@ -68,6 +69,7 @@ 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); + free_blacklist(&blacklst); parse_config_helper(absolutepath); END_TIMED_BLOCK(); } @@ -78,6 +80,7 @@ internal CF_NOTIFICATION_CALLBACK(keymap_handler) 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(); @@ -93,6 +96,7 @@ internal EVENT_TAP_CALLBACK(key_handler) 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"); @@ -103,6 +107,7 @@ internal EVENT_TAP_CALLBACK(key_handler) if (result) return NULL; } break; case NX_SYSDEFINED: { + if (table_find(&blacklst, carbon.process_name)) return event; if (!current_mode) return event; struct hotkey eventkey; @@ -236,7 +241,8 @@ int main(int argc, char **argv) signal(SIGCHLD, SIG_IGN); init_shell(); - table_init(&mode_map, 13, (table_hash_func) hash_mode, (table_compare_func) same_mode); + 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(); BEGIN_SCOPED_TIMED_BLOCK("parse_config"); diff --git a/src/tokenize.c b/src/tokenize.c index a0fd93d..eaf0e3a 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -79,6 +79,14 @@ eat_string(struct tokenizer *tokenizer) } } +internal void +eat_option(struct tokenizer *tokenizer) +{ + while (*tokenizer->at && !isspace(*tokenizer->at)) { + advance(tokenizer); + } +} + internal inline bool isidentifier(char c) { @@ -150,6 +158,15 @@ get_token(struct tokenizer *tokenizer) case '*': { token.type = Token_Wildcard; } break; case '[': { token.type = Token_BeginList; } break; case ']': { token.type = Token_EndList; } break; + case '.': { + token.text = tokenizer->at; + token.line = tokenizer->line; + token.cursor = tokenizer->cursor; + + eat_option(tokenizer); + token.length = tokenizer->at - token.text; + token.type = Token_Option; + } break; case '"': { token.text = tokenizer->at; token.line = tokenizer->line; diff --git a/src/tokenize.h b/src/tokenize.h index 4592ac9..4df5df3 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -56,6 +56,7 @@ enum token_type Token_Unbound, Token_Wildcard, Token_String, + Token_Option, Token_BeginList, Token_EndList, -- cgit v1.2.3 From ac494e3f72e397aaf3245b0401bd1b963bd4ebd3 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sun, 3 Mar 2019 22:24:14 +0100 Subject: #58 load/include additional config files --- README.md | 10 ++++++- examples/skhdrc | 9 ++++++- src/parse.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/parse.h | 10 ++++++- src/skhd.c | 30 +++++++++++++-------- 5 files changed, 121 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 3ab4481..24b9226 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,15 @@ command = command is executed through '$SHELL -c' and General options that configure the behaviour of **skhd**: ``` -# prevents skhd from monitoring events for listed processes +# specify a file that should be included as an additional config-file. +# treated as an absolutepath if the filename begins with '/' otherwise +# the file is relative to the path of the config-file it was loaded from. + +.load "/Users/Koe/.config/partial_skhdrc" +.load "partial_skhdrc" + +# prevents skhd from monitoring events for listed processes. + .blacklist [ "terminal" "qutebrowser" diff --git a/examples/skhdrc b/examples/skhdrc index bf0f7a3..48b5424 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -86,7 +86,14 @@ # "finder" : false # ] -# prevent skhd from monitoring events for specific applications +# specify a file that should be included as an additional config-file. +# treated as an absolutepath if the filename begins with '/' otherwise +# the file is relative to the path of the config-file it was loaded from. +# +# .load "/Users/Koe/.config/partial_skhdrc" +# .load "partial_skhdrc" + +# prevent skhd from monitoring events for specific applications. # # .blacklist [ # "kitty" diff --git a/src/parse.c b/src/parse.c index 4e673f4..9f14389 100644 --- a/src/parse.c +++ b/src/parse.c @@ -402,6 +402,32 @@ void parse_option_blacklist(struct parser *parser) } } +void parse_option_load(struct parser *parser, struct token option) +{ + struct token filename_token = parser_previous(parser); + char *filename = copy_string_count(filename_token.text, filename_token.length); + debug("\t%s\n", filename); + + if (*filename != '/') { + char *directory = file_directory(parser->file); + + size_t directory_length = strlen(directory); + size_t filename_length = strlen(filename); + size_t total_length = directory_length + filename_length + 2; + + char *absolutepath = malloc(total_length * sizeof(char)); + snprintf(absolutepath, total_length, "%s/%s", directory, filename); + free(filename); + + filename = absolutepath; + } + + buf_push(parser->load_directives, ((struct load_directive) { + .file = filename, + .option = option + })); +} + void parse_option(struct parser *parser) { parser_match(parser, Token_Option); @@ -414,22 +440,26 @@ void parse_option(struct parser *parser) } else { parser_report_error(parser, option, "expected '[' followed by list of process names\n"); } + } else if (token_equals(option, "load")) { + if (parser_match(parser, Token_String)) { + debug("load :: #%d {\n", option.line); + parse_option_load(parser, option); + debug("}\n"); + } else { + parser_report_error(parser, option, "expected filename\n"); + } } else { parser_report_error(parser, option, "invalid option specified\n"); } } -void parse_config(struct parser *parser) +bool parse_config(struct parser *parser) { struct mode *mode; struct hotkey *hotkey; while (!parser_eof(parser)) { - if (parser->error) { - free_mode_map(parser->mode_map); - free_blacklist(parser->blacklst); - return; - } + if (parser->error) break; if ((parser_check(parser, Token_Identifier)) || (parser_check(parser, Token_Modifier)) || @@ -450,6 +480,14 @@ void parse_config(struct parser *parser) parser_report_error(parser, parser_peek(parser), "expected decl, modifier or key-literal\n"); } } + + if (parser->error) { + free_mode_map(parser->mode_map); + free_blacklist(parser->blacklst); + return false; + } + + return true; } struct hotkey * @@ -550,11 +588,43 @@ void parser_report_error(struct parser *parser, struct token token, const char * parser->error = true; } +void parser_do_directives(struct parser *parser, struct hotloader *hotloader) +{ + bool error = false; + + for (int i = 0; i < buf_len(parser->load_directives); ++i) { + struct load_directive load = parser->load_directives[i]; + + struct parser directive_parser; + if (parser_init(&directive_parser, parser->mode_map, parser->blacklst, load.file)) { + hotloader_add_file(hotloader, load.file); + + if (parse_config(&directive_parser)) { + parser_do_directives(&directive_parser, hotloader); + } else { + error = true; + } + parser_destroy(&directive_parser); + } else { + warn("skhd: could not open file '%s' from load directive #%d:%d\n", load.file, load.option.line, load.option.cursor); + } + + free(load.file); + } + buf_free(parser->load_directives); + + if (error) { + free_mode_map(parser->mode_map); + free_blacklist(parser->blacklst); + } +} + bool parser_init(struct parser *parser, struct table *mode_map, struct table *blacklst, char *file) { memset(parser, 0, sizeof(struct parser)); char *buffer = read_file(file); if (buffer) { + parser->file = file; parser->mode_map = mode_map; parser->blacklst = blacklst; tokenizer_init(&parser->tokenizer, buffer); diff --git a/src/parse.h b/src/parse.h index 95813c2..6576f7a 100644 --- a/src/parse.h +++ b/src/parse.h @@ -4,18 +4,26 @@ #include "tokenize.h" #include +struct load_directive +{ + char *file; + struct token option; +}; + struct table; struct parser { + char *file; struct token previous_token; struct token current_token; struct tokenizer tokenizer; struct table *mode_map; struct table *blacklst; + struct load_directive *load_directives; bool error; }; -void parse_config(struct parser *parser); +bool parse_config(struct parser *parser); struct hotkey *parse_keypress(struct parser *parser); struct token parser_peek(struct parser *parser); diff --git a/src/skhd.c b/src/skhd.c index ceea857..c90c4b6 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -51,16 +51,33 @@ global struct table mode_map; global struct table blacklst; 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, &blacklst, absolutepath)) { - parse_config(&parser); + hotloader_end(&hotloader); + hotloader_add_file(&hotloader, absolutepath); + + if (parse_config(&parser)) { + parser_do_directives(&parser, &hotloader); + } parser_destroy(&parser); + + 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"); } @@ -70,7 +87,7 @@ internal HOTLOADER_CALLBACK(config_handler) debug("skhd: config-file has been modified.. reloading config\n"); free_mode_map(&mode_map); free_blacklist(&blacklst); - parse_config_helper(absolutepath); + parse_config_helper(config_file); END_TIMED_BLOCK(); } @@ -254,15 +271,6 @@ int main(int argc, char **argv) event_tap.mask = (1 << kCGEventKeyDown) | (1 << NX_SYSDEFINED); event_tap_begin(&event_tap, key_handler); END_SCOPED_TIMED_BLOCK(); - - BEGIN_SCOPED_TIMED_BLOCK("begin_hotloader"); - if (hotloader_add_file(&hotloader, config_file) && - hotloader_begin(&hotloader, config_handler)) { - debug("skhd: watching '%s' for changes\n", config_file); - } else { - warn("skhd: could not watch '%s'\n", config_file); - } - END_SCOPED_TIMED_BLOCK(); END_SCOPED_TIMED_BLOCK(); CFRunLoopRun(); -- cgit v1.2.3 From 3922cf456a6c711032a1a6650442be67cfbb80fb Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 7 Mar 2019 22:43:23 +0100 Subject: #54 ability to disable hotloader and trigger manual reload of config file --- README.md | 6 ++++ src/parse.c | 9 ++++-- src/skhd.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 24b9226..c4b1a8d 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,12 @@ Requires xcode-8 command-line tools. -c | --config: Specify location of config file skhd -c ~/.skhdrc +-r | --reload: Signal a running instance of skhd to reload its config file + skhd -r + +-h | --no-hotload: Disable system for hotloading config file + skhd -h + -k | --key: Synthesize a keypress (same syntax as when defining a hotkey) skhd -k "shift + alt - 7" diff --git a/src/parse.c b/src/parse.c index 9f14389..e2b4c4d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -588,7 +588,7 @@ void parser_report_error(struct parser *parser, struct token token, const char * parser->error = true; } -void parser_do_directives(struct parser *parser, struct hotloader *hotloader) +void parser_do_directives(struct parser *parser, struct hotloader *hotloader, bool thwart_hotloader) { bool error = false; @@ -597,13 +597,16 @@ void parser_do_directives(struct parser *parser, struct hotloader *hotloader) struct parser directive_parser; if (parser_init(&directive_parser, parser->mode_map, parser->blacklst, load.file)) { - hotloader_add_file(hotloader, load.file); + if (!thwart_hotloader) { + hotloader_add_file(hotloader, load.file); + } if (parse_config(&directive_parser)) { - parser_do_directives(&directive_parser, hotloader); + parser_do_directives(&directive_parser, hotloader, thwart_hotloader); } else { error = true; } + parser_destroy(&directive_parser); } else { warn("skhd: could not open file '%s' from load directive #%d:%d\n", load.file, load.option.line, load.option.cursor); diff --git a/src/skhd.c b/src/skhd.c index c90c4b6..1ae7976 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -5,6 +5,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -38,6 +42,7 @@ extern bool CGSIsSecureEventInputSet(); #define global static #define SKHD_CONFIG_FILE ".skhdrc" +#define SKHD_PID_FILE "/tmp/skhd.pid" global unsigned major_version = 0; global unsigned minor_version = 3; @@ -49,6 +54,7 @@ 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); @@ -58,21 +64,25 @@ parse_config_helper(char *absolutepath) { struct parser parser; if (parser_init(&parser, &mode_map, &blacklst, absolutepath)) { - hotloader_end(&hotloader); - hotloader_add_file(&hotloader, absolutepath); + if (!thwart_hotloader) { + hotloader_end(&hotloader); + hotloader_add_file(&hotloader, absolutepath); + } if (parse_config(&parser)) { - parser_do_directives(&parser, &hotloader); + parser_do_directives(&parser, &hotloader, thwart_hotloader); } parser_destroy(&parser); - 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); + 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 start watcher.. hotloading is not enabled\n"); } } else { warn("skhd: could not open file '%s'\n", absolutepath); @@ -137,18 +147,73 @@ internal EVENT_TAP_CALLBACK(key_handler) return event; } +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) +{ + pid_t pid = 0; + + int handle = open(SKHD_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) +{ + pid_t pid = getpid(); + + int handle = open(SKHD_PID_FILE, O_CREAT | O_WRONLY, 0644); + if (handle == -1) { + error("skhd: could not create pid-file! abort..\n"); + } + + if (flock(handle, LOCK_EX | LOCK_NB) == -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 = "VPvc:k:t:"; + const char *short_option = "VPvc:k:t:rh"; 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' }, { NULL, 0, NULL, 0 } }; @@ -167,6 +232,9 @@ parse_arguments(int argc, char **argv) case 'c': { config_file = copy_string(optarg); } break; + case 'h': { + thwart_hotloader = true; + } break; case 'k': { synthesize_key(optarg); return true; @@ -175,6 +243,11 @@ parse_arguments(int argc, char **argv) synthesize_text(optarg); return true; } break; + case 'r': { + pid_t pid = read_pid_file(); + if (pid) kill(pid, SIGUSR1); + return true; + } break; } } @@ -219,15 +292,17 @@ use_default_config_path(void) 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"); - if (getuid() == 0 || geteuid() == 0) { - error("skhd: running as root is not allowed! abort..\n"); - } + create_pid_file(); if (secure_keyboard_entry_enabled()) { error("skhd: secure keyboard entry is enabled! abort..\n"); @@ -257,6 +332,8 @@ int main(int argc, char **argv) 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); -- cgit v1.2.3 From ef2e8ce70b164edecda2412855b60d100d5361a5 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 7 Mar 2019 22:48:36 +0100 Subject: #54 add note regarding pid-file --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c4b1a8d..5f0d576 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ (although rewritten from scratch), that sacrifices the more advanced features in favour of increased responsiveness and performance. **skhd** is able to hotload its config file, meaning that hotkeys can be edited and updated live while **skhd** is running. +**skhd** uses a pid-file to make sure that only one instance is running at any moment in time. This also allows for the ability to trigger +a manual reload of the config file by invoking `skhd --reload` at any time while an instance of **skhd** is running. The pid-file is saved +as `/tmp/skhd.pid` and so the user that is running **skhd** must have write permission to said path. + feature comparison between **skhd** and **khd** | feature | skhd | khd | -- cgit v1.2.3 From c2070383295b0014356a56481acd3140f341f5a6 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Thu, 7 Mar 2019 22:51:37 +0100 Subject: v0.3.2 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index 1ae7976..b89f9f1 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -46,7 +46,7 @@ extern bool CGSIsSecureEventInputSet(); global unsigned major_version = 0; global unsigned minor_version = 3; -global unsigned patch_version = 1; +global unsigned patch_version = 2; global struct carbon_event carbon; global struct event_tap event_tap; -- cgit v1.2.3 From 757c3af5cf6cabb7c72bfd23ca2f1ab2324bee8d Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 15 Mar 2019 19:20:36 +0100 Subject: indent --- src/tokenize.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tokenize.h b/src/tokenize.h index 4df5df3..52d76fc 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -14,18 +14,18 @@ global const char *modifier_flags_str[] = global const char *literal_keycode_str[] = { - "return", "tab", "space", - "backspace", "escape", "delete", - "home", "end", "pageup", - "pagedown", "insert", "left", - "right", "up", "down", - "f1", "f2", "f3", - "f4", "f5", "f6", - "f7", "f8", "f9", - "f10", "f11", "f12", - "f13", "f14", "f15", - "f16", "f17", "f18", - "f19", "f20", + "return", "tab", "space", + "backspace", "escape", "delete", + "home", "end", "pageup", + "pagedown", "insert", "left", + "right", "up", "down", + "f1", "f2", "f3", + "f4", "f5", "f6", + "f7", "f8", "f9", + "f10", "f11", "f12", + "f13", "f14", "f15", + "f16", "f17", "f18", + "f19", "f20", "sound_up", "sound_down", "mute", "play", "previous", "next", -- cgit v1.2.3 From 3aac6931638a8d5da8563e179d71218354e908a6 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Fri, 15 Mar 2019 19:21:10 +0100 Subject: #70 fix memory leak, and code cleanup --- src/locale.c | 113 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 49 deletions(-) diff --git a/src/locale.c b/src/locale.c index f7aebe3..64f8b04 100644 --- a/src/locale.c +++ b/src/locale.c @@ -1,24 +1,25 @@ #include "locale.h" #include "hashtable.h" +#include "sbuffer.h" #include #include +#define array_count(a) (sizeof((a)) / sizeof(*(a))) + #define internal static #define global static global struct table keymap_table; +global char **keymap_keys; internal char * copy_cfstring(CFStringRef string) { - CFStringEncoding encoding = kCFStringEncodingUTF8; - CFIndex length = CFStringGetLength(string); - CFIndex bytes = CFStringGetMaximumSizeForEncoding(length, encoding); - char *result = malloc(bytes + 1); + CFIndex num_bytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8); + char *result = malloc(num_bytes + 1); // NOTE(koekeishiya): Boolean: typedef -> unsigned char; false = 0, true != 0 - Boolean success = CFStringGetCString(string, result, bytes + 1, encoding); - if (!success) { + if (!CFStringGetCString(string, result, num_bytes + 1, kCFStringEncodingUTF8)) { free(result); result = NULL; } @@ -51,50 +52,45 @@ same_keymap(const char *a, const char *b) return *a == '\0' && *b == '\0'; } -internal CFStringRef -cfstring_from_keycode(UCKeyboardLayout *keyboard_layout, CGKeyCode keycode) +internal void +free_keycode_map(void) { - UInt32 dead_key_state = 0; - UniCharCount max_string_length = 255; - UniCharCount string_length = 0; - UniChar unicode_string[max_string_length]; - - OSStatus status = UCKeyTranslate(keyboard_layout, keycode, - kUCKeyActionDown, 0, - LMGetKbdType(), 0, - &dead_key_state, - max_string_length, - &string_length, - unicode_string); - - if (string_length == 0 && dead_key_state) { - status = UCKeyTranslate(keyboard_layout, kVK_Space, - kUCKeyActionDown, 0, - LMGetKbdType(), 0, - &dead_key_state, - max_string_length, - &string_length, - unicode_string); - } - - if (string_length > 0 && status == noErr) { - return CFStringCreateWithCharacters(NULL, unicode_string, string_length); + for (int i = 0; i < buf_len(keymap_keys); ++i) { + free(keymap_keys[i]); } - return NULL; + buf_free(keymap_keys); + keymap_keys = NULL; } -uint32_t keycode_from_char(char key) +internal uint32_t layout_dependent_keycodes[] = { - char lookup_key[] = { key, '\0' }; - uint32_t keycode = (uint32_t) table_find(&keymap_table, &lookup_key); - return keycode; -} + kVK_ANSI_A, kVK_ANSI_B, kVK_ANSI_C, + kVK_ANSI_D, kVK_ANSI_E, kVK_ANSI_F, + kVK_ANSI_G, kVK_ANSI_H, kVK_ANSI_I, + kVK_ANSI_J, kVK_ANSI_K, kVK_ANSI_L, + kVK_ANSI_M, kVK_ANSI_N, kVK_ANSI_O, + kVK_ANSI_P, kVK_ANSI_Q, kVK_ANSI_R, + kVK_ANSI_S, kVK_ANSI_T, kVK_ANSI_U, + kVK_ANSI_V, kVK_ANSI_W, kVK_ANSI_X, + kVK_ANSI_Y, kVK_ANSI_Z, kVK_ANSI_0, + kVK_ANSI_1, kVK_ANSI_2, kVK_ANSI_3, + kVK_ANSI_4, kVK_ANSI_5, kVK_ANSI_6, + kVK_ANSI_7, kVK_ANSI_8, kVK_ANSI_9, + kVK_ANSI_Grave, kVK_ANSI_Equal, kVK_ANSI_Minus, + kVK_ANSI_RightBracket, kVK_ANSI_LeftBracket, kVK_ANSI_Quote, + kVK_ANSI_Semicolon, kVK_ANSI_Backslash, kVK_ANSI_Comma, + kVK_ANSI_Slash, kVK_ANSI_Period, kVK_ISO_Section +}; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" bool initialize_keycode_map(void) { + UniChar chars[255]; + UniCharCount len; + UInt32 state; + TISInputSourceRef keyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); CFDataRef uchr = (CFDataRef) TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); CFRelease(keyboard); @@ -102,23 +98,42 @@ bool initialize_keycode_map(void) UCKeyboardLayout *keyboard_layout = (UCKeyboardLayout *) CFDataGetBytePtr(uchr); if (!keyboard_layout) return false; + free_keycode_map(); table_free(&keymap_table); table_init(&keymap_table, - 131, + 61, (table_hash_func) hash_keymap, (table_compare_func) same_keymap); - for (unsigned index = 0; index < 128; ++index) { - CFStringRef key_string = cfstring_from_keycode(keyboard_layout, index); - if (!key_string) continue; - - char *c_key_string = copy_cfstring(key_string); - CFRelease(key_string); - if (!c_key_string) continue; - - table_add(&keymap_table, c_key_string, (void *)index); + for (int i = 0; i < array_count(layout_dependent_keycodes); ++i) { + if (UCKeyTranslate(keyboard_layout, + layout_dependent_keycodes[i], + kUCKeyActionDown, + 0, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, + &state, + array_count(chars), + &len, + chars) == noErr && len > 0) { + CFStringRef key_cfstring = CFStringCreateWithCharacters(NULL, chars, len); + char *key_cstring = copy_cfstring(key_cfstring); + CFRelease(key_cfstring); + + if (key_cstring) { + table_add(&keymap_table, key_cstring, (void *)layout_dependent_keycodes[i]); + buf_push(keymap_keys, key_cstring); + } + } } return true; } #pragma clang diagnostic pop + +uint32_t keycode_from_char(char key) +{ + char lookup_key[] = { key, '\0' }; + uint32_t keycode = (uint32_t) table_find(&keymap_table, &lookup_key); + return keycode; +} -- cgit v1.2.3 From feb4cc904df602fad2689492db3d227bb57f2708 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Mon, 8 Apr 2019 17:38:30 +0200 Subject: #51 output process that has 'Secure Keyboard Entry' enabled if detected during startup --- src/carbon.c | 8 ++++++++ src/carbon.h | 1 + src/skhd.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/carbon.c b/src/carbon.c index c6fddef..dcfc2d8 100644 --- a/src/carbon.c +++ b/src/carbon.c @@ -17,6 +17,14 @@ find_process_name_for_psn(ProcessSerialNumber *psn) return NULL; } +inline char * +find_process_name_for_pid(pid_t pid) +{ + ProcessSerialNumber psn; + GetProcessForPID(pid, &psn); + return find_process_name_for_psn(&psn); +} + internal inline char * find_active_process_name(void) { diff --git a/src/carbon.h b/src/carbon.h index d98792a..8ef9ed4 100644 --- a/src/carbon.h +++ b/src/carbon.h @@ -12,6 +12,7 @@ struct carbon_event char * volatile process_name; }; +char *find_process_name_for_pid(pid_t pid); bool carbon_event_init(struct carbon_event *carbon); #endif diff --git a/src/skhd.c b/src/skhd.c index b89f9f1..fb04e0e 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -35,7 +35,8 @@ #include "hotkey.c" #include "synthesize.c" -extern bool CGSIsSecureEventInputSet(); +extern CFDictionaryRef CGSCopyCurrentSessionDictionary(void); +extern bool CGSIsSecureEventInputSet(void); #define secure_keyboard_entry_enabled CGSIsSecureEventInputSet #define internal static @@ -290,6 +291,31 @@ use_default_config_path(void) config_file[length] = '\0'; } +internal void +dump_secure_keyboard_entry_process_info(void) +{ + 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) { @@ -305,7 +331,7 @@ int main(int argc, char **argv) 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()) { -- cgit v1.2.3 From 52cde83ab83b115f0c136a48b5825d57753c94b8 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 13 Apr 2019 15:43:37 +0200 Subject: update sample config --- examples/skhdrc | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/examples/skhdrc b/examples/skhdrc index 48b5424..3486c1f 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -41,6 +41,10 @@ # # -> = keypress is not consumed by skhd # +# * = matches every application not specified in +# +# ~ = application is unbound and keypress is forwarded per usual, when specified in a +# # NOTE(koekeishiya): A mode is declared according to the following rules: # # mode_decl = '::' '@' ':' | '::' ':' | @@ -169,19 +173,10 @@ cmd + alt - 3 : chunkc tiling::desktop --focus 3 cmd + alt - 4 : chunkc tiling::desktop --focus 4 cmd + alt - 5 : chunkc tiling::desktop --focus 5 cmd + alt - 6 : chunkc tiling::desktop --focus 6 -# cmd + alt - 7 : chunkc tiling::desktop --focus 7 - -# send window to desktop -shift + alt - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop) -shift + alt - z : chunkc tiling::window --send-to-desktop prev -shift + alt - c : chunkc tiling::window --send-to-desktop next -shift + alt - 1 : chunkc tiling::window --send-to-desktop 1 -shift + alt - 2 : chunkc tiling::window --send-to-desktop 2 -shift + alt - 3 : chunkc tiling::window --send-to-desktop 3 -shift + alt - 4 : chunkc tiling::window --send-to-desktop 4 -shift + alt - 5 : chunkc tiling::window --send-to-desktop 5 -shift + alt - 6 : chunkc tiling::window --send-to-desktop 6 -# shift + alt - 7 : chunkc tiling::window --send-to-desktop 7 +cmd + alt - 7 : chunkc tiling::desktop --focus 7 +cmd + alt - 8 : chunkc tiling::desktop --focus 8 +cmd + alt - 9 : chunkc tiling::desktop --focus 9 +cmd + alt - 0 : chunkc tiling::desktop --focus 10 # send window to desktop and follow focus shift + cmd - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop); chunkc tiling::desktop --focus $(chunkc get _last_active_desktop) @@ -193,7 +188,10 @@ shift + cmd - 3 : chunkc tiling::window --send-to-desktop 3; chunkc tiling::desk shift + cmd - 4 : chunkc tiling::window --send-to-desktop 4; chunkc tiling::desktop --focus 4 shift + cmd - 5 : chunkc tiling::window --send-to-desktop 5; chunkc tiling::desktop --focus 5 shift + cmd - 6 : chunkc tiling::window --send-to-desktop 6; chunkc tiling::desktop --focus 6 -# shift + cmd - 7 : chunkc tiling::window --send-to-desktop 7; chunkc tiling::desktop --focus 7 +shift + cmd - 7 : chunkc tiling::window --send-to-desktop 7; chunkc tiling::desktop --focus 7 +shift + cmd - 8 : chunkc tiling::window --send-to-desktop 8; chunkc tiling::desktop --focus 8 +shift + cmd - 9 : chunkc tiling::window --send-to-desktop 9; chunkc tiling::desktop --focus 9 +shift + cmd - 0 : chunkc tiling::window --send-to-desktop 10; chunkc tiling::desktop --focus 10 # focus monitor ctrl + alt - z : chunkc tiling::monitor -f prev -- cgit v1.2.3 From 5b06e5102032ce799ca0ad5d1620ca56ec0883a0 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 13 Apr 2019 19:09:39 +0200 Subject: #75 flag to launch in observer mode --- README.md | 3 +++ src/skhd.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f0d576..3100b3e 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,9 @@ Requires xcode-8 command-line tools. -c | --config: Specify location of config file skhd -c ~/.skhdrc +-o | --observe: Output keycode and modifiers of event. Ctrl+C to quit + skhd -o + -r | --reload: Signal a running instance of skhd to reload its config file skhd -r diff --git a/src/skhd.c b/src/skhd.c index fb04e0e..cd75802 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -114,6 +114,36 @@ internal CF_NOTIFICATION_CALLBACK(keymap_handler) 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) { switch (type) { @@ -205,7 +235,7 @@ internal bool parse_arguments(int argc, char **argv) { int option; - const char *short_option = "VPvc:k:t:rh"; + const char *short_option = "VPvc:k:t:rho"; struct option long_option[] = { { "verbose", no_argument, NULL, 'V' }, { "profile", no_argument, NULL, 'P' }, @@ -215,6 +245,7 @@ parse_arguments(int argc, char **argv) { "key", required_argument, NULL, 'k' }, { "text", required_argument, NULL, 't' }, { "reload", no_argument, NULL, 'r' }, + { "observe", no_argument, NULL, 'o' }, { NULL, 0, NULL, 0 } }; @@ -249,6 +280,12 @@ parse_arguments(int argc, char **argv) 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; } } -- cgit v1.2.3 From d487bd4c40b9a6c21c7d6c70707f09610b7d1129 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Sat, 13 Apr 2019 19:15:42 +0200 Subject: v0.3.3 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index cd75802..63d2d3d 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -47,7 +47,7 @@ extern bool CGSIsSecureEventInputSet(void); global unsigned major_version = 0; global unsigned minor_version = 3; -global unsigned patch_version = 2; +global unsigned patch_version = 3; global struct carbon_event carbon; global struct event_tap event_tap; -- cgit v1.2.3 From 97a2ea9744dfc1946bc022389caae834b62a298f Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Wed, 24 Apr 2019 17:57:44 +0200 Subject: #77 make pid-file user-specific --- src/skhd.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/skhd.c b/src/skhd.c index 63d2d3d..26334e8 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -43,7 +43,7 @@ extern bool CGSIsSecureEventInputSet(void); #define global static #define SKHD_CONFIG_FILE ".skhdrc" -#define SKHD_PID_FILE "/tmp/skhd.pid" +#define SKHD_PIDFILE_FMT "/tmp/skhd_%s.pid" global unsigned major_version = 0; global unsigned minor_version = 3; @@ -192,9 +192,17 @@ sigusr1_handler(int signal) internal pid_t read_pid_file(void) { + char pid_file[255] = {}; pid_t pid = 0; - int handle = open(SKHD_PID_FILE, O_RDWR); + 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"); } @@ -212,9 +220,17 @@ read_pid_file(void) internal void create_pid_file(void) { + char pid_file[255] = {}; pid_t pid = getpid(); - int handle = open(SKHD_PID_FILE, O_CREAT | O_WRONLY, 0644); + 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_WRONLY, 0644); if (handle == -1) { error("skhd: could not create pid-file! abort..\n"); } -- cgit v1.2.3 From 63eb2393377792943b91b6f3eb17776c02db921b Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 21 May 2019 17:34:53 +0200 Subject: #83 use fcntl instead of flock to acquire exclusive lock --- src/skhd.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/skhd.c b/src/skhd.c index 26334e8..abc9034 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -230,13 +230,21 @@ create_pid_file(void) 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_WRONLY, 0644); + int handle = open(pid_file, O_CREAT | O_RDWR, 0644); if (handle == -1) { error("skhd: could not create pid-file! abort..\n"); } - if (flock(handle, LOCK_EX | LOCK_NB) == -1) { - error("skhd: could not lock 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.. %d\n", errno); } else if (write(handle, &pid, sizeof(pid_t)) == -1) { error("skhd: could not write pid-file! abort..\n"); } -- cgit v1.2.3 From 895ff8e921246b0e81d3c70f0e9c7505205fbfab Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 21 May 2019 17:35:35 +0200 Subject: update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3100b3e..d0c4d4c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ **skhd** uses a pid-file to make sure that only one instance is running at any moment in time. This also allows for the ability to trigger a manual reload of the config file by invoking `skhd --reload` at any time while an instance of **skhd** is running. The pid-file is saved -as `/tmp/skhd.pid` and so the user that is running **skhd** must have write permission to said path. +as `/tmp/skhd_%USER.pid` and so the user that is running **skhd** must have write permission to said path. feature comparison between **skhd** and **khd** -- cgit v1.2.3 From ec57f56bb218a6f4effe7062493c81ec3f64f3e5 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 21 May 2019 17:35:43 +0200 Subject: v0.3.4 --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index abc9034..432f43c 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -47,7 +47,7 @@ extern bool CGSIsSecureEventInputSet(void); global unsigned major_version = 0; global unsigned minor_version = 3; -global unsigned patch_version = 3; +global unsigned patch_version = 4; global struct carbon_event carbon; global struct event_tap event_tap; -- cgit v1.2.3 From fc32b805c0abd349e36d1fe3a79026920760fc29 Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 21 May 2019 17:36:40 +0200 Subject: .. --- src/skhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skhd.c b/src/skhd.c index 432f43c..d67f74c 100644 --- a/src/skhd.c +++ b/src/skhd.c @@ -244,7 +244,7 @@ create_pid_file(void) }; if (fcntl(handle, F_SETLK, &lockfd) == -1) { - error("skhd: could not lock pid-file! abort.. %d\n", errno); + 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"); } -- cgit v1.2.3 From 22071ddd738ad591f42500ad8dbe818ffd1eb93d Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Tue, 21 May 2019 21:32:44 +0200 Subject: .. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d0c4d4c..f1647e5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ **skhd** uses a pid-file to make sure that only one instance is running at any moment in time. This also allows for the ability to trigger a manual reload of the config file by invoking `skhd --reload` at any time while an instance of **skhd** is running. The pid-file is saved -as `/tmp/skhd_%USER.pid` and so the user that is running **skhd** must have write permission to said path. +as `/tmp/skhd_$USER.pid` and so the user that is running **skhd** must have write permission to said path. feature comparison between **skhd** and **khd** -- cgit v1.2.3