mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
render wide chars
This commit is contained in:
@ -31,6 +31,7 @@ const uint MODE_CURSOR_RECT = 3u;
|
||||
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
|
||||
const uint MODE_CURSOR_BAR = 5u;
|
||||
const uint MODE_UNDERLINE = 6u;
|
||||
const uint MODE_WIDE_MASK = 128u; // 0b1000_0000
|
||||
|
||||
void main() {
|
||||
float a;
|
||||
|
@ -11,6 +11,7 @@ const uint MODE_CURSOR_RECT = 3u;
|
||||
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
|
||||
const uint MODE_CURSOR_BAR = 5u;
|
||||
const uint MODE_UNDERLINE = 6u;
|
||||
const uint MODE_WIDE_MASK = 128u; // 0b1000_0000
|
||||
|
||||
// The grid coordinates (x, y) where x < columns and y < rows
|
||||
layout (location = 0) in vec2 grid_coord;
|
||||
@ -77,8 +78,12 @@ uniform float glyph_baseline;
|
||||
*/
|
||||
|
||||
void main() {
|
||||
// We always forward our mode
|
||||
mode = mode_in;
|
||||
// Remove any masks from our mode
|
||||
uint mode_unmasked = mode_in & ~MODE_WIDE_MASK;
|
||||
|
||||
// We always forward our mode unmasked because the fragment
|
||||
// shader doesn't use any of the masks.
|
||||
mode = mode_unmasked;
|
||||
|
||||
// Top-left cell coordinates converted to world space
|
||||
// Example: (1,0) with a 30 wide cell is converted to (30,0)
|
||||
@ -103,12 +108,18 @@ void main() {
|
||||
position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
|
||||
position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.;
|
||||
|
||||
switch (mode_in) {
|
||||
// Scaled for wide chars
|
||||
vec2 cell_size_scaled = cell_size;
|
||||
if ((mode_in & MODE_WIDE_MASK) == MODE_WIDE_MASK) {
|
||||
cell_size_scaled.x = cell_size_scaled.x * 2;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case MODE_BG:
|
||||
// Calculate the final position of our cell in world space.
|
||||
// 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)
|
||||
cell_pos = cell_pos + cell_size * position;
|
||||
cell_pos = cell_pos + cell_size_scaled * position;
|
||||
|
||||
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||
color = bg_color_in / 255.0;
|
||||
@ -122,8 +133,8 @@ void main() {
|
||||
// TODO: for now, we assume this means it is a full width character
|
||||
// TODO: in the future, use unicode libs to verify this.
|
||||
vec2 glyph_size_downsampled = glyph_size;
|
||||
if (mode_in == MODE_FG_COLOR && glyph_size.x > cell_size.x) {
|
||||
glyph_size_downsampled.x = cell_size.x * 2;
|
||||
if (glyph_size.x > cell_size.x) {
|
||||
glyph_size_downsampled.x = cell_size_scaled.x;
|
||||
glyph_size_downsampled.y = glyph_size.y * (glyph_size_downsampled.x / glyph_size.x);
|
||||
glyph_offset_calc.y = glyph_offset.y * (glyph_size_downsampled.x / glyph_size.x);
|
||||
}
|
||||
@ -132,7 +143,7 @@ void main() {
|
||||
// to the baseline is the offset (+y is up). Our grid goes down.
|
||||
// So we flip it with `cell_size.y - glyph_offset.y`. The glyph_baseline
|
||||
// uniform sets our line baseline where characters "sit".
|
||||
glyph_offset_calc.y = cell_size.y - glyph_offset_calc.y - glyph_baseline;
|
||||
glyph_offset_calc.y = cell_size_scaled.y - glyph_offset_calc.y - glyph_baseline;
|
||||
|
||||
// Calculate the final position of the cell.
|
||||
cell_pos = cell_pos + glyph_size_downsampled * position + glyph_offset_calc;
|
||||
@ -141,7 +152,7 @@ void main() {
|
||||
// We need to convert our texture position and size to normalized
|
||||
// device coordinates (0 to 1.0) by dividing by the size of the texture.
|
||||
ivec2 text_size;
|
||||
switch(mode_in) {
|
||||
switch(mode) {
|
||||
case MODE_FG:
|
||||
text_size = textureSize(text, 0);
|
||||
break;
|
||||
@ -160,7 +171,7 @@ void main() {
|
||||
|
||||
case MODE_CURSOR_RECT:
|
||||
// Same as background since we're taking up the whole cell.
|
||||
cell_pos = cell_pos + cell_size * position;
|
||||
cell_pos = cell_pos + cell_size_scaled * position;
|
||||
|
||||
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||
color = bg_color_in / 255.0;
|
||||
@ -171,7 +182,7 @@ void main() {
|
||||
screen_cell_pos = cell_pos;
|
||||
|
||||
// Same as background since we're taking up the whole cell.
|
||||
cell_pos = cell_pos + cell_size * position;
|
||||
cell_pos = cell_pos + cell_size_scaled * position;
|
||||
|
||||
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||
color = bg_color_in / 255.0;
|
||||
@ -191,11 +202,11 @@ void main() {
|
||||
case MODE_UNDERLINE:
|
||||
// Make the underline a smaller version of our cell
|
||||
// TODO: use real font underline thickness
|
||||
vec2 underline_size = vec2(cell_size.x, cell_size.y*0.05);
|
||||
vec2 underline_size = vec2(cell_size_scaled.x, cell_size_scaled.y*0.05);
|
||||
|
||||
// Position our underline so that it is midway between the glyph
|
||||
// baseline and the bottom of the cell.
|
||||
vec2 underline_offset = vec2(cell_size.x, cell_size.y - (glyph_baseline / 2));
|
||||
vec2 underline_offset = vec2(cell_size_scaled.x, cell_size_scaled.y - (glyph_baseline / 2));
|
||||
|
||||
// Go to the bottom of the cell, take away the size of the
|
||||
// underline, and that is our position. We also float it slightly
|
||||
|
55
src/Grid.zig
55
src/Grid.zig
@ -109,7 +109,30 @@ const GPUCell = struct {
|
||||
bg_a: u8,
|
||||
|
||||
/// uint mode
|
||||
mode: u8,
|
||||
mode: GPUCellMode,
|
||||
};
|
||||
|
||||
const GPUCellMode = enum(u8) {
|
||||
bg = 1,
|
||||
fg = 2,
|
||||
fg_color = 7,
|
||||
cursor_rect = 3,
|
||||
cursor_rect_hollow = 4,
|
||||
cursor_bar = 5,
|
||||
underline = 6,
|
||||
|
||||
wide_mask = 0b1000_0000,
|
||||
|
||||
// Non-exhaustive because masks change it
|
||||
_,
|
||||
|
||||
/// Apply a mask to the mode.
|
||||
pub fn mask(self: GPUCellMode, m: GPUCellMode) GPUCellMode {
|
||||
return @intToEnum(
|
||||
GPUCellMode,
|
||||
@enumToInt(self) | @enumToInt(m),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(alloc: Allocator, config: *const Config) !Grid {
|
||||
@ -399,8 +422,19 @@ pub fn finalizeCells(self: *Grid, term: Terminal) !void {
|
||||
fn addCursor(self: *Grid, term: Terminal) void {
|
||||
// Add the cursor
|
||||
if (self.cursor_visible and term.screen.viewportIsBottom()) {
|
||||
const cell = term.screen.getCell(
|
||||
term.screen.cursor.y,
|
||||
term.screen.cursor.x,
|
||||
);
|
||||
|
||||
var mode: GPUCellMode = @intToEnum(
|
||||
GPUCellMode,
|
||||
@enumToInt(self.cursor_style),
|
||||
);
|
||||
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
|
||||
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
.mode = @enumToInt(self.cursor_style),
|
||||
.mode = mode,
|
||||
.grid_col = @intCast(u16, term.screen.cursor.x),
|
||||
.grid_row = @intCast(u16, term.screen.cursor.y),
|
||||
.fg_r = 0,
|
||||
@ -488,8 +522,11 @@ pub fn updateCell(
|
||||
|
||||
// If the cell has a background, we always draw it.
|
||||
if (colors.bg) |rgb| {
|
||||
var mode: GPUCellMode = .bg;
|
||||
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
|
||||
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
.mode = 1,
|
||||
.mode = mode,
|
||||
.grid_col = @intCast(u16, x),
|
||||
.grid_row = @intCast(u16, y),
|
||||
.glyph_x = 0,
|
||||
@ -517,14 +554,17 @@ pub fn updateCell(
|
||||
else
|
||||
.regular;
|
||||
|
||||
var mode: u8 = 2; // MODE_FG
|
||||
var mode: GPUCellMode = .fg;
|
||||
|
||||
// Get our glyph. Try our normal font atlas first.
|
||||
const goa = try self.font_set.getOrAddGlyph(self.alloc, cell.char, style);
|
||||
if (!goa.found_existing) self.atlas_dirty = true;
|
||||
if (goa.family == 1) mode = 7; // MODE_FG_COLOR
|
||||
if (goa.family == 1) mode = .fg_color;
|
||||
const glyph = goa.glyph;
|
||||
|
||||
// If the cell is wide, we need to note that in the mode
|
||||
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
|
||||
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
.mode = mode,
|
||||
.grid_col = @intCast(u16, x),
|
||||
@ -547,8 +587,11 @@ pub fn updateCell(
|
||||
}
|
||||
|
||||
if (cell.attrs.underline == 1) {
|
||||
var mode: GPUCellMode = .underline;
|
||||
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
|
||||
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
.mode = 6, // underline
|
||||
.mode = mode,
|
||||
.grid_col = @intCast(u16, x),
|
||||
.grid_row = @intCast(u16, y),
|
||||
.glyph_x = 0,
|
||||
|
Reference in New Issue
Block a user