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