renderer: receive message with viewport match selections

Doesn't draw yet
pull/9687/head
Mitchell Hashimoto 2025-11-16 07:05:32 -08:00
parent 061d157b50
commit 6c8ffb5fc1
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
4 changed files with 71 additions and 5 deletions

View File

@ -1328,9 +1328,44 @@ fn reportColorScheme(self: *Surface, force: bool) void {
}
fn searchCallback(event: terminal.search.Thread.Event, ud: ?*anyopaque) void {
// IMPORTANT: This function is run on the SEARCH THREAD! It is NOT SAFE
// to access anything other than values that never change on the surface.
// The surface is guaranteed to be valid for the lifetime of the search
// thread.
const self: *Surface = @ptrCast(@alignCast(ud.?));
_ = self;
_ = event;
self.searchCallback_(event) catch |err| {
log.warn("error in search callback err={}", .{err});
};
}
fn searchCallback_(
self: *Surface,
event: terminal.search.Thread.Event,
) !void {
switch (event) {
.viewport_matches => |matches_unowned| {
var arena: ArenaAllocator = .init(self.alloc);
errdefer arena.deinit();
const alloc = arena.allocator();
const matches = try alloc.dupe(terminal.highlight.Flattened, matches_unowned);
for (matches) |*m| m.* = try m.clone(alloc);
_ = self.renderer_thread.mailbox.push(
.{ .search_viewport_matches = .{
.arena = arena,
.matches = matches,
} },
.forever,
);
try self.renderer_thread.wakeup.notify();
},
// Unhandled, so far.
.total_matches,
.complete,
=> {},
}
}
/// Call this when modifiers change. This is safe to call even if modifiers

View File

@ -451,6 +451,14 @@ fn drainMailbox(self: *Thread) !void {
self.startDrawTimer();
},
.search_viewport_matches => |v| {
// Note we don't free the new value because we expect our
// allocators to match.
if (self.renderer.search_matches) |*m| m.arena.deinit();
self.renderer.search_matches = v;
self.renderer.search_matches_dirty = true;
},
.inspector => |v| self.flags.has_inspector = v,
.macos_display_id => |v| {

View File

@ -122,6 +122,16 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
scrollbar: terminal.Scrollbar,
scrollbar_dirty: bool,
/// The most recent viewport matches so that we can render search
/// matches in the visible frame. This is provided asynchronously
/// from the search thread so we have the dirty flag to also note
/// if we need to rebuild our cells to include search highlights.
///
/// Note that the selections MAY BE INVALID (point to PageList nodes
/// that do not exist anymore). These must be validated prior to use.
search_matches: ?renderer.Message.SearchMatches,
search_matches_dirty: bool,
/// The current set of cells to render. This is rebuilt on every frame
/// but we keep this around so that we don't reallocate. Each set of
/// cells goes into a separate shader.
@ -672,6 +682,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.focused = true,
.scrollbar = .zero,
.scrollbar_dirty = false,
.search_matches = null,
.search_matches_dirty = false,
// Render state
.cells = .{},
@ -744,7 +756,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
pub fn deinit(self: *Self) void {
self.terminal_state.deinit(self.alloc);
if (self.search_matches) |*m| m.arena.deinit();
self.swap_chain.deinit();
if (DisplayLink != void) {

View File

@ -1,6 +1,7 @@
const std = @import("std");
const assert = @import("../quirks.zig").inlineAssert;
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const configpkg = @import("../config.zig");
const font = @import("../font/main.zig");
const renderer = @import("../renderer.zig");
@ -10,7 +11,7 @@ const terminal = @import("../terminal/main.zig");
pub const Message = union(enum) {
/// Purposely crash the renderer. This is used for testing and debugging.
/// See the "crash" binding action.
crash: void,
crash,
/// A change in state in the window focus that this renderer is
/// rendering within. This is only sent when a change is detected so
@ -24,7 +25,7 @@ pub const Message = union(enum) {
/// Reset the cursor blink by immediately showing the cursor then
/// restarting the timer.
reset_cursor_blink: void,
reset_cursor_blink,
/// Change the font grid. This can happen for any number of reasons
/// including a font size change, family change, etc.
@ -52,12 +53,22 @@ pub const Message = union(enum) {
impl: *renderer.Renderer.DerivedConfig,
},
/// Matches for the current viewport from the search thread. These happen
/// async so they may be off for a frame or two from the actually rendered
/// viewport. The renderer must handle this gracefully.
search_viewport_matches: SearchMatches,
/// Activate or deactivate the inspector.
inspector: bool,
/// The macOS display ID has changed for the window.
macos_display_id: u32,
pub const SearchMatches = struct {
arena: ArenaAllocator,
matches: []const terminal.highlight.Flattened,
};
/// Initialize a change_config message.
pub fn initChangeConfig(alloc: Allocator, config: *const configpkg.Config) !Message {
const thread_ptr = try alloc.create(renderer.Thread.DerivedConfig);