mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #2036 from ghostty-org/padding-extend
`window-padding-color = extend` to extend nearest cell bg color to padding
This commit is contained in:
@ -20,6 +20,7 @@ pub const RepeatableCodepointMap = Config.RepeatableCodepointMap;
|
|||||||
pub const RepeatableFontVariation = Config.RepeatableFontVariation;
|
pub const RepeatableFontVariation = Config.RepeatableFontVariation;
|
||||||
pub const RepeatableString = Config.RepeatableString;
|
pub const RepeatableString = Config.RepeatableString;
|
||||||
pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
||||||
|
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
||||||
|
|
||||||
// Alternate APIs
|
// Alternate APIs
|
||||||
pub const CAPI = @import("config/CAPI.zig");
|
pub const CAPI = @import("config/CAPI.zig");
|
||||||
|
@ -669,6 +669,16 @@ keybind: Keybinds = .{},
|
|||||||
/// given a certain viewport size and grid cell size.
|
/// given a certain viewport size and grid cell size.
|
||||||
@"window-padding-balance": bool = false,
|
@"window-padding-balance": bool = false,
|
||||||
|
|
||||||
|
/// The color of the padding area of the window. Valid values are:
|
||||||
|
///
|
||||||
|
/// * `background` - The background color specified in `background`.
|
||||||
|
/// * `extend` - Extend the background color of the nearest grid cell.
|
||||||
|
///
|
||||||
|
/// The default value is "extend". This allows for smooth resizing of a
|
||||||
|
/// terminal grid without having visible empty areas around the edge. The edge
|
||||||
|
/// cells may appear slightly larger due to the extension.
|
||||||
|
@"window-padding-color": WindowPaddingColor = .extend,
|
||||||
|
|
||||||
/// Synchronize rendering with the screen refresh rate. If true, this will
|
/// Synchronize rendering with the screen refresh rate. If true, this will
|
||||||
/// minimize tearing and align redraws with the screen but may cause input
|
/// minimize tearing and align redraws with the screen but may cause input
|
||||||
/// latency. If false, this will maximize redraw frequency but may cause tearing,
|
/// latency. If false, this will maximize redraw frequency but may cause tearing,
|
||||||
@ -2678,6 +2688,11 @@ pub const OptionAsAlt = enum {
|
|||||||
right,
|
right,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const WindowPaddingColor = enum {
|
||||||
|
background,
|
||||||
|
extend,
|
||||||
|
};
|
||||||
|
|
||||||
/// Color represents a color using RGB.
|
/// Color represents a color using RGB.
|
||||||
///
|
///
|
||||||
/// This is a packed struct so that the C API to read color values just
|
/// This is a packed struct so that the C API to read color values just
|
||||||
|
@ -345,6 +345,7 @@ pub const DerivedConfig = struct {
|
|||||||
invert_selection_fg_bg: bool,
|
invert_selection_fg_bg: bool,
|
||||||
bold_is_bright: bool,
|
bold_is_bright: bool,
|
||||||
min_contrast: f32,
|
min_contrast: f32,
|
||||||
|
padding_color: configpkg.WindowPaddingColor,
|
||||||
custom_shaders: std.ArrayListUnmanaged([:0]const u8),
|
custom_shaders: std.ArrayListUnmanaged([:0]const u8),
|
||||||
links: link.Set,
|
links: link.Set,
|
||||||
vsync: bool,
|
vsync: bool,
|
||||||
@ -402,6 +403,7 @@ pub const DerivedConfig = struct {
|
|||||||
.invert_selection_fg_bg = config.@"selection-invert-fg-bg",
|
.invert_selection_fg_bg = config.@"selection-invert-fg-bg",
|
||||||
.bold_is_bright = config.@"bold-is-bright",
|
.bold_is_bright = config.@"bold-is-bright",
|
||||||
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
||||||
|
.padding_color = config.@"window-padding-color",
|
||||||
|
|
||||||
.selection_background = if (config.@"selection-background") |bg|
|
.selection_background = if (config.@"selection-background") |bg|
|
||||||
bg.toTerminalRGB()
|
bg.toTerminalRGB()
|
||||||
@ -621,6 +623,8 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.uniforms = .{
|
.uniforms = .{
|
||||||
.projection_matrix = undefined,
|
.projection_matrix = undefined,
|
||||||
.cell_size = undefined,
|
.cell_size = undefined,
|
||||||
|
.grid_size = undefined,
|
||||||
|
.grid_padding = undefined,
|
||||||
.min_contrast = options.config.min_contrast,
|
.min_contrast = options.config.min_contrast,
|
||||||
.cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) },
|
.cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) },
|
||||||
.cursor_color = undefined,
|
.cursor_color = undefined,
|
||||||
@ -831,15 +835,6 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void {
|
|||||||
const metrics = grid.metrics;
|
const metrics = grid.metrics;
|
||||||
self.grid_metrics = metrics;
|
self.grid_metrics = metrics;
|
||||||
|
|
||||||
// Reset our cell contents.
|
|
||||||
self.cells.resize(self.alloc, self.gridSize().?) catch |err| {
|
|
||||||
// The setFontGrid function can't fail but resizing our cell
|
|
||||||
// buffer definitely can fail. If it does, our renderer is probably
|
|
||||||
// screwed but let's just log it and continue until we can figure
|
|
||||||
// out a better way to handle this.
|
|
||||||
log.err("error resizing cells buffer err={}", .{err});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset our shaper cache. If our font changed (not just the size) then
|
// Reset our shaper cache. If our font changed (not just the size) then
|
||||||
// the data in the shaper cache may be invalid and cannot be used, so we
|
// the data in the shaper cache may be invalid and cannot be used, so we
|
||||||
// always clear the cache just in case.
|
// always clear the cache just in case.
|
||||||
@ -847,20 +842,21 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void {
|
|||||||
self.font_shaper_cache.deinit(self.alloc);
|
self.font_shaper_cache.deinit(self.alloc);
|
||||||
self.font_shaper_cache = font_shaper_cache;
|
self.font_shaper_cache = font_shaper_cache;
|
||||||
|
|
||||||
// Reset our viewport to force a rebuild
|
// Run a screen size update since this handles a lot of our uniforms
|
||||||
self.cells_viewport = null;
|
// that are grid size dependent and changing the font grid can change
|
||||||
|
// the grid size.
|
||||||
// Update our uniforms
|
//
|
||||||
self.uniforms = .{
|
// If the screen size isn't set, it will be eventually so that'll call
|
||||||
.projection_matrix = self.uniforms.projection_matrix,
|
// the setScreenSize automatically.
|
||||||
.cell_size = .{
|
if (self.screen_size) |size| {
|
||||||
@floatFromInt(metrics.cell_width),
|
self.setScreenSize(size, self.padding.explicit) catch |err| {
|
||||||
@floatFromInt(metrics.cell_height),
|
// The setFontGrid function can't fail but resizing our cell
|
||||||
},
|
// buffer definitely can fail. If it does, our renderer is probably
|
||||||
.min_contrast = self.uniforms.min_contrast,
|
// screwed but let's just log it and continue until we can figure
|
||||||
.cursor_pos = self.uniforms.cursor_pos,
|
// out a better way to handle this.
|
||||||
.cursor_color = self.uniforms.cursor_color,
|
log.err("error resizing cells buffer err={}", .{err});
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the frame data.
|
/// Update the frame data.
|
||||||
@ -1951,6 +1947,18 @@ pub fn setScreenSize(
|
|||||||
self.padding.explicit;
|
self.padding.explicit;
|
||||||
const padded_dim = dim.subPadding(padding);
|
const padded_dim = dim.subPadding(padding);
|
||||||
|
|
||||||
|
// Blank space around the grid.
|
||||||
|
const blank: renderer.Padding = switch (self.config.padding_color) {
|
||||||
|
// We can use zero padding because the backgroudn color is our
|
||||||
|
// clear color.
|
||||||
|
.background => .{},
|
||||||
|
|
||||||
|
.extend => dim.blankPadding(padding, grid_size, .{
|
||||||
|
.width = self.grid_metrics.cell_width,
|
||||||
|
.height = self.grid_metrics.cell_height,
|
||||||
|
}).add(padding),
|
||||||
|
};
|
||||||
|
|
||||||
// Set the size of the drawable surface to the bounds
|
// Set the size of the drawable surface to the bounds
|
||||||
self.layer.setProperty("drawableSize", macos.graphics.Size{
|
self.layer.setProperty("drawableSize", macos.graphics.Size{
|
||||||
.width = @floatFromInt(dim.width),
|
.width = @floatFromInt(dim.width),
|
||||||
@ -1970,6 +1978,16 @@ pub fn setScreenSize(
|
|||||||
@floatFromInt(self.grid_metrics.cell_width),
|
@floatFromInt(self.grid_metrics.cell_width),
|
||||||
@floatFromInt(self.grid_metrics.cell_height),
|
@floatFromInt(self.grid_metrics.cell_height),
|
||||||
},
|
},
|
||||||
|
.grid_size = .{
|
||||||
|
grid_size.columns,
|
||||||
|
grid_size.rows,
|
||||||
|
},
|
||||||
|
.grid_padding = .{
|
||||||
|
@floatFromInt(blank.top),
|
||||||
|
@floatFromInt(blank.right),
|
||||||
|
@floatFromInt(blank.bottom),
|
||||||
|
@floatFromInt(blank.left),
|
||||||
|
},
|
||||||
.min_contrast = old.min_contrast,
|
.min_contrast = old.min_contrast,
|
||||||
.cursor_pos = old.cursor_pos,
|
.cursor_pos = old.cursor_pos,
|
||||||
.cursor_color = old.cursor_color,
|
.cursor_color = old.cursor_color,
|
||||||
|
@ -138,10 +138,11 @@ const SetScreenSize = struct {
|
|||||||
return error.OpenGLUninitialized;
|
return error.OpenGLUninitialized;
|
||||||
|
|
||||||
// Apply our padding
|
// Apply our padding
|
||||||
|
const grid_size = r.gridSize(self.size);
|
||||||
const padding = if (r.padding.balance)
|
const padding = if (r.padding.balance)
|
||||||
renderer.Padding.balanced(
|
renderer.Padding.balanced(
|
||||||
self.size,
|
self.size,
|
||||||
r.gridSize(self.size),
|
grid_size,
|
||||||
.{
|
.{
|
||||||
.width = r.grid_metrics.cell_width,
|
.width = r.grid_metrics.cell_width,
|
||||||
.height = r.grid_metrics.cell_height,
|
.height = r.grid_metrics.cell_height,
|
||||||
@ -151,6 +152,18 @@ const SetScreenSize = struct {
|
|||||||
r.padding.explicit;
|
r.padding.explicit;
|
||||||
const padded_size = self.size.subPadding(padding);
|
const padded_size = self.size.subPadding(padding);
|
||||||
|
|
||||||
|
// Blank space around the grid.
|
||||||
|
const blank: renderer.Padding = switch (r.config.padding_color) {
|
||||||
|
// We can use zero padding because the backgroudn color is our
|
||||||
|
// clear color.
|
||||||
|
.background => .{},
|
||||||
|
|
||||||
|
.extend => self.size.blankPadding(padding, grid_size, .{
|
||||||
|
.width = r.grid_metrics.cell_width,
|
||||||
|
.height = r.grid_metrics.cell_height,
|
||||||
|
}).add(padding),
|
||||||
|
};
|
||||||
|
|
||||||
log.debug("GL api: screen size padded={} screen={} grid={} cell={} padding={}", .{
|
log.debug("GL api: screen size padded={} screen={} grid={} cell={} padding={}", .{
|
||||||
padded_size,
|
padded_size,
|
||||||
self.size,
|
self.size,
|
||||||
@ -189,6 +202,29 @@ const SetScreenSize = struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup our grid padding
|
||||||
|
{
|
||||||
|
const program = gl_state.cell_program;
|
||||||
|
const bind = try program.program.use();
|
||||||
|
defer bind.unbind();
|
||||||
|
try program.program.setUniform(
|
||||||
|
"grid_padding",
|
||||||
|
@Vector(4, f32){
|
||||||
|
@floatFromInt(blank.top),
|
||||||
|
@floatFromInt(blank.right),
|
||||||
|
@floatFromInt(blank.bottom),
|
||||||
|
@floatFromInt(blank.left),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
try program.program.setUniform(
|
||||||
|
"grid_size",
|
||||||
|
@Vector(2, f32){
|
||||||
|
@floatFromInt(grid_size.columns),
|
||||||
|
@floatFromInt(grid_size.rows),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Update our custom shader resolution
|
// Update our custom shader resolution
|
||||||
if (gl_state.custom) |*custom_state| {
|
if (gl_state.custom) |*custom_state| {
|
||||||
try custom_state.setScreenSize(self.size);
|
try custom_state.setScreenSize(self.size);
|
||||||
@ -252,6 +288,7 @@ pub const DerivedConfig = struct {
|
|||||||
invert_selection_fg_bg: bool,
|
invert_selection_fg_bg: bool,
|
||||||
bold_is_bright: bool,
|
bold_is_bright: bool,
|
||||||
min_contrast: f32,
|
min_contrast: f32,
|
||||||
|
padding_color: configpkg.WindowPaddingColor,
|
||||||
custom_shaders: std.ArrayListUnmanaged([:0]const u8),
|
custom_shaders: std.ArrayListUnmanaged([:0]const u8),
|
||||||
links: link.Set,
|
links: link.Set,
|
||||||
|
|
||||||
@ -308,6 +345,7 @@ pub const DerivedConfig = struct {
|
|||||||
.invert_selection_fg_bg = config.@"selection-invert-fg-bg",
|
.invert_selection_fg_bg = config.@"selection-invert-fg-bg",
|
||||||
.bold_is_bright = config.@"bold-is-bright",
|
.bold_is_bright = config.@"bold-is-bright",
|
||||||
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
||||||
|
.padding_color = config.@"window-padding-color",
|
||||||
|
|
||||||
.selection_background = if (config.@"selection-background") |bg|
|
.selection_background = if (config.@"selection-background") |bg|
|
||||||
bg.toTerminalRGB()
|
bg.toTerminalRGB()
|
||||||
|
@ -106,20 +106,31 @@ pub const Image = extern struct {
|
|||||||
|
|
||||||
/// The uniforms that are passed to the terminal cell shader.
|
/// The uniforms that are passed to the terminal cell shader.
|
||||||
pub const Uniforms = extern struct {
|
pub const Uniforms = extern struct {
|
||||||
|
// Note: all of the explicit aligmnments are copied from the
|
||||||
|
// MSL developer reference just so that we can be sure that we got
|
||||||
|
// it all exactly right.
|
||||||
|
|
||||||
/// The projection matrix for turning world coordinates to normalized.
|
/// The projection matrix for turning world coordinates to normalized.
|
||||||
/// This is calculated based on the size of the screen.
|
/// This is calculated based on the size of the screen.
|
||||||
projection_matrix: math.Mat,
|
projection_matrix: math.Mat align(16),
|
||||||
|
|
||||||
/// Size of a single cell in pixels, unscaled.
|
/// Size of a single cell in pixels, unscaled.
|
||||||
cell_size: [2]f32,
|
cell_size: [2]f32 align(8),
|
||||||
|
|
||||||
|
/// Size of the grid in columns and rows.
|
||||||
|
grid_size: [2]u16 align(4),
|
||||||
|
|
||||||
|
/// The padding around the terminal grid in pixels. In order:
|
||||||
|
/// top, right, bottom, left.
|
||||||
|
grid_padding: [4]f32 align(16),
|
||||||
|
|
||||||
/// The minimum contrast ratio for text. The contrast ratio is calculated
|
/// The minimum contrast ratio for text. The contrast ratio is calculated
|
||||||
/// according to the WCAG 2.0 spec.
|
/// according to the WCAG 2.0 spec.
|
||||||
min_contrast: f32,
|
min_contrast: f32 align(4),
|
||||||
|
|
||||||
/// The cursor position and color.
|
/// The cursor position and color.
|
||||||
cursor_pos: [2]u16,
|
cursor_pos: [2]u16 align(4),
|
||||||
cursor_color: [4]u8,
|
cursor_color: [4]u8 align(4),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The uniforms used for custom postprocess shaders.
|
/// The uniforms used for custom postprocess shaders.
|
||||||
|
@ -3,6 +3,8 @@ using namespace metal;
|
|||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
float4x4 projection_matrix;
|
float4x4 projection_matrix;
|
||||||
float2 cell_size;
|
float2 cell_size;
|
||||||
|
ushort2 grid_size;
|
||||||
|
float4 grid_padding;
|
||||||
float min_contrast;
|
float min_contrast;
|
||||||
ushort2 cursor_pos;
|
ushort2 cursor_pos;
|
||||||
uchar4 cursor_color;
|
uchar4 cursor_color;
|
||||||
@ -98,6 +100,21 @@ vertex CellBgVertexOut cell_bg_vertex(unsigned int vid [[vertex_id]],
|
|||||||
float2 cell_size_scaled = uniforms.cell_size;
|
float2 cell_size_scaled = uniforms.cell_size;
|
||||||
cell_size_scaled.x = cell_size_scaled.x * input.cell_width;
|
cell_size_scaled.x = cell_size_scaled.x * input.cell_width;
|
||||||
|
|
||||||
|
// If we're at the edge of the grid, we add our padding to the background
|
||||||
|
// to extend it. Note: grid_padding is top/right/bottom/left.
|
||||||
|
if (input.grid_pos.y == 0) {
|
||||||
|
cell_pos.y -= uniforms.grid_padding.r;
|
||||||
|
cell_size_scaled.y += uniforms.grid_padding.r;
|
||||||
|
} else if (input.grid_pos.y == uniforms.grid_size.y - 1) {
|
||||||
|
cell_size_scaled.y += uniforms.grid_padding.b;
|
||||||
|
}
|
||||||
|
if (input.grid_pos.x == 0) {
|
||||||
|
cell_pos.x -= uniforms.grid_padding.a;
|
||||||
|
cell_size_scaled.x += uniforms.grid_padding.a;
|
||||||
|
} else if (input.grid_pos.x == uniforms.grid_size.x - 1) {
|
||||||
|
cell_size_scaled.x += uniforms.grid_padding.g;
|
||||||
|
}
|
||||||
|
|
||||||
// Turn the cell position into a vertex point depending on the
|
// Turn the cell position into a vertex point depending on the
|
||||||
// vertex ID. Since we use instanced drawing, we have 4 vertices
|
// vertex ID. Since we use instanced drawing, we have 4 vertices
|
||||||
// for each corner of the cell. We can use vertex ID to determine
|
// for each corner of the cell. We can use vertex ID to determine
|
||||||
|
@ -55,6 +55,8 @@ flat out uint mode;
|
|||||||
uniform sampler2D text;
|
uniform sampler2D text;
|
||||||
uniform sampler2D text_color;
|
uniform sampler2D text_color;
|
||||||
uniform vec2 cell_size;
|
uniform vec2 cell_size;
|
||||||
|
uniform vec2 grid_size;
|
||||||
|
uniform vec4 grid_padding;
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
uniform float min_contrast;
|
uniform float min_contrast;
|
||||||
|
|
||||||
@ -167,6 +169,21 @@ void main() {
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case MODE_BG:
|
case MODE_BG:
|
||||||
|
// If we're at the edge of the grid, we add our padding to the background
|
||||||
|
// to extend it. Note: grid_padding is top/right/bottom/left.
|
||||||
|
if (grid_coord.y == 0) {
|
||||||
|
cell_pos.y -= grid_padding.r;
|
||||||
|
cell_size_scaled.y += grid_padding.r;
|
||||||
|
} else if (grid_coord.y == grid_size.y - 1) {
|
||||||
|
cell_size_scaled.y += grid_padding.b;
|
||||||
|
}
|
||||||
|
if (grid_coord.x == 0) {
|
||||||
|
cell_pos.x -= grid_padding.a;
|
||||||
|
cell_size_scaled.x += grid_padding.a;
|
||||||
|
} else if (grid_coord.x == grid_size.x - 1) {
|
||||||
|
cell_size_scaled.x += grid_padding.g;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate the final position of our cell in world space.
|
// Calculate the final position of our cell in world space.
|
||||||
// We have to add our cell size since our vertices are offset
|
// We have to add our cell size since our vertices are offset
|
||||||
// one cell up and to the left. (Do the math to verify yourself)
|
// one cell up and to the left. (Do the math to verify yourself)
|
||||||
|
@ -34,6 +34,25 @@ pub const ScreenSize = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates the amount of blank space around the grid. This is possible
|
||||||
|
/// when padding isn't balanced.
|
||||||
|
///
|
||||||
|
/// The "self" screen size here should be the unpadded screen.
|
||||||
|
pub fn blankPadding(self: ScreenSize, padding: Padding, grid: GridSize, cell: CellSize) Padding {
|
||||||
|
const grid_width = grid.columns * cell.width;
|
||||||
|
const grid_height = grid.rows * cell.height;
|
||||||
|
const padded_width = grid_width + (padding.left + padding.right);
|
||||||
|
const padded_height = grid_height + (padding.top + padding.bottom);
|
||||||
|
const leftover_width = self.width - padded_width;
|
||||||
|
const leftover_height = self.height - padded_height;
|
||||||
|
return .{
|
||||||
|
.top = 0,
|
||||||
|
.bottom = leftover_height,
|
||||||
|
.right = leftover_width,
|
||||||
|
.left = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if two sizes are equal.
|
/// Returns true if two sizes are equal.
|
||||||
pub fn equals(self: ScreenSize, other: ScreenSize) bool {
|
pub fn equals(self: ScreenSize, other: ScreenSize) bool {
|
||||||
return self.width == other.width and self.height == other.height;
|
return self.width == other.width and self.height == other.height;
|
||||||
|
Reference in New Issue
Block a user