diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index d220cdadc..94216c3aa 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -108,6 +108,9 @@ cursor_color: ?terminal.color.RGB, /// foreground color as the cursor color. cursor_invert: bool, +/// Whether blinking cells are currently visible. Synchronized with cursor blinking. +blink_visible: bool = true, + /// Padding options padding: renderer.Options.Padding, @@ -701,7 +704,7 @@ pub fn updateFrame( self: *OpenGL, surface: *apprt.Surface, state: *renderer.State, - cursor_blink_visible: bool, + blink_visible: bool, ) !void { _ = surface; @@ -770,7 +773,7 @@ pub fn updateFrame( const cursor_style = renderer.cursorStyle( state, self.focused, - cursor_blink_visible, + blink_visible, ); // Get our preedit state @@ -854,6 +857,9 @@ pub fn updateFrame( .color_palette = state.terminal.color_palette.colors, }; }; + + self.blink_visible = blink_visible; + defer { critical.screen.deinit(); if (critical.preedit) |p| p.deinit(self.alloc); @@ -1279,7 +1285,7 @@ pub fn rebuildCells( const screen_cell = row.cells(.all)[screen.cursor.x]; const x = screen.cursor.x - @intFromBool(screen_cell.wide == .spacer_tail); for (self.cells.items[start_i..]) |cell| { - if (cell.grid_col == x and cell.mode.isFg()) { + if (cell.grid_col == x and cell.mode.fg) { cursor_cell = cell; break; } @@ -1416,7 +1422,7 @@ pub fn rebuildCells( _ = try self.addCursor(screen, cursor_style, cursor_color); if (cursor_cell) |*cell| { - if (cell.mode.isFg() and cell.mode != .fg_color) { + if (cell.mode.fg and !cell.mode.fg_color) { const cell_color = if (self.cursor_invert) blk: { const sty = screen.cursor.page_pin.style(screen.cursor.page_cell); break :blk sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color; @@ -1436,8 +1442,8 @@ pub fn rebuildCells( // Some debug mode safety checks if (std.debug.runtime_safety) { - for (self.cells_bg.items) |cell| assert(cell.mode == .bg); - for (self.cells.items) |cell| assert(cell.mode != .bg); + for (self.cells_bg.items) |cell| assert(!cell.mode.fg); + for (self.cells.items) |cell| assert(cell.mode.fg); } } @@ -1469,7 +1475,7 @@ fn addPreeditCell( // Add our opaque background cell try self.cells_bg.append(self.alloc, .{ - .mode = .bg, + .mode = .{ .fg = false }, .grid_col = @intCast(x), .grid_row = @intCast(y), .grid_width = if (cp.wide) 2 else 1, @@ -1491,7 +1497,7 @@ fn addPreeditCell( // Add our text try self.cells.append(self.alloc, .{ - .mode = .fg, + .mode = .{ .fg = true }, .grid_col = @intCast(x), .grid_row = @intCast(y), .grid_width = if (cp.wide) 2 else 1, @@ -1558,7 +1564,7 @@ fn addCursor( }; try self.cells.append(self.alloc, .{ - .mode = .fg, + .mode = .{ .fg = true }, .grid_col = @intCast(x), .grid_row = @intCast(screen.cursor.y), .grid_width = if (wide) 2 else 1, @@ -1702,7 +1708,7 @@ fn updateCell( }; try self.cells_bg.append(self.alloc, .{ - .mode = .bg, + .mode = .{ .fg = false }, .grid_col = @intCast(x), .grid_row = @intCast(y), .grid_width = cell.gridWidth(), @@ -1743,16 +1749,20 @@ fn updateCell( }, ); + var mode: CellProgram.CellMode = .{ .fg = true }; + // If we're rendering a color font, we use the color atlas - const mode: CellProgram.CellMode = switch (try fgMode( + switch (try fgMode( render.presentation, cell_pin, )) { - .normal => .fg, - .color => .fg_color, - .constrained => .fg_constrained, - .powerline => .fg_powerline, - }; + .normal => {}, + .color => mode.fg_color = true, + .constrained => mode.fg_constrained = true, + .powerline => mode.fg_powerline = true, + } + + mode.fg_blink = style.flags.blink; try self.cells.append(self.alloc, .{ .mode = mode, @@ -1799,7 +1809,7 @@ fn updateCell( const color = style.underlineColor(palette) orelse colors.fg; try self.cells.append(self.alloc, .{ - .mode = .fg, + .mode = .{ .fg = true }, .grid_col = @intCast(x), .grid_row = @intCast(y), .grid_width = cell.gridWidth(), @@ -1832,7 +1842,7 @@ fn updateCell( ); try self.cells.append(self.alloc, .{ - .mode = .fg, + .mode = .{ .fg = true }, .grid_col = @intCast(x), .grid_row = @intCast(y), .grid_width = cell.gridWidth(), @@ -2156,6 +2166,10 @@ fn drawCellProgram( "padding_vertical_bottom", self.padding_extend_bottom, ); + try program.program.setUniform( + "blink_visible", + self.blink_visible, + ); } // Draw background images first diff --git a/src/renderer/opengl/CellProgram.zig b/src/renderer/opengl/CellProgram.zig index 48386362e..b68ee4feb 100644 --- a/src/renderer/opengl/CellProgram.zig +++ b/src/renderer/opengl/CellProgram.zig @@ -48,31 +48,14 @@ pub const Cell = extern struct { grid_width: u8, }; -pub const CellMode = enum(u8) { - bg = 1, - fg = 2, - fg_constrained = 3, - fg_color = 7, - fg_powerline = 15, +pub const CellMode = packed struct(u8) { + fg: bool, + fg_constrained: bool = false, + fg_color: bool = false, + fg_powerline: bool = false, + fg_blink: bool = false, - // Non-exhaustive because masks change it - _, - - /// Apply a mask to the mode. - pub fn mask(self: CellMode, m: CellMode) CellMode { - return @enumFromInt(@intFromEnum(self) | @intFromEnum(m)); - } - - pub fn isFg(self: CellMode) bool { - // Since we use bit tricks below, we want to ensure the enum - // doesn't change without us looking at this logic again. - comptime { - const info = @typeInfo(CellMode).Enum; - std.debug.assert(info.fields.len == 5); - } - - return @intFromEnum(self) & @intFromEnum(@as(CellMode, .fg)) != 0; - } + _padding: u3 = 0, }; pub fn init() !CellProgram { diff --git a/src/renderer/shaders/cell.f.glsl b/src/renderer/shaders/cell.f.glsl index f9c1ce2b1..9e0969daf 100644 --- a/src/renderer/shaders/cell.f.glsl +++ b/src/renderer/shaders/cell.f.glsl @@ -22,32 +22,29 @@ uniform sampler2D text_color; // Dimensions of the cell uniform vec2 cell_size; +uniform bool blink_visible; // See vertex shader -const uint MODE_BG = 1u; -const uint MODE_FG = 2u; -const uint MODE_FG_CONSTRAINED = 3u; -const uint MODE_FG_COLOR = 7u; -const uint MODE_FG_POWERLINE = 15u; +const uint MODE_FG = 1u; +const uint MODE_FG_CONSTRAINED = 2u; +const uint MODE_FG_COLOR = 4u; +const uint MODE_FG_POWERLINE = 8u; +const uint MODE_FG_BLINK = 16u; void main() { - float a; - - switch (mode) { - case MODE_BG: - out_FragColor = color; - break; - - case MODE_FG: - case MODE_FG_CONSTRAINED: - case MODE_FG_POWERLINE: - a = texture(text, glyph_tex_coords).r; - vec3 premult = color.rgb * color.a; - out_FragColor = vec4(premult.rgb*a, a); - break; - - case MODE_FG_COLOR: - out_FragColor = texture(text_color, glyph_tex_coords); - break; + if ((mode & MODE_FG) == 0u) { + // Background + out_FragColor = color; } + if ((mode & MODE_FG_BLINK) != 0u && !blink_visible) { + discard; + } + if ((mode & MODE_FG_COLOR) != 0u) { + out_FragColor = texture(text_color, glyph_tex_coords); + return; + } + + float a = texture(text, glyph_tex_coords).r; + vec3 premult = color.rgb * color.a; + out_FragColor = vec4(premult.rgb*a, a); } diff --git a/src/renderer/shaders/cell.v.glsl b/src/renderer/shaders/cell.v.glsl index 942b7ac44..12d2d4f4e 100644 --- a/src/renderer/shaders/cell.v.glsl +++ b/src/renderer/shaders/cell.v.glsl @@ -4,11 +4,11 @@ // used to multiplex multiple render modes into a single shader. // // NOTE: this must be kept in sync with the fragment shader -const uint MODE_BG = 1u; -const uint MODE_FG = 2u; -const uint MODE_FG_CONSTRAINED = 3u; -const uint MODE_FG_COLOR = 7u; -const uint MODE_FG_POWERLINE = 15u; +const uint MODE_FG = 1u; +const uint MODE_FG_CONSTRAINED = 2u; +const uint MODE_FG_COLOR = 4u; +const uint MODE_FG_POWERLINE = 8u; +const uint MODE_FG_BLINK = 16u; // The grid coordinates (x, y) where x < columns and y < rows layout (location = 0) in vec2 grid_coord; @@ -170,8 +170,8 @@ void main() { vec2 cell_size_scaled = cell_size; cell_size_scaled.x = cell_size_scaled.x * grid_width; - switch (mode) { - case MODE_BG: + if ((mode & MODE_FG) == 0u) { + // Draw background // If we're at the edge of the grid, we add our padding to the background // to extend it. Note: grid_padding is top/right/bottom/left. if (grid_coord.y == 0 && padding_vertical_top) { @@ -194,12 +194,7 @@ void main() { gl_Position = projection * vec4(cell_pos, cell_z, 1.0); color = color_in / 255.0; - break; - - case MODE_FG: - case MODE_FG_CONSTRAINED: - case MODE_FG_COLOR: - case MODE_FG_POWERLINE: + } else { vec2 glyph_offset_calc = glyph_offset; // The glyph_offset.y is the y bearing, a y value that when added @@ -211,7 +206,7 @@ void main() { // We also always constrain colored glyphs since we should have // their scaled cell size exactly correct. vec2 glyph_size_calc = glyph_size; - if (mode == MODE_FG_CONSTRAINED || mode == MODE_FG_COLOR) { + if ((mode & (MODE_FG_CONSTRAINED | MODE_FG_COLOR)) != 0u) { if (glyph_size.x > cell_size_scaled.x) { float new_y = glyph_size.y * (cell_size_scaled.x / glyph_size.x); glyph_offset_calc.y = glyph_offset_calc.y + ((glyph_size.y - new_y) / 2); @@ -227,16 +222,10 @@ 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) { - case MODE_FG_CONSTRAINED: - case MODE_FG_POWERLINE: - case MODE_FG: - text_size = textureSize(text, 0); - break; - - case MODE_FG_COLOR: - text_size = textureSize(text_color, 0); - break; + if ((mode & MODE_FG_COLOR) != 0u) { + text_size = textureSize(text_color, 0); + } else { + text_size = textureSize(text, 0); } vec2 glyph_tex_pos = glyph_pos / text_size; vec2 glyph_tex_size = glyph_size / text_size; @@ -250,11 +239,10 @@ void main() { // and Powerline glyphs to be unaffected (else parts of the line would // have different colors as some parts are displayed via background colors). vec4 color_final = color_in / 255.0; - if (min_contrast > 1.0 && mode == MODE_FG) { + if (min_contrast > 1.0 && (mode & ~(MODE_FG | MODE_FG_BLINK)) != 0u) { vec4 bg_color = bg_color_in / 255.0; color_final = contrasted_color(min_contrast, color_final, bg_color); } color = color_final; - break; } }