const std = @import("std"); const builtin = @import("builtin"); pub const TermSize = struct { width: u16, height: u16, }; pub fn getTerminalSize() !TermSize { // https://github.com/softprops/zig-termsize (https://github.com/softprops/zig-termsize/blob/main/src/main.zig) const stdout = std.fs.File.stdout(); switch (builtin.os.tag) { .windows => { var buf: std.os.windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; switch (std.os.windows.kernel32.GetConsoleScreenBufferInfo(stdout.handle, &buf)) { std.os.windows.TRUE => return TermSize{ .width = @intCast(buf.srWindow.Right - buf.srWindow.Left + 1), .height = @intCast(buf.srWindow.Bottom - buf.srWindow.Top + 1), }, else => return error.GetConsoleScreenBufferInfoFailed, } }, .linux, .macos => { var buf: std.posix.winsize = undefined; switch (std.posix.errno( std.posix.system.ioctl( stdout.handle, std.posix.T.IOCGWINSZ, @intFromPtr(&buf), ), )) { std.posix.E.SUCCESS => return TermSize{ .width = buf.col, .height = buf.row, }, else => return error.IoctlFailed, } }, else => return error.UnsupportedOperatingSystem, } } test "getTerminalSize" { const terminal_size = getTerminalSize() catch TermSize{ .height = 50, .width = 50 }; std.debug.print("Height: {}, Width {}\n", .{ terminal_size.height, terminal_size.width }); try std.testing.expect((terminal_size.height > 0) and (terminal_size.width > 0)); } pub fn getLongestSysInfoStringLen(strings: []const []const u8) usize { const ansi_reset = "\x1b[0m"; var longest_len: usize = 0; // Ignore the username@host and the separator for (strings[2..]) |s| { const ansi_restet_index = std.mem.indexOf(u8, s, ansi_reset); var start: usize = 0; if (ansi_restet_index != null) { // `start` is the index of the last character of the ANSI reset escape sequence + 1 start = ansi_restet_index.? + ansi_reset.len + 1; if (start > s.len) continue; } longest_len = @max(longest_len, s[start..].len); } return longest_len; } test "getLongestSysInfoStringLen" { const strings = [_][]const u8{ "", "", "test", "test-test", "test1" }; try std.testing.expectEqual(strings[3].len, getLongestSysInfoStringLen(strings[0..])); } pub fn countCodepoints(str: []const u8) !usize { var count: usize = 0; var i: usize = 0; while (i < str.len) { const byte_len = try std.unicode.utf8ByteSequenceLength(str[i]); if ((i + byte_len > str.len) or (!std.unicode.utf8ValidateSlice(str[i .. i + byte_len]))) return error.InvalidUtf8; count += 1; i += byte_len; } return count; } test "countCodepoints" { const str = " ████████████████"; try std.testing.expectEqual(28, try countCodepoints(str)); } pub fn getLongestAsciiArtRowLen(ascii_art: []const []const u8) !usize { var longest_len: usize = 0; for (ascii_art) |ascii_row| { longest_len = @max(longest_len, try countCodepoints(ascii_row)); } return longest_len; } test "getLongestAsciiArtRowLen" { const rows = [_][]const u8{ " ████████████████", "██░░░░██████████░░ ░░██████████░░░░██" }; try std.testing.expectEqual(40, try getLongestAsciiArtRowLen(rows[0..])); }