feat: add libvaxis renderer
This commit is contained in:
parent
b15fa47f30
commit
1cd3cb17ce
32 changed files with 1559 additions and 516 deletions
|
@ -77,15 +77,19 @@ pub const Plane = struct {
|
|||
return self.plane.dim_x();
|
||||
}
|
||||
|
||||
pub fn abs_yx_to_rel(self: Plane, y: ?*c_int, x: ?*c_int) void {
|
||||
self.plane.abs_yx_to_rel(y, x);
|
||||
pub fn abs_yx_to_rel(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
|
||||
var y_, var x_ = .{ y, x };
|
||||
self.plane.abs_yx_to_rel(&y_, &x_);
|
||||
return .{ y_, x_ };
|
||||
}
|
||||
|
||||
pub fn rel_yx_to_abs(self: Plane, y: ?*c_int, x: ?*c_int) void {
|
||||
self.plane.rel_yx_to_abs(y, x);
|
||||
pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
|
||||
var y_, var x_ = .{ y, x };
|
||||
self.plane.rel_yx_to_abs(&y_, &x_);
|
||||
return .{ y_, x_ };
|
||||
}
|
||||
|
||||
pub fn move_bottom(self: Plane) void {
|
||||
pub fn hide(self: Plane) void {
|
||||
self.plane.move_bottom();
|
||||
}
|
||||
|
||||
|
@ -244,4 +248,30 @@ pub const Plane = struct {
|
|||
.strikethrough => plane.plane.set_styles(nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn egc_length(_: Plane, egcs: []const u8, colcount: *c_int, abs_col: usize) usize {
|
||||
if (egcs[0] == '\t') {
|
||||
colcount.* = @intCast(8 - abs_col % 8);
|
||||
return 1;
|
||||
}
|
||||
return nc.ncegc_len(egcs, colcount) catch ret: {
|
||||
colcount.* = 1;
|
||||
break :ret 1;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn egc_chunk_width(plane: Plane, chunk_: []const u8, abs_col_: usize) usize {
|
||||
var abs_col = abs_col_;
|
||||
var chunk = chunk_;
|
||||
var colcount: usize = 0;
|
||||
var cols: c_int = 0;
|
||||
while (chunk.len > 0) {
|
||||
const bytes = plane.egc_length(chunk, &cols, abs_col);
|
||||
colcount += @intCast(cols);
|
||||
abs_col += @intCast(cols);
|
||||
if (chunk.len < bytes) break;
|
||||
chunk = chunk[bytes..];
|
||||
}
|
||||
return colcount;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
const nc = @import("notcurses");
|
||||
|
||||
pub fn length(egcs: []const u8, colcount: *c_int, abs_col: usize) usize {
|
||||
if (egcs[0] == '\t') {
|
||||
colcount.* = @intCast(8 - abs_col % 8);
|
||||
return 1;
|
||||
}
|
||||
return nc.ncegc_len(egcs, colcount) catch ret: {
|
||||
colcount.* = 1;
|
||||
break :ret 1;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn chunk_width(chunk_: []const u8, abs_col_: usize) usize {
|
||||
var abs_col = abs_col_;
|
||||
var chunk = chunk_;
|
||||
var colcount: usize = 0;
|
||||
var cols: c_int = 0;
|
||||
while (chunk.len > 0) {
|
||||
const bytes = length(chunk, &cols, abs_col);
|
||||
colcount += @intCast(cols);
|
||||
abs_col += @intCast(cols);
|
||||
if (chunk.len < bytes) break;
|
||||
chunk = chunk[bytes..];
|
||||
}
|
||||
return colcount;
|
||||
}
|
||||
|
||||
pub const ucs32_to_utf8 = nc.ucs32_to_utf8;
|
|
@ -1,6 +1,7 @@
|
|||
const nc = @import("notcurses");
|
||||
|
||||
pub const key = nc.key;
|
||||
pub const key_type = u32;
|
||||
pub const modifier = nc.mod;
|
||||
pub const event_type = nc.event_type;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ const log = @import("log");
|
|||
const nc = @import("notcurses");
|
||||
const Style = @import("theme").Style;
|
||||
|
||||
pub const egc = @import("egc.zig");
|
||||
pub const input = @import("input.zig");
|
||||
|
||||
pub const Plane = @import("Plane.zig").Plane;
|
||||
|
@ -18,7 +17,7 @@ const key = input.key;
|
|||
const event_type = input.event_type;
|
||||
|
||||
const Self = @This();
|
||||
const log_name = "ncrender";
|
||||
pub const log_name = "notcurses";
|
||||
|
||||
a: std.mem.Allocator,
|
||||
ctx: nc.Context,
|
||||
|
@ -79,6 +78,8 @@ pub fn deinit(self: *Self) void {
|
|||
self.bracketed_paste_buffer.deinit();
|
||||
}
|
||||
|
||||
pub fn run(_: *Self) !void {}
|
||||
|
||||
pub fn render(self: Self) !void {
|
||||
return self.ctx.render();
|
||||
}
|
||||
|
@ -103,11 +104,25 @@ pub fn leave_alternate_screen(self: Self) void {
|
|||
return self.ctx.leave_alternate_screen();
|
||||
}
|
||||
|
||||
pub fn process_input(self: *Self) !void {
|
||||
const InputError = error{
|
||||
OutOfMemory,
|
||||
InvalidCharacter,
|
||||
NoSpaceLeft,
|
||||
CborIntegerTooLarge,
|
||||
CborIntegerTooSmall,
|
||||
CborInvalidType,
|
||||
CborTooShort,
|
||||
Ucs32toUtf8Error,
|
||||
InvalidPadding,
|
||||
ReadInputError,
|
||||
WouldBlock,
|
||||
};
|
||||
|
||||
pub fn process_input(self: *Self) InputError!void {
|
||||
var input_buffer: [256]nc.Input = undefined;
|
||||
|
||||
while (true) {
|
||||
const nivec = try self.ctx.getvec_nblock(&input_buffer);
|
||||
const nivec = self.ctx.getvec_nblock(&input_buffer) catch return error.ReadInputError;
|
||||
if (nivec.len == 0)
|
||||
break;
|
||||
for (nivec) |*ni| {
|
||||
|
@ -191,7 +206,7 @@ fn handle_bracketed_paste_input(self: *Self, cbor_msg: []const u8) !bool {
|
|||
key.ENTER => try self.bracketed_paste_buffer.appendSlice("\n"),
|
||||
else => if (!key.synthesized_p(keypress)) {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = try egc.ucs32_to_utf8(&[_]u32{egc_}, &buf);
|
||||
const bytes = try ucs32_to_utf8(&[_]u32{egc_}, &buf);
|
||||
try self.bracketed_paste_buffer.appendSlice(buf[0..bytes]);
|
||||
} else {
|
||||
try self.handle_bracketed_paste_end();
|
||||
|
@ -506,3 +521,5 @@ pub fn cursor_enable(self: Self, y: c_int, x: c_int) !void {
|
|||
pub fn cursor_disable(self: Self) void {
|
||||
self.ctx.cursor_disable() catch {};
|
||||
}
|
||||
|
||||
pub const ucs32_to_utf8 = nc.ucs32_to_utf8;
|
||||
|
|
51
src/renderer/vaxis/Cell.zig
Normal file
51
src/renderer/vaxis/Cell.zig
Normal file
|
@ -0,0 +1,51 @@
|
|||
const vaxis = @import("vaxis");
|
||||
const Style = @import("theme").Style;
|
||||
|
||||
const Cell = @This();
|
||||
|
||||
cell: vaxis.Cell = .{},
|
||||
|
||||
pub inline fn set_style(self: *Cell, style_: Style) void {
|
||||
if (style_.fg) |fg| self.cell.style.fg = vaxis.Cell.Color.rgbFromUint(fg);
|
||||
if (style_.bg) |bg| self.cell.style.bg = vaxis.Cell.Color.rgbFromUint(bg);
|
||||
if (style_.fs) |fs| {
|
||||
self.cell.style.ul = .default;
|
||||
self.cell.style.ul_style = .off;
|
||||
self.cell.style.bold = false;
|
||||
self.cell.style.dim = false;
|
||||
self.cell.style.italic = false;
|
||||
self.cell.style.blink = false;
|
||||
self.cell.style.reverse = false;
|
||||
self.cell.style.invisible = false;
|
||||
self.cell.style.strikethrough = false;
|
||||
|
||||
switch (fs) {
|
||||
.normal => {},
|
||||
.bold => self.cell.style.bold = true,
|
||||
.italic => self.cell.style.italic = false,
|
||||
.underline => self.cell.style.ul_style = .single,
|
||||
.undercurl => self.cell.style.ul_style = .curly,
|
||||
.strikethrough => self.cell.style.strikethrough = true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn set_style_fg(self: *Cell, style_: Style) void {
|
||||
if (style_.fg) |fg| self.cell.style.fg = vaxis.Cell.Color.rgbFromUint(fg);
|
||||
}
|
||||
|
||||
pub inline fn set_style_bg(self: *Cell, style_: Style) void {
|
||||
if (style_.bg) |bg| self.cell.style.bg = vaxis.Cell.Color.rgbFromUint(bg);
|
||||
}
|
||||
|
||||
pub inline fn set_fg_rgb(self: *Cell, arg_rgb: c_uint) !void {
|
||||
self.cell.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(arg_rgb));
|
||||
}
|
||||
pub inline fn set_bg_rgb(self: *Cell, arg_rgb: c_uint) !void {
|
||||
self.cell.style.bg = vaxis.Cell.Color.rgbFromUint(@intCast(arg_rgb));
|
||||
}
|
||||
|
||||
pub fn columns(self: *const Cell) usize {
|
||||
// return if (self.cell.char.width == 0) self.window.gwidth(self.cell.char.grapheme) else self.cell.char.width; // FIXME?
|
||||
return self.cell.char.width;
|
||||
}
|
346
src/renderer/vaxis/Plane.zig
Normal file
346
src/renderer/vaxis/Plane.zig
Normal file
|
@ -0,0 +1,346 @@
|
|||
const std = @import("std");
|
||||
const Style = @import("theme").Style;
|
||||
const StyleBits = @import("style.zig").StyleBits;
|
||||
const Cell = @import("Cell.zig");
|
||||
const vaxis = @import("vaxis");
|
||||
|
||||
const Plane = @This();
|
||||
|
||||
window: vaxis.Window,
|
||||
row: i32 = 0,
|
||||
col: i32 = 0,
|
||||
name_buf: [128]u8,
|
||||
name_len: usize,
|
||||
cache: GraphemeCache = .{},
|
||||
style: vaxis.Cell.Style = .{},
|
||||
|
||||
pub const Options = struct {
|
||||
y: usize = 0,
|
||||
x: usize = 0,
|
||||
rows: usize = 0,
|
||||
cols: usize = 0,
|
||||
name: [*:0]const u8,
|
||||
flags: option = .none,
|
||||
};
|
||||
|
||||
pub const option = enum {
|
||||
none,
|
||||
VSCROLL,
|
||||
};
|
||||
|
||||
pub fn init(nopts: *const Options, parent_: Plane) !Plane {
|
||||
const opts = .{
|
||||
.x_off = nopts.x,
|
||||
.y_off = nopts.y,
|
||||
.width = .{ .limit = nopts.cols },
|
||||
.height = .{ .limit = nopts.rows },
|
||||
.border = .{},
|
||||
};
|
||||
var plane: Plane = .{
|
||||
.window = parent_.window.child(opts),
|
||||
.name_buf = undefined,
|
||||
.name_len = std.mem.span(nopts.name).len,
|
||||
};
|
||||
@memcpy(plane.name_buf[0..plane.name_len], nopts.name);
|
||||
return plane;
|
||||
}
|
||||
|
||||
pub fn deinit(_: *Plane) void {}
|
||||
|
||||
pub fn name(self: Plane, buf: []u8) []const u8 {
|
||||
@memcpy(buf[0..self.name_len], self.name_buf[0..self.name_len]);
|
||||
return buf[0..self.name_len];
|
||||
}
|
||||
|
||||
pub fn above(_: Plane) ?Plane {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn below(_: Plane) ?Plane {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn erase(self: Plane) void {
|
||||
self.window.clear();
|
||||
}
|
||||
|
||||
pub inline fn abs_y(self: Plane) c_int {
|
||||
return @intCast(self.window.y_off);
|
||||
}
|
||||
|
||||
pub inline fn abs_x(self: Plane) c_int {
|
||||
return @intCast(self.window.x_off);
|
||||
}
|
||||
|
||||
pub inline fn dim_y(self: Plane) c_uint {
|
||||
return @intCast(self.window.height);
|
||||
}
|
||||
|
||||
pub inline fn dim_x(self: Plane) c_uint {
|
||||
return @intCast(self.window.width);
|
||||
}
|
||||
|
||||
pub fn abs_yx_to_rel(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
|
||||
return .{ y - self.abs_y(), x - self.abs_x() };
|
||||
}
|
||||
|
||||
pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
|
||||
return .{ self.abs_y() + y, self.abs_x() + x };
|
||||
}
|
||||
|
||||
pub fn hide(_: Plane) void {}
|
||||
|
||||
pub fn move_yx(self: *Plane, y: c_int, x: c_int) !void {
|
||||
self.window.y_off = @intCast(y);
|
||||
self.window.x_off = @intCast(x);
|
||||
}
|
||||
|
||||
pub fn resize_simple(self: *Plane, ylen: c_uint, xlen: c_uint) !void {
|
||||
self.window.height = @intCast(ylen);
|
||||
self.window.width = @intCast(xlen);
|
||||
}
|
||||
|
||||
pub fn home(self: *Plane) void {
|
||||
self.row = 0;
|
||||
self.col = 0;
|
||||
}
|
||||
|
||||
pub fn print(self: *Plane, comptime fmt: anytype, args: anytype) !usize {
|
||||
var buf: [fmt.len + 4096]u8 = undefined;
|
||||
const text = try std.fmt.bufPrint(&buf, fmt, args);
|
||||
return self.putstr(text);
|
||||
}
|
||||
|
||||
pub fn print_aligned_right(self: *Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize {
|
||||
var buf: [fmt.len + 4096]u8 = undefined;
|
||||
const width = self.window.width;
|
||||
const text = try std.fmt.bufPrint(&buf, fmt, args);
|
||||
const text_width = self.egc_chunk_width(text, 0);
|
||||
self.row = @intCast(y);
|
||||
self.col = @intCast(if (text_width >= width) 0 else width - text_width);
|
||||
return self.putstr(text);
|
||||
}
|
||||
|
||||
pub fn print_aligned_center(self: *Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize {
|
||||
var buf: [fmt.len + 4096]u8 = undefined;
|
||||
const width = self.window.width;
|
||||
const text = try std.fmt.bufPrint(&buf, fmt, args);
|
||||
const text_width = self.egc_chunk_width(text, 0);
|
||||
self.row = @intCast(y);
|
||||
self.col = @intCast(if (text_width >= width) 0 else (width - text_width) / 2);
|
||||
return self.putstr(text);
|
||||
}
|
||||
|
||||
pub fn putstr(self: *Plane, text: []const u8) !usize {
|
||||
var result: usize = 0;
|
||||
const width = self.window.width;
|
||||
var iter = self.window.screen.unicode.graphemeIterator(text);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (self.col >= width) {
|
||||
self.row += 1;
|
||||
self.col = 0;
|
||||
}
|
||||
const s = grapheme.bytes(text);
|
||||
if (std.mem.eql(u8, s, "\n")) {
|
||||
self.row += 1;
|
||||
self.col = 0;
|
||||
result += 1;
|
||||
continue;
|
||||
}
|
||||
const w = self.window.gwidth(s);
|
||||
if (w == 0) continue;
|
||||
self.window.writeCell(@intCast(self.col), @intCast(self.row), .{
|
||||
.char = .{
|
||||
.grapheme = self.cache.put(s),
|
||||
.width = w,
|
||||
},
|
||||
.style = self.style,
|
||||
});
|
||||
self.col += @intCast(w);
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn putc(self: *Plane, cell: *const Cell) !usize {
|
||||
return self.putc_yx(@intCast(self.row), @intCast(self.col), cell);
|
||||
}
|
||||
|
||||
pub fn putc_yx(self: *Plane, y: c_int, x: c_int, cell: *const Cell) !usize {
|
||||
try self.cursor_move_yx(y, x);
|
||||
const w = if (cell.cell.char.width == 0) self.window.gwidth(cell.cell.char.grapheme) else cell.cell.char.width;
|
||||
if (w == 0) return w;
|
||||
self.window.writeCell(@intCast(self.col), @intCast(self.row), cell.cell);
|
||||
self.col += @intCast(w);
|
||||
if (self.col >= self.window.width) {
|
||||
self.row += 1;
|
||||
self.col = 0;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
pub fn cursor_yx(self: Plane, y: *c_uint, x: *c_uint) void {
|
||||
y.* = @intCast(self.row);
|
||||
x.* = @intCast(self.col);
|
||||
}
|
||||
|
||||
pub fn cursor_y(self: Plane) c_uint {
|
||||
return @intCast(self.row);
|
||||
}
|
||||
|
||||
pub fn cursor_x(self: Plane) c_uint {
|
||||
return @intCast(self.col);
|
||||
}
|
||||
|
||||
pub fn cursor_move_yx(self: *Plane, y: c_int, x: c_int) !void {
|
||||
if (self.window.height == 0 or self.window.width == 0) return;
|
||||
if (self.window.height <= y or self.window.width <= x) return;
|
||||
if (y >= 0)
|
||||
self.row = @intCast(y);
|
||||
if (x >= 0)
|
||||
self.col = @intCast(x);
|
||||
}
|
||||
|
||||
pub fn cursor_move_rel(self: *Plane, y: c_int, x: c_int) !void {
|
||||
if (self.window.height == 0 or self.window.width == 0) return error.OutOfBounds;
|
||||
const new_y: isize = @as(c_int, @intCast(self.row)) + y;
|
||||
const new_x: isize = @as(c_int, @intCast(self.col)) + x;
|
||||
if (new_y < 0 or new_x < 0) return error.OutOfBounds;
|
||||
if (self.window.height <= new_y or self.window.width <= new_x) return error.OutOfBounds;
|
||||
self.row = @intCast(new_y);
|
||||
self.col = @intCast(new_x);
|
||||
}
|
||||
|
||||
pub fn cell_init(self: Plane) Cell {
|
||||
return .{ .cell = .{ .style = self.style } };
|
||||
}
|
||||
|
||||
pub fn cell_load(self: *Plane, cell: *Cell, gcluster: [:0]const u8) !usize {
|
||||
cell.* = .{ .cell = .{ .style = self.style } };
|
||||
var cols: c_int = 0;
|
||||
const bytes = self.egc_length(gcluster, &cols, 0);
|
||||
cell.cell.char.grapheme = self.cache.put(gcluster[0..bytes]);
|
||||
cell.cell.char.width = @intCast(cols);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
pub fn at_cursor_cell(self: Plane, cell: *Cell) !usize {
|
||||
cell.* = .{};
|
||||
if (self.window.readCell(@intCast(self.col), @intCast(self.row))) |cell_| cell.cell = cell_;
|
||||
return cell.cell.char.grapheme.len;
|
||||
}
|
||||
|
||||
pub fn set_styles(self: Plane, stylebits: StyleBits) void {
|
||||
_ = self;
|
||||
_ = stylebits;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub fn on_styles(self: Plane, stylebits: StyleBits) void {
|
||||
_ = self;
|
||||
_ = stylebits;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub fn off_styles(self: Plane, stylebits: StyleBits) void {
|
||||
_ = self;
|
||||
_ = stylebits;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub fn set_fg_rgb(self: *Plane, channel: u32) !void {
|
||||
self.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(channel));
|
||||
}
|
||||
|
||||
pub fn set_bg_rgb(self: *Plane, channel: u32) !void {
|
||||
self.style.bg = vaxis.Cell.Color.rgbFromUint(@intCast(channel));
|
||||
}
|
||||
|
||||
pub fn set_fg_palindex(self: *Plane, idx: c_uint) !void {
|
||||
self.style.fg = .{ .index = @intCast(idx) };
|
||||
}
|
||||
|
||||
pub fn set_bg_palindex(self: *Plane, idx: c_uint) !void {
|
||||
self.style.bg = .{ .index = @intCast(idx) };
|
||||
}
|
||||
|
||||
pub fn set_channels(self: Plane, channels_: u64) void {
|
||||
_ = self;
|
||||
_ = channels_;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub inline fn set_base_style(self: *const Plane, egc_: [*c]const u8, style_: Style) void {
|
||||
_ = self;
|
||||
_ = egc_;
|
||||
_ = style_;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub fn set_base_style_transparent(self: Plane, egc_: [*:0]const u8, style_: Style) void {
|
||||
_ = self;
|
||||
_ = egc_;
|
||||
_ = style_;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub fn set_base_style_bg_transparent(self: Plane, egc_: [*:0]const u8, style_: Style) void {
|
||||
_ = self;
|
||||
_ = egc_;
|
||||
_ = style_;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
pub inline fn set_style(self: *Plane, style_: Style) void {
|
||||
if (style_.fg) |color| self.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(color));
|
||||
if (style_.bg) |color| self.style.bg = vaxis.Cell.Color.rgbFromUint(@intCast(color));
|
||||
// if (style_.fs) |fontstyle| ... FIXME
|
||||
}
|
||||
|
||||
pub inline fn set_style_bg_transparent(self: *Plane, style_: Style) void {
|
||||
if (style_.fg) |color| self.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(color));
|
||||
self.style.bg = .default;
|
||||
}
|
||||
|
||||
pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *c_int, abs_col: usize) usize {
|
||||
if (egcs[0] == '\t') {
|
||||
colcount.* = @intCast(8 - abs_col % 8);
|
||||
return 1;
|
||||
}
|
||||
var iter = self.window.screen.unicode.graphemeIterator(egcs);
|
||||
const grapheme = iter.next() orelse {
|
||||
colcount.* = 1;
|
||||
return 1;
|
||||
};
|
||||
const s = grapheme.bytes(egcs);
|
||||
const w = self.window.gwidth(s);
|
||||
colcount.* = @intCast(w);
|
||||
return s.len;
|
||||
}
|
||||
|
||||
pub fn egc_chunk_width(self: *const Plane, chunk_: []const u8, abs_col_: usize) usize {
|
||||
var abs_col = abs_col_;
|
||||
var chunk = chunk_;
|
||||
var colcount: usize = 0;
|
||||
var cols: c_int = 0;
|
||||
while (chunk.len > 0) {
|
||||
const bytes = self.egc_length(chunk, &cols, abs_col);
|
||||
colcount += @intCast(cols);
|
||||
abs_col += @intCast(cols);
|
||||
if (chunk.len < bytes) break;
|
||||
chunk = chunk[bytes..];
|
||||
}
|
||||
return colcount;
|
||||
}
|
||||
|
||||
const GraphemeCache = struct {
|
||||
buf: [1024 * 16]u8 = undefined,
|
||||
idx: usize = 0,
|
||||
|
||||
pub fn put(self: *GraphemeCache, bytes: []const u8) []u8 {
|
||||
if (self.idx + bytes.len > self.buf.len) self.idx = 0;
|
||||
defer self.idx += bytes.len;
|
||||
@memcpy(self.buf[self.idx .. self.idx + bytes.len], bytes);
|
||||
return self.buf[self.idx .. self.idx + bytes.len];
|
||||
}
|
||||
};
|
267
src/renderer/vaxis/input.zig
Normal file
267
src/renderer/vaxis/input.zig
Normal file
|
@ -0,0 +1,267 @@
|
|||
const Key = @import("vaxis").Key;
|
||||
const Mouse = @import("vaxis").Mouse;
|
||||
|
||||
pub const key = struct {
|
||||
pub const ENTER: key_type = Key.enter;
|
||||
pub const TAB: key_type = Key.tab;
|
||||
pub const ESC: key_type = Key.escape;
|
||||
pub const SPACE: key_type = Key.space;
|
||||
pub const BACKSPACE: key_type = Key.backspace;
|
||||
|
||||
pub const INS: key_type = Key.insert;
|
||||
pub const DEL: key_type = Key.delete;
|
||||
pub const LEFT: key_type = Key.left;
|
||||
pub const RIGHT: key_type = Key.right;
|
||||
pub const UP: key_type = Key.up;
|
||||
pub const DOWN: key_type = Key.down;
|
||||
pub const PGDOWN: key_type = Key.page_down;
|
||||
pub const PGUP: key_type = Key.page_up;
|
||||
pub const HOME: key_type = Key.home;
|
||||
pub const END: key_type = Key.end;
|
||||
pub const CAPS_LOCK: key_type = Key.caps_lock;
|
||||
pub const SCROLL_LOCK: key_type = Key.scroll_lock;
|
||||
pub const NUM_LOCK: key_type = Key.num_lock;
|
||||
pub const PRINT_SCREEN: key_type = Key.print_screen;
|
||||
pub const PAUSE: key_type = Key.pause;
|
||||
pub const MENU: key_type = Key.menu;
|
||||
pub const F01: key_type = Key.f1;
|
||||
pub const F02: key_type = Key.f2;
|
||||
pub const F03: key_type = Key.f3;
|
||||
pub const F04: key_type = Key.f4;
|
||||
pub const F05: key_type = Key.f5;
|
||||
pub const F06: key_type = Key.f6;
|
||||
pub const F07: key_type = Key.f7;
|
||||
pub const F08: key_type = Key.f8;
|
||||
pub const F09: key_type = Key.f9;
|
||||
pub const F10: key_type = Key.f10;
|
||||
pub const F11: key_type = Key.f11;
|
||||
pub const F12: key_type = Key.f12;
|
||||
pub const F13: key_type = Key.f13;
|
||||
pub const F14: key_type = Key.f14;
|
||||
pub const F15: key_type = Key.f15;
|
||||
pub const F16: key_type = Key.f16;
|
||||
pub const F17: key_type = Key.f17;
|
||||
pub const F18: key_type = Key.f18;
|
||||
pub const F19: key_type = Key.f19;
|
||||
pub const F20: key_type = Key.f20;
|
||||
pub const F21: key_type = Key.f21;
|
||||
pub const F22: key_type = Key.f22;
|
||||
pub const F23: key_type = Key.f23;
|
||||
pub const F24: key_type = Key.f24;
|
||||
pub const F25: key_type = Key.f25;
|
||||
pub const F26: key_type = Key.f26;
|
||||
pub const F27: key_type = Key.f27;
|
||||
pub const F28: key_type = Key.f28;
|
||||
pub const F29: key_type = Key.f29;
|
||||
pub const F30: key_type = Key.f30;
|
||||
pub const F31: key_type = Key.f31;
|
||||
pub const F32: key_type = Key.f32;
|
||||
pub const F33: key_type = Key.f33;
|
||||
pub const F34: key_type = Key.f34;
|
||||
pub const F35: key_type = Key.f35;
|
||||
|
||||
pub const F58: key_type = Key.iso_level_5_shift + 1; // FIXME bogus
|
||||
|
||||
pub const MEDIA_PLAY: key_type = Key.media_play;
|
||||
pub const MEDIA_PAUSE: key_type = Key.media_pause;
|
||||
pub const MEDIA_PPAUSE: key_type = Key.media_play_pause;
|
||||
pub const MEDIA_REV: key_type = Key.media_reverse;
|
||||
pub const MEDIA_STOP: key_type = Key.media_stop;
|
||||
pub const MEDIA_FF: key_type = Key.media_fast_forward;
|
||||
pub const MEDIA_REWIND: key_type = Key.media_rewind;
|
||||
pub const MEDIA_NEXT: key_type = Key.media_track_next;
|
||||
pub const MEDIA_PREV: key_type = Key.media_track_previous;
|
||||
pub const MEDIA_RECORD: key_type = Key.media_record;
|
||||
pub const MEDIA_LVOL: key_type = Key.lower_volume;
|
||||
pub const MEDIA_RVOL: key_type = Key.raise_volume;
|
||||
pub const MEDIA_MUTE: key_type = Key.mute_volume;
|
||||
pub const LSHIFT: key_type = Key.left_shift;
|
||||
pub const LCTRL: key_type = Key.left_control;
|
||||
pub const LALT: key_type = Key.left_alt;
|
||||
pub const LSUPER: key_type = Key.left_super;
|
||||
pub const LHYPER: key_type = Key.left_hyper;
|
||||
pub const LMETA: key_type = Key.left_meta;
|
||||
pub const RSHIFT: key_type = Key.right_shift;
|
||||
pub const RCTRL: key_type = Key.right_control;
|
||||
pub const RALT: key_type = Key.right_alt;
|
||||
pub const RSUPER: key_type = Key.right_super;
|
||||
pub const RHYPER: key_type = Key.right_hyper;
|
||||
pub const RMETA: key_type = Key.right_meta;
|
||||
pub const L3SHIFT: key_type = Key.iso_level_3_shift;
|
||||
pub const L5SHIFT: key_type = Key.iso_level_5_shift;
|
||||
|
||||
pub const MOTION: key_type = @intCast(@intFromEnum(Mouse.Button.none));
|
||||
pub const BUTTON1: key_type = @intCast(@intFromEnum(Mouse.Button.left));
|
||||
pub const BUTTON2: key_type = @intCast(@intFromEnum(Mouse.Button.middle));
|
||||
pub const BUTTON3: key_type = @intCast(@intFromEnum(Mouse.Button.right));
|
||||
pub const BUTTON4: key_type = @intCast(@intFromEnum(Mouse.Button.wheel_up));
|
||||
pub const BUTTON5: key_type = @intCast(@intFromEnum(Mouse.Button.wheel_down));
|
||||
// pub const BUTTON6: key_type = @intCast(@intFromEnum(Mouse.Button.button_6));
|
||||
// pub const BUTTON7: key_type = @intCast(@intFromEnum(Mouse.Button.button_7));
|
||||
pub const BUTTON8: key_type = @intCast(@intFromEnum(Mouse.Button.button_8));
|
||||
pub const BUTTON9: key_type = @intCast(@intFromEnum(Mouse.Button.button_9));
|
||||
pub const BUTTON10: key_type = @intCast(@intFromEnum(Mouse.Button.button_10));
|
||||
pub const BUTTON11: key_type = @intCast(@intFromEnum(Mouse.Button.button_11));
|
||||
|
||||
// pub const SIGNAL: key_type = Key.SIGNAL;
|
||||
// pub const EOF: key_type = Key.EOF;
|
||||
// pub const SCROLL_UP: key_type = Key.SCROLL_UP;
|
||||
// pub const SCROLL_DOWN: key_type = Key.SCROLL_DOWN;
|
||||
|
||||
/// Is this uint32_t a synthesized event?
|
||||
pub fn synthesized_p(w: u32) bool {
|
||||
return switch (w) {
|
||||
Key.up...Key.iso_level_5_shift => true,
|
||||
Key.enter => true,
|
||||
Key.tab => true,
|
||||
Key.escape => true,
|
||||
Key.space => true,
|
||||
Key.backspace => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
};
|
||||
pub const key_type = u21;
|
||||
|
||||
pub const modifier = struct {
|
||||
pub const SHIFT: modifier_type = 1;
|
||||
pub const ALT: modifier_type = 2;
|
||||
pub const CTRL: modifier_type = 4;
|
||||
pub const SUPER: modifier_type = 8;
|
||||
pub const HYPER: modifier_type = 16;
|
||||
pub const META: modifier_type = 32;
|
||||
pub const CAPSLOCK: modifier_type = 64;
|
||||
pub const NUMLOCK: modifier_type = 128;
|
||||
};
|
||||
pub const modifier_type = u32;
|
||||
|
||||
pub const event_type = struct {
|
||||
pub const PRESS: usize = 1;
|
||||
pub const REPEAT: usize = 2;
|
||||
pub const RELEASE: usize = 3;
|
||||
};
|
||||
|
||||
pub const utils = struct {
|
||||
pub fn isSuper(modifiers: u32) bool {
|
||||
return modifiers & modifier.SUPER != 0;
|
||||
}
|
||||
|
||||
pub fn isCtrl(modifiers: u32) bool {
|
||||
return modifiers & modifier.CTRL != 0;
|
||||
}
|
||||
|
||||
pub fn isShift(modifiers: u32) bool {
|
||||
return modifiers & modifier.SHIFT != 0;
|
||||
}
|
||||
|
||||
pub fn isAlt(modifiers: u32) bool {
|
||||
return modifiers & modifier.ALT != 0;
|
||||
}
|
||||
|
||||
pub fn key_id_string(k: u32) []const u8 {
|
||||
return switch (k) {
|
||||
key.ENTER => "ENTER",
|
||||
key.TAB => "TAB",
|
||||
key.ESC => "ESC",
|
||||
key.SPACE => "SPACE",
|
||||
key.BACKSPACE => "BACKSPACE",
|
||||
key.INS => "INS",
|
||||
key.DEL => "DEL",
|
||||
key.LEFT => "LEFT",
|
||||
key.RIGHT => "RIGHT",
|
||||
key.UP => "UP",
|
||||
key.DOWN => "DOWN",
|
||||
key.PGDOWN => "PGDOWN",
|
||||
key.PGUP => "PGUP",
|
||||
key.HOME => "HOME",
|
||||
key.END => "END",
|
||||
key.CAPS_LOCK => "CAPS_LOCK",
|
||||
key.SCROLL_LOCK => "SCROLL_LOCK",
|
||||
key.NUM_LOCK => "NUM_LOCK",
|
||||
key.PRINT_SCREEN => "PRINT_SCREEN",
|
||||
key.PAUSE => "PAUSE",
|
||||
key.MENU => "MENU",
|
||||
key.F01 => "F01",
|
||||
key.F02 => "F02",
|
||||
key.F03 => "F03",
|
||||
key.F04 => "F04",
|
||||
key.F05 => "F05",
|
||||
key.F06 => "F06",
|
||||
key.F07 => "F07",
|
||||
key.F08 => "F08",
|
||||
key.F09 => "F09",
|
||||
key.F10 => "F10",
|
||||
key.F11 => "F11",
|
||||
key.F12 => "F12",
|
||||
key.F13 => "F13",
|
||||
key.F14 => "F14",
|
||||
key.F15 => "F15",
|
||||
key.F16 => "F16",
|
||||
key.F17 => "F17",
|
||||
key.F18 => "F18",
|
||||
key.F19 => "F19",
|
||||
key.F20 => "F20",
|
||||
key.F21 => "F21",
|
||||
key.F22 => "F22",
|
||||
key.F23 => "F23",
|
||||
key.F24 => "F24",
|
||||
key.F25 => "F25",
|
||||
key.F26 => "F26",
|
||||
key.F27 => "F27",
|
||||
key.F28 => "F28",
|
||||
key.F29 => "F29",
|
||||
key.F30 => "F30",
|
||||
key.F31 => "F31",
|
||||
key.F32 => "F32",
|
||||
key.F33 => "F33",
|
||||
key.F34 => "F34",
|
||||
key.F35 => "F35",
|
||||
key.MEDIA_PLAY => "MEDIA_PLAY",
|
||||
key.MEDIA_PAUSE => "MEDIA_PAUSE",
|
||||
key.MEDIA_PPAUSE => "MEDIA_PPAUSE",
|
||||
key.MEDIA_REV => "MEDIA_REV",
|
||||
key.MEDIA_STOP => "MEDIA_STOP",
|
||||
key.MEDIA_FF => "MEDIA_FF",
|
||||
key.MEDIA_REWIND => "MEDIA_REWIND",
|
||||
key.MEDIA_NEXT => "MEDIA_NEXT",
|
||||
key.MEDIA_PREV => "MEDIA_PREV",
|
||||
key.MEDIA_RECORD => "MEDIA_RECORD",
|
||||
key.MEDIA_LVOL => "MEDIA_LVOL",
|
||||
key.MEDIA_RVOL => "MEDIA_RVOL",
|
||||
key.MEDIA_MUTE => "MEDIA_MUTE",
|
||||
key.LSHIFT => "LSHIFT",
|
||||
key.LCTRL => "LCTRL",
|
||||
key.LALT => "LALT",
|
||||
key.LSUPER => "LSUPER",
|
||||
key.LHYPER => "LHYPER",
|
||||
key.LMETA => "LMETA",
|
||||
key.RSHIFT => "RSHIFT",
|
||||
key.RCTRL => "RCTRL",
|
||||
key.RALT => "RALT",
|
||||
key.RSUPER => "RSUPER",
|
||||
key.RHYPER => "RHYPER",
|
||||
key.RMETA => "RMETA",
|
||||
key.L3SHIFT => "L3SHIFT",
|
||||
key.L5SHIFT => "L5SHIFT",
|
||||
else => "",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn button_id_string(k: u32) []const u8 {
|
||||
return switch (k) {
|
||||
key.MOTION => "MOTION",
|
||||
key.BUTTON1 => "BUTTON1",
|
||||
key.BUTTON2 => "BUTTON2",
|
||||
key.BUTTON3 => "BUTTON3",
|
||||
key.BUTTON4 => "BUTTON4",
|
||||
key.BUTTON5 => "BUTTON5",
|
||||
// key.BUTTON6 => "BUTTON6",
|
||||
// key.BUTTON7 => "BUTTON7",
|
||||
key.BUTTON8 => "BUTTON8",
|
||||
key.BUTTON9 => "BUTTON9",
|
||||
key.BUTTON10 => "BUTTON10",
|
||||
key.BUTTON11 => "BUTTON11",
|
||||
else => "",
|
||||
};
|
||||
}
|
||||
};
|
312
src/renderer/vaxis/renderer.zig
Normal file
312
src/renderer/vaxis/renderer.zig
Normal file
|
@ -0,0 +1,312 @@
|
|||
const std = @import("std");
|
||||
const cbor = @import("cbor");
|
||||
const log = @import("log");
|
||||
const Style = @import("theme").Style;
|
||||
|
||||
const vaxis = @import("vaxis");
|
||||
|
||||
pub const input = @import("input.zig");
|
||||
|
||||
pub const Plane = @import("Plane.zig");
|
||||
pub const Cell = @import("Cell.zig");
|
||||
|
||||
pub const style = @import("style.zig").StyleBits;
|
||||
|
||||
const mod = input.modifier;
|
||||
const key = input.key;
|
||||
const event_type = input.event_type;
|
||||
|
||||
const Self = @This();
|
||||
pub const log_name = "vaxis";
|
||||
|
||||
a: std.mem.Allocator,
|
||||
|
||||
vx: vaxis.Vaxis,
|
||||
|
||||
no_alternate: bool,
|
||||
event_buffer: std.ArrayList(u8),
|
||||
|
||||
bracketed_paste: bool = false,
|
||||
bracketed_paste_buffer: std.ArrayList(u8),
|
||||
|
||||
handler_ctx: *anyopaque,
|
||||
dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||
dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null,
|
||||
dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, dragging: bool, cbor_msg: []const u8) void = null,
|
||||
dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||
|
||||
logger: log.Logger,
|
||||
|
||||
const ModState = struct { ctrl: bool = false, shift: bool = false, alt: bool = false };
|
||||
|
||||
const Event = union(enum) {
|
||||
key_press: vaxis.Key,
|
||||
winsize: vaxis.Winsize,
|
||||
focus_in,
|
||||
};
|
||||
|
||||
pub fn init(a: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool) !Self {
|
||||
return .{
|
||||
.a = a,
|
||||
.vx = try vaxis.init(a, .{}),
|
||||
.no_alternate = no_alternate,
|
||||
.event_buffer = std.ArrayList(u8).init(a),
|
||||
.bracketed_paste_buffer = std.ArrayList(u8).init(a),
|
||||
.handler_ctx = handler_ctx,
|
||||
.logger = log.logger(log_name),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.vx.screen.tty.write(vaxis.ctlseqs.show_cursor);
|
||||
self.vx.screen.tty.flush();
|
||||
self.vx.deinit(self.a);
|
||||
self.bracketed_paste_buffer.deinit();
|
||||
self.event_buffer.deinit();
|
||||
}
|
||||
|
||||
pub fn run(self: *Self) !void {
|
||||
if (self.vx.tty == null) self.vx.tty = try vaxis.Tty.init();
|
||||
if (!self.no_alternate) try self.vx.enterAltScreen();
|
||||
try self.vx.queryTerminal();
|
||||
const ws = try vaxis.Tty.getWinsize(self.input_fd());
|
||||
try self.vx.resize(self.a, ws);
|
||||
self.vx.queueRefresh();
|
||||
try self.vx.setMouseMode(true);
|
||||
}
|
||||
|
||||
pub fn render(self: *Self) !void {
|
||||
self.vx.queueRefresh(); // FIXME: why do differential updates not work?
|
||||
return self.vx.render();
|
||||
}
|
||||
|
||||
pub fn refresh(self: *Self) !void {
|
||||
const ws = try vaxis.Tty.getWinsize(self.input_fd());
|
||||
try self.vx.resize(self.a, ws);
|
||||
self.vx.queueRefresh();
|
||||
}
|
||||
|
||||
pub fn stop(self: *Self) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn stdplane(self: *Self) Plane {
|
||||
const name = "root";
|
||||
var plane: Plane = .{
|
||||
.window = self.vx.window(),
|
||||
.name_buf = undefined,
|
||||
.name_len = name.len,
|
||||
};
|
||||
@memcpy(plane.name_buf[0..name.len], name);
|
||||
return plane;
|
||||
}
|
||||
|
||||
pub fn input_fd(self: Self) i32 {
|
||||
return self.vx.tty.?.fd;
|
||||
}
|
||||
|
||||
pub fn leave_alternate_screen(self: *Self) void {
|
||||
self.vx.exitAltScreen() catch {};
|
||||
}
|
||||
|
||||
pub fn process_input(self: *Self) !void {
|
||||
var parser: vaxis.Parser = .{
|
||||
.grapheme_data = &self.vx.screen.unicode.grapheme_data,
|
||||
};
|
||||
var buf: [1024]u8 = undefined;
|
||||
var start: usize = 0;
|
||||
const n = std.posix.read(self.input_fd(), &buf) catch |e| switch (e) {
|
||||
error.WouldBlock => return,
|
||||
else => return e,
|
||||
};
|
||||
while (start < n) {
|
||||
const result = try parser.parse(buf[start..n]);
|
||||
start += result.n;
|
||||
const event = result.event orelse continue;
|
||||
switch (event) {
|
||||
.key_press => |key_| {
|
||||
const cbor_msg = try self.fmtmsg(.{
|
||||
"I",
|
||||
event_type.PRESS,
|
||||
key_.codepoint,
|
||||
key_.shifted_codepoint orelse key_.codepoint,
|
||||
key_.text orelse input.utils.key_id_string(key_.codepoint),
|
||||
@as(u8, @bitCast(key_.mods)),
|
||||
});
|
||||
if (self.dispatch_input) |f| f(self.handler_ctx, cbor_msg);
|
||||
},
|
||||
.key_release => |*key_| {
|
||||
const cbor_msg = try self.fmtmsg(.{
|
||||
"I",
|
||||
event_type.RELEASE,
|
||||
key_.codepoint,
|
||||
key_.shifted_codepoint orelse key_.codepoint,
|
||||
key_.text orelse input.utils.key_id_string(key_.codepoint),
|
||||
@as(u8, @bitCast(key_.mods)),
|
||||
});
|
||||
if (self.dispatch_input) |f| f(self.handler_ctx, cbor_msg);
|
||||
},
|
||||
.mouse => |mouse| {
|
||||
if (self.dispatch_mouse) |f| switch (mouse.type) {
|
||||
.motion => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{
|
||||
"M",
|
||||
mouse.col,
|
||||
mouse.row,
|
||||
0,
|
||||
0,
|
||||
})),
|
||||
.press => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{
|
||||
"B",
|
||||
event_type.PRESS,
|
||||
@intFromEnum(mouse.button),
|
||||
input.utils.button_id_string(@intFromEnum(mouse.button)),
|
||||
mouse.col,
|
||||
mouse.row,
|
||||
0,
|
||||
0,
|
||||
})),
|
||||
.release => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{
|
||||
"B",
|
||||
event_type.RELEASE,
|
||||
@intFromEnum(mouse.button),
|
||||
input.utils.button_id_string(@intFromEnum(mouse.button)),
|
||||
mouse.col,
|
||||
mouse.row,
|
||||
0,
|
||||
0,
|
||||
})),
|
||||
.drag => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{
|
||||
"D",
|
||||
event_type.PRESS,
|
||||
@intFromEnum(mouse.button),
|
||||
input.utils.button_id_string(@intFromEnum(mouse.button)),
|
||||
mouse.col,
|
||||
mouse.row,
|
||||
0,
|
||||
0,
|
||||
})),
|
||||
};
|
||||
},
|
||||
.focus_in => {
|
||||
// FIXME
|
||||
},
|
||||
.focus_out => {
|
||||
// FIXME
|
||||
},
|
||||
.paste_start => {
|
||||
self.bracketed_paste = true;
|
||||
self.bracketed_paste_buffer.clearRetainingCapacity();
|
||||
},
|
||||
.paste_end => {
|
||||
defer self.bracketed_paste_buffer.clearAndFree();
|
||||
if (!self.bracketed_paste) return;
|
||||
self.bracketed_paste = false;
|
||||
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items }));
|
||||
},
|
||||
.cap_unicode => {
|
||||
self.vx.caps.unicode = .unicode;
|
||||
self.vx.screen.width_method = .unicode;
|
||||
},
|
||||
.cap_da1 => {
|
||||
std.Thread.Futex.wake(&self.vx.query_futex, 10);
|
||||
},
|
||||
.cap_kitty_keyboard => {
|
||||
self.vx.caps.kitty_keyboard = true;
|
||||
},
|
||||
.cap_kitty_graphics => {
|
||||
if (!self.vx.caps.kitty_graphics) {
|
||||
self.vx.caps.kitty_graphics = true;
|
||||
}
|
||||
},
|
||||
.cap_rgb => {
|
||||
self.vx.caps.rgb = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmtmsg(self: *Self, value: anytype) ![]const u8 {
|
||||
self.event_buffer.clearRetainingCapacity();
|
||||
try cbor.writeValue(self.event_buffer.writer(), value);
|
||||
return self.event_buffer.items;
|
||||
}
|
||||
|
||||
const OSC = "\x1B]"; // Operating System Command
|
||||
const ST = "\x1B\\"; // String Terminator
|
||||
const BEL = "\x07";
|
||||
const OSC0_title = OSC ++ "0;";
|
||||
const OSC52_clipboard = OSC ++ "52;c;";
|
||||
const OSC52_clipboard_paste = OSC ++ "52;p;";
|
||||
const OSC22_cursor = OSC ++ "22;";
|
||||
const OSC22_cursor_reply = OSC ++ "22:";
|
||||
|
||||
const CSI = "\x1B["; // Control Sequence Introducer
|
||||
const CSI_bracketed_paste_enable = CSI ++ "?2004h";
|
||||
const CSI_bracketed_paste_disable = CSI ++ "?2004h";
|
||||
const CIS_bracketed_paste_begin = CSI ++ "200~";
|
||||
const CIS_bracketed_paste_end = CSI ++ "201~";
|
||||
|
||||
pub fn set_terminal_title(text: []const u8) void {
|
||||
var writer = std.io.getStdOut().writer();
|
||||
var buf: [std.posix.PATH_MAX]u8 = undefined;
|
||||
const term_cmd = std.fmt.bufPrint(&buf, OSC0_title ++ "{s}" ++ BEL, .{text}) catch return;
|
||||
_ = writer.write(term_cmd) catch return;
|
||||
}
|
||||
|
||||
pub fn copy_to_system_clipboard(tmp_a: std.mem.Allocator, text: []const u8) void {
|
||||
copy_to_system_clipboard_with_errors(tmp_a, text) catch |e| log.logger(log_name).err("copy_to_system_clipboard", e);
|
||||
}
|
||||
|
||||
fn copy_to_system_clipboard_with_errors(tmp_a: std.mem.Allocator, text: []const u8) !void {
|
||||
var writer = std.io.getStdOut().writer();
|
||||
const encoder = std.base64.standard.Encoder;
|
||||
const size = OSC52_clipboard.len + encoder.calcSize(text.len) + ST.len;
|
||||
const buf = try tmp_a.alloc(u8, size);
|
||||
defer tmp_a.free(buf);
|
||||
@memcpy(buf[0..OSC52_clipboard.len], OSC52_clipboard);
|
||||
const b64 = encoder.encode(buf[OSC52_clipboard.len..], text);
|
||||
@memcpy(buf[OSC52_clipboard.len + b64.len ..], ST);
|
||||
_ = try writer.write(buf);
|
||||
}
|
||||
|
||||
pub fn request_system_clipboard() void {
|
||||
write_stdout(OSC52_clipboard ++ "?" ++ ST);
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_text(push_or_pop: bool) void {
|
||||
if (push_or_pop) mouse_cursor_push("text") else mouse_cursor_pop();
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_pointer(push_or_pop: bool) void {
|
||||
if (push_or_pop) mouse_cursor_push("pointer") else mouse_cursor_pop();
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_default(push_or_pop: bool) void {
|
||||
if (push_or_pop) mouse_cursor_push("default") else mouse_cursor_pop();
|
||||
}
|
||||
|
||||
fn mouse_cursor_push(comptime name: []const u8) void {
|
||||
write_stdout(OSC22_cursor ++ name ++ ST);
|
||||
}
|
||||
|
||||
fn mouse_cursor_pop() void {
|
||||
write_stdout(OSC22_cursor ++ "default" ++ ST);
|
||||
}
|
||||
|
||||
fn write_stdout(bytes: []const u8) void {
|
||||
_ = std.io.getStdOut().writer().write(bytes) catch |e| log.logger(log_name).err("stdout", e);
|
||||
}
|
||||
|
||||
pub fn cursor_enable(self: *Self, y: c_int, x: c_int) !void {
|
||||
self.vx.screen.cursor_vis = true;
|
||||
self.vx.screen.cursor_row = @intCast(y);
|
||||
self.vx.screen.cursor_col = @intCast(x);
|
||||
}
|
||||
|
||||
pub fn cursor_disable(self: *Self) void {
|
||||
self.vx.screen.cursor_vis = false;
|
||||
}
|
||||
|
||||
pub fn ucs32_to_utf8(ucs32: []const u32, utf8: []u8) !usize {
|
||||
return @intCast(try std.unicode.utf8Encode(@intCast(ucs32[0]), utf8));
|
||||
}
|
14
src/renderer/vaxis/style.zig
Normal file
14
src/renderer/vaxis/style.zig
Normal file
|
@ -0,0 +1,14 @@
|
|||
pub const StyleBits = packed struct(u5) {
|
||||
struck: bool = false,
|
||||
bold: bool = false,
|
||||
undercurl: bool = false,
|
||||
underline: bool = false,
|
||||
italic: bool = false,
|
||||
|
||||
pub const struck: StyleBits = .{ .struck = true };
|
||||
pub const bold: StyleBits = .{ .bold = true };
|
||||
pub const undercurl: StyleBits = .{ .undercurl = true };
|
||||
pub const underline: StyleBits = .{ .underline = true };
|
||||
pub const italic: StyleBits = .{ .italic = true };
|
||||
pub const normal: StyleBits = .{};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue