diff --git a/src/config.h b/src/config.h index ef6eedc9..0ac154d3 100644 --- a/src/config.h +++ b/src/config.h @@ -18,8 +18,8 @@ #endif /* If we are not compiling in developer mode, assume resources are embedded as - * a tar archive in the executable. Otherwise, look for resources in the file - * system. */ + * a tar archive in the executable. Otherwise, assume resources are files on + * disk. */ #define RESOURCES_EMBEDDED (!DEVELOPER) #define RESOURCE_RELOADING (DEVELOPER && !RESOURCES_EMBEDDED) @@ -28,7 +28,7 @@ #define IMAGE_PIXELS_PER_UNIT 128.0 -/* How many ticks back in time should the user blend between? +/* How many ticks back in time should the user thread blend between? * = * * E.g: At 1.5, the user thread will render 75ms back in time if the sim runs at 50tps */ diff --git a/src/log.h b/src/log.h index 2a9b8d95..24e565ff 100644 --- a/src/log.h +++ b/src/log.h @@ -59,67 +59,67 @@ void log_register_callback(log_event_callback_func *func); #if LOG_LEVEL(LOG_LEVEL_CRITICAL) # if LOG_INCLUDE_SOURCE_LOCATION # define log_critical(msg) _log(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, msg) -# define logf_critical(fmt, ...) _logf(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_critical(fmt_lit, ...) _logf(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # else # define log_critical(msg) _log(LOG_LEVEL_CRITICAL, msg) -# define logf_critical(fmt, ...) _logf(LOG_LEVEL_CRITICAL, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_critical(fmt_lit, ...) _logf(LOG_LEVEL_CRITICAL, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # endif #else # define log_critical(msg) -# define logf_critical(fmt, ...) +# define logf_critical(fmt_lit, ...) #endif #if LOG_LEVEL(LOG_LEVEL_ERROR) # if LOG_INCLUDE_SOURCE_LOCATION # define log_error(msg) _log(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, msg) -# define logf_error(fmt, ...) _logf(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_error(fmt_lit, ...) _logf(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # else # define log_error(msg) _log(LOG_LEVEL_ERROR, msg) -# define logf_error(fmt, ...) _logf(LOG_LEVEL_ERROR, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_error(fmt_lit, ...) _logf(LOG_LEVEL_ERROR, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # endif #else # define log_error(msg) -# define logf_error(fmt, ...) +# define logf_error(fmt_lit, ...) #endif #if LOG_LEVEL(LOG_LEVEL_WARNING) # if LOG_INCLUDE_SOURCE_LOCATION # define log_warning(msg) _log(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, msg) -# define logf_warning(fmt, ...) _logf(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_warning(fmt_lit, ...) _logf(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # else # define log_warning(msg) _log(LOG_LEVEL_WARNING, msg) -# define logf_warning(fmt, ...) _logf(LOG_LEVEL_WARNING, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_warning(fmt_lit, ...) _logf(LOG_LEVEL_WARNING, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # endif #else # define log_warning(msg) -# define logf_warning(fmt, ...) +# define logf_warning(fmt_lit, ...) #endif #if LOG_LEVEL(LOG_LEVEL_DEBUG) # if LOG_INCLUDE_SOURCE_LOCATION # define log_debug(msg) _log(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, msg) -# define logf_debug(fmt, ...) _logf(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # else # define log_debug(msg) _log(LOG_LEVEL_DEBUG, msg) -# define logf_debug(fmt, ...) _logf(LOG_LEVEL_DEBUG, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # endif #else # define log_debug(msg) -# define logf_debug(fmt, ...) +# define logf_debug(fmt_lit, ...) #endif #if LOG_LEVEL(LOG_LEVEL_INFO) # if LOG_INCLUDE_SOURCE_LOCATION # define log_info(msg) _log(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, msg) -# define logf_info(fmt, ...) _logf(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_info(fmt_lit, ...) _logf(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # else # define log_info(msg) _log(LOG_LEVEL_INFO, msg) -# define logf_info(fmt, ...) _logf(LOG_LEVEL_INFO, LIT(fmt) , ## __VA_ARGS__, FMT_END) +# define logf_info(fmt_lit, ...) _logf(LOG_LEVEL_INFO, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END) # endif #else # define log_info(msg) -# define logf_info(fmt, ...) +# define logf_info(fmt_lit, ...) #endif /* ========================== * diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index 885cc04f..8d055215 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -26,6 +26,7 @@ #pragma comment(lib, "d3dcompiler") #define MAX_CMD_BUFFERS 1024 +#define SHADER_INFO_LOOKUP_BINS 64 /* FIXME: Enable this and resolve unreleased references */ //#define D3D11_DEBUG RTC @@ -40,10 +41,6 @@ struct dx11_shader { ID3D11InputLayout *input_layout; ID3D11VertexShader *vs; ID3D11PixelShader *ps; - -#if RESOURCE_RELOADING - struct sys_datetime last_modified_load_attempt; /* The last modified time of the shader src file that a load has been attempted on */ -#endif }; struct dx11_constant_buffer_data { @@ -136,6 +133,11 @@ struct dx11_shader_desc { char *name_cstr; u32 vertex_size; D3D11_INPUT_ELEMENT_DESC input_layout_desc[64]; /* NULL terminated array */ + + /* Internal */ +#if RESOURCE_RELOADING + struct atomic_i32 should_reload; +#endif }; GLOBAL struct { @@ -166,6 +168,7 @@ GLOBAL struct { struct dx11_shader shaders[NUM_SHADERS]; struct dx11_shader_desc shader_info[NUM_SHADERS]; + struct fixed_dict shader_info_lookup; } G = ZI, DEBUG_ALIAS(G, G_renderer_d3d11); @@ -210,7 +213,8 @@ INTERNAL void init_shader_table(void) { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } - } + }, + { 0 } }; /* Grid shader layout */ @@ -224,8 +228,16 @@ INTERNAL void init_shader_table(void) { "THICKNESS", 0, DXGI_FORMAT_R32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "SPACING", 0, DXGI_FORMAT_R32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "OFFSET", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } - } + }, + { 0 } }; + + G.shader_info_lookup = fixed_dict_init(&G.arena, SHADER_INFO_LOOKUP_BINS); + for (u64 i = SHADER_NONE + 1; i < ARRAY_COUNT(G.shader_info); ++i) { + struct dx11_shader_desc *desc = &G.shader_info[i]; + struct string name = string_from_cstr_no_limit(desc->name_cstr); + fixed_dict_set(&G.arena, &G.shader_info_lookup, name, desc); + } } /* If shader compilation fails, then error string is returned allocated on `arena` */ @@ -317,58 +329,59 @@ INTERNAL void shader_release(struct dx11_shader *shader) } } -INTERNAL void reload_shaders(void) +INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_desc *desc) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); - for (u32 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) { - struct dx11_shader_desc *desc = &G.shader_info[i]; + { struct string name = string_from_cstr_no_limit(desc->name_cstr); - if (!resource_exists(name)) { - sys_panic(string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name))); - } - struct resource src_res = resource_open(name); - { - struct dx11_shader *old_shader = &G.shaders[i]; - b32 should_load = !old_shader->valid; -#if RESOURCE_RELOADING - struct sys_datetime last_modified = resource_get_time(&src_res).modified; - if (!MEMEQ_STRUCT(&last_modified, &old_shader->last_modified_load_attempt)) { - should_load = true; - } - old_shader->last_modified_load_attempt = last_modified; -#endif - if (should_load) { + struct string error_msg = ZI; + if (resource_exists(name)) { + struct resource src_res = resource_open(name); + { struct dx11_shader new_shader = ZI; - struct string error = shader_alloc(scratch.arena, &new_shader, desc, &src_res); -#if RESOURCE_RELOADING - new_shader.last_modified_load_attempt = last_modified; -#endif - if (error.len == 0) { + struct string comp_error = shader_alloc(scratch.arena, &new_shader, desc, &src_res); + if (comp_error.len == 0) { if (old_shader->valid) { shader_release(old_shader); } *old_shader = new_shader; } else { - struct string error_msg = string_format(scratch.arena, - LIT("Failed to compile shader \"%F\":\n\n%F"), - FMT_STR(name), - FMT_STR(error)); - if (old_shader->valid) { - log_error(error_msg); - } else { - sys_panic(error_msg); - } + error_msg = string_format(scratch.arena, + LIT("Failed to compile shader \"%F\":\n\n%F"), + FMT_STR(name), + FMT_STR(comp_error)); shader_release(&new_shader); } } + resource_close(&src_res); + } else { + error_msg = string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name)); + } + if (error_msg.len != 0) { + if (old_shader->valid) { + /* If shader failed to load but a working shader already exists, just error rather than panicking */ + log_error(error_msg); + } else { + sys_panic(error_msg); + } } - resource_close(&src_res); - } scratch_end(scratch); } +#if RESOURCE_RELOADING +INTERNAL RESOURCE_WATCH_CALLBACK_DEF(shader_resource_watch_callback, info) +{ + struct string name = info->name; + struct dx11_shader_desc *desc = (struct dx11_shader_desc *)fixed_dict_get(&G.shader_info_lookup, name); + if (desc) { + logf_info("Shader source file \"%F\" has changed", FMT_STR(name)); + atomic_i32_eval_exchange(&desc->should_reload, 1); + } +} +#endif + /* ========================== * * Startup * ========================== */ @@ -580,9 +593,18 @@ struct renderer_startup_receipt renderer_startup(struct sys_window *window) /* Init shaders */ logf_info("Compiling shaders"); - reload_shaders(); + for (u32 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) { + struct dx11_shader *shader = &G.shaders[i]; + struct dx11_shader_desc *desc = &G.shader_info[i]; + reload_shader(shader, desc); + } logf_info("Finished compiling shaders"); + /* Setup file change callbacks */ +#if RESOURCE_RELOADING + resource_register_watch_callback(shader_resource_watch_callback); +#endif + return (struct renderer_startup_receipt) { 0 }; } @@ -902,24 +924,21 @@ struct renderer_texture renderer_backbuffer_recreate(struct v2i32 size) void renderer_backbuffer_present(i32 vsync) { __prof; +#if RESOURCE_RELOADING + for (u64 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) { + struct dx11_shader_desc *desc = &G.shader_info[i]; + if (atomic_i32_eval_compare_exchange(&desc->should_reload, 1, 0) == 1) { + reload_shader(&G.shaders[i], desc); + } + } +#endif + renderer_capture_image_for_profiler(); { __profscope(IDXGISwapchain_Present); IDXGISwapChain1_Present(G.swapchain, vsync, 0); __profframe(0); } - -#if RESOURCE_RELOADING - { - i64 now_ns = sys_time_ns(); - const i64 reload_interval_ns = NS_FROM_SECONDS(0.1); - static f32 last_reload_ns = 0; - if (now_ns >= (last_reload_ns + reload_interval_ns)) { - reload_shaders(); - last_reload_ns = now_ns; - } - } -#endif } /* ========================== * diff --git a/src/resource.c b/src/resource.c index e5a4f352..8aef2d5c 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1,25 +1,50 @@ #include "resource.h" +#include "app.h" #include "arena.h" #include "tar.h" #include "incbin.h" -#if RESOURCES_EMBEDDED +/* ========================== * + * Global data + * ========================== */ -#include "inc.h" +#if RESOURCES_EMBEDDED +# include "inc.h" +#endif /* Add resource data to binary */ GLOBAL struct { struct arena arena; + +#if RESOURCES_EMBEDDED struct tar_archive archive; +#endif + +#if RESOURCE_RELOADING + struct sys_thread resource_watch_thread; + struct sys_mutex watch_callbacks_mutex; + b32 watch_stop; + resource_watch_callback *watch_callbacks[64]; + u64 num_watch_callbacks; +#endif } G = ZI, DEBUG_ALIAS(G, G_resource); + +/* ========================== * + * Startup + * ========================== */ + +#if RESOURCE_RELOADING +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(resource_watch_thread_entry_point, _); +INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(resource_shutdown); #endif struct resource_startup_receipt resource_startup(void) { + G.arena = arena_alloc(GIGABYTE(64)); + #if RESOURCES_EMBEDDED struct string embedded_data = inc_res_tar(); - G.arena = arena_alloc(GIGABYTE(64)); if (embedded_data.len <= 0) { sys_panic(LIT("No embedded resources found")); } @@ -31,9 +56,20 @@ struct resource_startup_receipt resource_startup(void) } #endif +#if RESOURCE_RELOADING + G.watch_callbacks_mutex = sys_mutex_alloc(); + app_register_exit_callback(&resource_shutdown); + G.resource_watch_thread = sys_thread_alloc(resource_watch_thread_entry_point, NULL, LIT("[P2] Resource watcher")); +#endif + + return (struct resource_startup_receipt) { 0 }; } +/* ========================== * + * Open / close + * ========================== */ + struct resource resource_open(struct string path) { __prof; @@ -70,13 +106,12 @@ void resource_close(struct resource *res_ptr) sys_file_map_close(res_ptr->_file_map); sys_file_close(res_ptr->_file); } - -struct sys_file_time resource_get_time(struct resource *res_ptr) -{ - return sys_file_get_time(res_ptr->_file); -} #endif +/* ========================== * + * Util + * ========================== */ + b32 resource_exists(struct string path) { __prof; @@ -87,3 +122,73 @@ b32 resource_exists(struct string path) return sys_is_file(path); #endif } + +#if !RESOURCES_EMBEDDED +struct sys_file_time resource_get_time(struct resource *res_ptr) +{ + return sys_file_get_time(res_ptr->_file); +} +#endif + +/* ========================== * + * Watch + * ========================== */ + +#if RESOURCE_RELOADING + +INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(resource_shutdown) +{ + __prof; + /* Wait until any watch callbacks finish before shutting down */ + struct sys_lock lock = sys_mutex_lock_e(&G.watch_callbacks_mutex); + { + G.watch_stop = true; + } + sys_mutex_unlock(&lock); +} + +void resource_register_watch_callback(resource_watch_callback *callback) +{ + struct sys_lock lock = sys_mutex_lock_e(&G.watch_callbacks_mutex); + { + if (G.num_watch_callbacks < ARRAY_COUNT(G.watch_callbacks)) { + G.watch_callbacks[G.num_watch_callbacks++] = callback; + } else { + sys_panic(LIT("Max resource watch callbacks reached")); + } + } + sys_mutex_unlock(&lock); +} + +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(resource_watch_thread_entry_point, _) +{ + (UNUSED)_; + struct temp_arena scratch = scratch_begin_no_conflict(); + struct sys_watch dir_watch = sys_watch_alloc(LIT("res")); + + /* NOTE: This thread is force-shutdown at the moment, however shutdown is guaranteed not to occur while the watch mutex is locked by the thread */ + volatile i32 run = true; + while (run) { + struct temp_arena temp = arena_temp_begin(scratch.arena); + struct sys_watch_info *info = sys_watch_wait(temp.arena, &dir_watch); + { + struct sys_lock lock = sys_mutex_lock_s(&G.watch_callbacks_mutex); + if (!G.watch_stop) { + while (info) { + for (u64 i = 0; i < G.num_watch_callbacks; ++i) { + resource_watch_callback *callback = G.watch_callbacks[i]; + callback(info); + } + info = info->next; + } + } + sys_mutex_unlock(&lock); + } + arena_temp_end(temp); + } + + sys_watch_release(&dir_watch); + scratch_end(scratch); +} + +#endif diff --git a/src/resource.h b/src/resource.h index 1a02703c..70780cd4 100644 --- a/src/resource.h +++ b/src/resource.h @@ -23,18 +23,35 @@ struct resource_startup_receipt resource_startup(void); struct resource resource_open(struct string path); -#define resource_get_data(res_ptr) (res_ptr)->_data - #if RESOURCES_EMBEDDED - #define resource_close(res_ptr) (UNUSED)res_ptr - #define resource_get_time(res_ptr) ((UNUSED)res_ptr, (struct sys_file_time) ZI) - #define resource_get_name(res_ptr) (res_ptr)->_file_name + #define resource_close(res_ptr) #else void resource_close(struct resource *res_ptr); - struct sys_file_time resource_get_time(struct resource *res_ptr); - #define resource_get_name(res_ptr) STRING((res_ptr)->_file_name_len, (res_ptr)->_file_name_text) #endif b32 resource_exists(struct string path); +#define resource_get_data(res_ptr) (res_ptr)->_data + +#if RESOURCES_EMBEDDED + #define resource_get_time(res_ptr) (struct sys_file_time) ZI +#else + struct sys_file_time resource_get_time(struct resource *res_ptr); +#endif + +#if RESOURCES_EMBEDDED + #define resource_get_name(res_ptr) (res_ptr)->_file_name +#else + #define resource_get_name(res_ptr) STRING((res_ptr)->_file_name_len, (res_ptr)->_file_name_text) +#endif + +#define RESOURCE_WATCH_CALLBACK_DEF(func_name, arg_info) void func_name(struct sys_watch_info *arg_info) +typedef RESOURCE_WATCH_CALLBACK_DEF(resource_watch_callback, info); + +#if RESOURCE_RELOADING + void resource_register_watch_callback(resource_watch_callback *callback); +#else + #define resource_register_watch_callback(callback) +#endif + #endif diff --git a/src/sys.h b/src/sys.h index 733f3180..958b9017 100644 --- a/src/sys.h +++ b/src/sys.h @@ -256,6 +256,32 @@ struct sys_file_filter sys_file_filter_begin(struct arena *arena, struct string b32 sys_file_filter_next(struct arena *arena, struct sys_file_filter *iter); void sys_file_filter_end(struct sys_file_filter *iter); +/* ========================== * + * Watch + * ========================== */ + +enum sys_watch_info_kind { + SYS_WATCH_INFO_KIND_UNKNOWN, + SYS_WATCH_INFO_KIND_ADDED, + SYS_WATCH_INFO_KIND_REMOVED, + SYS_WATCH_INFO_KIND_MODIFIED + +}; + +struct sys_watch { + u64 handle; +}; + +struct sys_watch_info { + enum sys_watch_info_kind kind; + struct string name; + struct sys_watch_info *next; +}; + +struct sys_watch sys_watch_alloc(struct string path); +void sys_watch_release(struct sys_watch *dw); +struct sys_watch_info *sys_watch_wait(struct arena *arena, struct sys_watch *dw); + /* ========================== * * Window * ========================== */ diff --git a/src/sys_win32.c b/src/sys_win32.c index f0c06881..039864a9 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -128,6 +128,11 @@ GLOBAL struct { struct win32_thread *threads_last; struct win32_thread *threads_first_free; + /* Watches */ + struct sys_mutex watches_mutex; + struct arena watches_arena; + struct win32_watch *watches_first_free; + /* Windows */ WNDCLASSEXW window_class; struct sys_mutex windows_mutex; @@ -663,6 +668,181 @@ void sys_file_filter_end(struct sys_file_filter *filter) } } +/* ========================== * + * Watch + * ========================== */ + +struct win32_watch { + HANDLE dir_handle; + struct win32_watch *next_free; + + u64 dir_path_len; + u8 dir_path_text[1024]; + u8 results_buff[KILOBYTE(64)]; +}; + +struct sys_watch sys_watch_alloc(struct string dir_path) +{ + struct temp_arena scratch = scratch_begin_no_conflict(); + + struct win32_watch *w32_watch = NULL; + { + struct sys_lock lock = sys_mutex_lock_e(&G.watches_mutex); + { + if (G.watches_first_free) { + w32_watch = G.watches_first_free; + G.watches_first_free = w32_watch->next_free; + } else { + w32_watch = arena_push(&G.watches_arena, struct win32_watch); + } + } + sys_mutex_unlock(&lock); + } + MEMZERO_STRUCT(w32_watch); + + if (dir_path.len > 0 && dir_path.len < (ARRAY_COUNT(w32_watch->dir_path_text) - 1)) { + u64 dir_path_len = dir_path.len; + MEMCPY(w32_watch->dir_path_text, dir_path.text, dir_path_len); + for (u64 i = 0; i < dir_path_len; ++i) { + if (w32_watch->dir_path_text[i] == '\\') { + w32_watch->dir_path_text[i] = '/'; + } + } + if (w32_watch->dir_path_text[dir_path_len - 1] != '/') { + w32_watch->dir_path_text[dir_path_len] = '/'; + ++dir_path_len; + } + w32_watch->dir_path_len = dir_path_len; + } else { + sys_panic(string_format(scratch.arena, LIT("Directory path too \"%F\" has invalid length"), FMT_STR(dir_path))); + } + + wchar_t *dir_path_wstr = wstr_from_string(scratch.arena, dir_path); + w32_watch->dir_handle = CreateFileW( + dir_path_wstr, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL + ); + + scratch_end(scratch); + + struct sys_watch watch = ZI; + watch.handle = (u64)w32_watch; + return watch; +} + +void sys_watch_release(struct sys_watch *dw) +{ + struct win32_watch *w32_watch = (struct win32_watch *)dw->handle; + CloseHandle(w32_watch->dir_handle); + + struct sys_lock lock = sys_mutex_lock_e(&G.watches_mutex); + { + w32_watch->next_free = G.watches_first_free; + G.watches_first_free = w32_watch; + } + sys_mutex_unlock(&lock); +} + +struct sys_watch_info *sys_watch_wait(struct arena *arena, struct sys_watch *dw) +{ + __prof; + struct win32_watch *w32_watch = (struct win32_watch *)dw->handle; + struct sys_watch_info *first_info = NULL; + struct sys_watch_info *last_info = NULL; + + DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY; + + b32 done = false; + while (!done) { + DWORD num_bytes_returned = 0; + BOOL success = ReadDirectoryChangesW(w32_watch->dir_handle, + w32_watch->results_buff, + ARRAY_COUNT(w32_watch->results_buff), + true, + filter, + &num_bytes_returned, + NULL, + NULL); + + if (success && num_bytes_returned > 0) { + i64 offset = 0; + while (!done) { + FILE_NOTIFY_INFORMATION *res = (FILE_NOTIFY_INFORMATION *)(w32_watch->results_buff + offset); + + struct sys_watch_info *info = arena_push_zero(arena, struct sys_watch_info); + if (last_info) { + last_info->next = info; + } else { + first_info = info; + } + + struct string16 name16 = ZI; + name16.text = res->FileName; + name16.len = res->FileNameLength / sizeof(wchar_t); + + info->name = string_copy(arena, STRING(w32_watch->dir_path_len, w32_watch->dir_path_text)); + info->name.len += string_from_string16(arena, name16).len; + for (u64 i = 0; i < info->name.len; ++i) { + if (info->name.text[i] == '\\') { + info->name.text[i] = '/'; + } + } + + switch (res->Action) { + case FILE_ACTION_ADDED: + { + info->kind = SYS_WATCH_INFO_KIND_ADDED; + } break; + + case FILE_ACTION_REMOVED: + { + info->kind = SYS_WATCH_INFO_KIND_REMOVED; + } break; + + case FILE_ACTION_MODIFIED: + { + info->kind = SYS_WATCH_INFO_KIND_MODIFIED; + } break; + + case FILE_ACTION_RENAMED_OLD_NAME: + { + info->kind = SYS_WATCH_INFO_KIND_REMOVED; + } break; + + case FILE_ACTION_RENAMED_NEW_NAME: + { + info->kind = SYS_WATCH_INFO_KIND_ADDED; + } break; + + default: + { + info->kind = SYS_WATCH_INFO_KIND_UNKNOWN; + } break; + } + + if (res->NextEntryOffset == 0) { + done = true; + } else { + offset += res->NextEntryOffset; + } + } + } + } + + return first_info; +} + /* ========================== * * Window * ========================== */ @@ -2100,6 +2280,10 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, G.threads_mutex = sys_mutex_alloc(); G.threads_arena = arena_alloc(GIGABYTE(64)); + /* Set up watches */ + G.watches_mutex = sys_mutex_alloc(); + G.watches_arena = arena_alloc(GIGABYTE(64)); + /* Set up windows */ G.windows_mutex = sys_mutex_alloc(); G.windows_arena = arena_alloc(GIGABYTE(64)); diff --git a/src/tar.c b/src/tar.c index 153efcaa..d10ad78b 100644 --- a/src/tar.c +++ b/src/tar.c @@ -61,9 +61,8 @@ INTERNAL u64 str_oct_to_u64(struct string str) /* `prefix` will be prepended to all file names in the archive * - * NOTE: The resulting archive merely points into the supplied tar data. No - * copying is done. Accessing the archive assumes that the data it's derived - * from is valid (AKA open if from a file / memory map). + * NOTE: The resulting archive merely points into the supplied tar data, no + * copying is done. Accessing the archive assumes that the data string is still valid. */ struct tar_archive tar_parse(struct arena *arena, struct string data, struct string prefix) {