diff --git a/src/config/Config.zig b/src/config/Config.zig index fa491e49ccc..f168bd753d4 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -873,6 +873,18 @@ palette: Palette = .{}, /// * `block_hollow` @"cursor-style": terminal.CursorStyle = .block, +/// The style of the unfocused cursor. When a window loses focus, +/// its cursor is always shown so its position stays visible. +/// +/// The default is `block_hollow`, which preserves the original behavior. +/// +/// When the window is focused, the cursor style is unaffected by this option +/// and instead follows `cursor-style`, `DECSCUSR` (`CSI q`), and shell +/// integration. +/// +/// Accepts the same values as `cursor-style`. +@"cursor-style-unfocused": terminal.CursorStyle = .block_hollow, + /// Sets the default blinking state of the cursor. This is just the default /// state; running programs may override the cursor style using `DECSCUSR` (`CSI /// q`). diff --git a/src/renderer/cursor.zig b/src/renderer/cursor.zig index 33992bc5559..9c972f25d0f 100644 --- a/src/renderer/cursor.zig +++ b/src/renderer/cursor.zig @@ -29,6 +29,7 @@ pub const StyleOptions = struct { preedit: bool = false, focused: bool = false, blink_visible: bool = false, + unfocused_style: Style = .block_hollow, }; /// Returns the cursor style to use for the current render state or null @@ -57,7 +58,7 @@ pub fn style( // If we're not focused, our cursor is always visible so that // we can show the hollow box. - if (!opts.focused) return .block_hollow; + if (!opts.focused) return opts.unfocused_style; // If the cursor is blinking and our blink state is not visible, // then we don't show the cursor. @@ -86,6 +87,31 @@ test "cursor: default uses configured style" { try testing.expect(style(&state, .{ .preedit = false, .focused = true, .blink_visible = false }) == null); } +test "cursor: unfocused uses configured unfocused style" { + const testing = std.testing; + const alloc = testing.allocator; + var term: terminal.Terminal = try .init(alloc, .{ .cols = 10, .rows = 10 }); + defer term.deinit(alloc); + + term.screens.active.cursor.cursor_style = .bar; + term.modes.set(.cursor_blinking, true); + + var state: terminal.RenderState = .empty; + defer state.deinit(alloc); + try state.update(alloc, &term); + + try testing.expect(style(&state, .{ .focused = false, .unfocused_style = .bar, .blink_visible = true }) == .bar); + try testing.expect(style(&state, .{ .focused = false, .unfocused_style = .bar, .blink_visible = false }) == .bar); + try testing.expect(style(&state, .{ .focused = false, .unfocused_style = .underline }) == .underline); + try testing.expect(style(&state, .{ .focused = false, .unfocused_style = .block }) == .block); + + try testing.expect(style(&state, .{ .focused = false }) == .block_hollow); + + try testing.expect(style(&state, .{ .focused = true, .unfocused_style = .underline, .blink_visible = true }) == .bar); + + try testing.expect(style(&state, .{ .preedit = true, .focused = false, .unfocused_style = .bar }) == .block); +} + test "cursor: blinking disabled" { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index 5102ad4d4a9..9575e576ddc 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -544,6 +544,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { cursor_color: ?configpkg.Config.TerminalColor, cursor_opacity: f64, cursor_text: ?configpkg.Config.TerminalColor, + cursor_style_unfocused: terminal.CursorStyle, background: terminal.color.RGB, background_opacity: f64, background_opacity_cells: bool, @@ -616,6 +617,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { .cursor_color = config.@"cursor-color", .cursor_text = config.@"cursor-text", .cursor_opacity = @max(0, @min(1, config.@"cursor-opacity")), + .cursor_style_unfocused = config.@"cursor-style-unfocused", .background = config.background.toTerminalRGB(), .foreground = config.foreground.toTerminalRGB(), @@ -1379,6 +1381,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { .preedit = critical.preedit != null, .focused = self.focused, .blink_visible = cursor_blink_visible, + .unfocused_style = .fromTerminal(self.config.cursor_style_unfocused), }), &critical.links, ) catch |err| {