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) {...@@ -335,11 +335,9 @@ pub const Operation = union(enum) {
335 .wasi => noreturn,335 .wasi => noreturn,
336 .windows => struct {336 .windows => struct {
337 file: File,337 file: File,
338 IoControlCode: std.os.windows.CTL_CODE,338 code: std.os.windows.CTL_CODE,
339 InputBuffer: ?*const anyopaque,339 in: []const u8 = &.{},
340 InputBufferLength: u32,340 out: []u8 = &.{},
341 OutputBuffer: ?*anyopaque,
342 OutputBufferLength: u32,
343341
344 pub const Result = std.os.windows.IO_STATUS_BLOCK;342 pub const Result = std.os.windows.IO_STATUS_BLOCK;
345 },343 },
lib/std/Io/Terminal.zig+16-10
...@@ -40,7 +40,8 @@ pub const Mode = union(enum) {...@@ -40,7 +40,8 @@ pub const Mode = union(enum) {
40 windows_api: WindowsApi,40 windows_api: WindowsApi,
4141
42 pub const WindowsApi = if (!is_windows) noreturn else struct {42 pub const WindowsApi = if (!is_windows) noreturn else struct {
43 handle: File.Handle,43 io: Io,
44 file: File,
44 reset_attributes: u16,45 reset_attributes: u16,
45 };46 };
4647
...@@ -65,20 +66,21 @@ pub const Mode = union(enum) {...@@ -65,20 +66,21 @@ pub const Mode = union(enum) {
65 }66 }
6667
67 if (is_windows and try file.isTty(io)) {68 if (is_windows and try file.isTty(io)) {
68 const windows = std.os.windows;69 var get_console_info = std.os.windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
69 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;70 switch (try get_console_info.operate(io, file)) {
70 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) {71 .SUCCESS => return .{ .windows_api = .{
71 return .{ .windows_api = .{72 .io = io,
72 .handle = file.handle,73 .file = file,
73 .reset_attributes = info.wAttributes,74 .reset_attributes = get_console_info.Data.wAttributes,
74 } };75 } },
76 else => {},
75 }77 }
76 }78 }
77 return if (force_color == true) .escape_codes else .no_color;79 return if (force_color == true) .escape_codes else .no_color;
78 }80 }
79};81};
8082
81pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error;83pub const SetColorError = Io.Cancelable || Io.UnexpectedError || Io.Writer.Error;
8284
83pub fn setColor(t: Terminal, color: Color) SetColorError!void {85pub fn setColor(t: Terminal, color: Color) SetColorError!void {
84 switch (t.mode) {86 switch (t.mode) {
...@@ -132,7 +134,11 @@ pub fn setColor(t: Terminal, color: Color) SetColorError!void {...@@ -132,7 +134,11 @@ pub fn setColor(t: Terminal, color: Color) SetColorError!void {
132 .reset => wa.reset_attributes,134 .reset => wa.reset_attributes,
133 };135 };
134 try t.writer.flush();136 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 }
136 },142 },
137 }143 }
138}144}
lib/std/Io/Threaded.zig+93-115
...@@ -3083,19 +3083,23 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren...@@ -3083,19 +3083,23 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
3083 }3083 }
3084 },3084 },
3085 .device_io_control => |o| {3085 .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 };
3086 if (o.file.flags.nonblocking) {3090 if (o.file.flags.nonblocking) {
3087 context.file = o.file.handle;3091 context.file = o.file.handle;
3088 switch (windows.ntdll.NtDeviceIoControlFile(3092 switch (NtControlFile(
3089 o.file.handle,3093 o.file.handle,
3090 null, // event3094 null, // event
3091 &batchApc,3095 &batchApc,
3092 b,3096 b,
3093 &context.iosb,3097 &context.iosb,
3094 o.IoControlCode,3098 o.code,
3095 o.InputBuffer,3099 if (o.in.len > 0) o.in.ptr else null,
3096 o.InputBufferLength,3100 @intCast(o.in.len),
3097 o.OutputBuffer,3101 if (o.out.len > 0) o.out.ptr else null,
3098 o.OutputBufferLength,3102 @intCast(o.out.len),
3099 )) {3103 )) {
3100 .PENDING, .SUCCESS => {},3104 .PENDING, .SUCCESS => {},
3101 .CANCELLED => unreachable,3105 .CANCELLED => unreachable,
...@@ -3108,17 +3112,17 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren...@@ -3108,17 +3112,17 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
3108 if (concurrency) return error.ConcurrencyUnavailable;3112 if (concurrency) return error.ConcurrencyUnavailable;
31093113
3110 const syscall: Syscall = try .start();3114 const syscall: Syscall = try .start();
3111 while (true) switch (windows.ntdll.NtDeviceIoControlFile(3115 while (true) switch (NtControlFile(
3112 o.file.handle,3116 o.file.handle,
3113 null, // event3117 null, // event
3114 null, // APC routine3118 null, // APC routine
3115 null, // APC context3119 null, // APC context
3116 &context.iosb,3120 &context.iosb,
3117 o.IoControlCode,3121 o.code,
3118 o.InputBuffer,3122 if (o.in.len > 0) o.in.ptr else null,
3119 o.InputBufferLength,3123 @intCast(o.in.len),
3120 o.OutputBuffer,3124 if (o.out.len > 0) o.out.ptr else null,
3121 o.OutputBufferLength,3125 @intCast(o.out.len),
3122 )) {3126 )) {
3123 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag3127 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
3124 .CANCELLED => {3128 .CANCELLED => {
...@@ -8547,29 +8551,24 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {...@@ -8547,29 +8551,24 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
85478551
8548fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {8552fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
8549 const t: *Threaded = @ptrCast(@alignCast(userdata));8553 const t: *Threaded = @ptrCast(@alignCast(userdata));
8550 _ = t;8554 return t.isTty(file);
8551 return isTty(file);
8552}8555}
85538556
8554fn isTty(file: File) Io.Cancelable!bool {8557fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
8555 if (is_windows) {8558 if (is_windows) {
8556 if (try isCygwinPty(file)) return true;8559 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8557 var out: windows.DWORD = undefined;8560 switch ((try t.deviceIoControl(&.{
8558 const syscall: Syscall = try .start();8561 .file = .{
8559 while (windows.kernel32.GetConsoleMode(file.handle, &out) == 0) {8562 .handle = windows.peb().ProcessParameters.ConsoleHandle,
8560 switch (windows.GetLastError()) {8563 .flags = .{ .nonblocking = false },
8561 .OPERATION_ABORTED => {8564 },
8562 try syscall.checkCancel();8565 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8563 continue;8566 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
8564 },8567 })).u.Status) {
8565 else => {8568 .SUCCESS => return true,
8566 syscall.finish();8569 .INVALID_HANDLE => return isCygwinPty(file),
8567 return false;8570 else => return false,
8568 },
8569 }
8570 }8571 }
8571 syscall.finish();
8572 return true;
8573 }8572 }
85748573
8575 if (builtin.link_libc) {8574 if (builtin.link_libc) {
...@@ -8637,35 +8636,26 @@ fn isTty(file: File) Io.Cancelable!bool {...@@ -8637,35 +8636,26 @@ fn isTty(file: File) Io.Cancelable!bool {
86378636
8638fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {8637fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
8639 const t: *Threaded = @ptrCast(@alignCast(userdata));8638 const t: *Threaded = @ptrCast(@alignCast(userdata));
8640 _ = t;
86418639
8642 if (!is_windows) {8640 if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
8643 if (try supportsAnsiEscapeCodes(file)) return;
8644 return error.NotTerminalDevice;
8645 }
86468641
8647 // For Windows Terminal, VT Sequences processing is enabled by default.8642 // For Windows Terminal, VT Sequences processing is enabled by default.
8648 var original_console_mode: windows.DWORD = 0;8643 const console: File = .{
86498644 .handle = windows.peb().ProcessParameters.ConsoleHandle,
8650 {8645 .flags = .{ .nonblocking = false },
8651 const syscall: Syscall = try .start();8646 };
8652 while (windows.kernel32.GetConsoleMode(file.handle, &original_console_mode) == 0) {8647 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
8653 switch (windows.GetLastError()) {8648 switch ((try t.deviceIoControl(&.{
8654 .OPERATION_ABORTED => {8649 .file = console,
8655 try syscall.checkCancel();8650 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8656 continue;8651 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
8657 },8652 })).u.Status) {
8658 else => {8653 .SUCCESS => {},
8659 syscall.finish();8654 .INVALID_HANDLE => return if (!try isCygwinPty(file)) error.NotTerminalDevice,
8660 if (try isCygwinPty(file)) return;8655 else => return error.NotTerminalDevice,
8661 return error.NotTerminalDevice;
8662 },
8663 }
8664 }
8665 syscall.finish();
8666 }8656 }
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
8670 // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.8660 // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
8671 // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/8661 // 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...@@ -8678,58 +8668,40 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
8678 // Additionally, the default console mode in Windows Terminal does not have8668 // Additionally, the default console mode in Windows Terminal does not have
8679 // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`8669 // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`
8680 // we end up matching the mode of Windows Terminal.8670 // we end up matching the mode of Windows Terminal.
8681 const requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;8671 var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
8682 const console_mode = original_console_mode | requested_console_modes;8672 get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
86838673 );
8684 {8674 switch ((try t.deviceIoControl(&.{
8685 const syscall: Syscall = try .start();8675 .file = console,
8686 while (windows.kernel32.SetConsoleMode(file.handle, console_mode) == 0) {8676 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8687 switch (windows.GetLastError()) {8677 .in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
8688 .OPERATION_ABORTED => {8678 })).u.Status) {
8689 try syscall.checkCancel();8679 .SUCCESS => {},
8690 continue;8680 else => |status| return windows.unexpectedStatus(status),
8691 },
8692 else => {
8693 syscall.finish();
8694 if (try isCygwinPty(file)) return;
8695 return error.NotTerminalDevice;
8696 },
8697 }
8698 }
8699 syscall.finish();
8700 }8681 }
8701}8682}
87028683
8703fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {8684fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
8704 const t: *Threaded = @ptrCast(@alignCast(userdata));8685 const t: *Threaded = @ptrCast(@alignCast(userdata));
8705 _ = t;8686 return t.supportsAnsiEscapeCodes(file);
8706 return supportsAnsiEscapeCodes(file);
8707}8687}
87088688
8709fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {8689fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
8710 if (is_windows) {8690 if (is_windows) {
8711 var console_mode: windows.DWORD = 0;8691 var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
87128692 switch ((try t.deviceIoControl(&.{
8713 const syscall: Syscall = try .start();8693 .file = .{
8714 while (windows.kernel32.GetConsoleMode(file.handle, &console_mode) == 0) {8694 .handle = windows.peb().ProcessParameters.ConsoleHandle,
8715 switch (windows.GetLastError()) {8695 .flags = .{ .nonblocking = false },
8716 .OPERATION_ABORTED => {8696 },
8717 try syscall.checkCancel();8697 .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
8718 continue;8698 .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
8719 },8699 })).u.Status) {
8720 else => {8700 .SUCCESS => if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0)
8721 syscall.finish();8701 return true,
8722 break;8702 .INVALID_HANDLE => return isCygwinPty(file),
8723 },8703 else => return false,
8724 }
8725 } else {
8726 syscall.finish();
8727 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) {
8728 return true;
8729 }
8730 }8704 }
8731
8732 return isCygwinPty(file);
8733 }8705 }
87348706
8735 if (native_os == .wasi) {8707 if (native_os == .wasi) {
...@@ -8739,7 +8711,7 @@ fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {...@@ -8739,7 +8711,7 @@ fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
8739 return false;8711 return false;
8740 }8712 }
87418713
8742 if (try isTty(file)) return true;8714 if (try t.isTty(file)) return true;
87438715
8744 return false;8716 return false;
8745}8717}
...@@ -14111,12 +14083,14 @@ fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelabl...@@ -14111,12 +14083,14 @@ fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelabl
1411114083
14112fn unlockStderr(userdata: ?*anyopaque) void {14084fn unlockStderr(userdata: ?*anyopaque) void {
14113 const t: *Threaded = @ptrCast(@alignCast(userdata));14085 const t: *Threaded = @ptrCast(@alignCast(userdata));
14114 t.stderr_writer.interface.flush() catch |err| switch (err) {14086 if (t.stderr_writer.err == null) t.stderr_writer.interface.flush() catch {};
14115 error.WriteFailed => switch (t.stderr_writer.err.?) {14087 if (t.stderr_writer.err) |err| {
14088 switch (err) {
14116 error.Canceled => recancelInner(),14089 error.Canceled => recancelInner(),
14117 else => {},14090 else => {},
14118 },14091 }
14119 };14092 t.stderr_writer.err = null;
14093 }
14120 t.stderr_writer.interface.end = 0;14094 t.stderr_writer.interface.end = 0;
14121 t.stderr_writer.interface.buffer = &.{};14095 t.stderr_writer.interface.buffer = &.{};
1412214096
...@@ -18848,20 +18822,24 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!...@@ -18848,20 +18822,24 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
18848fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {18822fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
18849 _ = t;18823 _ = t;
18850 if (is_windows) {18824 if (is_windows) {
18825 const NtControlFile = switch (o.code.DeviceType) {
18826 .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
18827 else => &windows.ntdll.NtDeviceIoControlFile,
18828 };
18851 var iosb: windows.IO_STATUS_BLOCK = undefined;18829 var iosb: windows.IO_STATUS_BLOCK = undefined;
18852 if (o.file.flags.nonblocking) {18830 if (o.file.flags.nonblocking) {
18853 var done: bool = false;18831 var done: bool = false;
18854 switch (windows.ntdll.NtDeviceIoControlFile(18832 switch (NtControlFile(
18855 o.file.handle,18833 o.file.handle,
18856 null, // event18834 null, // event
18857 flagApc,18835 flagApc,
18858 &done, // APC context18836 &done, // APC context
18859 &iosb,18837 &iosb,
18860 o.IoControlCode,18838 o.code,
18861 o.InputBuffer,18839 if (o.in.len > 0) o.in.ptr else null,
18862 o.InputBufferLength,18840 @intCast(o.in.len),
18863 o.OutputBuffer,18841 if (o.out.len > 0) o.out.ptr else null,
18864 o.OutputBufferLength,18842 @intCast(o.out.len),
18865 )) {18843 )) {
18866 // We must wait for the APC routine.18844 // We must wait for the APC routine.
18867 .PENDING, .SUCCESS => while (!done) {18845 .PENDING, .SUCCESS => while (!done) {
...@@ -18882,17 +18860,17 @@ fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Canc...@@ -18882,17 +18860,17 @@ fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Canc
18882 }18860 }
18883 } else {18861 } else {
18884 const syscall: Syscall = try .start();18862 const syscall: Syscall = try .start();
18885 while (true) switch (windows.ntdll.NtDeviceIoControlFile(18863 while (true) switch (NtControlFile(
18886 o.file.handle,18864 o.file.handle,
18887 null, // event18865 null, // event
18888 null, // APC routine18866 null, // APC routine
18889 null, // APC context18867 null, // APC context
18890 &iosb,18868 &iosb,
18891 o.IoControlCode,18869 o.code,
18892 o.InputBuffer,18870 if (o.in.len > 0) o.in.ptr else null,
18893 o.InputBufferLength,18871 @intCast(o.in.len),
18894 o.OutputBuffer,18872 if (o.out.len > 0) o.out.ptr else null,
18895 o.OutputBufferLength,18873 @intCast(o.out.len),
18896 )) {18874 )) {
18897 .PENDING => unreachable, // unrecoverable: wrong asynchronous flag18875 .PENDING => unreachable, // unrecoverable: wrong asynchronous flag
18898 .CANCELLED => {18876 .CANCELLED => {
lib/std/Progress.zig+102-69
...@@ -157,7 +157,7 @@ pub const TerminalMode = union(enum) {...@@ -157,7 +157,7 @@ pub const TerminalMode = union(enum) {
157 ansi_escape_codes,157 ansi_escape_codes,
158 /// This is not the same as being run on windows because other terminals158 /// This is not the same as being run on windows because other terminals
159 /// exist like MSYS/git-bash.159 /// exist like MSYS/git-bash.
160 windows_api: if (is_windows) WindowsApi else void,160 windows_api: if (is_windows) WindowsApi else noreturn,
161161
162 pub const WindowsApi = struct {162 pub const WindowsApi = struct {
163 /// The output code page of the console.163 /// The output code page of the console.
...@@ -614,33 +614,39 @@ pub fn start(io: Io, options: Options) Node {...@@ -614,33 +614,39 @@ pub fn start(io: Io, options: Options) Node {
614 if (stderr.enableAnsiEscapeCodes(io)) |_| {614 if (stderr.enableAnsiEscapeCodes(io)) |_| {
615 global_progress.terminal_mode = .ansi_escape_codes;615 global_progress.terminal_mode = .ansi_escape_codes;
616 } else |_| if (is_windows) {616 } else |_| if (is_windows) {
617 if (stderr.isTty(io)) |is_tty| {617 var get_console_cp = windows.CONSOLE.USER_IO.GET_CP(.Output);
618 if (is_tty) global_progress.terminal_mode = TerminalMode{ .windows_api = .{618 // Normally, we would pass `null` to `operate` here as the kernel32
619 .code_page = windows.kernel32.GetConsoleOutputCP(),619 // function does not accept a handle, however, if we pass one anyway,
620 } };620 // then we will get an error if the handle is not associated with
621 } else |err| switch (err) {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) {
622 error.Canceled => {624 error.Canceled => {
623 io.recancel();625 io.recancel();
624 return .none;626 return .none;
625 },627 },
628 }) {
629 .SUCCESS => global_progress.terminal_mode = .{ .windows_api = .{
630 .code_page = get_console_cp.Data.CodePage,
631 } },
632 .INVALID_HANDLE => {},
633 else => {},
626 }634 }
627 }635 }
628636 if (future: switch (global_progress.terminal_mode) {
629 if (global_progress.terminal_mode == .off) return .none;637 .off => return .none,
630638 .ansi_escape_codes => {
631 if (have_sigwinch) {639 if (have_sigwinch) {
632 const act: posix.Sigaction = .{640 const act: posix.Sigaction = .{
633 .handler = .{ .sigaction = handleSigWinch },641 .handler = .{ .sigaction = handleSigWinch },
634 .mask = posix.sigemptyset(),642 .mask = posix.sigemptyset(),
635 .flags = (posix.SA.SIGINFO | posix.SA.RESTART),643 .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
636 };644 };
637 posix.sigaction(.WINCH, &act, null);645 posix.sigaction(.WINCH, &act, null);
638 }646 }
639647 break :future io.concurrent(updateTask, .{io});
640 if (switch (global_progress.terminal_mode) {648 },
641 .off => unreachable, // handled a few lines above649 .windows_api => io.concurrent(windowsApiUpdateTask, .{io}),
642 .ansi_escape_codes => io.concurrent(updateTask, .{io}),
643 .windows_api => if (is_windows) io.concurrent(windowsApiUpdateTask, .{io}) else unreachable,
644 }) |future| {650 }) |future| {
645 global_progress.update_worker = future;651 global_progress.update_worker = future;
646 } else |err| {652 } else |err| {
...@@ -715,12 +721,24 @@ fn updateTask(io: Io) WorkerError!void {...@@ -715,12 +721,24 @@ fn updateTask(io: Io) WorkerError!void {
715 }721 }
716}722}
717723
718fn windowsApiWriteMarker() void {724const WindowsApiError = Io.Cancelable || Io.UnexpectedError;
725
726fn windowsApiWriteMarker(io: Io) WindowsApiError!void {
719 // Write the marker that we will use to find the beginning of the progress when clearing.727 // Write the marker that we will use to find the beginning of the progress when clearing.
720 // Note: This doesn't have to use WriteConsoleW, but doing so avoids dealing with the code page.728 // 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;729 const terminal = global_progress.terminal;
722 const handle = global_progress.terminal.handle;730 var write_console = windows.CONSOLE.USER_IO.WRITE(.WideCharacter);
723 _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null);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 }
724}742}
725743
726fn windowsApiUpdateTask(io: Io) WorkerError!void {744fn windowsApiUpdateTask(io: Io) WorkerError!void {
...@@ -743,19 +761,19 @@ fn windowsApiUpdateTask(io: Io) WorkerError!void {...@@ -743,19 +761,19 @@ fn windowsApiUpdateTask(io: Io) WorkerError!void {
743 error.Canceled => unreachable, // blocked761 error.Canceled => unreachable, // blocked
744 };762 };
745 defer io.unlockStderr();763 defer io.unlockStderr();
746 clearWrittenWindowsApi() catch {};764 clearWrittenWindowsApi(io) catch {};
747 }765 }
748 while (true) {766 while (true) {
749 const buffer, const nl_n = try computeRedraw(io, &serialized_buffer);767 const buffer, const nl_n = try computeRedraw(io, &serialized_buffer);
750 if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {768 if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
751 defer io.unlockStderr();769 defer io.unlockStderr();
752 try clearWrittenWindowsApi();770 try clearWrittenWindowsApi(io);
753 windowsApiWriteMarker();771 try windowsApiWriteMarker(io);
754 global_progress.need_clear = true;772 global_progress.need_clear = true;
755 locked_stderr.file_writer.interface.writeAll(buffer) catch |err| switch (err) {773 locked_stderr.file_writer.interface.writeAll(buffer) catch |err| switch (err) {
756 error.WriteFailed => return locked_stderr.file_writer.err.?,774 error.WriteFailed => return locked_stderr.file_writer.err.?,
757 };775 };
758 windowsApiMoveToMarker(nl_n) catch return;776 windowsApiMoveToMarker(io, nl_n) catch return;
759 }777 }
760778
761 try maybeUpdateSize(io, try wait(io, global_progress.refresh_rate_ns));779 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 {...@@ -859,7 +877,7 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize {
859 return start_i + bytes.len;877 return start_i + bytes.len;
860 },878 },
861 .windows_api => |windows_api| {879 .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) {
863 // Code page 437 is the default code page and contains the box drawing symbols881 // Code page 437 is the default code page and contains the box drawing symbols
864 437 => symbol.bytes(.code_page_437),882 437 => symbol.bytes(.code_page_437),
865 // UTF-8883 // UTF-8
...@@ -882,7 +900,7 @@ pub fn clearWrittenWithEscapeCodes(file_writer: *Io.File.Writer) Io.Writer.Error...@@ -882,7 +900,7 @@ pub fn clearWrittenWithEscapeCodes(file_writer: *Io.File.Writer) Io.Writer.Error
882/// U+25BA or â–º900/// U+25BA or â–º
883const windows_api_start_marker = 0x25BA;901const windows_api_start_marker = 0x25BA;
884902
885fn clearWrittenWindowsApi() error{Unexpected}!void {903fn clearWrittenWindowsApi(io: Io) WindowsApiError!void {
886 // This uses a 'marker' strategy. The idea is:904 // This uses a 'marker' strategy. The idea is:
887 // - Always write a marker (in this case U+25BA or â–º) at the beginning of the progress905 // - Always write a marker (in this case U+25BA or â–º) at the beginning of the progress
888 // - Get the current cursor position (at the end of the progress)906 // - Get the current cursor position (at the end of the progress)
...@@ -903,43 +921,60 @@ fn clearWrittenWindowsApi() error{Unexpected}!void {...@@ -903,43 +921,60 @@ fn clearWrittenWindowsApi() error{Unexpected}!void {
903 // character in order to be readable via ReadConsoleOutputAttribute. It doesn't seem921 // character in order to be readable via ReadConsoleOutputAttribute. It doesn't seem
904 // like any of the available attributes are invisible/benign.922 // like any of the available attributes are invisible/benign.
905 if (!global_progress.need_clear) return;923 if (!global_progress.need_clear) return;
906 const handle = global_progress.terminal.handle;924 const terminal = global_progress.terminal;
907 const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows;925 const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows;
908926
909 var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;927 var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
910 if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {928 switch (try get_console_info.operate(io, terminal)) {
911 return error.Unexpected;929 .SUCCESS => {},
930 else => |status| return windows.unexpectedStatus(status),
912 }931 }
913 var num_chars_written: windows.DWORD = undefined;932 var fill_spaces = windows.CONSOLE.USER_IO.FILL(
914 if (windows.kernel32.FillConsoleOutputCharacterW(handle, ' ', screen_area, console_info.dwCursorPosition, &num_chars_written) == 0) {933 .{ .WideCharacter = ' ' },
915 return error.Unexpected;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),
916 }940 }
917}941}
918942
919fn windowsApiMoveToMarker(nl_n: usize) error{Unexpected}!void {943fn windowsApiMoveToMarker(io: Io, nl_n: usize) WindowsApiError!void {
920 const handle = global_progress.terminal.handle;944 const terminal = global_progress.terminal;
921 var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;945 var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
922 if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {946 switch (try get_console_info.operate(io, terminal)) {
923 return error.Unexpected;947 .SUCCESS => {},
948 else => |status| return windows.unexpectedStatus(status),
924 }949 }
925 const cursor_pos = console_info.dwCursorPosition;950 const cursor_pos = get_console_info.Data.dwCursorPosition;
926 const expected_y = cursor_pos.Y - @as(i16, @intCast(nl_n));951 const expected_y = cursor_pos.Y - @as(i16, @intCast(nl_n));
927 var start_pos: windows.COORD = .{ .X = 0, .Y = expected_y };952 var start_pos: windows.COORD = .{ .X = 0, .Y = expected_y };
928 while (start_pos.Y >= 0) {953 while (start_pos.Y >= 0) : (start_pos.Y -= 1) {
929 var wchar: [1]u16 = undefined;954 var read_output_char = windows.CONSOLE.USER_IO.READ_OUTPUT_CHARACTER(start_pos, .WideCharacter);
930 var num_console_chars_read: windows.DWORD = undefined;955 var buffer: [1]windows.WCHAR = undefined;
931 if (windows.kernel32.ReadConsoleOutputCharacterW(handle, &wchar, wchar.len, start_pos, &num_console_chars_read) == 0) {956 switch ((try io.operate(.{ .device_io_control = .{
932 return error.Unexpected;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),
933 }968 }
934969 if (read_output_char.Data.nLength >= 1 and buffer[0] == windows_api_start_marker) break;
935 if (wchar[0] == windows_api_start_marker) break;
936 start_pos.Y -= 1;
937 } else {970 } else {
938 // If we couldn't find the marker, then just assume that no lines wrapped971 // If we couldn't find the marker, then just assume that no lines wrapped
939 start_pos = .{ .X = 0, .Y = expected_y };972 start_pos = .{ .X = 0, .Y = expected_y };
940 }973 }
941 if (windows.kernel32.SetConsoleCursorPosition(handle, start_pos) == 0) {974 var set_cursor_position = windows.CONSOLE.USER_IO.SET_CURSOR_POSITION(start_pos);
942 return error.Unexpected;975 switch (try set_cursor_position.operate(io, terminal)) {
976 .SUCCESS => {},
977 else => |status| return windows.unexpectedStatus(status),
943 }978 }
944}979}
945980
...@@ -1279,7 +1314,7 @@ fn computeRedraw(io: Io, serialized_buffer: *Serialized.Buffer) !struct { []u8,...@@ -1279,7 +1314,7 @@ fn computeRedraw(io: Io, serialized_buffer: *Serialized.Buffer) !struct { []u8,
1279 buf[i..][0..clear.len].* = clear.*;1314 buf[i..][0..clear.len].* = clear.*;
1280 i += clear.len;1315 i += clear.len;
1281 },1316 },
1282 .windows_api => if (!is_windows) unreachable,1317 .windows_api => {},
1283 }1318 }
12841319
1285 const root_node_index: Node.Index = @enumFromInt(0);1320 const root_node_index: Node.Index = @enumFromInt(0);
...@@ -1491,19 +1526,17 @@ fn maybeUpdateSize(io: Io, resize_flag: bool) !void {...@@ -1491,19 +1526,17 @@ fn maybeUpdateSize(io: Io, resize_flag: bool) !void {
1491 const file = global_progress.terminal;1526 const file = global_progress.terminal;
14921527
1493 if (is_windows) {1528 if (is_windows) {
1494 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;1529 var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
14951530 switch (try get_console_info.operate(io, file)) {
1496 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) {1531 .SUCCESS => {
1497 // In the old Windows console, dwSize.Y is the line count of the1532 global_progress.rows = @intCast(get_console_info.Data.dwWindowSize.Y);
1498 // entire scrollback buffer, so we use this instead so that we1533 global_progress.cols = @intCast(get_console_info.Data.dwWindowSize.X);
1499 // always get the size of the screen.1534 },
1500 const screen_height = info.srWindow.Bottom - info.srWindow.Top;1535 else => {
1501 global_progress.rows = @intCast(screen_height);1536 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
1502 global_progress.cols = @intCast(info.dwSize.X);1537 global_progress.rows = 25;
1503 } else {1538 global_progress.cols = 80;
1504 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});1539 },
1505 global_progress.rows = 25;
1506 global_progress.cols = 80;
1507 }1540 }
1508 } else {1541 } else {
1509 var winsize: posix.winsize = .{1542 var winsize: posix.winsize = .{
lib/std/log.zig+4-1
...@@ -80,7 +80,7 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool {...@@ -80,7 +80,7 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool {
80 return @intFromEnum(level) <= @intFromEnum(std.options.log_level);80 return @intFromEnum(level) <= @intFromEnum(std.options.log_level);
81}81}
8282
83pub const terminalMode = std.options.logTerminalMode;83pub const terminalMode = std.Options.logTerminalMode;
8484
85pub fn defaultTerminalMode() std.Io.Terminal.Mode {85pub fn defaultTerminalMode() std.Io.Terminal.Mode {
86 const stderr = std.debug.lockStderr(&.{}).terminal();86 const stderr = std.debug.lockStderr(&.{}).terminal();
...@@ -99,6 +99,9 @@ pub fn defaultLog(...@@ -99,6 +99,9 @@ pub fn defaultLog(
99 comptime format: []const u8,99 comptime format: []const u8,
100 args: anytype,100 args: anytype,
101) void {101) void {
102 const io = std.Options.debug_io;
103 const prev = io.swapCancelProtection(.blocked);
104 defer _ = io.swapCancelProtection(prev);
102 var buffer: [64]u8 = undefined;105 var buffer: [64]u8 = undefined;
103 const stderr = std.debug.lockStderr(&buffer).terminal();106 const stderr = std.debug.lockStderr(&buffer).terminal();
104 defer std.debug.unlockStderr();107 defer std.debug.unlockStderr();
lib/std/os/windows.zig+254-35
...@@ -649,6 +649,235 @@ pub const FILE = struct {...@@ -649,6 +649,235 @@ pub const FILE = struct {
649 };649 };
650};650};
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
652// ref: km/ntddk.h881// ref: km/ntddk.h
653882
654pub const PROCESSINFOCLASS = enum(c_int) {883pub const PROCESSINFOCLASS = enum(c_int) {
...@@ -1160,6 +1389,22 @@ pub const CTL_CODE = packed struct(ULONG) {...@@ -1160,6 +1389,22 @@ pub const CTL_CODE = packed struct(ULONG) {
1160};1389};
11611390
1162pub const IOCTL = struct {1391pub 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 };
1163 pub const KSEC = struct {1408 pub const KSEC = struct {
1164 pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };1409 pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
1165 };1410 };
...@@ -2663,29 +2908,6 @@ pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_...@@ -2663,29 +2908,6 @@ pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_
2663 };2908 };
2664}2909}
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
2689pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {2911pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
2690 const success = kernel32.SetFileCompletionNotificationModes(handle, flags);2912 const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
2691 if (success == FALSE) {2913 if (success == FALSE) {
...@@ -3244,6 +3466,7 @@ pub const ULONGLONG = u64;...@@ -3244,6 +3466,7 @@ pub const ULONGLONG = u64;
3244pub const LONGLONG = i64;3466pub const LONGLONG = i64;
3245pub const HLOCAL = HANDLE;3467pub const HLOCAL = HANDLE;
3246pub const LANGID = c_ushort;3468pub const LANGID = c_ushort;
3469pub const COLORREF = DWORD;
32473470
3248pub const WPARAM = usize;3471pub const WPARAM = usize;
3249pub const LPARAM = LONG_PTR;3472pub const LPARAM = LONG_PTR;
...@@ -3784,21 +4007,17 @@ pub const FileNotifyChangeFilter = packed struct(DWORD) {...@@ -3784,21 +4007,17 @@ pub const FileNotifyChangeFilter = packed struct(DWORD) {
3784 _pad: u20 = 0,4007 _pad: u20 = 0,
3785};4008};
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
3795pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;4010pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
3796pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;4011pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
37974012
3798pub const FOREGROUND_BLUE = 1;4013pub const FOREGROUND_BLUE = 0x0001;
3799pub const FOREGROUND_GREEN = 2;4014pub const FOREGROUND_GREEN = 0x0002;
3800pub const FOREGROUND_RED = 4;4015pub const FOREGROUND_RED = 0x0004;
3801pub const FOREGROUND_INTENSITY = 8;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
3803pub const LIST_ENTRY = extern struct {4022pub const LIST_ENTRY = extern struct {
3804 Flink: *LIST_ENTRY,4023 Flink: *LIST_ENTRY,
lib/std/os/windows/kernel32.zig-84
...@@ -4,7 +4,6 @@ const windows = std.os.windows;...@@ -4,7 +4,6 @@ const windows = std.os.windows;
4const ACCESS_MASK = windows.ACCESS_MASK;4const ACCESS_MASK = windows.ACCESS_MASK;
5const BOOL = windows.BOOL;5const BOOL = windows.BOOL;
6const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;6const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
7const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO;
8const COORD = windows.COORD;7const COORD = windows.COORD;
9const DWORD = windows.DWORD;8const DWORD = windows.DWORD;
10const FARPROC = windows.FARPROC;9const FARPROC = windows.FARPROC;
...@@ -191,89 +190,6 @@ pub extern "kernel32" fn CreateThread(...@@ -191,89 +190,6 @@ pub extern "kernel32" fn CreateThread(
191 lpThreadId: ?*DWORD,190 lpThreadId: ?*DWORD,
192) callconv(.winapi) ?HANDLE;191) 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
277// Code Libraries/Modules193// Code Libraries/Modules
278194
279// TODO: Wrapper around LdrGetDllFullName.195// TODO: Wrapper around LdrGetDllFullName.
lib/std/std.zig+4-2
...@@ -135,8 +135,6 @@ pub const Options = struct {...@@ -135,8 +135,6 @@ pub const Options = struct {
135 args: anytype,135 args: anytype,
136 ) void = log.defaultLog,136 ) void = log.defaultLog,
137137
138 logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode,
139
140 /// Overrides `std.heap.page_size_min`.138 /// Overrides `std.heap.page_size_min`.
141 page_size_min: ?usize = null,139 page_size_min: ?usize = null,
142 /// Overrides `std.heap.page_size_max`.140 /// Overrides `std.heap.page_size_max`.
...@@ -176,6 +174,10 @@ pub const Options = struct {...@@ -176,6 +174,10 @@ pub const Options = struct {
176 /// stack traces will just print an error to the relevant `Io.Writer` and return.174 /// stack traces will just print an error to the relevant `Io.Writer` and return.
177 allow_stack_tracing: bool = !@import("builtin").strip_debug_info,175 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
179 /// TODO This is a separate decl instead of a field as a workaround around181 /// TODO This is a separate decl instead of a field as a workaround around
180 /// compilation errors due to zig not being lazy enough.182 /// compilation errors due to zig not being lazy enough.
181 pub const elf_debug_info_search_paths: ?fn (exe_path: []const u8) switch (@import("builtin").object_format) {183 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 {...@@ -347,7 +347,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
347 if (msg.kind == .@"fatal error" or msg.kind == .@"error") {347 if (msg.kind == .@"fatal error" or msg.kind == .@"error") {
348 msg.write(stderr.terminal(), true) catch |err| switch (err) {348 msg.write(stderr.terminal(), true) catch |err| switch (err) {
349 error.WriteFailed => return stderr.file_writer.err.?,349 error.WriteFailed => return stderr.file_writer.err.?,
350 error.Unexpected => |e| return e,350 error.Canceled, error.Unexpected => |e| return e,
351 };351 };
352 return error.AroPreprocessorFailed;352 return error.AroPreprocessorFailed;
353 }353 }