unicode support in win32 layer

This commit is contained in:
jacob 2024-04-03 19:34:52 -05:00
parent db16c4b067
commit 9a8b712f98
10 changed files with 312 additions and 262 deletions

View File

@ -514,15 +514,6 @@ INLINE i64 clamp_i64(i64 v, i64 min, i64 max) { return v < min ? min : v > max ?
INLINE f32 clamp_f32(f32 v, f32 min, f32 max) { return v < min ? min : v > max ? max : v; } INLINE f32 clamp_f32(f32 v, f32 min, f32 max) { return v < min ? min : v > max ? max : v; }
INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ? max : v; } INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ? max : v; }
INLINE u64 cstr_len(char *cstr)
{
u64 len = 0;
for (char *c = cstr; *c != 0; ++c) {
++len;
}
return len;
}
/* ========================== * /* ========================== *
* Profiling * Profiling
* ========================== */ * ========================== */

View File

@ -186,7 +186,7 @@ struct asset *font_load_asset(struct string path, f32 point_size, b32 help)
STR("Font path \"%F\" too long!"), STR("Font path \"%F\" too long!"),
FMT_STR(path))); FMT_STR(path)));
} }
string_to_cstr_buff(path, BUFFER_FROM_ARRAY(params->path_cstr)); cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path);
params->path_len = path.len; params->path_len = path.len;
params->asset = asset; params->asset = asset;
params->point_size = point_size; params->point_size = point_size;

View File

@ -8,6 +8,7 @@
struct resource { struct resource {
struct buffer bytes; struct buffer bytes;
/* Internal */
#if !RESOURCES_EMBEDDED #if !RESOURCES_EMBEDDED
struct sys_file file; struct sys_file file;
struct sys_file_map file_map; struct sys_file_map file_map;

View File

@ -236,7 +236,7 @@ struct asset *sheet_load_asset(struct string path, b32 help)
STR("Sheet path \"%F\" too long!"), STR("Sheet path \"%F\" too long!"),
FMT_STR(path))); FMT_STR(path)));
} }
string_to_cstr_buff(path, BUFFER_FROM_ARRAY(params->path_cstr)); cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path);
params->path_len = path.len; params->path_len = path.len;
params->asset = asset; params->asset = asset;

View File

@ -191,7 +191,7 @@ struct asset *sound_load_asset(struct string path, u32 flags, b32 help)
STR("Sound path \"%F\" too long!"), STR("Sound path \"%F\" too long!"),
FMT_STR(path))); FMT_STR(path)));
} }
string_to_cstr_buff(path, BUFFER_FROM_ARRAY(params->path_cstr)); cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path);
params->path_len = path.len; params->path_len = path.len;
params->asset = asset; params->asset = asset;
params->flags = flags; params->flags = flags;

View File

@ -160,54 +160,17 @@ struct string string_copy(struct arena *arena, struct string src)
return str; return str;
} }
struct string string_copy_buff(struct buffer buff, struct string src) struct string string_copy_buff(struct buffer dest_buff, struct string src)
{ {
u64 len = min_u64(buff.size, src.len); u64 len = min_u64(dest_buff.size, src.len);
struct string str = { struct string str = {
.len = len, .len = len,
.text = buff.data .text = dest_buff.data
}; };
MEMCPY(str.text, src.text, src.len); MEMCPY(str.text, src.text, src.len);
return str; return str;
} }
/* TODO: Benchmark performance of appending each character while calculating size here */
// //struct string string_copy_cstr(struct arena *arena, char *cstr)
// //{
// // u8 *final_text = arena_next(arena);
// // char *c = cstr;
// // for (; *c != 0; ++c) {
// // u8 *new_char = arena_push(arena, 1);
// // *new_char = *c;
// // }
// // return (struct string) {
// // .len = c - cstr,
// // .text = final_text
// // };
// //}
struct string string_from_cstr(char *cstr)
{
struct string str = { 0 };
if (cstr) {
char *c = cstr;
while (*c != 0) {
++c;
}
str.len = c - cstr;
str.text = (u8 *)cstr;
}
return str;
}
struct string string_from_cstr_len(char *cstr, u64 len)
{
return (struct string) {
.text = (u8 *)cstr,
.len = len
};
}
struct string string_repeat(struct arena *arena, struct string src, u64 count) struct string string_repeat(struct arena *arena, struct string src, u64 count)
{ {
u64 final_len = src.len * count; u64 final_len = src.len * count;
@ -365,54 +328,6 @@ b32 string_ends_with(struct string str, struct string substring)
return false; return false;
} }
#if 0
/* NOTE: This is a LOSSY conversion.
* `wstr` must be null-terminated.
*/
struct string string_from_wstr(struct arena *arena, u16 *wstr)
{
u8 *final_text = arena_next(arena);
u16 *wchar = wstr;
for (; *wchar != 0; ++wchar) {
u8 *c = arena_push(arena, 1);
*c = (u8)(*wchar & 0xFF);
}
return (struct string) {
.len = wchar - wstr,
.text = final_text
};
}
#endif
char *string_to_cstr(struct arena *arena, struct string str)
{
u8 *text = arena_push_array(arena, u8, str.len + 1);
MEMCPY(text, str.text, str.len);
text[str.len] = '\0';
return (char *)text;
}
char *string_to_cstr_buff(struct string str, struct buffer buff)
{
if (buff.size > 0) {
u64 len = min_u64(str.len, buff.size - 1);
MEMCPY(buff.data, str.text, len);
buff.data[len] = '\0';
}
return (char *)buff.data;
}
wchar_t *string_to_wstr(struct arena *arena, struct string str)
{
/* FIXME: Do proper encoding. */
u16 *text = arena_push_array(arena, u16, str.len + 1);
for (u64 i = 0; i < str.len; ++i) {
text[i] = (u16)str.text[i];
}
text[str.len] = '\0';
return (wchar_t *)text;
}
/* ========================== * /* ========================== *
* Format * Format
* ========================== */ * ========================== */
@ -549,7 +464,7 @@ struct string _string_format(struct arena *arena, struct string fmt, ...)
} }
/* ========================== * /* ========================== *
* Unicode transformation * Unicode
* ========================== */ * ========================== */
/* utf8 <- utf16 */ /* utf8 <- utf16 */
@ -576,6 +491,30 @@ struct string string_from_string16(struct arena *arena, struct string16 str16)
return res; return res;
} }
/* utf8 <- utf32 */
struct string string_from_string32(struct arena *arena, struct string32 str32)
{
struct string res = {
.len = 0,
.text = arena_dry_push(arena, u8)
};
u64 pos32 = 0;
while (pos32 < str32.len) {
struct string32 str32_remaining = { .len = (str32.len - pos32), .text = str32.text + pos32 };
struct utf32_decode_result decoded = utf32_decode(str32_remaining);
struct utf8_encode_result encoded = utf8_encode(decoded.codepoint);
u8 *dest = arena_push_array(arena, u8, encoded.count8);
MEMCPY(dest, &encoded.chars8, encoded.count8);
pos32 += 1;
res.len += encoded.count8;
}
return res;
}
/* utf16 <- utf8 */ /* utf16 <- utf8 */
struct string16 string16_from_string(struct arena *arena, struct string str8) struct string16 string16_from_string(struct arena *arena, struct string str8)
{ {
@ -600,30 +539,6 @@ struct string16 string16_from_string(struct arena *arena, struct string str8)
return res; return res;
} }
/* utf8 <- utf32 */
struct string string_from_string32(struct arena *arena, struct string32 str32)
{
struct string res = {
.len = 0,
.text = arena_dry_push(arena, u8)
};
u64 pos32 = 0;
while (pos32 < str32.len) {
struct string32 str32_remaining = { .len = (str32.len - pos32), .text = str32.text + pos32 };
struct utf32_decode_result decoded = utf32_decode(str32_remaining);
struct utf8_encode_result encoded = utf8_encode(decoded.codepoint);
u8 *dest = arena_push_array(arena, u8, encoded.count8);
MEMCPY(dest, &encoded.chars8, encoded.count8);
pos32 += 1;
res.len += encoded.count8;
}
return res;
}
/* utf32 <- utf8 */ /* utf32 <- utf8 */
struct string32 string32_from_string(struct arena *arena, struct string str8) struct string32 string32_from_string(struct arena *arena, struct string str8)
{ {
@ -647,3 +562,93 @@ struct string32 string32_from_string(struct arena *arena, struct string str8)
return res; return res;
} }
/* ========================== *
* Legacy strings
* ========================== */
/* C narrow strings */
u64 cstr_len(char *cstr)
{
char *end = cstr;
while (*end) {
++end;
}
return end - cstr;
}
char *cstr_from_string(struct arena *arena, struct string src)
{
u8 *text = arena_push_array(arena, u8, src.len + 1);
MEMCPY(text, src.text, src.len);
text[src.len] = 0;
return (char *)text;
}
char *cstr_buff_from_string(struct buffer dest_buff, struct string src)
{
if (dest_buff.size > 0) {
u64 len = min_u64(src.len, dest_buff.size - 1);
MEMCPY(dest_buff.data, src.text, len);
dest_buff.data[len] = 0;
}
return (char *)dest_buff.data;
}
struct string string_from_cstr(char *cstr)
{
u64 len = cstr_len(cstr);
return (struct string) {
.len = len,
.text = (u8 *)cstr
};
}
struct string string_from_cstr_len(char *cstr, u64 len)
{
return (struct string) {
.text = (u8 *)cstr,
.len = len
};
}
/* C wide strings */
u64 wstr_len(wchar_t *wstr)
{
wchar_t *end = wstr;
while (*end) {
++end;
}
return end - wstr;
}
wchar_t *wstr_from_string(struct arena *arena, struct string src)
{
struct string16 str16 = string16_from_string(arena, src);
*arena_push(arena, u16) = 0;
return (wchar_t *)str16.text;
}
wchar_t *wstr_from_string16(struct arena *arena, struct string16 src)
{
u16 *text = arena_push_array(arena, u16, src.len + 1);
text[src.len] = 0;
return (wchar_t *)text;
}
struct string string_from_wstr(struct arena *arena, wchar_t *wstr)
{
struct string16 str16 = string16_from_wstr(wstr);
return string_from_string16(arena, str16);
}
struct string16 string16_from_wstr(wchar_t *wstr)
{
u64 len = wstr_len(wstr);
return (struct string16) {
.len = len,
.text = (u16 *)wstr
};
}

View File

@ -21,9 +21,7 @@ struct string string_from_float(struct arena *arena, f64 f, u32 precision);
* ========================== */ * ========================== */
struct string string_copy(struct arena *arena, struct string src); struct string string_copy(struct arena *arena, struct string src);
struct string string_copy_buff(struct buffer buff, struct string src); struct string string_copy_buff(struct buffer dest_buff, struct string src);
struct string string_from_cstr(char *cstr);
struct string string_from_cstr_len(char *cstr, u64 len);
struct string string_repeat(struct arena *arena, struct string src, u64 count); struct string string_repeat(struct arena *arena, struct string src, u64 count);
struct string string_cat(struct arena *arena, struct string str1, struct string str2); struct string string_cat(struct arena *arena, struct string str1, struct string str2);
struct string_array string_split(struct arena *arena, struct string str, struct string delim); struct string_array string_split(struct arena *arena, struct string str, struct string delim);
@ -32,9 +30,6 @@ b32 string_eq(struct string str1, struct string str2);
b32 string_contains(struct string str, struct string substring); b32 string_contains(struct string str, struct string substring);
b32 string_starts_with(struct string str, struct string substring); b32 string_starts_with(struct string str, struct string substring);
b32 string_ends_with(struct string str, struct string substring); b32 string_ends_with(struct string str, struct string substring);
char *string_to_cstr(struct arena *arena, struct string str);
char *string_to_cstr_buff(struct string str, struct buffer buff);
wchar_t *string_to_wstr(struct arena *arena, struct string str);
/* ========================== * /* ========================== *
* Format * Format
@ -86,12 +81,28 @@ struct string _string_format(struct arena *arena, struct string fmt, ...);
struct string string_formatv(struct arena *arena, struct string fmt, va_list args); struct string string_formatv(struct arena *arena, struct string fmt, va_list args);
/* ========================== * /* ========================== *
* Unicode transformation * Unicode
* ========================== */ * ========================== */
struct string string_from_string16(struct arena *arena, struct string16 str16); struct string string_from_string16(struct arena *arena, struct string16 str16);
struct string16 string16_from_string(struct arena *arena, struct string str8);
struct string string_from_string32(struct arena *arena, struct string32 str32); struct string string_from_string32(struct arena *arena, struct string32 str32);
struct string16 string16_from_string(struct arena *arena, struct string str8);
struct string32 string32_from_string(struct arena *arena, struct string str8); struct string32 string32_from_string(struct arena *arena, struct string str8);
/* ========================== *
* Legacy strings
* ========================== */
u64 cstr_len(char *cstr);
char *cstr_from_string(struct arena *arena, struct string src);
char *cstr_buff_from_string(struct buffer dest_buff, struct string src);
struct string string_from_cstr(char *cstr);
struct string string_from_cstr_len(char *cstr, u64 len);
u64 wstr_len(wchar_t *wstr);
wchar_t *wstr_from_string(struct arena *arena, struct string src);
wchar_t *wstr_from_string16(struct arena *arena, struct string16 src);
struct string string_from_wstr(struct arena *arena, wchar_t *wstr);
struct string16 string16_from_wstr(wchar_t *wstr);
#endif #endif

View File

@ -195,8 +195,8 @@ u64 sys_file_size(struct sys_file file);
* ========================== */ * ========================== */
struct sys_file_map { struct sys_file_map {
u64 handle;
struct buffer mapped_memory; struct buffer mapped_memory;
u64 handle;
}; };
struct sys_file_map sys_file_map_open_read(struct sys_file file); struct sys_file_map sys_file_map_open_read(struct sys_file file);
@ -207,18 +207,20 @@ struct buffer sys_file_map_data(struct sys_file_map map);
* Dir iter * Dir iter
* ========================== */ * ========================== */
/* Opaque */ struct sys_file_filter_result {
struct sys_dir_iter; struct string filename;
struct sys_dir_iter_info {
struct string file_name;
b32 is_dir; b32 is_dir;
}; };
struct sys_file_filter {
struct sys_file_filter_result info;
u64 handle;
};
/* Iterate all files in a directory */ /* Iterate all files in a directory */
struct sys_dir_iter *sys_dir_iter_begin(struct arena *arena, struct string dir_name); struct sys_file_filter sys_file_filter_begin(struct arena *arena, struct string pattern);
struct sys_dir_iter_info *sys_dir_iter_next(struct arena *arena, struct sys_dir_iter *iter); b32 sys_file_filter_next(struct arena *arena, struct sys_file_filter *iter);
void sys_dir_iter_end(struct sys_dir_iter *iter); void sys_file_filter_end(struct sys_file_filter *iter);
/* ========================== * /* ========================== *
* Window * Window

View File

@ -10,6 +10,7 @@
#include "math.h" #include "math.h"
#include "util.h" #include "util.h"
#include "tls.h" #include "tls.h"
#include "utf.h"
#include <Windows.h> #include <Windows.h>
#include <windowsx.h> #include <windowsx.h>
@ -27,6 +28,7 @@
#pragma comment(lib, "dwmapi") #pragma comment(lib, "dwmapi")
#define SYS_WINDOW_EVENT_LISTENERS_MAX 512 #define SYS_WINDOW_EVENT_LISTENERS_MAX 512
#define WINDOW_CLASS_NAME L"power_play_window_class"
struct win32_thread_params { struct win32_thread_params {
sys_thread_func *thread_func; sys_thread_func *thread_func;
@ -107,7 +109,7 @@ GLOBAL struct {
struct win32_thread_params *first_free_thread_params; struct win32_thread_params *first_free_thread_params;
/* Windows */ /* Windows */
WNDCLASSEX window_class; WNDCLASSEXW window_class;
struct sys_mutex windows_mutex; struct sys_mutex windows_mutex;
struct arena windows_arena; struct arena windows_arena;
struct win32_window *first_free_window; struct win32_window *first_free_window;
@ -187,39 +189,39 @@ void sys_memory_decommit(void *address, u64 size)
VirtualFree(address, size, MEM_DECOMMIT); VirtualFree(address, size, MEM_DECOMMIT);
} }
/* ========================== *
* Wchar
*
* TODO: Move wide char ops to string.c and actually do proper decoding
* ========================== */
INTERNAL struct string wchar_path_to_string(struct arena *arena, wchar_t *src)
{
struct string str = { 0, arena_dry_push(arena, u8) };
while (1) {
wchar_t wchar = src[str.len];
if (wchar != 0) {
if (wchar == '\\') {
wchar = '/';
}
u8 *c = arena_push(arena, u8);
/* FIXME: We're ignoring the high byte here */
*c = (u8)(wchar & 0xFF);
++str.len;
} else {
break;
}
}
return str;
}
/* ========================== * /* ========================== *
* File system * File system
* ========================== */ * ========================== */
INTERNAL struct string string_from_win32_path(struct arena *arena, wchar_t *src)
{
struct string res = {
.len = 0,
.text = arena_dry_push(arena, u8)
};
while (*src) {
struct string16 decode_str = { .len = *(src + 1) ? 2 : 1, .text = src };
struct utf16_decode_result decoded = utf16_decode(decode_str);
struct utf8_encode_result encoded = utf8_encode(decoded.codepoint);
u8 *dest = arena_push_array(arena, u8, encoded.count8);
for (u32 i = 0; i < encoded.count8; ++i) {
u8 byte = encoded.chars8[i];
if (byte == '\\') {
byte = '/';
}
dest[i] = byte;
}
res.len += encoded.count8;
src += decoded.advance16;
}
return res;
}
struct string sys_get_write_path(struct arena *arena) struct string sys_get_write_path(struct arena *arena)
{ {
wchar_t *p = NULL; u16 *p = NULL;
/* TODO: cache this? */ /* TODO: cache this? */
HRESULT res = SHGetKnownFolderPath( HRESULT res = SHGetKnownFolderPath(
&FOLDERID_LocalAppData, &FOLDERID_LocalAppData,
@ -229,7 +231,7 @@ struct string sys_get_write_path(struct arena *arena)
); );
struct string path = { 0 }; struct string path = { 0 };
if (res == S_OK) { if (res == S_OK) {
path = wchar_path_to_string(arena, p); path = string_from_win32_path(arena, p);
} }
CoTaskMemFree(p); CoTaskMemFree(p);
return path; return path;
@ -239,8 +241,8 @@ b32 sys_is_file(struct string path)
{ {
__prof; __prof;
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
const char *path_cstr = string_to_cstr(scratch.arena, path); wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
DWORD attributes = GetFileAttributes(path_cstr); DWORD attributes = GetFileAttributesW(path_wstr);
scratch_end(scratch); scratch_end(scratch);
return attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY); return attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY);
} }
@ -248,8 +250,8 @@ b32 sys_is_file(struct string path)
b32 sys_is_dir(struct string path) b32 sys_is_dir(struct string path)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
const char *path_cstr = string_to_cstr(scratch.arena, path); wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
DWORD attributes = GetFileAttributes(path_cstr); DWORD attributes = GetFileAttributesW(path_wstr);
scratch_end(scratch); scratch_end(scratch);
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY); return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY);
} }
@ -258,10 +260,35 @@ void sys_mkdir(struct string path)
{ {
__prof; __prof;
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
const char *path_cstr = string_to_cstr(scratch.arena, path); wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
b32 success = SHCreateDirectoryExA(NULL, path_cstr, NULL) == ERROR_SUCCESS; int err_code = SHCreateDirectory(NULL, path_wstr);
(UNUSED)success; struct string err = { 0 };
ASSERT(success); switch (err_code) {
case ERROR_BAD_PATHNAME: {
err = STR("Bad path name");
} break;
case ERROR_FILENAME_EXCED_RANGE: {
err = STR("Path name too long");
} break;
case ERROR_FILE_EXISTS: {
err = STR("A file already exists at this location");
} break;
case ERROR_CANCELLED: {
err = STR("User canceled the operation");
} break;
default: break;
}
if (err.len > 0) {
struct string msg = string_format(scratch.arena,
STR("Failed to create directory \"%F\": %F"),
FMT_STR(path),
FMT_STR(err));
sys_panic(msg);
}
scratch_end(scratch); scratch_end(scratch);
} }
@ -269,11 +296,11 @@ struct sys_file sys_file_open_read(struct string path)
{ {
__prof; __prof;
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
const char *path_cstr = string_to_cstr(scratch.arena, path); wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
/* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */ /* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */
HANDLE handle = CreateFileA( HANDLE handle = CreateFileW(
path_cstr, path_wstr,
GENERIC_READ, GENERIC_READ,
FILE_SHARE_READ, FILE_SHARE_READ,
NULL, NULL,
@ -290,9 +317,9 @@ struct sys_file sys_file_open_write(struct string path)
{ {
__prof; __prof;
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
const char *path_cstr = string_to_cstr(scratch.arena, path); wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
HANDLE handle = CreateFileA( HANDLE handle = CreateFileW(
path_cstr, path_wstr,
GENERIC_WRITE, GENERIC_WRITE,
0, 0,
NULL, NULL,
@ -310,9 +337,9 @@ struct sys_file sys_file_open_append(struct string path)
{ {
__prof; __prof;
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
const char *path_cstr = string_to_cstr(scratch.arena, path); wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
HANDLE handle = CreateFileA( HANDLE handle = CreateFileW(
path_cstr, path_wstr,
FILE_APPEND_DATA, FILE_APPEND_DATA,
FILE_SHARE_READ, FILE_SHARE_READ,
NULL, NULL,
@ -395,7 +422,7 @@ u64 sys_file_size(struct sys_file file)
struct sys_file_map sys_file_map_open_read(struct sys_file file) struct sys_file_map sys_file_map_open_read(struct sys_file file)
{ {
HANDLE map_handle = CreateFileMappingA( HANDLE map_handle = CreateFileMappingW(
(HANDLE)file.handle, (HANDLE)file.handle,
NULL, NULL,
PAGE_READONLY, PAGE_READONLY,
@ -451,60 +478,57 @@ struct buffer sys_file_map_data(struct sys_file_map map)
} }
/* ========================== * /* ========================== *
* Dir iter * File iter
* ========================== */ * ========================== */
struct sys_dir_iter { struct win32_file_filter {
HANDLE handle; HANDLE find_handle;
char *dir_path_cstr; wchar_t *filter_wstr;
}; };
struct sys_dir_iter *sys_dir_iter_begin(struct arena *arena, struct string dir_name) struct sys_file_filter sys_file_filter_begin(struct arena *arena, struct string pattern)
{ {
struct temp_arena scratch = scratch_begin(arena); struct sys_file_filter filter = { 0 };
struct sys_dir_iter *iter = arena_push_zero(arena, struct sys_dir_iter); struct win32_file_filter *filter_internal = arena_push_zero(arena, struct win32_file_filter);
if (dir_name.len >= 1 && dir_name.text[dir_name.len - 1] == '/') { filter_internal->filter_wstr = wstr_from_string(arena, pattern);
struct string dir_path_w_asterisk = string_cat(scratch.arena, dir_name, STR("*")); filter.handle = (u64)filter_internal;
iter->dir_path_cstr = string_to_cstr(arena, dir_path_w_asterisk); return filter;
} else {
/* Invalid directory path supplied */
ASSERT(false);
}
scratch_end(scratch);
return iter;
} }
struct sys_dir_iter_info *sys_dir_iter_next(struct arena *arena, struct sys_dir_iter *iter) b32 sys_file_filter_next(struct arena *arena, struct sys_file_filter *filter)
{ {
WIN32_FIND_DATA find_file_data = { 0 }; struct win32_file_filter *filter_internal = (struct win32_file_filter *)filter->handle;
WIN32_FIND_DATAW find_file_data = { 0 };
b32 found = false; b32 found = false;
if (iter->handle) { if (filter_internal->find_handle) {
found = FindNextFileA(iter->handle, &find_file_data); found = FindNextFileW(filter_internal->find_handle, &find_file_data);
} else if (iter->dir_path_cstr) { } else {
iter->handle = FindFirstFileExA(iter->dir_path_cstr, FindExInfoStandard, &find_file_data, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH); filter_internal->find_handle = FindFirstFileExW(filter_internal->filter_wstr, FindExInfoStandard, &find_file_data, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE | FIND_FIRST_EX_LARGE_FETCH);
found = iter->handle != INVALID_HANDLE_VALUE; found = filter_internal->find_handle != INVALID_HANDLE_VALUE;
} }
struct sys_dir_iter_info *info = NULL;
if (found) { if (found) {
struct string file_name = string_from_cstr(find_file_data.cFileName); struct string file_name = string_from_wstr(arena, find_file_data.cFileName);
if (string_eq(file_name, STR(".")) || string_eq(file_name, STR(".."))) { if (string_eq(file_name, STR(".")) || string_eq(file_name, STR(".."))) {
/* Skip initial '.' and '..' matches */ /* Skip initial '.' and '..' matches */
info = sys_dir_iter_next(arena, iter); found = sys_file_filter_next(arena, filter);
} else { } else {
info = arena_push_zero(arena, struct sys_dir_iter_info); filter->info.is_dir = find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
info->is_dir = find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; filter->info.filename = string_copy(arena, file_name);
info->file_name = string_copy(arena, file_name);
} }
} }
return info; return found;
} }
void sys_dir_iter_end(struct sys_dir_iter *iter) void sys_file_filter_end(struct sys_file_filter *filter)
{ {
FindClose(iter->handle); struct win32_file_filter *filter_internal = (struct win32_file_filter *)filter->handle;
if (filter_internal->find_handle) {
FindClose(filter_internal->find_handle);
}
} }
/* ========================== * /* ========================== *
@ -536,10 +560,10 @@ INTERNAL HWND win32_create_window(struct win32_window *window)
DWORD exstyle = WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP; DWORD exstyle = WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP;
/* TODO: Check for hwnd success */ /* TODO: Check for hwnd success */
HWND hwnd = CreateWindowExA( HWND hwnd = CreateWindowExW(
exstyle, exstyle,
L.window_class.lpszClassName, L.window_class.lpszClassName,
"", L"",
WS_OVERLAPPEDWINDOW, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
@ -556,7 +580,7 @@ INTERNAL HWND win32_create_window(struct win32_window *window)
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode)); DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode));
/* Set window as userdata */ /* Set window as userdata */
SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)window); SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window);
return hwnd; return hwnd;
} }
@ -779,7 +803,7 @@ INTERNAL void win32_update_window_from_settings(struct win32_window *window, str
if (fullscreen) { if (fullscreen) {
if (!old_fullscreen) { if (!old_fullscreen) {
/* Entering fullscreen */ /* Entering fullscreen */
SetWindowLongPtrA(hwnd, GWL_STYLE, WS_POPUP); SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP);
} }
rect = (RECT) { rect = (RECT) {
.left = 0, .left = 0,
@ -790,7 +814,7 @@ INTERNAL void win32_update_window_from_settings(struct win32_window *window, str
} else { } else {
if (old_fullscreen) { if (old_fullscreen) {
/* Leaving fullscreen */ /* Leaving fullscreen */
SetWindowLongPtrA(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); SetWindowLongPtrW(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
} }
rect = (RECT) { rect = (RECT) {
.left = settings->floating_x, .left = settings->floating_x,
@ -808,21 +832,26 @@ INTERNAL void win32_update_window_from_settings(struct win32_window *window, str
}; };
SetWindowPlacement(hwnd, &wp); SetWindowPlacement(hwnd, &wp);
SetWindowTextA(hwnd, settings->title); {
struct temp_arena scratch = scratch_begin_no_conflict();
wchar_t *title_wstr = wstr_from_string(scratch.arena, string_from_cstr(settings->title));
SetWindowTextW(hwnd, title_wstr);
scratch_end(scratch);
}
} }
INTERNAL void win32_window_wake(struct win32_window *window) INTERNAL void win32_window_wake(struct win32_window *window)
{ {
/* Post a blank message to the window's thread message queue to wake it. */ /* Post a blank message to the window's thread message queue to wake it. */
PostMessageA(window->hwnd, WM_NULL, 0, 0); PostMessageW(window->hwnd, WM_NULL, 0, 0);
} }
INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{ {
struct win32_window *window = (struct win32_window *)GetWindowLongPtrA(hwnd, GWLP_USERDATA); struct win32_window *window = (struct win32_window *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (!window) { if (!window) {
return DefWindowProcA(hwnd, msg, wparam, lparam); return DefWindowProcW(hwnd, msg, wparam, lparam);
} }
LRESULT result = 0; LRESULT result = 0;
@ -834,7 +863,7 @@ INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
} break; } break;
case WM_PAINT: { case WM_PAINT: {
result = DefWindowProcA(hwnd, msg, wparam, lparam); result = DefWindowProcW(hwnd, msg, wparam, lparam);
} break; } break;
case WM_MOVE: case WM_MOVE:
@ -842,13 +871,13 @@ INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
case WM_SIZE: case WM_SIZE:
case WM_SIZING: { case WM_SIZING: {
win32_update_window_from_system(window); win32_update_window_from_system(window);
result = DefWindowProcA(hwnd, msg, wparam, lparam); result = DefWindowProcW(hwnd, msg, wparam, lparam);
} break; } break;
/* Keyboard buttons */ /* Keyboard buttons */
case WM_SYSKEYUP: case WM_SYSKEYUP:
case WM_SYSKEYDOWN: { case WM_SYSKEYDOWN: {
result = DefWindowProcA(hwnd, msg, wparam, lparam); result = DefWindowProcW(hwnd, msg, wparam, lparam);
} FALLTHROUGH; } FALLTHROUGH;
case WM_KEYUP: case WM_KEYUP:
case WM_KEYDOWN: { case WM_KEYDOWN: {
@ -997,7 +1026,7 @@ INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
default: { default: {
result = DefWindowProcA(hwnd, msg, wparam, lparam); result = DefWindowProcW(hwnd, msg, wparam, lparam);
} break; } break;
} }
@ -1476,7 +1505,7 @@ INTERNAL DWORD WINAPI win32_thread_proc(LPVOID params)
struct string thread_name = string_from_cstr(thread_params.thread_name_cstr); struct string thread_name = string_from_cstr(thread_params.thread_name_cstr);
if (thread_name.len > 0) { if (thread_name.len > 0) {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
wchar_t *wc_thread_name = string_to_wstr(scratch.arena, thread_name); wchar_t *wc_thread_name = wstr_from_string(scratch.arena, thread_name);
SetThreadDescription(GetCurrentThread(), wc_thread_name); SetThreadDescription(GetCurrentThread(), wc_thread_name);
scratch_end(scratch); scratch_end(scratch);
} }
@ -1509,7 +1538,7 @@ struct sys_thread sys_thread_init(sys_thread_func *thread_func, void *thread_dat
tp->thread_data = thread_data; tp->thread_data = thread_data;
/* Copy thread name to params */ /* Copy thread name to params */
string_to_cstr_buff(thread_name, BUFFER_FROM_ARRAY(tp->thread_name_cstr)); cstr_buff_from_string(BUFFER_FROM_ARRAY(tp->thread_name_cstr), thread_name);
HANDLE handle = CreateThread( HANDLE handle = CreateThread(
NULL, NULL,
@ -1558,10 +1587,10 @@ void sys_thread_assert(u32 tid)
void sys_message_box(enum sys_message_box_kind kind, struct string message) void sys_message_box(enum sys_message_box_kind kind, struct string message)
{ {
char message_buff[4096] = { 0 }; struct temp_arena scratch = scratch_begin_no_conflict();
char *message_cstr = string_to_cstr_buff(message, BUFFER_FROM_ARRAY(message_buff)); wchar_t *message_wstr = wstr_from_string(scratch.arena, message);
const char *title = ""; const wchar_t *title = L"";
UINT mbox_type = 0; UINT mbox_type = 0;
switch (kind) { switch (kind) {
@ -1570,22 +1599,24 @@ void sys_message_box(enum sys_message_box_kind kind, struct string message)
} break; } break;
case SYS_MESSAGE_BOX_KIND_WARNING: { case SYS_MESSAGE_BOX_KIND_WARNING: {
title = "Warning"; title = L"Warning";
mbox_type = MB_ICONWARNING; mbox_type = MB_ICONWARNING;
} break; } break;
case SYS_MESSAGE_BOX_KIND_ERROR: { case SYS_MESSAGE_BOX_KIND_ERROR: {
title = "Error"; title = L"Error";
mbox_type = MB_ICONERROR; mbox_type = MB_ICONERROR;
} break; } break;
case SYS_MESSAGE_BOX_KIND_FATAL: { case SYS_MESSAGE_BOX_KIND_FATAL: {
title = "Fatal error"; title = L"Fatal error";
mbox_type = MB_ICONSTOP; mbox_type = MB_ICONSTOP;
} break; } break;
} }
MessageBoxExA(NULL, message_cstr, title, mbox_type, 0); MessageBoxExW(NULL, message_wstr, title, mbox_type, 0);
scratch_end(scratch);
} }
/* ========================== * /* ========================== *
@ -1650,21 +1681,30 @@ u32 sys_rand_u32(void)
return v; return v;
} }
void sys_panic_raw(char *msg_cstr) INTERNAL void panic_exit(void)
{ {
/* FIXME: Exit other threads before showing message box */
MessageBoxExA(NULL, msg_cstr, "Fatal error", MB_ICONSTOP, 0);
ASSERT(false); ASSERT(false);
sys_exit(); sys_exit();
} }
void sys_panic_raw(char *msg_cstr)
{
/* FIXME: Exit other threads before showing message box */
MessageBoxExA(NULL, msg_cstr, "Fatal error", MB_ICONSTOP, 0);
panic_exit();
}
void sys_panic(struct string msg) void sys_panic(struct string msg)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); /* FIXME: Exit other threads before showing message box */
logf_critical("Panicking: %F", FMT_STR(msg)); logf_critical("Panicking: %F", FMT_STR(msg));
struct string prepend = STR("A fatal error has occured and the application needs to exit:\n\n"); struct temp_arena scratch = scratch_begin_no_conflict();
msg = string_cat(scratch.arena, prepend, msg); msg = string_cat(scratch.arena,
sys_panic_raw(string_to_cstr(scratch.arena, msg)); STR("A fatal error has occured and the application needs to exit:\n\n"),
msg);
wchar_t *msg_wstr = wstr_from_string(scratch.arena, msg);
MessageBoxExW(NULL, msg_wstr, L"Fatal error", MB_ICONSTOP, 0);
panic_exit();
scratch_end(scratch); scratch_end(scratch);
} }
@ -1782,14 +1822,14 @@ INTERNAL SYS_THREAD_FUNC_DEF(app_thread_entry_point, arg)
app_entry_point(); app_entry_point();
} }
int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, _In_ LPSTR command_line, _In_ int show_code) int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, _In_ LPWSTR command_line, _In_ int show_code)
{ {
(UNUSED)instance; (UNUSED)instance;
(UNUSED)prev_instance; (UNUSED)prev_instance;
(UNUSED)command_line; (UNUSED)command_line;
(UNUSED)show_code; (UNUSED)show_code;
const char *error_msg = NULL; const wchar_t *error_msg = NULL;
SetThreadDescription(GetCurrentThread(), L"Main thread"); SetThreadDescription(GetCurrentThread(), L"Main thread");
@ -1816,7 +1856,7 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
L.thread_tls_index = TlsAlloc(); L.thread_tls_index = TlsAlloc();
if (L.thread_tls_index == TLS_OUT_OF_INDEXES) { if (L.thread_tls_index == TLS_OUT_OF_INDEXES) {
/* TODO: GetLastError */ /* TODO: GetLastError */
error_msg = "Platform initialization error: TLS_OUT_OF_INDEXES"; error_msg = L"Platform initialization error: TLS_OUT_OF_INDEXES";
goto error; goto error;
} }
@ -1840,9 +1880,9 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
/* Create window class */ /* Create window class */
{ {
/* Register the window class */ /* Register the window class */
WNDCLASSEX *wc = &L.window_class; WNDCLASSEXW *wc = &L.window_class;
wc->cbSize = sizeof(WNDCLASSEX); wc->cbSize = sizeof(WNDCLASSEX);
wc->lpszClassName = "power_play_window_class"; wc->lpszClassName = WINDOW_CLASS_NAME;
wc->hCursor = LoadCursor(NULL, IDC_ARROW); wc->hCursor = LoadCursor(NULL, IDC_ARROW);
wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
//wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
@ -1854,9 +1894,9 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
GetModuleFileName(instance, path, MAX_PATH); GetModuleFileName(instance, path, MAX_PATH);
ExtractIconEx(path, 0, &wc->hIcon, &wc->hIconSm, 1); ExtractIconEx(path, 0, &wc->hIcon, &wc->hIconSm, 1);
if (!RegisterClassExA(wc)) { if (!RegisterClassExW(wc)) {
/* TODO: GetLastError */ /* TODO: GetLastError */
error_msg = "Failed to register window class"; error_msg = L"Failed to register window class";
goto error; goto error;
} }
} }
@ -1871,14 +1911,14 @@ int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
/* TODO: GetLastError */ /* TODO: GetLastError */
error_msg = "Failed to register raw input device"; error_msg = L"Failed to register raw input device";
goto error; goto error;
} }
} }
error: error:
if (error_msg) { if (error_msg) {
MessageBoxExA(NULL, error_msg, "Fatal initialization error", MB_ICONSTOP, 0); MessageBoxExW(NULL, error_msg, L"Fatal initialization error", MB_ICONSTOP, 0);
ASSERT(false); ASSERT(false);
return 1; return 1;
} }
@ -1910,9 +1950,9 @@ __attribute((used))
int _fltused; int _fltused;
__attribute((used)) __attribute((used))
void __stdcall WinMainCRTStartup(void) void __stdcall wWinMainCRTStartup(void)
{ {
int result = WinMain(GetModuleHandle(0), 0, GetCommandLineA(), 0); int result = wWinMain(GetModuleHandle(0), 0, GetCommandLineW(), 0);
ExitProcess(result); ExitProcess(result);
} }

View File

@ -247,7 +247,7 @@ struct asset *texture_load_asset(struct string path, b32 help)
STR("Texture path \"%F\" too long!"), STR("Texture path \"%F\" too long!"),
FMT_STR(path))); FMT_STR(path)));
} }
string_to_cstr_buff(path, BUFFER_FROM_ARRAY(params->path_cstr)); cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path);
params->path_len = path.len; params->path_len = path.len;
params->asset = asset; params->asset = asset;