diff --git a/src/App.zig b/src/App.zig index 6f3d6fd31..0f33bb20a 100644 --- a/src/App.zig +++ b/src/App.zig @@ -76,18 +76,12 @@ pub fn run(self: App) !void { defer embed.deinit(self.alloc); try embed.start(); - // Notify the embedder to stop. We purposely do NOT wait for `join` - // here because handles with long timeouts may cause this to take a long - // time. We're exiting the app anyways if we're here so we let the OS - // clean up the threads. - defer embed.stop(); - // We need at least one handle in the event loop at all times so // that the loop doesn't spin 100% CPU. var timer = try libuv.Timer.init(self.alloc, self.loop); - defer timer.deinit(self.alloc); + errdefer timer.deinit(self.alloc); try timer.start((struct { - fn callback(_: libuv.Timer) void {} + fn callback(_: *libuv.Timer) void {} }).callback, 5000, 5000); while (!self.window.shouldClose()) { @@ -101,7 +95,14 @@ pub fn run(self: App) !void { try embed.loopRun(); } - // CLose our timer so that we can cleanly close the loop. - timer.close(null); - _ = try self.loop.run(.default); + // Close our timer so that we can cleanly close the loop. + timer.close((struct { + fn callback(t: *libuv.Timer) void { + const alloc = t.loop().getData(Allocator).?.*; + t.deinit(alloc); + } + }).callback); + + embed.stop(); + try embed.join(); } diff --git a/src/Grid.zig b/src/Grid.zig index 63fdbf58f..02ea575f9 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -10,7 +10,6 @@ const FontAtlas = @import("FontAtlas.zig"); const Terminal = @import("terminal/Terminal.zig"); const gl = @import("opengl.zig"); const gb = @import("gb_math.zig"); -const libuv = @import("libuv/main.zig"); const log = std.log.scoped(.grid); @@ -35,8 +34,9 @@ texture: gl.Texture, /// The font atlas. font_atlas: FontAtlas, -/// The timer for cursor blining. -cursor_timer: libuv.Timer, +/// Whether the cursor is visible or not. This is used to control cursor +/// blinking. +cursor_visible: bool, /// The raw structure that maps directly to the buffer sent to the vertex shader. const GPUCell = struct { @@ -72,11 +72,7 @@ const GPUCell = struct { mode: u8, }; -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); - +pub fn init(alloc: Allocator) !Grid { // Initialize our font atlas. We will initially populate the // font atlas with all the visible ASCII characters since they are common. var atlas = try Atlas.init(alloc, 512); @@ -214,17 +210,11 @@ pub fn init(alloc: Allocator, loop: libuv.Loop) !Grid { .vbo = vbo, .texture = tex, .font_atlas = font, - .cursor_timer = timer, + .cursor_visible = true, }; } 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.deinit(self.alloc); self.texture.destroy(); @@ -303,19 +293,21 @@ pub fn updateCells(self: *Grid, term: Terminal) !void { } // Draw the cursor - self.cells.appendAssumeCapacity(.{ - .mode = 1, - .grid_col = @intCast(u16, term.cursor.x), - .grid_row = @intCast(u16, term.cursor.y), - .fg_r = 0, - .fg_g = 0, - .fg_b = 0, - .fg_a = 0, - .bg_r = 0xFF, - .bg_g = 0xFF, - .bg_b = 0xFF, - .bg_a = 255, - }); + if (self.cursor_visible) { + self.cells.appendAssumeCapacity(.{ + .mode = 1, + .grid_col = @intCast(u16, term.cursor.x), + .grid_row = @intCast(u16, term.cursor.y), + .fg_r = 0, + .fg_g = 0, + .fg_b = 0, + .fg_a = 0, + .bg_r = 0xFF, + .bg_g = 0xFF, + .bg_b = 0xFF, + .bg_a = 255, + }); + } } /// Set the screen size for rendering. This will update the projection diff --git a/src/Window.zig b/src/Window.zig index 9a19fa376..80e5c86a6 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -35,6 +35,9 @@ pty: Pty, /// a renderer. terminal: Terminal, +/// Timer that blinks the cursor. +cursor_timer: libuv.Timer, + /// Create a new window. This allocates and returns a pointer because we /// need a stable pointer for user data callbacks. Therefore, a stack-only /// initialization is not currently possible. @@ -78,7 +81,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window { // Create our terminal grid with the initial window size const window_size = try window.getSize(); - var grid = try Grid.init(alloc, loop); + var grid = try Grid.init(alloc); try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height }); // Create our pty @@ -95,12 +98,20 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window { errdefer term.deinit(alloc); try term.append(alloc, "> "); + // Setup a timer for blinking the cursor + var timer = try libuv.Timer.init(alloc, loop); + errdefer timer.deinit(alloc); + errdefer timer.close(null); + timer.setData(self); + try timer.start(cursorTimerCallback, 800, 800); + self.* = .{ .alloc = alloc, .window = window, .grid = grid, .pty = pty, .terminal = term, + .cursor_timer = timer, }; // Setup our callbacks and user data @@ -113,6 +124,12 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window { } pub fn destroy(self: *Window) void { + self.cursor_timer.close((struct { + fn callback(t: *libuv.Timer) void { + const alloc = t.loop().getData(Allocator).?.*; + t.deinit(alloc); + } + }).callback); self.terminal.deinit(self.alloc); self.pty.deinit(); self.grid.deinit(); @@ -195,3 +212,9 @@ fn keyCallback( win.grid.updateCells(win.terminal) catch unreachable; } } + +fn cursorTimerCallback(t: *libuv.Timer) void { + const win = t.getData(Window) orelse return; + win.grid.cursor_visible = !win.grid.cursor_visible; + win.grid.updateCells(win.terminal) catch unreachable; +} diff --git a/src/libuv/Timer.zig b/src/libuv/Timer.zig index 5b0a288e9..b123cc738 100644 --- a/src/libuv/Timer.zig +++ b/src/libuv/Timer.zig @@ -32,14 +32,14 @@ pub fn deinit(self: *Timer, alloc: Allocator) void { /// and then repeatedly after repeat milliseconds. pub fn start( self: Timer, - comptime cb: fn (Timer) void, + comptime cb: fn (*Timer) void, timeout: u64, repeat: u64, ) !void { const Wrapper = struct { pub fn callback(handle: [*c]c.uv_timer_t) callconv(.C) void { - const newSelf: Timer = .{ .handle = handle }; - @call(.{ .modifier = .always_inline }, cb, .{newSelf}); + var newSelf: Timer = .{ .handle = handle }; + @call(.{ .modifier = .always_inline }, cb, .{&newSelf}); } }; @@ -65,7 +65,7 @@ test "Timer" { var called: bool = false; timer.setData(&called); try timer.start((struct { - fn callback(t: Timer) void { + fn callback(t: *Timer) void { t.getData(bool).?.* = true; t.close(null); } diff --git a/src/libuv/tests.zig b/src/libuv/tests.zig index d407dc182..b27c6214d 100644 --- a/src/libuv/tests.zig +++ b/src/libuv/tests.zig @@ -16,7 +16,7 @@ test "Async: cancel timer" { // Start a timer with a long timeout. This will block our loop. try timer.start((struct { - fn callback(_: libuv.Timer) void {} + fn callback(_: *libuv.Timer) void {} }).callback, 5000, 5000); var async_handle = try libuv.Async.init(testing.allocator, loop, (struct {