diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 9c34cbbec99d..4c21a12f0b9a 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -417,6 +417,17 @@ pub fn start(options: Options) Node { posix.sigaction(posix.SIG.WINCH, &act, null); } + std.io.tty.setInterruptSignalHandler(struct { + fn interrupt_signal_handler() void { + switch (global_progress.terminal_mode) { + .off => unreachable, // handled a few lines above + .ansi_escape_codes => clearWrittenWithEscapeCodes() catch {}, + .windows_api => if (is_windows) clearWrittenWindowsApi() catch {} else unreachable, + } + std.process.exit(1); + } + }.interrupt_signal_handler) catch {}; + if (switch (global_progress.terminal_mode) { .off => unreachable, // handled a few lines above .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}), diff --git a/lib/std/io/tty.zig b/lib/std/io/tty.zig index 83206a6a677d..7bb3ffd9edba 100644 --- a/lib/std/io/tty.zig +++ b/lib/std/io/tty.zig @@ -5,6 +5,46 @@ const process = std.process; const windows = std.os.windows; const native_os = builtin.os.tag; +/// Registers a global handler function to run if an interrupt signal is catched. +/// An interrupt signal is usually fired if Ctrl+C is pressed in the terminal that controls the process. +/// +/// Because this handler won't run if Ctrl+C isn't pressed by the user, registering this handler failed, +/// or the handler was overwritten by another setInterruptSignalHandler call, +/// this should be used only for non-critical cleanups or resets of terminal state and such. +/// +/// A program can only have one handler at a time. +/// +/// The handler will not exit the program after it runs. +pub fn setInterruptSignalHandler(comptime handler: *const fn () void) error{Unexpected}!void { + if (builtin.os.tag == .windows) { + const handler_routine = struct { + fn handler_routine(dwCtrlType: windows.DWORD) callconv(windows.WINAPI) windows.BOOL { + if (dwCtrlType == windows.CTRL_C_EVENT) { + handler(); + return windows.TRUE; + } else { + // Ignore this event. + return windows.FALSE; + } + } + }.handler_routine; + try windows.SetConsoleCtrlHandler(handler_routine, true); + } else { + const internal_handler = struct { + fn internal_handler(sig: c_int) callconv(.C) void { + std.debug.assert(sig == std.posix.SIG.INT); + handler(); + } + }.internal_handler; + const act = std.posix.Sigaction{ + .handler = .{ .handler = internal_handler }, + .mask = std.posix.empty_sigset, + .flags = 0, + }; + std.posix.sigaction(std.posix.SIG.INT, &act, null); + } +} + /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr). /// This includes feature checks for ANSI escape codes and the Windows console API, as well as /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.