From 0b7b8ba4a22786c327a739be67e285ded9348b34 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 16 Aug 2026 16:16:35 +0200 Subject: [PATCH 1/3] Set console codepage to utf-8 (on Windows), and restore old one on exit Avoid mojibake. --- base/runtime/entry_windows.odin | 59 ++++++++++++++++++++++++++++----- src/common.cpp | 14 ++++++++ src/main.cpp | 7 ++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/base/runtime/entry_windows.odin b/base/runtime/entry_windows.odin index d3b38bc9c..56cb9063d 100644 --- a/base/runtime/entry_windows.odin +++ b/base/runtime/entry_windows.odin @@ -5,6 +5,14 @@ package runtime import "base:intrinsics" +when !ODIN_BEDROCK { + @(private="file") + old_console_codepage: u32 + + @(private="file") + UTF_8 :: 65001 +} + when ODIN_BUILD_MODE == .Dynamic { @(link_name="DllMain", linkage="strong", require) DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 { @@ -16,10 +24,17 @@ when ODIN_BUILD_MODE == .Dynamic { switch dll_forward_reason { case .Process_Attach: - when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _startup_runtime() + old_console_codepage = GetConsoleOutputCP() + SetConsoleOutputCP(UTF_8) + } intrinsics.__entry_point() case .Process_Detach: - when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _cleanup_runtime() + SetConsoleOutputCP(old_console_codepage) + } case .Thread_Attach: break case .Thread_Detach: @@ -35,18 +50,32 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _startup_runtime() + old_console_codepage = GetConsoleOutputCP() + SetConsoleOutputCP(UTF_8) + } intrinsics.__entry_point() - when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _cleanup_runtime() + SetConsoleOutputCP(old_console_codepage) + } return 0 } } else when ODIN_NO_CRT { @(link_name="mainCRTStartup", linkage="strong", require) mainCRTStartup :: proc "system" () -> i32 { context = default_context() - when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _startup_runtime() + old_console_codepage = GetConsoleOutputCP() + SetConsoleOutputCP(UTF_8) + } intrinsics.__entry_point() - when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _cleanup_runtime() + SetConsoleOutputCP(old_console_codepage) + } return 0 } } else { @@ -54,10 +83,24 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _startup_runtime() + old_console_codepage = GetConsoleOutputCP() + SetConsoleOutputCP(UTF_8) + } intrinsics.__entry_point() - when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when !ODIN_BEDROCK { + #force_no_inline _cleanup_runtime() + SetConsoleOutputCP(old_console_codepage) + } return 0 } } +} + +foreign import kernel32 "system:Kernel32.lib" +@(default_calling_convention="system") +foreign kernel32 { + GetConsoleOutputCP :: proc() -> u32 --- + SetConsoleOutputCP :: proc(codepage: u32) -> b32 --- } \ No newline at end of file diff --git a/src/common.cpp b/src/common.cpp index 8338ec707..3b4557cde 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -65,6 +65,20 @@ template struct TypeIs64BitInteger { enum {value = false}; }; template <> struct TypeIs64BitInteger { enum {value = true}; }; template <> struct TypeIs64BitInteger { enum {value = true}; }; +#if defined(GB_SYSTEM_WINDOWS) +uint32_t old_console_codepage = 0; +void set_utf8_codepage() { + old_console_codepage = GetConsoleOutputCP(); +} + +void restore_old_codepage() { + // Nothing we can do if this fails, so we're not asserting or anything. + SetConsoleOutputCP(old_console_codepage); +} +#else +void set_utf8_codepage() {} +void restore_old_codepage() {} +#endif #include "unicode.cpp" diff --git a/src/main.cpp b/src/main.cpp index 74900587f..7d2f4d713 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3794,6 +3794,13 @@ int main(int arg_count, char const **arg_ptr) { defer (timings_destroy(&global_timings)); MAIN_TIME_SECTION("initialization"); + // NOTE(Jeroen): Set codepage to UTF-8 (Windows only) and restore on exit. + // Keep in mind this is for the compiler's own output only. + // Like error messages on lines containing unicode. + // Child processes will inherit the default codepage, + // and so must do their own codepage management if they want. + set_utf8_codepage(); + defer (restore_old_codepage()); init_string_interner(); init_global_error_collector(); From 7f532cad149f6da57d98d91d1299d704549bdbc4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 16 Aug 2026 17:01:41 +0200 Subject: [PATCH 2/3] Add -define:ODIN_CODEPAGE_MAGIC=false opt-out to the runtime --- base/runtime/entry_windows.odin | 44 ++++++++++++++------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/base/runtime/entry_windows.odin b/base/runtime/entry_windows.odin index 56cb9063d..119d50b54 100644 --- a/base/runtime/entry_windows.odin +++ b/base/runtime/entry_windows.odin @@ -5,7 +5,9 @@ package runtime import "base:intrinsics" -when !ODIN_BEDROCK { +ODIN_CODEPAGE_MAGIC :: #config(ODIN_CODEPAGE_MAGIC, !ODIN_BEDROCK) + +when ODIN_CODEPAGE_MAGIC { @(private="file") old_console_codepage: u32 @@ -24,17 +26,15 @@ when ODIN_BUILD_MODE == .Dynamic { switch dll_forward_reason { case .Process_Attach: - when !ODIN_BEDROCK { - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when ODIN_CODEPAGE_MAGIC { old_console_codepage = GetConsoleOutputCP() SetConsoleOutputCP(UTF_8) } intrinsics.__entry_point() case .Process_Detach: - when !ODIN_BEDROCK { - #force_no_inline _cleanup_runtime() - SetConsoleOutputCP(old_console_codepage) - } + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when ODIN_CODEPAGE_MAGIC { SetConsoleOutputCP(old_console_codepage) } case .Thread_Attach: break case .Thread_Detach: @@ -50,32 +50,28 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - when !ODIN_BEDROCK { - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when ODIN_CODEPAGE_MAGIC { old_console_codepage = GetConsoleOutputCP() SetConsoleOutputCP(UTF_8) } intrinsics.__entry_point() - when !ODIN_BEDROCK { - #force_no_inline _cleanup_runtime() - SetConsoleOutputCP(old_console_codepage) - } + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when ODIN_CODEPAGE_MAGIC { SetConsoleOutputCP(old_console_codepage) } return 0 } } else when ODIN_NO_CRT { @(link_name="mainCRTStartup", linkage="strong", require) mainCRTStartup :: proc "system" () -> i32 { context = default_context() - when !ODIN_BEDROCK { - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when ODIN_CODEPAGE_MAGIC { old_console_codepage = GetConsoleOutputCP() SetConsoleOutputCP(UTF_8) } intrinsics.__entry_point() - when !ODIN_BEDROCK { - #force_no_inline _cleanup_runtime() - SetConsoleOutputCP(old_console_codepage) - } + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when ODIN_CODEPAGE_MAGIC { SetConsoleOutputCP(old_console_codepage) } return 0 } } else { @@ -83,16 +79,14 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - when !ODIN_BEDROCK { - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } + when ODIN_CODEPAGE_MAGIC { old_console_codepage = GetConsoleOutputCP() SetConsoleOutputCP(UTF_8) } intrinsics.__entry_point() - when !ODIN_BEDROCK { - #force_no_inline _cleanup_runtime() - SetConsoleOutputCP(old_console_codepage) - } + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } + when ODIN_CODEPAGE_MAGIC { SetConsoleOutputCP(old_console_codepage) } return 0 } } From c63561b700b954717456cc4a73db79add35e1a13 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 16 Aug 2026 17:50:49 +0200 Subject: [PATCH 3/3] Also restore codepage on trap. --- base/runtime/core.odin | 20 ++++++++++++++++++++ base/runtime/entry_windows.odin | 17 ----------------- base/runtime/procs_windows_amd64.odin | 5 +++-- base/runtime/procs_windows_i386.odin | 4 +++- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 41d620bdd..0453583c0 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -945,5 +945,25 @@ default_assertion_contextless_failure_proc :: proc "contextless" (prefix, messag print_byte('\n') } } + when ODIN_CODEPAGE_MAGIC { + SetConsoleOutputCP(old_console_codepage) + } trap() } + +ODIN_CODEPAGE_MAGIC :: ODIN_OS == .Windows && #config(ODIN_CODEPAGE_MAGIC, !ODIN_BEDROCK) + +when ODIN_CODEPAGE_MAGIC { + @(private) + old_console_codepage: u32 + + @(private) + UTF_8 :: 65001 +} + +foreign import kernel32 "system:Kernel32.lib" +@(default_calling_convention="system") +foreign kernel32 { + GetConsoleOutputCP :: proc() -> u32 --- + SetConsoleOutputCP :: proc(codepage: u32) -> b32 --- +} \ No newline at end of file diff --git a/base/runtime/entry_windows.odin b/base/runtime/entry_windows.odin index 119d50b54..5e7753d6a 100644 --- a/base/runtime/entry_windows.odin +++ b/base/runtime/entry_windows.odin @@ -5,16 +5,6 @@ package runtime import "base:intrinsics" -ODIN_CODEPAGE_MAGIC :: #config(ODIN_CODEPAGE_MAGIC, !ODIN_BEDROCK) - -when ODIN_CODEPAGE_MAGIC { - @(private="file") - old_console_codepage: u32 - - @(private="file") - UTF_8 :: 65001 -} - when ODIN_BUILD_MODE == .Dynamic { @(link_name="DllMain", linkage="strong", require) DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 { @@ -90,11 +80,4 @@ when ODIN_BUILD_MODE == .Dynamic { return 0 } } -} - -foreign import kernel32 "system:Kernel32.lib" -@(default_calling_convention="system") -foreign kernel32 { - GetConsoleOutputCP :: proc() -> u32 --- - SetConsoleOutputCP :: proc(codepage: u32) -> b32 --- } \ No newline at end of file diff --git a/base/runtime/procs_windows_amd64.odin b/base/runtime/procs_windows_amd64.odin index 81d2cfb5d..cedbc2bd8 100644 --- a/base/runtime/procs_windows_amd64.odin +++ b/base/runtime/procs_windows_amd64.odin @@ -11,8 +11,9 @@ foreign kernel32 { windows_trap_array_bounds :: proc "contextless" () -> ! { EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C - - + when ODIN_CODEPAGE_MAGIC { + SetConsoleOutputCP(old_console_codepage) + } RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil) } diff --git a/base/runtime/procs_windows_i386.odin b/base/runtime/procs_windows_i386.odin index 99c314228..9dd97116d 100644 --- a/base/runtime/procs_windows_i386.odin +++ b/base/runtime/procs_windows_i386.odin @@ -15,7 +15,9 @@ windows_trap_array_bounds :: proc "contextless" () -> ! { foreign kernel32 { RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! --- } - + when ODIN_CODEPAGE_MAGIC { + SetConsoleOutputCP(old_console_codepage) + } RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil) }