diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 5d83cf701..eda31218e 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -8,6 +8,7 @@ const assert = std.debug.assert; const testing = std.testing; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; +const link = @import("link.zig"); const shadertoy = @import("shadertoy.zig"); const apprt = @import("../apprt.zig"); const configpkg = @import("../config.zig"); @@ -225,6 +226,7 @@ pub const DerivedConfig = struct { invert_selection_fg_bg: bool, custom_shaders: std.ArrayListUnmanaged([]const u8), custom_shader_animation: bool, + links: link.Set, pub fn init( alloc_gpa: Allocator, @@ -246,6 +248,12 @@ pub const DerivedConfig = struct { font_styles.set(.italic, config.@"font-style-italic" != .false); font_styles.set(.bold_italic, config.@"font-style-bold-italic" != .false); + // Our link configs + const links = try link.Set.fromConfig( + alloc, + config.link.links.items, + ); + return .{ .background_opacity = @max(0, @min(1, config.@"background-opacity")), .font_thicken = config.@"font-thicken", @@ -280,12 +288,15 @@ pub const DerivedConfig = struct { .custom_shaders = custom_shaders, .custom_shader_animation = config.@"custom-shader-animation", + .links = links, .arena = arena, }; } pub fn deinit(self: *DerivedConfig) void { + const alloc = self.arena.allocator(); + self.links.deinit(alloc); self.arena.deinit(); } }; @@ -598,6 +609,7 @@ pub fn updateFrame( gl_bg: terminal.color.RGB, selection: ?terminal.Selection, screen: terminal.Screen, + mouse: renderer.State.Mouse, preedit: ?renderer.State.Preedit, cursor_style: ?renderer.CursorStyle, }; @@ -665,6 +677,7 @@ pub fn updateFrame( .gl_bg = self.background_color, .selection = selection, .screen = screen_copy, + .mouse = state.mouse, .preedit = if (cursor_style != null) state.preedit else null, .cursor_style = cursor_style, }; @@ -683,6 +696,7 @@ pub fn updateFrame( try self.rebuildCells( critical.selection, &critical.screen, + critical.mouse, critical.preedit, critical.cursor_style, ); @@ -855,6 +869,7 @@ pub fn rebuildCells( self: *OpenGL, term_selection: ?terminal.Selection, screen: *terminal.Screen, + mouse: renderer.State.Mouse, preedit: ?renderer.State.Preedit, cursor_style_: ?renderer.CursorStyle, ) !void { @@ -877,9 +892,21 @@ pub fn rebuildCells( (screen.rows * screen.cols * 2) + 1, ); + // Create an arena for all our temporary allocations while rebuilding + var arena = ArenaAllocator.init(self.alloc); + defer arena.deinit(); + const arena_alloc = arena.allocator(); + // We've written no data to the GPU, refresh it all self.gl_cells_written = 0; + // Create our match set for the links. + var link_match_set = try self.config.links.matchSet( + arena_alloc, + screen, + mouse.point orelse .{}, + ); + // Determine our x/y range for preedit. We don't want to render anything // here because we will render the preedit separately. const preedit_range: ?struct { @@ -975,10 +1002,27 @@ pub fn rebuildCells( } } + // It this cell is within our hint range then we need to + // underline it. + const cell: terminal.Screen.Cell = cell: { + var cell = row.getCell(shaper_cell.x); + + // If our links contain this cell then we want to + // underline it. + if (link_match_set.orderedContains(.{ + .x = shaper_cell.x, + .y = y, + })) { + cell.attrs.underline = .single; + } + + break :cell cell; + }; + if (self.updateCell( term_selection, screen, - row.getCell(shaper_cell.x), + cell, shaper_cell, run, shaper_cell.x,