Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/ascii_art.zig
Original file line number Diff line number Diff line change
@@ -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 "";
}
Expand Down Expand Up @@ -204,6 +206,20 @@ fn getDistroLogo(distro_name: []const u8) []const u8 {
\\ -+
\\ -
},
.{
"NixOS",
\\ _ _ _ _
\\ __/ / \_____/ \ \__
\\ _/ _/ \_ \_
\\ /_ / _ _____ _ \ _\
\\ /_/ / \/ _ \/ \ \_\
\\ \_\ \_/\_/ \_/\_/ /_/
\\ _/ / \ \_
\\ /__/ __ ___ __ \__\
\\ \_ \ \ / _ \ / / _/
\\ \_ \ V / \ V / _/
\\ \_/ \_/ \_/
},
});

// Use the normalized name to look up the logo
Expand Down
82 changes: 72 additions & 10 deletions src/hardware.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 8 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions src/package.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
24 changes: 24 additions & 0 deletions src/system.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Loading