diff --git a/src/ascii.zig b/src/ascii.zig index bed067d..4630049 100644 --- a/src/ascii.zig +++ b/src/ascii.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const utils = @import("./utils.zig"); pub const Reset = "\x1b[0m"; pub const Bold = "\x1b[1m"; @@ -89,18 +90,31 @@ pub fn printAscii(allocator: std.mem.Allocator, sys_info_list: std.ArrayList([]u const ascii_art_items = ascii_art_content_list.items; const sys_info_items = sys_info_list.items; + const terminal_size = try utils.getTerminalSize(); + const terminal_width: usize = @intCast(terminal_size.width); + + const left_alignment: usize = 45; + + const longest_sys_info_string_len = utils.getLongestSysInfoStringLen(sys_info_items); + const can_print_ascii_art: bool = terminal_width > left_alignment + longest_sys_info_string_len; + const ascii_art_len: usize = ascii_art_items.len; const sys_info_len: usize = sys_info_items.len; - const max_len: usize = if (ascii_art_len > sys_info_len) ascii_art_len else sys_info_len; + + // NOTE: sys_info_len + 3 to be able to print the colors + const max_len: usize = if ((ascii_art_len > sys_info_len) and can_print_ascii_art) ascii_art_len else sys_info_len + 3; var i: usize = 0; while (i < max_len) : (i += 1) { - if (i < ascii_art_len) { - try stdout.print("{s:<40} \t", .{ascii_art_items[i]}); - } else { - try stdout.print("{s:<40}", .{""}); + // Print the ascii art if the width of the terminal is greater than the left alignment (45) + the longest sys info string length + if (can_print_ascii_art) { + if (i < ascii_art_len) { + try stdout.print("{s:<45}", .{ascii_art_items[i]}); + } else { + try stdout.print("{s:<45}", .{""}); + } + try bw.flush(); } - try bw.flush(); if (i < sys_info_len) { try stdout.print("{s}\n", .{sys_info_items[i]}); diff --git a/src/formatters.zig b/src/formatters.zig index 91d9786..5931087 100644 --- a/src/formatters.zig +++ b/src/formatters.zig @@ -78,7 +78,7 @@ pub fn getDefaultFormattedUptimeInfo(allocator: std.mem.Allocator) !Result { pub fn getFormattedUptimeInfo(allocator: std.mem.Allocator, key: []const u8, key_color: []const u8) !Result { const uptime = try detection.system.getSystemUptime(); - return Result{ .string = try std.fmt.allocPrint(allocator, "{s}{s}:{s} {} days, {} hours, {} minutes ", .{ key_color, key, ascii.Reset, uptime.days, uptime.hours, uptime.minutes }) }; + return Result{ .string = try std.fmt.allocPrint(allocator, "{s}{s}:{s} {} days, {} hours, {} minutes", .{ key_color, key, ascii.Reset, uptime.days, uptime.hours, uptime.minutes }) }; } pub fn getDefaultFormattedPackagesInfo(allocator: std.mem.Allocator) !Result { diff --git a/src/linux/system.zig b/src/linux/system.zig index 4609940..071a6d8 100644 --- a/src/linux/system.zig +++ b/src/linux/system.zig @@ -70,8 +70,8 @@ pub fn getKernelInfo(allocator: std.mem.Allocator) !KernelInfo { } return KernelInfo{ - .kernel_name = try allocator.dupe(u8, &uts.sysname), - .kernel_release = try allocator.dupe(u8, &uts.release), + .kernel_name = try allocator.dupe(u8, std.mem.sliceTo(&uts.sysname, 0)), + .kernel_release = try allocator.dupe(u8, std.mem.sliceTo(&uts.release, 0)), }; } diff --git a/src/utils.zig b/src/utils.zig new file mode 100644 index 0000000..a15816d --- /dev/null +++ b/src/utils.zig @@ -0,0 +1,77 @@ +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.io.getStdIn(); + + 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 = try getTerminalSize(); + + 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; + } + + 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..])); +}