diff --git a/src/Grid.zig b/src/Grid.zig index 06d3ecfc7..8526baae3 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -47,10 +47,21 @@ foreground: terminal.color.RGB, /// Available cursor styles for drawing. The values represents the mode value /// in the shader. -const CursorStyle = enum(u8) { +pub const CursorStyle = enum(u8) { box = 3, box_hollow = 4, bar = 5, + + /// Create a cursor style from the terminal style request. + pub fn fromTerminal(style: terminal.CursorStyle) ?CursorStyle { + return switch (style) { + .blinking_block, .steady_block => .box, + .blinking_bar, .steady_bar => .bar, + .blinking_underline, .steady_underline => null, // TODO + .default => .box, + else => null, + }; + } }; /// The raw structure that maps directly to the buffer sent to the vertex shader. diff --git a/src/Window.zig b/src/Window.zig index a0a8c0fab..2ce100506 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -36,6 +36,9 @@ alloc: Allocator, /// The glfw window handle. window: glfw.Window, +/// Whether the window is currently focused +focused: bool, + /// The terminal grid attached to this window. grid: Grid, @@ -56,6 +59,7 @@ terminal_stream: terminal.Stream(*Window), /// Timer that blinks the cursor. cursor_timer: libuv.Timer, +cursor_style: terminal.CursorStyle, /// Render at least 60fps. render_timer: RenderTimer, @@ -199,12 +203,14 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo self.* = .{ .alloc = alloc, .window = window, + .focused = false, .grid = grid, .pty = pty, .command = cmd, .terminal = term, .terminal_stream = .{ .handler = self }, .cursor_timer = timer, + .cursor_style = .blinking_block, .render_timer = try RenderTimer.init(loop, self, 16, 96), .pty_stream = stream, .config = config, @@ -285,6 +291,25 @@ fn queueWrite(self: *Window, data: []const u8) !void { } } +/// Updates te style of the cursor. +fn updateCursorStyle(self: *Window, style: Grid.CursorStyle, blink: bool) !void { + self.grid.cursor_style = style; + self.grid.cursor_visible = !blink; + + if (blink) { + try self.cursor_timer.start( + cursorTimerCallback, + 0, + self.cursor_timer.getRepeat(), + ); + } else { + try self.cursor_timer.stop(); + } + + // Always schedule a render when we change cursors + try self.render_timer.schedule(); +} + fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { const tracy = trace(@src()); defer tracy.end(); @@ -394,19 +419,25 @@ fn focusCallback(window: glfw.Window, focused: bool) void { const win = window.getUserPointer(Window) orelse return; + // If we aren't changing focus state, do nothing. I don't think this + // can happen but it costs very little to check. + if (win.focused == focused) return; + // We have to schedule a render because no matter what we're changing // the cursor. win.render_timer.schedule() catch unreachable; + // Set our focused state on the window. + win.focused = focused; + if (focused) { win.wakeup = true; - win.cursor_timer.start(cursorTimerCallback, 0, win.cursor_timer.getRepeat()) catch unreachable; - win.grid.cursor_style = .box; - win.grid.cursor_visible = false; + win.updateCursorStyle( + Grid.CursorStyle.fromTerminal(win.cursor_style) orelse .box, + win.cursor_style.blinking(), + ) catch unreachable; } else { - win.grid.cursor_visible = true; - win.grid.cursor_style = .box_hollow; - win.cursor_timer.stop() catch unreachable; + win.updateCursorStyle(.box_hollow, false) catch unreachable; } } @@ -676,7 +707,17 @@ pub fn setCursorStyle( ) !void { _ = self; - switch (style) { - else => log.warn("unimplemented cursor style: {}", .{style}), - } + // Get the style that we use in the renderer + const grid_style = Grid.CursorStyle.fromTerminal(style) orelse { + log.warn("unimplemented cursor style: {}", .{style}); + return; + }; + + // Set our style + self.cursor_style = style; + + // If we're currently focused, we update our style, since our unfocused + // cursor is manually managed. If we're not focused, we ignore it because + // it'll be updated the next time the window comes into focus. + if (self.focused) try self.updateCursorStyle(grid_style, style.blinking()); } diff --git a/src/terminal/ansi.zig b/src/terminal/ansi.zig index acfa4e56a..61221e93b 100644 --- a/src/terminal/ansi.zig +++ b/src/terminal/ansi.zig @@ -71,4 +71,12 @@ pub const CursorStyle = enum(u16) { // Non-exhaustive so that @intToEnum never fails for unsupported modes. _, + + /// True if the cursor should blink. + pub fn blinking(self: CursorStyle) bool { + return switch (self) { + .blinking_block, .blinking_underline, .blinking_bar => true, + else => false, + }; + } };