os_win32.c (9673B)
1 /* See LICENSE for license details. */ 2 #include "util.h" 3 4 #define STD_OUTPUT_HANDLE -11 5 #define STD_ERROR_HANDLE -12 6 7 #define PAGE_READWRITE 0x04 8 #define MEM_COMMIT 0x1000 9 #define MEM_RESERVE 0x2000 10 #define MEM_RELEASE 0x8000 11 12 #define GENERIC_WRITE 0x40000000 13 #define GENERIC_READ 0x80000000 14 15 #define PIPE_WAIT 0x00 16 #define PIPE_NOWAIT 0x01 17 18 #define PIPE_TYPE_BYTE 0x00 19 #define PIPE_ACCESS_INBOUND 0x01 20 21 #define FILE_SHARE_READ 0x00000001 22 #define FILE_MAP_ALL_ACCESS 0x000F001F 23 #define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 24 #define FILE_FLAG_OVERLAPPED 0x40000000 25 26 #define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 27 28 #define FILE_ACTION_MODIFIED 0x00000003 29 30 #define CREATE_ALWAYS 2 31 #define OPEN_EXISTING 3 32 33 #define ERROR_NO_DATA 232L 34 #define ERROR_PIPE_NOT_CONNECTED 233L 35 #define ERROR_PIPE_LISTENING 536L 36 37 #define THREAD_SET_LIMITED_INFORMATION 0x0400 38 39 typedef struct { 40 u16 wProcessorArchitecture; 41 u16 _pad1; 42 u32 dwPageSize; 43 iz lpMinimumApplicationAddress; 44 iz lpMaximumApplicationAddress; 45 u64 dwActiveProcessorMask; 46 u32 dwNumberOfProcessors; 47 u32 dwProcessorType; 48 u32 dwAllocationGranularity; 49 u16 wProcessorLevel; 50 u16 wProcessorRevision; 51 } w32_sys_info; 52 53 /* NOTE: this is packed because the w32 api designers are dumb and ordered the members 54 * incorrectly. They worked around it be making the ft* members a struct {u32, u32} which 55 * is aligned on a 4-byte boundary. Then in their documentation they explicitly tell you not 56 * to cast to u64 because "it can cause alignment faults on 64-bit Windows" - go figure */ 57 typedef struct __attribute__((packed)) { 58 u32 dwFileAttributes; 59 u64 ftCreationTime; 60 u64 ftLastAccessTime; 61 u64 ftLastWriteTime; 62 u32 dwVolumeSerialNumber; 63 u32 nFileSizeHigh; 64 u32 nFileSizeLow; 65 u32 nNumberOfLinks; 66 u32 nFileIndexHigh; 67 u32 nFileIndexLow; 68 } w32_file_info; 69 70 typedef struct { 71 u32 next_entry_offset; 72 u32 action; 73 u32 filename_size; 74 u16 filename[]; 75 } w32_file_notify_info; 76 77 typedef struct { 78 uptr internal, internal_high; 79 union { 80 struct {u32 off, off_high;}; 81 iptr pointer; 82 }; 83 iptr event_handle; 84 } w32_overlapped; 85 86 #define W32(r) __declspec(dllimport) r __stdcall 87 W32(b32) CloseHandle(iptr); 88 W32(b32) CopyFileA(c8 *, c8 *, b32); 89 W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); 90 W32(iptr) CreateFileMappingA(iptr, void *, u32, u32, u32, c8 *); 91 W32(iptr) CreateIoCompletionPort(iptr, iptr, uptr, u32); 92 W32(iptr) CreateNamedPipeA(c8 *, u32, u32, u32, u32, u32, u32, void *); 93 W32(iptr) CreateSemaphoreA(iptr, i64, i64, c8 *); 94 W32(iptr) CreateThread(iptr, uz, iptr, iptr, u32, u32 *); 95 W32(b32) DeleteFileA(c8 *); 96 W32(b32) DisconnectNamedPipe(iptr); 97 W32(void) ExitProcess(i32); 98 W32(b32) FreeLibrary(void *); 99 W32(i32) GetFileAttributesA(c8 *); 100 W32(b32) GetFileInformationByHandle(iptr, w32_file_info *); 101 W32(i32) GetLastError(void); 102 W32(void *) GetModuleHandleA(c8 *); 103 W32(void *) GetProcAddress(void *, c8 *); 104 W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); 105 W32(iptr) GetStdHandle(i32); 106 W32(void) GetSystemInfo(void *); 107 W32(void *) LoadLibraryA(c8 *); 108 W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); 109 W32(b32) ReadDirectoryChangesW(iptr, u8 *, u32, b32, u32, u32 *, void *, void *); 110 W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); 111 W32(b32) ReleaseSemaphore(iptr, i64, i64 *); 112 W32(i32) SetThreadDescription(iptr, u16 *); 113 W32(u32) WaitForSingleObjectEx(iptr, u32, b32); 114 W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); 115 W32(void *) VirtualAlloc(u8 *, iz, u32, u32); 116 W32(b32) VirtualFree(u8 *, iz, u32); 117 118 #ifdef _DEBUG 119 static void * 120 os_get_module(char *name, Stream *e) 121 { 122 void *result = GetModuleHandleA(name); 123 if (!result && e) { 124 s8 errs[] = {s8("os_get_module(\""), c_str_to_s8(name), s8("\"): ")}; 125 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 126 stream_append_i64(e, GetLastError()); 127 stream_append_byte(e, '\n'); 128 } 129 return result; 130 } 131 #endif 132 133 static OS_WRITE_FILE_FN(os_write_file) 134 { 135 i32 wlen = 0; 136 if (raw.len) WriteFile(file, raw.data, raw.len, &wlen, 0); 137 return raw.len == wlen; 138 } 139 140 static void __attribute__((noreturn)) 141 os_fatal(s8 msg) 142 { 143 os_write_file(GetStdHandle(STD_ERROR_HANDLE), msg); 144 ExitProcess(1); 145 unreachable(); 146 } 147 148 static OS_ALLOC_ARENA_FN(os_alloc_arena) 149 { 150 Arena result; 151 w32_sys_info Info; 152 GetSystemInfo(&Info); 153 154 if (capacity % Info.dwPageSize != 0) 155 capacity += (Info.dwPageSize - capacity % Info.dwPageSize); 156 157 iz oldsize = old.end - old.beg; 158 if (oldsize > capacity) 159 return old; 160 161 if (old.beg) 162 VirtualFree(old.beg, oldsize, MEM_RELEASE); 163 164 result.beg = VirtualAlloc(0, capacity, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 165 if (result.beg == NULL) 166 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 167 result.end = result.beg + capacity; 168 return result; 169 } 170 171 static OS_CLOSE_FN(os_close) 172 { 173 CloseHandle(file); 174 } 175 176 static OS_OPEN_FOR_WRITE_FN(os_open_for_write) 177 { 178 iptr result = CreateFileA(fname, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); 179 return result; 180 } 181 182 static OS_READ_WHOLE_FILE_FN(os_read_whole_file) 183 { 184 s8 result = {0}; 185 186 w32_file_info fileinfo; 187 iptr h = CreateFileA(file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); 188 if (h >= 0 && GetFileInformationByHandle(h, &fileinfo)) { 189 iz filesize = (iz)fileinfo.nFileSizeHigh << 32; 190 filesize |= (iz)fileinfo.nFileSizeLow; 191 result = s8_alloc(arena, filesize); 192 193 ASSERT(filesize <= (iz)U32_MAX); 194 195 i32 rlen; 196 if (!ReadFile(h, result.data, result.len, &rlen, 0) || rlen != result.len) 197 result = (s8){0}; 198 } 199 if (h >= 0) CloseHandle(h); 200 201 return result; 202 } 203 204 static OS_READ_FILE_FN(os_read_file) 205 { 206 i32 total_read = 0; 207 ReadFile(file, buf, size, &total_read, 0); 208 return total_read; 209 } 210 211 static OS_WRITE_NEW_FILE_FN(os_write_new_file) 212 { 213 if (raw.len > (iz)U32_MAX) { 214 os_write_file(GetStdHandle(STD_ERROR_HANDLE), 215 s8("os_write_file: files >4GB are not yet handled on win32\n")); 216 return 0; 217 } 218 219 iptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); 220 if (h == INVALID_FILE) 221 return 0; 222 223 b32 ret = os_write_file(h, raw); 224 CloseHandle(h); 225 226 return ret; 227 } 228 229 static b32 230 os_file_exists(char *path) 231 { 232 b32 result = GetFileAttributesA(path) != -1; 233 return result; 234 } 235 236 static Pipe 237 os_open_named_pipe(char *name) 238 { 239 iptr h = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE|PIPE_NOWAIT, 1, 240 0, MB(1), 0, 0); 241 return (Pipe){.file = h, .name = name}; 242 } 243 244 static void * 245 os_open_shared_memory_area(char *name, iz cap) 246 { 247 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, 0, cap, name); 248 if (h == INVALID_FILE) 249 return NULL; 250 251 return MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, cap); 252 } 253 254 static void * 255 os_load_library(char *name, char *temp_name, Stream *e) 256 { 257 if (temp_name) { 258 if (CopyFileA(name, temp_name, 0)) 259 name = temp_name; 260 } 261 262 void *result = LoadLibraryA(name); 263 if (!result && e) { 264 s8 errs[] = {s8("os_load_library(\""), c_str_to_s8(name), s8("\"): ")}; 265 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 266 stream_append_i64(e, GetLastError()); 267 stream_append_byte(e, '\n'); 268 } 269 270 if (temp_name) 271 DeleteFileA(temp_name); 272 273 return result; 274 } 275 276 static void * 277 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 278 { 279 void *result = 0; 280 if (h) { 281 result = GetProcAddress(h, name); 282 if (!result && e) { 283 s8 errs[] = {s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), s8("\"): ")}; 284 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 285 stream_append_i64(e, GetLastError()); 286 stream_append_byte(e, '\n'); 287 } 288 } 289 return result; 290 } 291 292 static void 293 os_unload_library(void *h) 294 { 295 FreeLibrary(h); 296 } 297 298 static OS_ADD_FILE_WATCH_FN(os_add_file_watch) 299 { 300 s8 directory = path; 301 directory.len = s8_scan_backwards(path, '\\'); 302 ASSERT(directory.len > 0); 303 304 u64 hash = s8_hash(directory); 305 FileWatchContext *fwctx = &os->file_watch_context; 306 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 307 if (!dir) { 308 ASSERT(path.data[directory.len] == '\\'); 309 310 dir = fwctx->directory_watches + fwctx->directory_watch_count++; 311 dir->hash = hash; 312 dir->name = push_s8_zero(a, directory); 313 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 314 OPEN_EXISTING, 315 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 316 317 w32_context *ctx = (w32_context *)os->context; 318 w32_io_completion_event *event = push_struct(a, typeof(*event)); 319 event->tag = W32_IO_FILE_WATCH; 320 event->context = (iptr)dir; 321 CreateIoCompletionPort(dir->handle, ctx->io_completion_handle, (uptr)event, 0); 322 323 dir->buffer = sub_arena(a, 4096 + sizeof(w32_overlapped), 64); 324 w32_overlapped *overlapped = (w32_overlapped *)(dir->buffer.beg + 4096); 325 zero_struct(overlapped); 326 327 ReadDirectoryChangesW(dir->handle, dir->buffer.beg, 4096, 0, 328 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, overlapped, 0); 329 } 330 331 insert_file_watch(dir, s8_cut_head(path, dir->name.len + 1), user_data, callback); 332 } 333 334 static iptr 335 os_create_thread(Arena arena, iptr user_context, s8 name, os_thread_entry_point_fn *fn) 336 { 337 iptr result = CreateThread(0, 0, (iptr)fn, user_context, 0, 0); 338 SetThreadDescription(result, s8_to_s16(&arena, name).data); 339 return result; 340 } 341 342 static iptr 343 os_create_sync_object(Arena *arena) 344 { 345 iptr result = CreateSemaphoreA(0, 0, 1, 0); 346 return result; 347 } 348 349 static void 350 os_sleep_thread(iptr sync_handle) 351 { 352 WaitForSingleObjectEx(sync_handle, 0xFFFFFFFF, 0); 353 } 354 355 static OS_WAKE_THREAD_FN(os_wake_thread) 356 { 357 ReleaseSemaphore(sync_handle, 1, 0); 358 } 359 360 iptr glfwGetWGLContext(iptr); 361 362 static iptr 363 os_get_native_gl_context(iptr window) 364 { 365 return glfwGetWGLContext(window); 366 }