feat: add libvaxis renderer

This commit is contained in:
CJ van den Berg 2024-05-01 22:03:33 +02:00
parent b15fa47f30
commit 1cd3cb17ce
32 changed files with 1559 additions and 516 deletions

View file

@ -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,

View file

@ -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",

View file

@ -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;

View file

@ -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 {

View file

@ -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 };
}

View file

@ -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;
}
};

View file

@ -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;

View file

@ -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;

View file

@ -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;

View 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;
}

View 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];
}
};

View 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 => "",
};
}
};

View 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));
}

View 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 = .{};
};

View file

@ -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

View file

@ -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 },

View file

@ -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 = .{

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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];
}

View file

@ -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];
}

View file

@ -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 {};
}

View file

@ -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);
}

View file

@ -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();

View file

@ -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_);

View file

@ -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];

View file

@ -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 };
}

View file

@ -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) });
}
}