diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 87575a15794f..3a7bc0d5afd9 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -377,7 +377,7 @@ pub fn start(options: Options) Node { } const stderr = std.io.getStdErr(); global_progress.terminal = stderr; - if (stderr.supportsAnsiEscapeCodes()) { + if (stderr.getOrEnableAnsiEscapeSupport()) { global_progress.terminal_mode = .ansi_escape_codes; } else if (is_windows and stderr.isTty()) { global_progress.terminal_mode = TerminalMode{ .windows_api = .{ diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 42a2ae71321b..e5d020542797 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -188,7 +188,7 @@ pub fn sync(self: File) SyncError!void { } /// Test whether the file refers to a terminal. -/// See also `supportsAnsiEscapeCodes`. +/// See also `getOrEnableAnsiEscapeSupport` and `supportsAnsiEscapeCodes`. pub fn isTty(self: File) bool { return posix.isatty(self.handle); } @@ -245,7 +245,47 @@ pub fn isCygwinPty(file: File) bool { std.mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null; } -/// Test whether ANSI escape codes will be treated as such. +/// Returns whether or not ANSI escape codes will be treated as such, +/// and attempts to enable support for ANSI escape codes if necessary +/// (on Windows). +/// +/// Returns `true` if ANSI escape codes are supported or support was +/// successfully enabled. Returns false if ANSI escape codes are not +/// supported or support was unable to be enabled. +/// +/// See also `supportsAnsiEscapeCodes`. +pub fn getOrEnableAnsiEscapeSupport(self: File) bool { + if (builtin.os.tag == .windows) { + var original_console_mode: windows.DWORD = 0; + + // For Windows Terminal, VT Sequences processing is enabled by default. + if (windows.kernel32.GetConsoleMode(self.handle, &original_console_mode) != 0) { + if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true; + + // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default. + // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/ + // Use Microsoft's recommended way to enable virtual terminal processing. + // https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing + var requested_console_modes: windows.DWORD = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | windows.DISABLE_NEWLINE_AUTO_RETURN; + var console_mode = original_console_mode | requested_console_modes; + if (windows.kernel32.SetConsoleMode(self.handle, console_mode) != 0) return true; + + // An application receiving ERROR_INVALID_PARAMETER with one of the newer console mode + // flags in the bit field should gracefully degrade behavior and try again. + requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING; + console_mode = original_console_mode | requested_console_modes; + if (windows.kernel32.SetConsoleMode(self.handle, console_mode) != 0) return true; + } + + return self.isCygwinPty(); + } + return self.supportsAnsiEscapeCodes(); +} + +/// Test whether ANSI escape codes will be treated as such without +/// attempting to enable support for ANSI escape codes. +/// +/// See also `getOrEnableAnsiEscapeSupport`. pub fn supportsAnsiEscapeCodes(self: File) bool { if (builtin.os.tag == .windows) { var console_mode: windows.DWORD = 0; diff --git a/lib/std/io/tty.zig b/lib/std/io/tty.zig index 9453d8dac9ae..83206a6a677d 100644 --- a/lib/std/io/tty.zig +++ b/lib/std/io/tty.zig @@ -8,6 +8,7 @@ const native_os = builtin.os.tag; /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr). /// This includes feature checks for ANSI escape codes and the Windows console API, as well as /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default. +/// Will attempt to enable ANSI escape code support if necessary/possible. pub fn detectConfig(file: File) Config { const force_color: ?bool = if (builtin.os.tag == .wasi) null // wasi does not support environment variables @@ -20,7 +21,7 @@ pub fn detectConfig(file: File) Config { if (force_color == false) return .no_color; - if (file.supportsAnsiEscapeCodes()) return .escape_codes; + if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes; if (native_os == .windows and file.isTty()) { var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;