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_RECT_HOLLOW = 4u;
|
||||||
const uint MODE_CURSOR_BAR = 5u;
|
const uint MODE_CURSOR_BAR = 5u;
|
||||||
const uint MODE_UNDERLINE = 6u;
|
const uint MODE_UNDERLINE = 6u;
|
||||||
|
const uint MODE_WIDE_MASK = 128u; // 0b1000_0000
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float a;
|
float a;
|
||||||
|
@ -11,6 +11,7 @@ const uint MODE_CURSOR_RECT = 3u;
|
|||||||
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
|
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
|
||||||
const uint MODE_CURSOR_BAR = 5u;
|
const uint MODE_CURSOR_BAR = 5u;
|
||||||
const uint MODE_UNDERLINE = 6u;
|
const uint MODE_UNDERLINE = 6u;
|
||||||
|
const uint MODE_WIDE_MASK = 128u; // 0b1000_0000
|
||||||
|
|
||||||
// The grid coordinates (x, y) where x < columns and y < rows
|
// The grid coordinates (x, y) where x < columns and y < rows
|
||||||
layout (location = 0) in vec2 grid_coord;
|
layout (location = 0) in vec2 grid_coord;
|
||||||
@ -77,8 +78,12 @@ uniform float glyph_baseline;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// We always forward our mode
|
// Remove any masks from our mode
|
||||||
mode = mode_in;
|
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
|
// Top-left cell coordinates converted to world space
|
||||||
// Example: (1,0) with a 30 wide cell is converted to (30,0)
|
// 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.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
|
||||||
position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.;
|
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:
|
case MODE_BG:
|
||||||
// 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)
|
||||||
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);
|
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||||
color = bg_color_in / 255.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: for now, we assume this means it is a full width character
|
||||||
// TODO: in the future, use unicode libs to verify this.
|
// TODO: in the future, use unicode libs to verify this.
|
||||||
vec2 glyph_size_downsampled = glyph_size;
|
vec2 glyph_size_downsampled = glyph_size;
|
||||||
if (mode_in == MODE_FG_COLOR && glyph_size.x > cell_size.x) {
|
if (glyph_size.x > cell_size.x) {
|
||||||
glyph_size_downsampled.x = cell_size.x * 2;
|
glyph_size_downsampled.x = cell_size_scaled.x;
|
||||||
glyph_size_downsampled.y = glyph_size.y * (glyph_size_downsampled.x / glyph_size.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);
|
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.
|
// 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
|
// So we flip it with `cell_size.y - glyph_offset.y`. The glyph_baseline
|
||||||
// uniform sets our line baseline where characters "sit".
|
// 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.
|
// Calculate the final position of the cell.
|
||||||
cell_pos = cell_pos + glyph_size_downsampled * position + glyph_offset_calc;
|
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
|
// 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.
|
// device coordinates (0 to 1.0) by dividing by the size of the texture.
|
||||||
ivec2 text_size;
|
ivec2 text_size;
|
||||||
switch(mode_in) {
|
switch(mode) {
|
||||||
case MODE_FG:
|
case MODE_FG:
|
||||||
text_size = textureSize(text, 0);
|
text_size = textureSize(text, 0);
|
||||||
break;
|
break;
|
||||||
@ -160,7 +171,7 @@ void main() {
|
|||||||
|
|
||||||
case MODE_CURSOR_RECT:
|
case MODE_CURSOR_RECT:
|
||||||
// Same as background since we're taking up the whole cell.
|
// 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);
|
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||||
color = bg_color_in / 255.0;
|
color = bg_color_in / 255.0;
|
||||||
@ -171,7 +182,7 @@ void main() {
|
|||||||
screen_cell_pos = cell_pos;
|
screen_cell_pos = cell_pos;
|
||||||
|
|
||||||
// Same as background since we're taking up the whole cell.
|
// 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);
|
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||||
color = bg_color_in / 255.0;
|
color = bg_color_in / 255.0;
|
||||||
@ -191,11 +202,11 @@ void main() {
|
|||||||
case MODE_UNDERLINE:
|
case MODE_UNDERLINE:
|
||||||
// Make the underline a smaller version of our cell
|
// Make the underline a smaller version of our cell
|
||||||
// TODO: use real font underline thickness
|
// 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
|
// Position our underline so that it is midway between the glyph
|
||||||
// baseline and the bottom of the cell.
|
// 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
|
// Go to the bottom of the cell, take away the size of the
|
||||||
// underline, and that is our position. We also float it slightly
|
// 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,
|
bg_a: u8,
|
||||||
|
|
||||||
/// uint mode
|
/// 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 {
|
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 {
|
fn addCursor(self: *Grid, term: Terminal) void {
|
||||||
// Add the cursor
|
// Add the cursor
|
||||||
if (self.cursor_visible and term.screen.viewportIsBottom()) {
|
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(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = @enumToInt(self.cursor_style),
|
.mode = mode,
|
||||||
.grid_col = @intCast(u16, term.screen.cursor.x),
|
.grid_col = @intCast(u16, term.screen.cursor.x),
|
||||||
.grid_row = @intCast(u16, term.screen.cursor.y),
|
.grid_row = @intCast(u16, term.screen.cursor.y),
|
||||||
.fg_r = 0,
|
.fg_r = 0,
|
||||||
@ -488,8 +522,11 @@ pub fn updateCell(
|
|||||||
|
|
||||||
// If the cell has a background, we always draw it.
|
// If the cell has a background, we always draw it.
|
||||||
if (colors.bg) |rgb| {
|
if (colors.bg) |rgb| {
|
||||||
|
var mode: GPUCellMode = .bg;
|
||||||
|
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = 1,
|
.mode = mode,
|
||||||
.grid_col = @intCast(u16, x),
|
.grid_col = @intCast(u16, x),
|
||||||
.grid_row = @intCast(u16, y),
|
.grid_row = @intCast(u16, y),
|
||||||
.glyph_x = 0,
|
.glyph_x = 0,
|
||||||
@ -517,14 +554,17 @@ pub fn updateCell(
|
|||||||
else
|
else
|
||||||
.regular;
|
.regular;
|
||||||
|
|
||||||
var mode: u8 = 2; // MODE_FG
|
var mode: GPUCellMode = .fg;
|
||||||
|
|
||||||
// Get our glyph. Try our normal font atlas first.
|
// Get our glyph. Try our normal font atlas first.
|
||||||
const goa = try self.font_set.getOrAddGlyph(self.alloc, cell.char, style);
|
const goa = try self.font_set.getOrAddGlyph(self.alloc, cell.char, style);
|
||||||
if (!goa.found_existing) self.atlas_dirty = true;
|
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;
|
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(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
.grid_col = @intCast(u16, x),
|
.grid_col = @intCast(u16, x),
|
||||||
@ -547,8 +587,11 @@ pub fn updateCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cell.attrs.underline == 1) {
|
if (cell.attrs.underline == 1) {
|
||||||
|
var mode: GPUCellMode = .underline;
|
||||||
|
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = 6, // underline
|
.mode = mode,
|
||||||
.grid_col = @intCast(u16, x),
|
.grid_col = @intCast(u16, x),
|
||||||
.grid_row = @intCast(u16, y),
|
.grid_row = @intCast(u16, y),
|
||||||
.glyph_x = 0,
|
.glyph_x = 0,
|
||||||
|
Reference in New Issue
Block a user