Set console codepage to utf-8 (on Windows), and restore old one on exit

Avoid mojibake.
This commit is contained in:
Jeroen van Rijn
2026-08-16 16:16:35 +02:00
parent e62ed261c5
commit 0b7b8ba4a2
3 changed files with 72 additions and 8 deletions

View File

@@ -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 ---
}

View File

@@ -65,6 +65,20 @@ template <typename T> struct TypeIs64BitInteger { enum {value = false}; };
template <> struct TypeIs64BitInteger<u64> { enum {value = true}; };
template <> struct TypeIs64BitInteger<i64> { 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"

View File

@@ -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();