| author | |
| committer | |
| log | c77e7146f5fa8e83c06cd6612b7298df06912974 |
| tree | c49c30a3bdeddddede96e5cb7c0d813342a34bda |
| parent | f1507599531e00f40fd31bbe39f2526e70241e10 |
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, | ||
| 343 | 341 | ||
| 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, |
| 41 | 41 | ||
| 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 | }; |
| 46 | 47 | ||
| ... | @@ -65,20 +66,21 @@ pub const Mode = union(enum) { | ... | @@ -65,20 +66,21 @@ pub const Mode = union(enum) { |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 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 | }; |
| 80 | 82 | ||
| 81 | pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; | 83 | pub const SetColorError = Io.Cancelable || Io.UnexpectedError || Io.Writer.Error; |
| 82 | 84 | ||
| 83 | pub fn setColor(t: Terminal, color: Color) SetColorError!void { | 85 | pub 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, // event | 3094 | 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; |
| 3109 | 3113 | ||
| 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, // event | 3117 | null, // event |
| 3114 | null, // APC routine | 3118 | null, // APC routine |
| 3115 | null, // APC context | 3119 | 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 flag | 3127 | .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 { |
| 8547 | 8551 | ||
| 8548 | fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool { | 8552 | fn 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 | } |
| 8553 | 8556 | ||
| 8554 | fn isTty(file: File) Io.Cancelable!bool { | 8557 | fn 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 | } |
| 8574 | 8573 | ||
| 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 { |
| 8637 | 8636 | ||
| 8638 | fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void { | 8637 | fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void { |
| 8639 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 8638 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 8640 | _ = t; | ||
| 8641 | 8639 | ||
| 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 | } | ||
| 8646 | 8641 | ||
| 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 = .{ |
| 8649 | 8644 | .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 | } |
| 8667 | 8657 | ||
| 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; |
| 8669 | 8659 | ||
| 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 have | 8668 | // 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, |
| 8683 | 8673 | ); | |
| 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 | } |
| 8702 | 8683 | ||
| 8703 | fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool { | 8684 | fn 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 | } |
| 8708 | 8688 | ||
| 8709 | fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool { | 8689 | fn 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; |
| 8712 | 8692 | 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 | } |
| 8734 | 8706 | ||
| 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 | } |
| 8741 | 8713 | ||
| 8742 | if (try isTty(file)) return true; | 8714 | if (try t.isTty(file)) return true; |
| 8743 | 8715 | ||
| 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 |
| 14111 | 14083 | ||
| 14112 | fn unlockStderr(userdata: ?*anyopaque) void { | 14084 | fn 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 = &.{}; |
| 14122 | 14096 | ||
| ... | @@ -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! |
| 18848 | fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result { | 18822 | fn 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, // event | 18834 | null, // event |
| 18857 | flagApc, | 18835 | flagApc, |
| 18858 | &done, // APC context | 18836 | &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, // event | 18865 | null, // event |
| 18888 | null, // APC routine | 18866 | null, // APC routine |
| 18889 | null, // APC context | 18867 | 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 flag | 18875 | .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 terminals | 158 | /// 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, |
| 161 | 161 | ||
| 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 | } |
| 628 | 636 | if (future: switch (global_progress.terminal_mode) { | |
| 629 | if (global_progress.terminal_mode == .off) return .none; | 637 | .off => return .none, |
| 630 | 638 | .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 | } |
| 639 | 647 | break :future io.concurrent(updateTask, .{io}); | |
| 640 | if (switch (global_progress.terminal_mode) { | 648 | }, |
| 641 | .off => unreachable, // handled a few lines above | 649 | .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 | } |
| 717 | 723 | ||
| 718 | fn windowsApiWriteMarker() void { | 724 | const WindowsApiError = Io.Cancelable || Io.UnexpectedError; |
| 725 | |||
| 726 | fn 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 | } |
| 725 | 743 | ||
| 726 | fn windowsApiUpdateTask(io: Io) WorkerError!void { | 744 | fn 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, // blocked | 761 | 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 | } |
| 760 | 778 | ||
| 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 symbols | 881 | // 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-8 | 883 | // 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 â–º |
| 883 | const windows_api_start_marker = 0x25BA; | 901 | const windows_api_start_marker = 0x25BA; |
| 884 | 902 | ||
| 885 | fn clearWrittenWindowsApi() error{Unexpected}!void { | 903 | fn 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 progress | 905 | // - 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 seem | 921 | // 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; |
| 908 | 926 | ||
| 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 | } |
| 918 | 942 | ||
| 919 | fn windowsApiMoveToMarker(nl_n: usize) error{Unexpected}!void { | 943 | fn 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 | } |
| 934 | 969 | 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 wrapped | 971 | // 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 | } |
| 945 | 980 | ||
| ... | @@ -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 | } |
| 1284 | 1319 | ||
| 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; |
| 1492 | 1527 | ||
| 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; |
| 1495 | 1530 | 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 the | 1532 | global_progress.rows = @intCast(get_console_info.Data.dwWindowSize.Y); |
| 1498 | // entire scrollback buffer, so we use this instead so that we | 1533 | 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 | } |
| 82 | 82 | ||
| 83 | pub const terminalMode = std.options.logTerminalMode; | 83 | pub const terminalMode = std.Options.logTerminalMode; |
| 84 | 84 | ||
| 85 | pub fn defaultTerminalMode() std.Io.Terminal.Mode { | 85 | pub 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 | }; |
| 651 | 651 | ||
| 652 | pub 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.h | 881 | // ref: km/ntddk.h |
| 653 | 882 | ||
| 654 | pub const PROCESSINFOCLASS = enum(c_int) { | 883 | pub 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 | }; |
| 1161 | 1390 | ||
| 1162 | pub const IOCTL = struct { | 1391 | pub 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 | } |
| 2665 | 2910 | ||
| 2666 | pub const SetConsoleTextAttributeError = error{Unexpected}; | ||
| 2667 | |||
| 2668 | pub 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 | |||
| 2676 | pub 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 | |||
| 2689 | pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void { | 2911 | pub 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; |
| 3244 | pub const LONGLONG = i64; | 3466 | pub const LONGLONG = i64; |
| 3245 | pub const HLOCAL = HANDLE; | 3467 | pub const HLOCAL = HANDLE; |
| 3246 | pub const LANGID = c_ushort; | 3468 | pub const LANGID = c_ushort; |
| 3469 | pub const COLORREF = DWORD; | ||
| 3247 | 3470 | ||
| 3248 | pub const WPARAM = usize; | 3471 | pub const WPARAM = usize; |
| 3249 | pub const LPARAM = LONG_PTR; | 3472 | pub 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 | }; |
| 3786 | 4009 | ||
| 3787 | pub 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 | |||
| 3795 | pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4; | 4010 | pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4; |
| 3796 | pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8; | 4011 | pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8; |
| 3797 | 4012 | ||
| 3798 | pub const FOREGROUND_BLUE = 1; | 4013 | pub const FOREGROUND_BLUE = 0x0001; |
| 3799 | pub const FOREGROUND_GREEN = 2; | 4014 | pub const FOREGROUND_GREEN = 0x0002; |
| 3800 | pub const FOREGROUND_RED = 4; | 4015 | pub const FOREGROUND_RED = 0x0004; |
| 3801 | pub const FOREGROUND_INTENSITY = 8; | 4016 | pub const FOREGROUND_INTENSITY = 0x0008; |
| 4017 | pub const BACKGROUND_BLUE = 0x0010; | ||
| 4018 | pub const BACKGROUND_GREEN = 0x0020; | ||
| 4019 | pub const BACKGROUND_RED = 0x0040; | ||
| 4020 | pub const BACKGROUND_INTENSITY = 0x0080; | ||
| 3802 | 4021 | ||
| 3803 | pub const LIST_ENTRY = extern struct { | 4022 | pub 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; |
| 4 | const ACCESS_MASK = windows.ACCESS_MASK; | 4 | const ACCESS_MASK = windows.ACCESS_MASK; |
| 5 | const BOOL = windows.BOOL; | 5 | const BOOL = windows.BOOL; |
| 6 | const CONDITION_VARIABLE = windows.CONDITION_VARIABLE; | 6 | const CONDITION_VARIABLE = windows.CONDITION_VARIABLE; |
| 7 | const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO; | ||
| 8 | const COORD = windows.COORD; | 7 | const COORD = windows.COORD; |
| 9 | const DWORD = windows.DWORD; | 8 | const DWORD = windows.DWORD; |
| 10 | const FARPROC = windows.FARPROC; | 9 | const 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; |
| 193 | 192 | ||
| 194 | // Locks, critical sections, initializers | ||
| 195 | |||
| 196 | // TODO: | ||
| 197 | // - dwMilliseconds -> LARGE_INTEGER. | ||
| 198 | // - RtlSleepConditionVariableSRW | ||
| 199 | // - return rc != .TIMEOUT | ||
| 200 | pub 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 | |||
| 209 | pub extern "kernel32" fn GetConsoleMode( | ||
| 210 | hConsoleHandle: HANDLE, | ||
| 211 | lpMode: *DWORD, | ||
| 212 | ) callconv(.winapi) BOOL; | ||
| 213 | |||
| 214 | pub extern "kernel32" fn SetConsoleMode( | ||
| 215 | hConsoleHandle: HANDLE, | ||
| 216 | dwMode: DWORD, | ||
| 217 | ) callconv(.winapi) BOOL; | ||
| 218 | |||
| 219 | pub extern "kernel32" fn GetConsoleScreenBufferInfo( | ||
| 220 | hConsoleOutput: HANDLE, | ||
| 221 | lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO, | ||
| 222 | ) callconv(.winapi) BOOL; | ||
| 223 | |||
| 224 | pub extern "kernel32" fn SetConsoleTextAttribute( | ||
| 225 | hConsoleOutput: HANDLE, | ||
| 226 | wAttributes: WORD, | ||
| 227 | ) callconv(.winapi) BOOL; | ||
| 228 | |||
| 229 | pub extern "kernel32" fn SetConsoleCtrlHandler( | ||
| 230 | HandlerRoutine: ?HANDLER_ROUTINE, | ||
| 231 | Add: BOOL, | ||
| 232 | ) callconv(.winapi) BOOL; | ||
| 233 | |||
| 234 | pub extern "kernel32" fn SetConsoleOutputCP( | ||
| 235 | wCodePageID: UINT, | ||
| 236 | ) callconv(.winapi) BOOL; | ||
| 237 | |||
| 238 | pub extern "kernel32" fn GetConsoleOutputCP() callconv(.winapi) UINT; | ||
| 239 | |||
| 240 | pub extern "kernel32" fn FillConsoleOutputAttribute( | ||
| 241 | hConsoleOutput: HANDLE, | ||
| 242 | wAttribute: WORD, | ||
| 243 | nLength: DWORD, | ||
| 244 | dwWriteCoord: COORD, | ||
| 245 | lpNumberOfAttrsWritten: *DWORD, | ||
| 246 | ) callconv(.winapi) BOOL; | ||
| 247 | |||
| 248 | pub extern "kernel32" fn FillConsoleOutputCharacterW( | ||
| 249 | hConsoleOutput: HANDLE, | ||
| 250 | cCharacter: WCHAR, | ||
| 251 | nLength: DWORD, | ||
| 252 | dwWriteCoord: COORD, | ||
| 253 | lpNumberOfCharsWritten: *DWORD, | ||
| 254 | ) callconv(.winapi) BOOL; | ||
| 255 | |||
| 256 | pub extern "kernel32" fn SetConsoleCursorPosition( | ||
| 257 | hConsoleOutput: HANDLE, | ||
| 258 | dwCursorPosition: COORD, | ||
| 259 | ) callconv(.winapi) BOOL; | ||
| 260 | |||
| 261 | pub 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 | |||
| 269 | pub 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/Modules | 193 | // Code Libraries/Modules |
| 278 | 194 | ||
| 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, |
| 137 | 137 | ||
| 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, |
| 178 | 176 | ||
| 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 around | 181 | /// 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 | } |