aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/ncurses.zig
diff options
context:
space:
mode:
authorjjanzen <jjanzen@jjanzen.ca>2025-01-21 23:26:23 -0600
committerjjanzen <jjanzen@jjanzen.ca>2025-01-21 23:26:23 -0600
commit900c8756edc3bc547dfd2cd47921ecd453bf703b (patch)
tree057be3b086c66bab292da8b2ae798e92f8db54b2 /src/frontend/ncurses.zig
parent385ca6511a8de0a4cc1dca774a3c743422c3d6be (diff)
doc comments and io refactor
Diffstat (limited to 'src/frontend/ncurses.zig')
-rw-r--r--src/frontend/ncurses.zig91
1 files changed, 51 insertions, 40 deletions
diff --git a/src/frontend/ncurses.zig b/src/frontend/ncurses.zig
index 2a560a8..3d4fbb7 100644
--- a/src/frontend/ncurses.zig
+++ b/src/frontend/ncurses.zig
@@ -1,3 +1,4 @@
+//! This module provides an implementation of IO using the ncurses library.
const std = @import("std");
const Action = @import("../actions.zig").Action;
const ncurses = @cImport({
@@ -17,7 +18,6 @@ const MESSAGE_PANEL_WIDTH = 100;
const MESSAGE_PANEL_HEIGHT = 3;
const STATUS_PANEL_WIDTH = 32;
const STATUS_PANEL_HEIGHT = 5;
-
const MIN_WIDTH = MAIN_PANEL_WIDTH + INSTRUCTION_PANEL_WIDTH;
const MIN_HEIGHT = MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT;
@@ -29,6 +29,7 @@ const KEY_STAIR_UP: i32 = '<';
const KEY_STAIR_DOWN: i32 = '>';
const KEY_QUIT: i32 = ncurses.KEY_F(1);
+/// Provide an IO struct that manages the state of the display and user input
pub const IO = struct {
main: ?*ncurses.WINDOW,
inst: ?*ncurses.WINDOW,
@@ -36,16 +37,19 @@ pub const IO = struct {
stat: ?*ncurses.WINDOW,
prev_tick_time: i64,
- fn validTermSize() bool {
+ fn validTermSize(self: *IO) bool {
+ _ = self; // should be uncallable without initialization
return ncurses.LINES >= MIN_HEIGHT and
ncurses.COLS >= MIN_WIDTH;
}
- fn colorSupport() bool {
+ fn colorSupport(self: *IO) bool {
+ _ = self; // should be uncallable without initialization
return ncurses.has_colors();
}
- fn createNewWin(height: i32, width: i32, y: i32, x: i32) ?*ncurses.WINDOW {
+ fn createNewWin(self: *IO, height: i32, width: i32, y: i32, x: i32) ?*ncurses.WINDOW {
+ _ = self; // should be uncallable without initialization
const local_win = ncurses.newwin(height, width, y, x);
_ = ncurses.box(local_win, 0, 0);
_ = ncurses.wrefresh(local_win);
@@ -64,6 +68,13 @@ pub const IO = struct {
}
}
+ fn createPanels(self: *IO) void {
+ self.createInstructionPanel();
+ self.createMessagePanel();
+ self.createStatisticsPanel();
+ self.createMainPanel();
+ }
+
fn handleResize(self: *IO) Action {
const lines = @as(usize, @intCast(ncurses.LINES));
const cols = @as(usize, @intCast(ncurses.COLS));
@@ -78,14 +89,10 @@ pub const IO = struct {
self.deleteWindows();
- if (!validTermSize()) {
+ if (!self.validTermSize()) {
_ = ncurses.mvprintw(0, 0, "Terminal must be at least %dx%d", @as(i32, @intCast(MIN_WIDTH)), @as(i32, @intCast(MIN_HEIGHT)));
} else {
- self.inst = createInstructionPanel();
- self.msgs = createMessagePanel();
- self.stat = createStatisticsPanel();
- self.main = createMainPanel();
- self.displayInstructions();
+ self.createPanels();
}
_ = ncurses.refresh();
@@ -93,7 +100,7 @@ pub const IO = struct {
return Action.illegal;
}
- pub fn displayInstructions(self: *IO) void {
+ fn displayInstructions(self: *IO) void {
self.formatInstruction(1, KEY_MOVE_LEFT, "move left");
self.formatInstruction(2, KEY_MOVE_DOWN, "move down");
self.formatInstruction(3, KEY_MOVE_UP, "move up");
@@ -104,6 +111,9 @@ pub const IO = struct {
_ = ncurses.wrefresh(self.inst);
}
+ /// Display a message in the message box.
+ /// Takes a format string and arguments.
+ /// If the message is too wide for the box, display "Message too long" instead.
pub fn displayMessage(self: *IO, comptime fmt: []const u8, args: anytype) void {
if (self.msgs == null) return;
@@ -126,7 +136,7 @@ pub const IO = struct {
_ = ncurses.wrefresh(self.msgs);
}
- pub fn displayStatus(self: *IO) void {
+ fn displayStatus(self: *IO) void {
for (1..STATUS_PANEL_HEIGHT - 1) |i| {
for (1..STATUS_PANEL_WIDTH - 1) |j| {
const i_32 = @as(i32, @intCast(i));
@@ -135,9 +145,14 @@ pub const IO = struct {
}
}
+ // TODO: Implement
+
_ = ncurses.wrefresh(self.stat);
}
+ /// An interface for user input and time processing.
+ /// Waits for the end of a tick and returns a tick action.
+ /// If input is given before the end of the tick, return that instead.
pub fn waitForTick(self: *IO) Action {
var new = std.time.milliTimestamp();
@@ -155,7 +170,7 @@ pub const IO = struct {
fn processInput(self: *IO) Action {
const ch = ncurses.getch();
- if (!validTermSize()) {
+ if (!self.validTermSize()) {
if (ch != KEY_QUIT and ch != ncurses.KEY_RESIZE) return Action.illegal;
}
@@ -172,17 +187,20 @@ pub const IO = struct {
};
}
- fn createInstructionPanel() ?*ncurses.WINDOW {
- return createNewWin(
+ fn createInstructionPanel(self: *IO) void {
+ self.inst = self.createNewWin(
INSTRUCTION_PANEL_HEIGHT,
INSTRUCTION_PANEL_WIDTH,
@divTrunc(ncurses.LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT, 2) + 1,
@divTrunc(ncurses.COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH, 2) + MAIN_PANEL_WIDTH - 1,
);
+ if (self.inst != null) {
+ self.displayInstructions();
+ }
}
- fn createMessagePanel() ?*ncurses.WINDOW {
- return createNewWin(
+ fn createMessagePanel(self: *IO) void {
+ self.msgs = self.createNewWin(
MESSAGE_PANEL_HEIGHT,
MESSAGE_PANEL_WIDTH,
@divTrunc(ncurses.LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT, 2) + MAIN_PANEL_HEIGHT,
@@ -190,8 +208,8 @@ pub const IO = struct {
);
}
- fn createStatisticsPanel() ?*ncurses.WINDOW {
- return createNewWin(
+ fn createStatisticsPanel(self: *IO) void {
+ self.stat = self.createNewWin(
STATUS_PANEL_HEIGHT,
STATUS_PANEL_WIDTH,
@divTrunc(ncurses.LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT, 2) + INSTRUCTION_PANEL_HEIGHT,
@@ -199,8 +217,8 @@ pub const IO = struct {
);
}
- fn createMainPanel() ?*ncurses.WINDOW {
- return createNewWin(
+ fn createMainPanel(self: *IO) void {
+ self.main = self.createNewWin(
MAIN_PANEL_HEIGHT,
MAIN_PANEL_WIDTH,
@divTrunc(ncurses.LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT, 2) + 1,
@@ -210,11 +228,20 @@ pub const IO = struct {
pub fn init() !IO {
_ = locale.setlocale(locale.LC_ALL, "");
+
+ var io = IO{
+ .inst = null,
+ .msgs = null,
+ .stat = null,
+ .main = null,
+ .prev_tick_time = std.time.milliTimestamp(),
+ };
+
if (ncurses.initscr() == null) {
return error.CursesInitFail;
}
- if (!colorSupport()) {
+ if (!io.colorSupport()) {
_ = ncurses.endwin();
return error.NoColorSupport;
}
@@ -231,26 +258,10 @@ pub const IO = struct {
_ = ncurses.wattron(ncurses.stdscr, ncurses.COLOR_PAIR(1));
_ = ncurses.refresh();
- var io: IO = undefined;
-
- if (!validTermSize()) {
+ if (!io.validTermSize()) {
_ = ncurses.mvprintw(0, 0, "Terminal must be at least %dx%d", @as(i32, @intCast(MIN_WIDTH)), @as(i32, @intCast(MIN_HEIGHT)));
- io = IO{
- .inst = null,
- .msgs = null,
- .stat = null,
- .main = null,
- .prev_tick_time = std.time.milliTimestamp(),
- };
} else {
- io = IO{
- .inst = createInstructionPanel(),
- .msgs = createMessagePanel(),
- .stat = createStatisticsPanel(),
- .main = createMainPanel(),
- .prev_tick_time = std.time.milliTimestamp(),
- };
- io.displayInstructions();
+ io.createPanels();
}
return io;