feat: add libvaxis renderer
This commit is contained in:
parent
b15fa47f30
commit
1cd3cb17ce
32 changed files with 1559 additions and 516 deletions
24
build.zig
24
build.zig
|
@ -22,6 +22,7 @@ pub fn build(b: *std.Build) void {
|
|||
const use_llvm_option = b.option(bool, "use_llvm", "Enable llvm backend (default: yes)");
|
||||
const use_lld_option = b.option(bool, "use_lld", "Enable lld backend (default: yes)");
|
||||
const use_system_notcurses = b.option(bool, "use_system_notcurses", "Build against system notcurses (default: no)") orelse false;
|
||||
const use_vaxis = b.option(bool, "use_vaxis", "Enable libvaxis rendering backend (default: no)") orelse false;
|
||||
|
||||
const tracy_enabled = if (enable_tracy_option) |enabled| enabled else false;
|
||||
const optimize_deps_enabled = if (optimize_deps_option) |enabled| enabled else true;
|
||||
|
@ -32,6 +33,7 @@ pub fn build(b: *std.Build) void {
|
|||
options.addOption(bool, "use_llvm", use_llvm_option orelse false);
|
||||
options.addOption(bool, "use_lld", use_lld_option orelse false);
|
||||
options.addOption(bool, "use_system_notcurses", use_system_notcurses);
|
||||
options.addOption(bool, "use_vaxis", use_vaxis);
|
||||
|
||||
const options_mod = options.createModule();
|
||||
|
||||
|
@ -57,6 +59,12 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
const notcurses_mod = notcurses_dep.module("notcurses");
|
||||
|
||||
const vaxis_dep = b.dependency("vaxis", .{
|
||||
.target = target,
|
||||
.optimize = dependency_optimize,
|
||||
});
|
||||
const vaxis_mod = vaxis_dep.module("vaxis");
|
||||
|
||||
const clap_dep = b.dependency("clap", .{
|
||||
.target = target,
|
||||
.optimize = dependency_optimize,
|
||||
|
@ -128,7 +136,17 @@ pub fn build(b: *std.Build) void {
|
|||
},
|
||||
});
|
||||
|
||||
const renderer_mod = notcurses_renderer_mod;
|
||||
const vaxis_renderer_mod = b.createModule(.{
|
||||
.root_source_file = .{ .path = "src/renderer/vaxis/renderer.zig" },
|
||||
.imports = &.{
|
||||
.{ .name = "vaxis", .module = vaxis_mod },
|
||||
.{ .name = "theme", .module = themes_dep.module("theme") },
|
||||
.{ .name = "cbor", .module = cbor_mod },
|
||||
.{ .name = "log", .module = log_mod },
|
||||
},
|
||||
});
|
||||
|
||||
const renderer_mod = if (use_vaxis) vaxis_renderer_mod else notcurses_renderer_mod;
|
||||
|
||||
const Buffer_mod = b.createModule(.{
|
||||
.root_source_file = .{ .path = "src/buffer/Buffer.zig" },
|
||||
|
@ -210,7 +228,7 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "flow",
|
||||
.name = if (use_vaxis) "flow-vaxis" else "flow",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
|
@ -238,7 +256,7 @@ pub fn build(b: *std.Build) void {
|
|||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const check_exe = b.addExecutable(.{
|
||||
.name = "flow",
|
||||
.name = if (use_vaxis) "flow-vaxis" else "flow",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
.url = "https://github.com/fjebaker/fuzzig/archive/c6a0e0ca1a24e55ebdce51c83b918d4325ca7032.tar.gz",
|
||||
.hash = "1220214dfb9a0806d9c8a059beb9e3b07811fd138cd5baeb9d1da432588920a084bf",
|
||||
},
|
||||
.vaxis = .{
|
||||
.url = "https://github.com/rockorager/libvaxis/archive/972129a764910c033042cfc50732002af62a0f83.tar.gz",
|
||||
.hash = "1220b34a066de45d8e58d3d85173bcd24edbd9e87b93791e621acc696d97e8e45c4c",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"include",
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
const std = @import("std");
|
||||
const Plane = @import("renderer").Plane;
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
const cwd = std.fs.cwd;
|
||||
|
||||
const egc_length = @import("renderer").egc.length;
|
||||
const egc_chunk_width = @import("renderer").egc.chunk_width;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const default_leaf_capacity = 64;
|
||||
|
@ -53,7 +51,7 @@ pub const WalkerMut = struct {
|
|||
pub const stop = WalkerMut{ .keep_walking = false };
|
||||
pub const found = WalkerMut{ .found = true };
|
||||
|
||||
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf) WalkerMut;
|
||||
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, plane: Plane) WalkerMut;
|
||||
};
|
||||
|
||||
pub const Walker = struct {
|
||||
|
@ -65,7 +63,7 @@ pub const Walker = struct {
|
|||
pub const stop = Walker{ .keep_walking = false };
|
||||
pub const found = Walker{ .found = true };
|
||||
|
||||
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf) Walker;
|
||||
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, plane: Plane) Walker;
|
||||
};
|
||||
|
||||
pub const Weights = struct {
|
||||
|
@ -147,7 +145,7 @@ pub const Leaf = struct {
|
|||
return self.buf.len == 0 and !self.bol and !self.eol;
|
||||
}
|
||||
|
||||
fn pos_to_width(self: *const Leaf, pos: *usize, abs_col_: usize) usize {
|
||||
fn pos_to_width(self: *const Leaf, pos: *usize, abs_col_: usize, plane: Plane) usize {
|
||||
var col: usize = 0;
|
||||
var abs_col = abs_col_;
|
||||
var cols: c_int = 0;
|
||||
|
@ -158,7 +156,7 @@ pub const Leaf = struct {
|
|||
buf = buf[1..];
|
||||
pos.* -= 1;
|
||||
} else {
|
||||
const bytes = egc_length(buf, &cols, abs_col);
|
||||
const bytes = plane.egc_length(buf, &cols, abs_col);
|
||||
buf = buf[bytes..];
|
||||
pos.* -= bytes;
|
||||
}
|
||||
|
@ -168,12 +166,12 @@ pub const Leaf = struct {
|
|||
return col;
|
||||
}
|
||||
|
||||
fn width(self: *const Leaf, abs_col: usize) usize {
|
||||
fn width(self: *const Leaf, abs_col: usize, plane: Plane) usize {
|
||||
var pos: usize = std.math.maxInt(usize);
|
||||
return self.pos_to_width(&pos, abs_col);
|
||||
return self.pos_to_width(&pos, abs_col, plane);
|
||||
}
|
||||
|
||||
inline fn width_to_pos(self: *const Leaf, col_: usize, abs_col_: usize) !usize {
|
||||
inline fn width_to_pos(self: *const Leaf, col_: usize, abs_col_: usize, plane: Plane) !usize {
|
||||
var abs_col = abs_col_;
|
||||
var col = col_;
|
||||
var cols: c_int = 0;
|
||||
|
@ -181,7 +179,7 @@ pub const Leaf = struct {
|
|||
return while (buf.len > 0) {
|
||||
if (col == 0)
|
||||
break @intFromPtr(buf.ptr) - @intFromPtr(self.buf.ptr);
|
||||
const bytes = egc_length(buf, &cols, abs_col);
|
||||
const bytes = plane.egc_length(buf, &cols, abs_col);
|
||||
buf = buf[bytes..];
|
||||
if (col < cols)
|
||||
break @intFromPtr(buf.ptr) - @intFromPtr(self.buf.ptr);
|
||||
|
@ -190,20 +188,20 @@ pub const Leaf = struct {
|
|||
} else error.BufferUnderrun;
|
||||
}
|
||||
|
||||
inline fn dump(self: *const Leaf, l: *ArrayList(u8), abs_col: usize) !void {
|
||||
inline fn dump(self: *const Leaf, l: *ArrayList(u8), abs_col: usize, plane: Plane) !void {
|
||||
var buf: [16]u8 = undefined;
|
||||
const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{self.width(abs_col)});
|
||||
const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{self.width(abs_col, plane)});
|
||||
if (self.bol)
|
||||
try l.appendSlice("BOL ");
|
||||
try l.appendSlice(wcwidth);
|
||||
try l.append('"');
|
||||
try debug_render_chunk(self.buf, l);
|
||||
try debug_render_chunk(self.buf, l, plane);
|
||||
try l.appendSlice("\" ");
|
||||
if (self.eol)
|
||||
try l.appendSlice("EOL ");
|
||||
}
|
||||
|
||||
fn debug_render_chunk(chunk: []const u8, l: *ArrayList(u8)) !void {
|
||||
fn debug_render_chunk(chunk: []const u8, l: *ArrayList(u8), plane: Plane) !void {
|
||||
var cols: c_int = 0;
|
||||
var buf = chunk;
|
||||
while (buf.len > 0) {
|
||||
|
@ -214,7 +212,7 @@ pub const Leaf = struct {
|
|||
buf = buf[1..];
|
||||
},
|
||||
else => {
|
||||
const bytes = egc_length(buf, &cols, 0);
|
||||
const bytes = plane.egc_length(buf, &cols, 0);
|
||||
var buf_: [4096]u8 = undefined;
|
||||
try l.appendSlice(try std.fmt.bufPrint(&buf_, "{s}", .{std.fmt.fmtSliceEscapeLower(buf[0..bytes])}));
|
||||
buf = buf[bytes..];
|
||||
|
@ -316,27 +314,27 @@ const Node = union(enum) {
|
|||
return leaves.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn walk_const(self: *const Node, f: Walker.F, ctx: *anyopaque) Walker {
|
||||
fn walk_const(self: *const Node, f: Walker.F, ctx: *anyopaque, plane: Plane) Walker {
|
||||
switch (self.*) {
|
||||
.node => |*node| {
|
||||
const left = node.left.walk_const(f, ctx);
|
||||
const left = node.left.walk_const(f, ctx, plane);
|
||||
if (!left.keep_walking) {
|
||||
var result = Walker{};
|
||||
result.err = left.err;
|
||||
result.found = left.found;
|
||||
return result;
|
||||
}
|
||||
const right = node.right.walk_const(f, ctx);
|
||||
const right = node.right.walk_const(f, ctx, plane);
|
||||
return node.merge_results_const(left, right);
|
||||
},
|
||||
.leaf => |*l| return f(ctx, l),
|
||||
.leaf => |*l| return f(ctx, l, plane),
|
||||
}
|
||||
}
|
||||
|
||||
fn walk(self: *const Node, a: Allocator, f: WalkerMut.F, ctx: *anyopaque) WalkerMut {
|
||||
fn walk(self: *const Node, a: Allocator, f: WalkerMut.F, ctx: *anyopaque, plane: Plane) WalkerMut {
|
||||
switch (self.*) {
|
||||
.node => |*node| {
|
||||
const left = node.left.walk(a, f, ctx);
|
||||
const left = node.left.walk(a, f, ctx, plane);
|
||||
if (!left.keep_walking) {
|
||||
var result = WalkerMut{};
|
||||
result.err = left.err;
|
||||
|
@ -346,26 +344,26 @@ const Node = union(enum) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
const right = node.right.walk(a, f, ctx);
|
||||
const right = node.right.walk(a, f, ctx, plane);
|
||||
return node.merge_results(a, left, right);
|
||||
},
|
||||
.leaf => |*l| return f(ctx, l),
|
||||
.leaf => |*l| return f(ctx, l, plane),
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_from_line_begin_const_internal(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque) Walker {
|
||||
fn walk_from_line_begin_const_internal(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque, plane: Plane) Walker {
|
||||
switch (self.*) {
|
||||
.node => |*node| {
|
||||
const left_bols = node.weights.bols;
|
||||
if (line >= left_bols)
|
||||
return node.right.walk_from_line_begin_const_internal(line - left_bols, f, ctx);
|
||||
const left_result = node.left.walk_from_line_begin_const_internal(line, f, ctx);
|
||||
const right_result = if (left_result.found and left_result.keep_walking) node.right.walk_const(f, ctx) else Walker{};
|
||||
return node.right.walk_from_line_begin_const_internal(line - left_bols, f, ctx, plane);
|
||||
const left_result = node.left.walk_from_line_begin_const_internal(line, f, ctx, plane);
|
||||
const right_result = if (left_result.found and left_result.keep_walking) node.right.walk_const(f, ctx, plane) else Walker{};
|
||||
return node.merge_results_const(left_result, right_result);
|
||||
},
|
||||
.leaf => |*l| {
|
||||
if (line == 0) {
|
||||
var result = f(ctx, l);
|
||||
var result = f(ctx, l, plane);
|
||||
if (result.err) |_| return result;
|
||||
result.found = true;
|
||||
return result;
|
||||
|
@ -375,18 +373,18 @@ const Node = union(enum) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn walk_from_line_begin_const(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque) !bool {
|
||||
const result = self.walk_from_line_begin_const_internal(line, f, ctx);
|
||||
pub fn walk_from_line_begin_const(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque, plane: Plane) !bool {
|
||||
const result = self.walk_from_line_begin_const_internal(line, f, ctx, plane);
|
||||
if (result.err) |e| return e;
|
||||
return result.found;
|
||||
}
|
||||
|
||||
fn walk_from_line_begin_internal(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque) WalkerMut {
|
||||
fn walk_from_line_begin_internal(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, plane: Plane) WalkerMut {
|
||||
switch (self.*) {
|
||||
.node => |*node| {
|
||||
const left_bols = node.weights.bols;
|
||||
if (line >= left_bols) {
|
||||
const right_result = node.right.walk_from_line_begin_internal(a, line - left_bols, f, ctx);
|
||||
const right_result = node.right.walk_from_line_begin_internal(a, line - left_bols, f, ctx, plane);
|
||||
if (right_result.replace) |p| {
|
||||
var result = WalkerMut{};
|
||||
result.err = right_result.err;
|
||||
|
@ -401,13 +399,13 @@ const Node = union(enum) {
|
|||
return right_result;
|
||||
}
|
||||
}
|
||||
const left_result = node.left.walk_from_line_begin_internal(a, line, f, ctx);
|
||||
const right_result = if (left_result.found and left_result.keep_walking) node.right.walk(a, f, ctx) else WalkerMut{};
|
||||
const left_result = node.left.walk_from_line_begin_internal(a, line, f, ctx, plane);
|
||||
const right_result = if (left_result.found and left_result.keep_walking) node.right.walk(a, f, ctx, plane) else WalkerMut{};
|
||||
return node.merge_results(a, left_result, right_result);
|
||||
},
|
||||
.leaf => |*l| {
|
||||
if (line == 0) {
|
||||
var result = f(ctx, l);
|
||||
var result = f(ctx, l, plane);
|
||||
if (result.err) |_| {
|
||||
result.replace = null;
|
||||
return result;
|
||||
|
@ -420,8 +418,8 @@ const Node = union(enum) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn walk_from_line_begin(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque) !struct { bool, ?Root } {
|
||||
const result = self.walk_from_line_begin_internal(a, line, f, ctx);
|
||||
pub fn walk_from_line_begin(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, plane: Plane) !struct { bool, ?Root } {
|
||||
const result = self.walk_from_line_begin_internal(a, line, f, ctx, plane);
|
||||
if (result.err) |e| return e;
|
||||
return .{ result.found, result.replace };
|
||||
}
|
||||
|
@ -461,27 +459,27 @@ const Node = union(enum) {
|
|||
}
|
||||
}
|
||||
|
||||
const EgcF = *const fn (ctx: *anyopaque, egc: []const u8, wcwidth: usize) Walker;
|
||||
const EgcF = *const fn (ctx: *anyopaque, egc: []const u8, wcwidth: usize, plane: Plane) Walker;
|
||||
|
||||
pub fn walk_egc_forward(self: *const Node, line: usize, walker_f: EgcF, walker_ctx: *anyopaque) !void {
|
||||
pub fn walk_egc_forward(self: *const Node, line: usize, walker_f: EgcF, walker_ctx: *anyopaque, plane_: Plane) !void {
|
||||
const Ctx = struct {
|
||||
walker_f: EgcF,
|
||||
walker_ctx: @TypeOf(walker_ctx),
|
||||
abs_col: usize = 0,
|
||||
fn walker(ctx_: *anyopaque, leaf: *const Self.Leaf) Walker {
|
||||
fn walker(ctx_: *anyopaque, leaf: *const Self.Leaf, plane: Plane) Walker {
|
||||
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
|
||||
var buf: []const u8 = leaf.buf;
|
||||
while (buf.len > 0) {
|
||||
var cols: c_int = undefined;
|
||||
const bytes = egc_length(buf, &cols, ctx.abs_col);
|
||||
const ret = ctx.walker_f(ctx.walker_ctx, buf[0..bytes], @intCast(cols));
|
||||
const bytes = plane.egc_length(buf, &cols, ctx.abs_col);
|
||||
const ret = ctx.walker_f(ctx.walker_ctx, buf[0..bytes], @intCast(cols), plane);
|
||||
if (ret.err) |e| return .{ .err = e };
|
||||
buf = buf[bytes..];
|
||||
ctx.abs_col += @intCast(cols);
|
||||
if (!ret.keep_walking) return Walker.stop;
|
||||
}
|
||||
if (leaf.eol) {
|
||||
const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1);
|
||||
const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1, plane);
|
||||
if (ret.err) |e| return .{ .err = e };
|
||||
if (!ret.keep_walking) return Walker.stop;
|
||||
ctx.abs_col = 0;
|
||||
|
@ -490,16 +488,16 @@ const Node = union(enum) {
|
|||
}
|
||||
};
|
||||
var ctx: Ctx = .{ .walker_f = walker_f, .walker_ctx = walker_ctx };
|
||||
const found = try self.walk_from_line_begin_const(line, Ctx.walker, &ctx);
|
||||
const found = try self.walk_from_line_begin_const(line, Ctx.walker, &ctx, plane_);
|
||||
if (!found) return error.NotFound;
|
||||
}
|
||||
|
||||
pub fn ecg_at(self: *const Node, line: usize, col: usize) error{NotFound}!struct { []const u8, usize, usize } {
|
||||
pub fn ecg_at(self: *const Node, line: usize, col: usize, plane: Plane) error{NotFound}!struct { []const u8, usize, usize } {
|
||||
const ctx_ = struct {
|
||||
col: usize,
|
||||
at: ?[]const u8 = null,
|
||||
wcwidth: usize = 0,
|
||||
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Walker {
|
||||
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Walker {
|
||||
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
|
||||
ctx.at = egc;
|
||||
ctx.wcwidth = wcwidth;
|
||||
|
@ -510,20 +508,20 @@ const Node = union(enum) {
|
|||
}
|
||||
};
|
||||
var ctx: ctx_ = .{ .col = col };
|
||||
self.walk_egc_forward(line, ctx_.walker, &ctx) catch return .{ "?", 1, 0 };
|
||||
self.walk_egc_forward(line, ctx_.walker, &ctx, plane) catch return .{ "?", 1, 0 };
|
||||
return if (ctx.at) |at| .{ at, ctx.wcwidth, ctx.col } else error.NotFound;
|
||||
}
|
||||
|
||||
pub fn test_at(self: *const Node, pred: *const fn (c: []const u8) bool, line: usize, col: usize) bool {
|
||||
const ecg, _, _ = self.ecg_at(line, col) catch return false;
|
||||
pub fn test_at(self: *const Node, pred: *const fn (c: []const u8) bool, line: usize, col: usize, plane: Plane) bool {
|
||||
const ecg, _, _ = self.ecg_at(line, col, plane) catch return false;
|
||||
return pred(ecg);
|
||||
}
|
||||
|
||||
pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(u16)) error{ Stop, NoSpaceLeft }!void {
|
||||
pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(u16), plane: Plane) error{ Stop, NoSpaceLeft }!void {
|
||||
const Ctx = struct {
|
||||
map: *ArrayList(u16),
|
||||
wcwidth: usize = 0,
|
||||
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Walker {
|
||||
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Walker {
|
||||
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
|
||||
var n = egc.len;
|
||||
while (n > 0) : (n -= 1) {
|
||||
|
@ -535,20 +533,20 @@ const Node = union(enum) {
|
|||
}
|
||||
};
|
||||
var ctx: Ctx = .{ .map = map };
|
||||
self.walk_egc_forward(line, Ctx.walker, &ctx) catch |e| return switch (e) {
|
||||
self.walk_egc_forward(line, Ctx.walker, &ctx, plane) catch |e| return switch (e) {
|
||||
error.NoSpaceLeft => error.NoSpaceLeft,
|
||||
else => error.Stop,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_range(self: *const Node, sel: Selection, copy_buf: ?[]u8, size: ?*usize, wcwidth_: ?*usize) error{ Stop, NoSpaceLeft }!?[]u8 {
|
||||
pub fn get_range(self: *const Node, sel: Selection, copy_buf: ?[]u8, size: ?*usize, wcwidth_: ?*usize, plane_: Plane) error{ Stop, NoSpaceLeft }!?[]u8 {
|
||||
const Ctx = struct {
|
||||
col: usize = 0,
|
||||
sel: Selection,
|
||||
out: ?[]u8,
|
||||
bytes: usize = 0,
|
||||
wcwidth: usize = 0,
|
||||
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Walker {
|
||||
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Walker {
|
||||
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
|
||||
if (ctx.col < ctx.sel.begin.col) {
|
||||
ctx.col += wcwidth;
|
||||
|
@ -581,7 +579,7 @@ const Node = union(enum) {
|
|||
ctx.sel.normalize();
|
||||
if (sel.begin.eql(sel.end))
|
||||
return error.Stop;
|
||||
self.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx) catch |e| return switch (e) {
|
||||
self.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx, plane_) catch |e| return switch (e) {
|
||||
error.NoSpaceLeft => error.NoSpaceLeft,
|
||||
else => error.Stop,
|
||||
};
|
||||
|
@ -590,20 +588,20 @@ const Node = union(enum) {
|
|||
return if (copy_buf) |buf_| buf_[0..ctx.bytes] else null;
|
||||
}
|
||||
|
||||
pub fn delete_range(self: *const Node, sel: Selection, a: Allocator, size: ?*usize) error{Stop}!Root {
|
||||
pub fn delete_range(self: *const Node, sel: Selection, a: Allocator, size: ?*usize, plane: Plane) error{Stop}!Root {
|
||||
var wcwidth: usize = 0;
|
||||
_ = self.get_range(sel, null, size, &wcwidth) catch return error.Stop;
|
||||
return self.del_chars(sel.begin.row, sel.begin.col, wcwidth, a) catch return error.Stop;
|
||||
_ = self.get_range(sel, null, size, &wcwidth, plane) catch return error.Stop;
|
||||
return self.del_chars(sel.begin.row, sel.begin.col, wcwidth, a, plane) catch return error.Stop;
|
||||
}
|
||||
|
||||
pub fn del_chars(self: *const Node, line: usize, col: usize, count: usize, a: Allocator) !Root {
|
||||
pub fn del_chars(self: *const Node, line: usize, col: usize, count: usize, a: Allocator, plane_: Plane) !Root {
|
||||
const Ctx = struct {
|
||||
a: Allocator,
|
||||
col: usize,
|
||||
abs_col: usize = 0,
|
||||
count: usize,
|
||||
delete_next_bol: bool = false,
|
||||
fn walker(Ctx: *anyopaque, leaf: *const Leaf) WalkerMut {
|
||||
fn walker(Ctx: *anyopaque, leaf: *const Leaf, plane: Plane) WalkerMut {
|
||||
const ctx = @as(*@This(), @ptrCast(@alignCast(Ctx)));
|
||||
var result = WalkerMut.keep_walking;
|
||||
if (ctx.delete_next_bol and ctx.count == 0) {
|
||||
|
@ -612,7 +610,7 @@ const Node = union(enum) {
|
|||
ctx.delete_next_bol = false;
|
||||
return result;
|
||||
}
|
||||
const leaf_wcwidth = leaf.width(ctx.abs_col);
|
||||
const leaf_wcwidth = leaf.width(ctx.abs_col, plane);
|
||||
const leaf_bol = leaf.bol and !ctx.delete_next_bol;
|
||||
ctx.delete_next_bol = false;
|
||||
const base_col = ctx.abs_col;
|
||||
|
@ -636,7 +634,7 @@ const Node = union(enum) {
|
|||
result.replace = Leaf.new(ctx.a, "", leaf_bol, leaf.eol) catch |e| return .{ .err = e };
|
||||
ctx.count = 0;
|
||||
} else {
|
||||
const pos = leaf.width_to_pos(ctx.count, base_col) catch |e| return .{ .err = e };
|
||||
const pos = leaf.width_to_pos(ctx.count, base_col, plane) catch |e| return .{ .err = e };
|
||||
result.replace = Leaf.new(ctx.a, leaf.buf[pos..], leaf_bol, leaf.eol) catch |e| return .{ .err = e };
|
||||
ctx.count = 0;
|
||||
}
|
||||
|
@ -650,7 +648,7 @@ const Node = union(enum) {
|
|||
} else {
|
||||
if (ctx.col + ctx.count >= leaf_wcwidth) {
|
||||
ctx.count -= leaf_wcwidth - ctx.col;
|
||||
const pos = leaf.width_to_pos(ctx.col, base_col) catch |e| return .{ .err = e };
|
||||
const pos = leaf.width_to_pos(ctx.col, base_col, plane) catch |e| return .{ .err = e };
|
||||
const leaf_eol = if (leaf.eol and ctx.count > 0) leaf_eol: {
|
||||
ctx.count -= 1;
|
||||
ctx.delete_next_bol = true;
|
||||
|
@ -659,8 +657,8 @@ const Node = union(enum) {
|
|||
result.replace = Leaf.new(ctx.a, leaf.buf[0..pos], leaf_bol, leaf_eol) catch |e| return .{ .err = e };
|
||||
ctx.col = 0;
|
||||
} else {
|
||||
const pos = leaf.width_to_pos(ctx.col, base_col) catch |e| return .{ .err = e };
|
||||
const pos_end = leaf.width_to_pos(ctx.col + ctx.count, base_col) catch |e| return .{ .err = e };
|
||||
const pos = leaf.width_to_pos(ctx.col, base_col, plane) catch |e| return .{ .err = e };
|
||||
const pos_end = leaf.width_to_pos(ctx.col + ctx.count, base_col, plane) catch |e| return .{ .err = e };
|
||||
const left = Leaf.new(ctx.a, leaf.buf[0..pos], leaf_bol, false) catch |e| return .{ .err = e };
|
||||
const right = Leaf.new(ctx.a, leaf.buf[pos_end..], false, leaf.eol) catch |e| return .{ .err = e };
|
||||
result.replace = Node.new(ctx.a, left, right) catch |e| return .{ .err = e };
|
||||
|
@ -674,7 +672,7 @@ const Node = union(enum) {
|
|||
}
|
||||
};
|
||||
var ctx: Ctx = .{ .a = a, .col = col, .count = count };
|
||||
const found, const root = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx);
|
||||
const found, const root = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx, plane_);
|
||||
return if (found) (if (root) |r| r else error.Stop) else error.NotFound;
|
||||
}
|
||||
|
||||
|
@ -704,36 +702,43 @@ const Node = union(enum) {
|
|||
return if (!found) error.NotFound;
|
||||
}
|
||||
|
||||
pub fn line_width(self: *const Node, line: usize) !usize {
|
||||
pub fn line_width(self: *const Node, line: usize, plane_: Plane) !usize {
|
||||
const do = struct {
|
||||
result: usize = 0,
|
||||
fn walker(ctx: *anyopaque, leaf: *const Leaf) Walker {
|
||||
fn walker(ctx: *anyopaque, leaf: *const Leaf, plane: Plane) Walker {
|
||||
const do = @as(*@This(), @ptrCast(@alignCast(ctx)));
|
||||
do.result += leaf.width(do.result);
|
||||
do.result += leaf.width(do.result, plane);
|
||||
return if (!leaf.eol) Walker.keep_walking else Walker.stop;
|
||||
}
|
||||
};
|
||||
var ctx: do = .{};
|
||||
const found = self.walk_from_line_begin_const(line, do.walker, &ctx) catch true;
|
||||
const found = self.walk_from_line_begin_const(line, do.walker, &ctx, plane_) catch true;
|
||||
return if (found) ctx.result else error.NotFound;
|
||||
}
|
||||
|
||||
pub fn pos_to_width(self: *const Node, line: usize, pos: usize) !usize {
|
||||
pub fn pos_to_width(self: *const Node, line: usize, pos: usize, plane_: Plane) !usize {
|
||||
const do = struct {
|
||||
result: usize = 0,
|
||||
pos: usize,
|
||||
fn walker(ctx: *anyopaque, leaf: *const Leaf) Walker {
|
||||
fn walker(ctx: *anyopaque, leaf: *const Leaf, plane: Plane) Walker {
|
||||
const do = @as(*@This(), @ptrCast(@alignCast(ctx)));
|
||||
do.result += leaf.pos_to_width(&do.pos, do.result);
|
||||
do.result += leaf.pos_to_width(&do.pos, do.result, plane);
|
||||
return if (!(leaf.eol or do.pos == 0)) Walker.keep_walking else Walker.stop;
|
||||
}
|
||||
};
|
||||
var ctx: do = .{ .pos = pos };
|
||||
const found = self.walk_from_line_begin_const(line, do.walker, &ctx) catch true;
|
||||
const found = self.walk_from_line_begin_const(line, do.walker, &ctx, plane_) catch true;
|
||||
return if (found) ctx.result else error.NotFound;
|
||||
}
|
||||
|
||||
pub fn insert_chars(self_: *const Node, line_: usize, col_: usize, s: []const u8, a: Allocator) !struct { usize, usize, Root } {
|
||||
pub fn insert_chars(
|
||||
self_: *const Node,
|
||||
line_: usize,
|
||||
col_: usize,
|
||||
s: []const u8,
|
||||
a: Allocator,
|
||||
plane_: Plane,
|
||||
) !struct { usize, usize, Root } {
|
||||
var self = self_;
|
||||
const Ctx = struct {
|
||||
a: Allocator,
|
||||
|
@ -742,9 +747,9 @@ const Node = union(enum) {
|
|||
s: []const u8,
|
||||
eol: bool,
|
||||
|
||||
fn walker(ctx: *anyopaque, leaf: *const Leaf) WalkerMut {
|
||||
fn walker(ctx: *anyopaque, leaf: *const Leaf, plane: Plane) WalkerMut {
|
||||
const Ctx = @as(*@This(), @ptrCast(@alignCast(ctx)));
|
||||
const leaf_wcwidth = leaf.width(Ctx.abs_col);
|
||||
const leaf_wcwidth = leaf.width(Ctx.abs_col, plane);
|
||||
const base_col = Ctx.abs_col;
|
||||
Ctx.abs_col += leaf_wcwidth;
|
||||
|
||||
|
@ -776,7 +781,7 @@ const Node = union(enum) {
|
|||
}
|
||||
|
||||
if (leaf_wcwidth > Ctx.col) {
|
||||
const pos = leaf.width_to_pos(Ctx.col, base_col) catch |e| return .{ .err = e };
|
||||
const pos = leaf.width_to_pos(Ctx.col, base_col, plane) catch |e| return .{ .err = e };
|
||||
if (Ctx.eol and Ctx.s.len == 0) {
|
||||
const left = Leaf.new(Ctx.a, leaf.buf[0..pos], leaf.bol, Ctx.eol) catch |e| return .{ .err = e };
|
||||
const right = Leaf.new(Ctx.a, leaf.buf[pos..], Ctx.eol, leaf.eol) catch |e| return .{ .err = e };
|
||||
|
@ -813,14 +818,14 @@ const Node = union(enum) {
|
|||
need_eol = false;
|
||||
}
|
||||
var ctx: Ctx = .{ .a = a, .col = col, .s = chunk, .eol = need_eol };
|
||||
const found, const replace = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx);
|
||||
const found, const replace = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx, plane_);
|
||||
if (!found) return error.NotFound;
|
||||
if (replace) |root| self = root;
|
||||
if (need_eol) {
|
||||
line += 1;
|
||||
col = 0;
|
||||
} else {
|
||||
col += egc_chunk_width(chunk, col);
|
||||
col += plane_.egc_chunk_width(chunk, col);
|
||||
}
|
||||
}
|
||||
return .{ line, col, self };
|
||||
|
@ -913,19 +918,19 @@ const Node = union(enum) {
|
|||
return self.store(ctx.writer());
|
||||
}
|
||||
|
||||
pub fn debug_render_chunks(self: *const Node, line: usize, output: *ArrayList(u8)) !void {
|
||||
pub fn debug_render_chunks(self: *const Node, line: usize, output: *ArrayList(u8), plane_: Plane) !void {
|
||||
const ctx_ = struct {
|
||||
l: *ArrayList(u8),
|
||||
wcwidth: usize = 0,
|
||||
fn walker(ctx_: *anyopaque, leaf: *const Leaf) Walker {
|
||||
fn walker(ctx_: *anyopaque, leaf: *const Leaf, plane: Plane) Walker {
|
||||
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
|
||||
leaf.dump(ctx.l, ctx.wcwidth) catch |e| return .{ .err = e };
|
||||
ctx.wcwidth += leaf.width(ctx.wcwidth);
|
||||
leaf.dump(ctx.l, ctx.wcwidth, plane) catch |e| return .{ .err = e };
|
||||
ctx.wcwidth += leaf.width(ctx.wcwidth, plane);
|
||||
return if (!leaf.eol) Walker.keep_walking else Walker.stop;
|
||||
}
|
||||
};
|
||||
var ctx: ctx_ = .{ .l = output };
|
||||
const found = self.walk_from_line_begin_const(line, ctx_.walker, &ctx) catch true;
|
||||
const found = self.walk_from_line_begin_const(line, ctx_.walker, &ctx, plane_) catch true;
|
||||
if (!found) return error.NotFound;
|
||||
|
||||
var buf: [16]u8 = undefined;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const std = @import("std");
|
||||
const cbor = @import("cbor");
|
||||
const Plane = @import("renderer").Plane;
|
||||
const Buffer = @import("Buffer.zig");
|
||||
const View = @import("View.zig");
|
||||
const Selection = @import("Selection.zig");
|
||||
|
@ -26,20 +27,20 @@ pub inline fn right_of(self: Self, other: Self) bool {
|
|||
return if (self.row > other.row) true else if (self.row == other.row and self.col > other.col) true else false;
|
||||
}
|
||||
|
||||
pub fn clamp_to_buffer(self: *Self, root: Buffer.Root) void {
|
||||
pub fn clamp_to_buffer(self: *Self, root: Buffer.Root, plane: Plane) void {
|
||||
self.row = @min(self.row, root.lines() - 1);
|
||||
self.col = @min(self.col, root.line_width(self.row) catch 0);
|
||||
self.col = @min(self.col, root.line_width(self.row, plane) catch 0);
|
||||
}
|
||||
|
||||
fn follow_target(self: *Self, root: Buffer.Root) void {
|
||||
self.col = @min(self.target, root.line_width(self.row) catch 0);
|
||||
fn follow_target(self: *Self, root: Buffer.Root, plane: Plane) void {
|
||||
self.col = @min(self.target, root.line_width(self.row, plane) catch 0);
|
||||
}
|
||||
|
||||
fn move_right_no_target(self: *Self, root: Buffer.Root) !void {
|
||||
fn move_right_no_target(self: *Self, root: Buffer.Root, plane: Plane) !void {
|
||||
const lines = root.lines();
|
||||
if (lines <= self.row) return error.Stop;
|
||||
if (self.col < root.line_width(self.row) catch 0) {
|
||||
_, const wcwidth, const offset = root.ecg_at(self.row, self.col) catch return error.Stop;
|
||||
if (self.col < root.line_width(self.row, plane) catch 0) {
|
||||
_, const wcwidth, const offset = root.ecg_at(self.row, self.col, plane) catch return error.Stop;
|
||||
self.col += wcwidth - offset;
|
||||
} else if (self.row < lines - 1) {
|
||||
self.col = 0;
|
||||
|
@ -47,75 +48,75 @@ fn move_right_no_target(self: *Self, root: Buffer.Root) !void {
|
|||
} else return error.Stop;
|
||||
}
|
||||
|
||||
pub fn move_right(self: *Self, root: Buffer.Root) !void {
|
||||
try self.move_right_no_target(root);
|
||||
pub fn move_right(self: *Self, root: Buffer.Root, plane: Plane) !void {
|
||||
try self.move_right_no_target(root, plane);
|
||||
self.target = self.col;
|
||||
}
|
||||
|
||||
fn move_left_no_target(self: *Self, root: Buffer.Root) !void {
|
||||
fn move_left_no_target(self: *Self, root: Buffer.Root, plane: Plane) !void {
|
||||
if (self.col == 0) {
|
||||
if (self.row == 0) return error.Stop;
|
||||
self.row -= 1;
|
||||
self.col = root.line_width(self.row) catch 0;
|
||||
self.col = root.line_width(self.row, plane) catch 0;
|
||||
} else {
|
||||
_, const wcwidth, _ = root.ecg_at(self.row, self.col - 1) catch return error.Stop;
|
||||
_, const wcwidth, _ = root.ecg_at(self.row, self.col - 1, plane) catch return error.Stop;
|
||||
if (self.col > wcwidth) self.col -= wcwidth else self.col = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_left(self: *Self, root: Buffer.Root) !void {
|
||||
try self.move_left_no_target(root);
|
||||
pub fn move_left(self: *Self, root: Buffer.Root, plane: Plane) !void {
|
||||
try self.move_left_no_target(root, plane);
|
||||
self.target = self.col;
|
||||
}
|
||||
|
||||
pub fn move_up(self: *Self, root: Buffer.Root) !void {
|
||||
pub fn move_up(self: *Self, root: Buffer.Root, plane: Plane) !void {
|
||||
if (self.row > 0) {
|
||||
self.row -= 1;
|
||||
self.follow_target(root);
|
||||
self.move_left_no_target(root) catch return;
|
||||
try self.move_right_no_target(root);
|
||||
self.follow_target(root, plane);
|
||||
self.move_left_no_target(root, plane) catch return;
|
||||
try self.move_right_no_target(root, plane);
|
||||
} else return error.Stop;
|
||||
}
|
||||
|
||||
pub fn move_down(self: *Self, root: Buffer.Root) !void {
|
||||
pub fn move_down(self: *Self, root: Buffer.Root, plane: Plane) !void {
|
||||
if (self.row < root.lines() - 1) {
|
||||
self.row += 1;
|
||||
self.follow_target(root);
|
||||
self.move_left_no_target(root) catch return;
|
||||
try self.move_right_no_target(root);
|
||||
self.follow_target(root, plane);
|
||||
self.move_left_no_target(root, plane) catch return;
|
||||
try self.move_right_no_target(root, plane);
|
||||
} else return error.Stop;
|
||||
}
|
||||
|
||||
pub fn move_page_up(self: *Self, root: Buffer.Root, view: *const View) void {
|
||||
pub fn move_page_up(self: *Self, root: Buffer.Root, view: *const View, plane: Plane) void {
|
||||
self.row = if (self.row > view.rows) self.row - view.rows else 0;
|
||||
self.follow_target(root);
|
||||
self.move_left_no_target(root) catch return;
|
||||
self.move_right_no_target(root) catch return;
|
||||
self.follow_target(root, plane);
|
||||
self.move_left_no_target(root, plane) catch return;
|
||||
self.move_right_no_target(root, plane) catch return;
|
||||
}
|
||||
|
||||
pub fn move_page_down(self: *Self, root: Buffer.Root, view: *const View) void {
|
||||
pub fn move_page_down(self: *Self, root: Buffer.Root, view: *const View, plane: Plane) void {
|
||||
if (root.lines() < view.rows) {
|
||||
self.move_buffer_last(root);
|
||||
self.move_buffer_last(root, plane);
|
||||
} else if (self.row < root.lines() - view.rows - 1) {
|
||||
self.row += view.rows;
|
||||
} else self.row = root.lines() - 1;
|
||||
self.follow_target(root);
|
||||
self.move_left_no_target(root) catch return;
|
||||
self.move_right_no_target(root) catch return;
|
||||
self.follow_target(root, plane);
|
||||
self.move_left_no_target(root, plane) catch return;
|
||||
self.move_right_no_target(root, plane) catch return;
|
||||
}
|
||||
|
||||
pub fn move_to(self: *Self, root: Buffer.Root, row: usize, col: usize) !void {
|
||||
pub fn move_to(self: *Self, root: Buffer.Root, row: usize, col: usize, plane: Plane) !void {
|
||||
if (row < root.lines()) {
|
||||
self.row = row;
|
||||
self.col = @min(col, root.line_width(self.row) catch return error.Stop);
|
||||
self.col = @min(col, root.line_width(self.row, plane) catch return error.Stop);
|
||||
self.target = self.col;
|
||||
} else return error.Stop;
|
||||
}
|
||||
|
||||
pub fn move_abs(self: *Self, root: Buffer.Root, v: *View, y: usize, x: usize) !void {
|
||||
pub fn move_abs(self: *Self, root: Buffer.Root, v: *View, y: usize, x: usize, plane: Plane) !void {
|
||||
self.row = v.row + y;
|
||||
self.col = v.col + x;
|
||||
self.clamp_to_buffer(root);
|
||||
self.clamp_to_buffer(root, plane);
|
||||
self.target = self.col;
|
||||
}
|
||||
|
||||
|
@ -124,8 +125,8 @@ pub fn move_begin(self: *Self) void {
|
|||
self.target = self.col;
|
||||
}
|
||||
|
||||
pub fn move_end(self: *Self, root: Buffer.Root) void {
|
||||
if (self.row < root.lines()) self.col = root.line_width(self.row) catch 0;
|
||||
pub fn move_end(self: *Self, root: Buffer.Root, plane: Plane) void {
|
||||
if (self.row < root.lines()) self.col = root.line_width(self.row, plane) catch 0;
|
||||
self.target = std.math.maxInt(u32);
|
||||
}
|
||||
|
||||
|
@ -135,32 +136,32 @@ pub fn move_buffer_begin(self: *Self) void {
|
|||
self.target = 0;
|
||||
}
|
||||
|
||||
pub fn move_buffer_end(self: *Self, root: Buffer.Root) void {
|
||||
pub fn move_buffer_end(self: *Self, root: Buffer.Root, plane: Plane) void {
|
||||
self.row = root.lines() - 1;
|
||||
self.move_end(root);
|
||||
self.move_end(root, plane);
|
||||
if (self.col == 0) self.target = 0;
|
||||
}
|
||||
|
||||
fn move_buffer_first(self: *Self, root: Buffer.Root) void {
|
||||
fn move_buffer_first(self: *Self, root: Buffer.Root, plane: Plane) void {
|
||||
self.row = 0;
|
||||
self.follow_target(root);
|
||||
self.follow_target(root, plane);
|
||||
}
|
||||
|
||||
fn move_buffer_last(self: *Self, root: Buffer.Root) void {
|
||||
fn move_buffer_last(self: *Self, root: Buffer.Root, plane: Plane) void {
|
||||
self.row = root.lines() - 1;
|
||||
self.follow_target(root);
|
||||
self.follow_target(root, plane);
|
||||
}
|
||||
|
||||
fn is_at_begin(self: *const Self) bool {
|
||||
return self.col == 0;
|
||||
}
|
||||
|
||||
fn is_at_end(self: *const Self, root: Buffer.Root) bool {
|
||||
return if (self.row < root.lines()) self.col == root.line_width(self.row) catch 0 else true;
|
||||
fn is_at_end(self: *const Self, root: Buffer.Root, plane: Plane) bool {
|
||||
return if (self.row < root.lines()) self.col == root.line_width(self.row, plane) catch 0 else true;
|
||||
}
|
||||
|
||||
pub fn test_at(self: *const Self, root: Buffer.Root, pred: *const fn (c: []const u8) bool) bool {
|
||||
return root.test_at(pred, self.row, self.col);
|
||||
pub fn test_at(self: *const Self, root: Buffer.Root, pred: *const fn (c: []const u8) bool, plane: Plane) bool {
|
||||
return root.test_at(pred, self.row, self.col, plane);
|
||||
}
|
||||
|
||||
pub fn write(self: *const Self, writer: Buffer.MetaWriter) !void {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const std = @import("std");
|
||||
const Plane = @import("renderer").Plane;
|
||||
const Buffer = @import("Buffer.zig");
|
||||
const Cursor = @import("Cursor.zig");
|
||||
|
||||
|
@ -15,12 +16,12 @@ pub fn from_cursor(cursor: *const Cursor) Self {
|
|||
return .{ .begin = cursor.*, .end = cursor.* };
|
||||
}
|
||||
|
||||
pub fn line_from_cursor(cursor: Cursor, root: Buffer.Root) Self {
|
||||
pub fn line_from_cursor(cursor: Cursor, root: Buffer.Root, plane: Plane) Self {
|
||||
var begin = cursor;
|
||||
var end = cursor;
|
||||
begin.move_begin();
|
||||
end.move_end(root);
|
||||
end.move_right(root) catch {};
|
||||
end.move_end(root, plane);
|
||||
end.move_right(root, plane) catch {};
|
||||
return .{ .begin = begin, .end = end };
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = .{};
|
||||
};
|
|
@ -33,21 +33,21 @@ on_resize: *const fn (ctx: ?*anyopaque, self: *Self, pos_: Widget.Box) void = on
|
|||
pub fn createH(a: Allocator, parent: Widget, name: [:0]const u8, layout_: Layout) !*Self {
|
||||
const self: *Self = try a.create(Self);
|
||||
self.* = try init(a, parent, name, .horizontal, layout_, Box{});
|
||||
self.plane.move_bottom();
|
||||
self.plane.hide();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createV(a: Allocator, parent: Widget, name: [:0]const u8, layout_: Layout) !*Self {
|
||||
const self: *Self = try a.create(Self);
|
||||
self.* = try init(a, parent, name, .vertical, layout_, Box{});
|
||||
self.plane.move_bottom();
|
||||
self.plane.hide();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createBox(a: Allocator, parent: Widget, name: [:0]const u8, dir: Direction, layout_: Layout, box: Box) !*Self {
|
||||
const self: *Self = try a.create(Self);
|
||||
self.* = try init(a, parent, name, dir, layout_, box);
|
||||
self.plane.move_bottom();
|
||||
self.plane.hide();
|
||||
return self;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,18 @@
|
|||
const Plane = @import("renderer").Plane;
|
||||
const Style = @import("theme").Style;
|
||||
|
||||
pub fn print_string_large(n: Plane, s: []const u8) !void {
|
||||
pub fn print_string_large(n: *Plane, s: []const u8, style: Style) !void {
|
||||
for (s) |c|
|
||||
print_char_large(n, c) catch break;
|
||||
print_char_large(n, c, style) catch break;
|
||||
}
|
||||
|
||||
pub fn print_char_large(n: Plane, char: u8) !void {
|
||||
pub fn print_char_large(n: *Plane, char: u8, style: Style) !void {
|
||||
const bitmap = font8x8[char];
|
||||
for (0..8) |y| {
|
||||
for (0..8) |x| {
|
||||
const set = bitmap[y] & @as(usize, 1) << @intCast(x);
|
||||
if (set != 0) {
|
||||
_ = try n.putstr("█");
|
||||
try write_cell(n, "█", style);
|
||||
} else {
|
||||
n.cursor_move_rel(0, 1) catch {};
|
||||
}
|
||||
|
@ -21,14 +22,14 @@ pub fn print_char_large(n: Plane, char: u8) !void {
|
|||
n.cursor_move_rel(-8, 8) catch {};
|
||||
}
|
||||
|
||||
pub fn print_string_medium(n: Plane, s: []const u8) !void {
|
||||
pub fn print_string_medium(n: *Plane, s: []const u8, style: Style) !void {
|
||||
for (s) |c|
|
||||
print_char_medium(n, c) catch break;
|
||||
print_char_medium(n, c, style) catch break;
|
||||
}
|
||||
|
||||
const QUADBLOCKS = [_][:0]const u8{ " ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█" };
|
||||
|
||||
pub fn print_char_medium(n: Plane, char: u8) !void {
|
||||
pub fn print_char_medium(n: *Plane, char: u8, style: Style) !void {
|
||||
const bitmap = font8x8[char];
|
||||
for (0..4) |y| {
|
||||
for (0..4) |x| {
|
||||
|
@ -43,7 +44,7 @@ pub fn print_char_medium(n: Plane, char: u8) !void {
|
|||
const quadidx: u4 = setbr | setbl | settr | settl;
|
||||
const c = QUADBLOCKS[quadidx];
|
||||
if (quadidx != 0) {
|
||||
_ = try n.putstr(c);
|
||||
try write_cell(n, c, style);
|
||||
} else {
|
||||
n.cursor_move_rel(0, 1) catch {};
|
||||
}
|
||||
|
@ -53,6 +54,14 @@ pub fn print_char_medium(n: Plane, char: u8) !void {
|
|||
n.cursor_move_rel(-4, 4) catch {};
|
||||
}
|
||||
|
||||
fn write_cell(n: *Plane, egc: [:0]const u8, style: Style) !void {
|
||||
var cell = n.cell_init();
|
||||
_ = n.at_cursor_cell(&cell) catch return;
|
||||
_ = n.cell_load(&cell, egc) catch {};
|
||||
cell.set_style_fg(style);
|
||||
_ = n.putc(&cell) catch {};
|
||||
}
|
||||
|
||||
pub const font8x8: [128][8]u8 = [128][8]u8{
|
||||
[8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
[8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
|
|
|
@ -15,7 +15,6 @@ const command = @import("command.zig");
|
|||
const fonts = @import("fonts.zig");
|
||||
|
||||
a: std.mem.Allocator,
|
||||
background: Plane,
|
||||
plane: Plane,
|
||||
parent: Plane,
|
||||
fire: ?Fire = null,
|
||||
|
@ -26,8 +25,6 @@ const Self = @This();
|
|||
|
||||
pub fn create(a: std.mem.Allocator, parent: Widget) !Widget {
|
||||
const self: *Self = try a.create(Self);
|
||||
var background = try Plane.init(&(Widget.Box{}).opts("background"), parent.plane.*);
|
||||
errdefer background.deinit();
|
||||
var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
|
||||
errdefer n.deinit();
|
||||
|
||||
|
@ -35,7 +32,6 @@ pub fn create(a: std.mem.Allocator, parent: Widget) !Widget {
|
|||
self.* = .{
|
||||
.a = a,
|
||||
.parent = parent.plane.*,
|
||||
.background = background,
|
||||
.plane = n,
|
||||
.menu = try Menu.create(*Self, a, w, .{ .ctx = self, .on_render = menu_on_render }),
|
||||
};
|
||||
|
@ -56,7 +52,6 @@ pub fn deinit(self: *Self, a: std.mem.Allocator) void {
|
|||
self.menu.deinit(a);
|
||||
self.commands.deinit();
|
||||
self.plane.deinit();
|
||||
self.background.deinit();
|
||||
if (self.fire) |*fire| fire.deinit();
|
||||
a.destroy(self);
|
||||
}
|
||||
|
@ -130,16 +125,13 @@ fn menu_action_quit(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))
|
|||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
const more = self.menu.render(theme);
|
||||
|
||||
self.background.set_base_style(" ", theme.editor);
|
||||
self.background.erase();
|
||||
self.background.home();
|
||||
self.plane.set_base_style_transparent("", theme.editor);
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
if (self.fire) |*fire| fire.render();
|
||||
|
||||
const more = self.menu.render(theme);
|
||||
|
||||
const style_title = if (tui.find_scope_style(theme, "function")) |sty| sty.style else theme.editor;
|
||||
const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor;
|
||||
|
||||
|
@ -147,19 +139,16 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
|||
const subtext = "a programmer's text editor";
|
||||
|
||||
if (self.plane.dim_x() > 120 and self.plane.dim_y() > 22) {
|
||||
self.plane.set_style_bg_transparent(style_title);
|
||||
self.plane.cursor_move_yx(2, 4) catch return more;
|
||||
fonts.print_string_large(self.plane, title) catch return more;
|
||||
fonts.print_string_large(&self.plane, title, style_title) catch return more;
|
||||
|
||||
self.plane.set_style_bg_transparent(style_subtext);
|
||||
self.plane.cursor_move_yx(10, 8) catch return more;
|
||||
fonts.print_string_medium(self.plane, subtext) catch return more;
|
||||
fonts.print_string_medium(&self.plane, subtext, style_subtext) catch return more;
|
||||
|
||||
self.menu.resize(.{ .y = 15, .x = 10, .w = 32 });
|
||||
} else if (self.plane.dim_x() > 55 and self.plane.dim_y() > 16) {
|
||||
self.plane.set_style_bg_transparent(style_title);
|
||||
self.plane.cursor_move_yx(2, 4) catch return more;
|
||||
fonts.print_string_medium(self.plane, title) catch return more;
|
||||
fonts.print_string_medium(&self.plane, title, style_title) catch return more;
|
||||
|
||||
self.plane.set_style_bg_transparent(style_subtext);
|
||||
self.plane.cursor_move_yx(7, 6) catch return more;
|
||||
|
@ -181,13 +170,11 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
|||
}
|
||||
|
||||
pub fn handle_resize(self: *Self, pos: Widget.Box) void {
|
||||
self.background.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return;
|
||||
self.background.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return;
|
||||
self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return;
|
||||
self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return;
|
||||
if (self.fire) |*fire| {
|
||||
fire.deinit();
|
||||
self.fire = Fire.init(self.a, self.plane, pos) catch return;
|
||||
self.fire = Fire.init(self.a, self.plane) catch return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +200,7 @@ const cmds = struct {
|
|||
self.fire = if (self.fire) |*fire| ret: {
|
||||
fire.deinit();
|
||||
break :ret null;
|
||||
} else Fire.init(self.a, self.background, Widget.Box.from(self.background)) catch |e| return tp.exit_error(e);
|
||||
} else Fire.init(self.a, self.plane) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -239,7 +226,8 @@ const Fire = struct {
|
|||
const MAX_COLOR = 256;
|
||||
const LAST_COLOR = MAX_COLOR - 1;
|
||||
|
||||
fn init(a: std.mem.Allocator, plane: Plane, pos: Widget.Box) !Fire {
|
||||
fn init(a: std.mem.Allocator, plane: Plane) !Fire {
|
||||
const pos = Widget.Box.from(plane);
|
||||
const FIRE_H = @as(u16, @intCast(pos.h)) * 2;
|
||||
const FIRE_W = @as(u16, @intCast(pos.w));
|
||||
var self: Fire = .{
|
||||
|
|
|
@ -99,11 +99,11 @@ fn inspect_location(self: *Self, row: usize, col: usize) void {
|
|||
|
||||
fn get_buffer_text(self: *Self, buf: []u8, sel: Buffer.Selection) ?[]const u8 {
|
||||
const root = self.editor.get_current_root() orelse return null;
|
||||
return root.get_range(sel, buf, null, null) catch return null;
|
||||
return root.get_range(sel, buf, null, null, self.plane) catch return null;
|
||||
}
|
||||
|
||||
fn dump_highlight(self: *Self, range: syntax.Range, scope: []const u8, id: u32, _: usize, ast_node: *const syntax.Node) error{Stop}!void {
|
||||
const sel = self.pos_cache.range_to_selection(range, self.editor.get_current_root() orelse return) orelse return;
|
||||
const sel = self.pos_cache.range_to_selection(range, self.editor.get_current_root() orelse return, self.plane) orelse return;
|
||||
if (self.need_clear) {
|
||||
self.need_clear = false;
|
||||
self.clear();
|
||||
|
@ -138,7 +138,7 @@ fn dump_highlight(self: *Self, range: syntax.Range, scope: []const u8, id: u32,
|
|||
const ast_parent = parent.asSExpressionString();
|
||||
_ = self.plane.print("parent: {s}\n", .{ast_parent}) catch {};
|
||||
syntax.Node.freeSExpressionString(ast_parent);
|
||||
const sel_parent = self.pos_cache.range_to_selection(parent.getRange(), self.editor.get_current_root() orelse return) orelse return;
|
||||
const sel_parent = self.pos_cache.range_to_selection(parent.getRange(), self.editor.get_current_root() orelse return, self.plane) orelse return;
|
||||
var match_parent = ed.Match.from_selection(sel_parent);
|
||||
if (self.theme) |theme| match_parent.style = .{ .bg = theme.editor_gutter_added.fg };
|
||||
switch (update_match) {
|
||||
|
|
|
@ -4,7 +4,7 @@ const root = @import("root");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -250,7 +250,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const root = @import("root");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../../tui.zig");
|
||||
const command = @import("../../../command.zig");
|
||||
|
@ -251,7 +251,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const root = @import("root");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../../tui.zig");
|
||||
const command = @import("../../../command.zig");
|
||||
|
@ -453,7 +453,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const root = @import("root");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../../tui.zig");
|
||||
const command = @import("../../../command.zig");
|
||||
|
@ -411,7 +411,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const tp = @import("thespian");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
|
@ -161,7 +161,7 @@ fn mapRelease(self: *Self, keypress: u32, _: u32, _: u32) tp.result {
|
|||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
if (self.input.len + 16 > self.buf.len)
|
||||
try self.flush_input();
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
self.input = self.buf[0 .. self.input.len + bytes];
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const tp = @import("thespian");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
|
@ -158,7 +158,7 @@ fn mapRelease(self: *Self, keypress: u32, _: u32, _: u32) tp.result {
|
|||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
if (self.input.len + 16 > self.buf.len)
|
||||
try self.flush_input();
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
self.input = self.buf[0 .. self.input.len + bytes];
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const tp = @import("thespian");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
|
@ -114,7 +114,7 @@ fn execute_operation(self: *Self, c: u32) void {
|
|||
},
|
||||
};
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch return;
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch return;
|
||||
command.executeName(cmd, command.fmt(.{buf[0..bytes]})) catch {};
|
||||
command.executeName("exit_mini_mode", .{}) catch {};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ const tp = @import("thespian");
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
|
@ -125,7 +125,7 @@ fn mapRelease(_: *Self, _: u32, _: u32, _: u32) tp.result {}
|
|||
|
||||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
var buf: [32]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.file_path.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ const planeutils = @import("renderer").planeutils;
|
|||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -89,13 +89,13 @@ fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c
|
|||
var len = cbor.decodeArrayHeader(&iter) catch return false;
|
||||
while (len > 0) : (len -= 1) {
|
||||
if (cbor.matchValue(&iter, cbor.extract(&index)) catch break) {
|
||||
render_cell(button.plane, 0, index + 1, theme.editor_match) catch break;
|
||||
render_cell(&button.plane, 0, index + 1, theme.editor_match) catch break;
|
||||
} else break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn render_cell(plane: Plane, y: usize, x: usize, style: Widget.Theme.Style) !void {
|
||||
fn render_cell(plane: *Plane, y: usize, x: usize, style: Widget.Theme.Style) !void {
|
||||
plane.cursor_move_yx(@intCast(y), @intCast(x)) catch return;
|
||||
var cell = plane.cell_init();
|
||||
_ = plane.at_cursor_cell(&cell) catch return;
|
||||
|
@ -313,7 +313,7 @@ fn delete_code_point(self: *Self) tp.result {
|
|||
|
||||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.inputbox.text.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
self.inputbox.cursor = self.inputbox.text.items.len;
|
||||
return self.start_query();
|
||||
|
|
|
@ -123,7 +123,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
|||
defer frame.deinit();
|
||||
self.plane.set_base_style(" ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar);
|
||||
self.plane.erase();
|
||||
smooth_bar_at(self.plane, @intCast(self.pos_scrn), @intCast(self.view_scrn)) catch {};
|
||||
smooth_bar_at(&self.plane, @intCast(self.pos_scrn), @intCast(self.view_scrn)) catch {};
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ const eighths_b = [_][]const u8{ "█", "▇", "▆", "▅", "▄", "▃", "▂"
|
|||
const eighths_t = [_][]const u8{ " ", "▔", "🮂", "🮃", "▀", "🮄", "🮅", "🮆" };
|
||||
const eighths_c: i32 = @intCast(eighths_b.len);
|
||||
|
||||
fn smooth_bar_at(plane: Plane, pos_: i32, size_: i32) !void {
|
||||
fn smooth_bar_at(plane: *Plane, pos_: i32, size_: i32) !void {
|
||||
const height: i32 = @intCast(plane.dim_y());
|
||||
var size = @max(size_, 8);
|
||||
const pos: i32 = @min(height * eighths_c - size, pos_);
|
||||
|
|
|
@ -195,7 +195,7 @@ fn animate(self: *Self) bool {
|
|||
else
|
||||
frame;
|
||||
|
||||
smooth_block_at(self.plane, pos);
|
||||
smooth_block_at(&self.plane, pos);
|
||||
return false;
|
||||
// return pos != 0;
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ const eighths_l = [_][]const u8{ "█", "▉", "▊", "▋", "▌", "▍", "▎"
|
|||
const eighths_r = [_][]const u8{ " ", "▕", "🮇", "🮈", "▐", "🮉", "🮊", "🮋" };
|
||||
const eighths_c = eighths_l.len;
|
||||
|
||||
fn smooth_block_at(plane: Plane, pos: u64) void {
|
||||
fn smooth_block_at(plane: *Plane, pos: u64) void {
|
||||
const blk = @mod(pos, eighths_c) + 1;
|
||||
const l = eighths_l[eighths_c - blk];
|
||||
const r = eighths_r[eighths_c - blk];
|
||||
|
|
|
@ -3,7 +3,6 @@ const Allocator = std.mem.Allocator;
|
|||
const tp = @import("thespian");
|
||||
const tracy = @import("tracy");
|
||||
const root = @import("root");
|
||||
const egc = @import("renderer").egc;
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
const style = @import("renderer").style;
|
||||
|
@ -25,9 +24,9 @@ pub fn create(a: Allocator, parent: Plane) !Widget {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn layout(_: *void, _: *Button.State(void)) Widget.Layout {
|
||||
pub fn layout(_: *void, btn: *Button.State(void)) Widget.Layout {
|
||||
const name = tui.get_mode();
|
||||
const width = egc.chunk_width(name, 0);
|
||||
const width = btn.plane.egc_chunk_width(name, 0);
|
||||
const padding: usize = if (is_mini_mode()) 3 else 2;
|
||||
return .{ .static = width + padding };
|
||||
}
|
||||
|
|
|
@ -93,13 +93,6 @@ fn init(a: Allocator) !*Self {
|
|||
const frame_time = std.time.us_per_s / conf.frame_rate;
|
||||
const frame_clock = try tp.metronome.init(frame_time);
|
||||
|
||||
const fd_stdin = try tp.file_descriptor.init("stdin", ctx.input_fd());
|
||||
// const fd_stdin = try tp.file_descriptor.init("stdin", std.os.STDIN_FILENO);
|
||||
const n = ctx.stdplane();
|
||||
|
||||
try frame_clock.start();
|
||||
try fd_stdin.wait_read();
|
||||
|
||||
self.* = .{
|
||||
.a = a,
|
||||
.config = conf,
|
||||
|
@ -107,7 +100,7 @@ fn init(a: Allocator) !*Self {
|
|||
.frame_time = frame_time,
|
||||
.frame_clock = frame_clock,
|
||||
.frame_clock_running = true,
|
||||
.fd_stdin = fd_stdin,
|
||||
.fd_stdin = undefined,
|
||||
.receiver = Receiver.init(receive, self),
|
||||
.mainview = undefined,
|
||||
.message_filters = MessageFilter.List.init(a),
|
||||
|
@ -120,6 +113,15 @@ fn init(a: Allocator) !*Self {
|
|||
};
|
||||
instance_ = self;
|
||||
defer instance_ = null;
|
||||
|
||||
try self.rdr.run();
|
||||
self.fd_stdin = try tp.file_descriptor.init("stdin", self.rdr.input_fd());
|
||||
// self.fd_stdin = try tp.file_descriptor.init("stdin", std.os.STDIN_FILENO);
|
||||
const n = self.rdr.stdplane();
|
||||
|
||||
try frame_clock.start();
|
||||
try self.fd_stdin.wait_read();
|
||||
|
||||
self.rdr.handler_ctx = self;
|
||||
self.rdr.dispatch_input = dispatch_input;
|
||||
self.rdr.dispatch_mouse = dispatch_mouse;
|
||||
|
@ -129,7 +131,7 @@ fn init(a: Allocator) !*Self {
|
|||
errdefer self.deinit();
|
||||
try self.listen_sigwinch();
|
||||
self.mainview = try mainview.create(a, n);
|
||||
try ctx.render();
|
||||
try self.rdr.render();
|
||||
try self.save_config();
|
||||
if (tp.env.get().is("restore-session")) {
|
||||
command.executeName("restore_session", .{}) catch |e| self.logger.err("restore_session", e);
|
||||
|
@ -305,7 +307,7 @@ fn render(self: *Self, current_time: i64) void {
|
|||
};
|
||||
|
||||
{
|
||||
const frame = tracy.initZone(@src(), .{ .name = "notcurses render" });
|
||||
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
|
||||
defer frame.deinit();
|
||||
self.rdr.render() catch |e| self.logger.err("render", e);
|
||||
}
|
||||
|
@ -342,7 +344,10 @@ fn dispatch_input_fd(self: *Self, m: tp.message) error{Exit}!bool {
|
|||
var err_msg: []u8 = "";
|
||||
if (try m.match(.{ "fd", "stdin", "read_ready" })) {
|
||||
self.fd_stdin.wait_read() catch |e| return tp.exit_error(e);
|
||||
self.rdr.process_input() catch |e| return tp.exit_error(e);
|
||||
self.rdr.process_input() catch |e| switch (e) {
|
||||
error.WouldBlock => return true,
|
||||
else => return tp.exit_error(e),
|
||||
};
|
||||
try self.dispatch_flush_input_event();
|
||||
if (self.unrendered_input_events_count > 0 and !self.frame_clock_running)
|
||||
need_render();
|
||||
|
@ -512,18 +517,17 @@ const cmds = struct {
|
|||
const l = log.logger("z stack");
|
||||
defer l.deinit();
|
||||
var buf: [256]u8 = undefined;
|
||||
var buf_parent: [256]u8 = undefined;
|
||||
var z: i32 = 0;
|
||||
var n = self.rdr.stdplane();
|
||||
while (n.below()) |n_| : (n = n_) {
|
||||
z -= 1;
|
||||
l.print("{d} {s} {s}", .{ z, n_.name(&buf), n_.parent().name(&buf_parent) });
|
||||
l.print("{d} {s}", .{ z, n_.name(&buf) });
|
||||
}
|
||||
z = 0;
|
||||
n = self.rdr.stdplane();
|
||||
while (n.above()) |n_| : (n = n_) {
|
||||
z += 1;
|
||||
l.print("{d} {s} {s}", .{ z, n_.name(&buf), n_.parent().name(&buf_parent) });
|
||||
l.print("{d} {s}", .{ z, n_.name(&buf) });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue