custom shaders: add colorscheme information to shader uniforms

Adds palette and color scheme uniforms to custom shaders, allowing
custom shaders to access terminal color information:

  - iPalette[256]: Full 256-color terminal palette (RGB)
  - iBackgroundColor, iForegroundColor: Terminal colors (RGB)
  - iCursorColor, iCursorText: Cursor colors (RGB)
  - iSelectionBackgroundColor, iSelectionForegroundColor: Selection
colors (RGB)

Colors are normalized to [0.0, 1.0] range and update when the palette
changes via OSC sequences or configuration changes. The palette_dirty
flag tracks when colors need to be refreshed, initialized to true to
ensure correct colors on new surfaces.
This commit is contained in:
ClearAspect
2025-11-20 15:47:56 -05:00
committed by Mitchell Hashimoto
parent 245886d568
commit 8d2eb280db
4 changed files with 131 additions and 1 deletions

View File

@@ -2883,6 +2883,24 @@ keybind: Keybinds = .{},
/// (e.g., modifier key presses, link hover events in unfocused split panes).
/// Check `iFocus > 0` to determine if the surface is currently focused.
///
/// * `vec3 iPalette[256]` - The 256-color terminal palette.
///
/// RGB values for all 256 colors in the terminal palette, normalized
/// to [0.0, 1.0]. Index 0-15 are the ANSI colors, 16-231 are the 6x6x6
/// color cube, and 232-255 are the grayscale colors.
///
/// * `vec3 iBackgroundColor` - Terminal background color (RGB).
///
/// * `vec3 iForegroundColor` - Terminal foreground color (RGB).
///
/// * `vec3 iCursorColor` - Terminal cursor color (RGB).
///
/// * `vec3 iCursorText` - Terminal cursor text color (RGB).
///
/// * `vec3 iSelectionBackgroundColor` - Selection background color (RGB).
///
/// * `vec3 iSelectionForegroundColor` - Selection foreground color (RGB).
///
/// If the shader fails to compile, the shader will be ignored. Any errors
/// related to shader compilation will not show up as configuration errors
/// and only show up in the log, since shader compilation happens after

View File

@@ -227,11 +227,17 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
/// a large screen.
terminal_state_frame_count: usize = 0,
/// Captured dirty for reference after updateFrame() clears the flag
/// To be used for shader uniforms.
///
/// Initialized to true because we need to get the correct palette even on
/// a new Surface. Otherwise we end up with it initialized to 0's
palette_dirty: bool = true,
const HighlightTag = enum(u8) {
search_match,
search_match_selected,
};
/// Swap chain which maintains multiple copies of the state needed to
/// render a frame, so that we can start building the next frame while
/// the previous frame is still being processed on the GPU.
@@ -752,6 +758,13 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.cursor_change_time = 0,
.time_focus = 0,
.focus = 1, // assume focused initially
.palette = @splat(@splat(0)),
.background_color = @splat(0),
.foreground_color = @splat(0),
.cursor_color = @splat(0),
.cursor_text = @splat(0),
.selection_background_color = @splat(0),
.selection_foreground_color = @splat(0),
},
.bg_image_buffer = undefined,
@@ -1209,6 +1222,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
};
};
self.palette_dirty |= state.terminal.flags.dirty.palette;
break :critical .{
.links = links,
.mouse = state.mouse,
@@ -2300,6 +2315,89 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
0,
};
// Renderer state required for getting the colors
const state: *renderer.State = &self.surface_mailbox.surface.renderer_state;
// (Updates on OSC sequence changes and configuration changes)
if (self.palette_dirty) {
// 256-color palette
for (state.terminal.colors.palette.current, 0..) |color, i| {
self.custom_shader_uniforms.palette[i] = .{
@as(f32, @floatFromInt(color.r)) / 255.0,
@as(f32, @floatFromInt(color.g)) / 255.0,
@as(f32, @floatFromInt(color.b)) / 255.0,
1.0,
};
}
// Background color
if (state.terminal.colors.background.get()) |bg| {
self.custom_shader_uniforms.background_color = .{
@as(f32, @floatFromInt(bg.r)) / 255.0,
@as(f32, @floatFromInt(bg.g)) / 255.0,
@as(f32, @floatFromInt(bg.b)) / 255.0,
1.0,
};
}
// Foreground color
if (state.terminal.colors.foreground.get()) |fg| {
self.custom_shader_uniforms.foreground_color = .{
@as(f32, @floatFromInt(fg.r)) / 255.0,
@as(f32, @floatFromInt(fg.g)) / 255.0,
@as(f32, @floatFromInt(fg.b)) / 255.0,
1.0,
};
}
// Cursor color
if (state.terminal.colors.cursor.get()) |cursor_color| {
self.custom_shader_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,
1.0,
};
}
// NOTE: the following could be optimized to follow a change in
// config for a slight optimization however this is only 12 bytes
// each being updated and likely isn't a cause for concern
// Cursor text color
if (self.config.cursor_text) |cursor_text| {
self.custom_shader_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,
1.0,
};
}
// Selection background color
if (self.config.selection_background) |seletion_bg| {
self.custom_shader_uniforms.selection_background_color = .{
@as(f32, @floatFromInt(seletion_bg.color.r)) / 255.0,
@as(f32, @floatFromInt(seletion_bg.color.g)) / 255.0,
@as(f32, @floatFromInt(seletion_bg.color.b)) / 255.0,
1.0,
};
}
// Selection foreground color
if (self.config.selection_foreground) |selection_fg| {
self.custom_shader_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,
};
}
self.palette_dirty = false;
}
// Update custom cursor uniforms, if we have a cursor.
if (self.cells.getCursorGlyph()) |cursor| {
const cursor_width: f32 = @floatFromInt(cursor.glyph_size[0]);

View File

@@ -18,6 +18,13 @@ layout(binding = 1, std140) uniform Globals {
uniform float iTimeCursorChange;
uniform float iTimeFocus;
uniform int iFocus;
uniform vec3 iPalette[256];
uniform vec3 iBackgroundColor;
uniform vec3 iForegroundColor;
uniform vec3 iCursorColor;
uniform vec3 iCursorText;
uniform vec3 iSelectionForegroundColor;
uniform vec3 iSelectionBackgroundColor;
};
layout(binding = 0) uniform sampler2D iChannel0;

View File

@@ -27,6 +27,13 @@ pub const Uniforms = extern struct {
cursor_change_time: f32 align(4),
time_focus: f32 align(4),
focus: i32 align(4),
palette: [256][4]f32 align(16),
background_color: [4]f32 align(16),
foreground_color: [4]f32 align(16),
cursor_color: [4]f32 align(16),
cursor_text: [4]f32 align(16),
selection_background_color: [4]f32 align(16),
selection_foreground_color: [4]f32 align(16),
};
/// The target to load shaders for.