mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-08 19:06:36 +00:00
renderer/metal: port
This commit is contained in:
@@ -623,7 +623,6 @@ pub fn updateFrame(
|
|||||||
// Data we extract out of the critical area.
|
// Data we extract out of the critical area.
|
||||||
const Critical = struct {
|
const Critical = struct {
|
||||||
bg: terminal.color.RGB,
|
bg: terminal.color.RGB,
|
||||||
selection: ?terminal.Selection,
|
|
||||||
screen: terminal.Screen,
|
screen: terminal.Screen,
|
||||||
mouse: renderer.State.Mouse,
|
mouse: renderer.State.Mouse,
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
@@ -657,25 +656,13 @@ pub fn updateFrame(
|
|||||||
// We used to share terminal state, but we've since learned through
|
// We used to share terminal state, but we've since learned through
|
||||||
// analysis that it is faster to copy the terminal state than to
|
// analysis that it is faster to copy the terminal state than to
|
||||||
// hold the lock while rebuilding GPU cells.
|
// hold the lock while rebuilding GPU cells.
|
||||||
const viewport_bottom = state.terminal.screen.viewportIsBottom();
|
var screen_copy = try state.terminal.screen.clone(
|
||||||
var screen_copy = if (viewport_bottom) try state.terminal.screen.clone(
|
|
||||||
self.alloc,
|
self.alloc,
|
||||||
.{ .active = 0 },
|
.{ .viewport = .{} },
|
||||||
.{ .active = state.terminal.rows - 1 },
|
null,
|
||||||
) else try state.terminal.screen.clone(
|
|
||||||
self.alloc,
|
|
||||||
.{ .viewport = 0 },
|
|
||||||
.{ .viewport = state.terminal.rows - 1 },
|
|
||||||
);
|
);
|
||||||
errdefer screen_copy.deinit();
|
errdefer screen_copy.deinit();
|
||||||
|
|
||||||
// Convert our selection to viewport points because we copy only
|
|
||||||
// the viewport above.
|
|
||||||
const selection: ?terminal.Selection = if (state.terminal.screen.selection) |sel|
|
|
||||||
sel.toViewport(&state.terminal.screen)
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
|
|
||||||
// Whether to draw our cursor or not.
|
// Whether to draw our cursor or not.
|
||||||
const cursor_style = renderer.cursorStyle(
|
const cursor_style = renderer.cursorStyle(
|
||||||
state,
|
state,
|
||||||
@@ -694,13 +681,15 @@ pub fn updateFrame(
|
|||||||
// If we have Kitty graphics data, we enter a SLOW SLOW SLOW path.
|
// If we have Kitty graphics data, we enter a SLOW SLOW SLOW path.
|
||||||
// We only do this if the Kitty image state is dirty meaning only if
|
// We only do this if the Kitty image state is dirty meaning only if
|
||||||
// it changes.
|
// it changes.
|
||||||
if (state.terminal.screen.kitty_images.dirty) {
|
// TODO(paged-terminal)
|
||||||
try self.prepKittyGraphics(state.terminal);
|
if (false) {
|
||||||
|
if (state.terminal.screen.kitty_images.dirty) {
|
||||||
|
try self.prepKittyGraphics(state.terminal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break :critical .{
|
break :critical .{
|
||||||
.bg = self.background_color,
|
.bg = self.background_color,
|
||||||
.selection = selection,
|
|
||||||
.screen = screen_copy,
|
.screen = screen_copy,
|
||||||
.mouse = state.mouse,
|
.mouse = state.mouse,
|
||||||
.preedit = preedit,
|
.preedit = preedit,
|
||||||
@@ -715,7 +704,6 @@ pub fn updateFrame(
|
|||||||
|
|
||||||
// Build our GPU cells
|
// Build our GPU cells
|
||||||
try self.rebuildCells(
|
try self.rebuildCells(
|
||||||
critical.selection,
|
|
||||||
&critical.screen,
|
&critical.screen,
|
||||||
critical.mouse,
|
critical.mouse,
|
||||||
critical.preedit,
|
critical.preedit,
|
||||||
@@ -1536,16 +1524,21 @@ pub fn setScreenSize(
|
|||||||
/// down to the GPU yet.
|
/// down to the GPU yet.
|
||||||
fn rebuildCells(
|
fn rebuildCells(
|
||||||
self: *Metal,
|
self: *Metal,
|
||||||
term_selection: ?terminal.Selection,
|
|
||||||
screen: *terminal.Screen,
|
screen: *terminal.Screen,
|
||||||
mouse: renderer.State.Mouse,
|
mouse: renderer.State.Mouse,
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
cursor_style_: ?renderer.CursorStyle,
|
cursor_style_: ?renderer.CursorStyle,
|
||||||
color_palette: *const terminal.color.Palette,
|
color_palette: *const terminal.color.Palette,
|
||||||
) !void {
|
) !void {
|
||||||
|
const rows_usize: usize = @intCast(screen.pages.rows);
|
||||||
|
const cols_usize: usize = @intCast(screen.pages.cols);
|
||||||
|
|
||||||
// Bg cells at most will need space for the visible screen size
|
// Bg cells at most will need space for the visible screen size
|
||||||
self.cells_bg.clearRetainingCapacity();
|
self.cells_bg.clearRetainingCapacity();
|
||||||
try self.cells_bg.ensureTotalCapacity(self.alloc, screen.rows * screen.cols);
|
try self.cells_bg.ensureTotalCapacity(
|
||||||
|
self.alloc,
|
||||||
|
rows_usize * cols_usize,
|
||||||
|
);
|
||||||
|
|
||||||
// Over-allocate just to ensure we don't allocate again during loops.
|
// Over-allocate just to ensure we don't allocate again during loops.
|
||||||
self.cells.clearRetainingCapacity();
|
self.cells.clearRetainingCapacity();
|
||||||
@@ -1554,21 +1547,24 @@ fn rebuildCells(
|
|||||||
|
|
||||||
// * 3 for glyph + underline + strikethrough for each cell
|
// * 3 for glyph + underline + strikethrough for each cell
|
||||||
// + 1 for cursor
|
// + 1 for cursor
|
||||||
(screen.rows * screen.cols * 3) + 1,
|
(rows_usize * cols_usize * 3) + 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create an arena for all our temporary allocations while rebuilding
|
// Create an arena for all our temporary allocations while rebuilding
|
||||||
var arena = ArenaAllocator.init(self.alloc);
|
var arena = ArenaAllocator.init(self.alloc);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const arena_alloc = arena.allocator();
|
const arena_alloc = arena.allocator();
|
||||||
|
_ = arena_alloc;
|
||||||
|
_ = mouse;
|
||||||
|
|
||||||
// Create our match set for the links.
|
// Create our match set for the links.
|
||||||
var link_match_set: link.MatchSet = if (mouse.point) |mouse_pt| try self.config.links.matchSet(
|
// TODO(paged-terminal)
|
||||||
arena_alloc,
|
// var link_match_set: link.MatchSet = if (mouse.point) |mouse_pt| try self.config.links.matchSet(
|
||||||
screen,
|
// arena_alloc,
|
||||||
mouse_pt,
|
// screen,
|
||||||
mouse.mods,
|
// mouse_pt,
|
||||||
) else .{};
|
// mouse.mods,
|
||||||
|
// ) else .{};
|
||||||
|
|
||||||
// Determine our x/y range for preedit. We don't want to render anything
|
// Determine our x/y range for preedit. We don't want to render anything
|
||||||
// here because we will render the preedit separately.
|
// here because we will render the preedit separately.
|
||||||
@@ -1577,7 +1573,7 @@ fn rebuildCells(
|
|||||||
x: [2]usize,
|
x: [2]usize,
|
||||||
cp_offset: usize,
|
cp_offset: usize,
|
||||||
} = if (preedit) |preedit_v| preedit: {
|
} = if (preedit) |preedit_v| preedit: {
|
||||||
const range = preedit_v.range(screen.cursor.x, screen.cols - 1);
|
const range = preedit_v.range(screen.cursor.x, screen.pages.cols - 1);
|
||||||
break :preedit .{
|
break :preedit .{
|
||||||
.y = screen.cursor.y,
|
.y = screen.cursor.y,
|
||||||
.x = .{ range.start, range.end },
|
.x = .{ range.start, range.end },
|
||||||
@@ -1591,9 +1587,9 @@ fn rebuildCells(
|
|||||||
var cursor_cell: ?mtl_shaders.Cell = null;
|
var cursor_cell: ?mtl_shaders.Cell = null;
|
||||||
|
|
||||||
// Build each cell
|
// Build each cell
|
||||||
var rowIter = screen.rowIterator(.viewport);
|
var row_it = screen.pages.rowIterator(.right_down, .{ .viewport = .{} }, null);
|
||||||
var y: usize = 0;
|
var y: usize = 0;
|
||||||
while (rowIter.next()) |row| {
|
while (row_it.next()) |row| {
|
||||||
defer y += 1;
|
defer y += 1;
|
||||||
|
|
||||||
// True if this is the row with our cursor. There are a lot of conditions
|
// True if this is the row with our cursor. There are a lot of conditions
|
||||||
@@ -1628,8 +1624,8 @@ fn rebuildCells(
|
|||||||
defer if (cursor_row) {
|
defer if (cursor_row) {
|
||||||
// If we're on a wide spacer tail, then we want to look for
|
// If we're on a wide spacer tail, then we want to look for
|
||||||
// the previous cell.
|
// the previous cell.
|
||||||
const screen_cell = row.getCell(screen.cursor.x);
|
const screen_cell = row.cells(.all)[screen.cursor.x];
|
||||||
const x = screen.cursor.x - @intFromBool(screen_cell.attrs.wide_spacer_tail);
|
const x = screen.cursor.x - @intFromBool(screen_cell.wide == .spacer_tail);
|
||||||
for (self.cells.items[start_i..]) |cell| {
|
for (self.cells.items[start_i..]) |cell| {
|
||||||
if (cell.grid_pos[0] == @as(f32, @floatFromInt(x)) and
|
if (cell.grid_pos[0] == @as(f32, @floatFromInt(x)) and
|
||||||
(cell.mode == .fg or cell.mode == .fg_color))
|
(cell.mode == .fg or cell.mode == .fg_color))
|
||||||
@@ -1643,15 +1639,16 @@ fn rebuildCells(
|
|||||||
// We need to get this row's selection if there is one for proper
|
// We need to get this row's selection if there is one for proper
|
||||||
// run splitting.
|
// run splitting.
|
||||||
const row_selection = sel: {
|
const row_selection = sel: {
|
||||||
if (term_selection) |sel| {
|
// TODO(paged-terminal)
|
||||||
const screen_point = (terminal.point.Viewport{
|
// if (screen.selection) |sel| {
|
||||||
.x = 0,
|
// const screen_point = (terminal.point.Viewport{
|
||||||
.y = y,
|
// .x = 0,
|
||||||
}).toScreen(screen);
|
// .y = y,
|
||||||
if (sel.containedRow(screen, screen_point)) |row_sel| {
|
// }).toScreen(screen);
|
||||||
break :sel row_sel;
|
// if (sel.containedRow(screen, screen_point)) |row_sel| {
|
||||||
}
|
// break :sel row_sel;
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
break :sel null;
|
break :sel null;
|
||||||
};
|
};
|
||||||
@@ -1659,6 +1656,7 @@ fn rebuildCells(
|
|||||||
// Split our row into runs and shape each one.
|
// Split our row into runs and shape each one.
|
||||||
var iter = self.font_shaper.runIterator(
|
var iter = self.font_shaper.runIterator(
|
||||||
self.font_group,
|
self.font_group,
|
||||||
|
screen,
|
||||||
row,
|
row,
|
||||||
row_selection,
|
row_selection,
|
||||||
if (shape_cursor) screen.cursor.x else null,
|
if (shape_cursor) screen.cursor.x else null,
|
||||||
@@ -1679,24 +1677,23 @@ fn rebuildCells(
|
|||||||
|
|
||||||
// It this cell is within our hint range then we need to
|
// It this cell is within our hint range then we need to
|
||||||
// underline it.
|
// underline it.
|
||||||
const cell: terminal.Screen.Cell = cell: {
|
const cell: terminal.Pin = cell: {
|
||||||
var cell = row.getCell(shaper_cell.x);
|
var copy = row;
|
||||||
|
copy.x = shaper_cell.x;
|
||||||
|
break :cell copy;
|
||||||
|
|
||||||
// If our links contain this cell then we want to
|
// TODO(paged-terminal)
|
||||||
// underline it.
|
// // If our links contain this cell then we want to
|
||||||
if (link_match_set.orderedContains(.{
|
// // underline it.
|
||||||
.x = shaper_cell.x,
|
// if (link_match_set.orderedContains(.{
|
||||||
.y = y,
|
// .x = shaper_cell.x,
|
||||||
})) {
|
// .y = y,
|
||||||
cell.attrs.underline = .single;
|
// })) {
|
||||||
}
|
// cell.attrs.underline = .single;
|
||||||
|
// }
|
||||||
break :cell cell;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.updateCell(
|
if (self.updateCell(
|
||||||
term_selection,
|
|
||||||
screen,
|
|
||||||
cell,
|
cell,
|
||||||
color_palette,
|
color_palette,
|
||||||
shaper_cell,
|
shaper_cell,
|
||||||
@@ -1714,9 +1711,6 @@ fn rebuildCells(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set row is not dirty anymore
|
|
||||||
row.setDirty(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the cursor at the end so that it overlays everything. If we have
|
// Add the cursor at the end so that it overlays everything. If we have
|
||||||
@@ -1744,7 +1738,8 @@ fn rebuildCells(
|
|||||||
break :cursor_style;
|
break :cursor_style;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = self.addCursor(screen, cursor_style);
|
_ = cursor_style;
|
||||||
|
//_ = self.addCursor(screen, cursor_style);
|
||||||
if (cursor_cell) |*cell| {
|
if (cursor_cell) |*cell| {
|
||||||
if (cell.mode == .fg) {
|
if (cell.mode == .fg) {
|
||||||
cell.color = if (self.config.cursor_text) |txt|
|
cell.color = if (self.config.cursor_text) |txt|
|
||||||
@@ -1766,9 +1761,7 @@ fn rebuildCells(
|
|||||||
|
|
||||||
fn updateCell(
|
fn updateCell(
|
||||||
self: *Metal,
|
self: *Metal,
|
||||||
selection: ?terminal.Selection,
|
cell_pin: terminal.Pin,
|
||||||
screen: *terminal.Screen,
|
|
||||||
cell: terminal.Screen.Cell,
|
|
||||||
palette: *const terminal.color.Palette,
|
palette: *const terminal.color.Palette,
|
||||||
shaper_cell: font.shape.Cell,
|
shaper_cell: font.shape.Cell,
|
||||||
shaper_run: font.shape.TextRun,
|
shaper_run: font.shape.TextRun,
|
||||||
@@ -1790,45 +1783,35 @@ fn updateCell(
|
|||||||
// True if this cell is selected
|
// True if this cell is selected
|
||||||
// TODO(perf): we can check in advance if selection is in
|
// TODO(perf): we can check in advance if selection is in
|
||||||
// our viewport at all and not run this on every point.
|
// our viewport at all and not run this on every point.
|
||||||
const selected: bool = if (selection) |sel| selected: {
|
const selected = false;
|
||||||
const screen_point = (terminal.point.Viewport{
|
// TODO(paged-terminal)
|
||||||
.x = x,
|
// const selected: bool = if (screen.selection) |sel| selected: {
|
||||||
.y = y,
|
// const screen_point = (terminal.point.Viewport{
|
||||||
}).toScreen(screen);
|
// .x = x,
|
||||||
|
// .y = y,
|
||||||
|
// }).toScreen(screen);
|
||||||
|
//
|
||||||
|
// break :selected sel.contains(screen_point);
|
||||||
|
// } else false;
|
||||||
|
|
||||||
break :selected sel.contains(screen_point);
|
const rac = cell_pin.rowAndCell();
|
||||||
} else false;
|
const cell = rac.cell;
|
||||||
|
const style = cell_pin.style(cell);
|
||||||
|
|
||||||
// The colors for the cell.
|
// The colors for the cell.
|
||||||
const colors: BgFg = colors: {
|
const colors: BgFg = colors: {
|
||||||
// The normal cell result
|
// The normal cell result
|
||||||
const cell_res: BgFg = if (!cell.attrs.inverse) .{
|
const cell_res: BgFg = if (!style.flags.inverse) .{
|
||||||
// In normal mode, background and fg match the cell. We
|
// In normal mode, background and fg match the cell. We
|
||||||
// un-optionalize the fg by defaulting to our fg color.
|
// un-optionalize the fg by defaulting to our fg color.
|
||||||
.bg = switch (cell.bg) {
|
.bg = style.bg(cell, palette),
|
||||||
.none => null,
|
.fg = style.fg(palette) orelse self.foreground_color,
|
||||||
.indexed => |i| palette[i],
|
|
||||||
.rgb => |rgb| rgb,
|
|
||||||
},
|
|
||||||
.fg = switch (cell.fg) {
|
|
||||||
.none => self.foreground_color,
|
|
||||||
.indexed => |i| palette[i],
|
|
||||||
.rgb => |rgb| rgb,
|
|
||||||
},
|
|
||||||
} else .{
|
} else .{
|
||||||
// In inverted mode, the background MUST be set to something
|
// In inverted mode, the background MUST be set to something
|
||||||
// (is never null) so it is either the fg or default fg. The
|
// (is never null) so it is either the fg or default fg. The
|
||||||
// fg is either the bg or default background.
|
// fg is either the bg or default background.
|
||||||
.bg = switch (cell.fg) {
|
.bg = style.fg(palette) orelse self.foreground_color,
|
||||||
.none => self.foreground_color,
|
.fg = style.bg(cell, palette) orelse self.background_color,
|
||||||
.indexed => |i| palette[i],
|
|
||||||
.rgb => |rgb| rgb,
|
|
||||||
},
|
|
||||||
.fg = switch (cell.bg) {
|
|
||||||
.none => self.background_color,
|
|
||||||
.indexed => |i| palette[i],
|
|
||||||
.rgb => |rgb| rgb,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we are selected, we our colors are just inverted fg/bg
|
// If we are selected, we our colors are just inverted fg/bg
|
||||||
@@ -1846,7 +1829,7 @@ fn updateCell(
|
|||||||
// If the cell is "invisible" then we just make fg = bg so that
|
// If the cell is "invisible" then we just make fg = bg so that
|
||||||
// the cell is transparent but still copy-able.
|
// the cell is transparent but still copy-able.
|
||||||
const res: BgFg = selection_res orelse cell_res;
|
const res: BgFg = selection_res orelse cell_res;
|
||||||
if (cell.attrs.invisible) {
|
if (style.flags.invisible) {
|
||||||
break :colors BgFg{
|
break :colors BgFg{
|
||||||
.bg = res.bg,
|
.bg = res.bg,
|
||||||
.fg = res.bg orelse self.background_color,
|
.fg = res.bg orelse self.background_color,
|
||||||
@@ -1857,7 +1840,7 @@ fn updateCell(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Alpha multiplier
|
// Alpha multiplier
|
||||||
const alpha: u8 = if (cell.attrs.faint) 175 else 255;
|
const alpha: u8 = if (style.flags.faint) 175 else 255;
|
||||||
|
|
||||||
// If the cell has a background, we always draw it.
|
// If the cell has a background, we always draw it.
|
||||||
const bg: [4]u8 = if (colors.bg) |rgb| bg: {
|
const bg: [4]u8 = if (colors.bg) |rgb| bg: {
|
||||||
@@ -1874,11 +1857,11 @@ fn updateCell(
|
|||||||
if (selected) break :bg_alpha default;
|
if (selected) break :bg_alpha default;
|
||||||
|
|
||||||
// If we're reversed, do not apply background opacity
|
// If we're reversed, do not apply background opacity
|
||||||
if (cell.attrs.inverse) break :bg_alpha default;
|
if (style.flags.inverse) break :bg_alpha default;
|
||||||
|
|
||||||
// If we have a background and its not the default background
|
// If we have a background and its not the default background
|
||||||
// then we apply background opacity
|
// then we apply background opacity
|
||||||
if (cell.bg != .none and !rgb.eql(self.background_color)) {
|
if (style.bg(cell, palette) != null and !rgb.eql(self.background_color)) {
|
||||||
break :bg_alpha default;
|
break :bg_alpha default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1892,7 +1875,7 @@ fn updateCell(
|
|||||||
self.cells_bg.appendAssumeCapacity(.{
|
self.cells_bg.appendAssumeCapacity(.{
|
||||||
.mode = .bg,
|
.mode = .bg,
|
||||||
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
||||||
.cell_width = cell.widthLegacy(),
|
.cell_width = cell.gridWidth(),
|
||||||
.color = .{ rgb.r, rgb.g, rgb.b, bg_alpha },
|
.color = .{ rgb.r, rgb.g, rgb.b, bg_alpha },
|
||||||
.bg_color = .{ 0, 0, 0, 0 },
|
.bg_color = .{ 0, 0, 0, 0 },
|
||||||
});
|
});
|
||||||
@@ -1906,7 +1889,7 @@ fn updateCell(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If the cell has a character, draw it
|
// If the cell has a character, draw it
|
||||||
if (cell.char > 0) fg: {
|
if (cell.hasText()) fg: {
|
||||||
// Render
|
// Render
|
||||||
const glyph = try self.font_group.renderGlyph(
|
const glyph = try self.font_group.renderGlyph(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
@@ -1920,11 +1903,8 @@ fn updateCell(
|
|||||||
|
|
||||||
const mode: mtl_shaders.Cell.Mode = switch (try fgMode(
|
const mode: mtl_shaders.Cell.Mode = switch (try fgMode(
|
||||||
&self.font_group.group,
|
&self.font_group.group,
|
||||||
screen,
|
cell_pin,
|
||||||
cell,
|
|
||||||
shaper_run,
|
shaper_run,
|
||||||
x,
|
|
||||||
y,
|
|
||||||
)) {
|
)) {
|
||||||
.normal => .fg,
|
.normal => .fg,
|
||||||
.color => .fg_color,
|
.color => .fg_color,
|
||||||
@@ -1934,7 +1914,7 @@ fn updateCell(
|
|||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
||||||
.cell_width = cell.widthLegacy(),
|
.cell_width = cell.gridWidth(),
|
||||||
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
|
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
|
||||||
.bg_color = bg,
|
.bg_color = bg,
|
||||||
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
||||||
@@ -1946,8 +1926,8 @@ fn updateCell(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell.attrs.underline != .none) {
|
if (style.flags.underline != .none) {
|
||||||
const sprite: font.Sprite = switch (cell.attrs.underline) {
|
const sprite: font.Sprite = switch (style.flags.underline) {
|
||||||
.none => unreachable,
|
.none => unreachable,
|
||||||
.single => .underline,
|
.single => .underline,
|
||||||
.double => .underline_double,
|
.double => .underline_double,
|
||||||
@@ -1961,17 +1941,17 @@ fn updateCell(
|
|||||||
font.sprite_index,
|
font.sprite_index,
|
||||||
@intFromEnum(sprite),
|
@intFromEnum(sprite),
|
||||||
.{
|
.{
|
||||||
.cell_width = if (cell.attrs.wide) 2 else 1,
|
.cell_width = if (cell.wide == .wide) 2 else 1,
|
||||||
.grid_metrics = self.grid_metrics,
|
.grid_metrics = self.grid_metrics,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const color = if (cell.attrs.underline_color) cell.underline_fg else colors.fg;
|
const color = style.underlineColor(palette) orelse colors.fg;
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = .fg,
|
.mode = .fg,
|
||||||
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
||||||
.cell_width = cell.widthLegacy(),
|
.cell_width = cell.gridWidth(),
|
||||||
.color = .{ color.r, color.g, color.b, alpha },
|
.color = .{ color.r, color.g, color.b, alpha },
|
||||||
.bg_color = bg,
|
.bg_color = bg,
|
||||||
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
||||||
@@ -1980,11 +1960,11 @@ fn updateCell(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell.attrs.strikethrough) {
|
if (style.flags.strikethrough) {
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = .strikethrough,
|
.mode = .strikethrough,
|
||||||
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
||||||
.cell_width = cell.widthLegacy(),
|
.cell_width = cell.gridWidth(),
|
||||||
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
|
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
|
||||||
.bg_color = bg,
|
.bg_color = bg,
|
||||||
});
|
});
|
||||||
@@ -2002,21 +1982,14 @@ fn addCursor(
|
|||||||
// we're on the wide characer tail.
|
// we're on the wide characer tail.
|
||||||
const wide, const x = cell: {
|
const wide, const x = cell: {
|
||||||
// The cursor goes over the screen cursor position.
|
// The cursor goes over the screen cursor position.
|
||||||
const cell = screen.getCell(
|
const cell = screen.cursor.page_cell;
|
||||||
.active,
|
if (cell.wide != .spacer_tail or screen.cursor.x == 0)
|
||||||
screen.cursor.y,
|
break :cell .{ cell.wide == .wide, screen.cursor.x };
|
||||||
screen.cursor.x,
|
|
||||||
);
|
|
||||||
if (!cell.attrs.wide_spacer_tail or screen.cursor.x == 0)
|
|
||||||
break :cell .{ cell.attrs.wide, screen.cursor.x };
|
|
||||||
|
|
||||||
// If we're part of a wide character, we move the cursor back to
|
// If we're part of a wide character, we move the cursor back to
|
||||||
// the actual character.
|
// the actual character.
|
||||||
break :cell .{ screen.getCell(
|
const prev_cell = screen.cursorCellLeft(1);
|
||||||
.active,
|
break :cell .{ prev_cell.wide == .wide, screen.cursor.x - 1 };
|
||||||
screen.cursor.y,
|
|
||||||
screen.cursor.x - 1,
|
|
||||||
).attrs.wide, screen.cursor.x - 1 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const color = self.cursor_color orelse self.foreground_color;
|
const color = self.cursor_color orelse self.foreground_color;
|
||||||
|
@@ -21,11 +21,8 @@ pub const FgMode = enum {
|
|||||||
/// renderer.
|
/// renderer.
|
||||||
pub fn fgMode(
|
pub fn fgMode(
|
||||||
group: *font.Group,
|
group: *font.Group,
|
||||||
screen: *terminal.Screen,
|
cell_pin: terminal.Pin,
|
||||||
cell: terminal.Screen.Cell,
|
|
||||||
shaper_run: font.shape.TextRun,
|
shaper_run: font.shape.TextRun,
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
) !FgMode {
|
) !FgMode {
|
||||||
const presentation = try group.presentationFromIndex(shaper_run.font_index);
|
const presentation = try group.presentationFromIndex(shaper_run.font_index);
|
||||||
return switch (presentation) {
|
return switch (presentation) {
|
||||||
@@ -41,42 +38,55 @@ pub fn fgMode(
|
|||||||
// the subsequent character is empty, then we allow it to use
|
// the subsequent character is empty, then we allow it to use
|
||||||
// the full glyph size. See #1071.
|
// the full glyph size. See #1071.
|
||||||
.text => text: {
|
.text => text: {
|
||||||
if (!ziglyph.general_category.isPrivateUse(@intCast(cell.char)) and
|
const cell = cell_pin.rowAndCell().cell;
|
||||||
!ziglyph.blocks.isDingbats(@intCast(cell.char)))
|
const cp = cell.codepoint();
|
||||||
|
|
||||||
|
if (!ziglyph.general_category.isPrivateUse(cp) and
|
||||||
|
!ziglyph.blocks.isDingbats(cp))
|
||||||
{
|
{
|
||||||
break :text .normal;
|
break :text .normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We exempt the Powerline range from this since they exhibit
|
// We exempt the Powerline range from this since they exhibit
|
||||||
// box-drawing behavior and should not be constrained.
|
// box-drawing behavior and should not be constrained.
|
||||||
if (isPowerline(cell.char)) {
|
if (isPowerline(cp)) {
|
||||||
break :text .normal;
|
break :text .normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are at the end of the screen its definitely constrained
|
// If we are at the end of the screen its definitely constrained
|
||||||
if (x == screen.cols - 1) break :text .constrained;
|
if (cell_pin.x == cell_pin.page.data.size.cols - 1) break :text .constrained;
|
||||||
|
|
||||||
// If we have a previous cell and it was PUA then we need to
|
// If we have a previous cell and it was PUA then we need to
|
||||||
// also constrain. This is so that multiple PUA glyphs align.
|
// also constrain. This is so that multiple PUA glyphs align.
|
||||||
// As an exception, we ignore powerline glyphs since they are
|
// As an exception, we ignore powerline glyphs since they are
|
||||||
// used for box drawing and we consider them whitespace.
|
// used for box drawing and we consider them whitespace.
|
||||||
if (x > 0) prev: {
|
if (cell_pin.x > 0) prev: {
|
||||||
const prev_cell = screen.getCell(.active, y, x - 1);
|
const prev_cp = prev_cp: {
|
||||||
|
var copy = cell_pin;
|
||||||
|
copy.x -= 1;
|
||||||
|
const prev_cell = copy.rowAndCell().cell;
|
||||||
|
break :prev_cp prev_cell.codepoint();
|
||||||
|
};
|
||||||
|
|
||||||
// Powerline is whitespace
|
// Powerline is whitespace
|
||||||
if (isPowerline(prev_cell.char)) break :prev;
|
if (isPowerline(prev_cp)) break :prev;
|
||||||
|
|
||||||
if (ziglyph.general_category.isPrivateUse(@intCast(prev_cell.char))) {
|
if (ziglyph.general_category.isPrivateUse(prev_cp)) {
|
||||||
break :text .constrained;
|
break :text .constrained;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the next cell is empty, then we allow it to use the
|
// If the next cell is empty, then we allow it to use the
|
||||||
// full glyph size.
|
// full glyph size.
|
||||||
const next_cell = screen.getCell(.active, y, x + 1);
|
const next_cp = next_cp: {
|
||||||
if (next_cell.char == 0 or
|
var copy = cell_pin;
|
||||||
next_cell.char == ' ' or
|
copy.x += 1;
|
||||||
isPowerline(next_cell.char))
|
const next_cell = copy.rowAndCell().cell;
|
||||||
|
break :next_cp next_cell.codepoint();
|
||||||
|
};
|
||||||
|
if (next_cp == 0 or
|
||||||
|
next_cp == ' ' or
|
||||||
|
isPowerline(next_cp))
|
||||||
{
|
{
|
||||||
break :text .normal;
|
break :text .normal;
|
||||||
}
|
}
|
||||||
@@ -88,7 +98,7 @@ pub fn fgMode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the codepoint is a part of the Powerline range.
|
// Returns true if the codepoint is a part of the Powerline range.
|
||||||
fn isPowerline(char: u32) bool {
|
fn isPowerline(char: u21) bool {
|
||||||
return switch (char) {
|
return switch (char) {
|
||||||
0xE0B0...0xE0C8, 0xE0CA, 0xE0CC...0xE0D2, 0xE0D4 => true,
|
0xE0B0...0xE0C8, 0xE0CA, 0xE0CC...0xE0D2, 0xE0D4 => true,
|
||||||
else => false,
|
else => false,
|
||||||
|
@@ -227,6 +227,7 @@ pub fn clonePool(
|
|||||||
.pages = pages,
|
.pages = pages,
|
||||||
.no_scrollback = self.no_scrollback,
|
.no_scrollback = self.no_scrollback,
|
||||||
|
|
||||||
|
// TODO: selection
|
||||||
// TODO: let's make this reasonble
|
// TODO: let's make this reasonble
|
||||||
.cursor = undefined,
|
.cursor = undefined,
|
||||||
};
|
};
|
||||||
|
@@ -779,6 +779,14 @@ pub const Cell = packed struct(u64) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The width in grid cells that this cell takes up.
|
||||||
|
pub fn gridWidth(self: Cell) u2 {
|
||||||
|
return switch (self.wide) {
|
||||||
|
.narrow, .spacer_head, .spacer_tail => 1,
|
||||||
|
.wide => 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hasStyling(self: Cell) bool {
|
pub fn hasStyling(self: Cell) bool {
|
||||||
return self.style_id != style.default_id;
|
return self.style_id != style.default_id;
|
||||||
}
|
}
|
||||||
|
@@ -51,6 +51,56 @@ pub const Style = struct {
|
|||||||
return std.mem.eql(u8, std.mem.asBytes(&self), def);
|
return std.mem.eql(u8, std.mem.asBytes(&self), def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the bg color for a cell with this style given the cell
|
||||||
|
/// that has this style and the palette to use.
|
||||||
|
///
|
||||||
|
/// Note that generally if a cell is a color-only cell, it SHOULD
|
||||||
|
/// only have the default style, but this is meant to work with the
|
||||||
|
/// default style as well.
|
||||||
|
pub fn bg(
|
||||||
|
self: Style,
|
||||||
|
cell: *const page.Cell,
|
||||||
|
palette: *const color.Palette,
|
||||||
|
) ?color.RGB {
|
||||||
|
return switch (cell.content_tag) {
|
||||||
|
.bg_color_palette => palette[cell.content.color_palette],
|
||||||
|
.bg_color_rgb => rgb: {
|
||||||
|
const rgb = cell.content.color_rgb;
|
||||||
|
break :rgb .{ .r = rgb.r, .g = rgb.g, .b = rgb.b };
|
||||||
|
},
|
||||||
|
|
||||||
|
else => switch (self.bg_color) {
|
||||||
|
.none => null,
|
||||||
|
.palette => |idx| palette[idx],
|
||||||
|
.rgb => |rgb| rgb,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the fg color for a cell with this style given the palette.
|
||||||
|
pub fn fg(
|
||||||
|
self: Style,
|
||||||
|
palette: *const color.Palette,
|
||||||
|
) ?color.RGB {
|
||||||
|
return switch (self.fg_color) {
|
||||||
|
.none => null,
|
||||||
|
.palette => |idx| palette[idx],
|
||||||
|
.rgb => |rgb| rgb,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the underline color for this style.
|
||||||
|
pub fn underlineColor(
|
||||||
|
self: Style,
|
||||||
|
palette: *const color.Palette,
|
||||||
|
) ?color.RGB {
|
||||||
|
return switch (self.underline_color) {
|
||||||
|
.none => null,
|
||||||
|
.palette => |idx| palette[idx],
|
||||||
|
.rgb => |rgb| rgb,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a bg-color only cell from this style, if it exists.
|
/// Returns a bg-color only cell from this style, if it exists.
|
||||||
pub fn bgCell(self: Style) ?page.Cell {
|
pub fn bgCell(self: Style) ?page.Cell {
|
||||||
return switch (self.bg_color) {
|
return switch (self.bg_color) {
|
||||||
|
Reference in New Issue
Block a user