diff --git a/src/font/sprite.zig b/src/font/sprite.zig index c3c311b35..d026bccb5 100644 --- a/src/font/sprite.zig +++ b/src/font/sprite.zig @@ -20,6 +20,10 @@ pub const Sprite = enum(u32) { underline_dashed, underline_curly, + cursor_rect, + cursor_hollow_rect, + cursor_bar, + // Note: we don't currently put the box drawing glyphs in here because // there are a LOT and I'm lazy. What I want to do is spend more time // studying the patterns to see if we can programmatically build our diff --git a/src/font/sprite/Box.zig b/src/font/sprite/Box.zig index db9add6f6..f7635bd15 100644 --- a/src/font/sprite/Box.zig +++ b/src/font/sprite/Box.zig @@ -16,6 +16,7 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const font = @import("../main.zig"); +const Sprite = @import("../sprite.zig").Sprite; const log = std.log.scoped(.box_font); @@ -30,6 +31,7 @@ thickness: u32, /// The thickness of a line. const Thickness = enum { + super_light, light, heavy, @@ -38,6 +40,7 @@ const Thickness = enum { /// to be in pixels. fn height(self: Thickness, base: u32) u32 { return switch (self) { + .super_light => @max(base / 2, 1), .light => base, .heavy => base * 2, }; @@ -303,6 +306,12 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void 0x1fb8a => self.draw_right_three_quarters_block(canvas), 0x1fb8b => self.draw_right_seven_eighths_block(canvas), + // Not official box characters but special characters we hide + // in the high bits of a unicode codepoint. + @enumToInt(Sprite.cursor_rect) => self.draw_cursor_rect(canvas), + @enumToInt(Sprite.cursor_hollow_rect) => self.draw_cursor_hollow_rect(canvas), + @enumToInt(Sprite.cursor_bar) => self.draw_cursor_bar(canvas), + else => return error.InvalidCodepoint, } } @@ -2532,6 +2541,27 @@ fn draw_dash_vertical( self.vline(canvas, y[3], y[3] + h[3], (self.width - thick_px) / 2, thick_px); } +fn draw_cursor_rect(self: Box, canvas: *font.sprite.Canvas) void { + const thick_px = Thickness.super_light.height(self.thickness); + + self.rect(canvas, 0, 0, self.width - thick_px, self.height - thick_px); +} + +fn draw_cursor_hollow_rect(self: Box, canvas: *font.sprite.Canvas) void { + const thick_px = Thickness.super_light.height(self.thickness); + + self.vline(canvas, 0, self.height, 0, thick_px); + self.vline(canvas, 0, self.height, self.width - thick_px, thick_px); + self.hline(canvas, 0, self.width, 0, thick_px); + self.hline(canvas, 0, self.width, self.height - thick_px, thick_px); +} + +fn draw_cursor_bar(self: Box, canvas: *font.sprite.Canvas) void { + const thick_px = Thickness.light.height(self.thickness); + + self.vline(canvas, 0, self.height - thick_px, 0, thick_px); +} + fn vline_middle(self: Box, canvas: *font.sprite.Canvas, thickness: Thickness) void { const thick_px = thickness.height(self.thickness); self.vline(canvas, 0, self.height, (self.width - thick_px) / 2, thick_px); diff --git a/src/font/sprite/Face.zig b/src/font/sprite/Face.zig index 9a81a298b..a7615c110 100644 --- a/src/font/sprite/Face.zig +++ b/src/font/sprite/Face.zig @@ -95,6 +95,11 @@ const Kind = enum { .underline_dashed, .underline_curly, => .underline, + + .cursor_rect, + .cursor_hollow_rect, + .cursor_bar, + => .box, }, // Box fonts diff --git a/src/font/sprite/underline.zig b/src/font/sprite/underline.zig index e13fb7d32..abf52236b 100644 --- a/src/font/sprite/underline.zig +++ b/src/font/sprite/underline.zig @@ -71,6 +71,7 @@ const Draw = struct { .underline_dotted => self.drawDotted(canvas), .underline_dashed => self.drawDashed(canvas), .underline_curly => self.drawCurly(canvas), + else => unreachable, } } diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 1ea85267b..fd8082750 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -114,18 +114,7 @@ const GPUCellMode = enum(u8) { bg = 1, fg = 2, fg_color = 7, - cursor_rect = 3, - cursor_rect_hollow = 4, - cursor_bar = 5, strikethrough = 8, - - pub fn fromCursor(cursor: renderer.CursorStyle) GPUCellMode { - return switch (cursor) { - .box => .cursor_rect, - .box_hollow => .cursor_rect_hollow, - .bar => .cursor_bar, - }; - } }; /// Returns the hints that we want for this @@ -1005,14 +994,33 @@ fn addCursor(self: *Metal, screen: *terminal.Screen) void { .b = 0xFF, }; + const sprite: font.Sprite = switch (self.cursor_style) { + .box => .cursor_rect, + .box_hollow => .cursor_hollow_rect, + .bar => .cursor_bar, + }; + + const glyph = self.font_group.renderGlyph( + self.alloc, + font.sprite_index, + @enumToInt(sprite), + null, + ) catch |err| { + log.warn("error rendering cursor glyph err={}", .{err}); + return; + }; + self.cells.appendAssumeCapacity(.{ - .mode = GPUCellMode.fromCursor(self.cursor_style), + .mode = .fg, .grid_pos = .{ @intToFloat(f32, screen.cursor.x), @intToFloat(f32, screen.cursor.y), }, .cell_width = if (cell.attrs.wide) 2 else 1, .color = .{ color.r, color.g, color.b, 0xFF }, + .glyph_pos = .{ glyph.atlas_x, glyph.atlas_y }, + .glyph_size = .{ glyph.width, glyph.height }, + .glyph_offset = .{ glyph.offset_x, glyph.offset_y }, }); } diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 1cf860d88..ccb6686bb 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -218,22 +218,11 @@ const GPUCellMode = enum(u8) { bg = 1, fg = 2, fg_color = 7, - cursor_rect = 3, - cursor_rect_hollow = 4, - cursor_bar = 5, strikethrough = 8, // Non-exhaustive because masks change it _, - pub fn fromCursor(cursor: renderer.CursorStyle) GPUCellMode { - return switch (cursor) { - .box => .cursor_rect, - .box_hollow => .cursor_rect_hollow, - .bar => .cursor_bar, - }; - } - /// Apply a mask to the mode. pub fn mask(self: GPUCellMode, m: GPUCellMode) GPUCellMode { return @intToEnum( @@ -961,19 +950,41 @@ fn addCursor(self: *OpenGL, screen: *terminal.Screen) void { .b = 0xFF, }; + const sprite: font.Sprite = switch (self.cursor_style) { + .box => .cursor_rect, + .box_hollow => .cursor_hollow_rect, + .bar => .cursor_bar, + }; + + const glyph = self.font_group.renderGlyph( + self.alloc, + font.sprite_index, + @enumToInt(sprite), + null, + ) catch |err| { + log.warn("error rendering cursor glyph err={}", .{err}); + return; + }; + self.cells.appendAssumeCapacity(.{ - .mode = GPUCellMode.fromCursor(self.cursor_style), + .mode = .fg, .grid_col = @intCast(u16, screen.cursor.x), .grid_row = @intCast(u16, screen.cursor.y), .grid_width = if (cell.attrs.wide) 2 else 1, - .fg_r = 0, - .fg_g = 0, - .fg_b = 0, - .fg_a = 0, - .bg_r = color.r, - .bg_g = color.g, - .bg_b = color.b, - .bg_a = 255, + .fg_r = color.r, + .fg_g = color.g, + .fg_b = color.b, + .fg_a = 255, + .bg_r = 0, + .bg_g = 0, + .bg_b = 0, + .bg_a = 0, + .glyph_x = glyph.atlas_x, + .glyph_y = glyph.atlas_y, + .glyph_width = glyph.width, + .glyph_height = glyph.height, + .glyph_offset_x = glyph.offset_x, + .glyph_offset_y = glyph.offset_y, }); } diff --git a/src/renderer/shaders/cell.f.glsl b/src/renderer/shaders/cell.f.glsl index 080748216..336fd073c 100644 --- a/src/renderer/shaders/cell.f.glsl +++ b/src/renderer/shaders/cell.f.glsl @@ -27,9 +27,6 @@ uniform vec2 cell_size; const uint MODE_BG = 1u; const uint MODE_FG = 2u; const uint MODE_FG_COLOR = 7u; -const uint MODE_CURSOR_RECT = 3u; -const uint MODE_CURSOR_RECT_HOLLOW = 4u; -const uint MODE_CURSOR_BAR = 5u; const uint MODE_STRIKETHROUGH = 8u; void main() { @@ -49,50 +46,6 @@ void main() { out_FragColor = texture(text_color, glyph_tex_coords); break; - case MODE_CURSOR_RECT: - out_FragColor = color; - break; - - case MODE_CURSOR_RECT_HOLLOW: - // Okay so yeah this is probably horrendously slow and a shader - // should never do this, but we only ever render a cursor for ONE - // rectangle so we take the slowdown for that one. - - // Default to no color. - out_FragColor = vec4(0., 0., 0., 0.); - - // We subtracted one from cell size because our coordinates start at 0. - // So a width of 50 means max pixel of 49. - vec2 cell_size_coords = cell_size - 1; - - // Apply padding - vec2 padding = vec2(1.,1.); - cell_size_coords = cell_size_coords - (padding * 2); - vec2 screen_cell_pos_padded = screen_cell_pos + padding; - - // Convert our frag coord to offset of this cell. We have to subtract - // 0.5 because the frag coord is in center pixels. - vec2 cell_frag_coord = gl_FragCoord.xy - screen_cell_pos_padded - 0.5; - - // If the frag coords are in the bounds, then we color it. - const float eps = 0.1; - if (cell_frag_coord.x >= 0 && cell_frag_coord.y >= 0 && - cell_frag_coord.x <= cell_size_coords.x && - cell_frag_coord.y <= cell_size_coords.y) { - if (abs(cell_frag_coord.x) < eps || - abs(cell_frag_coord.x - cell_size_coords.x) < eps || - abs(cell_frag_coord.y) < eps || - abs(cell_frag_coord.y - cell_size_coords.y) < eps) { - out_FragColor = color; - } - } - - break; - - case MODE_CURSOR_BAR: - out_FragColor = color; - break; - case MODE_STRIKETHROUGH: out_FragColor = color; break; diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index 8d1119b0c..2e50f4938 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -5,9 +5,6 @@ enum Mode : uint8_t { MODE_BG = 1u, MODE_FG = 2u, MODE_FG_COLOR = 7u, - MODE_CURSOR_RECT = 3u, - MODE_CURSOR_RECT_HOLLOW = 4u, - MODE_CURSOR_BAR = 5u, MODE_STRIKETHROUGH = 8u, }; @@ -124,33 +121,6 @@ vertex VertexOut uber_vertex( break; } - case MODE_CURSOR_RECT: - // Same as background since we're taking up the whole cell. - cell_pos = cell_pos + cell_size_scaled * position; - - out.position = uniforms.projection_matrix * float4(cell_pos, 0.0f, 1.0); - break; - - case MODE_CURSOR_RECT_HOLLOW: - // Top-left position of this cell is needed for the hollow rect. - out.tex_coord = cell_pos; - - // Same as background since we're taking up the whole cell. - cell_pos = cell_pos + cell_size_scaled * position; - out.position = uniforms.projection_matrix * float4(cell_pos, 0.0f, 1.0); - break; - - case MODE_CURSOR_BAR: { - // Make the bar a smaller version of our cell - float2 bar_size = float2(uniforms.cell_size.x * 0.2, uniforms.cell_size.y); - - // Same as background since we're taking up the whole cell. - cell_pos = cell_pos + bar_size * position; - - out.position = uniforms.projection_matrix * float4(cell_pos, 0.0f, 1.0); - break; - } - case MODE_STRIKETHROUGH: { // Strikethrough Y value is just our thickness float2 strikethrough_size = float2(cell_size_scaled.x, uniforms.strikethrough_thickness); @@ -201,47 +171,6 @@ fragment float4 uber_fragment( return textureColor.sample(textureSampler, coord); } - case MODE_CURSOR_RECT: - return in.color; - - case MODE_CURSOR_RECT_HOLLOW: { - // Okay so yeah this is probably horrendously slow and a shader - // should never do this, but we only ever render a cursor for ONE - // rectangle so we take the slowdown for that one. - - // We subtracted one from cell size because our coordinates start at 0. - // So a width of 50 means max pixel of 49. - float2 cell_size_coords = in.cell_size - 1; - - // Apply padding - float2 padding = float2(1.0f, 1.0f); - cell_size_coords = cell_size_coords - (padding * 2); - float2 screen_cell_pos_padded = in.tex_coord + padding; - - // Convert our frag coord to offset of this cell. We have to subtract - // 0.5 because the frag coord is in center pixels. - float2 cell_frag_coord = in.position.xy - screen_cell_pos_padded - 0.5; - - // If the frag coords are in the bounds, then we color it. - const float eps = 0.1; - if (cell_frag_coord.x >= 0 && cell_frag_coord.y >= 0 && - cell_frag_coord.x <= cell_size_coords.x && - cell_frag_coord.y <= cell_size_coords.y) { - if (abs(cell_frag_coord.x) < eps || - abs(cell_frag_coord.x - cell_size_coords.x) < eps || - abs(cell_frag_coord.y) < eps || - abs(cell_frag_coord.y - cell_size_coords.y) < eps) { - return in.color; - } - } - - // Default to no color. - return float4(0.0f); - } - - case MODE_CURSOR_BAR: - return in.color; - case MODE_STRIKETHROUGH: return in.color; } diff --git a/src/renderer/shaders/cell.v.glsl b/src/renderer/shaders/cell.v.glsl index f20b3dfc5..216970b9b 100644 --- a/src/renderer/shaders/cell.v.glsl +++ b/src/renderer/shaders/cell.v.glsl @@ -7,9 +7,6 @@ const uint MODE_BG = 1u; const uint MODE_FG = 2u; const uint MODE_FG_COLOR = 7u; -const uint MODE_CURSOR_RECT = 3u; -const uint MODE_CURSOR_RECT_HOLLOW = 4u; -const uint MODE_CURSOR_BAR = 5u; const uint MODE_STRIKETHROUGH = 8u; // The grid coordinates (x, y) where x < columns and y < rows @@ -167,36 +164,6 @@ void main() { color = fg_color_in / 255.; break; - case MODE_CURSOR_RECT: - // Same as background since we're taking up the whole cell. - cell_pos = cell_pos + cell_size_scaled * position; - - gl_Position = projection * vec4(cell_pos, cell_z, 1.0); - color = bg_color_in / 255.0; - break; - - case MODE_CURSOR_RECT_HOLLOW: - // Top-left position of this cell is needed for the hollow rect. - screen_cell_pos = cell_pos; - - // Same as background since we're taking up the whole cell. - cell_pos = cell_pos + cell_size_scaled * position; - - gl_Position = projection * vec4(cell_pos, cell_z, 1.0); - color = bg_color_in / 255.0; - break; - - case MODE_CURSOR_BAR: - // Make the bar a smaller version of our cell - vec2 bar_size = vec2(cell_size.x * 0.2, cell_size.y); - - // Same as background since we're taking up the whole cell. - cell_pos = cell_pos + bar_size * position; - - gl_Position = projection * vec4(cell_pos, cell_z, 1.0); - color = bg_color_in / 255.0; - break; - case MODE_STRIKETHROUGH: // Strikethrough Y value is just our thickness vec2 strikethrough_size = vec2(cell_size_scaled.x, strikethrough_thickness);