mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
blinking cursor
This commit is contained in:
23
src/App.zig
23
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();
|
||||
}
|
||||
|
22
src/Grid.zig
22
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,6 +293,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
||||
}
|
||||
|
||||
// Draw the cursor
|
||||
if (self.cursor_visible) {
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
.mode = 1,
|
||||
.grid_col = @intCast(u16, term.cursor.x),
|
||||
@ -316,6 +307,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
||||
.bg_b = 0xFF,
|
||||
.bg_a = 255,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the screen size for rendering. This will update the projection
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user