ncdu-zig/src/ui.zig

147 lines
5.4 KiB
Zig
Raw Normal View History

// Ncurses wrappers and TUI helper functions.
const std = @import("std");
const main = @import("main.zig");
pub const c = @cImport({
@cInclude("stdio.h");
@cInclude("string.h");
@cInclude("unistd.h");
@cInclude("curses.h");
});
var inited: bool = false;
pub var rows: i32 = undefined;
pub var cols: i32 = undefined;
pub fn die(comptime fmt: []const u8, args: anytype) noreturn {
deinit();
_ = std.io.getStdErr().writer().print(fmt, args) catch {};
std.process.exit(1);
}
const StyleAttr = struct { fg: i16, bg: i16, attr: u32 };
const StyleDef = struct {
name: []const u8,
off: StyleAttr,
dark: StyleAttr,
fn style(self: *const @This()) StyleAttr {
return switch (main.config.ui_color) {
.off => self.off,
.dark => self.dark,
};
}
};
const styles = [_]StyleDef{
.{ .name = "default",
.off = .{ .fg = -1, .bg = -1, .attr = 0 },
.dark = .{ .fg = -1, .bg = -1, .attr = 0 } },
.{ .name = "box_title",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_BOLD },
.dark = .{ .fg = c.COLOR_BLUE, .bg = -1, .attr = c.A_BOLD } },
.{ .name = "hd", // header + footer
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_BLACK, .bg = c.COLOR_CYAN, .attr = 0 } },
.{ .name = "sel",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_WHITE, .bg = c.COLOR_GREEN, .attr = c.A_BOLD } },
.{ .name = "num",
.off = .{ .fg = -1, .bg = -1, .attr = 0 },
.dark = .{ .fg = c.COLOR_YELLOW, .bg = -1, .attr = c.A_BOLD } },
.{ .name = "num_hd",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_YELLOW, .bg = c.COLOR_CYAN, .attr = c.A_BOLD } },
.{ .name = "num_sel",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_YELLOW, .bg = c.COLOR_GREEN, .attr = c.A_BOLD } },
.{ .name = "key",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_BOLD },
.dark = .{ .fg = c.COLOR_YELLOW, .bg = -1, .attr = c.A_BOLD } },
.{ .name = "key_hd",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_BOLD|c.A_REVERSE },
.dark = .{ .fg = c.COLOR_YELLOW, .bg = c.COLOR_CYAN, .attr = c.A_BOLD } },
.{ .name = "dir",
.off = .{ .fg = -1, .bg = -1, .attr = 0 },
.dark = .{ .fg = c.COLOR_BLUE, .bg = -1, .attr = c.A_BOLD } },
.{ .name = "dir_sel",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_BLUE, .bg = c.COLOR_GREEN, .attr = c.A_BOLD } },
.{ .name = "flag",
.off = .{ .fg = -1, .bg = -1, .attr = 0 },
.dark = .{ .fg = c.COLOR_RED, .bg = -1, .attr = 0 } },
.{ .name = "flag_sel",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_RED, .bg = c.COLOR_GREEN, .attr = 0 } },
.{ .name = "graph",
.off = .{ .fg = -1, .bg = -1, .attr = 0 },
.dark = .{ .fg = c.COLOR_MAGENTA, .bg = -1, .attr = 0 } },
.{ .name = "graph_sel",
.off = .{ .fg = -1, .bg = -1, .attr = c.A_REVERSE },
.dark = .{ .fg = c.COLOR_MAGENTA, .bg = c.COLOR_GREEN, .attr = 0 } },
};
const Style = lbl: {
var fields: [styles.len]std.builtin.TypeInfo.EnumField = undefined;
var decls = [_]std.builtin.TypeInfo.Declaration{};
inline for (styles) |s, i| {
fields[i] = .{
.name = s.name,
.value = i,
};
}
break :lbl @Type(.{
.Enum = .{
.layout = .Auto,
.tag_type = u8,
.fields = &fields,
.decls = &decls,
.is_exhaustive = true,
}
});
};
pub fn style(s: Style) void {
_ = c.attr_set(styles[@enumToInt(s)].style().attr, @enumToInt(s)+1, null);
}
fn updateSize() void {
// getmax[yx] macros are marked as "legacy", but Zig can't deal with the "proper" getmaxyx macro.
rows = c.getmaxy(c.stdscr);
cols = c.getmaxx(c.stdscr);
}
pub fn init() void {
if (inited) return;
if (main.config.nc_tty) {
var tty = c.fopen("/dev/tty", "r+");
if (tty == null) die("Error opening /dev/tty: {s}.\n", .{ c.strerror(std.c.getErrno(-1)) });
var term = c.newterm(null, tty, tty);
if (term == null) die("Error initializing ncurses.\n", .{});
_ = c.set_term(term);
} else {
if (c.isatty(0) != 1) die("Standard input is not a TTY. Did you mean to import a file using '-f -'?\n", .{});
if (c.initscr() == null) die("Error initializing ncurses.\n", .{});
}
updateSize();
_ = c.cbreak();
_ = c.noecho();
_ = c.curs_set(0);
_ = c.keypad(c.stdscr, true);
_ = c.start_color();
_ = c.use_default_colors();
for (styles) |s, i| _ = c.init_pair(@intCast(i16, i+1), s.style().fg, s.style().bg);
inited = true;
}
pub fn deinit() void {
if (!inited) return;
_ = c.erase();
_ = c.refresh();
_ = c.endwin();
inited = false;
}