Files
zterm/examples/demo.zig
Yves Biener 1ad7bf0d48 WIP: exec element with initial implementation
Problem is that the main application actually does not create a pty and
instead uses the current one (for better compatability for ssh based
hosting).

The current problem is the fact that the child process should not
take over (and never give back too) the input / output handling of the
current pts. Such that both applications can receive inputs accordingly
(in best case actually controlled by the main application).
2025-07-12 20:26:47 +02:00

163 lines
5.0 KiB
Zig

const QuitText = struct {
const text = "Press ctrl+c to quit. Press ctrl+n to launch helix.";
pub fn element(this: *@This()) App.Element {
return .{ .ptr = this, .vtable = &.{ .content = content } };
}
pub fn content(ctx: *anyopaque, cells: []zterm.Cell, size: zterm.Point) !void {
_ = ctx;
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
const y = 2;
const x = size.x / 2 -| (text.len / 2);
const anchor = (y * size.x) + x;
for (text, 0..) |cp, idx| {
cells[anchor + idx].style.fg = .white;
cells[anchor + idx].style.bg = .black;
cells[anchor + idx].cp = cp;
// NOTE do not write over the contents of this `Container`'s `Size`
if (anchor + idx == cells.len - 1) break;
}
}
};
pub fn main() !void {
errdefer |err| log.err("Application Error: {any}", .{err});
// TODO maybe create own allocator as some sort of arena allocator to have consistent memory usage
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer if (gpa.deinit() == .leak) log.err("memory leak", .{});
const allocator = gpa.allocator();
var app: App = .init;
var renderer = zterm.Renderer.Buffered.init(allocator);
defer renderer.deinit();
var quit_text: QuitText = .{};
// TODO what should the demo application do?
// - some sort of chat? -> write messages and have them displayed in a scrollable array at the right hand side?
// - on the left some buttons?
var box = try App.Container.init(allocator, .{
.border = .{
.color = .blue,
.sides = .all,
},
.layout = .{
.gap = 1,
.padding = .vertical(2),
.direction = .vertical,
},
.size = .{
.dim = .{ .y = 90 },
},
}, .{});
try box.append(try App.Container.init(allocator, .{
.rectangle = .{ .fill = .light_green },
}, .{}));
try box.append(try App.Container.init(allocator, .{
.rectangle = .{ .fill = .light_green },
}, .{}));
try box.append(try App.Container.init(allocator, .{
.rectangle = .{ .fill = .light_green },
}, .{}));
defer box.deinit();
var scrollable: App.Scrollable = .init(box, .disabled);
var container = try App.Container.init(allocator, .{
.layout = .{
.gap = 2,
.separator = .{ .enabled = true },
.padding = .{ .top = 5, .bottom = 3, .left = 3, .right = 3 },
.direction = .horizontal,
},
}, quit_text.element());
try container.append(try App.Container.init(allocator, .{}, scrollable.element()));
var environment = try std.process.getEnvMap(allocator);
defer environment.deinit();
var editor: App.Exec = .init(
allocator,
&.{
"tty",
},
&environment,
&app.queue,
);
defer editor.deinit();
try container.append(try App.Container.init(allocator, .{
.border = .{
.color = .light_blue,
.sides = .all,
},
.size = .{
.dim = .{ .x = 100 },
},
}, editor.element()));
try container.append(try App.Container.init(allocator, .{
.rectangle = .{ .fill = .blue },
.size = .{
.dim = .{ .x = 30 },
},
}, .{}));
defer container.deinit(); // also de-initializes the children
try app.start();
defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err});
var process_in: std.ArrayListUnmanaged(u8) = .empty;
defer process_in.deinit(allocator);
var process_out: std.ArrayListUnmanaged(u8) = .empty;
defer process_out.deinit(allocator);
// event loop
while (true) {
const event = app.nextEvent();
log.debug("received event: {s}", .{@tagName(event)});
// pre event handling
switch (event) {
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
// NOTE errors could be displayed in another container in case one was received, etc. to provide the user with feedback
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
else => {},
}
// NOTE returned errors should be propagated back to the application
container.handle(event) catch |err| app.postEvent(.{
.err = .{
.err = err,
.msg = "Container Event handling failed",
},
});
// post event handling
switch (event) {
.quit => break,
else => {},
}
container.resize(try renderer.resize());
container.reposition(.{});
try renderer.render(@TypeOf(container), &container);
try renderer.flush();
}
}
pub const panic = App.panic_handler;
const log = std.log.scoped(.default);
const std = @import("std");
const assert = std.debug.assert;
const zterm = @import("zterm");
const input = zterm.input;
const App = zterm.App(union(enum) {});