mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 01:24:49 +01:00
std.Threaded: replace console kernel32 functions with ntdll
This commit is contained in:
parent
f150759953
commit
c77e7146f5
9 changed files with 477 additions and 322 deletions
|
|
@ -335,11 +335,9 @@ pub const Operation = union(enum) {
|
|||
.wasi => noreturn,
|
||||
.windows => struct {
|
||||
file: File,
|
||||
IoControlCode: std.os.windows.CTL_CODE,
|
||||
InputBuffer: ?*const anyopaque,
|
||||
InputBufferLength: u32,
|
||||
OutputBuffer: ?*anyopaque,
|
||||
OutputBufferLength: u32,
|
||||
code: std.os.windows.CTL_CODE,
|
||||
in: []const u8 = &.{},
|
||||
out: []u8 = &.{},
|
||||
|
||||
pub const Result = std.os.windows.IO_STATUS_BLOCK;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ pub const Mode = union(enum) {
|
|||
windows_api: WindowsApi,
|
||||
|
||||
pub const WindowsApi = if (!is_windows) noreturn else struct {
|
||||
handle: File.Handle,
|
||||
io: Io,
|
||||
file: File,
|
||||
reset_attributes: u16,
|
||||
};
|
||||
|
||||
|
|
@ -65,20 +66,21 @@ pub const Mode = union(enum) {
|
|||
}
|
||||
|
||||
if (is_windows and try file.isTty(io)) {
|
||||
const windows = std.os.windows;
|
||||
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) {
|
||||
return .{ .windows_api = .{
|
||||
.handle = file.handle,
|
||||
.reset_attributes = info.wAttributes,
|
||||
} };
|
||||
var get_console_info = std.os.windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
|
||||
switch (try get_console_info.operate(io, file)) {
|
||||
.SUCCESS => return .{ .windows_api = .{
|
||||
.io = io,
|
||||
.file = file,
|
||||
.reset_attributes = get_console_info.Data.wAttributes,
|
||||
} },
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
return if (force_color == true) .escape_codes else .no_color;
|
||||
}
|
||||
};
|
||||
|
||||
pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error;
|
||||
pub const SetColorError = Io.Cancelable || Io.UnexpectedError || Io.Writer.Error;
|
||||
|
||||
pub fn setColor(t: Terminal, color: Color) SetColorError!void {
|
||||
switch (t.mode) {
|
||||
|
|
@ -132,7 +134,11 @@ pub fn setColor(t: Terminal, color: Color) SetColorError!void {
|
|||
.reset => wa.reset_attributes,
|
||||
};
|
||||
try t.writer.flush();
|
||||
try windows.SetConsoleTextAttribute(wa.handle, attributes);
|
||||
var set_text_attribute = windows.CONSOLE.USER_IO.SET_TEXT_ATTRIBUTE(attributes);
|
||||
switch (try set_text_attribute.operate(wa.io, wa.file)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3083,19 +3083,23 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
|
|||
}
|
||||
},
|
||||
.device_io_control => |o| {
|
||||
const NtControlFile = switch (o.code.DeviceType) {
|
||||
.FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
|
||||
else => &windows.ntdll.NtDeviceIoControlFile,
|
||||
};
|
||||
if (o.file.flags.nonblocking) {
|
||||
context.file = o.file.handle;
|
||||
switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
switch (NtControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
&batchApc,
|
||||
b,
|
||||
&context.iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
o.code,
|
||||
if (o.in.len > 0) o.in.ptr else null,
|
||||
@intCast(o.in.len),
|
||||
if (o.out.len > 0) o.out.ptr else null,
|
||||
@intCast(o.out.len),
|
||||
)) {
|
||||
.PENDING, .SUCCESS => {},
|
||||
.CANCELLED => unreachable,
|
||||
|
|
@ -3108,17 +3112,17 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
|
|||
if (concurrency) return error.ConcurrencyUnavailable;
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
while (true) switch (NtControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
null, // APC routine
|
||||
null, // APC context
|
||||
&context.iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
o.code,
|
||||
if (o.in.len > 0) o.in.ptr else null,
|
||||
@intCast(o.in.len),
|
||||
if (o.out.len > 0) o.out.ptr else null,
|
||||
@intCast(o.out.len),
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
|
||||
.CANCELLED => {
|
||||
|
|
@ -8547,29 +8551,24 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
|
|||
|
||||
fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
return isTty(file);
|
||||
return t.isTty(file);
|
||||
}
|
||||
|
||||
fn isTty(file: File) Io.Cancelable!bool {
|
||||
fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
|
||||
if (is_windows) {
|
||||
if (try isCygwinPty(file)) return true;
|
||||
var out: windows.DWORD = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (windows.kernel32.GetConsoleMode(file.handle, &out) == 0) {
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
syscall.finish();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
.file = .{
|
||||
.handle = windows.peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
},
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
|
||||
})).u.Status) {
|
||||
.SUCCESS => return true,
|
||||
.INVALID_HANDLE => return isCygwinPty(file),
|
||||
else => return false,
|
||||
}
|
||||
syscall.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (builtin.link_libc) {
|
||||
|
|
@ -8637,35 +8636,26 @@ fn isTty(file: File) Io.Cancelable!bool {
|
|||
|
||||
fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
|
||||
if (!is_windows) {
|
||||
if (try supportsAnsiEscapeCodes(file)) return;
|
||||
return error.NotTerminalDevice;
|
||||
}
|
||||
if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
|
||||
|
||||
// For Windows Terminal, VT Sequences processing is enabled by default.
|
||||
var original_console_mode: windows.DWORD = 0;
|
||||
|
||||
{
|
||||
const syscall: Syscall = try .start();
|
||||
while (windows.kernel32.GetConsoleMode(file.handle, &original_console_mode) == 0) {
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
syscall.finish();
|
||||
if (try isCygwinPty(file)) return;
|
||||
return error.NotTerminalDevice;
|
||||
},
|
||||
}
|
||||
}
|
||||
syscall.finish();
|
||||
const console: File = .{
|
||||
.handle = windows.peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
};
|
||||
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
.file = console,
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
|
||||
})).u.Status) {
|
||||
.SUCCESS => {},
|
||||
.INVALID_HANDLE => return if (!try isCygwinPty(file)) error.NotTerminalDevice,
|
||||
else => return error.NotTerminalDevice,
|
||||
}
|
||||
|
||||
if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
|
||||
if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
|
||||
|
||||
// For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
|
||||
// https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
|
||||
|
|
@ -8678,58 +8668,40 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
|
|||
// Additionally, the default console mode in Windows Terminal does not have
|
||||
// `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`
|
||||
// we end up matching the mode of Windows Terminal.
|
||||
const requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
const console_mode = original_console_mode | requested_console_modes;
|
||||
|
||||
{
|
||||
const syscall: Syscall = try .start();
|
||||
while (windows.kernel32.SetConsoleMode(file.handle, console_mode) == 0) {
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
syscall.finish();
|
||||
if (try isCygwinPty(file)) return;
|
||||
return error.NotTerminalDevice;
|
||||
},
|
||||
}
|
||||
}
|
||||
syscall.finish();
|
||||
var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
|
||||
get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
);
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
.file = console,
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
|
||||
})).u.Status) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
|
||||
fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
return supportsAnsiEscapeCodes(file);
|
||||
return t.supportsAnsiEscapeCodes(file);
|
||||
}
|
||||
|
||||
fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
|
||||
fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
|
||||
if (is_windows) {
|
||||
var console_mode: windows.DWORD = 0;
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (windows.kernel32.GetConsoleMode(file.handle, &console_mode) == 0) {
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
syscall.finish();
|
||||
break;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
syscall.finish();
|
||||
if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) {
|
||||
return true;
|
||||
}
|
||||
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
.file = .{
|
||||
.handle = windows.peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
},
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
|
||||
})).u.Status) {
|
||||
.SUCCESS => if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0)
|
||||
return true,
|
||||
.INVALID_HANDLE => return isCygwinPty(file),
|
||||
else => return false,
|
||||
}
|
||||
|
||||
return isCygwinPty(file);
|
||||
}
|
||||
|
||||
if (native_os == .wasi) {
|
||||
|
|
@ -8739,7 +8711,7 @@ fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (try isTty(file)) return true;
|
||||
if (try t.isTty(file)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -14111,12 +14083,14 @@ fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelabl
|
|||
|
||||
fn unlockStderr(userdata: ?*anyopaque) void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
t.stderr_writer.interface.flush() catch |err| switch (err) {
|
||||
error.WriteFailed => switch (t.stderr_writer.err.?) {
|
||||
if (t.stderr_writer.err == null) t.stderr_writer.interface.flush() catch {};
|
||||
if (t.stderr_writer.err) |err| {
|
||||
switch (err) {
|
||||
error.Canceled => recancelInner(),
|
||||
else => {},
|
||||
},
|
||||
};
|
||||
}
|
||||
t.stderr_writer.err = null;
|
||||
}
|
||||
t.stderr_writer.interface.end = 0;
|
||||
t.stderr_writer.interface.buffer = &.{};
|
||||
|
||||
|
|
@ -18848,20 +18822,24 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
|
|||
fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
|
||||
_ = t;
|
||||
if (is_windows) {
|
||||
const NtControlFile = switch (o.code.DeviceType) {
|
||||
.FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
|
||||
else => &windows.ntdll.NtDeviceIoControlFile,
|
||||
};
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
if (o.file.flags.nonblocking) {
|
||||
var done: bool = false;
|
||||
switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
switch (NtControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
flagApc,
|
||||
&done, // APC context
|
||||
&iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
o.code,
|
||||
if (o.in.len > 0) o.in.ptr else null,
|
||||
@intCast(o.in.len),
|
||||
if (o.out.len > 0) o.out.ptr else null,
|
||||
@intCast(o.out.len),
|
||||
)) {
|
||||
// We must wait for the APC routine.
|
||||
.PENDING, .SUCCESS => while (!done) {
|
||||
|
|
@ -18882,17 +18860,17 @@ fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Canc
|
|||
}
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
while (true) switch (NtControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
null, // APC routine
|
||||
null, // APC context
|
||||
&iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
o.code,
|
||||
if (o.in.len > 0) o.in.ptr else null,
|
||||
@intCast(o.in.len),
|
||||
if (o.out.len > 0) o.out.ptr else null,
|
||||
@intCast(o.out.len),
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong asynchronous flag
|
||||
.CANCELLED => {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ pub const TerminalMode = union(enum) {
|
|||
ansi_escape_codes,
|
||||
/// This is not the same as being run on windows because other terminals
|
||||
/// exist like MSYS/git-bash.
|
||||
windows_api: if (is_windows) WindowsApi else void,
|
||||
windows_api: if (is_windows) WindowsApi else noreturn,
|
||||
|
||||
pub const WindowsApi = struct {
|
||||
/// The output code page of the console.
|
||||
|
|
@ -614,33 +614,39 @@ pub fn start(io: Io, options: Options) Node {
|
|||
if (stderr.enableAnsiEscapeCodes(io)) |_| {
|
||||
global_progress.terminal_mode = .ansi_escape_codes;
|
||||
} else |_| if (is_windows) {
|
||||
if (stderr.isTty(io)) |is_tty| {
|
||||
if (is_tty) global_progress.terminal_mode = TerminalMode{ .windows_api = .{
|
||||
.code_page = windows.kernel32.GetConsoleOutputCP(),
|
||||
} };
|
||||
} else |err| switch (err) {
|
||||
var get_console_cp = windows.CONSOLE.USER_IO.GET_CP(.Output);
|
||||
// Normally, we would pass `null` to `operate` here as the kernel32
|
||||
// function does not accept a handle, however, if we pass one anyway,
|
||||
// then we will get an error if the handle is not associated with
|
||||
// this process's console, effectively combining an `isTty` check
|
||||
// into the same syscall.
|
||||
switch (get_console_cp.operate(io, stderr) catch |err| switch (err) {
|
||||
error.Canceled => {
|
||||
io.recancel();
|
||||
return .none;
|
||||
},
|
||||
}) {
|
||||
.SUCCESS => global_progress.terminal_mode = .{ .windows_api = .{
|
||||
.code_page = get_console_cp.Data.CodePage,
|
||||
} },
|
||||
.INVALID_HANDLE => {},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (global_progress.terminal_mode == .off) return .none;
|
||||
|
||||
if (have_sigwinch) {
|
||||
const act: posix.Sigaction = .{
|
||||
.handler = .{ .sigaction = handleSigWinch },
|
||||
.mask = posix.sigemptyset(),
|
||||
.flags = (posix.SA.SIGINFO | posix.SA.RESTART),
|
||||
};
|
||||
posix.sigaction(.WINCH, &act, null);
|
||||
}
|
||||
|
||||
if (switch (global_progress.terminal_mode) {
|
||||
.off => unreachable, // handled a few lines above
|
||||
.ansi_escape_codes => io.concurrent(updateTask, .{io}),
|
||||
.windows_api => if (is_windows) io.concurrent(windowsApiUpdateTask, .{io}) else unreachable,
|
||||
if (future: switch (global_progress.terminal_mode) {
|
||||
.off => return .none,
|
||||
.ansi_escape_codes => {
|
||||
if (have_sigwinch) {
|
||||
const act: posix.Sigaction = .{
|
||||
.handler = .{ .sigaction = handleSigWinch },
|
||||
.mask = posix.sigemptyset(),
|
||||
.flags = (posix.SA.SIGINFO | posix.SA.RESTART),
|
||||
};
|
||||
posix.sigaction(.WINCH, &act, null);
|
||||
}
|
||||
break :future io.concurrent(updateTask, .{io});
|
||||
},
|
||||
.windows_api => io.concurrent(windowsApiUpdateTask, .{io}),
|
||||
}) |future| {
|
||||
global_progress.update_worker = future;
|
||||
} else |err| {
|
||||
|
|
@ -715,12 +721,24 @@ fn updateTask(io: Io) WorkerError!void {
|
|||
}
|
||||
}
|
||||
|
||||
fn windowsApiWriteMarker() void {
|
||||
const WindowsApiError = Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
fn windowsApiWriteMarker(io: Io) WindowsApiError!void {
|
||||
// Write the marker that we will use to find the beginning of the progress when clearing.
|
||||
// Note: This doesn't have to use WriteConsoleW, but doing so avoids dealing with the code page.
|
||||
var num_chars_written: windows.DWORD = undefined;
|
||||
const handle = global_progress.terminal.handle;
|
||||
_ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null);
|
||||
const terminal = global_progress.terminal;
|
||||
var write_console = windows.CONSOLE.USER_IO.WRITE(.WideCharacter);
|
||||
const buffer = [1]windows.WCHAR{windows_api_start_marker};
|
||||
switch ((try io.operate(.{ .device_io_control = .{
|
||||
.file = terminal,
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&write_console.request(null, 1, .{
|
||||
.{ .Size = @sizeOf(@TypeOf(buffer)), .Pointer = &buffer },
|
||||
}, 0, .{})),
|
||||
} })).device_io_control.u.Status) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
|
||||
fn windowsApiUpdateTask(io: Io) WorkerError!void {
|
||||
|
|
@ -743,19 +761,19 @@ fn windowsApiUpdateTask(io: Io) WorkerError!void {
|
|||
error.Canceled => unreachable, // blocked
|
||||
};
|
||||
defer io.unlockStderr();
|
||||
clearWrittenWindowsApi() catch {};
|
||||
clearWrittenWindowsApi(io) catch {};
|
||||
}
|
||||
while (true) {
|
||||
const buffer, const nl_n = try computeRedraw(io, &serialized_buffer);
|
||||
if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
|
||||
defer io.unlockStderr();
|
||||
try clearWrittenWindowsApi();
|
||||
windowsApiWriteMarker();
|
||||
try clearWrittenWindowsApi(io);
|
||||
try windowsApiWriteMarker(io);
|
||||
global_progress.need_clear = true;
|
||||
locked_stderr.file_writer.interface.writeAll(buffer) catch |err| switch (err) {
|
||||
error.WriteFailed => return locked_stderr.file_writer.err.?,
|
||||
};
|
||||
windowsApiMoveToMarker(nl_n) catch return;
|
||||
windowsApiMoveToMarker(io, nl_n) catch return;
|
||||
}
|
||||
|
||||
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 {
|
|||
return start_i + bytes.len;
|
||||
},
|
||||
.windows_api => |windows_api| {
|
||||
const bytes = if (!is_windows) unreachable else switch (windows_api.code_page) {
|
||||
const bytes = switch (windows_api.code_page) {
|
||||
// Code page 437 is the default code page and contains the box drawing symbols
|
||||
437 => symbol.bytes(.code_page_437),
|
||||
// UTF-8
|
||||
|
|
@ -882,7 +900,7 @@ pub fn clearWrittenWithEscapeCodes(file_writer: *Io.File.Writer) Io.Writer.Error
|
|||
/// U+25BA or ►
|
||||
const windows_api_start_marker = 0x25BA;
|
||||
|
||||
fn clearWrittenWindowsApi() error{Unexpected}!void {
|
||||
fn clearWrittenWindowsApi(io: Io) WindowsApiError!void {
|
||||
// This uses a 'marker' strategy. The idea is:
|
||||
// - Always write a marker (in this case U+25BA or ►) at the beginning of the progress
|
||||
// - Get the current cursor position (at the end of the progress)
|
||||
|
|
@ -903,43 +921,60 @@ fn clearWrittenWindowsApi() error{Unexpected}!void {
|
|||
// character in order to be readable via ReadConsoleOutputAttribute. It doesn't seem
|
||||
// like any of the available attributes are invisible/benign.
|
||||
if (!global_progress.need_clear) return;
|
||||
const handle = global_progress.terminal.handle;
|
||||
const terminal = global_progress.terminal;
|
||||
const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows;
|
||||
|
||||
var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {
|
||||
return error.Unexpected;
|
||||
var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
|
||||
switch (try get_console_info.operate(io, terminal)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
var num_chars_written: windows.DWORD = undefined;
|
||||
if (windows.kernel32.FillConsoleOutputCharacterW(handle, ' ', screen_area, console_info.dwCursorPosition, &num_chars_written) == 0) {
|
||||
return error.Unexpected;
|
||||
var fill_spaces = windows.CONSOLE.USER_IO.FILL(
|
||||
.{ .WideCharacter = ' ' },
|
||||
screen_area,
|
||||
get_console_info.Data.dwCursorPosition,
|
||||
);
|
||||
switch (try fill_spaces.operate(io, terminal)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
|
||||
fn windowsApiMoveToMarker(nl_n: usize) error{Unexpected}!void {
|
||||
const handle = global_progress.terminal.handle;
|
||||
var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {
|
||||
return error.Unexpected;
|
||||
fn windowsApiMoveToMarker(io: Io, nl_n: usize) WindowsApiError!void {
|
||||
const terminal = global_progress.terminal;
|
||||
var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
|
||||
switch (try get_console_info.operate(io, terminal)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
const cursor_pos = console_info.dwCursorPosition;
|
||||
const cursor_pos = get_console_info.Data.dwCursorPosition;
|
||||
const expected_y = cursor_pos.Y - @as(i16, @intCast(nl_n));
|
||||
var start_pos: windows.COORD = .{ .X = 0, .Y = expected_y };
|
||||
while (start_pos.Y >= 0) {
|
||||
var wchar: [1]u16 = undefined;
|
||||
var num_console_chars_read: windows.DWORD = undefined;
|
||||
if (windows.kernel32.ReadConsoleOutputCharacterW(handle, &wchar, wchar.len, start_pos, &num_console_chars_read) == 0) {
|
||||
return error.Unexpected;
|
||||
while (start_pos.Y >= 0) : (start_pos.Y -= 1) {
|
||||
var read_output_char = windows.CONSOLE.USER_IO.READ_OUTPUT_CHARACTER(start_pos, .WideCharacter);
|
||||
var buffer: [1]windows.WCHAR = undefined;
|
||||
switch ((try io.operate(.{ .device_io_control = .{
|
||||
.file = .{
|
||||
.handle = windows.peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
},
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&read_output_char.request(terminal, 0, .{}, 1, .{
|
||||
.{ .Size = @sizeOf(@TypeOf(buffer)), .Pointer = &buffer },
|
||||
})),
|
||||
} })).device_io_control.u.Status) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
|
||||
if (wchar[0] == windows_api_start_marker) break;
|
||||
start_pos.Y -= 1;
|
||||
if (read_output_char.Data.nLength >= 1 and buffer[0] == windows_api_start_marker) break;
|
||||
} else {
|
||||
// If we couldn't find the marker, then just assume that no lines wrapped
|
||||
start_pos = .{ .X = 0, .Y = expected_y };
|
||||
}
|
||||
if (windows.kernel32.SetConsoleCursorPosition(handle, start_pos) == 0) {
|
||||
return error.Unexpected;
|
||||
var set_cursor_position = windows.CONSOLE.USER_IO.SET_CURSOR_POSITION(start_pos);
|
||||
switch (try set_cursor_position.operate(io, terminal)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1279,7 +1314,7 @@ fn computeRedraw(io: Io, serialized_buffer: *Serialized.Buffer) !struct { []u8,
|
|||
buf[i..][0..clear.len].* = clear.*;
|
||||
i += clear.len;
|
||||
},
|
||||
.windows_api => if (!is_windows) unreachable,
|
||||
.windows_api => {},
|
||||
}
|
||||
|
||||
const root_node_index: Node.Index = @enumFromInt(0);
|
||||
|
|
@ -1491,19 +1526,17 @@ fn maybeUpdateSize(io: Io, resize_flag: bool) !void {
|
|||
const file = global_progress.terminal;
|
||||
|
||||
if (is_windows) {
|
||||
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) {
|
||||
// In the old Windows console, dwSize.Y is the line count of the
|
||||
// entire scrollback buffer, so we use this instead so that we
|
||||
// always get the size of the screen.
|
||||
const screen_height = info.srWindow.Bottom - info.srWindow.Top;
|
||||
global_progress.rows = @intCast(screen_height);
|
||||
global_progress.cols = @intCast(info.dwSize.X);
|
||||
} else {
|
||||
std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
|
||||
global_progress.rows = 25;
|
||||
global_progress.cols = 80;
|
||||
var get_console_info = windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
|
||||
switch (try get_console_info.operate(io, file)) {
|
||||
.SUCCESS => {
|
||||
global_progress.rows = @intCast(get_console_info.Data.dwWindowSize.Y);
|
||||
global_progress.cols = @intCast(get_console_info.Data.dwWindowSize.X);
|
||||
},
|
||||
else => {
|
||||
std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
|
||||
global_progress.rows = 25;
|
||||
global_progress.cols = 80;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
var winsize: posix.winsize = .{
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool {
|
|||
return @intFromEnum(level) <= @intFromEnum(std.options.log_level);
|
||||
}
|
||||
|
||||
pub const terminalMode = std.options.logTerminalMode;
|
||||
pub const terminalMode = std.Options.logTerminalMode;
|
||||
|
||||
pub fn defaultTerminalMode() std.Io.Terminal.Mode {
|
||||
const stderr = std.debug.lockStderr(&.{}).terminal();
|
||||
|
|
@ -99,6 +99,9 @@ pub fn defaultLog(
|
|||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const io = std.Options.debug_io;
|
||||
const prev = io.swapCancelProtection(.blocked);
|
||||
defer _ = io.swapCancelProtection(prev);
|
||||
var buffer: [64]u8 = undefined;
|
||||
const stderr = std.debug.lockStderr(&buffer).terminal();
|
||||
defer std.debug.unlockStderr();
|
||||
|
|
|
|||
|
|
@ -649,6 +649,235 @@ pub const FILE = struct {
|
|||
};
|
||||
};
|
||||
|
||||
pub const CONSOLE = struct {
|
||||
pub const USER_IO = struct {
|
||||
pub const INFO = struct {
|
||||
pub const CP = extern struct {
|
||||
/// GetCP: output
|
||||
/// SetCP: input
|
||||
CodePage: UINT,
|
||||
/// input
|
||||
Mode: MODE,
|
||||
|
||||
pub const MODE = enum(BOOLEAN) {
|
||||
Input = FALSE,
|
||||
Output = TRUE,
|
||||
};
|
||||
};
|
||||
|
||||
pub const WRITE = extern struct {
|
||||
/// output, in bytes
|
||||
Size: DWORD,
|
||||
/// input
|
||||
Mode: MODE,
|
||||
|
||||
pub const MODE = enum(BOOLEAN) {
|
||||
Character = FALSE,
|
||||
WideCharacter = TRUE,
|
||||
};
|
||||
};
|
||||
|
||||
pub const FILL = extern struct {
|
||||
/// input
|
||||
dwWriteCoord: COORD,
|
||||
/// input
|
||||
Tag: WITH.Tag,
|
||||
/// input
|
||||
With: WITH.Payload,
|
||||
/// input/output, in characters
|
||||
nLength: DWORD,
|
||||
|
||||
pub const WITH = union(enum(DWORD)) {
|
||||
Character: CHAR = 1,
|
||||
WideCharacter: WCHAR = 2,
|
||||
Attribute: WORD = 3,
|
||||
|
||||
pub const Tag = @typeInfo(WITH).@"union".tag_type.?;
|
||||
pub const Payload = PAYLOAD: {
|
||||
const with_fields = @typeInfo(WITH).@"union".fields;
|
||||
var field_names: [with_fields.len][]const u8 = undefined;
|
||||
var field_types: [with_fields.len]type = undefined;
|
||||
for (with_fields, &field_names, &field_types) |field, *field_name, *field_type| {
|
||||
field_name.* = field.name;
|
||||
field_type.* = field.type;
|
||||
}
|
||||
break :PAYLOAD @Union(.@"extern", null, &field_names, &field_types, &@splat(.{}));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/// all output
|
||||
pub const SCREEN_BUFFER = extern struct {
|
||||
dwSize: COORD,
|
||||
dwCursorPosition: COORD,
|
||||
dwWindowPosition: COORD,
|
||||
wAttributes: WORD,
|
||||
dwWindowSize: COORD,
|
||||
dwMaximumWindowSize: COORD,
|
||||
wPopupAttributes: WORD,
|
||||
bFullscreenSupported: BOOL,
|
||||
ColorTable: [16]COLORREF,
|
||||
};
|
||||
|
||||
pub const READ_OUTPUT_CHARACTER = extern struct {
|
||||
/// input
|
||||
dwReadCoord: COORD,
|
||||
Mode: MODE,
|
||||
/// output, in characters
|
||||
nLength: DWORD,
|
||||
|
||||
pub const MODE = enum(DWORD) {
|
||||
Character = 1,
|
||||
WideCharacter = 2,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pub fn GET_CP(mode: INFO.CP.MODE) Header.With(INFO.CP) {
|
||||
return .init(.GetCP, .{ .CodePage = undefined, .Mode = mode });
|
||||
}
|
||||
pub const GET_MODE: Header.With(DWORD) = .init(.GetMode, undefined);
|
||||
pub fn SET_MODE(mode: DWORD) Header.With(DWORD) {
|
||||
return .init(.SetMode, mode);
|
||||
}
|
||||
pub fn WRITE(mode: INFO.WRITE.MODE) Header.With(INFO.WRITE) {
|
||||
return .init(.Write, .{ .Size = undefined, .Mode = mode });
|
||||
}
|
||||
pub fn FILL(with: INFO.FILL.WITH, len: DWORD, coord: COORD) Header.With(INFO.FILL) {
|
||||
return .init(.Fill, .{
|
||||
.dwWriteCoord = coord,
|
||||
.Tag = with,
|
||||
.With = switch (with) {
|
||||
inline else => |payload, tag| @unionInit(
|
||||
INFO.FILL.WITH.Payload,
|
||||
@tagName(tag),
|
||||
payload,
|
||||
),
|
||||
},
|
||||
.nLength = len,
|
||||
});
|
||||
}
|
||||
pub fn SET_CP(mode: INFO.CP.MODE, cp: UINT) Header.With(INFO.CP) {
|
||||
return .init(.SetCP, .{ .CodePage = cp, .Mode = mode });
|
||||
}
|
||||
pub const GET_SCREEN_BUFFER_INFO: Header.With(INFO.SCREEN_BUFFER) =
|
||||
.init(.GetScreenBufferInfo, undefined);
|
||||
pub fn SET_CURSOR_POSITION(coord: COORD) Header.With(COORD) {
|
||||
return .init(.SetCursorPosition, coord);
|
||||
}
|
||||
pub fn SET_TEXT_ATTRIBUTE(attribute: WORD) Header.With(WORD) {
|
||||
return .init(.SetTextAttribute, attribute);
|
||||
}
|
||||
pub fn READ_OUTPUT_CHARACTER(
|
||||
coord: COORD,
|
||||
mode: INFO.READ_OUTPUT_CHARACTER.MODE,
|
||||
) Header.With(INFO.READ_OUTPUT_CHARACTER) {
|
||||
return .init(.ReadOutputCharacter, .{
|
||||
.dwReadCoord = coord,
|
||||
.Mode = mode,
|
||||
.nLength = undefined,
|
||||
});
|
||||
}
|
||||
|
||||
pub const InputBuffer = extern struct {
|
||||
Size: u32,
|
||||
Pointer: *const anyopaque,
|
||||
};
|
||||
|
||||
pub const OutputBuffer = extern struct {
|
||||
Size: u32,
|
||||
Pointer: *anyopaque,
|
||||
};
|
||||
|
||||
pub fn Request(comptime in_len: u32, comptime out_len: u32) type {
|
||||
return extern struct {
|
||||
Handle: ?HANDLE,
|
||||
InputBuffersLength: u32,
|
||||
OutputBuffersLength: u32,
|
||||
InputBuffers: [in_len]InputBuffer,
|
||||
OutputBuffers: [out_len]OutputBuffer,
|
||||
|
||||
pub fn init(
|
||||
handle: ?HANDLE,
|
||||
in: [in_len]InputBuffer,
|
||||
out: [out_len]OutputBuffer,
|
||||
) @This() {
|
||||
return .{
|
||||
.Handle = handle,
|
||||
.InputBuffersLength = in_len,
|
||||
.OutputBuffersLength = out_len,
|
||||
.InputBuffers = in,
|
||||
.OutputBuffers = out,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Header = extern struct {
|
||||
Operation: Operation,
|
||||
Size: u32,
|
||||
|
||||
pub fn With(comptime Data: type) type {
|
||||
return extern struct {
|
||||
Header: Header,
|
||||
Data: Data,
|
||||
|
||||
pub fn init(operation: Operation, data: Data) @This() {
|
||||
return .{
|
||||
.Header = .{ .Operation = operation, .Size = @sizeOf(Data) },
|
||||
.Data = data,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn request(
|
||||
with: *@This(),
|
||||
file: ?Io.File,
|
||||
comptime in_len: u32,
|
||||
in: [in_len]InputBuffer,
|
||||
comptime out_len: u32,
|
||||
out: [out_len]OutputBuffer,
|
||||
) Request(1 + in_len, 1 + out_len) {
|
||||
return .init(
|
||||
if (file) |f| f.handle else null,
|
||||
[1]InputBuffer{.{
|
||||
.Size = @offsetOf(@This(), "Data") + @sizeOf(Data),
|
||||
.Pointer = with,
|
||||
}} ++ in,
|
||||
[1]OutputBuffer{.{ .Size = @sizeOf(Data), .Pointer = &with.Data }} ++ out,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn operate(with: *@This(), io: Io, file: ?Io.File) Io.Cancelable!NTSTATUS {
|
||||
return (try io.operate(.{ .device_io_control = .{
|
||||
.file = .{
|
||||
.handle = peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
},
|
||||
.code = IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&with.request(file, 0, .{}, 0, .{})),
|
||||
} })).device_io_control.u.Status;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Operation = enum(u32) {
|
||||
GetCP = 0x1000000,
|
||||
GetMode = 0x1000001,
|
||||
SetMode = 0x1000002,
|
||||
Read = 0x1000005,
|
||||
Write = 0x1000006,
|
||||
Fill = 0x2000000,
|
||||
SetCP = 0x2000004,
|
||||
GetScreenBufferInfo = 0x2000007,
|
||||
SetCursorPosition = 0x200000a,
|
||||
SetTextAttribute = 0x200000d,
|
||||
ReadOutputCharacter = 0x200000f,
|
||||
_,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// ref: km/ntddk.h
|
||||
|
||||
pub const PROCESSINFOCLASS = enum(c_int) {
|
||||
|
|
@ -1160,6 +1389,22 @@ pub const CTL_CODE = packed struct(ULONG) {
|
|||
};
|
||||
|
||||
pub const IOCTL = struct {
|
||||
pub const CONDRV = struct {
|
||||
pub const READ_IO: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 1, .Method = .OUT_DIRECT, .Access = .ANY };
|
||||
pub const COMPLETE_IO: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 2, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const READ_INPUT: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 3, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const WRITE_OUTPUT: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 4, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const ISSUE_USER_IO: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 5, .Method = .OUT_DIRECT, .Access = .ANY };
|
||||
pub const DISCONNECT_PIPE: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 6, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const SET_SERVER_INFORMATION: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 7, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const GET_SERVER_PID: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 8, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const GET_DISPLAY_SIZE: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 9, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const UPDATE_DISPLAY: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 10, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const SET_CURSOR: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 11, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const ALLOW_VIA_UIACCESS: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 12, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const LAUNCH_SERVER: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 13, .Method = .NEITHER, .Access = .ANY };
|
||||
pub const GET_FONT_SIZE: CTL_CODE = .{ .DeviceType = .CONSOLE, .Function = 14, .Method = .NEITHER, .Access = .ANY };
|
||||
};
|
||||
pub const KSEC = struct {
|
||||
pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
|
||||
};
|
||||
|
|
@ -2663,29 +2908,6 @@ pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_
|
|||
};
|
||||
}
|
||||
|
||||
pub const SetConsoleTextAttributeError = error{Unexpected};
|
||||
|
||||
pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
|
||||
if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
|
||||
switch (GetLastError()) {
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn SetConsoleCtrlHandler(handler_routine: ?HANDLER_ROUTINE, add: bool) !void {
|
||||
const success = kernel32.SetConsoleCtrlHandler(
|
||||
handler_routine,
|
||||
if (add) TRUE else FALSE,
|
||||
);
|
||||
|
||||
if (success == FALSE) {
|
||||
return switch (GetLastError()) {
|
||||
else => |err| unexpectedError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
|
||||
const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
|
||||
if (success == FALSE) {
|
||||
|
|
@ -3244,6 +3466,7 @@ pub const ULONGLONG = u64;
|
|||
pub const LONGLONG = i64;
|
||||
pub const HLOCAL = HANDLE;
|
||||
pub const LANGID = c_ushort;
|
||||
pub const COLORREF = DWORD;
|
||||
|
||||
pub const WPARAM = usize;
|
||||
pub const LPARAM = LONG_PTR;
|
||||
|
|
@ -3784,21 +4007,17 @@ pub const FileNotifyChangeFilter = packed struct(DWORD) {
|
|||
_pad: u20 = 0,
|
||||
};
|
||||
|
||||
pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
|
||||
dwSize: COORD,
|
||||
dwCursorPosition: COORD,
|
||||
wAttributes: WORD,
|
||||
srWindow: SMALL_RECT,
|
||||
dwMaximumWindowSize: COORD,
|
||||
};
|
||||
|
||||
pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
|
||||
pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
|
||||
|
||||
pub const FOREGROUND_BLUE = 1;
|
||||
pub const FOREGROUND_GREEN = 2;
|
||||
pub const FOREGROUND_RED = 4;
|
||||
pub const FOREGROUND_INTENSITY = 8;
|
||||
pub const FOREGROUND_BLUE = 0x0001;
|
||||
pub const FOREGROUND_GREEN = 0x0002;
|
||||
pub const FOREGROUND_RED = 0x0004;
|
||||
pub const FOREGROUND_INTENSITY = 0x0008;
|
||||
pub const BACKGROUND_BLUE = 0x0010;
|
||||
pub const BACKGROUND_GREEN = 0x0020;
|
||||
pub const BACKGROUND_RED = 0x0040;
|
||||
pub const BACKGROUND_INTENSITY = 0x0080;
|
||||
|
||||
pub const LIST_ENTRY = extern struct {
|
||||
Flink: *LIST_ENTRY,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ const windows = std.os.windows;
|
|||
const ACCESS_MASK = windows.ACCESS_MASK;
|
||||
const BOOL = windows.BOOL;
|
||||
const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
|
||||
const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO;
|
||||
const COORD = windows.COORD;
|
||||
const DWORD = windows.DWORD;
|
||||
const FARPROC = windows.FARPROC;
|
||||
|
|
@ -191,89 +190,6 @@ pub extern "kernel32" fn CreateThread(
|
|||
lpThreadId: ?*DWORD,
|
||||
) callconv(.winapi) ?HANDLE;
|
||||
|
||||
// Locks, critical sections, initializers
|
||||
|
||||
// TODO:
|
||||
// - dwMilliseconds -> LARGE_INTEGER.
|
||||
// - RtlSleepConditionVariableSRW
|
||||
// - return rc != .TIMEOUT
|
||||
pub extern "kernel32" fn SleepConditionVariableSRW(
|
||||
ConditionVariable: *CONDITION_VARIABLE,
|
||||
SRWLock: *SRWLOCK,
|
||||
dwMilliseconds: DWORD,
|
||||
Flags: ULONG,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// Console management
|
||||
|
||||
pub extern "kernel32" fn GetConsoleMode(
|
||||
hConsoleHandle: HANDLE,
|
||||
lpMode: *DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn SetConsoleMode(
|
||||
hConsoleHandle: HANDLE,
|
||||
dwMode: DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn GetConsoleScreenBufferInfo(
|
||||
hConsoleOutput: HANDLE,
|
||||
lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn SetConsoleTextAttribute(
|
||||
hConsoleOutput: HANDLE,
|
||||
wAttributes: WORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn SetConsoleCtrlHandler(
|
||||
HandlerRoutine: ?HANDLER_ROUTINE,
|
||||
Add: BOOL,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn SetConsoleOutputCP(
|
||||
wCodePageID: UINT,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn GetConsoleOutputCP() callconv(.winapi) UINT;
|
||||
|
||||
pub extern "kernel32" fn FillConsoleOutputAttribute(
|
||||
hConsoleOutput: HANDLE,
|
||||
wAttribute: WORD,
|
||||
nLength: DWORD,
|
||||
dwWriteCoord: COORD,
|
||||
lpNumberOfAttrsWritten: *DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn FillConsoleOutputCharacterW(
|
||||
hConsoleOutput: HANDLE,
|
||||
cCharacter: WCHAR,
|
||||
nLength: DWORD,
|
||||
dwWriteCoord: COORD,
|
||||
lpNumberOfCharsWritten: *DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn SetConsoleCursorPosition(
|
||||
hConsoleOutput: HANDLE,
|
||||
dwCursorPosition: COORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn WriteConsoleW(
|
||||
hConsoleOutput: HANDLE,
|
||||
lpBuffer: [*]const u16,
|
||||
nNumberOfCharsToWrite: DWORD,
|
||||
lpNumberOfCharsWritten: ?*DWORD,
|
||||
lpReserved: ?LPVOID,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn ReadConsoleOutputCharacterW(
|
||||
hConsoleOutput: HANDLE,
|
||||
lpCharacter: [*]u16,
|
||||
nLength: DWORD,
|
||||
dwReadCoord: COORD,
|
||||
lpNumberOfCharsRead: *DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// Code Libraries/Modules
|
||||
|
||||
// TODO: Wrapper around LdrGetDllFullName.
|
||||
|
|
|
|||
|
|
@ -135,8 +135,6 @@ pub const Options = struct {
|
|||
args: anytype,
|
||||
) void = log.defaultLog,
|
||||
|
||||
logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode,
|
||||
|
||||
/// Overrides `std.heap.page_size_min`.
|
||||
page_size_min: ?usize = null,
|
||||
/// Overrides `std.heap.page_size_max`.
|
||||
|
|
@ -176,6 +174,10 @@ pub const Options = struct {
|
|||
/// stack traces will just print an error to the relevant `Io.Writer` and return.
|
||||
allow_stack_tracing: bool = !@import("builtin").strip_debug_info,
|
||||
|
||||
/// TODO This is a separate decl instead of a field as a workaround around
|
||||
/// compilation errors due to zig not being lazy enough.
|
||||
pub const logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode;
|
||||
|
||||
/// TODO This is a separate decl instead of a field as a workaround around
|
||||
/// compilation errors due to zig not being lazy enough.
|
||||
pub const elf_debug_info_search_paths: ?fn (exe_path: []const u8) switch (@import("builtin").object_format) {
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
|
|||
if (msg.kind == .@"fatal error" or msg.kind == .@"error") {
|
||||
msg.write(stderr.terminal(), true) catch |err| switch (err) {
|
||||
error.WriteFailed => return stderr.file_writer.err.?,
|
||||
error.Unexpected => |e| return e,
|
||||
error.Canceled, error.Unexpected => |e| return e,
|
||||
};
|
||||
return error.AroPreprocessorFailed;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue