diff --git a/src/ascii_art.zig b/src/ascii_art.zig index 8555749..71ab6f7 100644 --- a/src/ascii_art.zig +++ b/src/ascii_art.zig @@ -1,5 +1,7 @@ const std = @import("std"); + +// We stop assuming logo lines is of the same length as the info block fn safeNextLine(iter: *std.mem.SplitIterator(u8, .sequence)) []const u8 { return iter.next() orelse ""; } @@ -204,6 +206,20 @@ fn getDistroLogo(distro_name: []const u8) []const u8 { \\ -+ \\ - }, + .{ + "NixOS", + \\ _ _ _ _ + \\ __/ / \_____/ \ \__ + \\ _/ _/ \_ \_ + \\ /_ / _ _____ _ \ _\ + \\ /_/ / \/ _ \/ \ \_\ + \\ \_\ \_/\_/ \_/\_/ /_/ + \\ _/ / \ \_ + \\ /__/ __ ___ __ \__\ + \\ \_ \ \ / _ \ / / _/ + \\ \_ \ V / \ V / _/ + \\ \_/ \_/ \_/ + }, }); // Use the normalized name to look up the logo diff --git a/src/hardware.zig b/src/hardware.zig index f963b10..ac40689 100644 --- a/src/hardware.zig +++ b/src/hardware.zig @@ -45,19 +45,81 @@ pub fn getCPUInfo(allocator: std.mem.Allocator) ![]u8 { } pub fn getGPUInfo(allocator: std.mem.Allocator) ![]u8 { - const lspci_output = try executeCommand(allocator, &[_][]const u8{"lspci"}); - defer allocator.free(lspci_output); - - // Parse the GPU information - var lines = std.mem.splitSequence(u8, lspci_output, "\n"); - while (lines.next()) |line| { - if (std.mem.indexOf(u8, line, "VGA compatible controller:")) |vga_index| { - const gpu_info = std.mem.trim(u8, line[vga_index + "VGA compatible controller:".len ..], " "); - return try allocator.dupe(u8, gpu_info); + // --- 1. Try lspci --- + if (executeCommand(allocator, &[_][]const u8{"lspci"})) |lspci_output| { + defer allocator.free(lspci_output); + + var lines = std.mem.splitSequence(u8, lspci_output, "\n"); + while (lines.next()) |line| { + if (std.mem.indexOf(u8, line, "VGA compatible controller:")) |vga_index| { + const gpu_info = std.mem.trim( + u8, + line[vga_index + "VGA compatible controller:".len ..], + " " + ); + return try allocator.dupe(u8, gpu_info); + } } + } else |_| { + // silently ignore if command missing } - return try allocator.dupe(u8, "Unknown"); + // --- 2. Try glxinfo --- + if (executeCommand(allocator, &[_][]const u8{"glxinfo"})) |glx_output| { + defer allocator.free(glx_output); + + var lines = std.mem.splitSequence(u8, glx_output, "\n"); + while (lines.next()) |line| { + if (std.mem.startsWith(u8, line, "OpenGL renderer string:")) { + const gpu_info = std.mem.trim( + u8, + line["OpenGL renderer string:".len ..], + " " + ); + return try allocator.dupe(u8, gpu_info); + } + } + } else |_| {} + + // --- 3. Try vulkaninfo --- + if (executeCommand(allocator, &[_][]const u8{"vulkaninfo"})) |vk_output| { + defer allocator.free(vk_output); + + var lines = std.mem.splitSequence(u8, vk_output, "\n"); + while (lines.next()) |line| { + if (std.mem.indexOf(u8, line, "deviceName")) |idx| { + const gpu_info = std.mem.trim( + u8, + line[idx + "deviceName".len ..], + " :\t" + ); + return try allocator.dupe(u8, gpu_info); + } + } + } else |_| {} + + // --- 4. Try sysfs (/sys/class/drm) --- + if (std.fs.cwd().openFile("/sys/class/drm/card0/device/vendor", .{})) |vendor_file| { + defer vendor_file.close(); + if (std.fs.cwd().openFile("/sys/class/drm/card0/device/device", .{})) |device_file| { + defer device_file.close(); + + const vendor = try vendor_file.readToEndAlloc(allocator, 16); + defer allocator.free(vendor); + const device = try device_file.readToEndAlloc(allocator, 16); + defer allocator.free(device); + + const combined = try std.fmt.allocPrint( + allocator, + "PCI Vendor: {s}, Device: {s}", + .{ std.mem.trim(u8, vendor, "\n "), std.mem.trim(u8, device, "\n ") } + ); + return combined; + } else |_| {} + } else |_| {} + + // --- Fallback --- + return try allocator.dupe(u8, "Unknown GPU"); } fn executeCommand(allocator: std.mem.Allocator, argv: []const []const u8) ![]const u8 { diff --git a/src/main.zig b/src/main.zig index e578cd5..345353e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -55,7 +55,14 @@ pub fn main() !void { const shell_version = try system.getShellVersion(allocator); defer allocator.free(shell_version); - const uptime = try system.executeCommand(allocator, &[_][]const u8{ "uptime", "-p" }); + const uptime = system.executeUptimeCommand(allocator, &[_][]const u8{ "uptime", "-p" }) catch |err| blk: { + // Because NixOS does not have a -p + if (err == error.CommandFailed) { + break :blk try system.executeUptimeCommand(allocator, &[_][]const u8{ "uptime" }); + } else { + return err; + } + }; defer allocator.free(uptime); //read hardware model diff --git a/src/package.zig b/src/package.zig index 2dbc8dd..3d3e7ed 100644 --- a/src/package.zig +++ b/src/package.zig @@ -14,6 +14,8 @@ pub fn getInstalledPackagesCount(allocator: std.mem.Allocator) !usize { return try getDebianPackageCount(allocator); } else if (std.mem.eql(u8, distro_family, "fedora") or std.mem.eql(u8, distro_family, "rhel")) { return try getFedoraPackageCount(allocator); + } else if (std.mem.eql(u8, distro_family, "nixos")) { + return try getNixPackageCount(allocator); } else { return error.UnsupportedDistro; } @@ -88,3 +90,29 @@ fn getFedoraPackageCount(allocator: std.mem.Allocator) !usize { return count; } + +fn getNixPackageCount(allocator: std.mem.Allocator) !usize { + var process = std.process.Child.init(&.{ "nix-store", "--query", "--requisites", "/run/current-system" }, allocator); + + process.stdout_behavior = .Pipe; + process.stderr_behavior = .Pipe; + + try process.spawn(); + const stdout = try process.stdout.?.readToEndAlloc(allocator, std.math.maxInt(usize)); + defer allocator.free(stdout); + + const term = try process.wait(); + switch (term) { + .Exited => |code| if (code != 0) return error.NixCommandFailed, + else => return error.NixCommandFailed, + } + + var count: usize = 0; + var lines = std.mem.splitSequence(u8, stdout, "\n"); + while (lines.next()) |line| { + if (line.len > 0) { + count += 1; + } + } + return count; +} diff --git a/src/system.zig b/src/system.zig index 6d8100d..3a8b1e9 100644 --- a/src/system.zig +++ b/src/system.zig @@ -120,3 +120,27 @@ pub fn executeCommand(allocator: std.mem.Allocator, argv: []const []const u8) ![ return try result.toOwnedSlice(allocator); } + +pub fn executeUptimeCommand(allocator: std.mem.Allocator, argv: []const []const u8) ![] u8 { + var result = std.ArrayListUnmanaged(u8){}; + defer result.deinit(allocator); + + var child = std.process.Child.init(argv, allocator); + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stdout = try child.stdout.?.readToEndAlloc(allocator, std.math.maxInt(usize)); + defer allocator.free(stdout); + + try result.appendSlice(allocator, stdout); + + const term = try child.wait(); + if (term.Exited != 0) { + return error.CommandFailed; + } + + return result.toOwnedSlice(allocator); +} +