blinking cursor

This commit is contained in:
Mitchell Hashimoto
2022-04-22 15:33:50 -07:00
parent f8b305df62
commit cd602b660c
5 changed files with 61 additions and 45 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 {