screen: implement initial screen data structure

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
Tim Culverhouse 2024-01-18 23:17:57 -06:00
parent a9c97d051b
commit 266c5ec224
6 changed files with 102 additions and 17 deletions

View file

@ -3,29 +3,41 @@ const odditui = @import("odditui");
const log = std.log.scoped(.main);
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const deinit_status = gpa.deinit();
//fail test; can't try in defer as defer is executed after we return
if (deinit_status == .leak) {
log.err("memory leak", .{});
}
}
const alloc = gpa.allocator();
var app: odditui.App(Event) = try odditui.App(Event).init(.{});
defer app.deinit();
defer app.deinit(alloc);
try app.start();
defer app.stop();
outer: while (true) {
const event = app.nextEvent();
log.debug("event: {}\r\n", .{event});
switch (event) {
.key_press => |key| {
if (key.codepoint == 'c' and key.mods.ctrl) {
break :outer;
}
},
.winsize => {},
.winsize => |ws| {
try app.resize(alloc, ws.rows, ws.cols);
},
else => {},
}
log.debug("event: {}\r\n", .{event});
}
}
const Event = union(enum) {
key_press: odditui.Key,
winsize: std.os.system.winsize,
winsize: odditui.Winsize,
mouse: u8,
};

43
src/Screen.zig Normal file
View file

@ -0,0 +1,43 @@
const std = @import("std");
const Cell = @import("cell.zig").Cell;
const Screen = @This();
width: usize,
height: usize,
buf: []Cell = undefined,
pub fn init() Screen {
return Screen{
.width = 0,
.height = 0,
};
}
pub fn deinit(self: *Screen, alloc: std.mem.Allocator) void {
alloc.free(self.buf);
}
pub fn resize(self: *Screen, alloc: std.mem.Allocator, w: usize, h: usize) !void {
alloc.free(self.buf);
self.buf = try alloc.alloc(Cell, w * h);
self.width = w;
self.height = h;
}
/// writes a cell to a location. 0 indexed
pub fn writeCell(self: *Screen, cell: Cell, row: usize, col: usize) void {
if (self.width < col) {
// column out of bounds
return;
}
if (self.height < row) {
// height out of bounds
return;
}
const i = (col * self.width) + row;
std.debug.assert(i < self.buf.len);
self.buf[i] = cell;
}

View file

@ -218,7 +218,14 @@ fn ior(inout: u32, group: usize, num: usize, len: usize) usize {
return (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num));
}
fn getWinsize(fd: os.fd_t) !os.system.winsize {
pub const Winsize = struct {
rows: usize,
cols: usize,
x_pixel: usize,
y_pixel: usize,
};
fn getWinsize(fd: os.fd_t) !Winsize {
var winsize = os.system.winsize{
.ws_row = 0,
.ws_col = 0,
@ -228,6 +235,11 @@ fn getWinsize(fd: os.fd_t) !os.system.winsize {
const err = os.system.ioctl(fd, TIOCGWINSZ, @intFromPtr(&winsize));
if (os.errno(err) == .SUCCESS)
return winsize;
return Winsize{
.rows = winsize.ws_row,
.cols = winsize.ws_col,
.x_pixel = winsize.ws_xpixel,
.y_pixel = winsize.ws_ypixel,
};
return error.IoctlError;
}

View file

@ -3,11 +3,17 @@ const std = @import("std");
const queue = @import("queue.zig");
const Tty = @import("Tty.zig");
const Key = @import("Key.zig");
const Screen = @import("Screen.zig");
/// App is the entrypoint for an odditui application. The provided type T should
/// be a tagged union which contains all of the events the application will
/// handle. Odditui will look for the following fields on the union and, if
/// found, emit them via the "nextEvent" method
///
/// The following events *must* be in your enum
/// - `key_press: Key`, for key press events
/// - `winsize: std.os.system.winsize`, for resize events. Must call app.resize
/// when receiving this event
pub fn App(comptime T: type) type {
return struct {
const Self = @This();
@ -23,6 +29,8 @@ pub fn App(comptime T: type) type {
tty: ?Tty,
screen: Screen,
/// Runtime options
const Options = struct {};
@ -31,14 +39,16 @@ pub fn App(comptime T: type) type {
return Self{
.queue = .{},
.tty = null,
.screen = Screen.init(),
};
}
pub fn deinit(self: *Self) void {
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
if (self.tty) |_| {
var tty = &self.tty.?;
tty.deinit();
}
self.screen.deinit(alloc);
}
/// spawns the input thread to start listening to the tty for input
@ -66,6 +76,13 @@ pub fn App(comptime T: type) type {
pub fn postEvent(self: *Self, event: T) void {
self.queue.push(event);
}
/// resize allocates a slice of cellsequal to the number of cells
/// required to display the screen (ie width x height). Any previous screen is
/// freed when resizing
pub fn resize(self: *Self, alloc: std.mem.Allocator, w: usize, h: usize) !void {
try self.screen.resize(alloc, w, h);
}
};
}

View file

@ -9,10 +9,19 @@ pub const Character = struct {
};
pub const Style = struct {
pub const Underline = enum {
off,
single,
double,
curly,
dotted,
dashed,
};
fg: Color = .default,
bg: Color = .default,
ul: Color = .default,
ul_style: UnderlineStyle = .off,
ul_style: Underline = .off,
url: ?[]const u8 = null,
url_params: ?[]const u8 = null,
};
@ -22,12 +31,3 @@ pub const Color = union(enum) {
index: u8,
rgb: [3]u8,
};
pub const UnderlineStyle = enum {
off,
single,
double,
curly,
dotted,
dashed,
};

View file

@ -1,5 +1,6 @@
pub const App = @import("app.zig").App;
pub const Key = @import("Key.zig");
pub const Winsize = @import("Tty.zig").Winsize;
test {
_ = @import("Key.zig");