this repo has no description
13
fork

Configure Feed

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

widgets(terminal): simple key encoding, alt screen

+106 -5
+27 -1
src/widgets/terminal/Screen.zig
··· 13 13 style: vaxis.Style = .{}, 14 14 uri: std.ArrayList(u8) = undefined, 15 15 uri_id: std.ArrayList(u8) = undefined, 16 - width: u8 = 0, 16 + width: u8 = 1, 17 17 18 18 wrapped: bool = false, 19 19 dirty: bool = true, 20 + 21 + pub fn erase(self: *Cell, bg: vaxis.Color) void { 22 + self.char.clearRetainingCapacity(); 23 + self.char.append(' ') catch unreachable; // we never completely free this list 24 + self.style = .{}; 25 + self.style.bg = bg; 26 + self.uri.clearRetainingCapacity(); 27 + self.uri_id.clearRetainingCapacity(); 28 + self.width = 1; 29 + self.wrapped = false; 30 + self.dirty = true; 31 + } 20 32 }; 21 33 22 34 pub const Cursor = struct { ··· 128 140 .char = .{ .grapheme = cell.char.items, .width = cell.width }, 129 141 .style = cell.style, 130 142 }; 143 + } 144 + 145 + /// returns true if the current cursor position is within the scrolling region 146 + pub fn withinScrollingRegion(self: Screen) bool { 147 + return self.scrolling_region.contains(self.cursor.col, self.cursor.row); 131 148 } 132 149 133 150 /// writes a cell to a location. 0 indexed ··· 288 305 self.cursor.pending_wrap = false; 289 306 self.cursor.col -= cnt; 290 307 } 308 + 309 + pub fn eraseRight(self: *Screen) void { 310 + self.cursor.pending_wrap = false; 311 + const end = (self.cursor.row * self.width) + (self.width); 312 + var i = (self.cursor.row * self.width) + self.cursor.col; 313 + while (i < end) : (i += 1) { 314 + self.buf[i].erase(self.cursor.style.bg); 315 + } 316 + }
+79 -4
src/widgets/terminal/Terminal.zig
··· 11 11 const Winsize = vaxis.Winsize; 12 12 const Screen = @import("Screen.zig"); 13 13 const DisplayWidth = @import("DisplayWidth"); 14 + const Key = vaxis.Key; 14 15 15 16 const grapheme = @import("grapheme"); 16 17 ··· 26 27 pub const Mode = struct { 27 28 origin: bool = false, 28 29 cursor: bool = true, 30 + sync: bool = false, 31 + }; 32 + 33 + pub const InputEvent = union(enum) { 34 + key_press: vaxis.Key, 29 35 }; 30 36 31 37 allocator: std.mem.Allocator, ··· 134 140 135 141 pub fn draw(self: *Terminal, win: vaxis.Window) !void { 136 142 // TODO: check sync 137 - if (self.back_mutex.tryLock()) { 143 + if (self.back_mutex.tryLock() and !self.mode.sync) { 138 144 defer self.back_mutex.unlock(); 139 145 try self.back_screen.copyTo(&self.front_screen); 140 146 } ··· 153 159 win.showCursor(self.front_screen.cursor.col, self.front_screen.cursor.row); 154 160 } 155 161 162 + pub fn update(self: *Terminal, event: InputEvent) !void { 163 + switch (event) { 164 + .key_press => |key| try self.encodeKey(key, true), 165 + } 166 + } 167 + 168 + fn opaqueWrite(ptr: *const anyopaque, buf: []const u8) !usize { 169 + const self: *const Terminal = @ptrCast(@alignCast(ptr)); 170 + return posix.write(self.pty.pty, buf); 171 + } 172 + 173 + pub fn anyWriter(self: *const Terminal) std.io.AnyWriter { 174 + return .{ 175 + .context = self, 176 + .writeFn = Terminal.opaqueWrite, 177 + }; 178 + } 179 + 156 180 fn opaqueRead(ptr: *const anyopaque, buf: []u8) !usize { 157 181 const self: *const Terminal = @ptrCast(@alignCast(ptr)); 158 182 return posix.read(self.pty.pty, buf); ··· 199 223 'B' => { // CUD 200 224 var iter = seq.iterator(u16); 201 225 const delta = iter.next() orelse 1; 202 - self.back_screen.cursor.row = @min(self.back_screen.height - 1, self.back_screen.cursor.row + delta); 226 + self.back_screen.cursor.row = @min( 227 + self.back_screen.height - 1, 228 + self.back_screen.cursor.row + delta, 229 + ); 230 + }, 231 + 'C' => { 232 + self.back_screen.cursor.pending_wrap = false; 233 + var iter = seq.iterator(u16); 234 + const delta = iter.next() orelse 1; 235 + const within = self.back_screen.withinScrollingRegion(); 236 + if (within) 237 + self.back_screen.cursor.col = @min( 238 + self.back_screen.cursor.col + delta, 239 + self.back_screen.scrolling_region.right, 240 + ) 241 + else 242 + self.back_screen.cursor.col = @min( 243 + self.back_screen.cursor.col + delta, 244 + self.back_screen.width, 245 + ); 203 246 }, 204 247 'H' => { // CUP 205 248 var iter = seq.iterator(u16); ··· 207 250 const col = iter.next() orelse 1; 208 251 self.back_screen.cursor.col = col - 1; 209 252 self.back_screen.cursor.row = row - 1; 253 + }, 254 + 'K' => { 255 + // TODO selective erase (private_marker == '?') 256 + var iter = seq.iterator(u8); 257 + const ps = iter.next() orelse 0; 258 + switch (ps) { 259 + 0 => self.back_screen.eraseRight(), 260 + 1 => {}, 261 + 2 => {}, 262 + else => continue, 263 + } 210 264 }, 211 265 'h', 'l' => { 212 266 var iter = seq.iterator(u16); ··· 256 310 257 311 pub fn setMode(self: *Terminal, mode: u16, val: bool) void { 258 312 switch (mode) { 259 - 25 => { 260 - self.mode.cursor = val; 313 + 25 => self.mode.cursor = val, 314 + 1049 => { 315 + if (val) 316 + self.back_screen = &self.back_screen_alt 317 + else 318 + self.back_screen = &self.back_screen_pri; 261 319 }, 320 + 2026 => self.mode.sync = val, 262 321 else => return, 263 322 } 264 323 } 324 + 325 + pub fn encodeKey(self: *Terminal, key: vaxis.Key, press: bool) !void { 326 + switch (press) { 327 + true => { 328 + if (key.text) |text| { 329 + try self.anyWriter().writeAll(text); 330 + return; 331 + } 332 + switch (key.codepoint) { 333 + 0x00...0x7F => try self.anyWriter().writeByte(@intCast(key.codepoint)), 334 + else => {}, 335 + } 336 + }, 337 + false => {}, 338 + } 339 + }