From 7a4bddd37bfb1f758a2302c04ec8e77ecae3e49b Mon Sep 17 00:00:00 2001 From: ClearAspect Date: Mon, 8 Dec 2025 22:11:36 -0500 Subject: [PATCH] renderer: added cursor style and visibility uniforms Specifically: iCurrentCursorStyle iPreviousCursorStyle iCurrentCursorVisible iPreviousCursorVisible Visibility calculated and updated independently from the typical cursor unifrom updates to preserve cursor style even when not in the viewport or set to be hidden --- src/config/Config.zig | 14 +++++++++++ src/renderer/cursor.zig | 2 +- src/renderer/generic.zig | 29 +++++++++++++++------- src/renderer/shaders/shadertoy_prefix.glsl | 9 +++++++ src/renderer/shadertoy.zig | 3 +++ 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 9b0e6cc0f..0a6ca9c9f 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2946,6 +2946,20 @@ keybind: Keybinds = .{}, /// /// * `vec4 iPreviousCursorColor` - Color of the previous terminal cursor. /// +/// * `vec4 iCurrentCursorStyle` - Style of the terminal cursor +/// +/// Macros simplified use are defined for the various cursor styles: +/// +/// - `CURSORSTYLE_BLOCK` or `0` +/// - `CURSORSTYLE_BLOCK_HOLLOW` or `1` +/// - `CURSORSTYLE_BAR` or `2` +/// - `CURSORSTYLE_UNDERLINE` or `3` +/// - `CURSORSTYLE_LOCK` or `4` +/// +/// * `vec4 iPreviousCursorStyle` - Style of the previous terminal cursor +/// +/// * `vec4 iCursorVisible` - Visibility of the terminal cursor. +/// /// * `float iTimeCursorChange` - Timestamp of terminal cursor change. /// /// When the terminal cursor changes position or color, this is set to diff --git a/src/renderer/cursor.zig b/src/renderer/cursor.zig index cddda9871..33992bc55 100644 --- a/src/renderer/cursor.zig +++ b/src/renderer/cursor.zig @@ -15,7 +15,7 @@ pub const Style = enum { lock, /// Create a cursor style from the terminal style request. - pub fn fromTerminal(term: terminal.CursorStyle) ?Style { + pub fn fromTerminal(term: terminal.CursorStyle) Style { return switch (term) { .bar => .bar, .block => .block, diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index f57339893..ff632f64a 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -756,6 +756,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type { .previous_cursor = @splat(0), .current_cursor_color = @splat(0), .previous_cursor_color = @splat(0), + .current_cursor_style = 0, + .previous_cursor_style = 0, + .cursor_visible = 0, .cursor_change_time = 0, .time_focus = 0, .focus = 1, // assume focused initially @@ -2011,11 +2014,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // Only update when terminal state is dirty. if (self.terminal_state.dirty == .false) return; + const uniforms: *shadertoy.Uniforms = &self.custom_shader_uniforms; const colors: *const terminal.RenderState.Colors = &self.terminal_state.colors; // 256-color palette for (colors.palette, 0..) |color, i| { - self.custom_shader_uniforms.palette[i] = .{ + uniforms.palette[i] = .{ @as(f32, @floatFromInt(color.r)) / 255.0, @as(f32, @floatFromInt(color.g)) / 255.0, @as(f32, @floatFromInt(color.b)) / 255.0, @@ -2024,7 +2028,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { } // Background color - self.custom_shader_uniforms.background_color = .{ + uniforms.background_color = .{ @as(f32, @floatFromInt(colors.background.r)) / 255.0, @as(f32, @floatFromInt(colors.background.g)) / 255.0, @as(f32, @floatFromInt(colors.background.b)) / 255.0, @@ -2032,7 +2036,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { }; // Foreground color - self.custom_shader_uniforms.foreground_color = .{ + uniforms.foreground_color = .{ @as(f32, @floatFromInt(colors.foreground.r)) / 255.0, @as(f32, @floatFromInt(colors.foreground.g)) / 255.0, @as(f32, @floatFromInt(colors.foreground.b)) / 255.0, @@ -2041,7 +2045,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // Cursor color if (colors.cursor) |cursor_color| { - self.custom_shader_uniforms.cursor_color = .{ + uniforms.cursor_color = .{ @as(f32, @floatFromInt(cursor_color.r)) / 255.0, @as(f32, @floatFromInt(cursor_color.g)) / 255.0, @as(f32, @floatFromInt(cursor_color.b)) / 255.0, @@ -2055,7 +2059,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // Cursor text color if (self.config.cursor_text) |cursor_text| { - self.custom_shader_uniforms.cursor_text = .{ + uniforms.cursor_text = .{ @as(f32, @floatFromInt(cursor_text.color.r)) / 255.0, @as(f32, @floatFromInt(cursor_text.color.g)) / 255.0, @as(f32, @floatFromInt(cursor_text.color.b)) / 255.0, @@ -2065,7 +2069,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // Selection background color if (self.config.selection_background) |selection_bg| { - self.custom_shader_uniforms.selection_background_color = .{ + uniforms.selection_background_color = .{ @as(f32, @floatFromInt(selection_bg.color.r)) / 255.0, @as(f32, @floatFromInt(selection_bg.color.g)) / 255.0, @as(f32, @floatFromInt(selection_bg.color.b)) / 255.0, @@ -2075,13 +2079,21 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // Selection foreground color if (self.config.selection_foreground) |selection_fg| { - self.custom_shader_uniforms.selection_foreground_color = .{ + uniforms.selection_foreground_color = .{ @as(f32, @floatFromInt(selection_fg.color.r)) / 255.0, @as(f32, @floatFromInt(selection_fg.color.g)) / 255.0, @as(f32, @floatFromInt(selection_fg.color.b)) / 255.0, 1.0, }; } + + // Cursor visibility + uniforms.cursor_visible = @intFromBool(self.terminal_state.cursor.visible); + + // Cursor style + const cursor_style: renderer.CursorStyle = .fromTerminal(self.terminal_state.cursor.visual_style); + uniforms.previous_cursor_style = uniforms.current_cursor_style; + uniforms.current_cursor_style = @as(i32, @intFromEnum(cursor_style)); } /// Update per-frame custom shader uniforms. @@ -2091,7 +2103,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // We only need to do this if we have custom shaders. if (!self.has_custom_shaders) return; - const uniforms = &self.custom_shader_uniforms; + const uniforms: *shadertoy.Uniforms = &self.custom_shader_uniforms; const now = try std.time.Instant.now(); defer self.last_frame_time = now; @@ -2125,7 +2137,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type { 0, }; - // Update custom cursor uniforms, if we have a cursor. if (self.cells.getCursorGlyph()) |cursor| { const cursor_width: f32 = @floatFromInt(cursor.glyph_size[0]); const cursor_height: f32 = @floatFromInt(cursor.glyph_size[1]); diff --git a/src/renderer/shaders/shadertoy_prefix.glsl b/src/renderer/shaders/shadertoy_prefix.glsl index 661bd233d..4b6d091b8 100644 --- a/src/renderer/shaders/shadertoy_prefix.glsl +++ b/src/renderer/shaders/shadertoy_prefix.glsl @@ -15,6 +15,9 @@ layout(binding = 1, std140) uniform Globals { uniform vec4 iPreviousCursor; uniform vec4 iCurrentCursorColor; uniform vec4 iPreviousCursorColor; + uniform int iCurrentCursorStyle; + uniform int iPreviousCursorStyle; + uniform int iCursorVisible; uniform float iTimeCursorChange; uniform float iTimeFocus; uniform int iFocus; @@ -27,6 +30,12 @@ layout(binding = 1, std140) uniform Globals { uniform vec3 iSelectionBackgroundColor; }; +#define CURSORSTYLE_BLOCK 0 +#define CURSORSTYLE_BLOCK_HOLLOW 1 +#define CURSORSTYLE_BAR 2 +#define CURSORSTYLE_UNDERLINE 3 +#define CURSORSTYLE_LOCK 4 + layout(binding = 0) uniform sampler2D iChannel0; // These are unused currently by Ghostty: diff --git a/src/renderer/shadertoy.zig b/src/renderer/shadertoy.zig index 7d0ad4b0a..556c28293 100644 --- a/src/renderer/shadertoy.zig +++ b/src/renderer/shadertoy.zig @@ -24,6 +24,9 @@ pub const Uniforms = extern struct { previous_cursor: [4]f32 align(16), current_cursor_color: [4]f32 align(16), previous_cursor_color: [4]f32 align(16), + current_cursor_style: i32 align(4), + previous_cursor_style: i32 align(4), + cursor_visible: i32 align(4), cursor_change_time: f32 align(4), time_focus: f32 align(4), focus: i32 align(4),