aboutsummaryrefslogtreecommitdiff
path: root/src/hotload.c
blob: c0e82793a441d0eb6f996993cd5904539814fea9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "hotload.h"
#include "hotkey.h"

#include <stdlib.h>
#include <string.h>

#define internal static

internal char *
copy_string(const char *s)
{
    unsigned length = strlen(s);
    char *result = malloc(length + 1);
    memcpy(result, s, length);
    result[length] = '\0';
    return result;
}

internal char *
file_directory(const char *file)
{
    char *last_slash = strrchr(file, '/');
    *last_slash = '\0';
    char *directory = copy_string(file);
    *last_slash = '/';
    return directory;
}

internal char *
file_name(const char *file)
{
    char *last_slash = strrchr(file, '/');
    char *name = copy_string(last_slash + 1);
    return name;
}

bool hotloader_watched_file(struct hotloader *hotloader, char *absolutepath)
{
    bool success = false;
    for(unsigned index = 0; success == 0 && 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) {
                success = true;
            }
        }

        free(filename);
        free(directory);
    }

    return success;
}

void hotloader_add_file(struct hotloader *hotloader, const char *file)
{
    if(!hotloader->enabled) {
        struct watched_file watch_info;
        watch_info.directory = file_directory(file);
        watch_info.filename = file_name(file);
        hotloader->watch_list[hotloader->watch_count++] = watch_info;
        printf("hotload: watching file '%s' in directory '%s'\n", watch_info.filename, watch_info.directory);
    }
}

bool hotloader_begin(struct hotloader *hotloader, hotloader_callback *callback)
{
    if(!hotloader->enabled) {
        if(hotloader->watch_count) {
            CFStringRef string_refs[hotloader->watch_count];
            for(unsigned index = 0; index < hotloader->watch_count; ++index) {
                string_refs[index] = CFStringCreateWithCString(kCFAllocatorDefault,
                                                               hotloader->watch_list[index].directory,
                                                               kCFStringEncodingUTF8);
            }

            FSEventStreamContext context = {};
            context.info = (void *) hotloader;

            hotloader->enabled = true;
            hotloader->path = (CFArrayRef) CFArrayCreate(NULL, (const void **) string_refs, hotloader->watch_count, &kCFTypeArrayCallBacks);
            hotloader->flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
            hotloader->stream = FSEventStreamCreate(NULL,
                                                    callback,
                                                    &context,
                                                    hotloader->path,
                                                    kFSEventStreamEventIdSinceNow,
                                                    0.5,
                                                    hotloader->flags);
            FSEventStreamScheduleWithRunLoop(hotloader->stream, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
            FSEventStreamStart(hotloader->stream);
            return true;
        }
    }
    return false;
}

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].directory);
            free(hotloader->watch_list[index].filename);
            CFRelease(string_ref);
        }

        CFRelease(hotloader->path);
        memset(hotloader, 0, sizeof(struct hotloader));
    }
}