You can test this.
Test program
const std = @import("std");
pub fn main() !void {
var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const args = try std.process.argsAlloc(arena);
if (args.len <= 1) {
return error.MissingArguments;
}
const stderr = std.io.getStdErr();
if (std.mem.eql(u8, args[1], "get")) {
std.debug.print("supportsAnsiEscapeCodes: {}\n", .{stderr.supportsAnsiEscapeCodes()});
} else if (std.mem.eql(u8, args[1], "getorset")) {
std.debug.print("getOrEnableAnsiEscapeSupport: {}\n", .{stderr.getOrEnableAnsiEscapeSupport()});
} else if (std.mem.eql(u8, args[1], "unset")) {
var original_console_mode: std.os.windows.DWORD = 0;
if (std.os.windows.kernel32.GetConsoleMode(stderr.handle, &original_console_mode) != 0) {
const console_mode = original_console_mode ^ std.os.windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (std.os.windows.kernel32.SetConsoleMode(stderr.handle, console_mode) != 0) {
std.debug.print("unset ENABLE_VIRTUAL_TERMINAL_PROCESSING\n", .{});
}
}
std.debug.print("supportsAnsiEscapeCodes: {}\n", .{stderr.supportsAnsiEscapeCodes()});
}
const escape_code_tty = std.io.tty.Config{ .escape_codes = {} };
try escape_code_tty.setColor(stderr.writer(), .green);
std.debug.print("hello\n", .{});
try escape_code_tty.setColor(stderr.writer(), .reset);
}
In Windows Terminal (where ENABLE_VIRTUAL_TERMINAL_PROCESSING
is enabled by default):
> vt.exe get
supportsAnsiEscapeCodes: true
hello
> vt.exe unset
unset ENABLE_VIRTUAL_TERMINAL_PROCESSING
supportsAnsiEscapeCodes: false
←[32mhello←[0m
> vt.exe get
supportsAnsiEscapeCodes: true
hello
In cmd.exe
(where ENABLE_VIRTUAL_TERMINAL_PROCESSING
is disabled by default, but can be enabled):
> vt.exe get
supportsAnsiEscapeCodes: false
←[32mhello←[0m
> vt.exe getorset
getOrEnableAnsiEscapeSupport: true
hello
> vt.exe get
supportsAnsiEscapeCodes: false
←[32mhello←[0m
That is, the setting does not persist between subsequent processes. However, note the caveat mentioned here:
A caveat here is that child processes can affect the console mode of parent processes, see #16526 (comment) for an example of how this can cause problems. So this may not actually be fully safe to do, as spawning Zig as a child process can end up messing with the console mode of other processes unexpectedly.
This is different than how the code page setting works.
Code page test program
const std = @import("std");
pub fn main() !void {
std.debug.print("→ code page at startup: {}\n", .{std.os.windows.kernel32.GetConsoleOutputCP()});
std.debug.print("→ setting code page to 65001 (UTF-8)...\n", .{});
std.debug.assert(std.os.windows.kernel32.SetConsoleOutputCP(65001) != 0);
std.debug.print("→ done\n", .{});
std.debug.print("→ code page at exit: {}\n", .{std.os.windows.kernel32.GetConsoleOutputCP()});
}
> codepage.exe
→ code page at startup: 437
→ setting code page to 65001 (UTF-8)...
→ done
→ code page at exit: 65001
> codepage.exe
→ code page at startup: 65001
→ setting code page to 65001 (UTF-8)...
→ done
→ code page at exit: 65001
(same thing happens in both Windows Terminal and cmd.exe)
(weird side note: running chcp
still reports Active code page: 437
even though GetConsoleOutputCP
returns 65001
; EDIT: chcp
seems to look at the input code page, calling SetConsoleCP
changes what chcp
reports)