feat(gui): clipboard, title, cursor, attention
This commit is contained in:
parent
273be78055
commit
b1b50b7ff0
2 changed files with 118 additions and 5 deletions
|
|
@ -42,6 +42,26 @@ var font_name_len: usize = 0;
|
|||
var font_dirty: std.atomic.Value(bool) = .init(true);
|
||||
var stop_requested: std.atomic.Value(bool) = .init(false);
|
||||
|
||||
// Window title (written from TUI thread, applied by wio thread)
|
||||
var title_mutex: std.Thread.Mutex = .{};
|
||||
var title_buf: [512]u8 = undefined;
|
||||
var title_len: usize = 0;
|
||||
var title_dirty: std.atomic.Value(bool) = .init(false);
|
||||
|
||||
// Clipboard write (heap-allocated, transferred to wio thread)
|
||||
var clipboard_mutex: std.Thread.Mutex = .{};
|
||||
var clipboard_write: ?[]u8 = null;
|
||||
|
||||
// Clipboard read request
|
||||
var clipboard_read_pending: std.atomic.Value(bool) = .init(false);
|
||||
|
||||
// Mouse cursor (stored as wio.Cursor tag value)
|
||||
var pending_cursor: std.atomic.Value(u8) = .init(@intFromEnum(wio.Cursor.arrow));
|
||||
var cursor_dirty: std.atomic.Value(bool) = .init(false);
|
||||
|
||||
// Window attention request
|
||||
var attention_pending: std.atomic.Value(bool) = .init(false);
|
||||
|
||||
// Current font — written and read only from the wio thread (after gpu.init).
|
||||
var wio_font: gpu.Font = .{ .cell_size = .{ .x = 8, .y = 16 } };
|
||||
|
||||
|
|
@ -136,6 +156,53 @@ pub fn setFontFace(name: []const u8) void {
|
|||
requestRender();
|
||||
}
|
||||
|
||||
pub fn setWindowTitle(title: []const u8) void {
|
||||
title_mutex.lock();
|
||||
defer title_mutex.unlock();
|
||||
const copy_len = @min(title.len, title_buf.len);
|
||||
@memcpy(title_buf[0..copy_len], title[0..copy_len]);
|
||||
title_len = copy_len;
|
||||
title_dirty.store(true, .release);
|
||||
wio.cancelWait();
|
||||
}
|
||||
|
||||
pub fn setClipboard(text: []const u8) void {
|
||||
const allocator = gpa.allocator();
|
||||
const copy = allocator.dupe(u8, text) catch return;
|
||||
clipboard_mutex.lock();
|
||||
defer clipboard_mutex.unlock();
|
||||
if (clipboard_write) |old| allocator.free(old);
|
||||
clipboard_write = copy;
|
||||
wio.cancelWait();
|
||||
}
|
||||
|
||||
pub fn requestClipboard() void {
|
||||
clipboard_read_pending.store(true, .release);
|
||||
wio.cancelWait();
|
||||
}
|
||||
|
||||
pub fn setMouseCursor(shape: vaxis.Mouse.Shape) void {
|
||||
const cursor: wio.Cursor = switch (shape) {
|
||||
.default => .arrow,
|
||||
.text => .text,
|
||||
.pointer => .hand,
|
||||
.help => .arrow,
|
||||
.progress => .arrow_busy,
|
||||
.wait => .busy,
|
||||
.@"ew-resize" => .size_ew,
|
||||
.@"ns-resize" => .size_ns,
|
||||
.cell => .crosshair,
|
||||
};
|
||||
pending_cursor.store(@intFromEnum(cursor), .release);
|
||||
cursor_dirty.store(true, .release);
|
||||
wio.cancelWait();
|
||||
}
|
||||
|
||||
pub fn requestAttention() void {
|
||||
attention_pending.store(true, .release);
|
||||
wio.cancelWait();
|
||||
}
|
||||
|
||||
// ── Internal helpers (wio thread only) ────────────────────────────────────
|
||||
|
||||
// Reload wio_font from current settings. Called only from the wio thread.
|
||||
|
|
@ -324,10 +391,42 @@ fn wioLoop() void {
|
|||
const row_cell: i32 = @intCast(@divTrunc(@as(i32, @intCast(mouse_pos.y)), wio_font.cell_size.y));
|
||||
tui_pid.send(.{ "RDR", "B", @as(u8, 1), btn_id, col_cell, row_cell, @as(i32, 0), @as(i32, 0) }) catch {};
|
||||
},
|
||||
.focused => window.enableTextInput(.{}),
|
||||
.unfocused => window.disableTextInput(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Apply pending cross-thread requests from the TUI thread.
|
||||
if (title_dirty.swap(false, .acq_rel)) {
|
||||
title_mutex.lock();
|
||||
const t = title_buf[0..title_len];
|
||||
title_mutex.unlock();
|
||||
window.setTitle(t);
|
||||
}
|
||||
{
|
||||
clipboard_mutex.lock();
|
||||
const pending = clipboard_write;
|
||||
clipboard_write = null;
|
||||
clipboard_mutex.unlock();
|
||||
if (pending) |text| {
|
||||
defer allocator.free(text);
|
||||
window.setClipboardText(text);
|
||||
}
|
||||
}
|
||||
if (clipboard_read_pending.swap(false, .acq_rel)) {
|
||||
if (window.getClipboardText(allocator)) |text| {
|
||||
defer allocator.free(text);
|
||||
tui_pid.send(.{ "RDR", "system_clipboard", text }) catch {};
|
||||
}
|
||||
}
|
||||
if (cursor_dirty.swap(false, .acq_rel)) {
|
||||
window.setCursor(@enumFromInt(pending_cursor.load(.acquire)));
|
||||
}
|
||||
if (attention_pending.swap(false, .acq_rel)) {
|
||||
window.requestAttention();
|
||||
}
|
||||
|
||||
// Paint if the tui pushed new screen data.
|
||||
// Take ownership of the snap (set screen_snap = null under the mutex)
|
||||
// so the TUI thread cannot free the backing memory while we use it.
|
||||
|
|
|
|||
|
|
@ -325,6 +325,18 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void {
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
var text: []const u8 = undefined;
|
||||
if (try cbor.match(msg, .{
|
||||
cbor.any,
|
||||
"system_clipboard",
|
||||
cbor.extract(&text),
|
||||
})) {
|
||||
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", text }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return error.UnexpectedRendererEvent;
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +347,7 @@ pub fn set_sgr_pixel_mode_support(self: *Self, enable: bool) void {
|
|||
|
||||
pub fn set_terminal_title(self: *Self, text: []const u8) void {
|
||||
_ = self;
|
||||
_ = text;
|
||||
app.setWindowTitle(text);
|
||||
}
|
||||
|
||||
pub fn set_terminal_style(self: *Self, style_: Style) void {
|
||||
|
|
@ -398,23 +410,26 @@ pub fn request_windows_clipboard(self: *Self) void {
|
|||
|
||||
pub fn request_mouse_cursor(self: *Self, shape: MouseCursorShape, push_or_pop: bool) void {
|
||||
_ = self;
|
||||
_ = shape;
|
||||
_ = push_or_pop;
|
||||
app.setMouseCursor(shape);
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_text(self: *Self, push_or_pop: bool) void {
|
||||
_ = self;
|
||||
_ = push_or_pop;
|
||||
app.setMouseCursor(.text);
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_pointer(self: *Self, push_or_pop: bool) void {
|
||||
_ = self;
|
||||
_ = push_or_pop;
|
||||
app.setMouseCursor(.pointer);
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_default(self: *Self, push_or_pop: bool) void {
|
||||
_ = self;
|
||||
_ = push_or_pop;
|
||||
app.setMouseCursor(.default);
|
||||
}
|
||||
|
||||
pub fn cursor_enable(self: *Self, y: i32, x: i32, shape: CursorShape) !void {
|
||||
|
|
@ -440,11 +455,10 @@ pub fn show_multi_cursor_yx(self: *Self, y: i32, x: i32) !void {
|
|||
|
||||
pub fn copy_to_system_clipboard(self: *Self, text: []const u8) void {
|
||||
_ = self;
|
||||
_ = text;
|
||||
// TODO: implement
|
||||
app.setClipboard(text);
|
||||
}
|
||||
|
||||
pub fn request_system_clipboard(self: *Self) void {
|
||||
_ = self;
|
||||
// TODO: implement
|
||||
app.requestClipboard();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue