diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 0fda9cbe4..0cbc1615e 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -631,6 +631,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .min_contrast = options.config.min_contrast, .cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) }, .cursor_color = undefined, + .wide_cursor = false, }, // Fonts @@ -2034,6 +2035,7 @@ pub fn setScreenSize( .min_contrast = old.min_contrast, .cursor_pos = old.cursor_pos, .cursor_color = old.cursor_color, + .wide_cursor = old.wide_cursor, }; // Reset our cell contents if our grid size has changed. @@ -2353,10 +2355,19 @@ fn rebuildCells( // If the cursor is visible then we set our uniforms. if (style == .block and screen.viewportIsBottom()) { + const wide = screen.cursor.page_cell.wide; + self.uniforms.cursor_pos = .{ - screen.cursor.x, + switch (wide) { + .narrow, .spacer_head, .wide => screen.cursor.x, + .spacer_tail => screen.cursor.x -| 1, + }, screen.cursor.y, }; + self.uniforms.wide_cursor = switch (wide) { + .narrow, .spacer_head => false, + .wide, .spacer_tail => true, + }; const uniform_color = if (self.cursor_invert) blk: { const sty = screen.cursor.page_pin.style(screen.cursor.page_cell); diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 7ea3f6ec8..f9246aa22 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -1227,10 +1227,15 @@ pub fn rebuildCells( }; } else null; - // This is the cell that has [mode == .fg] and is underneath our cursor. - // We keep track of it so that we can invert the colors so the character - // remains visible. - var cursor_cell: ?CellProgram.Cell = null; + // These are all the foreground cells underneath the cursor. + // + // We keep track of these so that we can invert the colors and move them + // in front of the block cursor so that the character remains visible. + // + // We init with a capacity of 4 to account for decorations such + // as underline and strikethrough, as well as combining chars. + var cursor_cells = try std.ArrayListUnmanaged(CellProgram.Cell).initCapacity(arena_alloc, 4); + defer cursor_cells.deinit(arena_alloc); if (rebuild) { switch (self.config.padding_color) { @@ -1277,14 +1282,24 @@ pub fn rebuildCells( // the cell with the cursor. const start_i: usize = self.cells.items.len; defer if (cursor_row) { - // If we're on a wide spacer tail, then we want to look for - // the previous cell. - const screen_cell = row.cells(.all)[screen.cursor.x]; - const x = screen.cursor.x - @intFromBool(screen_cell.wide == .spacer_tail); + const x = screen.cursor.x; + const wide = row.cells(.all)[x].wide; + const min_x = switch (wide) { + .narrow, .spacer_head, .wide => x, + .spacer_tail => x -| 1, + }; + const max_x = switch (wide) { + .narrow, .spacer_head, .spacer_tail => x, + .wide => x +| 1, + }; for (self.cells.items[start_i..]) |cell| { - if (cell.grid_col == x and cell.mode.isFg()) { - cursor_cell = cell; - break; + if (cell.grid_col < min_x or cell.grid_col > max_x) continue; + if (cell.mode.isFg()) { + cursor_cells.append(arena_alloc, cell) catch { + // We silently ignore if this fails because + // worst case scenario some combining glyphs + // aren't visible under the cursor '\_('-')_/' + }; } } }; @@ -1422,7 +1437,7 @@ pub fn rebuildCells( }; _ = try self.addCursor(screen, cursor_style, cursor_color); - if (cursor_cell) |*cell| { + for (cursor_cells.items) |*cell| { if (cell.mode.isFg() and cell.mode != .fg_color) { const cell_color = if (self.cursor_invert) blk: { const sty = screen.cursor.page_pin.style(screen.cursor.page_cell); diff --git a/src/renderer/metal/shaders.zig b/src/renderer/metal/shaders.zig index 2a202de30..c1c403848 100644 --- a/src/renderer/metal/shaders.zig +++ b/src/renderer/metal/shaders.zig @@ -137,6 +137,9 @@ pub const Uniforms = extern struct { cursor_pos: [2]u16 align(4), cursor_color: [4]u8 align(4), + // Whether the cursor is 2 cells wide. + wide_cursor: bool align(1), + const PaddingExtend = packed struct(u8) { left: bool = false, right: bool = false, diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index b6af33824..44540e198 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -18,6 +18,7 @@ struct Uniforms { float min_contrast; ushort2 cursor_pos; uchar4 cursor_color; + bool wide_cursor; }; //------------------------------------------------------------------- @@ -293,7 +294,11 @@ vertex CellTextVertexOut cell_text_vertex( // If this cell is the cursor cell, then we need to change the color. if ( in.mode != MODE_TEXT_CURSOR && - in.grid_pos.x == uniforms.cursor_pos.x && + ( + in.grid_pos.x == uniforms.cursor_pos.x || + uniforms.wide_cursor && + in.grid_pos.x == uniforms.cursor_pos.x + 1 + ) && in.grid_pos.y == uniforms.cursor_pos.y ) { out.color = float4(uniforms.cursor_color) / 255.0f;