diff --git a/src/Window.zig b/src/Window.zig index a35338583..7de185282 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -367,6 +367,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .config = config, .renderer_state = &self.renderer_state, .renderer_wakeup = render_thread.wakeup, + .renderer_mailbox = render_thread.mailbox, }); errdefer io.deinit(); diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index dc606f979..1b4a9112a 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -300,8 +300,8 @@ pub fn setFocus(self: *Metal, focus: bool) !void { } /// Called to toggle the blink state of the cursor -pub fn blinkCursor(self: *Metal) void { - self.cursor_visible = !self.cursor_visible; +pub fn blinkCursor(self: *Metal, reset: bool) void { + self.cursor_visible = reset or !self.cursor_visible; } /// The primary render callback that is completely thread-safe. diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 76c7a3443..b5479ce69 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -442,8 +442,8 @@ pub fn setFocus(self: *OpenGL, focus: bool) !void { } /// Called to toggle the blink state of the cursor -pub fn blinkCursor(self: *OpenGL) void { - self.cursor_visible = !self.cursor_visible; +pub fn blinkCursor(self: *OpenGL, reset: bool) void { + self.cursor_visible = reset or !self.cursor_visible; } /// The primary render callback that is completely thread-safe. diff --git a/src/renderer/Thread.zig b/src/renderer/Thread.zig index 8aff8c7e1..ebc06fd8d 100644 --- a/src/renderer/Thread.zig +++ b/src/renderer/Thread.zig @@ -15,7 +15,7 @@ const log = std.log.scoped(.renderer_thread); /// The type used for sending messages to the IO thread. For now this is /// hardcoded with a capacity. We can make this a comptime parameter in /// the future if we want it configurable. -const Mailbox = BlockingQueue(renderer.Message, 64); +pub const Mailbox = BlockingQueue(renderer.Message, 64); /// 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 @@ -227,14 +227,22 @@ fn drainMailbox(self: *Thread) !void { // If we're focused, we immediately show the cursor again // and then restart the timer. if (!try self.cursor_h.isActive()) { + self.renderer.blinkCursor(true); try self.cursor_h.start( cursorTimerCallback, - 0, + self.cursor_h.getRepeat(), self.cursor_h.getRepeat(), ); } } }, + + .reset_cursor_blink => { + self.renderer.blinkCursor(true); + if (try self.cursor_h.isActive()) { + _ = try self.cursor_h.again(); + } + }, } } } @@ -278,7 +286,7 @@ fn cursorTimerCallback(h: *libuv.Timer) void { return; }; - t.renderer.blinkCursor(); + t.renderer.blinkCursor(false); t.wakeup.send() catch {}; } diff --git a/src/renderer/message.zig b/src/renderer/message.zig index 1d60d0343..87cbc9f4e 100644 --- a/src/renderer/message.zig +++ b/src/renderer/message.zig @@ -8,4 +8,8 @@ pub const Message = union(enum) { /// rendering within. This is only sent when a change is detected so /// the renderer is expected to handle all of these. focus: bool, + + /// Reset the cursor blink by immediately showing the cursor then + /// restarting the timer. + reset_cursor_blink: void, }; diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 37c189fed..88e791b9a 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -40,6 +40,9 @@ renderer_state: *renderer.State, /// a repaint should happen. renderer_wakeup: libuv.Async, +/// The mailbox for notifying the renderer of things. +renderer_mailbox: *renderer.Thread.Mailbox, + /// The cached grid size whenever a resize is called. grid_size: renderer.GridSize, @@ -103,6 +106,7 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec { .terminal_stream = undefined, .renderer_state = opts.renderer_state, .renderer_wakeup = opts.renderer_wakeup, + .renderer_mailbox = opts.renderer_mailbox, .grid_size = opts.grid_size, .data = null, }; @@ -141,6 +145,7 @@ pub fn threadEnter(self: *Exec, loop: libuv.Loop) !ThreadData { .read_arena = std.heap.ArenaAllocator.init(alloc), .renderer_state = self.renderer_state, .renderer_wakeup = self.renderer_wakeup, + .renderer_mailbox = self.renderer_mailbox, .data_stream = stream, .terminal_stream = .{ .handler = .{ @@ -238,6 +243,9 @@ const EventData = struct { /// a repaint should happen. renderer_wakeup: libuv.Async, + /// The mailbox for notifying the renderer of things. + renderer_mailbox: *renderer.Thread.Mailbox, + /// The data stream is the main IO for the pty. data_stream: libuv.Tty, @@ -334,18 +342,16 @@ fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void { return; }; + // Whenever a character is typed, we ensure the cursor is in the + // non-blink state so it is rendered if visible. + _ = ev.renderer_mailbox.push(.{ + .reset_cursor_blink = {}, + }, .{ .forever = {} }); + // We are modifying terminal state from here on out ev.renderer_state.mutex.lock(); defer ev.renderer_state.mutex.unlock(); - // 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; - // TODO - // if (win.terminal_cursor.timer.isActive() catch false) { - // _ = win.terminal_cursor.timer.again() catch null; - // } - // Schedule a render ev.queueRender() catch unreachable; diff --git a/src/termio/Options.zig b/src/termio/Options.zig index 7577181f5..752516e77 100644 --- a/src/termio/Options.zig +++ b/src/termio/Options.zig @@ -22,3 +22,6 @@ renderer_state: *renderer.State, /// A handle to wake up the renderer. This hints to the renderer that that /// a repaint should happen. renderer_wakeup: libuv.Async, + +/// The mailbox for renderer messages. +renderer_mailbox: *renderer.Thread.Mailbox,