diff options
Diffstat (limited to 'src/frontend')
-rw-r--r-- | src/frontend/ncurses.zig | 91 |
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; |