From cd705359e8a6b677704b359fa37a1ae210d40abb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 5 Nov 2022 19:30:15 -0700 Subject: [PATCH] Window thread is now single event loop! --- src/App.zig | 69 +------------------------------------------------- src/Window.zig | 49 ++--------------------------------- 2 files changed, 3 insertions(+), 115 deletions(-) diff --git a/src/App.zig b/src/App.zig index 7867db4c7..89e96192e 100644 --- a/src/App.zig +++ b/src/App.zig @@ -7,7 +7,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const glfw = @import("glfw"); const Window = @import("Window.zig"); -const libuv = @import("libuv"); const tracy = @import("tracy"); const Config = @import("config.zig").Config; @@ -20,11 +19,6 @@ alloc: Allocator, /// single window operations. 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. config: *const Config, @@ -32,63 +26,23 @@ config: *const Config, /// up the renderer state, compiles the shaders, etc. This is the primary /// "startup" logic. 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 - var window = try Window.create(alloc, loop, config); + var window = try Window.create(alloc, config); errdefer window.destroy(); return App{ .alloc = alloc, .window = window, - .loop = loop, .config = config, }; } 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 |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; } 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()) { // 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. @@ -96,26 +50,5 @@ pub fn run(self: App) !void { // Mark this so we're in a totally different "frame" 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(); } diff --git a/src/Window.zig b/src/Window.zig index 7de185282..3b6f60021 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -14,11 +14,9 @@ const termio = @import("termio.zig"); const objc = @import("objc"); const glfw = @import("glfw"); const imgui = @import("imgui"); -const libuv = @import("libuv"); const Pty = @import("Pty.zig"); const font = @import("font/main.zig"); const Command = @import("Command.zig"); -const SegmentedPool = @import("segmented_pool.zig").SegmentedPool; const trace = @import("tracy").trace; const terminal = @import("terminal/main.zig"); const Config = @import("config.zig").Config; @@ -70,6 +68,7 @@ mouse: Mouse, io: termio.Impl, io_thread: termio.Thread, io_thr: std.Thread, + /// The dimensions of the grid in rows and columns. 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. 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. const Mouse = struct { /// 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 /// need a stable pointer for user data callbacks. Therefore, a stack-only /// initialization is not currently possible. -pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Window { - _ = loop; +pub fn create(alloc: Allocator, config: *const Config) !*Window { var self = try alloc.create(Window); 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_bold_ttf = @embedFile("font/res/FiraCode-Bold.ttf"); const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf");