mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +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);
|
defer embed.deinit(self.alloc);
|
||||||
try embed.start();
|
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
|
// We need at least one handle in the event loop at all times so
|
||||||
// that the loop doesn't spin 100% CPU.
|
// that the loop doesn't spin 100% CPU.
|
||||||
var timer = try libuv.Timer.init(self.alloc, self.loop);
|
var timer = try libuv.Timer.init(self.alloc, self.loop);
|
||||||
defer timer.deinit(self.alloc);
|
errdefer timer.deinit(self.alloc);
|
||||||
try timer.start((struct {
|
try timer.start((struct {
|
||||||
fn callback(_: libuv.Timer) void {}
|
fn callback(_: *libuv.Timer) void {}
|
||||||
}).callback, 5000, 5000);
|
}).callback, 5000, 5000);
|
||||||
|
|
||||||
while (!self.window.shouldClose()) {
|
while (!self.window.shouldClose()) {
|
||||||
@ -101,7 +95,14 @@ pub fn run(self: App) !void {
|
|||||||
try embed.loopRun();
|
try embed.loopRun();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLose our timer so that we can cleanly close the loop.
|
// Close our timer so that we can cleanly close the loop.
|
||||||
timer.close(null);
|
timer.close((struct {
|
||||||
_ = try self.loop.run(.default);
|
fn callback(t: *libuv.Timer) void {
|
||||||
|
const alloc = t.loop().getData(Allocator).?.*;
|
||||||
|
t.deinit(alloc);
|
||||||
|
}
|
||||||
|
}).callback);
|
||||||
|
|
||||||
|
embed.stop();
|
||||||
|
try embed.join();
|
||||||
}
|
}
|
||||||
|
48
src/Grid.zig
48
src/Grid.zig
@ -10,7 +10,6 @@ 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);
|
||||||
|
|
||||||
@ -35,8 +34,9 @@ texture: gl.Texture,
|
|||||||
/// The font atlas.
|
/// The font atlas.
|
||||||
font_atlas: FontAtlas,
|
font_atlas: FontAtlas,
|
||||||
|
|
||||||
/// The timer for cursor blining.
|
/// Whether the cursor is visible or not. This is used to control cursor
|
||||||
cursor_timer: libuv.Timer,
|
/// blinking.
|
||||||
|
cursor_visible: bool,
|
||||||
|
|
||||||
/// 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 {
|
||||||
@ -72,11 +72,7 @@ const GPUCell = struct {
|
|||||||
mode: u8,
|
mode: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, loop: libuv.Loop) !Grid {
|
pub fn init(alloc: Allocator) !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);
|
||||||
@ -214,17 +210,11 @@ pub fn init(alloc: Allocator, loop: libuv.Loop) !Grid {
|
|||||||
.vbo = vbo,
|
.vbo = vbo,
|
||||||
.texture = tex,
|
.texture = tex,
|
||||||
.font_atlas = font,
|
.font_atlas = font,
|
||||||
.cursor_timer = timer,
|
.cursor_visible = true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
@ -303,19 +293,21 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the cursor
|
// Draw the cursor
|
||||||
self.cells.appendAssumeCapacity(.{
|
if (self.cursor_visible) {
|
||||||
.mode = 1,
|
self.cells.appendAssumeCapacity(.{
|
||||||
.grid_col = @intCast(u16, term.cursor.x),
|
.mode = 1,
|
||||||
.grid_row = @intCast(u16, term.cursor.y),
|
.grid_col = @intCast(u16, term.cursor.x),
|
||||||
.fg_r = 0,
|
.grid_row = @intCast(u16, term.cursor.y),
|
||||||
.fg_g = 0,
|
.fg_r = 0,
|
||||||
.fg_b = 0,
|
.fg_g = 0,
|
||||||
.fg_a = 0,
|
.fg_b = 0,
|
||||||
.bg_r = 0xFF,
|
.fg_a = 0,
|
||||||
.bg_g = 0xFF,
|
.bg_r = 0xFF,
|
||||||
.bg_b = 0xFF,
|
.bg_g = 0xFF,
|
||||||
.bg_a = 255,
|
.bg_b = 0xFF,
|
||||||
});
|
.bg_a = 255,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the screen size for rendering. This will update the projection
|
/// Set the screen size for rendering. This will update the projection
|
||||||
|
@ -35,6 +35,9 @@ pty: Pty,
|
|||||||
/// a renderer.
|
/// a renderer.
|
||||||
terminal: Terminal,
|
terminal: Terminal,
|
||||||
|
|
||||||
|
/// Timer that blinks the cursor.
|
||||||
|
cursor_timer: libuv.Timer,
|
||||||
|
|
||||||
/// 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.
|
||||||
@ -78,7 +81,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*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, loop);
|
var grid = try Grid.init(alloc);
|
||||||
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
|
||||||
@ -95,12 +98,20 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window {
|
|||||||
errdefer term.deinit(alloc);
|
errdefer term.deinit(alloc);
|
||||||
try term.append(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.* = .{
|
self.* = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.window = window,
|
.window = window,
|
||||||
.grid = grid,
|
.grid = grid,
|
||||||
.pty = pty,
|
.pty = pty,
|
||||||
.terminal = term,
|
.terminal = term,
|
||||||
|
.cursor_timer = timer,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup our callbacks and user data
|
// 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 {
|
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.terminal.deinit(self.alloc);
|
||||||
self.pty.deinit();
|
self.pty.deinit();
|
||||||
self.grid.deinit();
|
self.grid.deinit();
|
||||||
@ -195,3 +212,9 @@ fn keyCallback(
|
|||||||
win.grid.updateCells(win.terminal) catch unreachable;
|
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.
|
/// and then repeatedly after repeat milliseconds.
|
||||||
pub fn start(
|
pub fn start(
|
||||||
self: Timer,
|
self: Timer,
|
||||||
comptime cb: fn (Timer) void,
|
comptime cb: fn (*Timer) void,
|
||||||
timeout: u64,
|
timeout: u64,
|
||||||
repeat: u64,
|
repeat: u64,
|
||||||
) !void {
|
) !void {
|
||||||
const Wrapper = struct {
|
const Wrapper = struct {
|
||||||
pub fn callback(handle: [*c]c.uv_timer_t) callconv(.C) void {
|
pub fn callback(handle: [*c]c.uv_timer_t) callconv(.C) void {
|
||||||
const newSelf: Timer = .{ .handle = handle };
|
var newSelf: Timer = .{ .handle = handle };
|
||||||
@call(.{ .modifier = .always_inline }, cb, .{newSelf});
|
@call(.{ .modifier = .always_inline }, cb, .{&newSelf});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ test "Timer" {
|
|||||||
var called: bool = false;
|
var called: bool = false;
|
||||||
timer.setData(&called);
|
timer.setData(&called);
|
||||||
try timer.start((struct {
|
try timer.start((struct {
|
||||||
fn callback(t: Timer) void {
|
fn callback(t: *Timer) void {
|
||||||
t.getData(bool).?.* = true;
|
t.getData(bool).?.* = true;
|
||||||
t.close(null);
|
t.close(null);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ test "Async: cancel timer" {
|
|||||||
|
|
||||||
// Start a timer with a long timeout. This will block our loop.
|
// Start a timer with a long timeout. This will block our loop.
|
||||||
try timer.start((struct {
|
try timer.start((struct {
|
||||||
fn callback(_: libuv.Timer) void {}
|
fn callback(_: *libuv.Timer) void {}
|
||||||
}).callback, 5000, 5000);
|
}).callback, 5000, 5000);
|
||||||
|
|
||||||
var async_handle = try libuv.Async.init(testing.allocator, loop, (struct {
|
var async_handle = try libuv.Async.init(testing.allocator, loop, (struct {
|
||||||
|
Reference in New Issue
Block a user