mirror of
https://github.com/FelixKratz/SketchyBar
synced 2024-11-10 05:44:16 +00:00
add hotload functionality
The hotload feature is disabled by default and can be controlled via --hotload <boolean>. When enabled, a change to any file in the CONFIG_DIR will lead to an automatic reload of the config. When a hotload event occurs, all items are removed and all settings/defaults are reset. Then the sketchybarrc is executed again, which gives the same effect as restarting the program manually.
This commit is contained in:
parent
bfceb847dd
commit
b91c61a520
14 changed files with 178 additions and 58 deletions
4
makefile
4
makefile
|
@ -14,7 +14,9 @@ SRC = src
|
|||
_OBJ = alias.o background.o bar_item.o custom_events.o event.o graph.o \
|
||||
image.o mouse.o shadow.o font.o text.o message.o mouse.o bar.o color.o \
|
||||
window.o bar_manager.o display.o event_loop.o group.o mach.o popup.o \
|
||||
animation.o workspace.om volume.o slider.o power.o wifi.om media.om
|
||||
animation.o workspace.om volume.o slider.o power.o wifi.om media.om \
|
||||
hotload.o
|
||||
|
||||
OBJ = $(patsubst %, $(ODIR)/%, $(_OBJ))
|
||||
|
||||
.PHONY: all clean arm x86 profile leak universal
|
||||
|
|
|
@ -245,3 +245,18 @@ bool animator_update(struct animator* animator) {
|
|||
|
||||
return needs_refresh;
|
||||
}
|
||||
|
||||
void animator_destroy(struct animator* animator) {
|
||||
if (animator->animation_count > 0) {
|
||||
CFRunLoopRemoveTimer(CFRunLoopGetMain(),
|
||||
animator->clock,
|
||||
kCFRunLoopCommonModes);
|
||||
CFRelease(animator->clock);
|
||||
|
||||
for (int i = 0; i < animator->animation_count; i++) {
|
||||
animation_destroy(animator->animations[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (animator->animations) free(animator->animations);
|
||||
}
|
||||
|
|
|
@ -120,3 +120,4 @@ bool animator_cancel(struct animator* animator, void* target, animator_function*
|
|||
void animator_cancel_locked(struct animator* animator, void* target, animator_function* function);
|
||||
bool animator_update(struct animator* animator);
|
||||
void animator_lock(struct animator* animator);
|
||||
void animator_destroy(struct animator* animator);
|
||||
|
|
|
@ -760,7 +760,7 @@ void bar_item_inherit_from_item(struct bar_item* bar_item, struct bar_item* ance
|
|||
}
|
||||
}
|
||||
|
||||
void bar_item_destroy(struct bar_item* bar_item) {
|
||||
void bar_item_destroy(struct bar_item* bar_item, bool free_memory) {
|
||||
if (bar_item->name) free(bar_item->name);
|
||||
if (bar_item->script) free(bar_item->script);
|
||||
if (bar_item->click_script) free(bar_item->click_script);
|
||||
|
@ -786,7 +786,7 @@ void bar_item_destroy(struct bar_item* bar_item) {
|
|||
}
|
||||
if (bar_item->windows) free(bar_item->windows);
|
||||
|
||||
free(bar_item);
|
||||
if (free_memory) free(bar_item);
|
||||
}
|
||||
|
||||
void bar_item_serialize(struct bar_item* bar_item, FILE* rsp) {
|
||||
|
|
|
@ -99,7 +99,7 @@ struct bar_item* bar_item_create();
|
|||
void bar_item_inherit_from_item(struct bar_item* bar_item, struct bar_item* ancestor);
|
||||
void bar_item_init(struct bar_item* bar_item, struct bar_item* default_item);
|
||||
void bar_item_serialize(struct bar_item* bar_item, FILE* rsp);
|
||||
void bar_item_destroy(struct bar_item* bar_item);
|
||||
void bar_item_destroy(struct bar_item* bar_item, bool free_memory);
|
||||
|
||||
bool bar_item_is_shown(struct bar_item* bar_item);
|
||||
void bar_item_needs_update(struct bar_item* bar_item);
|
||||
|
|
|
@ -23,6 +23,7 @@ void bar_manager_init(struct bar_manager* bar_manager) {
|
|||
bar_manager->bar_needs_update = false;
|
||||
bar_manager->bars = NULL;
|
||||
bar_manager->bar_count = 0;
|
||||
bar_manager->bar_items = NULL;
|
||||
bar_manager->bar_item_count = 0;
|
||||
bar_manager->displays = DISPLAY_ALL_PATTERN;
|
||||
bar_manager->position = POSITION_TOP;
|
||||
|
@ -169,7 +170,7 @@ void bar_manager_remove_item(struct bar_manager* bar_manager, struct bar_item* b
|
|||
sizeof(struct bar_item*)*bar_manager->bar_item_count);
|
||||
}
|
||||
|
||||
bar_item_destroy(bar_item);
|
||||
bar_item_destroy(bar_item, true);
|
||||
}
|
||||
|
||||
bool bar_manager_set_margin(struct bar_manager* bar_manager, int margin) {
|
||||
|
@ -991,17 +992,27 @@ void bar_manager_handle_notification(struct bar_manager* bar_manager, struct not
|
|||
}
|
||||
|
||||
void bar_manager_destroy(struct bar_manager* bar_manager) {
|
||||
animator_destroy(&bar_manager->animator);
|
||||
|
||||
for (int i = 0; i < bar_manager->bar_item_count; i++) {
|
||||
bar_item_destroy(bar_manager->bar_items[i]);
|
||||
bar_item_destroy(bar_manager->bar_items[i], true);
|
||||
}
|
||||
if (bar_manager->bar_items) free(bar_manager->bar_items);
|
||||
for (int i = 0; i < bar_manager->bar_count; i++) {
|
||||
bar_destroy(bar_manager->bars[i]);
|
||||
}
|
||||
|
||||
bar_item_destroy(&bar_manager->default_item, false);
|
||||
custom_events_destroy(&bar_manager->custom_events);
|
||||
background_destroy(&bar_manager->background);
|
||||
|
||||
if (bar_manager->bars) free(bar_manager->bars);
|
||||
CFRunLoopRemoveTimer(CFRunLoopGetMain(),
|
||||
bar_manager->clock,
|
||||
kCFRunLoopCommonModes);
|
||||
|
||||
CFRunLoopTimerInvalidate(bar_manager->clock);
|
||||
CFRelease(bar_manager->clock);
|
||||
}
|
||||
|
||||
void bar_manager_serialize(struct bar_manager* bar_manager, FILE* rsp) {
|
||||
|
|
|
@ -17,6 +17,7 @@ void custom_event_destroy(struct custom_event* custom_event) {
|
|||
|
||||
void custom_events_init(struct custom_events* custom_events) {
|
||||
custom_events->count = 0;
|
||||
custom_events->events = NULL;
|
||||
|
||||
// System Events
|
||||
custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_FRONT_APP_SWITCHED), NULL);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "event.h"
|
||||
#include "event_loop.h"
|
||||
#include "hotload.h"
|
||||
|
||||
extern struct event_loop g_event_loop;
|
||||
extern struct bar_manager g_bar_manager;
|
||||
|
@ -388,3 +389,11 @@ EVENT_CALLBACK(EVENT_HANDLER_MEDIA_CHANGED) {
|
|||
free(context);
|
||||
return EVENT_SUCCESS;
|
||||
}
|
||||
|
||||
EVENT_CALLBACK(EVENT_HANDLER_HOTLOAD) {
|
||||
bar_manager_destroy(&g_bar_manager);
|
||||
bar_manager_init(&g_bar_manager);
|
||||
bar_manager_begin(&g_bar_manager);
|
||||
exec_config_file();
|
||||
return EVENT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ EVENT_CALLBACK(EVENT_HANDLER_BRIGHTNESS_CHANGED);
|
|||
EVENT_CALLBACK(EVENT_HANDLER_POWER_SOURCE_CHANGED);
|
||||
EVENT_CALLBACK(EVENT_HANDLER_MEDIA_CHANGED);
|
||||
EVENT_CALLBACK(EVENT_HANDLER_DISTRIBUTED_NOTIFICATION);
|
||||
EVENT_CALLBACK(EVENT_HANDLER_HOTLOAD);
|
||||
|
||||
#define EVENT_QUEUED 0x0
|
||||
#define EVENT_PROCESSED 0x1
|
||||
|
@ -70,6 +71,7 @@ enum event_type {
|
|||
POWER_SOURCE_CHANGED,
|
||||
MEDIA_CHANGED,
|
||||
DISTRIBUTED_NOTIFICATION,
|
||||
HOTLOAD,
|
||||
|
||||
EVENT_TYPE_COUNT
|
||||
};
|
||||
|
@ -101,6 +103,7 @@ static const char *event_type_str[] = {
|
|||
[POWER_SOURCE_CHANGED] = "power_source_changed",
|
||||
[MEDIA_CHANGED] = "media_changed",
|
||||
[DISTRIBUTED_NOTIFICATION] = "distributed_notification",
|
||||
[HOTLOAD] = "hotload",
|
||||
|
||||
[EVENT_TYPE_COUNT] = "event_type_count"
|
||||
};
|
||||
|
@ -131,6 +134,7 @@ static event_callback *event_handler[] = {
|
|||
[SHELL_REFRESH] = EVENT_HANDLER_SHELL_REFRESH,
|
||||
[ANIMATOR_REFRESH] = EVENT_HANDLER_ANIMATOR_REFRESH,
|
||||
[MACH_MESSAGE] = EVENT_HANDLER_MACH_MESSAGE,
|
||||
[HOTLOAD] = EVENT_HANDLER_HOTLOAD,
|
||||
};
|
||||
|
||||
struct event {
|
||||
|
|
101
src/hotload.c
Normal file
101
src/hotload.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "bar_manager.h"
|
||||
#include "event.h"
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <libgen.h>
|
||||
|
||||
extern char g_config_file[4096];
|
||||
extern char g_name[256];
|
||||
pthread_mutex_t hotload_lock;
|
||||
bool g_hotload = false;
|
||||
|
||||
void hotload_set_state(int state) {
|
||||
g_hotload = state;
|
||||
}
|
||||
|
||||
int hotload_get_state() {
|
||||
return g_hotload;
|
||||
}
|
||||
|
||||
static bool get_config_file(char *restrict filename, char *restrict buffer, int buffer_size) {
|
||||
char *xdg_home = getenv("XDG_CONFIG_HOME");
|
||||
if (xdg_home && *xdg_home) {
|
||||
snprintf(buffer, buffer_size, "%s/%s/%s", xdg_home, g_name, filename);
|
||||
if (file_exists(buffer)) return true;
|
||||
}
|
||||
|
||||
char *home = getenv("HOME");
|
||||
if (!home) return false;
|
||||
|
||||
snprintf(buffer, buffer_size, "%s/.config/%s/%s", home, g_name, filename);
|
||||
if (file_exists(buffer)) return true;
|
||||
|
||||
snprintf(buffer, buffer_size, "%s/.%s", home, filename);
|
||||
return file_exists(buffer);
|
||||
}
|
||||
|
||||
void exec_config_file() {
|
||||
if (!*g_config_file
|
||||
&& !get_config_file("sketchybarrc", g_config_file, sizeof(g_config_file))) {
|
||||
printf("could not locate config file..\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file_exists(g_config_file)) {
|
||||
printf("file '%s' does not exist..\n", g_config_file);
|
||||
return;
|
||||
}
|
||||
|
||||
setenv("CONFIG_DIR", dirname(g_config_file), 1);
|
||||
|
||||
if (!ensure_executable_permission(g_config_file)) {
|
||||
printf("could not set the executable permission bit for '%s'\n", g_config_file);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fork_exec(g_config_file, NULL)) {
|
||||
printf("failed to execute file '%s'\n", g_config_file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handler(ConstFSEventStreamRef stream,
|
||||
void* context,
|
||||
size_t count,
|
||||
void* paths,
|
||||
const FSEventStreamEventFlags* flags,
|
||||
const FSEventStreamEventId* ids) {
|
||||
if (g_hotload && count > 0) {
|
||||
struct event *event = event_create(&g_event_loop, HOTLOAD, NULL);
|
||||
event_loop_post(&g_event_loop, event);
|
||||
}
|
||||
}
|
||||
|
||||
int begin_receiving_config_change_events() {
|
||||
char* file = dirname(g_config_file);
|
||||
CFStringRef file_ref = CFStringCreateWithCString(
|
||||
kCFAllocatorDefault, file, kCFStringEncodingUTF8);
|
||||
|
||||
CFArrayRef paths = CFArrayCreate(NULL,
|
||||
(const void**)&file_ref,
|
||||
1,
|
||||
&kCFTypeArrayCallBacks);
|
||||
|
||||
FSEventStreamRef stream = FSEventStreamCreate(
|
||||
kCFAllocatorDefault,
|
||||
handler,
|
||||
NULL,
|
||||
paths,
|
||||
kFSEventStreamEventIdSinceNow,
|
||||
0.5,
|
||||
kFSEventStreamCreateFlagNoDefer
|
||||
| kFSEventStreamCreateFlagFileEvents);
|
||||
|
||||
CFRelease(file_ref);
|
||||
CFRelease(paths);
|
||||
|
||||
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),
|
||||
kCFRunLoopDefaultMode);
|
||||
|
||||
FSEventStreamStart(stream);
|
||||
return 0;
|
||||
}
|
9
src/hotload.h
Normal file
9
src/hotload.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
#define HOTLOAD_STATE_ENABLED true
|
||||
#define HOTLOAD_STATE_DISABLED false
|
||||
|
||||
void exec_config_file();
|
||||
void begin_receiving_config_change_events();
|
||||
void hotload_set_state(int state);
|
||||
int hotload_get_state();
|
|
@ -1,5 +1,6 @@
|
|||
#include "message.h"
|
||||
#include "misc/defines.h"
|
||||
#include "hotload.h"
|
||||
|
||||
extern struct event_loop g_event_loop;
|
||||
extern struct bar_manager g_bar_manager;
|
||||
|
@ -696,6 +697,9 @@ void handle_message_mach(struct mach_buffer* buffer) {
|
|||
} else if (token_equals(command, DOMAIN_EXIT)) {
|
||||
bar_manager_destroy(&g_bar_manager);
|
||||
exit(0);
|
||||
} else if (token_equals(command, DOMAIN_HOTLOAD)) {
|
||||
struct token token = get_token(&message);
|
||||
hotload_set_state(evaluate_boolean_state(token, hotload_get_state()));
|
||||
} else {
|
||||
char* rbr_msg = get_batch_line(&message);
|
||||
respond(rsp, "[!] Unknown domain '%s'\n", command.text);
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#define DOMAIN_EXIT "--exit"
|
||||
|
||||
#define DOMAIN_HOTLOAD "--hotload"
|
||||
|
||||
#define SUB_DOMAIN_ICON "icon"
|
||||
#define SUB_DOMAIN_LABEL "label"
|
||||
#define SUB_DOMAIN_BACKGROUND "background"
|
||||
|
|
|
@ -7,22 +7,23 @@
|
|||
#include "power.h"
|
||||
#include "wifi.h"
|
||||
#include "misc/help.h"
|
||||
#include <libgen.h>
|
||||
#include "media.h"
|
||||
#include "hotload.h"
|
||||
#include <libgen.h>
|
||||
|
||||
#define LCFILE_PATH_FMT "/tmp/%s_%s.lock"
|
||||
#define LCFILE_PATH_FMT "/tmp/%s_%s.lock"
|
||||
|
||||
#define CLIENT_OPT_LONG "--message"
|
||||
#define CLIENT_OPT_SHRT "-m"
|
||||
#define CLIENT_OPT_LONG "--message"
|
||||
#define CLIENT_OPT_SHRT "-m"
|
||||
|
||||
#define VERSION_OPT_LONG "--version"
|
||||
#define VERSION_OPT_SHRT "-v"
|
||||
#define VERSION_OPT_LONG "--version"
|
||||
#define VERSION_OPT_SHRT "-v"
|
||||
|
||||
#define CONFIG_OPT_LONG "--config"
|
||||
#define CONFIG_OPT_SHRT "-c"
|
||||
#define CONFIG_OPT_LONG "--config"
|
||||
#define CONFIG_OPT_SHRT "-c"
|
||||
|
||||
#define HELP_OPT_LONG "--help"
|
||||
#define HELP_OPT_SHRT "-h"
|
||||
#define HELP_OPT_LONG "--help"
|
||||
#define HELP_OPT_SHRT "-h"
|
||||
|
||||
#define MAJOR 2
|
||||
#define MINOR 15
|
||||
|
@ -115,48 +116,6 @@ static void acquire_lockfile(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool get_config_file(char *restrict filename, char *restrict buffer, int buffer_size) {
|
||||
char *xdg_home = getenv("XDG_CONFIG_HOME");
|
||||
if (xdg_home && *xdg_home) {
|
||||
snprintf(buffer, buffer_size, "%s/%s/%s", xdg_home, g_name, filename);
|
||||
if (file_exists(buffer)) return true;
|
||||
}
|
||||
|
||||
char *home = getenv("HOME");
|
||||
if (!home) return false;
|
||||
|
||||
snprintf(buffer, buffer_size, "%s/.config/%s/%s", home, g_name, filename);
|
||||
if (file_exists(buffer)) return true;
|
||||
|
||||
snprintf(buffer, buffer_size, "%s/.%s", home, filename);
|
||||
return file_exists(buffer);
|
||||
}
|
||||
|
||||
static void exec_config_file(void) {
|
||||
if (!*g_config_file
|
||||
&& !get_config_file("sketchybarrc", g_config_file, sizeof(g_config_file))) {
|
||||
printf("could not locate config file..\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file_exists(g_config_file)) {
|
||||
printf("file '%s' does not exist..\n", g_config_file);
|
||||
return;
|
||||
}
|
||||
|
||||
setenv("CONFIG_DIR", dirname(g_config_file), 1);
|
||||
|
||||
if (!ensure_executable_permission(g_config_file)) {
|
||||
printf("could not set the executable permission bit for '%s'\n", g_config_file);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fork_exec(g_config_file, NULL)) {
|
||||
printf("failed to execute file '%s'\n", g_config_file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
static inline void init_misc_settings(void) {
|
||||
|
@ -258,6 +217,8 @@ int main(int argc, char **argv) {
|
|||
initialize_media_events();
|
||||
|
||||
exec_config_file();
|
||||
|
||||
begin_receiving_config_change_events();
|
||||
RunApplicationEventLoop();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue