From c701506d132104293b05d1b93bbfa254ee479dbc Mon Sep 17 00:00:00 2001 From: Squibid Date: Thu, 25 Dec 2025 08:12:34 -0500 Subject: [PATCH] make arcade picker --- src/subcmds/arcade/arcade.zig | 97 +++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/src/subcmds/arcade/arcade.zig b/src/subcmds/arcade/arcade.zig index 7c93c6d..99e11a7 100644 --- a/src/subcmds/arcade/arcade.zig +++ b/src/subcmds/arcade/arcade.zig @@ -1,8 +1,95 @@ const std = @import("std"); +const gpa = std.heap.page_allocator; const Conway = @import("conway.zig"); -const games = enum{ conway }; +const games = enum(u16) { + @"Conways Game of Life", +}; + +fn ui(width: u32, height: u32, writer: *std.Io.Writer) !void { + var fd = [_]std.posix.pollfd{.{ + .fd = std.posix.STDIN_FILENO, + .events = std.posix.POLL.IN, + .revents = 0 + }}; + + // here we're disabling character echoing and newline requirements on input + const oldios = try std.posix.tcgetattr(std.posix.STDIN_FILENO); + defer std.posix.tcsetattr(std.posix.STDIN_FILENO, std.posix.TCSA.NOW, oldios) catch { + // oh well we've left the terminal in a shitty state, not much we can + // do to fix this. + }; + var newios = oldios; + + newios.lflag.ECHO = false; + newios.lflag.ICANON = false; + try std.posix.tcsetattr(std.posix.STDIN_FILENO, std.posix.TCSA.NOW, newios); + + _ = try writer.write("\x1B[?25l"); // hide cursor + var running = true; + var cursor_y: i64 = 0; + while (running) { + try writer.print("\x1B[1;1H\x1B[2J", .{}); // clear + + try writer.print("\x1B[{};{}H", .{(height - 5) / 2, 0}); // y, x + try writer.alignBuffer(" @@@@@@ @@@@@@@ @@@@@@@ @@@@@@ @@@@@@@ @@@@@@@@", width, .center, ' '); + try writer.alignBuffer("@@! @@@ @@! @@@ !@@ @@! @@@ @@! @@@ @@! ", width, .center, ' '); + try writer.alignBuffer("@!@!@!@! @!@!!@! !@! @!@!@!@! @!@ !@! @!!!:! ", width, .center, ' '); + try writer.alignBuffer("!!: !!! !!: :!! :!! !!: !!! !!: !!! !!: ", width, .center, ' '); + try writer.alignBuffer(" : : : : : : :: :: : : : : :: : : : :: :::", width, .center, ' '); + + cursor_y = @intCast(std.math.clamp(cursor_y, 0, @typeInfo(games).@"enum".fields.len - 1)); + inline for (@typeInfo(games).@"enum".fields, 0..) |f, i| { + try writer.print("\x1B[{};{}H", .{ // y, x + ((height + i) / 2) + 3, + (width - f.name.len) / 2, + }); + try writer.print("{s}{s}\x1B[0m\n", .{ + if (i == cursor_y) "\x1B[47;30;1m" else "\x1B[40;37m", + f.name, + }); + } + try writer.flush(); + + _ = try std.posix.poll(@constCast(&fd), -1); + var buf: [50]u8 = undefined; + _ = try std.fs.File.stdin().read(@constCast(&buf)); + for (buf) |c| switch (c) { + 'q' => { + running = false; + break; + }, + + 'j' => cursor_y += 1, + 'k' => cursor_y -= 1, + 'g' => cursor_y = 1, + 'G' => cursor_y = @intFromEnum(games.@"Conways Game of Life"), + ' ' => { + // reset the terminal state for the game + try std.posix.tcsetattr(std.posix.STDIN_FILENO, std.posix.TCSA.NOW, oldios); + _ = try writer.write("\x1B[?25h"); // show cursor + defer std.posix.tcsetattr(std.posix.STDIN_FILENO, std.posix.TCSA.NOW, newios) catch { + // not much we can do + }; + + switch (cursor_y) { + @intFromEnum(games.@"Conways Game of Life") => try Conway.play(width, height, writer), + else => @panic("Game does not exist."), + } + + _ = try writer.write("\x1B[?25l"); // hide cursor + try writer.flush(); + }, + else => break, + }; + } + + // Andrew kelly said to not defer writes cause we need to ensure that we + // flush properly. He's right. + _ = try writer.write("\x1B[?25h"); // show cursor + try writer.flush(); +} pub export fn arcade_subcmd(_: *anyopaque, argc: c_int, argv: [*c]u8) callconv(.c) void { _ = argc; @@ -18,8 +105,8 @@ pub export fn arcade_subcmd(_: *anyopaque, argc: c_int, argv: [*c]u8) callconv(. else => unreachable, } - // const title = @embedFile("arcade.txt"); - var buf: [4096]u8 = undefined; - const writer = std.fs.File.stdout().writer(&buf); - Conway.play(terminfo.col, terminfo.row, @constCast(&writer.interface)) catch unreachable; + const buf: []u8 = gpa.alloc(u8, terminfo.col * terminfo.row) catch @panic("oom"); + const writer = std.fs.File.stdout().writer(buf); + + ui(terminfo.col, terminfo.row, @constCast(&writer.interface)) catch unreachable; }