feat(gui): add gui cursor rendering
This commit is contained in:
parent
b0d32f3581
commit
546cf1f6dc
5 changed files with 156 additions and 21 deletions
|
|
@ -15,6 +15,14 @@ pub const FsParams = extern struct {
|
||||||
col_count: i32,
|
col_count: i32,
|
||||||
row_count: i32,
|
row_count: i32,
|
||||||
viewport_height: i32,
|
viewport_height: i32,
|
||||||
|
// Primary cursor (position + appearance)
|
||||||
|
cursor_col: i32,
|
||||||
|
cursor_row: i32,
|
||||||
|
cursor_shape: i32, // 0=block, 1=beam, 2=underline
|
||||||
|
cursor_vis: i32, // 0=hidden, 1=visible
|
||||||
|
cursor_color: [4]f32, // RGBA normalized [0,1]
|
||||||
|
// Secondary cursor colour (positions encoded in ShaderCell._pad)
|
||||||
|
sec_cursor_color: [4]f32, // RGBA normalized [0,1]
|
||||||
};
|
};
|
||||||
|
|
||||||
const vs_src =
|
const vs_src =
|
||||||
|
|
@ -34,6 +42,12 @@ const fs_src =
|
||||||
\\uniform int col_count;
|
\\uniform int col_count;
|
||||||
\\uniform int row_count;
|
\\uniform int row_count;
|
||||||
\\uniform int viewport_height;
|
\\uniform int viewport_height;
|
||||||
|
\\uniform int cursor_col;
|
||||||
|
\\uniform int cursor_row;
|
||||||
|
\\uniform int cursor_shape;
|
||||||
|
\\uniform int cursor_vis;
|
||||||
|
\\uniform vec4 cursor_color;
|
||||||
|
\\uniform vec4 sec_cursor_color;
|
||||||
\\uniform sampler2D glyph_tex_glyph_smp;
|
\\uniform sampler2D glyph_tex_glyph_smp;
|
||||||
\\uniform usampler2D cell_tex_cell_smp;
|
\\uniform usampler2D cell_tex_cell_smp;
|
||||||
\\out vec4 frag_color;
|
\\out vec4 frag_color;
|
||||||
|
|
@ -59,7 +73,7 @@ const fs_src =
|
||||||
\\ return;
|
\\ return;
|
||||||
\\ }
|
\\ }
|
||||||
\\
|
\\
|
||||||
\\ // Fetch cell: texel = (glyph_index, bg_packed, fg_packed, 0)
|
\\ // Fetch cell: texel = (glyph_index, bg_packed, fg_packed, cursor_flag)
|
||||||
\\ uvec4 cell = texelFetch(cell_tex_cell_smp, ivec2(col, row), 0);
|
\\ uvec4 cell = texelFetch(cell_tex_cell_smp, ivec2(col, row), 0);
|
||||||
\\ vec4 bg = unpack_rgba(cell.g);
|
\\ vec4 bg = unpack_rgba(cell.g);
|
||||||
\\ vec4 fg = unpack_rgba(cell.b);
|
\\ vec4 fg = unpack_rgba(cell.b);
|
||||||
|
|
@ -78,9 +92,31 @@ const fs_src =
|
||||||
\\ gr * cell_size_y + cell_px_y);
|
\\ gr * cell_size_y + cell_px_y);
|
||||||
\\ float glyph_alpha = texelFetch(glyph_tex_glyph_smp, atlas_coord, 0).r;
|
\\ float glyph_alpha = texelFetch(glyph_tex_glyph_smp, atlas_coord, 0).r;
|
||||||
\\
|
\\
|
||||||
\\ // Blend fg over bg
|
\\ // Cursor detection
|
||||||
\\ vec3 color = mix(bg.rgb, fg.rgb, fg.a * glyph_alpha);
|
\\ bool is_primary = (cursor_vis != 0) && (col == cursor_col) && (row == cursor_row);
|
||||||
\\ frag_color = vec4(color, 1.0);
|
\\ bool is_secondary = (cell.a != 0u);
|
||||||
|
\\
|
||||||
|
\\ vec3 final_bg = bg.rgb;
|
||||||
|
\\ vec3 final_fg = fg.rgb;
|
||||||
|
\\
|
||||||
|
\\ if (is_primary || is_secondary) {
|
||||||
|
\\ vec4 cur = is_primary ? cursor_color : sec_cursor_color;
|
||||||
|
\\ int shape = cursor_shape;
|
||||||
|
\\
|
||||||
|
\\ if (shape == 1) {
|
||||||
|
\\ // Beam: 2px vertical bar at left edge of cell
|
||||||
|
\\ if (cell_px_x < 2) { frag_color = vec4(cur.rgb, 1.0); return; }
|
||||||
|
\\ } else if (shape == 2) {
|
||||||
|
\\ // Underline: 2px horizontal bar at bottom of cell
|
||||||
|
\\ if (cell_px_y >= cell_size_y - 2) { frag_color = vec4(cur.rgb, 1.0); return; }
|
||||||
|
\\ } else {
|
||||||
|
\\ // Block: cursor colour as bg, inverted for glyph contrast
|
||||||
|
\\ final_bg = cur.rgb;
|
||||||
|
\\ final_fg = vec3(1.0) - cur.rgb;
|
||||||
|
\\ }
|
||||||
|
\\ }
|
||||||
|
\\
|
||||||
|
\\ frag_color = vec4(mix(final_bg, final_fg, fg.a * glyph_alpha), 1.0);
|
||||||
\\}
|
\\}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -91,7 +127,7 @@ pub fn shaderDesc(backend: sg.Backend) sg.ShaderDesc {
|
||||||
desc.vertex_func.source = vs_src;
|
desc.vertex_func.source = vs_src;
|
||||||
desc.fragment_func.source = fs_src;
|
desc.fragment_func.source = fs_src;
|
||||||
|
|
||||||
// Fragment uniform block: 4 individual INT uniforms
|
// Fragment uniform block: individual uniforms (GLCORE uses glUniform* calls)
|
||||||
desc.uniform_blocks[0].stage = .FRAGMENT;
|
desc.uniform_blocks[0].stage = .FRAGMENT;
|
||||||
desc.uniform_blocks[0].size = @sizeOf(FsParams);
|
desc.uniform_blocks[0].size = @sizeOf(FsParams);
|
||||||
desc.uniform_blocks[0].layout = .NATIVE;
|
desc.uniform_blocks[0].layout = .NATIVE;
|
||||||
|
|
@ -100,6 +136,12 @@ pub fn shaderDesc(backend: sg.Backend) sg.ShaderDesc {
|
||||||
desc.uniform_blocks[0].glsl_uniforms[2] = .{ .type = .INT, .glsl_name = "col_count" };
|
desc.uniform_blocks[0].glsl_uniforms[2] = .{ .type = .INT, .glsl_name = "col_count" };
|
||||||
desc.uniform_blocks[0].glsl_uniforms[3] = .{ .type = .INT, .glsl_name = "row_count" };
|
desc.uniform_blocks[0].glsl_uniforms[3] = .{ .type = .INT, .glsl_name = "row_count" };
|
||||||
desc.uniform_blocks[0].glsl_uniforms[4] = .{ .type = .INT, .glsl_name = "viewport_height" };
|
desc.uniform_blocks[0].glsl_uniforms[4] = .{ .type = .INT, .glsl_name = "viewport_height" };
|
||||||
|
desc.uniform_blocks[0].glsl_uniforms[5] = .{ .type = .INT, .glsl_name = "cursor_col" };
|
||||||
|
desc.uniform_blocks[0].glsl_uniforms[6] = .{ .type = .INT, .glsl_name = "cursor_row" };
|
||||||
|
desc.uniform_blocks[0].glsl_uniforms[7] = .{ .type = .INT, .glsl_name = "cursor_shape" };
|
||||||
|
desc.uniform_blocks[0].glsl_uniforms[8] = .{ .type = .INT, .glsl_name = "cursor_vis" };
|
||||||
|
desc.uniform_blocks[0].glsl_uniforms[9] = .{ .type = .FLOAT4, .glsl_name = "cursor_color" };
|
||||||
|
desc.uniform_blocks[0].glsl_uniforms[10] = .{ .type = .FLOAT4, .glsl_name = "sec_cursor_color" };
|
||||||
|
|
||||||
// Glyph atlas texture: R8 → sample_type = FLOAT
|
// Glyph atlas texture: R8 → sample_type = FLOAT
|
||||||
desc.views[0].texture = .{
|
desc.views[0].texture = .{
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,16 @@ pub const Cell = gui_cell.Cell;
|
||||||
pub const Color = gui_cell.Rgba8;
|
pub const Color = gui_cell.Rgba8;
|
||||||
const Rgba8 = gui_cell.Rgba8;
|
const Rgba8 = gui_cell.Rgba8;
|
||||||
|
|
||||||
|
pub const CursorShape = enum(i32) { block = 0, beam = 1, underline = 2 };
|
||||||
|
|
||||||
|
pub const CursorInfo = struct {
|
||||||
|
vis: bool = false,
|
||||||
|
row: u16 = 0,
|
||||||
|
col: u16 = 0,
|
||||||
|
shape: CursorShape = .block,
|
||||||
|
color: Color = Color.initRgb(255, 255, 255),
|
||||||
|
};
|
||||||
|
|
||||||
const log = std.log.scoped(.gpu);
|
const log = std.log.scoped(.gpu);
|
||||||
|
|
||||||
// Maximum glyph atlas dimension. 4096 is universally supported and gives
|
// Maximum glyph atlas dimension. 4096 is universally supported and gives
|
||||||
|
|
@ -350,6 +360,8 @@ pub fn paint(
|
||||||
col_count: u16,
|
col_count: u16,
|
||||||
top: u16,
|
top: u16,
|
||||||
cells: []const Cell,
|
cells: []const Cell,
|
||||||
|
cursor: CursorInfo,
|
||||||
|
secondary_cursors: []const CursorInfo,
|
||||||
) void {
|
) void {
|
||||||
const shader_col_count: u16 = @intCast(@divTrunc(client_size.x, font.cell_size.x));
|
const shader_col_count: u16 = @intCast(@divTrunc(client_size.x, font.cell_size.x));
|
||||||
const shader_row_count: u16 = @intCast(@divTrunc(client_size.y, font.cell_size.y));
|
const shader_row_count: u16 = @intCast(@divTrunc(client_size.y, font.cell_size.y));
|
||||||
|
|
@ -388,6 +400,13 @@ pub fn paint(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark secondary cursor cells in the _pad field (read by fragment shader).
|
||||||
|
for (secondary_cursors) |sc| {
|
||||||
|
if (!sc.vis) continue;
|
||||||
|
if (sc.row >= shader_row_count or sc.col >= shader_col_count) continue;
|
||||||
|
shader_cells[@as(usize, sc.row) * shader_col_count + sc.col]._pad = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Upload glyph atlas to GPU if any new glyphs were rasterized this frame.
|
// Upload glyph atlas to GPU if any new glyphs were rasterized this frame.
|
||||||
if (state.glyph_atlas_dirty) flushGlyphAtlas(state);
|
if (state.glyph_atlas_dirty) flushGlyphAtlas(state);
|
||||||
|
|
||||||
|
|
@ -429,12 +448,23 @@ pub fn paint(
|
||||||
bindings.samplers[1] = global.cell_sampler;
|
bindings.samplers[1] = global.cell_sampler;
|
||||||
sg.applyBindings(bindings);
|
sg.applyBindings(bindings);
|
||||||
|
|
||||||
|
const sec_color: Color = if (secondary_cursors.len > 0)
|
||||||
|
secondary_cursors[0].color
|
||||||
|
else
|
||||||
|
Color.initRgb(255, 255, 255);
|
||||||
|
|
||||||
const fs_params = builtin_shader.FsParams{
|
const fs_params = builtin_shader.FsParams{
|
||||||
.cell_size_x = font.cell_size.x,
|
.cell_size_x = font.cell_size.x,
|
||||||
.cell_size_y = font.cell_size.y,
|
.cell_size_y = font.cell_size.y,
|
||||||
.col_count = shader_col_count,
|
.col_count = shader_col_count,
|
||||||
.row_count = shader_row_count,
|
.row_count = shader_row_count,
|
||||||
.viewport_height = @intCast(client_size.y),
|
.viewport_height = @intCast(client_size.y),
|
||||||
|
.cursor_col = cursor.col,
|
||||||
|
.cursor_row = cursor.row,
|
||||||
|
.cursor_shape = @intFromEnum(cursor.shape),
|
||||||
|
.cursor_vis = if (cursor.vis) 1 else 0,
|
||||||
|
.cursor_color = colorToVec4(cursor.color),
|
||||||
|
.sec_cursor_color = colorToVec4(sec_color),
|
||||||
};
|
};
|
||||||
sg.applyUniforms(0, .{
|
sg.applyUniforms(0, .{
|
||||||
.ptr = &fs_params,
|
.ptr = &fs_params,
|
||||||
|
|
@ -446,6 +476,15 @@ pub fn paint(
|
||||||
// Note: caller (app.zig) calls sg.commit() and window.swapBuffers()
|
// Note: caller (app.zig) calls sg.commit() and window.swapBuffers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn colorToVec4(c: Color) [4]f32 {
|
||||||
|
return .{
|
||||||
|
@as(f32, @floatFromInt(c.r)) / 255.0,
|
||||||
|
@as(f32, @floatFromInt(c.g)) / 255.0,
|
||||||
|
@as(f32, @floatFromInt(c.b)) / 255.0,
|
||||||
|
@as(f32, @floatFromInt(c.a)) / 255.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn oom(e: error{OutOfMemory}) noreturn {
|
fn oom(e: error{OutOfMemory}) noreturn {
|
||||||
@panic(@errorName(e));
|
@panic(@errorName(e));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@ const gui_config = @import("gui_config");
|
||||||
|
|
||||||
const log = std.log.scoped(.wio_app);
|
const log = std.log.scoped(.wio_app);
|
||||||
|
|
||||||
|
// Re-export cursor types so renderer.zig (which imports 'app' but not 'gpu')
|
||||||
|
// can use them without a direct dependency on the gpu module.
|
||||||
|
pub const CursorInfo = gpu.CursorInfo;
|
||||||
|
pub const CursorShape = gpu.CursorShape;
|
||||||
|
pub const GpuColor = gpu.Color;
|
||||||
|
|
||||||
// ── Shared state (protected by screen_mutex) ──────────────────────────────
|
// ── Shared state (protected by screen_mutex) ──────────────────────────────
|
||||||
|
|
||||||
const ScreenSnapshot = struct {
|
const ScreenSnapshot = struct {
|
||||||
|
|
@ -31,6 +37,9 @@ const ScreenSnapshot = struct {
|
||||||
widths: []u8,
|
widths: []u8,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
|
// Cursor state (set by renderer thread, consumed by wio thread)
|
||||||
|
cursor: gpu.CursorInfo,
|
||||||
|
secondary_cursors: []gpu.CursorInfo, // heap-allocated, freed with snapshot
|
||||||
};
|
};
|
||||||
|
|
||||||
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
|
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
|
||||||
|
|
@ -92,7 +101,11 @@ pub fn stop() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called from the tui thread to push a new screen to the GPU thread.
|
/// Called from the tui thread to push a new screen to the GPU thread.
|
||||||
pub fn updateScreen(vx_screen: *const vaxis.Screen) void {
|
pub fn updateScreen(
|
||||||
|
vx_screen: *const vaxis.Screen,
|
||||||
|
cursor: gpu.CursorInfo,
|
||||||
|
secondary_cursors: []const gpu.CursorInfo,
|
||||||
|
) void {
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
const cell_count: usize = @as(usize, vx_screen.width) * @as(usize, vx_screen.height);
|
const cell_count: usize = @as(usize, vx_screen.width) * @as(usize, vx_screen.height);
|
||||||
|
|
||||||
|
|
@ -106,6 +119,13 @@ pub fn updateScreen(vx_screen: *const vaxis.Screen) void {
|
||||||
allocator.free(new_codepoints);
|
allocator.free(new_codepoints);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
const new_sec = allocator.alloc(gpu.CursorInfo, secondary_cursors.len) catch {
|
||||||
|
allocator.free(new_cells);
|
||||||
|
allocator.free(new_codepoints);
|
||||||
|
allocator.free(new_widths);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
@memcpy(new_sec, secondary_cursors);
|
||||||
|
|
||||||
// Convert vaxis cells → gpu.Cell (colours only; glyph indices filled on GPU thread).
|
// Convert vaxis cells → gpu.Cell (colours only; glyph indices filled on GPU thread).
|
||||||
for (vx_screen.buf[0..cell_count], new_cells, new_codepoints, new_widths) |*vc, *gc, *cp, *wt| {
|
for (vx_screen.buf[0..cell_count], new_cells, new_codepoints, new_widths) |*vc, *gc, *cp, *wt| {
|
||||||
|
|
@ -131,6 +151,7 @@ pub fn updateScreen(vx_screen: *const vaxis.Screen) void {
|
||||||
allocator.free(old.cells);
|
allocator.free(old.cells);
|
||||||
allocator.free(old.codepoints);
|
allocator.free(old.codepoints);
|
||||||
allocator.free(old.widths);
|
allocator.free(old.widths);
|
||||||
|
allocator.free(old.secondary_cursors);
|
||||||
}
|
}
|
||||||
screen_snap = .{
|
screen_snap = .{
|
||||||
.cells = new_cells,
|
.cells = new_cells,
|
||||||
|
|
@ -138,6 +159,8 @@ pub fn updateScreen(vx_screen: *const vaxis.Screen) void {
|
||||||
.widths = new_widths,
|
.widths = new_widths,
|
||||||
.width = vx_screen.width,
|
.width = vx_screen.width,
|
||||||
.height = vx_screen.height,
|
.height = vx_screen.height,
|
||||||
|
.cursor = cursor,
|
||||||
|
.secondary_cursors = new_sec,
|
||||||
};
|
};
|
||||||
|
|
||||||
screen_pending.store(true, .release);
|
screen_pending.store(true, .release);
|
||||||
|
|
@ -561,6 +584,7 @@ fn wioLoop() void {
|
||||||
allocator.free(s.cells);
|
allocator.free(s.cells);
|
||||||
allocator.free(s.codepoints);
|
allocator.free(s.codepoints);
|
||||||
allocator.free(s.widths);
|
allocator.free(s.widths);
|
||||||
|
allocator.free(s.secondary_cursors);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.size = .{ .x = win_size.width, .y = win_size.height };
|
state.size = .{ .x = win_size.width, .y = win_size.height };
|
||||||
|
|
@ -595,6 +619,8 @@ fn wioLoop() void {
|
||||||
s.width,
|
s.width,
|
||||||
0,
|
0,
|
||||||
cells_with_glyphs,
|
cells_with_glyphs,
|
||||||
|
s.cursor,
|
||||||
|
s.secondary_cursors,
|
||||||
);
|
);
|
||||||
sg.commit();
|
sg.commit();
|
||||||
window.swapBuffers();
|
window.swapBuffers();
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,11 @@ dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||||
thread: ?std.Thread = null,
|
thread: ?std.Thread = null,
|
||||||
window_ready: bool = false,
|
window_ready: bool = false,
|
||||||
|
|
||||||
|
cursor_info: app.CursorInfo = .{},
|
||||||
|
cursor_color: app.GpuColor = app.GpuColor.initRgb(255, 255, 255),
|
||||||
|
secondary_cursors: std.ArrayListUnmanaged(app.CursorInfo) = .{},
|
||||||
|
secondary_color: app.GpuColor = app.GpuColor.initRgb(255, 255, 255),
|
||||||
|
|
||||||
const global = struct {
|
const global = struct {
|
||||||
var init_called: bool = false;
|
var init_called: bool = false;
|
||||||
};
|
};
|
||||||
|
|
@ -92,6 +97,7 @@ pub fn init(
|
||||||
.dispatch_initialized = dispatch_initialized,
|
.dispatch_initialized = dispatch_initialized,
|
||||||
};
|
};
|
||||||
result.vx.caps.unicode = .unicode;
|
result.vx.caps.unicode = .unicode;
|
||||||
|
result.vx.caps.multi_cursor = true;
|
||||||
result.vx.screen.width_method = .unicode;
|
result.vx.screen.width_method = .unicode;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +107,7 @@ pub fn deinit(self: *Self) void {
|
||||||
var drop: std.Io.Writer.Discarding = .init(&.{});
|
var drop: std.Io.Writer.Discarding = .init(&.{});
|
||||||
self.vx.deinit(self.allocator, &drop.writer);
|
self.vx.deinit(self.allocator, &drop.writer);
|
||||||
self.event_buffer.deinit();
|
self.event_buffer.deinit();
|
||||||
|
self.secondary_cursors.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: *Self) Error!void {
|
pub fn run(self: *Self) Error!void {
|
||||||
|
|
@ -123,7 +130,7 @@ fn fmtmsg(self: *Self, value: anytype) std.Io.Writer.Error![]const u8 {
|
||||||
|
|
||||||
pub fn render(self: *Self) error{}!void {
|
pub fn render(self: *Self) error{}!void {
|
||||||
if (!self.window_ready) return;
|
if (!self.window_ready) return;
|
||||||
app.updateScreen(&self.vx.screen);
|
app.updateScreen(&self.vx.screen, self.cursor_info, self.secondary_cursors.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigwinch(self: *Self) !void {
|
pub fn sigwinch(self: *Self) !void {
|
||||||
|
|
@ -414,13 +421,11 @@ pub fn get_fontfaces(self: *Self) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminal_cursor_color(self: *Self, color: Color) void {
|
pub fn set_terminal_cursor_color(self: *Self, color: Color) void {
|
||||||
_ = self;
|
self.cursor_color = themeColorToGpu(color);
|
||||||
_ = color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminal_secondary_cursor_color(self: *Self, color: Color) void {
|
pub fn set_terminal_secondary_cursor_color(self: *Self, color: Color) void {
|
||||||
_ = self;
|
self.secondary_color = themeColorToGpu(color);
|
||||||
_ = color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminal_working_directory(self: *Self, absolute_path: []const u8) void {
|
pub fn set_terminal_working_directory(self: *Self, absolute_path: []const u8) void {
|
||||||
|
|
@ -462,24 +467,48 @@ 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;
|
self.cursor_info = .{
|
||||||
_ = y;
|
.vis = true,
|
||||||
_ = x;
|
.row = if (y < 0) 0 else @intCast(y),
|
||||||
_ = shape;
|
.col = if (x < 0) 0 else @intCast(x),
|
||||||
|
.shape = vaxisCursorShape(shape),
|
||||||
|
.color = self.cursor_color,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor_disable(self: *Self) void {
|
pub fn cursor_disable(self: *Self) void {
|
||||||
_ = self;
|
self.cursor_info.vis = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_all_multi_cursors(self: *Self) !void {
|
pub fn clear_all_multi_cursors(self: *Self) !void {
|
||||||
_ = self;
|
self.secondary_cursors.clearRetainingCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_multi_cursor_yx(self: *Self, y: i32, x: i32) !void {
|
pub fn show_multi_cursor_yx(self: *Self, y: i32, x: i32) !void {
|
||||||
_ = self;
|
try self.secondary_cursors.append(self.allocator, .{
|
||||||
_ = y;
|
.vis = true,
|
||||||
_ = x;
|
.row = if (y < 0) 0 else @intCast(y),
|
||||||
|
.col = if (x < 0) 0 else @intCast(x),
|
||||||
|
.shape = self.cursor_info.shape,
|
||||||
|
.color = self.secondary_color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn themeColorToGpu(color: Color) app.GpuColor {
|
||||||
|
return .{
|
||||||
|
.r = @truncate(color.color >> 16),
|
||||||
|
.g = @truncate(color.color >> 8),
|
||||||
|
.b = @truncate(color.color),
|
||||||
|
.a = color.alpha,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vaxisCursorShape(shape: CursorShape) app.CursorShape {
|
||||||
|
return switch (shape) {
|
||||||
|
.default, .block, .block_blink => .block,
|
||||||
|
.beam, .beam_blink => .beam,
|
||||||
|
.underline, .underline_blink => .underline,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_to_system_clipboard(self: *Self, text: []const u8) void {
|
pub fn copy_to_system_clipboard(self: *Self, text: []const u8) void {
|
||||||
|
|
|
||||||
|
|
@ -2190,7 +2190,6 @@ pub fn is_cursor_beam() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_native_cursor() bool {
|
pub fn has_native_cursor() bool {
|
||||||
if (build_options.gui) return false;
|
|
||||||
return current().config_.enable_terminal_cursor;
|
return current().config_.enable_terminal_cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue