feat(gui): add gui cursor blink rendering
This commit is contained in:
parent
546cf1f6dc
commit
1c1886defc
3 changed files with 69 additions and 9 deletions
|
|
@ -61,6 +61,15 @@ cursor_color: app.GpuColor = app.GpuColor.initRgb(255, 255, 255),
|
||||||
secondary_cursors: std.ArrayListUnmanaged(app.CursorInfo) = .{},
|
secondary_cursors: std.ArrayListUnmanaged(app.CursorInfo) = .{},
|
||||||
secondary_color: app.GpuColor = app.GpuColor.initRgb(255, 255, 255),
|
secondary_color: app.GpuColor = app.GpuColor.initRgb(255, 255, 255),
|
||||||
|
|
||||||
|
cursor_blink: bool = false,
|
||||||
|
blink_on: bool = true,
|
||||||
|
blink_epoch: i64 = 0,
|
||||||
|
blink_period_us: i64 = 500_000,
|
||||||
|
blink_idle_us: i64 = 15_000_000,
|
||||||
|
blink_last_change: i64 = 0,
|
||||||
|
prev_cursor: app.CursorInfo = .{},
|
||||||
|
prev_cursor_blink: bool = false,
|
||||||
|
|
||||||
const global = struct {
|
const global = struct {
|
||||||
var init_called: bool = false;
|
var init_called: bool = false;
|
||||||
};
|
};
|
||||||
|
|
@ -128,9 +137,47 @@ fn fmtmsg(self: *Self, value: anytype) std.Io.Writer.Error![]const u8 {
|
||||||
return self.event_buffer.written();
|
return self.event_buffer.written();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Self) error{}!void {
|
pub fn render(self: *Self) error{}!bool {
|
||||||
if (!self.window_ready) return;
|
if (!self.window_ready) return false;
|
||||||
app.updateScreen(&self.vx.screen, self.cursor_info, self.secondary_cursors.items);
|
|
||||||
|
var cursor = self.cursor_info;
|
||||||
|
|
||||||
|
// Detect changes since the last rendered frame. Reset blink epoch and idle
|
||||||
|
// timer on any meaningful change so the cursor snaps to visible immediately.
|
||||||
|
if (cursor.vis != self.prev_cursor.vis or
|
||||||
|
cursor.row != self.prev_cursor.row or
|
||||||
|
cursor.col != self.prev_cursor.col or
|
||||||
|
cursor.shape != self.prev_cursor.shape or
|
||||||
|
self.cursor_blink != self.prev_cursor_blink)
|
||||||
|
{
|
||||||
|
const now = std.time.microTimestamp();
|
||||||
|
if (cursor.vis) {
|
||||||
|
self.blink_epoch = now;
|
||||||
|
self.blink_on = true;
|
||||||
|
}
|
||||||
|
self.blink_last_change = now;
|
||||||
|
}
|
||||||
|
self.prev_cursor = cursor;
|
||||||
|
self.prev_cursor_blink = self.cursor_blink;
|
||||||
|
|
||||||
|
// Apply blink unless the cursor has been idle for too long.
|
||||||
|
if (cursor.vis and self.cursor_blink) {
|
||||||
|
const now = std.time.microTimestamp();
|
||||||
|
const idle = now - self.blink_last_change;
|
||||||
|
if (idle < self.blink_idle_us) {
|
||||||
|
const elapsed = @mod(now - self.blink_epoch, self.blink_period_us * 2);
|
||||||
|
self.blink_on = elapsed < self.blink_period_us;
|
||||||
|
cursor.vis = self.blink_on;
|
||||||
|
} else {
|
||||||
|
cursor.vis = true; // freeze visible after idle timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.updateScreen(&self.vx.screen, cursor, self.secondary_cursors.items);
|
||||||
|
|
||||||
|
if (!self.cursor_info.vis or !self.cursor_blink) return false;
|
||||||
|
const idle = std.time.microTimestamp() - self.blink_last_change;
|
||||||
|
return idle < self.blink_idle_us;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigwinch(self: *Self) !void {
|
pub fn sigwinch(self: *Self) !void {
|
||||||
|
|
@ -467,6 +514,7 @@ pub fn request_mouse_cursor_default(self: *Self, push_or_pop: bool) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor_enable(self: *Self, y: i32, x: i32, shape: CursorShape) !void {
|
pub fn cursor_enable(self: *Self, y: i32, x: i32, shape: CursorShape) !void {
|
||||||
|
self.cursor_blink = isBlink(shape);
|
||||||
self.cursor_info = .{
|
self.cursor_info = .{
|
||||||
.vis = true,
|
.vis = true,
|
||||||
.row = if (y < 0) 0 else @intCast(y),
|
.row = if (y < 0) 0 else @intCast(y),
|
||||||
|
|
@ -503,6 +551,13 @@ fn themeColorToGpu(color: Color) app.GpuColor {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn isBlink(shape: CursorShape) bool {
|
||||||
|
return switch (shape) {
|
||||||
|
.default, .block_blink, .beam_blink, .underline_blink => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn vaxisCursorShape(shape: CursorShape) app.CursorShape {
|
fn vaxisCursorShape(shape: CursorShape) app.CursorShape {
|
||||||
return switch (shape) {
|
return switch (shape) {
|
||||||
.default, .block, .block_blink => .block,
|
.default, .block, .block_blink => .block,
|
||||||
|
|
|
||||||
|
|
@ -219,10 +219,11 @@ pub fn run(self: *Self) Error!void {
|
||||||
try self.loop.start();
|
try self.loop.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Self) !void {
|
pub fn render(self: *Self) !bool {
|
||||||
if (in_panic.load(.acquire)) return;
|
if (in_panic.load(.acquire)) return false;
|
||||||
try self.vx.render(self.tty.writer());
|
try self.vx.render(self.tty.writer());
|
||||||
try self.tty.writer().flush();
|
try self.tty.writer().flush();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigwinch(self: *Self) !void {
|
pub fn sigwinch(self: *Self) !void {
|
||||||
|
|
|
||||||
|
|
@ -681,13 +681,17 @@ fn render(self: *Self) void {
|
||||||
top_layer_.draw(self.rdr_.stdplane());
|
top_layer_.draw(self.rdr_.stdplane());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
const renderer_more = ret: {
|
||||||
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
|
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
|
||||||
defer frame.deinit();
|
defer frame.deinit();
|
||||||
self.rdr_.render() catch |e| self.logger.err("render", e);
|
const m = self.rdr_.render() catch |e| blk: {
|
||||||
|
self.logger.err("render", e);
|
||||||
|
break :blk false;
|
||||||
|
};
|
||||||
tracy.frameMark();
|
tracy.frameMark();
|
||||||
self.unrendered_input_events_count = 0;
|
self.unrendered_input_events_count = 0;
|
||||||
}
|
break :ret m;
|
||||||
|
};
|
||||||
self.top_layer_reset();
|
self.top_layer_reset();
|
||||||
|
|
||||||
self.idle_frame_count = if (self.unrendered_input_events_count > 0)
|
self.idle_frame_count = if (self.unrendered_input_events_count > 0)
|
||||||
|
|
@ -695,7 +699,7 @@ fn render(self: *Self) void {
|
||||||
else
|
else
|
||||||
self.idle_frame_count + 1;
|
self.idle_frame_count + 1;
|
||||||
|
|
||||||
if (more or self.idle_frame_count < idle_frames or self.no_sleep) {
|
if (more or renderer_more or self.idle_frame_count < idle_frames or self.no_sleep) {
|
||||||
if (!self.frame_clock_running) {
|
if (!self.frame_clock_running) {
|
||||||
self.frame_clock.start() catch {};
|
self.frame_clock.start() catch {};
|
||||||
self.frame_clock_running = true;
|
self.frame_clock_running = true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue