authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-02-04 18:12:29-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-02-05 07:41:25-05:00
logc77e7146f5fa8e83c06cd6612b7298df06912974
treec49c30a3bdeddddede96e5cb7c0d813342a34bda
parentf1507599531e00f40fd31bbe39f2526e70241e10

std.Threaded: replace console kernel32 functions with ntdll


9 files changed, 477 insertions(+), 322 deletions(-)

lib/std/Io.zig+3-5
......@@ -335,11 +335,9 @@ pub const Operation = union(enum) {
335335 .wasi => noreturn,
336336 .windows => struct {
337337 file: File,
338 IoControlCode: std.os.windows.CTL_CODE,
339 InputBuffer: ?*const anyopaque,
340 InputBufferLength: u32,
341 OutputBuffer: ?*anyopaque,
342 OutputBufferLength: u32,
338 code: std.os.windows.CTL_CODE,
339 in: []const u8 = &.{},
340 out: []u8 = &.{},
343341
344342 pub const Result = std.os.windows.IO_STATUS_BLOCK;
345343 },
lib/std/Io/Terminal.zig+16-10
......@@ -40,7 +40,8 @@ pub const Mode = union(enum) {
4040 windows_api: WindowsApi,
4141
4242 pub const WindowsApi = if (!is_windows) noreturn else struct {
43 handle: File.Handle,
43 io: Io,
44 file: File,
4445 reset_attributes: u16,
4546 };
4647
......@@ -65,20 +66,21 @@ pub const Mode = union(enum) {
6566 }
6667
6768 if (is_windows and try file.isTty(io)) {
68 const windows = std.os.windows;
69 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
70 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) {
71 return .{ .windows_api = .{
72 .handle = file.handle,
73 .reset_attributes = info.wAttributes,
74 } };
69 var get_console_info = std.os.windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
70 switch (try get_console_info.operate(io, file)) {
71 .SUCCESS => return .{ .windows_api = .{
72 .io = io,
73 .file = file,
74 .reset_attributes = get_console_info.Data.wAttributes,
75 } },
76 else => {},
7577 }
7678 }
7779 return if (force_color == true) .escape_codes else .no_color;
7880 }
7981};
8082
81pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error;
83pub const SetColorError = Io.Cancelable || Io.UnexpectedError || Io.Writer.Error;
8284
8385pub fn setColor(t: Terminal, color: Color) SetColorError!void {
8486 switch (t.mode) {
......@@ -132,7 +134,11 @@ pub fn setColor(t: Terminal, color: Color) SetColorError!void {
132134 .reset => wa.reset_attributes,
133135 };
134136 try t.writer.flush();
135 try windows.SetConsoleTextAttribute(wa.handle, attributes);
137 var set_text_attribute = windows.CONSOLE.USER_IO.SET_TEXT_ATTRIBUTE(attributes);
138 switch (try set_text_attribute.operate(wa.io, wa.file)) {
139 .SUCCESS => {},
140 else => |status| return windows.unexpectedStatus(status),
141 }
136142 },
137143 }
138144}
lib/std/Io/Threaded.zig+93-115
......@@ -3083,19 +3083,23 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
30833083 }
30843084 },
30853085 .device_io_control => |o| {
3086 const NtControlFile = switch (o.code.DeviceType) {
3087 .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
3088 else => &windows.ntdll.NtDeviceIoControlFile,
3089 };
30863090 if (o.file.flags.nonblocking) {
30873091 context.file = o.file.handle;
3088 switch (windows.ntdll.NtDeviceIoControlFile(
3092 switch (NtControlFile(
30893093 o.file.handle,
30903094 null, // event
30913095 &batchApc,
30923096 b,
30933097 &context.iosb,
3094 o.IoControlCode,
3095 o.InputBuffer,
3096 o.InputBufferLength,
3097 o.OutputBuffer,
3098 o.OutputBufferLength,
3098 o.code,
3099 if (o.in.len > 0) o.in.ptr else null,
3100 @intCast(o.in.len),
3101 if (o.out.len > 0) o.out.ptr else null,
3102 @intCast(o.out.len),
30993103 )) {
31003104 .PENDING, .SUCCESS => {},
31013105 .CANCELLED => unreachable,
......@@ -3108,17 +3112,17 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
31083112 if (concurrency) return error.ConcurrencyUnavailable;
31093113
31103114 const syscall: Syscall = try .start();
3111 while (true) switch (windows.ntdll.NtDeviceIoControlFile(
3115 while (true) switch (NtControlFile(
31123116 o.file.handle,
31133117 null, // event
31143118 null, // APC routine
31153119 null, // APC context
31163120 &context.iosb,
3117 o.IoControlCode,
3118 o.InputBuffer,
3119 o.InputBufferLength,
3120 o.OutputBuffer,
3121 o.OutputBufferLength,
3121 o.code,
3122 if (o.in.len > 0) o.in.ptr else null,
3123 @intCast(o.in.len),
3124 if (o.out.len > 0) o.out.ptr else null,
3125 @intCast(o.out.len),
31223126 )) {
31233127 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
31243128 .CANCELLED => {
......@@ -8547,29 +8551,24 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
85478551
85488552fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
85498553 const t: *Threaded = @ptrCast(@alignCast(userdata));
8550 _ = t;
8551 return isTty(file);
8554 return t.isTty(file);
85528555}
85538556
8554fn isTty(file: File) Io.Cancelable!bool {
8557fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
85558558 if (is_windows) {
8556 if (try isCygwinPty(file)) return true;
8557 var out: windows.DWORD = undefined;
8558 const syscall: Syscall = try .start();
8559 while (windows.kernel32.GetConsoleMode(file.handle, &out) == 0) {
8560 switch (windows.GetLastError()) {
8561 .OPERATION_ABORTED => {
8562 try syscall.checkCancel();
8563 continue;
8564 },
8565 else => {
8566 syscall.finish();
8567 return false;
8568 },
8569 }
8559 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8560 switch ((try t.deviceIoControl(&.{
8561 .file = .{
8562 .handle = windows.peb().ProcessParameters.ConsoleHandle,
8563 .flags = .{ .nonblocking = false },
8564 },
8565 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8566 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
8567 })).u.Status) {
8568 .SUCCESS => return true,
8569 .INVALID_HANDLE => return isCygwinPty(file),
8570 else => return false,
85708571 }
8571 syscall.finish();
8572 return true;
85738572 }
85748573
85758574 if (builtin.link_libc) {
......@@ -8637,35 +8636,26 @@ fn isTty(file: File) Io.Cancelable!bool {
86378636
86388637fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
86398638 const t: *Threaded = @ptrCast(@alignCast(userdata));
8640 _ = t;
86418639
8642 if (!is_windows) {
8643 if (try supportsAnsiEscapeCodes(file)) return;
8644 return error.NotTerminalDevice;
8645 }
8640 if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
86468641
86478642 // For Windows Terminal, VT Sequences processing is enabled by default.
8648 var original_console_mode: windows.DWORD = 0;
8649
8650 {
8651 const syscall: Syscall = try .start();
8652 while (windows.kernel32.GetConsoleMode(file.handle, &original_console_mode) == 0) {
8653 switch (windows.GetLastError()) {
8654 .OPERATION_ABORTED => {
8655 try syscall.checkCancel();
8656 continue;
8657 },
8658 else => {
8659 syscall.finish();
8660 if (try isCygwinPty(file)) return;
8661 return error.NotTerminalDevice;
8662 },
8663 }
8664 }
8665 syscall.finish();
8643 const console: File = .{
8644 .handle = windows.peb().ProcessParameters.ConsoleHandle,
8645 .flags = .{ .nonblocking = false },
8646 };
8647 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8648 switch ((try t.deviceIoControl(&.{
8649 .file = console,
8650 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8651 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
8652 })).u.Status) {
8653 .SUCCESS => {},
8654 .INVALID_HANDLE => return if (!try isCygwinPty(file)) error.NotTerminalDevice,
8655 else => return error.NotTerminalDevice,
86668656 }
86678657
8668 if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
8658 if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
86698659
86708660 // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
86718661 // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
......@@ -8678,58 +8668,40 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
86788668 // Additionally, the default console mode in Windows Terminal does not have
86798669 // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`
86808670 // we end up matching the mode of Windows Terminal.
8681 const requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
8682 const console_mode = original_console_mode | requested_console_modes;
8683
8684 {
8685 const syscall: Syscall = try .start();
8686 while (windows.kernel32.SetConsoleMode(file.handle, console_mode) == 0) {
8687 switch (windows.GetLastError()) {
8688 .OPERATION_ABORTED => {
8689 try syscall.checkCancel();
8690 continue;
8691 },
8692 else => {
8693 syscall.finish();
8694 if (try isCygwinPty(file)) return;
8695 return error.NotTerminalDevice;
8696 },
8697 }
8698 }
8699 syscall.finish();
8671 var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
8672 get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
8673 );
8674 switch ((try t.deviceIoControl(&.{
8675 .file = console,
8676 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8677 .in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
8678 })).u.Status) {
8679 .SUCCESS => {},
8680 else => |status| return windows.unexpectedStatus(status),
87008681 }
87018682}
87028683
87038684fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
87048685 const t: *Threaded = @ptrCast(@alignCast(userdata));
8705 _ = t;
8706 return supportsAnsiEscapeCodes(file);
8686 return t.supportsAnsiEscapeCodes(file);
87078687}
87088688
8709fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
8689fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
87108690 if (is_windows) {
8711 var console_mode: windows.DWORD = 0;
8712
8713 const syscall: Syscall = try .start();
8714 while (windows.kernel32.GetConsoleMode(file.handle, &console_mode) == 0) {
8715 switch (windows.GetLastError()) {
8716 .OPERATION_ABORTED => {
8717 try syscall.checkCancel();
8718 continue;
8719 },
8720 else => {
8721 syscall.finish();
8722 break;
8723 },
8724 }
8725 } else {
8726 syscall.finish();
8727 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) {
8728 return true;
8729 }
8691 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8692 switch ((try t.deviceIoControl(&.{
8693 .file = .{
8694 .handle = windows.peb().ProcessParameters.ConsoleHandle,
8695 .flags = .{ .nonblocking = false },
8696 },
8697 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8698 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
8699 })).u.Status) {
8700 .SUCCESS => if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0)
8701 return true,
8702 .INVALID_HANDLE => return isCygwinPty(file),
8703 else => return false,
87308704 }
8731
8732 return isCygwinPty(file);
87338705 }
87348706
87358707 if (native_os == .wasi) {
......@@ -8739,7 +8711,7 @@ fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
87398711 return false;
87408712 }
87418713
8742 if (try isTty(file)) return true;
8714 if (try t.isTty(file)) return true;
87438715
87448716 return false;
87458717}
......@@ -14111,12 +14083,14 @@ fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelabl
1411114083
1411214084fn unlockStderr(userdata: ?*anyopaque) void {
1411314085 const t: *Threaded = @ptrCast(@alignCast(userdata));
14114 t.stderr_writer.interface.flush() catch |err| switch (err) {
14115 error.WriteFailed => switch (t.stderr_writer.err.?) {
14086 if (t.stderr_writer.err == null) t.stderr_writer.interface.flush() catch {};
14087 if (t.stderr_writer.err) |err| {
14088 switch (err) {
1411614089 error.Canceled => recancelInner(),
1411714090 else => {},
14118 },
14119 };
14091 }
14092 t.stderr_writer.err = null;
14093 }
1412014094 t.stderr_writer.interface.end = 0;
1412114095 t.stderr_writer.interface.buffer = &.{};
1412214096
......@@ -18848,20 +18822,24 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
1884818822fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
1884918823 _ = t;
1885018824 if (is_windows) {
18825 const NtControlFile = switch (o.code.DeviceType) {
18826 .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
18827 else => &windows.ntdll.NtDeviceIoControlFile,
18828 };
1885118829 var iosb: windows.IO_STATUS_BLOCK = undefined;
1885218830 if (o.file.flags.nonblocking) {
1885318831 var done: bool = false;
18854 switch (windows.ntdll.NtDeviceIoControlFile(
18832 switch (NtControlFile(
1885518833 o.file.handle,
1885618834 null, // event
1885718835 flagApc,
1885818836 &done, // APC context
1885918837 &iosb,
18860 o.IoControlCode,
18861 o.InputBuffer,
18862 o.InputBufferLength,
18863 o.OutputBuffer,
18864 o.OutputBufferLength,
18838 o.code,
18839 if (o.in.len > 0) o.in.ptr else null,
18840 @intCast(o.in.len),
18841 if (o.out.len > 0) o.out.ptr else null,
18842 @intCast(o.out.len),
1886518843 )) {
1886618844 // We must wait for the APC routine.
1886718845 .PENDING, .SUCCESS => while (!done) {
......@@ -18882,17 +18860,17 @@ fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Canc
1888218860 }
1888318861 } else {
1888418862 const syscall: Syscall = try .start();
18885 while (true) switch (windows.ntdll.NtDeviceIoControlFile(
18863 while (true) switch (NtControlFile(
1888618864 o.file.handle,
1888718865 null, // event
1888818866 null, // APC routine
1888918867 null, // APC context
1889018868 &iosb,
18891 o.IoControlCode,
18892 o.InputBuffer,
18893 o.InputBufferLength,
18894 o.OutputBuffer,
18895 o.OutputBufferLength,
18869 o.code,
18870 if (o.in.len > 0) o.in.ptr else null,
18871 @intCast(o.in.len),
18872 if (o.out.len > 0) o.out.ptr else null,
18873 @intCast(o.out.len),
1889618874 )) {
1889718875 .PENDING => unreachable, // unrecoverable: wrong asynchronous flag
1889818876 .CANCELLED => {
lib/std/Progress.zig+102-69
......@@ -157,7 +157,7 @@ pub const TerminalMode = union(enum) {
157157 ansi_escape_codes,
158158 /// This is not the same as being run on windows because other terminals
159159 /// exist like MSYS/git-bash.
160 windows_api: if (is_windows) WindowsApi else void,
160 windows_api: if (is_windows) WindowsApi else noreturn,
161161
162162 pub const WindowsApi = struct {
163163 /// The output code page of the console.
......@@ -614,33 +614,39 @@ pub fn start(io: Io, options: Options) Node {
614614 if (stderr.enableAnsiEscapeCodes(io)) |_| {
615615 global_progress.terminal_mode = .ansi_escape_codes;
616616 } else |_| if (is_windows) {
617 if (stderr.isTty(io)) |is_tty| {
618 if (is_tty) global_progress.terminal_mode = TerminalMode{ .windows_api = .{
619 .code_page = windows.kernel32.GetConsoleOutputCP(),
620 } };
621 } else |err| switch (err) {
617 var get_console_cp = windows.CONSOLE.USER_IO.GET_CP(.Output);
618 // Normally, we would pass `null` to `operate` here as the kernel32
619 // function does not accept a handle, however, if we pass one anyway,
620 // then we will get an error if the handle is not associated with
621 // this process's console, effectively combining an `isTty` check
622 // into the same syscall.
623 switch (get_console_cp.operate(io, stderr) catch |err| switch (err) {
622624 error.Canceled => {
623625 io.recancel();
624626 return .none;
625627 },
628 }) {
629 .SUCCESS => global_progress.terminal_mode = .{ .windows_api = .{
630 .code_page = get_console_cp.Data.CodePage,
631 } },
632 .INVALID_HANDLE => {},
633 else => {},
626634 }
627635 }
628
629 if (global_progress.terminal_mode == .off) return .none;
630
631 if (have_sigwinch) {
632 const act: posix.Sigaction = .{
633 .handler = .{ .sigaction = handleSigWinch },
634 .mask = posix.sigemptyset(),
635 .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
636 };
637 posix.sigaction(.WINCH, &act, null);
638 }
639
640 if (switch (global_progress.terminal_mode) {
641 .off => unreachable, // handled a few lines above
642 .ansi_escape_codes => io.concurrent(updateTask, .{io}),
643 .windows_api => if (is_windows) io.concurrent(windowsApiUpdateTask, .{io}) else unreachable,
636 if (future: switch (global_progress.terminal_mode) {
637 .off => return .none,
638 .ansi_escape_codes => {
639 if (have_sigwinch) {
640 const act: posix.Sigaction = .{
641 .handler = .{ .sigaction = handleSigWinch },
642 .mask = posix.sigemptyset(),
643 .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
644 };
645 posix.sigaction(.WINCH, &act, null);
646 }
647 break :future io.concurrent(updateTask, .{io});
648 },
649 .windows_api => io.concurrent(windowsApiUpdateTask, .{io}),
644650 }) |future| {
645651 global_progress.update_worker = future;
646652 } else |err| {
......@@ -715,12 +721,24 @@ fn updateTask(io: Io) WorkerError!void {
715721 }
716722}
717723
718fn windowsApiWriteMarker() void {
724const WindowsApiError = Io.Cancelable || Io.UnexpectedError;
725
726fn windowsApiWriteMarker(io: Io) WindowsApiError!void {
719727 // Write the marker that we will use to find the beginning of the progress when clearing.
720728 // Note: This doesn't have to use WriteConsoleW, but doing so avoids dealing with the code page.
721 var num_chars_written: windows.DWORD = undefined;
722 const handle = global_progress.terminal.handle;
723 _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null);
729 const terminal = global_progress.terminal;
730 var write_console = windows.CONSOLE.USER_IO.WRITE(.WideCharacter);
731 const buffer = [1]windows.WCHAR{windows_api_start_marker};
732 switch ((try io.operate(.{ .device_io_control = .{
733 .file = terminal,
734 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
735 .in = @ptrCast(&write_console.request(null, 1, .{
736 .{ .Size = @sizeOf(@TypeOf(buffer)), .Pointer = &buffer },
737 }, 0, .{})),
738 } })).device_io_control.u.Status) {
739 .SUCCESS => {},
740 else => |status| return windows.unexpectedStatus(status),
741 }
724742}
725743
726744fn windowsApiUpdateTask(io: Io) WorkerError!void {
......@@ -743,19 +761,19 @@ fn windowsApiUpdateTask(io: Io) WorkerError!void {
743761 error.Canceled => unreachable, // blocked
744762 };
745763 defer io.unlockStderr();
746 clearWrittenWindowsApi() catch {};
764 clearWrittenWindowsApi(io) catch {};
747765 }
748766 while (true) {
749767 const buffer, const nl_n = try computeRedraw(io, &serialized_buffer);
750768 if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
751769 defer io.unlockStderr();
752 try clearWrittenWindowsApi();
753 windowsApiWriteMarker();
770 try clearWrittenWindowsApi(io);
771 try windowsApiWriteMarker(io);
754772 global_progress.need_clear = true;
755773 locked_stderr.file_writer.interface.writeAll(buffer) catch |err| switch (err) {
756774 error.WriteFailed => return locked_stderr.file_writer.err.?,
757775 };
758 windowsApiMoveToMarker(nl_n) catch return;
776 windowsApiMoveToMarker(io, nl_n) catch return;
759777 }
760778
761779 try maybeUpdateSize(io, try wait(io, global_progress.refresh_rate_ns));
......@@ -859,7 +877,7 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize {
859877 return start_i + bytes.len;
860878 },
861879 .windows_api => |windows_api| {
862 const bytes = if (!is_windows) unreachable else switch (windows_api.code_page) {
880 const bytes = switch (windows_api.code_page) {
863881 // Code page 437 is the default code page and contains the box drawing symbols
864882 437 => symbol.bytes(.code_page_437),
865883 // UTF-8
......@@ -882,7 +900,7 @@ pub fn clearWrittenWithEscapeCodes(file_writer: *Io.File.Writer) Io.Writer.Error
882900/// U+25BA or ►
883901const windows_api_start_marker = 0x25BA;
884902
885fn clearWrittenWindowsApi() error{Unexpected}!void {
903fn clearWrittenWindowsApi(io: Io) WindowsApiError!void {
886904 // This uses a 'marker' strategy. The idea is:
887905 // - Always write a marker (in this case U+25BA or ►) at the beginning of the progress
888906 // - Get the current cursor position (at the end of the progress)
......@@ -903,43 +921,60 @@ fn clearWrittenWindowsApi() error{Unexpected}!void {
903921 // character in order to be readable via ReadConsoleOutputAttribute. It doesn't seem
904922 // like any of the available attributes are invisible/benign.
905923 if (!global_progress.need_clear) return;
906 const handle = global_progress.terminal.handle;
924 const terminal = global_progress.terminal;
907925 const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows;
908926
909 var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
910 if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {
911 return error.Unexpected;
927 var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
928 switch (try get_console_info.operate(io, terminal)) {
929 .SUCCESS => {},
930 else => |status| return windows.unexpectedStatus(status),
912931 }
913 var num_chars_written: windows.DWORD = undefined;
914 if (windows.kernel32.FillConsoleOutputCharacterW(handle, ' ', screen_area, console_info.dwCursorPosition, &num_chars_written) == 0) {
915 return error.Unexpected;
932 var fill_spaces = windows.CONSOLE.USER_IO.FILL(
933 .{ .WideCharacter = ' ' },
934 screen_area,
935 get_console_info.Data.dwCursorPosition,
936 );
937 switch (try fill_spaces.operate(io, terminal)) {
938 .SUCCESS => {},
939 else => |status| return windows.unexpectedStatus(status),
916940 }
917941}
918942
919fn windowsApiMoveToMarker(nl_n: usize) error{Unexpected}!void {
920 const handle = global_progress.terminal.handle;
921 var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
922 if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {
923 return error.Unexpected;
943fn windowsApiMoveToMarker(io: Io, nl_n: usize) WindowsApiError!void {
944 const terminal = global_progress.terminal;
945 var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
946 switch (try get_console_info.operate(io, terminal)) {
947 .SUCCESS => {},
948 else => |status| return windows.unexpectedStatus(status),
924949 }
925 const cursor_pos = console_info.dwCursorPosition;
950 const cursor_pos = get_console_info.Data.dwCursorPosition;
926951 const expected_y = cursor_pos.Y - @as(i16, @intCast(nl_n));
927952 var start_pos: windows.COORD = .{ .X = 0, .Y = expected_y };
928 while (start_pos.Y >= 0) {
929 var wchar: [1]u16 = undefined;
930 var num_console_chars_read: windows.DWORD = undefined;
931 if (windows.kernel32.ReadConsoleOutputCharacterW(handle, &wchar, wchar.len, start_pos, &num_console_chars_read) == 0) {
932 return error.Unexpected;
953 while (start_pos.Y >= 0) : (start_pos.Y -= 1) {
954 var read_output_char = windows.CONSOLE.USER_IO.READ_OUTPUT_CHARACTER(start_pos, .WideCharacter);
955 var buffer: [1]windows.WCHAR = undefined;
956 switch ((try io.operate(.{ .device_io_control = .{
957 .file = .{
958 .handle = windows.peb().ProcessParameters.ConsoleHandle,
959 .flags = .{ .nonblocking = false },
960 },
961 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
962 .in = @ptrCast(&read_output_char.request(terminal, 0, .{}, 1, .{
963 .{ .Size = @sizeOf(@TypeOf(buffer)), .Pointer = &buffer },
964 })),
965 } })).device_io_control.u.Status) {
966 .SUCCESS => {},
967 else => |status| return windows.unexpectedStatus(status),
933968 }
934
935 if (wchar[0] == windows_api_start_marker) break;
936 start_pos.Y -= 1;
969 if (read_output_char.Data.nLength >= 1 and buffer[0] == windows_api_start_marker) break;
937970 } else {
938971 // If we couldn't find the marker, then just assume that no lines wrapped
939972 start_pos = .{ .X = 0, .Y = expected_y };
940973 }
941 if (windows.kernel32.SetConsoleCursorPosition(handle, start_pos) == 0) {
942 return error.Unexpected;
974 var set_cursor_position = windows.CONSOLE.USER_IO.SET_CURSOR_POSITION(start_pos);
975 switch (try set_cursor_position.operate(io, terminal)) {
976 .SUCCESS => {},
977 else => |status| return windows.unexpectedStatus(status),
943978 }
944979}
945980
......@@ -1279,7 +1314,7 @@ fn computeRedraw(io: Io, serialized_buffer: *Serialized.Buffer) !struct { []u8,
12791314 buf[i..][0..clear.len].* = clear.*;
12801315 i += clear.len;
12811316 },
1282 .windows_api => if (!is_windows) unreachable,
1317 .windows_api => {},
12831318 }
12841319
12851320 const root_node_index: Node.Index = @enumFromInt(0);
......@@ -1491,19 +1526,17 @@ fn maybeUpdateSize(io: Io, resize_flag: bool) !void {
14911526 const file = global_progress.terminal;
14921527
14931528 if (is_windows) {
1494 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
1495
1496 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) {
1497 // In the old Windows console, dwSize.Y is the line count of the
1498 // entire scrollback buffer, so we use this instead so that we
1499 // always get the size of the screen.
1500 const screen_height = info.srWindow.Bottom - info.srWindow.Top;
1501 global_progress.rows = @intCast(screen_height);
1502 global_progress.cols = @intCast(info.dwSize.X);
1503 } else {
1504 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
1505 global_progress.rows = 25;
1506 global_progress.cols = 80;
1529 var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
1530 switch (try get_console_info.operate(io, file)) {
1531 .SUCCESS => {
1532 global_progress.rows = @intCast(get_console_info.Data.dwWindowSize.Y);
1533 global_progress.cols = @intCast(get_console_info.Data.dwWindowSize.X);
1534 },
1535 else => {
1536 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
1537 global_progress.rows = 25;
1538 global_progress.cols = 80;
1539 },
15071540 }
15081541 } else {
15091542 var winsize: posix.winsize = .{
lib/std/log.zig+4-1
......@@ -80,7 +80,7 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool {
8080 return @intFromEnum(level) <= @intFromEnum(std.options.log_level);
8181}
8282
83pub const terminalMode = std.options.logTerminalMode;
83pub const terminalMode = std.Options.logTerminalMode;
8484
8585pub fn defaultTerminalMode() std.Io.Terminal.Mode {
8686 const stderr = std.debug.lockStderr(&.{}).terminal();
......@@ -99,6 +99,9 @@ pub fn defaultLog(
9999 comptime format: []const u8,
100100 args: anytype,
101101) void {
102 const io = std.Options.debug_io;
103 const prev = io.swapCancelProtection(.blocked);
104 defer _ = io.swapCancelProtection(prev);
102105 var buffer: [64]u8 = undefined;
103106 const stderr = std.debug.lockStderr(&buffer).terminal();
104107 defer std.debug.unlockStderr();
lib/std/os/windows.zig+254-35
......@@ -649,6 +649,235 @@ pub const FILE = struct {
649649 };
650650};
651651
652pub const CONSOLE = struct {
653 pub const USER_IO = struct {
654 pub const INFO = struct {
655 pub const CP = extern struct {
656 /// GetCP: output
657 /// SetCP: input
658 CodePage: UINT,
659 /// input
660 Mode: MODE,
661
662 pub const MODE = enum(BOOLEAN) {
663 Input = FALSE,
664 Output = TRUE,
665 };
666 };
667
668 pub const WRITE = extern struct {
669 /// output, in bytes
670 Size: DWORD,
671 /// input
672 Mode: MODE,
673
674 pub const MODE = enum(BOOLEAN) {
675 Character = FALSE,
676 WideCharacter = TRUE,
677 };
678 };
679
680 pub const FILL = extern struct {
681 /// input
682 dwWriteCoord: COORD,
683 /// input
684 Tag: WITH.Tag,
685 /// input
686 With: WITH.Payload,
687 /// input/output, in characters
688 nLength: DWORD,
689
690 pub const WITH = union(enum(DWORD)) {
691 Character: CHAR = 1,
692 WideCharacter: WCHAR = 2,
693 Attribute: WORD = 3,
694
695 pub const Tag = @typeInfo(WITH).@"union".tag_type.?;
696 pub const Payload = PAYLOAD: {
697 const with_fields = @typeInfo(WITH).@"union".fields;
698 var field_names: [with_fields.len][]const u8 = undefined;
699 var field_types: [with_fields.len]type = undefined;
700 for (with_fields, &field_names, &field_types) |field, *field_name, *field_type| {
701 field_name.* = field.name;
702 field_type.* = field.type;
703 }
704 break :PAYLOAD @Union(.@"extern", null, &field_names, &field_types, &@splat(.{}));
705 };
706 };
707 };
708
709 /// all output
710 pub const SCREEN_BUFFER = extern struct {
711 dwSize: COORD,
712 dwCursorPosition: COORD,
713 dwWindowPosition: COORD,
714 wAttributes: WORD,
715 dwWindowSize: COORD,
716 dwMaximumWindowSize: COORD,
717 wPopupAttributes: WORD,
718 bFullscreenSupported: BOOL,
719 ColorTable: [16]COLORREF,
720 };
721
722 pub const READ_OUTPUT_CHARACTER = extern struct {
723 /// input
724 dwReadCoord: COORD,
725 Mode: MODE,
726 /// output, in characters
727 nLength: DWORD,
728
729 pub const MODE = enum(DWORD) {
730 Character = 1,
731 WideCharacter = 2,
732 };
733 };
734 };
735
736 pub fn GET_CP(mode: INFO.CP.MODE) Header.With(INFO.CP) {
737 return .init(.GetCP, .{ .CodePage = undefined, .Mode = mode });
738 }
739 pub const GET_MODE: Header.With(DWORD) = .init(.GetMode, undefined);
740 pub fn SET_MODE(mode: DWORD) Header.With(DWORD) {
741 return .init(.SetMode, mode);
742 }
743 pub fn WRITE(mode: INFO.WRITE.MODE) Header.With(INFO.WRITE) {
744 return .init(.Write, .{ .Size = undefined, .Mode = mode });
745 }
746 pub fn FILL(with: INFO.FILL.WITH, len: DWORD, coord: COORD) Header.With(INFO.FILL) {
747 return .init(.Fill, .{
748 .dwWriteCoord = coord,
749 .Tag = with,
750 .With = switch (with) {
751 inline else => |payload, tag| @unionInit(
752 INFO.FILL.WITH.Payload,
753 @tagName(tag),
754 payload,
755 ),
756 },
757 .nLength = len,
758 });
759 }
760 pub fn SET_CP(mode: INFO.CP.MODE, cp: UINT) Header.With(INFO.CP) {
761 return .init(.SetCP, .{ .CodePage = cp, .Mode = mode });
762 }
763 pub const GET_SCREEN_BUFFER_INFO: Header.With(INFO.SCREEN_BUFFER) =
764 .init(.GetScreenBufferInfo, undefined);
765 pub fn SET_CURSOR_POSITION(coord: COORD) Header.With(COORD) {
766 return .init(.SetCursorPosition, coord);
767 }
768 pub fn SET_TEXT_ATTRIBUTE(attribute: WORD) Header.With(WORD) {
769 return .init(.SetTextAttribute, attribute);
770 }
771 pub fn READ_OUTPUT_CHARACTER(
772 coord: COORD,
773 mode: INFO.READ_OUTPUT_CHARACTER.MODE,
774 ) Header.With(INFO.READ_OUTPUT_CHARACTER) {
775 return .init(.ReadOutputCharacter, .{
776 .dwReadCoord = coord,
777 .Mode = mode,
778 .nLength = undefined,
779 });
780 }
781
782 pub const InputBuffer = extern struct {
783 Size: u32,
784 Pointer: *const anyopaque,
785 };
786
787 pub const OutputBuffer = extern struct {
788 Size: u32,
789 Pointer: *anyopaque,
790 };
791
792 pub fn Request(comptime in_len: u32, comptime out_len: u32) type {
793 return extern struct {
794 Handle: ?HANDLE,
795 InputBuffersLength: u32,
796 OutputBuffersLength: u32,
797 InputBuffers: [in_len]InputBuffer,
798 OutputBuffers: [out_len]OutputBuffer,
799
800 pub fn init(
801 handle: ?HANDLE,
802 in: [in_len]InputBuffer,
803 out: [out_len]OutputBuffer,
804 ) @This() {
805 return .{
806 .Handle = handle,
807 .InputBuffersLength = in_len,
808 .OutputBuffersLength = out_len,
809 .InputBuffers = in,
810 .OutputBuffers = out,
811 };
812 }
813 };
814 }
815
816 pub const Header = extern struct {
817 Operation: Operation,
818 Size: u32,
819
820 pub fn With(comptime Data: type) type {
821 return extern struct {
822 Header: Header,
823 Data: Data,
824
825 pub fn init(operation: Operation, data: Data) @This() {
826 return .{
827 .Header = .{ .Operation = operation, .Size = @sizeOf(Data) },
828 .Data = data,
829 };
830 }
831
832 pub fn request(
833 with: *@This(),
834 file: ?Io.File,
835 comptime in_len: u32,
836 in: [in_len]InputBuffer,
837 comptime out_len: u32,
838 out: [out_len]OutputBuffer,
839 ) Request(1 + in_len, 1 + out_len) {
840 return .init(
841 if (file) |f| f.handle else null,
842 [1]InputBuffer{.{
843 .Size = @offsetOf(@This(), "Data") + @sizeOf(Data),
844 .Pointer = with,
845 }} ++ in,
846 [1]OutputBuffer{.{ .Size = @sizeOf(Data), .Pointer = &with.Data }} ++ out,
847 );
848 }
849
850 pub fn operate(with: *@This(), io: Io, file: ?Io.File) Io.Cancelable!NTSTATUS {
851 return (try io.operate(.{ .device_io_control = .{
852 .file = .{
853 .handle = peb().ProcessParameters.ConsoleHandle,
854 .flags = .{ .nonblocking = false },
855 },
856 .code = IOCTL.CONDRV.ISSUE_USER_IO,
857 .in = @ptrCast(&with.request(file, 0, .{}, 0, .{})),
858 } })).device_io_control.u.Status;
859 }
860 };
861 }
862 };
863
864 pub const Operation = enum(u32) {
865 GetCP = 0x1000000,
866 GetMode = 0x1000001,
867 SetMode = 0x1000002,
868 Read = 0x1000005,
869 Write = 0x1000006,
870 Fill = 0x2000000,
871 SetCP = 0x2000004,
872 GetScreenBufferInfo = 0x2000007,
873 SetCursorPosition = 0x200000a,
874 SetTextAttribute = 0x200000d,
875 ReadOutputCharacter = 0x200000f,
876 _,
877 };
878 };
879};
880
652881// ref: km/ntddk.h
653882
654883pub const PROCESSINFOCLASS = enum(c_int) {
......@@ -1160,6 +1389,22 @@ pub const CTL_CODE = packed struct(ULONG) {
11601389};
11611390
11621391pub const IOCTL = struct {
1392 pub const CONDRV = struct {
1393 pub const READ_IO: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 1, .Method = .OUT_DIRECT, .Access = .ANY };
1394 pub const COMPLETE_IO: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 2, .Method = .NEITHER, .Access = .ANY };
1395 pub const READ_INPUT: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 3, .Method = .NEITHER, .Access = .ANY };
1396 pub const WRITE_OUTPUT: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 4, .Method = .NEITHER, .Access = .ANY };
1397 pub const ISSUE_USER_IO: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 5, .Method = .OUT_DIRECT, .Access = .ANY };
1398 pub const DISCONNECT_PIPE: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 6, .Method = .NEITHER, .Access = .ANY };
1399 pub const SET_SERVER_INFORMATION: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 7, .Method = .NEITHER, .Access = .ANY };
1400 pub const GET_SERVER_PID: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 8, .Method = .NEITHER, .Access = .ANY };
1401 pub const GET_DISPLAY_SIZE: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 9, .Method = .NEITHER, .Access = .ANY };
1402 pub const UPDATE_DISPLAY: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 10, .Method = .NEITHER, .Access = .ANY };
1403 pub const SET_CURSOR: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 11, .Method = .NEITHER, .Access = .ANY };
1404 pub const ALLOW_VIA_UIACCESS: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 12, .Method = .NEITHER, .Access = .ANY };
1405 pub const LAUNCH_SERVER: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 13, .Method = .NEITHER, .Access = .ANY };
1406 pub const GET_FONT_SIZE: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 14, .Method = .NEITHER, .Access = .ANY };
1407 };
11631408 pub const KSEC = struct {
11641409 pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
11651410 };
......@@ -2663,29 +2908,6 @@ pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_
26632908 };
26642909}
26652910
2666pub const SetConsoleTextAttributeError = error{Unexpected};
2667
2668pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
2669 if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
2670 switch (GetLastError()) {
2671 else => |err| return unexpectedError(err),
2672 }
2673 }
2674}
2675
2676pub fn SetConsoleCtrlHandler(handler_routine: ?HANDLER_ROUTINE, add: bool) !void {
2677 const success = kernel32.SetConsoleCtrlHandler(
2678 handler_routine,
2679 if (add) TRUE else FALSE,
2680 );
2681
2682 if (success == FALSE) {
2683 return switch (GetLastError()) {
2684 else => |err| unexpectedError(err),
2685 };
2686 }
2687}
2688
26892911pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
26902912 const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
26912913 if (success == FALSE) {
......@@ -3244,6 +3466,7 @@ pub const ULONGLONG = u64;
32443466pub const LONGLONG = i64;
32453467pub const HLOCAL = HANDLE;
32463468pub const LANGID = c_ushort;
3469pub const COLORREF = DWORD;
32473470
32483471pub const WPARAM = usize;
32493472pub const LPARAM = LONG_PTR;
......@@ -3784,21 +4007,17 @@ pub const FileNotifyChangeFilter = packed struct(DWORD) {
37844007 _pad: u20 = 0,
37854008};
37864009
3787pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
3788 dwSize: COORD,
3789 dwCursorPosition: COORD,
3790 wAttributes: WORD,
3791 srWindow: SMALL_RECT,
3792 dwMaximumWindowSize: COORD,
3793};
3794
37954010pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
37964011pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
37974012
3798pub const FOREGROUND_BLUE = 1;
3799pub const FOREGROUND_GREEN = 2;
3800pub const FOREGROUND_RED = 4;
3801pub const FOREGROUND_INTENSITY = 8;
4013pub const FOREGROUND_BLUE = 0x0001;
4014pub const FOREGROUND_GREEN = 0x0002;
4015pub const FOREGROUND_RED = 0x0004;
4016pub const FOREGROUND_INTENSITY = 0x0008;
4017pub const BACKGROUND_BLUE = 0x0010;
4018pub const BACKGROUND_GREEN = 0x0020;
4019pub const BACKGROUND_RED = 0x0040;
4020pub const BACKGROUND_INTENSITY = 0x0080;
38024021
38034022pub const LIST_ENTRY = extern struct {
38044023 Flink: *LIST_ENTRY,
lib/std/os/windows/kernel32.zig-84
......@@ -4,7 +4,6 @@ const windows = std.os.windows;
44const ACCESS_MASK = windows.ACCESS_MASK;
55const BOOL = windows.BOOL;
66const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
7const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO;
87const COORD = windows.COORD;
98const DWORD = windows.DWORD;
109const FARPROC = windows.FARPROC;
......@@ -191,89 +190,6 @@ pub extern "kernel32" fn CreateThread(
191190 lpThreadId: ?*DWORD,
192191) callconv(.winapi) ?HANDLE;
193192
194// Locks, critical sections, initializers
195
196// TODO:
197// - dwMilliseconds -> LARGE_INTEGER.
198// - RtlSleepConditionVariableSRW
199// - return rc != .TIMEOUT
200pub extern "kernel32" fn SleepConditionVariableSRW(
201 ConditionVariable: *CONDITION_VARIABLE,
202 SRWLock: *SRWLOCK,
203 dwMilliseconds: DWORD,
204 Flags: ULONG,
205) callconv(.winapi) BOOL;
206
207// Console management
208
209pub extern "kernel32" fn GetConsoleMode(
210 hConsoleHandle: HANDLE,
211 lpMode: *DWORD,
212) callconv(.winapi) BOOL;
213
214pub extern "kernel32" fn SetConsoleMode(
215 hConsoleHandle: HANDLE,
216 dwMode: DWORD,
217) callconv(.winapi) BOOL;
218
219pub extern "kernel32" fn GetConsoleScreenBufferInfo(
220 hConsoleOutput: HANDLE,
221 lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO,
222) callconv(.winapi) BOOL;
223
224pub extern "kernel32" fn SetConsoleTextAttribute(
225 hConsoleOutput: HANDLE,
226 wAttributes: WORD,
227) callconv(.winapi) BOOL;
228
229pub extern "kernel32" fn SetConsoleCtrlHandler(
230 HandlerRoutine: ?HANDLER_ROUTINE,
231 Add: BOOL,
232) callconv(.winapi) BOOL;
233
234pub extern "kernel32" fn SetConsoleOutputCP(
235 wCodePageID: UINT,
236) callconv(.winapi) BOOL;
237
238pub extern "kernel32" fn GetConsoleOutputCP() callconv(.winapi) UINT;
239
240pub extern "kernel32" fn FillConsoleOutputAttribute(
241 hConsoleOutput: HANDLE,
242 wAttribute: WORD,
243 nLength: DWORD,
244 dwWriteCoord: COORD,
245 lpNumberOfAttrsWritten: *DWORD,
246) callconv(.winapi) BOOL;
247
248pub extern "kernel32" fn FillConsoleOutputCharacterW(
249 hConsoleOutput: HANDLE,
250 cCharacter: WCHAR,
251 nLength: DWORD,
252 dwWriteCoord: COORD,
253 lpNumberOfCharsWritten: *DWORD,
254) callconv(.winapi) BOOL;
255
256pub extern "kernel32" fn SetConsoleCursorPosition(
257 hConsoleOutput: HANDLE,
258 dwCursorPosition: COORD,
259) callconv(.winapi) BOOL;
260
261pub extern "kernel32" fn WriteConsoleW(
262 hConsoleOutput: HANDLE,
263 lpBuffer: [*]const u16,
264 nNumberOfCharsToWrite: DWORD,
265 lpNumberOfCharsWritten: ?*DWORD,
266 lpReserved: ?LPVOID,
267) callconv(.winapi) BOOL;
268
269pub extern "kernel32" fn ReadConsoleOutputCharacterW(
270 hConsoleOutput: HANDLE,
271 lpCharacter: [*]u16,
272 nLength: DWORD,
273 dwReadCoord: COORD,
274 lpNumberOfCharsRead: *DWORD,
275) callconv(.winapi) BOOL;
276
277193// Code Libraries/Modules
278194
279195// TODO: Wrapper around LdrGetDllFullName.
lib/std/std.zig+4-2
......@@ -135,8 +135,6 @@ pub const Options = struct {
135135 args: anytype,
136136 ) void = log.defaultLog,
137137
138 logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode,
139
140138 /// Overrides `std.heap.page_size_min`.
141139 page_size_min: ?usize = null,
142140 /// Overrides `std.heap.page_size_max`.
......@@ -176,6 +174,10 @@ pub const Options = struct {
176174 /// stack traces will just print an error to the relevant `Io.Writer` and return.
177175 allow_stack_tracing: bool = !@import("builtin").strip_debug_info,
178176
177 /// TODO This is a separate decl instead of a field as a workaround around
178 /// compilation errors due to zig not being lazy enough.
179 pub const logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode;
180
179181 /// TODO This is a separate decl instead of a field as a workaround around
180182 /// compilation errors due to zig not being lazy enough.
181183 pub const elf_debug_info_search_paths: ?fn (exe_path: []const u8) switch (@import("builtin").object_format) {
src/libs/mingw.zig+1-1
......@@ -347,7 +347,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
347347 if (msg.kind == .@"fatal error" or msg.kind == .@"error") {
348348 msg.write(stderr.terminal(), true) catch |err| switch (err) {
349349 error.WriteFailed => return stderr.file_writer.err.?,
350 error.Unexpected => |e| return e,
350 error.Canceled, error.Unexpected => |e| return e,
351351 };
352352 return error.AroPreprocessorFailed;
353353 }