From aa98e3ca3aaf121b3a8f2933a91ad6453ccc8880 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 5 Nov 2022 19:18:22 -0700 Subject: [PATCH] Move cursor timer to renderer --- src/Window.zig | 30 ++--------------------- src/renderer/Metal.zig | 7 +++++- src/renderer/OpenGL.zig | 7 +++++- src/renderer/State.zig | 4 --- src/renderer/Thread.zig | 54 ++++++++++++++++++++++++++++++++++++++++- src/termio/Exec.zig | 2 +- 6 files changed, 68 insertions(+), 36 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index 4d8421584..a35338583 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -70,10 +70,6 @@ mouse: Mouse, io: termio.Impl, io_thread: termio.Thread, io_thr: std.Thread, - -/// Cursor state. -terminal_cursor: Cursor, - /// The dimensions of the grid in rows and columns. grid_size: renderer.GridSize, @@ -137,6 +133,7 @@ const Mouse = struct { /// 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; var self = try alloc.create(Window); errdefer alloc.destroy(self); @@ -344,13 +341,6 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .height = @floatToInt(u32, renderer_impl.cell_size.height * 4), }, .{ .width = null, .height = null }); - // 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, 600, 600); - // Create the cursor const cursor = try glfw.Cursor.createStandard(.ibeam); errdefer cursor.destroy(); @@ -398,7 +388,6 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .cursor = .{ .style = .blinking_block, .visible = true, - .blink = false, }, .terminal = &self.io.terminal, .devmode = if (!DevMode.enabled) null else &DevMode.instance, @@ -408,7 +397,6 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .io = io, .io_thread = io_thread, .io_thr = undefined, - .terminal_cursor = .{ .timer = timer }, .grid_size = grid_size, .config = config, @@ -516,13 +504,6 @@ pub fn destroy(self: *Window) void { self.window.destroy(); - self.terminal_cursor.timer.close((struct { - fn callback(t: *libuv.Timer) void { - const alloc = t.loop().getData(Allocator).?.*; - t.deinit(alloc); - } - }).callback); - // We can destroy the cursor right away. glfw will just revert any // windows using it to the default. self.cursor.destroy(); @@ -921,15 +902,8 @@ fn focusCallback(window: glfw.Window, focused: bool) void { .focus = focused, }, .{ .forever = {} }); - // We have to schedule a render because no matter what we're changing - // the cursor. If we're focused its reappearing, if we're not then - // its changing to hollow and not blinking. + // Schedule render which also drains our mailbox win.queueRender() catch unreachable; - - if (focused) - win.terminal_cursor.startTimer() catch unreachable - else - win.terminal_cursor.stopTimer() catch unreachable; } fn refreshCallback(window: glfw.Window) void { diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index f6f23d350..dc606f979 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -299,6 +299,11 @@ pub fn setFocus(self: *Metal, focus: bool) !void { self.focused = focus; } +/// Called to toggle the blink state of the cursor +pub fn blinkCursor(self: *Metal) void { + self.cursor_visible = !self.cursor_visible; +} + /// The primary render callback that is completely thread-safe. pub fn render( self: *Metal, @@ -325,7 +330,7 @@ pub fn render( // Setup our cursor state if (self.focused) { - self.cursor_visible = state.cursor.visible and !state.cursor.blink; + self.cursor_visible = self.cursor_visible and state.cursor.visible; self.cursor_style = renderer.CursorStyle.fromTerminal(state.cursor.style) orelse .box; } else { self.cursor_visible = true; diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index e42bbc60a..76c7a3443 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -441,6 +441,11 @@ pub fn setFocus(self: *OpenGL, focus: bool) !void { self.focused = focus; } +/// Called to toggle the blink state of the cursor +pub fn blinkCursor(self: *OpenGL) void { + self.cursor_visible = !self.cursor_visible; +} + /// The primary render callback that is completely thread-safe. pub fn render( self: *OpenGL, @@ -465,7 +470,7 @@ pub fn render( // Setup our cursor state if (self.focused) { - self.cursor_visible = state.cursor.visible and !state.cursor.blink; + self.cursor_visible = self.cursor_visible and state.cursor.visible; self.cursor_style = renderer.CursorStyle.fromTerminal(state.cursor.style) orelse .box; } else { self.cursor_visible = true; diff --git a/src/renderer/State.zig b/src/renderer/State.zig index f172a1dd8..f06d1784c 100644 --- a/src/renderer/State.zig +++ b/src/renderer/State.zig @@ -33,8 +33,4 @@ pub const Cursor = struct { /// "blink" settings, see "blink" for that. This is used to turn the /// cursor ON or OFF. visible: bool = true, - - /// Whether the cursor is currently blinking. If it is blinking, then - /// the cursor will not be rendered. - blink: bool = false, }; diff --git a/src/renderer/Thread.zig b/src/renderer/Thread.zig index feb31ebdc..8aff8c7e1 100644 --- a/src/renderer/Thread.zig +++ b/src/renderer/Thread.zig @@ -32,6 +32,9 @@ stop: libuv.Async, /// The timer used for rendering render_h: libuv.Timer, +/// The timer used for cursor blinking +cursor_h: libuv.Timer, + /// The windo we're rendering to. window: glfw.Window, @@ -92,6 +95,15 @@ pub fn init( } }).callback); + // Setup a timer for blinking the cursor + var cursor_timer = try libuv.Timer.init(alloc, loop); + errdefer cursor_timer.close((struct { + fn callback(t: *libuv.Timer) void { + const alloc_h = t.loop().getData(Allocator).?.*; + t.deinit(alloc_h); + } + }).callback); + // The mailbox for messaging this thread var mailbox = try Mailbox.create(alloc); errdefer mailbox.destroy(alloc); @@ -101,6 +113,7 @@ pub fn init( .wakeup = wakeup_h, .stop = stop_h, .render_h = render_h, + .cursor_h = cursor_timer, .window = window, .renderer = renderer_impl, .state = state, @@ -134,6 +147,12 @@ pub fn deinit(self: *Thread) void { h.deinit(handle_alloc); } }).callback); + self.cursor_h.close((struct { + fn callback(h: *libuv.Timer) void { + const handle_alloc = h.loop().getData(Allocator).?.*; + h.deinit(handle_alloc); + } + }).callback); // Run the loop one more time, because destroying our other things // like windows usually cancel all our event loop stuff and we need @@ -175,6 +194,10 @@ fn threadMain_(self: *Thread) !void { defer self.render_h.setData(null); try self.wakeup.send(); + // Setup a timer for blinking the cursor + self.cursor_h.setData(self); + try self.cursor_h.start(cursorTimerCallback, 600, 600); + // Run log.debug("starting renderer thread", .{}); defer log.debug("exiting renderer thread", .{}); @@ -193,7 +216,25 @@ fn drainMailbox(self: *Thread) !void { while (drain.next()) |message| { log.debug("mailbox message={}", .{message}); switch (message) { - .focus => |v| try self.renderer.setFocus(v), + .focus => |v| { + // Set it on the renderer + try self.renderer.setFocus(v); + + if (!v) { + // If we're not focused, then we stop the cursor blink + try self.cursor_h.stop(); + } else { + // If we're focused, we immediately show the cursor again + // and then restart the timer. + if (!try self.cursor_h.isActive()) { + try self.cursor_h.start( + cursorTimerCallback, + 0, + self.cursor_h.getRepeat(), + ); + } + } + }, } } } @@ -230,6 +271,17 @@ fn renderCallback(h: *libuv.Timer) void { log.warn("error rendering err={}", .{err}); } +fn cursorTimerCallback(h: *libuv.Timer) void { + const t = h.getData(Thread) orelse { + // This shouldn't happen so we log it. + log.warn("render callback fired without data set", .{}); + return; + }; + + t.renderer.blinkCursor(); + t.wakeup.send() catch {}; +} + fn stopCallback(h: *libuv.Async) void { h.loop().stop(); } diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 0ecbd6032..37c189fed 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -340,7 +340,7 @@ fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void { // Whenever a character is typed, we ensure the cursor is in the // non-blink state so it is rendered if visible. - ev.renderer_state.cursor.blink = false; + //ev.renderer_state.cursor.blink = false; // TODO // if (win.terminal_cursor.timer.isActive() catch false) { // _ = win.terminal_cursor.timer.again() catch null;