refactor: zigify imports and correct minor mistakes
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
@@ -36,7 +34,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
// library
|
// library
|
||||||
const lib = b.addModule("zterm", .{
|
const lib = b.addModule("zterm", .{
|
||||||
.root_source_file = b.path("src/zterm.zig"),
|
.root_source_file = b.path("src/root.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
@@ -184,7 +182,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
// zig build test
|
// zig build test
|
||||||
const lib_unit_tests = b.addTest(.{
|
const lib_unit_tests = b.addTest(.{
|
||||||
.root_source_file = b.path("src/zterm.zig"),
|
.root_source_file = b.path("src/root.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
@@ -196,3 +194,5 @@ pub fn build(b: *std.Build) void {
|
|||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&run_lib_unit_tests.step);
|
test_step.dependOn(&run_lib_unit_tests.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
// This is a [Semantic Version](https://semver.org/).
|
// This is a [Semantic Version](https://semver.org/).
|
||||||
// In a future version of Zig it will be used for package deduplication.
|
// In a future version of Zig it will be used for package deduplication.
|
||||||
.version = "0.2.0",
|
.version = "0.3.0",
|
||||||
|
|
||||||
// Tracks the earliest Zig version that the package considers to be a
|
// Tracks the earliest Zig version that the package considers to be a
|
||||||
// supported use case.
|
// supported use case.
|
||||||
|
|||||||
@@ -1,11 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const input = zterm.input;
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit. Press ctrl+n to launch helix.";
|
const text = "Press ctrl+c to quit. Press ctrl+n to launch helix.";
|
||||||
|
|
||||||
@@ -158,6 +150,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
if (key.eql(.{ .cp = 'n', .mod = .{ .ctrl = true } })) {
|
if (key.eql(.{ .cp = 'n', .mod = .{ .ctrl = true } })) {
|
||||||
try app.interrupt();
|
try app.interrupt();
|
||||||
|
renderer.size = .{}; // reset size, such that next resize will cause a full re-draw!
|
||||||
defer app.start() catch @panic("could not start app event loop");
|
defer app.start() catch @panic("could not start app event loop");
|
||||||
var child = std.process.Child.init(&.{"hx"}, allocator);
|
var child = std.process.Child.init(&.{"hx"}, allocator);
|
||||||
_ = child.spawnAndWait() catch |err| app.postEvent(.{
|
_ = child.spawnAndWait() catch |err| app.postEvent(.{
|
||||||
@@ -188,10 +181,16 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const input = zterm.input;
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {
|
|
||||||
click: [:0]const u8,
|
|
||||||
});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -139,10 +130,17 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {
|
||||||
|
click: [:0]const u8,
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {
|
|
||||||
accept: []u21,
|
|
||||||
});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -219,10 +210,17 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {
|
||||||
|
accept: []u21,
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,11 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const input = zterm.input;
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -190,10 +182,16 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const input = zterm.input;
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -150,10 +143,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -106,10 +99,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -98,10 +91,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -114,10 +107,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -97,10 +90,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -93,10 +86,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const zterm = @import("zterm");
|
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {});
|
|
||||||
|
|
||||||
const log = std.log.scoped(.default);
|
|
||||||
|
|
||||||
const QuitText = struct {
|
const QuitText = struct {
|
||||||
const text = "Press ctrl+c to quit.";
|
const text = "Press ctrl+c to quit.";
|
||||||
|
|
||||||
@@ -148,10 +141,15 @@ pub fn main() !void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try renderer.resize();
|
container.resize(try renderer.resize());
|
||||||
container.resize(renderer.size);
|
|
||||||
container.reposition(.{});
|
container.reposition(.{});
|
||||||
try renderer.render(@TypeOf(container), &container);
|
try renderer.render(@TypeOf(container), &container);
|
||||||
try renderer.flush();
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.default);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const zterm = @import("zterm");
|
||||||
|
const App = zterm.App(union(enum) {});
|
||||||
|
|||||||
81
src/app.zig
81
src/app.zig
@@ -1,19 +1,4 @@
|
|||||||
//! Application type for TUI-applications
|
//! Application type for TUI-applications
|
||||||
const std = @import("std");
|
|
||||||
const code_point = @import("code_point");
|
|
||||||
const event = @import("event.zig");
|
|
||||||
const input = @import("input.zig");
|
|
||||||
const terminal = @import("terminal.zig");
|
|
||||||
const queue = @import("queue.zig");
|
|
||||||
|
|
||||||
const mergeTaggedUnions = event.mergeTaggedUnions;
|
|
||||||
const isTaggedUnion = event.isTaggedUnion;
|
|
||||||
|
|
||||||
const Mouse = input.Mouse;
|
|
||||||
const Key = input.Key;
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.app);
|
|
||||||
|
|
||||||
/// Create the App Type with the associated user events _E_ which describes
|
/// Create the App Type with the associated user events _E_ which describes
|
||||||
/// an tagged union for all the user events that can be send through the
|
/// an tagged union for all the user events that can be send through the
|
||||||
@@ -39,18 +24,10 @@ pub fn App(comptime E: type) type {
|
|||||||
@compileError("Provided user event `E` for `App(comptime E: type)` is not of type `union(enum)`.");
|
@compileError("Provided user event `E` for `App(comptime E: type)` is not of type `union(enum)`.");
|
||||||
}
|
}
|
||||||
return struct {
|
return struct {
|
||||||
pub const Event = mergeTaggedUnions(event.SystemEvent, E);
|
|
||||||
pub const Container = @import("container.zig").Container(Event);
|
|
||||||
const element = @import("element.zig");
|
|
||||||
pub const Element = element.Element(Event);
|
|
||||||
pub const Scrollable = element.Scrollable(Event);
|
|
||||||
pub const Exec = element.Exec(Event, Queue);
|
|
||||||
pub const Queue = queue.Queue(Event, 256);
|
|
||||||
|
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
thread: ?std.Thread = null,
|
thread: ?Thread = null,
|
||||||
quit_event: std.Thread.ResetEvent,
|
quit_event: Thread.ResetEvent,
|
||||||
termios: ?std.posix.termios = null,
|
termios: ?posix.termios = null,
|
||||||
|
|
||||||
pub const SignalHandler = struct {
|
pub const SignalHandler = struct {
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
@@ -69,9 +46,9 @@ pub fn App(comptime E: type) type {
|
|||||||
this.postEvent(.init);
|
this.postEvent(.init);
|
||||||
|
|
||||||
this.quit_event.reset();
|
this.quit_event.reset();
|
||||||
this.thread = try std.Thread.spawn(.{}, @This().run, .{this});
|
this.thread = try Thread.spawn(.{}, @This().run, .{this});
|
||||||
|
|
||||||
var termios: std.posix.termios = undefined;
|
var termios: posix.termios = undefined;
|
||||||
try terminal.enableRawMode(&termios);
|
try terminal.enableRawMode(&termios);
|
||||||
if (this.termios) |_| {} else this.termios = termios;
|
if (this.termios) |_| {} else this.termios = termios;
|
||||||
|
|
||||||
@@ -192,9 +169,9 @@ pub fn App(comptime E: type) type {
|
|||||||
// CSI number ~
|
// CSI number ~
|
||||||
// CSI number ; modifier ~
|
// CSI number ; modifier ~
|
||||||
// CSI number ; modifier:event_type ; text_as_codepoint ~
|
// CSI number ; modifier:event_type ; text_as_codepoint ~
|
||||||
var field_iter = std.mem.splitScalar(u8, sequence[2 .. sequence.len - 1], ';');
|
var field_iter = mem.splitScalar(u8, sequence[2 .. sequence.len - 1], ';');
|
||||||
const number_buf = field_iter.next() orelse unreachable; // always will have one field
|
const number_buf = field_iter.next() orelse unreachable; // always will have one field
|
||||||
const number = std.fmt.parseUnsigned(u16, number_buf, 10) catch break;
|
const number = fmt.parseUnsigned(u16, number_buf, 10) catch break;
|
||||||
|
|
||||||
const key: Key = .{
|
const key: Key = .{
|
||||||
.cp = switch (number) {
|
.cp = switch (number) {
|
||||||
@@ -231,11 +208,11 @@ pub fn App(comptime E: type) type {
|
|||||||
std.debug.assert(sequence.len >= 4);
|
std.debug.assert(sequence.len >= 4);
|
||||||
if (sequence[2] != '<') break;
|
if (sequence[2] != '<') break;
|
||||||
|
|
||||||
const delim1 = std.mem.indexOfScalarPos(u8, sequence, 3, ';') orelse break;
|
const delim1 = mem.indexOfScalarPos(u8, sequence, 3, ';') orelse break;
|
||||||
const button_mask = std.fmt.parseUnsigned(u16, sequence[3..delim1], 10) catch break;
|
const button_mask = fmt.parseUnsigned(u16, sequence[3..delim1], 10) catch break;
|
||||||
const delim2 = std.mem.indexOfScalarPos(u8, sequence, delim1 + 1, ';') orelse break;
|
const delim2 = mem.indexOfScalarPos(u8, sequence, delim1 + 1, ';') orelse break;
|
||||||
const px = std.fmt.parseUnsigned(u16, sequence[delim1 + 1 .. delim2], 10) catch break;
|
const px = fmt.parseUnsigned(u16, sequence[delim1 + 1 .. delim2], 10) catch break;
|
||||||
const py = std.fmt.parseUnsigned(u16, sequence[delim2 + 1 .. sequence.len - 1], 10) catch break;
|
const py = fmt.parseUnsigned(u16, sequence[delim2 + 1 .. sequence.len - 1], 10) catch break;
|
||||||
|
|
||||||
const mouse_bits = packed struct {
|
const mouse_bits = packed struct {
|
||||||
const motion: u8 = 0b00100000;
|
const motion: u8 = 0b00100000;
|
||||||
@@ -280,9 +257,9 @@ pub fn App(comptime E: type) type {
|
|||||||
't' => {
|
't' => {
|
||||||
// XTWINOPS
|
// XTWINOPS
|
||||||
// Split first into fields delimited by ';'
|
// Split first into fields delimited by ';'
|
||||||
var iter = std.mem.splitScalar(u8, sequence[2 .. sequence.len - 1], ';');
|
var iter = mem.splitScalar(u8, sequence[2 .. sequence.len - 1], ';');
|
||||||
const ps = iter.first();
|
const ps = iter.first();
|
||||||
if (std.mem.eql(u8, "48", ps)) {
|
if (mem.eql(u8, "48", ps)) {
|
||||||
// in band window resize
|
// in band window resize
|
||||||
// CSI 48 ; height ; width ; height_pix ; width_pix t
|
// CSI 48 ; height ; width ; height_pix ; width_pix t
|
||||||
const width_char = iter.next() orelse break;
|
const width_char = iter.next() orelse break;
|
||||||
@@ -291,8 +268,8 @@ pub fn App(comptime E: type) type {
|
|||||||
_ = width_char;
|
_ = width_char;
|
||||||
_ = height_char;
|
_ = height_char;
|
||||||
// this.postEvent(.{ .size = .{
|
// this.postEvent(.{ .size = .{
|
||||||
// .x = std.fmt.parseUnsigned(u16, width_char, 10) catch break,
|
// .x = fmt.parseUnsigned(u16, width_char, 10) catch break,
|
||||||
// .y = std.fmt.parseUnsigned(u16, height_char, 10) catch break,
|
// .y = fmt.parseUnsigned(u16, height_char, 10) catch break,
|
||||||
// } });
|
// } });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -338,5 +315,31 @@ pub fn App(comptime E: type) type {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const element = @import("element.zig");
|
||||||
|
pub const Event = mergeTaggedUnions(event.SystemEvent, E);
|
||||||
|
pub const Container = @import("container.zig").Container(Event);
|
||||||
|
pub const Element = element.Element(Event);
|
||||||
|
pub const Scrollable = element.Scrollable(Event);
|
||||||
|
pub const Exec = element.Exec(Event, Queue);
|
||||||
|
pub const Queue = queue.Queue(Event, 256);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.app);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const mem = std.mem;
|
||||||
|
const fmt = std.fmt;
|
||||||
|
const posix = std.posix;
|
||||||
|
const Thread = std.Thread;
|
||||||
|
const code_point = @import("code_point");
|
||||||
|
const event = @import("event.zig");
|
||||||
|
const input = @import("input.zig");
|
||||||
|
const terminal = @import("terminal.zig");
|
||||||
|
const queue = @import("queue.zig");
|
||||||
|
const mergeTaggedUnions = event.mergeTaggedUnions;
|
||||||
|
const isTaggedUnion = event.isTaggedUnion;
|
||||||
|
const Mouse = input.Mouse;
|
||||||
|
const Key = input.Key;
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
|||||||
11
src/cell.zig
11
src/cell.zig
@@ -1,11 +1,8 @@
|
|||||||
const std = @import("std");
|
//! Cell type containing content and formatting for each character in the terminal screen.
|
||||||
const Style = @import("style.zig");
|
|
||||||
|
|
||||||
pub const Cell = @This();
|
|
||||||
|
|
||||||
style: Style = .{ .emphasis = &.{} },
|
|
||||||
// TODO embrace `zg` dependency more due to utf-8 encoding
|
// TODO embrace `zg` dependency more due to utf-8 encoding
|
||||||
cp: u21 = ' ',
|
cp: u21 = ' ',
|
||||||
|
style: Style = .{ .emphasis = &.{} },
|
||||||
|
|
||||||
pub fn eql(this: Cell, other: Cell) bool {
|
pub fn eql(this: Cell, other: Cell) bool {
|
||||||
return this.cp == other.cp and this.style.eql(other.style);
|
return this.cp == other.cp and this.style.eql(other.style);
|
||||||
@@ -20,6 +17,10 @@ pub fn value(this: Cell, writer: anytype) !void {
|
|||||||
try this.style.value(writer, this.cp);
|
try this.style.value(writer, this.cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Style = @import("style.zig");
|
||||||
|
const Cell = @This();
|
||||||
|
|
||||||
test "ascii styled text" {
|
test "ascii styled text" {
|
||||||
const cells: [4]Cell = .{
|
const cells: [4]Cell = .{
|
||||||
.{ .cp = 'Y', .style = .{ .fg = .green, .bg = .grey, .emphasis = &.{} } },
|
.{ .cp = 'Y', .style = .{ .fg = .green, .bg = .grey, .emphasis = &.{} } },
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Color = enum(u8) {
|
pub const Color = enum(u8) {
|
||||||
default = 0,
|
default = 0,
|
||||||
black = 16,
|
black = 16,
|
||||||
@@ -25,16 +23,19 @@ pub const Color = enum(u8) {
|
|||||||
pub inline fn write(this: Color, writer: anytype, comptime coloring: enum { fg, bg, ul }) !void {
|
pub inline fn write(this: Color, writer: anytype, comptime coloring: enum { fg, bg, ul }) !void {
|
||||||
if (this == .default) {
|
if (this == .default) {
|
||||||
switch (coloring) {
|
switch (coloring) {
|
||||||
.fg => try std.fmt.format(writer, "39", .{}),
|
.fg => try format(writer, "39", .{}),
|
||||||
.bg => try std.fmt.format(writer, "49", .{}),
|
.bg => try format(writer, "49", .{}),
|
||||||
.ul => try std.fmt.format(writer, "59", .{}),
|
.ul => try format(writer, "59", .{}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (coloring) {
|
switch (coloring) {
|
||||||
.fg => try std.fmt.format(writer, "38;5;{d}", .{@intFromEnum(this)}),
|
.fg => try format(writer, "38;5;{d}", .{@intFromEnum(this)}),
|
||||||
.bg => try std.fmt.format(writer, "48;5;{d}", .{@intFromEnum(this)}),
|
.bg => try format(writer, "48;5;{d}", .{@intFromEnum(this)}),
|
||||||
.ul => try std.fmt.format(writer, "58;5;{d}", .{@intFromEnum(this)}),
|
.ul => try format(writer, "58;5;{d}", .{@intFromEnum(this)}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const format = std.fmt.format;
|
||||||
|
|||||||
@@ -1,22 +1,5 @@
|
|||||||
const std = @import("std");
|
|
||||||
const input = @import("input.zig");
|
|
||||||
|
|
||||||
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
|
||||||
|
|
||||||
const Cell = @import("cell.zig");
|
|
||||||
const Color = @import("color.zig").Color;
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
const Style = @import("style.zig");
|
|
||||||
const Error = @import("error.zig").Error;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.container);
|
|
||||||
|
|
||||||
/// Border configuration struct
|
/// Border configuration struct
|
||||||
pub const Border = packed struct {
|
pub const Border = packed struct {
|
||||||
// corners:
|
|
||||||
const rounded_border: [6]u21 = .{ '╭', '─', '╮', '│', '╰', '╯' };
|
|
||||||
const squared_border: [6]u21 = .{ '┌', '─', '┐', '│', '└', '┘' };
|
|
||||||
|
|
||||||
/// Color to use for the border
|
/// Color to use for the border
|
||||||
color: Color = .default,
|
color: Color = .default,
|
||||||
/// Configure the corner type to be used for the border
|
/// Configure the corner type to be used for the border
|
||||||
@@ -40,13 +23,12 @@ pub const Border = packed struct {
|
|||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
pub fn content(this: @This(), cells: []Cell, size: Point) void {
|
pub fn content(this: @This(), cells: []Cell, size: Point) void {
|
||||||
std.debug.assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||||
|
|
||||||
const frame = switch (this.corners) {
|
const frame: [6]u21 = switch (this.corners) {
|
||||||
.rounded => Border.rounded_border,
|
.rounded => .{ '╭', '─', '╮', '│', '╰', '╯' },
|
||||||
.squared => Border.squared_border,
|
.squared => .{ '┌', '─', '┐', '│', '└', '┘' },
|
||||||
};
|
};
|
||||||
std.debug.assert(frame.len == 6);
|
|
||||||
|
|
||||||
// render top and bottom border
|
// render top and bottom border
|
||||||
if (this.sides.top or this.sides.bottom) {
|
if (this.sides.top or this.sides.bottom) {
|
||||||
@@ -156,7 +138,7 @@ pub const Rectangle = packed struct {
|
|||||||
|
|
||||||
// NOTE caller owns `Cells` slice and ensures that `cells.len == size.x * size.y`
|
// NOTE caller owns `Cells` slice and ensures that `cells.len == size.x * size.y`
|
||||||
pub fn content(this: @This(), cells: []Cell, size: Point) void {
|
pub fn content(this: @This(), cells: []Cell, size: Point) void {
|
||||||
std.debug.assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||||
|
|
||||||
for (0..size.y) |row| {
|
for (0..size.y) |row| {
|
||||||
for (0..size.x) |col| {
|
for (0..size.x) |col| {
|
||||||
@@ -300,11 +282,6 @@ pub const Rectangle = packed struct {
|
|||||||
|
|
||||||
/// Layout configuration struct
|
/// Layout configuration struct
|
||||||
pub const Layout = packed struct {
|
pub const Layout = packed struct {
|
||||||
// separator.line:
|
|
||||||
const line: [2]u21 = .{ '│', '─' };
|
|
||||||
const dotted: [2]u21 = .{ '┆', '┄' };
|
|
||||||
const double: [2]u21 = .{ '║', '═' };
|
|
||||||
|
|
||||||
/// control the direction in which child elements are laid out
|
/// control the direction in which child elements are laid out
|
||||||
direction: enum(u1) { horizontal, vertical } = .horizontal,
|
direction: enum(u1) { horizontal, vertical } = .horizontal,
|
||||||
/// Padding outside of the child elements
|
/// Padding outside of the child elements
|
||||||
@@ -343,13 +320,13 @@ pub const Layout = packed struct {
|
|||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
pub fn content(this: @This(), comptime C: type, cells: []Cell, origin: Point, size: Point, children: []const C) void {
|
pub fn content(this: @This(), comptime C: type, cells: []Cell, origin: Point, size: Point, children: []const C) void {
|
||||||
std.debug.assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||||
|
|
||||||
if (this.separator.enabled and children.len > 1) {
|
if (this.separator.enabled and children.len > 1) {
|
||||||
const line_cps: [2]u21 = switch (this.separator.line) {
|
const line_cps: [2]u21 = switch (this.separator.line) {
|
||||||
.line => line,
|
.line => .{ '│', '─' },
|
||||||
.dotted => dotted,
|
.dotted => .{ '┆', '┄' },
|
||||||
.double => double,
|
.double => .{ '║', '═' },
|
||||||
};
|
};
|
||||||
const gap: u16 = (this.gap + 1) / 2;
|
const gap: u16 = (this.gap + 1) / 2;
|
||||||
|
|
||||||
@@ -574,7 +551,7 @@ pub fn Container(comptime Event: type) type {
|
|||||||
|
|
||||||
const Element = @import("element.zig").Element(Event);
|
const Element = @import("element.zig").Element(Event);
|
||||||
return struct {
|
return struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
origin: Point,
|
origin: Point,
|
||||||
size: Point,
|
size: Point,
|
||||||
properties: Properties,
|
properties: Properties,
|
||||||
@@ -592,7 +569,7 @@ pub fn Container(comptime Event: type) type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
properties: Properties,
|
properties: Properties,
|
||||||
element: Element,
|
element: Element,
|
||||||
) !@This() {
|
) !@This() {
|
||||||
@@ -872,7 +849,7 @@ pub fn Container(comptime Event: type) type {
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
.mouse => |mouse| if (mouse.in(this.origin, this.size)) {
|
.mouse => |mouse| if (mouse.in(this.origin, this.size)) {
|
||||||
// the element receives the mouse event with relative position
|
// the element receives the mouse event with relative position
|
||||||
std.debug.assert(mouse.x >= this.origin.x and mouse.y >= this.origin.y);
|
assert(mouse.x >= this.origin.x and mouse.y >= this.origin.y);
|
||||||
var relative_mouse: input.Mouse = mouse;
|
var relative_mouse: input.Mouse = mouse;
|
||||||
relative_mouse.x -= this.origin.x;
|
relative_mouse.x -= this.origin.x;
|
||||||
relative_mouse.y -= this.origin.y;
|
relative_mouse.y -= this.origin.y;
|
||||||
@@ -907,10 +884,21 @@ pub fn Container(comptime Event: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.container);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const input = @import("input.zig");
|
||||||
|
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
||||||
|
const Cell = @import("cell.zig");
|
||||||
|
const Color = @import("color.zig").Color;
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
const Style = @import("style.zig");
|
||||||
|
const Error = @import("error.zig").Error;
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = Border;
|
@import("std").testing.refAllDeclsRecursive(@This());
|
||||||
_ = Layout;
|
|
||||||
_ = Rectangle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Container Fixed and Grow Size Vertical" {
|
test "Container Fixed and Grow Size Vertical" {
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
//! Interface for Element's which describe the contents of a `Container`.
|
//! Interface for Element's which describe the contents of a `Container`.
|
||||||
const std = @import("std");
|
|
||||||
const input = @import("input.zig");
|
|
||||||
|
|
||||||
const Container = @import("container.zig").Container;
|
|
||||||
const Cell = @import("cell.zig");
|
|
||||||
const Mouse = input.Mouse;
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
|
|
||||||
pub fn Element(Event: type) type {
|
pub fn Element(Event: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
@@ -188,8 +181,14 @@ pub fn Scrollable(Event: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const input = @import("input.zig");
|
||||||
|
const Container = @import("container.zig").Container;
|
||||||
|
const Cell = @import("cell.zig");
|
||||||
|
const Mouse = input.Mouse;
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
|
||||||
// TODO nested scrollable `Container`s?'
|
// TODO nested scrollable `Container`s?'
|
||||||
// TODO reaction only for when the event is actually pushed to the corresponding `Container` rendered container
|
|
||||||
|
|
||||||
test "scrollable vertical" {
|
test "scrollable vertical" {
|
||||||
const event = @import("event.zig");
|
const event = @import("event.zig");
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
//! Events which are defined by the library. They might be extended by user
|
//! Events which are defined by the library. They might be extended by user
|
||||||
//! events. See `App` for more details about user defined events.
|
//! events. See `App` for more details about user defined events.
|
||||||
const std = @import("std");
|
|
||||||
const input = @import("input.zig");
|
|
||||||
const terminal = @import("terminal.zig");
|
|
||||||
|
|
||||||
const Key = input.Key;
|
|
||||||
const Mouse = input.Mouse;
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
|
|
||||||
/// System events available to every `zterm.App`
|
/// System events available to every `zterm.App`
|
||||||
pub const SystemEvent = union(enum) {
|
pub const SystemEvent = union(enum) {
|
||||||
@@ -17,6 +10,7 @@ pub const SystemEvent = union(enum) {
|
|||||||
quit,
|
quit,
|
||||||
/// Error event to notify other containers about a recoverable error
|
/// Error event to notify other containers about a recoverable error
|
||||||
err: struct {
|
err: struct {
|
||||||
|
/// actual error
|
||||||
err: anyerror,
|
err: anyerror,
|
||||||
/// associated error message
|
/// associated error message
|
||||||
msg: []const u8,
|
msg: []const u8,
|
||||||
@@ -92,3 +86,10 @@ pub fn isTaggedUnion(comptime E: type) bool {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const input = @import("input.zig");
|
||||||
|
const terminal = @import("terminal.zig");
|
||||||
|
const Key = input.Key;
|
||||||
|
const Mouse = input.Mouse;
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
//! Input module for `zterm`. Contains structs to represent key events and mouse events.
|
//! Input module for `zterm`. Contains structs to represent key events and mouse events.
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
|
|
||||||
pub const Mouse = packed struct {
|
pub const Mouse = packed struct {
|
||||||
x: u16,
|
x: u16,
|
||||||
@@ -32,7 +29,7 @@ pub const Mouse = packed struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn eql(this: @This(), other: @This()) bool {
|
pub fn eql(this: @This(), other: @This()) bool {
|
||||||
return std.meta.eql(this, other);
|
return meta.eql(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in(this: @This(), origin: Point, size: Point) bool {
|
pub fn in(this: @This(), origin: Point, size: Point) bool {
|
||||||
@@ -65,7 +62,7 @@ pub const Key = packed struct {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn eql(this: @This(), other: @This()) bool {
|
pub fn eql(this: @This(), other: @This()) bool {
|
||||||
return std.meta.eql(this, other);
|
return meta.eql(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO might be useful to use the std.ascii stuff!
|
// TODO might be useful to use the std.ascii stuff!
|
||||||
@@ -92,24 +89,25 @@ pub const Key = packed struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "isAscii with ascii character" {
|
test "isAscii with ascii character" {
|
||||||
try std.testing.expectEqual(true, isAscii(.{ .cp = 'c' }));
|
try testing.expectEqual(true, isAscii(.{ .cp = 'c' }));
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = 'c', .mod = .{ .ctrl = true } }));
|
try testing.expectEqual(false, isAscii(.{ .cp = 'c', .mod = .{ .ctrl = true } }));
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = 'c', .mod = .{ .alt = true } }));
|
try testing.expectEqual(false, isAscii(.{ .cp = 'c', .mod = .{ .alt = true } }));
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = 'c', .mod = .{ .alt = true, .ctrl = true } }));
|
try testing.expectEqual(false, isAscii(.{ .cp = 'c', .mod = .{ .alt = true, .ctrl = true } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "isAscii with non-ascii character" {
|
test "isAscii with non-ascii character" {
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = Escape }));
|
try testing.expectEqual(false, isAscii(.{ .cp = Escape }));
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = Enter }));
|
try testing.expectEqual(false, isAscii(.{ .cp = Enter }));
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = Enter, .mod = .{ .alt = true } }));
|
try testing.expectEqual(false, isAscii(.{ .cp = Enter, .mod = .{ .alt = true } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "isAscii with excluded input.Delete" {
|
test "isAscii with excluded input.Delete" {
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = Delete }));
|
try testing.expectEqual(false, isAscii(.{ .cp = Delete }));
|
||||||
try std.testing.expectEqual(false, isAscii(.{ .cp = Delete, .mod = .{ .alt = false, .ctrl = false } }));
|
try testing.expectEqual(false, isAscii(.{ .cp = Delete, .mod = .{ .alt = false, .ctrl = false } }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: std.ascii has the escape codes too!
|
||||||
// codepoints for keys
|
// codepoints for keys
|
||||||
pub const Tab: u21 = 0x09;
|
pub const Tab: u21 = 0x09;
|
||||||
pub const Enter: u21 = 0x0D;
|
pub const Enter: u21 = 0x0D;
|
||||||
@@ -225,3 +223,8 @@ pub const RightHyper: u21 = 57451;
|
|||||||
pub const RightMeta: u21 = 57452;
|
pub const RightMeta: u21 = 57452;
|
||||||
pub const IsoLevel3Shift: u21 = 57453;
|
pub const IsoLevel3Shift: u21 = 57453;
|
||||||
pub const IsoLevel5Shift: u21 = 57454;
|
pub const IsoLevel5Shift: u21 = 57454;
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const meta = std.meta;
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
const testing = std.testing;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// taken from https://github.com/rockorager/libvaxis/blob/main/src/queue.zig (MIT-License)
|
// taken from https://github.com/rockorager/libvaxis/blob/main/src/queue.zig (MIT-License)
|
||||||
// with slight modifications
|
// with slight modifications
|
||||||
const std = @import("std");
|
|
||||||
const assert = std.debug.assert;
|
|
||||||
|
|
||||||
/// Thread safe. Fixed size. Blocking push and pop.
|
/// Queue implementation. Thread safe. Fixed size. Blocking push and pop. Polling through tryPop and tryPush.
|
||||||
pub fn Queue(comptime T: type, comptime size: usize) type {
|
pub fn Queue(comptime T: type, comptime size: usize) type {
|
||||||
return struct {
|
return struct {
|
||||||
buf: [size]T = undefined,
|
buf: [size]T = undefined,
|
||||||
@@ -135,8 +133,12 @@ pub fn Queue(comptime T: type, comptime size: usize) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Thread = std.Thread;
|
||||||
const cfg = Thread.SpawnConfig{ .allocator = testing.allocator };
|
const cfg = Thread.SpawnConfig{ .allocator = testing.allocator };
|
||||||
|
|
||||||
test "Queue: simple push / pop" {
|
test "Queue: simple push / pop" {
|
||||||
var queue: Queue(u8, 16) = .{};
|
var queue: Queue(u8, 16) = .{};
|
||||||
queue.push(1);
|
queue.push(1);
|
||||||
@@ -146,7 +148,6 @@ test "Queue: simple push / pop" {
|
|||||||
try testing.expectEqual(2, queue.pop());
|
try testing.expectEqual(2, queue.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Thread = std.Thread;
|
|
||||||
fn testPushPop(q: *Queue(u8, 2)) !void {
|
fn testPushPop(q: *Queue(u8, 2)) !void {
|
||||||
q.push(3);
|
q.push(3);
|
||||||
try testing.expectEqual(2, q.pop());
|
try testing.expectEqual(2, q.pop());
|
||||||
@@ -199,7 +200,7 @@ fn sleepyPop(q: *Queue(u8, 2)) !void {
|
|||||||
try Thread.yield();
|
try Thread.yield();
|
||||||
std.time.sleep(std.time.ns_per_s);
|
std.time.sleep(std.time.ns_per_s);
|
||||||
// Finally, let that other thread go.
|
// Finally, let that other thread go.
|
||||||
try std.testing.expectEqual(1, q.pop());
|
try testing.expectEqual(1, q.pop());
|
||||||
|
|
||||||
// This won't continue until the other thread has had a chance to
|
// This won't continue until the other thread has had a chance to
|
||||||
// put at least one item in the queue.
|
// put at least one item in the queue.
|
||||||
@@ -218,7 +219,7 @@ fn sleepyPop(q: *Queue(u8, 2)) !void {
|
|||||||
std.time.sleep(std.time.ns_per_s / 2);
|
std.time.sleep(std.time.ns_per_s / 2);
|
||||||
|
|
||||||
// Pop that thing and we're done.
|
// Pop that thing and we're done.
|
||||||
try std.testing.expectEqual(2, q.pop());
|
try testing.expectEqual(2, q.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Fill, block, fill, block" {
|
test "Fill, block, fill, block" {
|
||||||
@@ -238,15 +239,15 @@ test "Fill, block, fill, block" {
|
|||||||
|
|
||||||
// Just to make sure the sleeps are yielding to this thread, make
|
// Just to make sure the sleeps are yielding to this thread, make
|
||||||
// sure it took at least 900ms to do the push.
|
// sure it took at least 900ms to do the push.
|
||||||
try std.testing.expect(then - now > 900);
|
try testing.expect(then - now > 900);
|
||||||
|
|
||||||
// This should block again, waiting for the other thread.
|
// This should block again, waiting for the other thread.
|
||||||
queue.push(4);
|
queue.push(4);
|
||||||
|
|
||||||
// And once that push has gone through, the other thread's done.
|
// And once that push has gone through, the other thread's done.
|
||||||
thread.join();
|
thread.join();
|
||||||
try std.testing.expectEqual(3, queue.pop());
|
try testing.expectEqual(3, queue.pop());
|
||||||
try std.testing.expectEqual(4, queue.pop());
|
try testing.expectEqual(4, queue.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sleepyPush(q: *Queue(u8, 1)) !void {
|
fn sleepyPush(q: *Queue(u8, 1)) !void {
|
||||||
@@ -284,8 +285,8 @@ test "Drain, block, drain, block" {
|
|||||||
|
|
||||||
var queue: Queue(u8, 1) = .{};
|
var queue: Queue(u8, 1) = .{};
|
||||||
const thread = try Thread.spawn(cfg, sleepyPush, .{&queue});
|
const thread = try Thread.spawn(cfg, sleepyPush, .{&queue});
|
||||||
try std.testing.expectEqual(1, queue.pop());
|
try testing.expectEqual(1, queue.pop());
|
||||||
try std.testing.expectEqual(2, queue.pop());
|
try testing.expectEqual(2, queue.pop());
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
const std = @import("std");
|
//! Renderer for `zterm`.
|
||||||
const terminal = @import("terminal.zig");
|
|
||||||
|
|
||||||
const Cell = @import("cell.zig");
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
|
|
||||||
/// Double-buffered intermediate rendering pipeline
|
/// Double-buffered intermediate rendering pipeline
|
||||||
pub const Buffered = struct {
|
pub const Buffered = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
created: bool,
|
created: bool,
|
||||||
size: Point,
|
size: Point,
|
||||||
screen: []Cell,
|
screen: []Cell,
|
||||||
virtual_screen: []Cell,
|
virtual_screen: []Cell,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) @This() {
|
pub fn init(allocator: Allocator) @This() {
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.created = false,
|
.created = false,
|
||||||
@@ -29,9 +25,9 @@ pub const Buffered = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(this: *@This()) !void {
|
pub fn resize(this: *@This()) !Point {
|
||||||
const size = terminal.getTerminalSize();
|
const size = terminal.getTerminalSize();
|
||||||
if (std.meta.eql(this.size, size)) return;
|
if (meta.eql(this.size, size)) return this.size;
|
||||||
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
const n = @as(usize, this.size.x) * @as(usize, this.size.y);
|
const n = @as(usize, this.size.x) * @as(usize, this.size.y);
|
||||||
@@ -52,6 +48,7 @@ pub const Buffered = struct {
|
|||||||
@memset(this.virtual_screen, .{});
|
@memset(this.virtual_screen, .{});
|
||||||
}
|
}
|
||||||
try this.clear();
|
try this.clear();
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the entire screen and reset the screen buffer, to force a re-draw with the next `flush` call.
|
/// Clear the entire screen and reset the screen buffer, to force a re-draw with the next `flush` call.
|
||||||
@@ -118,3 +115,10 @@ pub const Buffered = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const meta = std.meta;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const terminal = @import("terminal.zig");
|
||||||
|
const Cell = @import("cell.zig");
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ pub const Renderer = @import("render.zig");
|
|||||||
// Container Configurations
|
// Container Configurations
|
||||||
pub const Border = container.Border;
|
pub const Border = container.Border;
|
||||||
pub const Rectangle = container.Rectangle;
|
pub const Rectangle = container.Rectangle;
|
||||||
pub const Scroll = container.Scroll;
|
|
||||||
pub const Layout = container.Layout;
|
pub const Layout = container.Layout;
|
||||||
|
|
||||||
pub const Cell = @import("cell.zig");
|
pub const Cell = @import("cell.zig");
|
||||||
@@ -7,11 +7,13 @@
|
|||||||
|
|
||||||
// taken from https://github.com/rockorager/libvaxis/blob/main/src/Cell.zig (MIT-License)
|
// taken from https://github.com/rockorager/libvaxis/blob/main/src/Cell.zig (MIT-License)
|
||||||
// with slight modifications
|
// with slight modifications
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const Color = @import("color.zig").Color;
|
fg: Color = .default,
|
||||||
|
bg: Color = .default,
|
||||||
pub const Style = @This();
|
ul: Color = .default,
|
||||||
|
cursor: bool = false,
|
||||||
|
ul_style: Underline = .off,
|
||||||
|
emphasis: []const Emphasis,
|
||||||
|
|
||||||
pub const Underline = enum {
|
pub const Underline = enum {
|
||||||
off,
|
off,
|
||||||
@@ -34,42 +36,43 @@ pub const Emphasis = enum(u8) {
|
|||||||
strikethrough,
|
strikethrough,
|
||||||
};
|
};
|
||||||
|
|
||||||
fg: Color = .default,
|
|
||||||
bg: Color = .default,
|
|
||||||
ul: Color = .default,
|
|
||||||
cursor: bool = false,
|
|
||||||
ul_style: Underline = .off,
|
|
||||||
emphasis: []const Emphasis,
|
|
||||||
|
|
||||||
pub fn eql(this: Style, other: Style) bool {
|
pub fn eql(this: Style, other: Style) bool {
|
||||||
return std.meta.eql(this, other);
|
return meta.eql(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO might be useful to use the std.ascii stuff!
|
// TODO might be useful to use the std.ascii stuff!
|
||||||
|
|
||||||
pub fn value(this: Style, writer: anytype, cp: u21) !void {
|
pub fn value(this: Style, writer: anytype, cp: u21) !void {
|
||||||
var buffer: [4]u8 = undefined;
|
var buffer: [4]u8 = undefined;
|
||||||
const bytes = try std.unicode.utf8Encode(cp, &buffer);
|
const bytes = try unicode.utf8Encode(cp, &buffer);
|
||||||
std.debug.assert(bytes > 0);
|
assert(bytes > 0);
|
||||||
// build ansi sequence for 256 colors ...
|
// build ansi sequence for 256 colors ...
|
||||||
// foreground
|
// foreground
|
||||||
try std.fmt.format(writer, "\x1b[", .{});
|
try format(writer, "\x1b[", .{});
|
||||||
try this.fg.write(writer, .fg);
|
try this.fg.write(writer, .fg);
|
||||||
// background
|
// background
|
||||||
try std.fmt.format(writer, ";", .{});
|
try format(writer, ";", .{});
|
||||||
try this.bg.write(writer, .bg);
|
try this.bg.write(writer, .bg);
|
||||||
// underline
|
// underline
|
||||||
// FIX assert that if the underline property is set that the ul style and the attribute for underlining is available
|
// FIX assert that if the underline property is set that the ul style and the attribute for underlining is available
|
||||||
try std.fmt.format(writer, ";", .{});
|
try format(writer, ";", .{});
|
||||||
try this.ul.write(writer, .ul);
|
try this.ul.write(writer, .ul);
|
||||||
// append styles (aka attributes like bold, italic, strikethrough, etc.)
|
// append styles (aka attributes like bold, italic, strikethrough, etc.)
|
||||||
for (this.emphasis) |attribute| try std.fmt.format(writer, ";{d}", .{@intFromEnum(attribute)});
|
for (this.emphasis) |attribute| try format(writer, ";{d}", .{@intFromEnum(attribute)});
|
||||||
try std.fmt.format(writer, "m", .{});
|
try format(writer, "m", .{});
|
||||||
// content
|
// content
|
||||||
try std.fmt.format(writer, "{s}", .{buffer[0..bytes]});
|
try format(writer, "{s}", .{buffer[0..bytes]});
|
||||||
try std.fmt.format(writer, "\x1b[0m", .{});
|
try format(writer, "\x1b[0m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO implement helper functions for terminal capabilities:
|
// TODO implement helper functions for terminal capabilities:
|
||||||
// - links / url display (osc 8)
|
// - links / url display (osc 8)
|
||||||
// - show / hide cursor?
|
// - show / hide cursor?
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const unicode = std.unicode;
|
||||||
|
const meta = std.meta;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const format = std.fmt.format;
|
||||||
|
const Color = @import("color.zig").Color;
|
||||||
|
const Style = @This();
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
const std = @import("std");
|
|
||||||
const code_point = @import("code_point");
|
|
||||||
const ctlseqs = @import("ctlseqs.zig");
|
|
||||||
const input = @import("input.zig");
|
|
||||||
|
|
||||||
const Key = input.Key;
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
const Size = @import("point.zig").Point;
|
|
||||||
const Cell = @import("cell.zig");
|
|
||||||
|
|
||||||
const log = std.log.scoped(.terminal);
|
|
||||||
|
|
||||||
// Ref: https://vt100.net/docs/vt510-rm/DECRPM.html
|
// Ref: https://vt100.net/docs/vt510-rm/DECRPM.html
|
||||||
pub const ReportMode = enum {
|
pub const ReportMode = enum {
|
||||||
not_recognized,
|
not_recognized,
|
||||||
@@ -21,62 +9,62 @@ pub const ReportMode = enum {
|
|||||||
|
|
||||||
/// Gets number of rows and columns in the terminal
|
/// Gets number of rows and columns in the terminal
|
||||||
pub fn getTerminalSize() Size {
|
pub fn getTerminalSize() Size {
|
||||||
var ws: std.posix.winsize = undefined;
|
var ws: posix.winsize = undefined;
|
||||||
_ = std.posix.system.ioctl(std.posix.STDIN_FILENO, std.posix.T.IOCGWINSZ, @intFromPtr(&ws));
|
_ = posix.system.ioctl(posix.STDIN_FILENO, posix.T.IOCGWINSZ, @intFromPtr(&ws));
|
||||||
return .{ .x = ws.col, .y = ws.row };
|
return .{ .x = ws.col, .y = ws.row };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn saveScreen() !void {
|
pub fn saveScreen() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.save_screen);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.save_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restoreScreen() !void {
|
pub fn restoreScreen() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.restore_screen);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.restore_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enterAltScreen() !void {
|
pub fn enterAltScreen() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.smcup);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.smcup);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exitAltScreen() !void {
|
pub fn exitAltScreen() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.rmcup);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.rmcup);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clearScreen() !void {
|
pub fn clearScreen() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.clear_screen);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.clear_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hideCursor() !void {
|
pub fn hideCursor() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.hide_cursor);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.hide_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn showCursor() !void {
|
pub fn showCursor() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.show_cursor);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.show_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setCursorPositionHome() !void {
|
pub fn setCursorPositionHome() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.home);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.home);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enableMouseSupport() !void {
|
pub fn enableMouseSupport() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.mouse_set);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.mouse_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disableMouseSupport() !void {
|
pub fn disableMouseSupport() !void {
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, ctlseqs.mouse_reset);
|
_ = try posix.write(posix.STDIN_FILENO, ctlseqs.mouse_reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(buf: []u8) !usize {
|
pub fn read(buf: []u8) !usize {
|
||||||
return try std.posix.read(std.posix.STDIN_FILENO, buf);
|
return try posix.read(posix.STDIN_FILENO, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(buf: []const u8) !usize {
|
pub fn write(buf: []const u8) !usize {
|
||||||
return try std.posix.write(std.posix.STDIN_FILENO, buf);
|
return try posix.write(posix.STDIN_FILENO, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contextWrite(context: @This(), data: []const u8) anyerror!usize {
|
fn contextWrite(context: @This(), data: []const u8) anyerror!usize {
|
||||||
_ = context;
|
_ = context;
|
||||||
return try std.posix.write(std.posix.STDOUT_FILENO, data);
|
return try posix.write(posix.STDOUT_FILENO, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Writer = std.io.Writer(
|
const Writer = std.io.Writer(
|
||||||
@@ -92,18 +80,18 @@ pub fn writer() Writer {
|
|||||||
pub fn setCursorPosition(pos: Point) !void {
|
pub fn setCursorPosition(pos: Point) !void {
|
||||||
var buf: [64]u8 = undefined;
|
var buf: [64]u8 = undefined;
|
||||||
const value = try std.fmt.bufPrint(&buf, "\x1b[{d};{d}H", .{ pos.y + 1, pos.x + 1 });
|
const value = try std.fmt.bufPrint(&buf, "\x1b[{d};{d}H", .{ pos.y + 1, pos.x + 1 });
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, value);
|
_ = try posix.write(posix.STDIN_FILENO, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getCursorPosition() !Size.Position {
|
pub fn getCursorPosition() !Size.Position {
|
||||||
// Needs Raw mode (no wait for \n) to work properly cause
|
// Needs Raw mode (no wait for \n) to work properly cause
|
||||||
// control sequence will not be written without it.
|
// control sequence will not be written without it.
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, "\x1b[6n");
|
_ = try posix.write(posix.STDIN_FILENO, "\x1b[6n");
|
||||||
|
|
||||||
var buf: [64]u8 = undefined;
|
var buf: [64]u8 = undefined;
|
||||||
|
|
||||||
// format: \x1b, "[", R1,..., Rn, ";", C1, ..., Cn, "R"
|
// format: \x1b, "[", R1,..., Rn, ";", C1, ..., Cn, "R"
|
||||||
const len = try std.posix.read(std.posix.STDIN_FILENO, &buf);
|
const len = try posix.read(posix.STDIN_FILENO, &buf);
|
||||||
|
|
||||||
if (!isCursorPosition(buf[0..len])) {
|
if (!isCursorPosition(buf[0..len])) {
|
||||||
return error.InvalidValueReturned;
|
return error.InvalidValueReturned;
|
||||||
@@ -166,8 +154,8 @@ pub fn isCursorPosition(buf: []u8) bool {
|
|||||||
///
|
///
|
||||||
/// `bak`: pointer to store termios struct backup before
|
/// `bak`: pointer to store termios struct backup before
|
||||||
/// altering, this is used to disable raw mode.
|
/// altering, this is used to disable raw mode.
|
||||||
pub fn enableRawMode(bak: *std.posix.termios) !void {
|
pub fn enableRawMode(bak: *posix.termios) !void {
|
||||||
var termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO);
|
var termios = try posix.tcgetattr(posix.STDIN_FILENO);
|
||||||
bak.* = termios;
|
bak.* = termios;
|
||||||
|
|
||||||
// termios flags used by termios(3)
|
// termios flags used by termios(3)
|
||||||
@@ -192,20 +180,20 @@ pub fn enableRawMode(bak: *std.posix.termios) !void {
|
|||||||
termios.cflag.CSIZE = .CS8;
|
termios.cflag.CSIZE = .CS8;
|
||||||
termios.cflag.PARENB = false;
|
termios.cflag.PARENB = false;
|
||||||
|
|
||||||
termios.cc[@intFromEnum(std.posix.V.MIN)] = 1;
|
termios.cc[@intFromEnum(posix.V.MIN)] = 1;
|
||||||
termios.cc[@intFromEnum(std.posix.V.TIME)] = 0;
|
termios.cc[@intFromEnum(posix.V.TIME)] = 0;
|
||||||
|
|
||||||
try std.posix.tcsetattr(
|
try posix.tcsetattr(
|
||||||
std.posix.STDIN_FILENO,
|
posix.STDIN_FILENO,
|
||||||
.FLUSH,
|
.FLUSH,
|
||||||
termios,
|
termios,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reverts `enableRawMode` to restore initial functionality.
|
/// Reverts `enableRawMode` to restore initial functionality.
|
||||||
pub fn disableRawMode(bak: *std.posix.termios) !void {
|
pub fn disableRawMode(bak: *posix.termios) !void {
|
||||||
try std.posix.tcsetattr(
|
try posix.tcsetattr(
|
||||||
std.posix.STDIN_FILENO,
|
posix.STDIN_FILENO,
|
||||||
.FLUSH,
|
.FLUSH,
|
||||||
bak.*,
|
bak.*,
|
||||||
);
|
);
|
||||||
@@ -215,13 +203,13 @@ pub fn disableRawMode(bak: *std.posix.termios) !void {
|
|||||||
pub fn canSynchornizeOutput() !bool {
|
pub fn canSynchornizeOutput() !bool {
|
||||||
// Needs Raw mode (no wait for \n) to work properly cause
|
// Needs Raw mode (no wait for \n) to work properly cause
|
||||||
// control sequence will not be written without it.
|
// control sequence will not be written without it.
|
||||||
_ = try std.posix.write(std.posix.STDIN_FILENO, "\x1b[?2026$p");
|
_ = try posix.write(posix.STDIN_FILENO, "\x1b[?2026$p");
|
||||||
|
|
||||||
var buf: [64]u8 = undefined;
|
var buf: [64]u8 = undefined;
|
||||||
|
|
||||||
// format: \x1b, "[", "?", "2", "0", "2", "6", ";", n, "$", "y"
|
// format: \x1b, "[", "?", "2", "0", "2", "6", ";", n, "$", "y"
|
||||||
const len = try std.posix.read(std.posix.STDIN_FILENO, &buf);
|
const len = try posix.read(posix.STDIN_FILENO, &buf);
|
||||||
if (!std.mem.eql(u8, buf[0..len], "\x1b[?2026;") or len < 9) {
|
if (!mem.eql(u8, buf[0..len], "\x1b[?2026;") or len < 9) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,3 +226,16 @@ fn getReportMode(ps: u8) ReportMode {
|
|||||||
else => ReportMode.not_recognized,
|
else => ReportMode.not_recognized,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = std.log.scoped(.terminal);
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const mem = std.mem;
|
||||||
|
const posix = std.posix;
|
||||||
|
const code_point = @import("code_point");
|
||||||
|
const ctlseqs = @import("ctlseqs.zig");
|
||||||
|
const input = @import("input.zig");
|
||||||
|
const Key = input.Key;
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
const Size = @import("point.zig").Point;
|
||||||
|
const Cell = @import("cell.zig");
|
||||||
|
|||||||
@@ -1,23 +1,12 @@
|
|||||||
//! Testing namespace for `zterm` to provide testing capabilities for `Containers`, `Event` handling, `App`s and `Element` implementations.
|
//! Testing namespace for `zterm` to provide testing capabilities for `Containers`, `Event` handling, `App`s and `Element` implementations.
|
||||||
const std = @import("std");
|
|
||||||
const event = @import("event.zig");
|
|
||||||
const Container = @import("container.zig").Container;
|
|
||||||
|
|
||||||
const Cell = @import("cell.zig");
|
|
||||||
const DisplayWidth = @import("DisplayWidth");
|
|
||||||
const Point = @import("point.zig").Point;
|
|
||||||
|
|
||||||
// TODO how would I describe the expected screens?
|
|
||||||
// - including styling?
|
|
||||||
// - compare generated strings instead? -> how would this be generated for the user?
|
|
||||||
|
|
||||||
/// Single-buffer test rendering pipeline for testing purposes.
|
/// Single-buffer test rendering pipeline for testing purposes.
|
||||||
pub const Renderer = struct {
|
pub const Renderer = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
size: Point,
|
size: Point,
|
||||||
screen: []Cell,
|
screen: []Cell,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, size: Point) @This() {
|
pub fn init(allocator: Allocator, size: Point) @This() {
|
||||||
const screen = allocator.alloc(Cell, @as(usize, size.x) * @as(usize, size.y)) catch @panic("testing.zig: Out of memory.");
|
const screen = allocator.alloc(Cell, @as(usize, size.x) * @as(usize, size.y)) catch @panic("testing.zig: Out of memory.");
|
||||||
@memset(screen, .{});
|
@memset(screen, .{});
|
||||||
|
|
||||||
@@ -116,7 +105,7 @@ pub const Renderer = struct {
|
|||||||
/// }, &container, @import("test/container/border.all.zon"));
|
/// }, &container, @import("test/container/border.all.zon"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn expectContainerScreen(size: Point, container: *Container(event.SystemEvent), expected: []const Cell) !void {
|
pub fn expectContainerScreen(size: Point, container: *Container(event.SystemEvent), expected: []const Cell) !void {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = testing.allocator;
|
||||||
var renderer: Renderer = .init(allocator, size);
|
var renderer: Renderer = .init(allocator, size);
|
||||||
defer renderer.deinit();
|
defer renderer.deinit();
|
||||||
|
|
||||||
@@ -133,10 +122,10 @@ pub fn expectContainerScreen(size: Point, container: *Container(event.SystemEven
|
|||||||
/// the contents of a given screen from the `zterm.testing.Renderer`. See
|
/// the contents of a given screen from the `zterm.testing.Renderer`. See
|
||||||
/// `zterm.testing.expectContainerScreen` for an example usage.
|
/// `zterm.testing.expectContainerScreen` for an example usage.
|
||||||
pub fn expectEqualCells(origin: Point, size: Point, expected: []const Cell, actual: []const Cell) !void {
|
pub fn expectEqualCells(origin: Point, size: Point, expected: []const Cell, actual: []const Cell) !void {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = testing.allocator;
|
||||||
|
|
||||||
try std.testing.expectEqual(expected.len, actual.len);
|
try testing.expectEqual(expected.len, actual.len);
|
||||||
try std.testing.expectEqual(expected.len, @as(usize, size.y) * @as(usize, size.x));
|
try testing.expectEqual(expected.len, @as(usize, size.y) * @as(usize, size.x));
|
||||||
|
|
||||||
var expected_cps = try std.ArrayList(Cell).initCapacity(allocator, size.x);
|
var expected_cps = try std.ArrayList(Cell).initCapacity(allocator, size.x);
|
||||||
defer expected_cps.deinit();
|
defer expected_cps.deinit();
|
||||||
@@ -199,3 +188,12 @@ pub fn expectEqualCells(origin: Point, size: Point, expected: []const Cell, actu
|
|||||||
try std_writer.writeAll(output.items);
|
try std_writer.writeAll(output.items);
|
||||||
return error.TestExpectEqualCells;
|
return error.TestExpectEqualCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const event = @import("event.zig");
|
||||||
|
const Container = @import("container.zig").Container;
|
||||||
|
const Cell = @import("cell.zig");
|
||||||
|
const DisplayWidth = @import("DisplayWidth");
|
||||||
|
const Point = @import("point.zig").Point;
|
||||||
|
|||||||
Reference in New Issue
Block a user