this repo has no description
13
fork

Configure Feed

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

image: remove testing alias

-309
-1
src/Image.zig
··· 1 1 const std = @import("std"); 2 2 const fmt = std.fmt; 3 3 const math = std.math; 4 - const testing = std.testing; 5 4 const base64 = std.base64.standard.Encoder; 6 5 const zigimg = @import("zigimg"); 7 6
-308
src/Tty.zig
··· 1 - const std = @import("std"); 2 - const builtin = @import("builtin"); 3 - const posix = std.posix; 4 - const Loop = @import("Loop.zig").Loop; 5 - const Parser = @import("Parser.zig"); 6 - const GraphemeCache = @import("GraphemeCache.zig"); 7 - const ctlseqs = @import("ctlseqs.zig"); 8 - const grapheme = @import("grapheme"); 9 - 10 - const log = std.log.scoped(.tty); 11 - 12 - const Tty = @This(); 13 - 14 - const Writer = std.io.Writer(posix.fd_t, posix.WriteError, posix.write); 15 - const BufferedWriter = std.io.BufferedWriter(4096, Writer); 16 - 17 - /// the original state of the terminal, prior to calling makeRaw 18 - termios: posix.termios, 19 - 20 - /// The file descriptor we are using for I/O 21 - fd: posix.fd_t, 22 - 23 - should_quit: bool = false, 24 - 25 - buffered_writer: BufferedWriter, 26 - 27 - /// initializes a Tty instance by opening /dev/tty and "making it raw" 28 - pub fn init() !Tty { 29 - // Open our tty 30 - const fd = try posix.open("/dev/tty", .{ .ACCMODE = .RDWR }, 0); 31 - 32 - // Set the termios of the tty 33 - const termios = try makeRaw(fd); 34 - 35 - return Tty{ 36 - .fd = fd, 37 - .termios = termios, 38 - .buffered_writer = std.io.bufferedWriter(Writer{ .context = fd }), 39 - }; 40 - } 41 - 42 - /// release resources associated with the Tty return it to its original state 43 - pub fn deinit(self: *Tty) void { 44 - // always show the cursor on exit 45 - _ = self.write(ctlseqs.show_cursor) catch {}; 46 - self.flush() catch {}; 47 - posix.tcsetattr(self.fd, .FLUSH, self.termios) catch |err| { 48 - log.err("couldn't restore terminal: {}", .{err}); 49 - }; 50 - if (builtin.os.tag != .macos) // closing /dev/tty may block indefinitely on macos 51 - posix.close(self.fd); 52 - } 53 - 54 - /// stops the run loop 55 - pub fn stop(self: *Tty) void { 56 - self.should_quit = true; 57 - _ = posix.write(self.fd, ctlseqs.device_status_report) catch |err| { 58 - log.err("TTY Stop Error: {}", .{err}); 59 - }; 60 - } 61 - 62 - /// read input from the tty 63 - pub fn run( 64 - self: *Tty, 65 - comptime Event: type, 66 - loop: *Loop(Event), 67 - grapheme_data: *const grapheme.GraphemeData, 68 - paste_allocator: ?std.mem.Allocator, 69 - ) !void { 70 - // get our initial winsize 71 - const winsize = try getWinsize(self.fd); 72 - if (@hasField(Event, "winsize")) { 73 - loop.postEvent(.{ .winsize = winsize }); 74 - } 75 - 76 - // Build a winch handler. We need build this struct to get an anonymous 77 - // function which can post the winsize event 78 - // TODO: more signals, move this outside of this function? 79 - const WinchHandler = struct { 80 - const Self = @This(); 81 - 82 - var vx_winch: *Loop(Event) = undefined; 83 - var fd: posix.fd_t = undefined; 84 - 85 - fn init(vx_arg: *Loop(Event), fd_arg: posix.fd_t) !void { 86 - vx_winch = vx_arg; 87 - fd = fd_arg; 88 - var act = posix.Sigaction{ 89 - .handler = .{ .handler = Self.handleWinch }, 90 - .mask = switch (builtin.os.tag) { 91 - .macos => 0, 92 - .linux => posix.empty_sigset, 93 - else => @compileError("os not supported"), 94 - }, 95 - .flags = 0, 96 - }; 97 - 98 - try posix.sigaction(posix.SIG.WINCH, &act, null); 99 - } 100 - 101 - fn handleWinch(_: c_int) callconv(.C) void { 102 - const ws = getWinsize(fd) catch { 103 - return; 104 - }; 105 - if (@hasField(Event, "winsize")) { 106 - vx_winch.postEvent(.{ .winsize = ws }); 107 - } 108 - } 109 - }; 110 - try WinchHandler.init(loop, self.fd); 111 - 112 - // initialize a grapheme cache 113 - var cache: GraphemeCache = .{}; 114 - 115 - var parser: Parser = .{ 116 - .grapheme_data = grapheme_data, 117 - }; 118 - 119 - // initialize the read buffer 120 - var buf: [1024]u8 = undefined; 121 - var read_start: usize = 0; 122 - // read loop 123 - while (!self.should_quit) { 124 - const n = try posix.read(self.fd, buf[read_start..]); 125 - var start: usize = 0; 126 - while (start < n) { 127 - const result = try parser.parse(buf[start..n], paste_allocator); 128 - if (result.n == 0) { 129 - // copy the read to the beginning. We don't use memcpy because 130 - // this could be overlapping, and it's also rare 131 - const initial_start = start; 132 - while (start < n) : (start += 1) { 133 - buf[start - initial_start] = buf[start]; 134 - } 135 - read_start = start - initial_start + 1; 136 - continue; 137 - } 138 - read_start = 0; 139 - start += result.n; 140 - 141 - const event = result.event orelse continue; 142 - switch (event) { 143 - .key_press => |key| { 144 - if (@hasField(Event, "key_press")) { 145 - // HACK: yuck. there has to be a better way 146 - var mut_key = key; 147 - if (key.text) |text| { 148 - mut_key.text = cache.put(text); 149 - } 150 - loop.postEvent(.{ .key_press = mut_key }); 151 - } 152 - }, 153 - .key_release => |*key| { 154 - if (@hasField(Event, "key_release")) { 155 - // HACK: yuck. there has to be a better way 156 - var mut_key = key; 157 - if (key.text) |text| { 158 - mut_key.text = cache.put(text); 159 - } 160 - loop.postEvent(.{ .key_release = mut_key }); 161 - } 162 - }, 163 - .mouse => |mouse| { 164 - if (@hasField(Event, "mouse")) { 165 - loop.postEvent(.{ .mouse = loop.vaxis.translateMouse(mouse) }); 166 - } 167 - }, 168 - .focus_in => { 169 - if (@hasField(Event, "focus_in")) { 170 - loop.postEvent(.focus_in); 171 - } 172 - }, 173 - .focus_out => { 174 - if (@hasField(Event, "focus_out")) { 175 - loop.postEvent(.focus_out); 176 - } 177 - }, 178 - .paste_start => { 179 - if (@hasField(Event, "paste_start")) { 180 - loop.postEvent(.paste_start); 181 - } 182 - }, 183 - .paste_end => { 184 - if (@hasField(Event, "paste_end")) { 185 - loop.postEvent(.paste_end); 186 - } 187 - }, 188 - .paste => |text| { 189 - if (@hasField(Event, "paste")) { 190 - loop.postEvent(.{ .paste = text }); 191 - } else { 192 - if (paste_allocator) |_| 193 - paste_allocator.?.free(text); 194 - } 195 - }, 196 - .color_report => |report| { 197 - if (@hasField(Event, "color_report")) { 198 - loop.postEvent(.{ .color_report = report }); 199 - } 200 - }, 201 - .color_scheme => |scheme| { 202 - if (@hasField(Event, "color_scheme")) { 203 - loop.postEvent(.{ .color_scheme = scheme }); 204 - } 205 - }, 206 - .cap_kitty_keyboard => { 207 - log.info("kitty keyboard capability detected", .{}); 208 - loop.vaxis.caps.kitty_keyboard = true; 209 - }, 210 - .cap_kitty_graphics => { 211 - if (!loop.vaxis.caps.kitty_graphics) { 212 - log.info("kitty graphics capability detected", .{}); 213 - loop.vaxis.caps.kitty_graphics = true; 214 - } 215 - }, 216 - .cap_rgb => { 217 - log.info("rgb capability detected", .{}); 218 - loop.vaxis.caps.rgb = true; 219 - }, 220 - .cap_unicode => { 221 - log.info("unicode capability detected", .{}); 222 - loop.vaxis.caps.unicode = .unicode; 223 - loop.vaxis.screen.width_method = .unicode; 224 - }, 225 - .cap_sgr_pixels => { 226 - log.info("pixel mouse capability detected", .{}); 227 - loop.vaxis.caps.sgr_pixels = true; 228 - }, 229 - .cap_color_scheme_updates => { 230 - log.info("color_scheme_updates capability detected", .{}); 231 - loop.vaxis.caps.color_scheme_updates = true; 232 - }, 233 - .cap_da1 => { 234 - std.Thread.Futex.wake(&loop.vaxis.query_futex, 10); 235 - }, 236 - } 237 - } 238 - } 239 - } 240 - 241 - /// write to the tty. These writes are buffered and require calling flush to 242 - /// flush writes to the tty 243 - pub fn write(self: *Tty, bytes: []const u8) !usize { 244 - return self.buffered_writer.write(bytes); 245 - } 246 - 247 - /// flushes the write buffer to the tty 248 - pub fn flush(self: *Tty) !void { 249 - try self.buffered_writer.flush(); 250 - } 251 - 252 - /// makeRaw enters the raw state for the terminal. 253 - pub fn makeRaw(fd: posix.fd_t) !posix.termios { 254 - const state = try posix.tcgetattr(fd); 255 - var raw = state; 256 - // see termios(3) 257 - raw.iflag.IGNBRK = false; 258 - raw.iflag.BRKINT = false; 259 - raw.iflag.PARMRK = false; 260 - raw.iflag.ISTRIP = false; 261 - raw.iflag.INLCR = false; 262 - raw.iflag.IGNCR = false; 263 - raw.iflag.ICRNL = false; 264 - raw.iflag.IXON = false; 265 - 266 - raw.oflag.OPOST = false; 267 - 268 - raw.lflag.ECHO = false; 269 - raw.lflag.ECHONL = false; 270 - raw.lflag.ICANON = false; 271 - raw.lflag.ISIG = false; 272 - raw.lflag.IEXTEN = false; 273 - 274 - raw.cflag.CSIZE = .CS8; 275 - raw.cflag.PARENB = false; 276 - 277 - raw.cc[@intFromEnum(posix.V.MIN)] = 1; 278 - raw.cc[@intFromEnum(posix.V.TIME)] = 0; 279 - try posix.tcsetattr(fd, .FLUSH, raw); 280 - return state; 281 - } 282 - 283 - /// The size of the terminal screen 284 - pub const Winsize = struct { 285 - rows: usize, 286 - cols: usize, 287 - x_pixel: usize, 288 - y_pixel: usize, 289 - }; 290 - 291 - pub fn getWinsize(fd: posix.fd_t) !Winsize { 292 - var winsize = posix.winsize{ 293 - .ws_row = 0, 294 - .ws_col = 0, 295 - .ws_xpixel = 0, 296 - .ws_ypixel = 0, 297 - }; 298 - 299 - const err = posix.system.ioctl(fd, posix.T.IOCGWINSZ, @intFromPtr(&winsize)); 300 - if (posix.errno(err) == .SUCCESS) 301 - return Winsize{ 302 - .rows = winsize.ws_row, 303 - .cols = winsize.ws_col, 304 - .x_pixel = winsize.ws_xpixel, 305 - .y_pixel = winsize.ws_ypixel, 306 - }; 307 - return error.IoctlError; 308 - }