this repo has no description
1const std = @import("std");
2const vaxis = @import("vaxis");
3const Cell = vaxis.Cell;
4const TextInput = vaxis.widgets.TextInput;
5const border = vaxis.widgets.border;
6
7const log = std.log.scoped(.main);
8
9// Our Event. This can contain internal events as well as Vaxis events.
10// Internal events can be posted into the same queue as vaxis events to allow
11// for a single event loop with exhaustive switching. Booya
12const Event = union(enum) {
13 key_press: vaxis.Key,
14 mouse: vaxis.Mouse,
15 winsize: vaxis.Winsize,
16 focus_in,
17 focus_out,
18 foo: u8,
19};
20
21pub fn main(init: std.process.Init) !void {
22 const io = init.io;
23 const alloc = init.gpa;
24
25 // Initialize a tty
26 var buffer: [1024]u8 = undefined;
27 var tty = try vaxis.Tty.init(io, &buffer);
28 defer tty.deinit();
29
30 // Use a buffered writer for better performance. There are a lot of writes
31 // in the render loop and this can have a significant savings
32 const writer = tty.writer();
33
34 // Initialize Vaxis
35 var vx = try vaxis.init(io, alloc, init.environ_map, .{
36 .kitty_keyboard_flags = .{ .report_events = true },
37 });
38 defer vx.deinit(alloc, tty.writer());
39
40 var loop: vaxis.Loop(Event) = .init(io, &tty, &vx);
41
42 // Start the read loop. This puts the terminal in raw mode and begins
43 // reading user input
44 try loop.start();
45 defer loop.stop();
46
47 // Optionally enter the alternate screen
48 try vx.enterAltScreen(writer);
49
50 // We'll adjust the color index every keypress for the border
51 var color_idx: u8 = 0;
52
53 // init our text input widget. The text input widget needs an allocator to
54 // store the contents of the input
55 var text_input = TextInput.init(alloc);
56 defer text_input.deinit();
57
58 try vx.setMouseMode(writer, true);
59
60 try writer.flush();
61 // Sends queries to terminal to detect certain features. This should
62 // _always_ be called, but is left to the application to decide when
63 try vx.queryTerminal(tty.writer(), .fromSeconds(1));
64
65 // The main event loop. Vaxis provides a thread safe, blocking, buffered
66 // queue which can serve as the primary event queue for an application
67 while (true) {
68 // nextEvent blocks until an event is in the queue
69 const event = try loop.nextEvent();
70 // log.debug("event: {}", .{event});
71 // exhaustive switching ftw. Vaxis will send events if your Event
72 // enum has the fields for those events (ie "key_press", "winsize")
73 switch (event) {
74 .key_press => |key| {
75 color_idx = switch (color_idx) {
76 255 => 0,
77 else => color_idx + 1,
78 };
79 if (key.matches('c', .{ .ctrl = true })) {
80 break;
81 } else if (key.matches('l', .{ .ctrl = true })) {
82 vx.queueRefresh();
83 } else if (key.matches('n', .{ .ctrl = true })) {
84 try vx.notify(tty.writer(), "vaxis", "hello from vaxis");
85 loop.stop();
86 var child = try std.process.spawn(io, .{
87 .argv = &.{"nvim"},
88 .stdin = .inherit,
89 .stdout = .inherit,
90 .stderr = .inherit,
91 });
92 _ = try child.wait(io);
93 try loop.start();
94 try vx.enterAltScreen(tty.writer());
95 vx.queueRefresh();
96 } else if (key.matches(vaxis.Key.enter, .{}) or key.matches('j', .{ .ctrl = true })) {
97 text_input.clearAndFree();
98 } else {
99 try text_input.update(.{ .key_press = key });
100 }
101 },
102
103 // winsize events are sent to the application to ensure that all
104 // resizes occur in the main thread. This lets us avoid expensive
105 // locks on the screen. All applications must handle this event
106 // unless they aren't using a screen (IE only detecting features)
107 //
108 // This is the only call that the core of Vaxis needs an allocator
109 // for. The allocations are because we keep a copy of each cell to
110 // optimize renders. When resize is called, we allocated two slices:
111 // one for the screen, and one for our buffered screen. Each cell in
112 // the buffered screen contains an ArrayList(u8) to be able to store
113 // the grapheme for that cell Each cell is initialized with a size
114 // of 1, which is sufficient for all of ASCII. Anything requiring
115 // more than one byte will incur an allocation on the first render
116 // after it is drawn. Thereafter, it will not allocate unless the
117 // screen is resized
118 .winsize => |ws| try vx.resize(alloc, tty.writer(), ws),
119 else => {},
120 }
121
122 // vx.window() returns the root window. This window is the size of the
123 // terminal and can spawn child windows as logical areas. Child windows
124 // cannot draw outside of their bounds
125 const win = vx.window();
126
127 // Clear the entire space because we are drawing in immediate mode.
128 // vaxis double buffers the screen. This new frame will be compared to
129 // the old and only updated cells will be drawn
130 win.clear();
131 // draw the text_input using a bordered window
132 const style: vaxis.Style = .{
133 .fg = .{ .index = color_idx },
134 };
135 const child = win.child(.{
136 .x_off = win.width / 2 - 20,
137 .y_off = win.height / 2 - 3,
138 .width = 40,
139 .height = 3,
140 .border = .{
141 .where = .all,
142 .style = style,
143 },
144 });
145 text_input.draw(child);
146
147 // Render the screen
148 try vx.render(writer);
149 try writer.flush();
150 }
151}
152
153test {
154 std.testing.refAllDecls(@This());
155}