mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
Window thread is now single event loop!
This commit is contained in:
69
src/App.zig
69
src/App.zig
@ -7,7 +7,6 @@ const std = @import("std");
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const glfw = @import("glfw");
|
const glfw = @import("glfw");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
const libuv = @import("libuv");
|
|
||||||
const tracy = @import("tracy");
|
const tracy = @import("tracy");
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
|
|
||||||
@ -20,11 +19,6 @@ alloc: Allocator,
|
|||||||
/// single window operations.
|
/// single window operations.
|
||||||
window: *Window,
|
window: *Window,
|
||||||
|
|
||||||
// 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,
|
|
||||||
|
|
||||||
// The configuration for the app.
|
// The configuration for the app.
|
||||||
config: *const Config,
|
config: *const Config,
|
||||||
|
|
||||||
@ -32,63 +26,23 @@ config: *const Config,
|
|||||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||||
/// "startup" logic.
|
/// "startup" logic.
|
||||||
pub fn init(alloc: Allocator, config: *const Config) !App {
|
pub fn init(alloc: Allocator, config: *const Config) !App {
|
||||||
// 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
|
// Create the window
|
||||||
var window = try Window.create(alloc, loop, config);
|
var window = try Window.create(alloc, config);
|
||||||
errdefer window.destroy();
|
errdefer window.destroy();
|
||||||
|
|
||||||
return App{
|
return App{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.window = window,
|
.window = window,
|
||||||
.loop = loop,
|
|
||||||
.config = config,
|
.config = config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *App) void {
|
pub fn deinit(self: *App) void {
|
||||||
self.window.destroy();
|
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 |err|
|
|
||||||
log.err("error finalizing event loop: {}", .{err});
|
|
||||||
|
|
||||||
// Dealloc our allocator copy
|
|
||||||
self.alloc.destroy(self.loop.getData(Allocator).?);
|
|
||||||
|
|
||||||
self.loop.deinit(self.alloc);
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: App) !void {
|
pub fn run(self: App) !void {
|
||||||
// We are embedding two event loops: glfw and libuv. To do this, we
|
|
||||||
// create a separate thread that watches for libuv events and notifies
|
|
||||||
// glfw to wake up so we can run the libuv tick.
|
|
||||||
var embed = try libuv.Embed.init(self.alloc, self.loop, (struct {
|
|
||||||
fn callback() void {
|
|
||||||
glfw.postEmptyEvent() catch unreachable;
|
|
||||||
}
|
|
||||||
}).callback);
|
|
||||||
defer embed.deinit(self.alloc);
|
|
||||||
try embed.start();
|
|
||||||
|
|
||||||
// This async handle is used to "wake up" the embed thread so we can
|
|
||||||
// exit immediately once the windows want to close.
|
|
||||||
var async_h = try libuv.Async.init(self.alloc, self.loop, (struct {
|
|
||||||
fn callback(_: *libuv.Async) void {}
|
|
||||||
}).callback);
|
|
||||||
|
|
||||||
while (!self.window.shouldClose()) {
|
while (!self.window.shouldClose()) {
|
||||||
// Block for any glfw events. This may also be an "empty" event
|
// Block for any glfw events. This may also be an "empty" event
|
||||||
// posted by the libuv watcher so that we trigger a libuv loop tick.
|
// posted by the libuv watcher so that we trigger a libuv loop tick.
|
||||||
@ -96,26 +50,5 @@ pub fn run(self: App) !void {
|
|||||||
|
|
||||||
// Mark this so we're in a totally different "frame"
|
// Mark this so we're in a totally different "frame"
|
||||||
tracy.frameMark();
|
tracy.frameMark();
|
||||||
|
|
||||||
// Run the libuv loop
|
|
||||||
const frame = tracy.frame("libuv");
|
|
||||||
defer frame.end();
|
|
||||||
try embed.loopRun();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the embed thread to stop. We do this before we send on the
|
|
||||||
// async handle so that when the thread goes around it exits.
|
|
||||||
embed.stop();
|
|
||||||
|
|
||||||
// Wake up the event loop and schedule our close.
|
|
||||||
try async_h.send();
|
|
||||||
async_h.close((struct {
|
|
||||||
fn callback(h: *libuv.Async) void {
|
|
||||||
const alloc = h.loop().getData(Allocator).?.*;
|
|
||||||
h.deinit(alloc);
|
|
||||||
}
|
|
||||||
}).callback);
|
|
||||||
|
|
||||||
// Wait for the thread to end which should be almost instant.
|
|
||||||
try embed.join();
|
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,9 @@ const termio = @import("termio.zig");
|
|||||||
const objc = @import("objc");
|
const objc = @import("objc");
|
||||||
const glfw = @import("glfw");
|
const glfw = @import("glfw");
|
||||||
const imgui = @import("imgui");
|
const imgui = @import("imgui");
|
||||||
const libuv = @import("libuv");
|
|
||||||
const Pty = @import("Pty.zig");
|
const Pty = @import("Pty.zig");
|
||||||
const font = @import("font/main.zig");
|
const font = @import("font/main.zig");
|
||||||
const Command = @import("Command.zig");
|
const Command = @import("Command.zig");
|
||||||
const SegmentedPool = @import("segmented_pool.zig").SegmentedPool;
|
|
||||||
const trace = @import("tracy").trace;
|
const trace = @import("tracy").trace;
|
||||||
const terminal = @import("terminal/main.zig");
|
const terminal = @import("terminal/main.zig");
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
@ -70,6 +68,7 @@ mouse: Mouse,
|
|||||||
io: termio.Impl,
|
io: termio.Impl,
|
||||||
io_thread: termio.Thread,
|
io_thread: termio.Thread,
|
||||||
io_thr: std.Thread,
|
io_thr: std.Thread,
|
||||||
|
|
||||||
/// The dimensions of the grid in rows and columns.
|
/// The dimensions of the grid in rows and columns.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
|
||||||
@ -82,30 +81,6 @@ config: *const Config,
|
|||||||
/// like such as "control-v" will write a "v" even if they're intercepted.
|
/// like such as "control-v" will write a "v" even if they're intercepted.
|
||||||
ignore_char: bool = false,
|
ignore_char: bool = false,
|
||||||
|
|
||||||
/// Information related to the current cursor for the window.
|
|
||||||
//
|
|
||||||
// QUESTION(mitchellh): should this be attached to the Screen instead?
|
|
||||||
// I'm not sure if the cursor settings stick to the screen, i.e. if you
|
|
||||||
// change to an alternate screen if those are preserved. Need to check this.
|
|
||||||
const Cursor = struct {
|
|
||||||
/// Timer for cursor blinking.
|
|
||||||
timer: libuv.Timer,
|
|
||||||
|
|
||||||
/// Start (or restart) the timer. This is idempotent.
|
|
||||||
pub fn startTimer(self: Cursor) !void {
|
|
||||||
try self.timer.start(
|
|
||||||
cursorTimerCallback,
|
|
||||||
0,
|
|
||||||
self.timer.getRepeat(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stop the timer. This is idempotent.
|
|
||||||
pub fn stopTimer(self: Cursor) !void {
|
|
||||||
try self.timer.stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Mouse state for the window.
|
/// Mouse state for the window.
|
||||||
const Mouse = struct {
|
const Mouse = struct {
|
||||||
/// The last tracked mouse button state by button.
|
/// The last tracked mouse button state by button.
|
||||||
@ -132,8 +107,7 @@ const Mouse = struct {
|
|||||||
/// 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.
|
||||||
pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Window {
|
pub fn create(alloc: Allocator, config: *const Config) !*Window {
|
||||||
_ = loop;
|
|
||||||
var self = try alloc.create(Window);
|
var self = try alloc.create(Window);
|
||||||
errdefer alloc.destroy(self);
|
errdefer alloc.destroy(self);
|
||||||
|
|
||||||
@ -1458,25 +1432,6 @@ fn posToViewport(self: Window, xpos: f64, ypos: f64) terminal.point.Viewport {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursorTimerCallback(t: *libuv.Timer) void {
|
|
||||||
const tracy = trace(@src());
|
|
||||||
defer tracy.end();
|
|
||||||
|
|
||||||
const win = t.getData(Window) orelse return;
|
|
||||||
|
|
||||||
// We are modifying renderer state from here on out
|
|
||||||
win.renderer_state.mutex.lock();
|
|
||||||
defer win.renderer_state.mutex.unlock();
|
|
||||||
|
|
||||||
// If the cursor is currently invisible, then we do nothing. Ideally
|
|
||||||
// in this state the timer would be cancelled but no big deal.
|
|
||||||
if (!win.renderer_state.cursor.visible) return;
|
|
||||||
|
|
||||||
// Swap blink state and schedule a render
|
|
||||||
win.renderer_state.cursor.blink = !win.renderer_state.cursor.blink;
|
|
||||||
win.queueRender() catch unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
const face_ttf = @embedFile("font/res/FiraCode-Regular.ttf");
|
const face_ttf = @embedFile("font/res/FiraCode-Regular.ttf");
|
||||||
const face_bold_ttf = @embedFile("font/res/FiraCode-Bold.ttf");
|
const face_bold_ttf = @embedFile("font/res/FiraCode-Bold.ttf");
|
||||||
const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf");
|
const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf");
|
||||||
|
Reference in New Issue
Block a user