mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
pass around the event loop, setup a timer to prove it works
This commit is contained in:
28
src/App.zig
28
src/App.zig
@ -18,21 +18,30 @@ alloc: Allocator,
|
||||
/// single window operations.
|
||||
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,
|
||||
|
||||
/// Initialize the main app instance. This creates the main window, sets
|
||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||
/// "startup" logic.
|
||||
pub fn init(alloc: Allocator) !App {
|
||||
// Create the window
|
||||
var window = try Window.create(alloc);
|
||||
errdefer window.destroy();
|
||||
|
||||
// Create the event loop
|
||||
var loop = try libuv.Loop.init(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{
|
||||
.alloc = alloc,
|
||||
.window = window,
|
||||
@ -42,6 +51,15 @@ pub fn init(alloc: Allocator) !App {
|
||||
|
||||
pub fn deinit(self: *App) void {
|
||||
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.* = undefined;
|
||||
}
|
||||
|
17
src/Grid.zig
17
src/Grid.zig
@ -10,6 +10,7 @@ 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);
|
||||
|
||||
@ -34,6 +35,9 @@ texture: gl.Texture,
|
||||
/// The font atlas.
|
||||
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.
|
||||
const GPUCell = struct {
|
||||
/// vec2 grid_coord
|
||||
@ -68,7 +72,11 @@ const GPUCell = struct {
|
||||
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
|
||||
// font atlas with all the visible ASCII characters since they are common.
|
||||
var atlas = try Atlas.init(alloc, 512);
|
||||
@ -206,10 +214,17 @@ pub fn init(alloc: Allocator) !Grid {
|
||||
.vbo = vbo,
|
||||
.texture = tex,
|
||||
.font_atlas = font,
|
||||
.cursor_timer = timer,
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -11,6 +11,7 @@ const Allocator = std.mem.Allocator;
|
||||
const Grid = @import("Grid.zig");
|
||||
const glfw = @import("glfw");
|
||||
const gl = @import("opengl.zig");
|
||||
const libuv = @import("libuv/main.zig");
|
||||
const Pty = @import("Pty.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
|
||||
/// need a stable pointer for user data callbacks. Therefore, a stack-only
|
||||
/// 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);
|
||||
errdefer alloc.destroy(self);
|
||||
|
||||
@ -77,7 +78,7 @@ pub fn create(alloc: Allocator) !*Window {
|
||||
|
||||
// Create our terminal grid with the initial window size
|
||||
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 });
|
||||
|
||||
// Create our pty
|
||||
|
@ -70,6 +70,19 @@ pub fn backendTimeout(self: Loop) c_int {
|
||||
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().
|
||||
pub const RunMode = enum(c.uv_run_mode) {
|
||||
default = c.UV_RUN_DEFAULT,
|
||||
@ -81,6 +94,10 @@ test {
|
||||
var loop = try init(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.expectEqual(@as(u32, 0), try loop.run(.nowait));
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ test "Timer: close callback" {
|
||||
var data: u8 = 42;
|
||||
timer.setData(&data);
|
||||
timer.close((struct {
|
||||
fn callback(v: Timer) void {
|
||||
fn callback(v: *Timer) void {
|
||||
var dataPtr = v.getData(u8).?;
|
||||
dataPtr.* = 24;
|
||||
}
|
||||
|
@ -27,15 +27,15 @@ pub fn Handle(comptime T: type) type {
|
||||
//
|
||||
// In-progress requests, like uv_connect_t or uv_write_t, are cancelled
|
||||
// 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|
|
||||
(struct {
|
||||
pub fn callback(handle: [*c]c.uv_handle_t) callconv(.C) void {
|
||||
// We get the raw handle, so we need to reconstruct
|
||||
// the T. This is mutable because a lot of the libuv APIs
|
||||
// are non-const but modifying it makes no sense.
|
||||
const param: T = .{ .handle = @ptrCast(HandleType, handle) };
|
||||
@call(.{ .modifier = .always_inline }, f, .{param});
|
||||
var param: T = .{ .handle = @ptrCast(HandleType, handle) };
|
||||
@call(.{ .modifier = .always_inline }, f, .{¶m});
|
||||
}
|
||||
}).callback
|
||||
else
|
||||
|
Reference in New Issue
Block a user