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:
Felix Kratz 2023-07-18 16:36:35 +02:00
parent bfceb847dd
commit b91c61a520
14 changed files with 178 additions and 58 deletions

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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
View 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
View 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();

View file

@ -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);

View file

@ -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"

View file

@ -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;
}