main_linux.c (11166B)
1 /* See LICENSE for license details. */ 2 #ifndef BEAMFORMER_DEBUG 3 #define BEAMFORMER_IMPORT static 4 #define BEAMFORMER_EXPORT static 5 #define BASE_EXPORT static 6 #endif 7 8 #define BASE_IMPORT static 9 10 #include "base_platform.h" 11 12 #if !OS_LINUX 13 #error This file is only meant to be compiled for Linux 14 #endif 15 16 #include "beamformer.c" 17 #include "base_linux.c" 18 19 #define OS_SHARED_MEMORY_SIZE GB(2) 20 21 #define OS_DEBUG_LIB_NAME "./beamformer.so" 22 #define OS_DEBUG_LIB_TEMP_NAME "./beamformer_temp.so" 23 24 #define OS_CUDA_LIB_NAME "./external/cuda_toolkit.so" 25 #define OS_CUDA_LIB_TEMP_NAME "./external/cuda_toolkit_temp.so" 26 27 #define OS_RENDERDOC_SONAME "librenderdoc.so" 28 29 #define OS_VULKAN_SONAME_LIST \ 30 X("libvulkan.so") \ 31 X("libvulkan.so.1") \ 32 33 #include <dlfcn.h> 34 35 global BeamformerInput beamformer_input_main; 36 37 BEAMFORMER_IMPORT void 38 os_console_log(u8 *data, i64 length) 39 { 40 os_write_file(STDERR_FILENO, data, length); 41 } 42 43 BEAMFORMER_IMPORT void 44 os_fatal(u8 *data, i64 length) 45 { 46 os_write_file(STDERR_FILENO, data, length); 47 os_exit(1); 48 unreachable(); 49 } 50 51 BEAMFORMER_IMPORT void 52 os_release_handle(OSHandle h) 53 { 54 if ValidHandle(h) 55 close(h.value[0]); 56 } 57 58 BEAMFORMER_IMPORT void * 59 os_lookup_symbol(OSLibrary library, const char *symbol) 60 { 61 void *result = 0; 62 if ValidHandle(library) result = dlsym((void *)library.value[0], symbol); 63 return result; 64 } 65 66 function void * 67 allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity) 68 { 69 u64 rounded_capacity = round_up_to(requested_capacity, ARCH_X64? KB(4) : os_linux_context.system_info.page_size); 70 void *result = 0; 71 i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 72 if (fd > 0 && ftruncate(fd, rounded_capacity) != -1) { 73 void *new = mmap(0, rounded_capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 74 if (new != MAP_FAILED) { 75 *capacity = rounded_capacity; 76 result = new; 77 } 78 } 79 if (fd > 0) close(fd); 80 return result; 81 } 82 83 function OSLinuxFileWatchDirectory * 84 os_lookup_file_watch_directory(u64 hash) 85 { 86 OSLinuxFileWatchDirectory *result = 0; 87 for (OSLinuxFileWatchDirectory *fwd = os_linux_context.file_watch_directories.first; fwd; fwd = fwd->next) { 88 if (fwd->hash == hash) { 89 result = fwd; 90 break; 91 } 92 } 93 return result; 94 } 95 96 function void 97 os_linux_add_file_watch(str8 path, void *user_context, OSLinuxFileWatchKind kind) 98 { 99 str8 directory = path; 100 directory.length = str8_scan_backwards(path, '/'); 101 assert(directory.length > 0); 102 103 u64 hash = u64_hash_from_str8(directory); 104 OSLinuxFileWatchDirectory *dir = os_lookup_file_watch_directory(hash); 105 if (!dir) { 106 assert(path.data[directory.length] == '/'); 107 OSLinuxEntity *fwd = os_entity_allocate(OSLinuxEntityKind_FileWatchDirectory); 108 dir = &fwd->as.file_watch_directory; 109 DLLInsert(0, os_linux_context.file_watch_directories.first, 110 os_linux_context.file_watch_directories.last, dir, next, prev); 111 112 dir->hash = hash; 113 dir->name = push_str8(os_linux_context.arena, directory); 114 u32 mask = IN_MOVED_TO|IN_CLOSE_WRITE; 115 dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask); 116 } 117 118 OSLinuxEntity *fwe = os_entity_allocate(OSLinuxEntityKind_FileWatch); 119 OSLinuxFileWatch *fw = &fwe->as.file_watch; 120 DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev); 121 fw->user_context = user_context; 122 fw->hash = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1)); 123 fw->kind = kind; 124 fw->parent = dir; 125 } 126 127 BEAMFORMER_IMPORT void 128 os_add_file_watch(const char *path, int64_t path_length, void *user_context) 129 { 130 str8 path_str = {.data = (u8 *)path, .length = path_length}; 131 os_linux_add_file_watch(path_str, user_context, OSLinuxFileWatchKind_User); 132 } 133 134 function void 135 os_window_resize_callback(void *window, i32 width, i32 height) 136 { 137 OSWindow event_window = {0}; 138 for (OSLinuxEntity *we = os_linux_context.windows.first; we; we = we->as.window.next) { 139 if (we->as.window.handle == window) { 140 event_window.value[0] = (u64)we; 141 break; 142 } 143 } 144 145 os_push_input_event(beamformer_input, (BeamformerInputEvent){ 146 .kind = BeamformerInputEventKind_WindowResize, 147 .window_resize = { 148 .width = (u32)width, .height = (u32)height, 149 .window = event_window, 150 }, 151 }); 152 153 raylib_window_resize(window, width, height); 154 } 155 156 BEAMFORMER_IMPORT OSWindow 157 os_window_create(u8 *title, i64 title_length, i32 width, i32 height) 158 { 159 OSLinuxEntity *we = os_entity_allocate(OSLinuxEntityKind_Window); 160 OSWindow result = {(u64)we}; 161 DLLInsert(0, os_linux_context.windows.first, os_linux_context.windows.last, we, as.window.next, as.window.prev); 162 163 SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); 164 165 str8 name = {.data = title, .length = title_length}; 166 Temp scratch; 167 DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) 168 DeferLoop(scratch = temp_begin(os_linux_context.arena), temp_end(scratch)) 169 { 170 str8 title_string = push_str8(scratch.arena, name); 171 InitWindow(width, height, (char *)title_string.data); 172 } 173 174 we->as.window.handle = GetPlatformWindowHandle(); 175 os_window_equip_common(&beamformer_input_main, we->as.window.handle); 176 raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback); 177 178 /* NOTE: do this after initing so that the window starts out floating in tiling wm */ 179 SetWindowState(FLAG_WINDOW_RESIZABLE); 180 SetWindowMinSize(320, 240); 181 182 return result; 183 } 184 185 BEAMFORMER_IMPORT u8 * 186 os_get_clipboard_text(i64 *length) 187 { 188 str8 result = str8_from_c_str((char *)GetClipboardText()); 189 if (length) *length = result.length; 190 return result.data; 191 } 192 193 BEAMFORMER_IMPORT void 194 os_set_clipboard_text(u8 *data, i64 length) 195 { 196 Temp scratch; 197 DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) 198 DeferLoop(scratch = temp_begin(os_linux_context.arena), temp_end(scratch)) 199 { 200 str8 string = push_str8(scratch.arena, (str8){.data = data, .length = length}); 201 SetClipboardText((char *)string.data); 202 } 203 } 204 205 function OSLibrary 206 load_library(char *name, char *temp_name, u32 flags) 207 { 208 if (temp_name && os_copy_file(name, temp_name)) 209 name = temp_name; 210 211 OSLibrary result = {(u64)dlopen(name, flags)}; 212 if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue; 213 214 if (temp_name) unlink(temp_name); 215 216 return result; 217 } 218 219 #if BEAMFORMER_DEBUG 220 function void 221 debug_library_reload(void *beamformer) 222 { 223 local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue}; 224 225 if ValidHandle(beamformer_library_handle) { 226 beamformer_debug_hot_release(beamformer, &beamformer_input_main); 227 dlclose((void *)beamformer_library_handle.value[0]); 228 beamformer_library_handle = (OSLibrary){OSInvalidHandleValue}; 229 } 230 231 OSLibrary new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL); 232 if (InvalidHandle(beamformer_library_handle) && InvalidHandle(new_handle)) 233 fatal(str8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n")); 234 235 if ValidHandle(new_handle) { 236 beamformer_debug_hot_reload(new_handle); 237 beamformer_library_handle = new_handle; 238 } 239 } 240 #else 241 #define debug_library_reload(a) (void)(a) 242 #endif /* BEAMFORMER_DEBUG */ 243 244 function void 245 load_platform_libraries(void) 246 { 247 #if BEAMFORMER_DEBUG 248 debug_library_reload(0); 249 os_linux_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, 250 OSLinuxFileWatchKind_Platform); 251 #endif 252 253 BeamformerInput *input = &beamformer_input_main; 254 input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue}; 255 #define X(name) \ 256 if InvalidHandle(input->vulkan_library_handle) \ 257 input->vulkan_library_handle = load_library(name, 0, RTLD_NOW|RTLD_LOCAL); 258 OS_VULKAN_SONAME_LIST 259 #undef X 260 261 if InvalidHandle(input->vulkan_library_handle) 262 fatal(str8("[os] fatal error: failed to find valid vulkan library\n")); 263 264 input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL); 265 266 #if BEAMFORMER_RENDERDOC_HOOKS 267 local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue}; 268 renderdoc_handle = load_library(OS_RENDERDOC_SONAME, 0, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD); 269 load_renderdoc_functions(input, renderdoc_handle); 270 #endif 271 } 272 273 function void 274 dispatch_file_watch_events(void *beamformer) 275 { 276 local_persist alignas(16) u8 mem[KB(4)]; 277 struct inotify_event *event; 278 279 u64 current_time = os_timer_count(); 280 281 i64 rlen; 282 while ((rlen = read(os_linux_context.inotify_handle, mem, countof(mem))) > 0) { 283 for (u8 *data = mem; data < mem + rlen; data += sizeof(*event) + event->len) { 284 event = (struct inotify_event *)data; 285 for (OSLinuxFileWatchDirectory *dir = os_linux_context.file_watch_directories.first; dir; dir = dir->next) { 286 if (event->wd != dir->handle) 287 continue; 288 289 str8 file = str8_from_c_str(event->name); 290 u64 hash = u64_hash_from_str8(file); 291 for (OSLinuxFileWatch *fw = dir->first_child; fw; fw = fw->next) if (fw->hash == hash) { 292 // NOTE(rnp): avoid multiple updates in a single frame 293 if (fw->update_time < current_time) { 294 BeamformerInputEvent input_event = {0}; 295 if (fw->kind == OSLinuxFileWatchKind_Platform) { 296 assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); 297 if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) 298 debug_library_reload(beamformer); 299 input_event.kind = (u64)fw->user_context; 300 } else { 301 input_event.kind = BeamformerInputEventKind_FileEvent; 302 input_event.file_watch_user_context = fw->user_context; 303 } 304 os_push_input_event(&beamformer_input_main, input_event); 305 } 306 fw->update_time = current_time; 307 break; 308 } 309 } 310 } 311 } 312 } 313 314 BASE_IMPORT void 315 entry_point(i32 argc, char *argv[]) 316 { 317 os_linux_context.inotify_handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); 318 319 BeamformerInput *input = &beamformer_input_main; 320 input->shared_memory = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE, 321 &input->shared_memory_size); 322 if (input->shared_memory) { 323 input->shared_memory_name = str8(OS_SHARED_MEMORY_NAME).data; 324 input->shared_memory_name_length = str8(OS_SHARED_MEMORY_NAME).length; 325 } 326 327 os_push_input_event(input, (BeamformerInputEvent){ 328 .kind = BeamformerInputEventKind_ExecutableReload, 329 }); 330 331 load_platform_libraries(); 332 333 void *beamformer = beamformer_init(input); 334 335 struct pollfd fds[1] = {{0}}; 336 fds[0].fd = os_linux_context.inotify_handle; 337 fds[0].events = POLLIN; 338 339 while (!WindowShouldClose() && !beamformer_should_close(beamformer, input)) { 340 os_build_frame_input(input); 341 342 poll(fds, countof(fds), 0); 343 if (fds[0].revents & POLLIN) 344 dispatch_file_watch_events(beamformer); 345 346 beamformer_frame_step(beamformer, input); 347 348 // NOTE(rnp): this must happen at the end of frame to allow the pre loop events through 349 // TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause 350 // glfw to call the input callbacks in during EndDrawing() 351 //input->event_count = 0; 352 } 353 354 beamformer_terminate(beamformer, input); 355 356 /* NOTE: make sure this will get cleaned up after external 357 * programs release their references */ 358 shm_unlink(OS_SHARED_MEMORY_NAME); 359 }