screen: implement initial screen data structure
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
a9c97d051b
commit
266c5ec224
6 changed files with 102 additions and 17 deletions
|
@ -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
43
src/Screen.zig
Normal 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;
|
||||
}
|
16
src/Tty.zig
16
src/Tty.zig
|
@ -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;
|
||||
}
|
||||
|
|
19
src/app.zig
19
src/app.zig
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
20
src/cell.zig
20
src/cell.zig
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue