pull/11875/merge
brianc442 2026-06-03 09:56:50 +02:00 committed by GitHub
commit a317760231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 50 additions and 19 deletions

View File

@ -107,6 +107,11 @@ flags: packed struct {
/// This is true when the view is focused. This defaults to true
/// and it is up to the apprt to set the correct value.
focused: bool = true,
/// This is true when blinking text should be visible and false when
/// it should not be visible. This is toggled on a timer by the thread
/// automatically.
text_blink_visible: bool = false,
} = .{},
pub const DerivedConfig = struct {
@ -417,6 +422,7 @@ fn drainMailbox(self: *Thread) !void {
// and then restart the timer.
if (self.cursor_c.state() != .active) {
self.flags.cursor_blink_visible = true;
self.flags.text_blink_visible = true;
self.cursor_h.run(
&self.loop,
&self.cursor_c,
@ -620,6 +626,7 @@ fn renderCallback(
t.renderer.updateFrame(
t.state,
t.flags.cursor_blink_visible,
t.flags.text_blink_visible,
) catch |err|
log.warn("error rendering err={}", .{err});
@ -652,6 +659,7 @@ fn cursorTimerCallback(
};
t.flags.cursor_blink_visible = !t.flags.cursor_blink_visible;
t.flags.text_blink_visible = !t.flags.text_blink_visible;
t.wakeup.notify() catch {};
t.cursor_h.run(

View File

@ -115,6 +115,10 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
/// True if the window is focused
focused: bool,
/// True if blinking text should be displayed and false when it should
/// not be visible. Toggled on the same timer as cursor_blink_visible.
text_blink_visible: bool,
/// Flag to indicate that our focus state changed for custom
/// shaders to update their state.
custom_shader_focused_changed: bool = false,
@ -705,6 +709,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.grid_metrics = font_critical.metrics,
.size = options.size,
.focused = true,
.text_blink_visible = true,
.scrollbar = .zero,
.scrollbar_dirty = false,
.last_bottom_node = null,
@ -1124,6 +1129,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self: *Self,
state: *renderer.State,
cursor_blink_visible: bool,
text_blink_visible: bool,
) Allocator.Error!void {
// const start = std.time.Instant.now() catch unreachable;
// const start_micro = std.time.microTimestamp();
@ -1360,6 +1366,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self.draw_mutex.lock();
defer self.draw_mutex.unlock();
if (text_blink_visible != self.text_blink_visible) {
self.text_blink_visible = text_blink_visible;
self.markDirty();
}
// Build our GPU cells
self.rebuildCells(
critical.preedit,
@ -1369,6 +1380,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.blink_visible = cursor_blink_visible,
}),
&critical.links,
self.text_blink_visible,
) catch |err| {
// This means we weren't able to allocate our buffer
// to update the cells. In this case, we continue with
@ -2309,6 +2321,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
preedit: ?renderer.State.Preedit,
cursor_style_: ?renderer.CursorStyle,
links: *const terminal.RenderState.CellSet,
text_blink_visible: bool,
) Allocator.Error!void {
const state: *terminal.RenderState = &self.terminal_state;
@ -2432,6 +2445,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
selection,
highlights,
links,
text_blink_visible,
) catch |err| {
// This should never happen except under exceptional
// scenarios. In this case, we don't want to corrupt
@ -2616,6 +2630,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
selection: ?[2]terminal.size.CellCountInt,
highlights: *const std.ArrayList(terminal.RenderState.Highlight),
links: *const terminal.RenderState.CellSet,
text_blink_visible: bool,
) !void {
const state = &self.terminal_state;
@ -2926,7 +2941,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
if (style.flags.invisible) {
continue;
}
// If blinking and in the hidden phase, skip all foreground
// rendering (matches xterm behavior)
const blink_hidden = style.flags.blink and !text_blink_visible;
// Give links a single underline, unless they already have
// an underline, in which case use a double underline to
// distinguish them.
@ -2946,26 +2963,28 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// We draw underlines first so that they layer underneath text.
// This improves readability when a colored underline is used
// which intersects parts of the text (descenders).
if (underline != .none) self.addUnderline(
@intCast(x),
@intCast(y),
underline,
style.underlineColor(&state.colors.palette) orelse fg,
alpha,
) catch |err| {
log.warn(
"error adding underline to cell, will be invalid x={} y={}, err={}",
.{ x, y, err },
);
};
if (style.flags.overline) self.addOverline(@intCast(x), @intCast(y), fg, alpha) catch |err| {
if (!blink_hidden) {
if (underline != .none) self.addUnderline(
@intCast(x),
@intCast(y),
underline,
style.underlineColor(&state.colors.palette) orelse fg,
alpha,
) catch |err| {
log.warn(
"error adding underline to cell, will be invalid x={} y={}, err={}",
.{ x, y, err },
);
};
}
if (!blink_hidden) {
if (style.flags.overline) self.addOverline(@intCast(x), @intCast(y), fg, alpha) catch |err| {
log.warn(
"error adding overline to cell, will be invalid x={} y={}, err={}",
.{ x, y, err },
);
};
};
}
// If we're at or past the end of our shaper run then
// we need to get the next run from the run iterator.
if (shaper_cells != null and shaper_cells_i >= shaper_cells.?.len) {
@ -3022,7 +3041,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
run.offset + shaped_cells[shaper_cells_i].x == x) : ({
shaper_cells_i += 1;
}) {
self.addGlyph(
if (!blink_hidden) {
self.addGlyph(
@intCast(x),
@intCast(y),
state.cols,
@ -3037,11 +3057,13 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.{ x, y, err },
);
};
}
}
}
// Finally, draw a strikethrough if necessary.
if (style.flags.strikethrough) self.addStrikethrough(
if (!blink_hidden) {
if (style.flags.strikethrough) self.addStrikethrough(
@intCast(x),
@intCast(y),
fg,
@ -3052,6 +3074,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.{ x, y, err },
);
};
}
}
}