From 65ab4db4cf1b54a279acac97eb251cbb713203fc Mon Sep 17 00:00:00 2001 From: Elias Aebi Date: Sat, 17 Feb 2024 18:25:16 +0100 Subject: [PATCH] macOS: implement the directory watcher --- gral_macos.m | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/gral_macos.m b/gral_macos.m index ab4221f..4ca71a4 100644 --- a/gral_macos.m +++ b/gral_macos.m @@ -54,6 +54,17 @@ static int utf16_index_to_utf8(CFStringRef string, NSUInteger index) { return i8; } +@interface CallbackObject: NSObject { +@public + void (*callback)(void *user_data); + void *user_data; +} +@end +@implementation CallbackObject +- (void)invoke:(id)object { + callback(user_data); +} +@end /*================ APPLICATION @@ -125,7 +136,7 @@ void gral_image_delete(struct gral_image *image) { } struct gral_font *gral_font_create(struct gral_window *window, char const *name, float size) { - CFStringRef string = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8); CTFontRef font = CTFontCreateWithName(string, size, NULL); CFRelease(string); return (struct gral_font *)font; @@ -149,7 +160,7 @@ void gral_font_get_metrics(struct gral_window *window, struct gral_font *font, f } struct gral_text *gral_text_create(struct gral_window *window, char const *text, struct gral_font *font) { - CFStringRef string = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8); + CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, text, kCFStringEncodingUTF8); CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(NULL, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(attributes, kCTFontAttributeName, font); CFAttributedStringRef attributed_string = CFAttributedStringCreate(NULL, string, attributes); @@ -679,7 +690,7 @@ - (void)invoke:(NSTimer *)timer { } @end struct gral_timer *gral_window_create_timer(struct gral_window *window, int milliseconds, void (*callback)(void *user_data), void *user_data) { - TimerCallbackObject *callback_object = [[TimerCallbackObject alloc] init]; + CallbackObject *callback_object = [[CallbackObject alloc] init]; callback_object->callback = callback; callback_object->user_data = user_data; NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:milliseconds/1000.0 target:callback_object selector:@selector(invoke:) userInfo:nil repeats:YES]; @@ -703,7 +714,7 @@ - (void)invoke:(id)object { } @end void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { - MainThreadCallbackObject *callback_object = [[MainThreadCallbackObject alloc] init]; + CallbackObject *callback_object = [[CallbackObject alloc] init]; callback_object->callback = callback; callback_object->user_data = user_data; [callback_object performSelectorOnMainThread:@selector(invoke:) withObject:nil waitUntilDone:NO]; @@ -795,13 +806,49 @@ void gral_directory_remove(char const *path) { rmdir(path); } +static void event_stream_callback(ConstFSEventStreamRef streamRef, void *info, size_t numEvents, void *eventPaths, FSEventStreamEventFlags const *eventFlags, FSEventStreamEventId const *eventIds) { + CallbackObject *callback_object = info; + for (size_t i = 0; i < numEvents; i++) { + callback_object->callback(callback_object->user_data); + } +} +static void callback_object_release(void const *info) { + CallbackObject *callback_object = info; + [callback_object release]; +} +static void const *callback_object_retain(void const *info) { + CallbackObject *callback_object = info; + [callback_object retain]; + return info; +} + struct gral_directory_watcher *gral_directory_watch(char const *path, void (*callback)(void *user_data), void *user_data) { - // TODO: implement - return NULL; + CallbackObject *callback_object = [[CallbackObject alloc] init]; + callback_object->callback = callback; + callback_object->user_data = user_data; + FSEventStreamContext context; + context.copyDescription = NULL; + context.info = callback_object; + context.release = callback_object_release; + context.retain = callback_object_retain; + context.version = 0; + CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8); + CFArrayRef paths = CFArrayCreate(kCFAllocatorDefault, (void const **)&string, 1, &kCFTypeArrayCallBacks); + CFRelease(string); + FSEventStreamRef event_stream = FSEventStreamCreate(kCFAllocatorDefault, event_stream_callback, &context, paths, kFSEventStreamEventIdSinceNow, 0.0, kFSEventStreamCreateFlagNone); + CFRelease(paths); + [callback_object release]; + FSEventStreamScheduleWithRunLoop(event_stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + FSEventStreamStart(event_stream); + return (struct gral_directory_watcher *)event_stream; } void gral_directory_watcher_delete(struct gral_directory_watcher *directory_watcher) { - // TODO: implement + FSEventStreamRef event_stream = (FSEventStreamRef)directory_watcher; + FSEventStreamStop(event_stream); + //FSEventStreamUnscheduleFromRunLoop(event_stream, ); + FSEventStreamInvalidate(event_stream); + FSEventStreamRelease(event_stream); }