this repo has no description
13
fork

Configure Feed

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

widgets(terminal): handle exiting events

+160 -9
+29 -2
src/widgets/terminal/Command.zig
··· 3 3 const std = @import("std"); 4 4 const builtin = @import("builtin"); 5 5 const Pty = @import("Pty.zig"); 6 + const Terminal = @import("Terminal.zig"); 6 7 7 8 const posix = std.posix; 8 9 ··· 40 41 try posix.dup2(self.pty.tty, std.posix.STDOUT_FILENO); 41 42 try posix.dup2(self.pty.tty, std.posix.STDERR_FILENO); 42 43 43 - // posix.close(self.pty.tty); 44 - // if (self.pty.pty > 2) posix.close(self.pty.pty); 44 + posix.close(self.pty.tty); 45 + if (self.pty.pty > 2) posix.close(self.pty.pty); 45 46 46 47 // exec 47 48 const err = std.posix.execvpeZ(argv_buf.ptr[0].?, argv_buf.ptr, envp); 48 49 _ = err catch {}; 50 + @panic("a"); 51 + // const EOT = "\x04"; 52 + // _ = std.posix.write(self.pty.tty, EOT) catch {}; 49 53 } 50 54 55 + var act = posix.Sigaction{ 56 + .handler = .{ .handler = handleSigChild }, 57 + .mask = switch (builtin.os.tag) { 58 + .macos => 0, 59 + .linux => posix.empty_sigset, 60 + else => @compileError("os not supported"), 61 + }, 62 + .flags = 0, 63 + }; 64 + try posix.sigaction(posix.SIG.CHLD, &act, null); 65 + 51 66 // we are the parent 52 67 self.pid = @intCast(pid); 53 68 return; 69 + } 70 + 71 + fn handleSigChild(_: c_int) callconv(.C) void { 72 + std.log.err("sigchild", .{}); 73 + const result = std.posix.waitpid(-1, 0); 74 + 75 + Terminal.global_vt_mutex.lock(); 76 + defer Terminal.global_vt_mutex.unlock(); 77 + if (Terminal.global_vts) |vts| { 78 + var vt = vts.get(result.pid) orelse return; 79 + vt.event_queue.push(.exited); 80 + } 54 81 } 55 82 56 83 pub fn kill(self: *Command) void {
+68 -1
src/widgets/terminal/Screen.zig
··· 29 29 self.wrapped = false; 30 30 self.dirty = true; 31 31 } 32 + 33 + pub fn copyFrom(self: *Cell, src: Cell) !void { 34 + self.char.clearRetainingCapacity(); 35 + try self.char.appendSlice(src.char.items); 36 + self.style = src.style; 37 + self.uri.clearRetainingCapacity(); 38 + try self.uri.appendSlice(src.uri.items); 39 + self.uri_id.clearRetainingCapacity(); 40 + try self.uri_id.appendSlice(src.uri_id.items); 41 + self.width = src.width; 42 + self.wrapped = src.wrapped; 43 + 44 + self.dirty = true; 45 + } 32 46 }; 33 47 34 48 pub const Cursor = struct { ··· 198 212 // Inside scrolling region *and* at bottom of screen, we scroll contents up and insert a 199 213 // blank line 200 214 // TODO: scrollback if scrolling region is entire visible screen 201 - @panic("TODO"); 215 + try self.deleteLine(1); 216 + return; 202 217 } 203 218 self.cursor.row += 1; 204 219 } ··· 311 326 self.buf[i].erase(self.cursor.style.bg); 312 327 } 313 328 } 329 + 330 + /// delete n lines from te bottom of te scrolling region 331 + pub fn deleteLine(self: *Screen, n: usize) !void { 332 + if (n == 0) return; 333 + 334 + // Don't delete if outside scroll region 335 + if (!self.withinScrollingRegion()) return; 336 + 337 + self.cursor.pending_wrap = false; 338 + 339 + // Number of rows from here to bottom of scroll region or n 340 + const cnt = @min(self.scrolling_region.bottom - self.cursor.row + 1, n); 341 + const stride = (self.width) * cnt; 342 + 343 + var row: usize = self.scrolling_region.top; 344 + while (row <= self.scrolling_region.bottom) : (row += 1) { 345 + var col: usize = self.scrolling_region.left; 346 + while (col <= self.scrolling_region.right) : (col += 1) { 347 + const i = (row * self.width) + col; 348 + if (row + cnt > self.scrolling_region.bottom) 349 + self.buf[i].erase(self.cursor.style.bg) 350 + else 351 + try self.buf[i].copyFrom(self.buf[i + stride]); 352 + } 353 + } 354 + } 355 + 356 + /// insert n lines at the top of the scrolling region 357 + pub fn insertLine(self: *Screen, n: usize) !void { 358 + if (n == 0) return; 359 + 360 + // Don't insert if outside scroll region 361 + if (!self.withinScrollingRegion()) return; 362 + 363 + self.cursor.pending_wrap = false; 364 + 365 + // Number of rows from here to top of scroll region or n 366 + const cnt = @min(self.cursor.row - self.scrolling_region.top + 1, n); 367 + const stride = (self.width) * cnt; 368 + 369 + var row: usize = self.scrolling_region.bottom; 370 + while (row > self.scrolling_region.top) : (row -= 1) { 371 + var col: usize = self.scrolling_region.left; 372 + while (col <= self.scrolling_region.right) : (col += 1) { 373 + const i = (row * self.width) + col; 374 + if (row - cnt < self.scrolling_region.top) 375 + self.buf[i].erase(self.cursor.style.bg) 376 + else 377 + try self.buf[i].copyFrom(self.buf[i - stride]); 378 + } 379 + } 380 + }
+63 -6
src/widgets/terminal/Terminal.zig
··· 12 12 const Screen = @import("Screen.zig"); 13 13 const DisplayWidth = @import("DisplayWidth"); 14 14 const Key = vaxis.Key; 15 + const Queue = vaxis.Queue(Event, 16); 16 + 17 + pub const Event = union(enum) { 18 + exited, 19 + bell, 20 + }; 15 21 16 22 const grapheme = @import("grapheme"); 17 23 ··· 33 39 pub const InputEvent = union(enum) { 34 40 key_press: vaxis.Key, 35 41 }; 42 + 43 + pub var global_vt_mutex: std.Thread.Mutex = .{}; 44 + pub var global_vts: ?std.AutoHashMap(i32, *Terminal) = null; 36 45 37 46 allocator: std.mem.Allocator, 38 47 scrollback_size: usize, ··· 58 67 59 68 mode: Mode = .{}, 60 69 61 - pending_events: struct { 62 - bell: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), 63 - } = .{}, 70 + event_queue: Queue = .{}, 64 71 65 72 /// initialize a Terminal. This sets the size of the underlying pty and allocates the sizes of the 66 73 /// screen ··· 93 100 /// release all resources of the Terminal 94 101 pub fn deinit(self: *Terminal) void { 95 102 self.should_quit = true; 103 + 104 + pid: { 105 + global_vt_mutex.lock(); 106 + defer global_vt_mutex.unlock(); 107 + var vts = global_vts orelse break :pid; 108 + if (self.cmd.pid) |pid| 109 + _ = vts.remove(pid); 110 + if (vts.count() == 0) vts.deinit(); 111 + } 96 112 self.cmd.kill(); 97 113 if (self.thread) |thread| { 98 114 // write an EOT into the tty to trigger a read on our thread ··· 112 128 self.back_screen = &self.back_screen_pri; 113 129 114 130 try self.cmd.spawn(self.allocator); 131 + 132 + { 133 + // add to our global list 134 + global_vt_mutex.lock(); 135 + defer global_vt_mutex.unlock(); 136 + if (global_vts == null) 137 + global_vts = std.AutoHashMap(i32, *Terminal).init(self.allocator); 138 + if (self.cmd.pid) |pid| 139 + try global_vts.?.put(pid, self); 140 + } 141 + 115 142 self.thread = try std.Thread.spawn(.{}, Terminal.run, .{self}); 116 143 } 117 144 ··· 160 187 } 161 188 } 162 189 190 + pub fn tryEvent(self: *Terminal) ?Event { 191 + return self.event_queue.tryPop(); 192 + } 193 + 163 194 pub fn update(self: *Terminal, event: InputEvent) !void { 164 195 switch (event) { 165 196 .key_press => |key| try self.encodeKey(key, true), ··· 216 247 } 217 248 }, 218 249 .c0 => |b| try self.handleC0(b), 219 - .escape => |str| std.log.err("unhandled escape: {s}", .{str}), 250 + .escape => |_| {}, // std.log.err("unhandled escape: {s}", .{str}), 220 251 .ss2 => |ss2| std.log.err("unhandled ss2: {c}", .{ss2}), 221 252 .ss3 => |ss3| std.log.err("unhandled ss3: {c}", .{ss3}), 222 253 .csi => |seq| { ··· 250 281 self.back_screen.width, 251 282 ); 252 283 }, 253 - 'H' => { // CUP 284 + 'H', 'f' => { // CUP 254 285 var iter = seq.iterator(u16); 255 286 const row = iter.next() orelse 1; 256 287 const col = iter.next() orelse 1; ··· 268 299 else => continue, 269 300 } 270 301 }, 302 + 'L' => { 303 + var iter = seq.iterator(u16); 304 + const n = iter.next() orelse 1; 305 + try self.back_screen.insertLine(n); 306 + }, 307 + 'M' => { 308 + var iter = seq.iterator(u16); 309 + const n = iter.next() orelse 1; 310 + try self.back_screen.deleteLine(n); 311 + }, 271 312 'h', 'l' => { 272 313 var iter = seq.iterator(u16); 273 314 const mode = iter.next() orelse continue; ··· 293 334 } 294 335 } 295 336 }, 337 + 'r' => { 338 + if (seq.intermediate) |_| { 339 + // TODO: XTRESTORE 340 + continue; 341 + } 342 + if (seq.private_marker) |_| { 343 + // TODO: DECCARA 344 + continue; 345 + } 346 + // DECSTBM 347 + var iter = seq.iterator(u16); 348 + const top = iter.next() orelse 1; 349 + const bottom = iter.next() orelse self.back_screen.height; 350 + self.back_screen.scrolling_region.top = top - 1; 351 + self.back_screen.scrolling_region.bottom = bottom - 1; 352 + }, 296 353 else => std.log.err("unhandled CSI: {}", .{seq}), 297 354 } 298 355 }, ··· 307 364 .NUL, .SOH, .STX => {}, 308 365 .EOT => {}, // we send EOT to quit the read thread 309 366 .ENQ => {}, 310 - .BEL => self.pending_events.bell.store(true, .unordered), 367 + .BEL => self.event_queue.push(.bell), 311 368 .BS => self.back_screen.cursorLeft(1), 312 369 .HT => {}, // TODO: HT 313 370 .LF, .VT, .FF => try self.back_screen.index(),