Initializes a global Progress instance.
Asserts there is only one global Progress instance.
Call Node.end when done.
If an error occurs, start_failure will be populated.
pub fn start(io: Io, options: Options) Node
pub fn start(io: Io, options: Options) Node {
// Ensure there is only 1 global Progress object.
if (global_progress.node_end_index != 0) {
debug_start_trace.dump();
unreachable;
}
debug_start_trace.add("first initialized here");
@memset(&global_progress.node_parents, .unused);
@memset(&global_progress.ipc, .{ .locked = false, .valid = false, .generation = 0 });
const root_node = Node.init(@fromBackingInt(@intCast(0)), .none, options.root_name, options.estimated_total_items);
global_progress.node_end_index = 1;
assert(options.draw_buffer.len >= 200);
global_progress.draw_buffer = options.draw_buffer;
global_progress.refresh_rate_ns = @intCast(options.refresh_rate_ns.toNanoseconds());
global_progress.initial_delay_ns = @intCast(options.initial_delay_ns.toNanoseconds());
if (noop_impl) return .none;
global_progress.io = io;
if (io.vtable.progressParentFile(io.userdata)) |ipc_file| {
global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, ipc_file }) catch |err| {
global_progress.start_failure = .{ .spawn_ipc_worker = err };
return .none;
};
} else |env_err| switch (env_err) {
error.EnvironmentVariableMissing => {
if (options.disable_printing) return .none;
const stderr: Io.File = .stderr();
global_progress.terminal = stderr;
if (stderr.enableAnsiEscapeCodes(io)) |_| {
global_progress.terminal_mode = .ansi_escape_codes;
} else |_| if (is_windows) {
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 (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| {
global_progress.start_failure = .{ .spawn_update_worker = err };
return .none;
}
},
else => |e| {
global_progress.start_failure = .{ .parent_ipc = e };
return .none;
},
}
return root_node;
}