this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

get counter example working and fix up cli and text_input

authored by

Jeffrey C. Ollie and committed by
Tim Culverhouse
1c74bbb8 45d4a4f4

+97 -59
+1 -2
examples/cli.zig
··· 15 15 var vx = try vaxis.init(io, alloc, init.environ_map, .{}); 16 16 defer vx.deinit(alloc, tty.writer()); 17 17 18 - var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx }; 19 - try loop.init(io); 18 + var loop: vaxis.Loop(Event) = .init(io, &tty, &vx); 20 19 21 20 try loop.start(); 22 21 defer loop.stop();
+4 -6
examples/counter.zig
··· 111 111 } 112 112 }; 113 113 114 - pub fn main() !void { 115 - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 116 - defer _ = gpa.deinit(); 117 - 118 - const allocator = gpa.allocator(); 114 + pub fn main(init: std.process.Init) !void { 115 + const io = init.io; 116 + const allocator = init.gpa; 119 117 120 - var app = try vxfw.App.init(allocator); 118 + var app = try vxfw.App.init(io, allocator, init.environ_map); 121 119 defer app.deinit(); 122 120 123 121 // We heap allocate our model because we will require a stable pointer to it in our Button
+3 -7
examples/text_input.zig
··· 22 22 const io = init.io; 23 23 const alloc = init.gpa; 24 24 25 - // Initalize a tty 25 + // Initialize a tty 26 26 var buffer: [1024]u8 = undefined; 27 27 var tty = try vaxis.Tty.init(io, &buffer); 28 28 defer tty.deinit(); ··· 37 37 }); 38 38 defer vx.deinit(alloc, tty.writer()); 39 39 40 - var loop: vaxis.Loop(Event) = .{ 41 - .vaxis = &vx, 42 - .tty = &tty, 43 - }; 44 - try loop.init(io); 40 + var loop: vaxis.Loop(Event) = .init(io, &tty, &vx); 45 41 46 42 // Start the read loop. This puts the terminal in raw mode and begins 47 43 // reading user input ··· 71 67 while (true) { 72 68 // nextEvent blocks until an event is in the queue 73 69 const event = try loop.nextEvent(); 74 - log.debug("event: {}", .{event}); 70 + // log.debug("event: {}", .{event}); 75 71 // exhaustive switching ftw. Vaxis will send events if your Event 76 72 // enum has the fields for those events (ie "key_press", "winsize") 77 73 switch (event) {
+45 -9
src/Loop.zig
··· 16 16 17 17 const Event = T; 18 18 19 + io: std.Io, 19 20 tty: *Tty, 20 21 vaxis: *Vaxis, 21 22 22 - queue: Queue(T, 512) = undefined, 23 - thread: ?std.Thread = null, 23 + queue: Queue(T, 512), 24 + thread: ?std.Io.Future(void) = null, 24 25 should_quit: bool = false, 25 26 26 27 /// Initialize the event loop. This is an intrusive init so that we have 27 28 /// a stable pointer to register signal callbacks with posix TTYs 28 - pub fn init(self: *Self, io: std.Io) !void { 29 - self.queue = .init(io); 29 + pub fn init(io: std.Io, tty: *Tty, vx: *Vaxis) Self { 30 + return .{ 31 + .io = io, 32 + .tty = tty, 33 + .vaxis = vx, 34 + .queue = .init(io), 35 + }; 36 + } 37 + 38 + pub fn installResizeHandler(self: *Self) !void { 30 39 switch (builtin.os.tag) { 31 40 .windows => {}, 32 41 else => { ··· 44 53 /// spawns the input thread to read input from the tty 45 54 pub fn start(self: *Self) !void { 46 55 if (self.thread) |_| return; 47 - self.thread = try std.Thread.spawn(.{}, Self.ttyRun, .{ 56 + self.thread = try self.io.concurrent(Self.ttyRun, .{ 48 57 self, 49 58 self.vaxis.opts.system_clipboard_allocator, 50 59 }); ··· 58 67 // trigger a read 59 68 self.vaxis.deviceStatusReport(self.tty.writer()) catch {}; 60 69 61 - if (self.thread) |thread| { 62 - thread.join(); 70 + if (self.thread) |*thread| { 71 + thread.await(self.io); 63 72 self.thread = null; 64 73 self.should_quit = false; 65 74 } ··· 102 111 } 103 112 } 104 113 114 + const TtyRunError = error{ 115 + AccessDenied, 116 + Canceled, 117 + ConnectionResetByPeer, 118 + EndOfStream, 119 + InputOutput, 120 + InvalidCharacter, 121 + InvalidColorSpec, 122 + InvalidPadding, 123 + InvalidUTF8, 124 + IoctlError, 125 + IsDir, 126 + LockViolation, 127 + NoSpaceLeft, 128 + NotOpenForReading, 129 + OutOfMemory, 130 + Overflow, 131 + SocketUnconnected, 132 + SystemResources, 133 + Unexpected, 134 + WouldBlock, 135 + }; 136 + 105 137 /// read input from the tty. This is run in a separate thread 106 - fn ttyRun( 138 + fn ttyRun(self: *Self, paste_allocator: ?std.mem.Allocator) void { 139 + self._ttyRun(paste_allocator) catch {}; 140 + } 141 + 142 + fn _ttyRun( 107 143 self: *Self, 108 144 paste_allocator: ?std.mem.Allocator, 109 - ) !void { 145 + ) TtyRunError!void { 110 146 // Return early if we're in test mode to avoid infinite loops 111 147 if (builtin.is_test) return; 112 148
+1 -1
src/queue.zig
··· 96 96 const was_full = self.isFullLH(); 97 97 const item = self.popLH(); 98 98 if (was_full) { 99 - self.not_full.signal(); 99 + self.not_full.signal(self.io); 100 100 } 101 101 return item; 102 102 }
+32 -26
src/vxfw/App.zig
··· 11 11 12 12 const App = @This(); 13 13 14 + io: std.Io, 14 15 allocator: Allocator, 15 16 tty: vaxis.Tty, 16 17 vx: vaxis.Vaxis, ··· 27 28 /// Create an application. We require stable pointers to do the set up, so this will create an App 28 29 /// object on the heap. Call destroy when the app is complete to reset terminal state and release 29 30 /// resources 30 - pub fn init(allocator: Allocator) !App { 31 + pub fn init(io: std.Io, allocator: Allocator, env_map: *std.process.Environ.Map) !App { 31 32 var app: App = .{ 33 + .io = io, 32 34 .allocator = allocator, 33 35 .tty = undefined, 34 - .vx = try vaxis.init(allocator, .{ 36 + .vx = try vaxis.init(io, allocator, env_map, .{ 35 37 .system_clipboard_allocator = allocator, 36 38 .kitty_keyboard_flags = .{ 37 39 .report_events = true, 38 40 }, 39 41 }), 40 - .timers = std.ArrayList(vxfw.Tick){}, 42 + .timers = .empty, 41 43 .wants_focus = null, 42 44 .buffer = undefined, 43 45 }; 44 - app.tty = try vaxis.Tty.init(&app.buffer); 46 + app.tty = try vaxis.Tty.init(io, &app.buffer); 45 47 return app; 46 48 } 47 49 ··· 55 57 const tty = &self.tty; 56 58 const vx = &self.vx; 57 59 58 - var loop: EventLoop = .{ .tty = tty, .vaxis = vx }; 60 + var loop: EventLoop = .init(self.io, tty, vx); 59 61 try loop.start(); 60 62 defer loop.stop(); 61 63 62 64 // Send the init event 63 - loop.postEvent(.init); 65 + try loop.postEvent(.init); 64 66 // Also always initialize the app with a focus event 65 - loop.postEvent(.focus_in); 67 + try loop.postEvent(.focus_in); 66 68 67 69 try vx.enterAltScreen(tty.writer()); 68 70 try vx.queryTerminal(tty.writer(), 1 * std.time.ns_per_s); ··· 70 72 try vx.subscribeToColorSchemeUpdates(tty.writer()); 71 73 72 74 { 73 - // This part deserves a comment. loop.init installs a signal handler for the tty. We wait to 74 - // init the loop until we know if we need this handler. We don't need it if the terminal 75 - // supports in-band-resize 76 - if (!vx.state.in_band_resize) try loop.init(); 75 + // This part deserves a comment. loop.installResizeHandler installs 76 + // a signal handler for the tty. We wait to installResizeHandler the 77 + // loop until we know if we need this handler. We don't need it if the 78 + // terminal supports in-band-resize. 79 + if (!vx.state.in_band_resize) try loop.installResizeHandler(); 77 80 } 78 81 79 82 // NOTE: We don't use pixel mouse anywhere ··· 82 85 83 86 vxfw.DrawContext.init(vx.screen.width_method); 84 87 85 - const framerate: u64 = if (opts.framerate > 0) opts.framerate else 60; 86 88 // Calculate tick rate 87 - const tick_ms: u64 = @divFloor(std.time.ms_per_s, framerate); 89 + const framerate: u64 = if (opts.framerate > 0) opts.framerate else 60; 90 + const tick: std.Io.Duration = .fromNanoseconds(@divFloor(std.time.ns_per_s, framerate)); 88 91 89 92 // Set up arena and context 90 93 var arena = std.heap.ArenaAllocator.init(self.allocator); ··· 97 100 defer focus_handler.deinit(self.allocator); 98 101 99 102 // Timestamp of our next frame 100 - var next_frame_ms: u64 = @intCast(std.time.milliTimestamp()); 103 + var next_frame = std.Io.Timestamp.now(self.io, .real); 101 104 102 105 // Create our event context 103 106 var ctx: vxfw.EventContext = .{ 107 + .io = self.io, 104 108 .alloc = self.allocator, 105 109 .phase = .capturing, 106 - .cmds = vxfw.CommandList{}, 110 + .cmds = .empty, 107 111 .consume_event = false, 108 112 .redraw = false, 109 113 .quit = false, ··· 111 115 defer ctx.cmds.deinit(self.allocator); 112 116 113 117 while (true) { 114 - const now_ms: u64 = @intCast(std.time.milliTimestamp()); 115 - if (now_ms >= next_frame_ms) { 118 + const now = std.Io.Timestamp.now(self.io, .real); 119 + const duration = next_frame.durationTo(now); 120 + if (duration.nanoseconds <= 0) { 116 121 // Deadline exceeded. Schedule the next frame 117 - next_frame_ms = now_ms + tick_ms; 122 + next_frame = now.addDuration(tick); 118 123 } else { 119 124 // Sleep until the deadline 120 - std.Thread.sleep((next_frame_ms - now_ms) * std.time.ns_per_ms); 121 - next_frame_ms += tick_ms; 125 + try self.io.sleep(duration, .real); 126 + next_frame = next_frame.addDuration(tick); 122 127 } 123 128 124 129 try self.checkTimers(&ctx); 125 130 126 131 { 127 - loop.queue.lock(); 132 + try loop.queue.lock(); 128 133 defer loop.queue.unlock(); 129 134 while (loop.queue.drain()) |event| { 130 135 defer { ··· 296 301 } 297 302 298 303 fn checkTimers(self: *App, ctx: *vxfw.EventContext) anyerror!void { 299 - const now_ms = std.time.milliTimestamp(); 304 + const now: std.Io.Timestamp = .now(self.io, .real); 300 305 301 306 // timers are always sorted descending 302 307 while (self.timers.pop()) |tick| { 303 - if (now_ms < tick.deadline_ms) { 308 + const duration = now.durationTo(tick.deadline); 309 + if (duration.nanoseconds < 0) { 304 310 // re-add the timer 305 311 try self.timers.append(self.allocator, tick); 306 312 break; ··· 342 348 // For mouse events we store the last frame and use that for hit testing 343 349 const last_frame = surface; 344 350 345 - var hits = std.ArrayList(vxfw.HitResult){}; 351 + var hits: std.ArrayList(vxfw.HitResult) = .empty; 346 352 defer hits.deinit(app.allocator); 347 353 const sub: vxfw.SubSurface = .{ 348 354 .origin = .{ .row = 0, .col = 0 }, ··· 401 407 const last_frame = self.last_frame; 402 408 self.mouse = mouse; 403 409 404 - var hits = std.ArrayList(vxfw.HitResult){}; 410 + var hits: std.ArrayList(vxfw.HitResult) = .empty; 405 411 defer hits.deinit(app.allocator); 406 412 const sub: vxfw.SubSurface = .{ 407 413 .origin = .{ .row = 0, .col = 0 }, ··· 516 522 return .{ 517 523 .root = root, 518 524 .focused_widget = root, 519 - .path_to_focused = std.ArrayList(Widget){}, 525 + .path_to_focused = .empty, 520 526 }; 521 527 } 522 528
+11 -8
src/vxfw/vxfw.zig
··· 50 50 tick, // An event from a Tick command 51 51 init, // sent when the application starts 52 52 mouse_leave, // The mouse has left the widget 53 - mouse_enter, // The mouse has enterred the widget 53 + mouse_enter, // The mouse has entered the widget 54 54 }; 55 55 56 56 pub const Tick = struct { 57 - deadline_ms: i64, 57 + deadline: std.Io.Timestamp, 58 58 widget: Widget, 59 59 60 60 pub fn lessThan(_: void, lhs: Tick, rhs: Tick) bool { 61 - return lhs.deadline_ms > rhs.deadline_ms; 61 + return lhs.deadline.nanoseconds > rhs.deadline.nanoseconds; 62 62 } 63 63 64 64 pub fn in(io: std.Io, ms: u32, widget: Widget) Command { 65 - const now = std.Io.Timestamp.now(io, .real).toMilliseconds(); 66 - return .{ .tick = .{ 67 - .deadline_ms = now + ms, 68 - .widget = widget, 69 - } }; 65 + const now: std.Io.Timestamp = .now(io, .real); 66 + const deadline = now.addDuration(.fromMilliseconds(ms)); 67 + return .{ 68 + .tick = .{ 69 + .deadline = deadline, 70 + .widget = widget, 71 + }, 72 + }; 70 73 } 71 74 }; 72 75