pass around the event loop, setup a timer to prove it works

This commit is contained in:
Mitchell Hashimoto
2022-04-22 13:56:39 -07:00
parent 7d48135e58
commit f8b305df62
6 changed files with 63 additions and 12 deletions

View File

@ -18,21 +18,30 @@ alloc: Allocator,
/// single window operations. /// single window operations.
window: *Window, window: *Window,
// The main event loop for the application. // The main event loop for the application. The user data of this loop
// is always the allocator used to create the loop. This is a convenience
// so that users of the loop always have an allocator.
loop: libuv.Loop, loop: libuv.Loop,
/// Initialize the main app instance. This creates the main window, sets /// Initialize the main app instance. This creates the main window, sets
/// up the renderer state, compiles the shaders, etc. This is the primary /// up the renderer state, compiles the shaders, etc. This is the primary
/// "startup" logic. /// "startup" logic.
pub fn init(alloc: Allocator) !App { pub fn init(alloc: Allocator) !App {
// Create the window
var window = try Window.create(alloc);
errdefer window.destroy();
// Create the event loop // Create the event loop
var loop = try libuv.Loop.init(alloc); var loop = try libuv.Loop.init(alloc);
errdefer loop.deinit(alloc); errdefer loop.deinit(alloc);
// We always store allocator pointer on the loop data so that
// handles can use our global allocator.
const allocPtr = try alloc.create(Allocator);
errdefer alloc.destroy(allocPtr);
allocPtr.* = alloc;
loop.setData(allocPtr);
// Create the window
var window = try Window.create(alloc, loop);
errdefer window.destroy();
return App{ return App{
.alloc = alloc, .alloc = alloc,
.window = window, .window = window,
@ -42,6 +51,15 @@ pub fn init(alloc: Allocator) !App {
pub fn deinit(self: *App) void { pub fn deinit(self: *App) void {
self.window.destroy(); self.window.destroy();
// Run the loop one more time, because destroying our other things
// like windows usually cancel all our event loop stuff and we need
// one more run through to finalize all the closes.
_ = self.loop.run(.default) catch unreachable;
// Dealloc our allocator copy
self.alloc.destroy(self.loop.getData(Allocator).?);
self.loop.deinit(self.alloc); self.loop.deinit(self.alloc);
self.* = undefined; self.* = undefined;
} }

View File

@ -10,6 +10,7 @@ const FontAtlas = @import("FontAtlas.zig");
const Terminal = @import("terminal/Terminal.zig"); const Terminal = @import("terminal/Terminal.zig");
const gl = @import("opengl.zig"); const gl = @import("opengl.zig");
const gb = @import("gb_math.zig"); const gb = @import("gb_math.zig");
const libuv = @import("libuv/main.zig");
const log = std.log.scoped(.grid); const log = std.log.scoped(.grid);
@ -34,6 +35,9 @@ texture: gl.Texture,
/// The font atlas. /// The font atlas.
font_atlas: FontAtlas, font_atlas: FontAtlas,
/// The timer for cursor blining.
cursor_timer: libuv.Timer,
/// The raw structure that maps directly to the buffer sent to the vertex shader. /// The raw structure that maps directly to the buffer sent to the vertex shader.
const GPUCell = struct { const GPUCell = struct {
/// vec2 grid_coord /// vec2 grid_coord
@ -68,7 +72,11 @@ const GPUCell = struct {
mode: u8, mode: u8,
}; };
pub fn init(alloc: Allocator) !Grid { pub fn init(alloc: Allocator, loop: libuv.Loop) !Grid {
// Setup the timer for our cursor.
var timer = try libuv.Timer.init(alloc, loop);
errdefer timer.deinit(alloc);
// Initialize our font atlas. We will initially populate the // Initialize our font atlas. We will initially populate the
// font atlas with all the visible ASCII characters since they are common. // font atlas with all the visible ASCII characters since they are common.
var atlas = try Atlas.init(alloc, 512); var atlas = try Atlas.init(alloc, 512);
@ -206,10 +214,17 @@ pub fn init(alloc: Allocator) !Grid {
.vbo = vbo, .vbo = vbo,
.texture = tex, .texture = tex,
.font_atlas = font, .font_atlas = font,
.cursor_timer = timer,
}; };
} }
pub fn deinit(self: *Grid) void { pub fn deinit(self: *Grid) void {
self.cursor_timer.close((struct {
fn callback(t: *libuv.Timer) void {
const alloc = t.loop().getData(Allocator).?;
t.deinit(alloc.*);
}
}).callback);
self.font_atlas.atlas.deinit(self.alloc); self.font_atlas.atlas.deinit(self.alloc);
self.font_atlas.deinit(self.alloc); self.font_atlas.deinit(self.alloc);
self.texture.destroy(); self.texture.destroy();

View File

@ -11,6 +11,7 @@ const Allocator = std.mem.Allocator;
const Grid = @import("Grid.zig"); const Grid = @import("Grid.zig");
const glfw = @import("glfw"); const glfw = @import("glfw");
const gl = @import("opengl.zig"); const gl = @import("opengl.zig");
const libuv = @import("libuv/main.zig");
const Pty = @import("Pty.zig"); const Pty = @import("Pty.zig");
const Terminal = @import("terminal/Terminal.zig"); const Terminal = @import("terminal/Terminal.zig");
@ -37,7 +38,7 @@ terminal: Terminal,
/// Create a new window. This allocates and returns a pointer because we /// Create a new window. This allocates and returns a pointer because we
/// need a stable pointer for user data callbacks. Therefore, a stack-only /// need a stable pointer for user data callbacks. Therefore, a stack-only
/// initialization is not currently possible. /// initialization is not currently possible.
pub fn create(alloc: Allocator) !*Window { pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window {
var self = try alloc.create(Window); var self = try alloc.create(Window);
errdefer alloc.destroy(self); errdefer alloc.destroy(self);
@ -77,7 +78,7 @@ pub fn create(alloc: Allocator) !*Window {
// Create our terminal grid with the initial window size // Create our terminal grid with the initial window size
const window_size = try window.getSize(); const window_size = try window.getSize();
var grid = try Grid.init(alloc); var grid = try Grid.init(alloc, loop);
try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height }); try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height });
// Create our pty // Create our pty

View File

@ -70,6 +70,19 @@ pub fn backendTimeout(self: Loop) c_int {
return c.uv_backend_timeout(self.loop); return c.uv_backend_timeout(self.loop);
} }
/// Sets loop->data to data.
pub fn setData(self: Loop, pointer: ?*anyopaque) void {
c.uv_loop_set_data(self.loop, pointer);
}
/// Returns loop->data.
pub fn getData(self: Loop, comptime DT: type) ?*DT {
return if (c.uv_loop_get_data(self.loop)) |ptr|
@ptrCast(?*DT, @alignCast(@alignOf(DT), ptr))
else
null;
}
/// Mode used to run the loop with uv_run(). /// Mode used to run the loop with uv_run().
pub const RunMode = enum(c.uv_run_mode) { pub const RunMode = enum(c.uv_run_mode) {
default = c.UV_RUN_DEFAULT, default = c.UV_RUN_DEFAULT,
@ -81,6 +94,10 @@ test {
var loop = try init(testing.allocator); var loop = try init(testing.allocator);
defer loop.deinit(testing.allocator); defer loop.deinit(testing.allocator);
var data: u8 = 42;
loop.setData(&data);
try testing.expect(loop.getData(u8).?.* == 42);
try testing.expect((try loop.backendFd()) > 0); try testing.expect((try loop.backendFd()) > 0);
try testing.expectEqual(@as(u32, 0), try loop.run(.nowait)); try testing.expectEqual(@as(u32, 0), try loop.run(.nowait));
} }

View File

@ -85,7 +85,7 @@ test "Timer: close callback" {
var data: u8 = 42; var data: u8 = 42;
timer.setData(&data); timer.setData(&data);
timer.close((struct { timer.close((struct {
fn callback(v: Timer) void { fn callback(v: *Timer) void {
var dataPtr = v.getData(u8).?; var dataPtr = v.getData(u8).?;
dataPtr.* = 24; dataPtr.* = 24;
} }

View File

@ -27,15 +27,15 @@ pub fn Handle(comptime T: type) type {
// //
// In-progress requests, like uv_connect_t or uv_write_t, are cancelled // In-progress requests, like uv_connect_t or uv_write_t, are cancelled
// and have their callbacks called asynchronously with status=UV_ECANCELED. // and have their callbacks called asynchronously with status=UV_ECANCELED.
pub fn close(self: T, comptime cb: ?fn (T) void) void { pub fn close(self: T, comptime cb: ?fn (*T) void) void {
const cbParam = if (cb) |f| const cbParam = if (cb) |f|
(struct { (struct {
pub fn callback(handle: [*c]c.uv_handle_t) callconv(.C) void { pub fn callback(handle: [*c]c.uv_handle_t) callconv(.C) void {
// We get the raw handle, so we need to reconstruct // We get the raw handle, so we need to reconstruct
// the T. This is mutable because a lot of the libuv APIs // the T. This is mutable because a lot of the libuv APIs
// are non-const but modifying it makes no sense. // are non-const but modifying it makes no sense.
const param: T = .{ .handle = @ptrCast(HandleType, handle) }; var param: T = .{ .handle = @ptrCast(HandleType, handle) };
@call(.{ .modifier = .always_inline }, f, .{param}); @call(.{ .modifier = .always_inline }, f, .{&param});
} }
}).callback }).callback
else else