diff --git a/src/Window.zig b/src/Window.zig index 39d23b69c..eefa6596e 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -67,8 +67,7 @@ pty_stream: libuv.Tty, write_req_pool: SegmentedPool(libuv.WriteReq.T, WRITE_REQ_PREALLOC) = .{}, /// The pool of available buffers for writing to the pty. -/// TODO: [1]u8 is probably not right. -write_buf_pool: SegmentedPool([1]u8, WRITE_REQ_PREALLOC) = .{}, +write_buf_pool: SegmentedPool([64]u8, WRITE_REQ_PREALLOC) = .{}, /// Set this to true whenver an event occurs that we may want to wake up /// the event loop. Only set this from the main thread. @@ -246,6 +245,26 @@ pub fn shouldClose(self: Window) bool { return self.window.shouldClose(); } +/// Queue a write to the pty. +fn queueWrite(self: *Window, data: []const u8) !void { + // We go through and chunk the data if necessary to fit into + // our cached buffers that we can queue to the stream. + var i: usize = 0; + while (i < data.len) { + const req = try self.write_req_pool.get(); + const buf = try self.write_buf_pool.get(); + const end = @minimum(data.len, i + buf.len); + std.mem.copy(u8, buf, data[i..end]); + try self.pty_stream.write( + .{ .req = req }, + &[1][]u8{buf[0..(end - i)]}, + ttyWrite, + ); + + i += end; + } +} + fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { const tracy = trace(@src()); defer tracy.end(); @@ -288,14 +307,7 @@ fn charCallback(window: glfw.Window, codepoint: u21) void { const win = window.getUserPointer(Window) orelse return; // Write the character to the pty - const req = win.write_req_pool.get() catch unreachable; - const buf = win.write_buf_pool.get() catch unreachable; - buf[0] = @intCast(u8, codepoint); - win.pty_stream.write( - .{ .req = req }, - &[1][]u8{buf[0..1]}, - ttyWrite, - ) catch unreachable; + win.queueWrite(&[1]u8{@intCast(u8, codepoint)}) catch unreachable; } fn keyCallback( @@ -597,3 +609,17 @@ pub fn setAttribute(self: *Window, attr: terminal.Attribute) !void { log.warn("error setting attribute {}: {}", .{ attr, err }), } } + +pub fn deviceAttributes( + self: *Window, + req: terminal.DeviceAttributeReq, + params: []const u16, +) !void { + _ = params; + + switch (req) { + .primary => self.queueWrite("\x1B[?6c") catch |err| + log.warn("error queueing device attr response: {}", .{err}), + else => log.warn("unimplemented device attributes req: {}", .{req}), + } +} diff --git a/src/terminal/ansi.zig b/src/terminal/ansi.zig index eb77b49da..d1d581f4c 100644 --- a/src/terminal/ansi.zig +++ b/src/terminal/ansi.zig @@ -42,3 +42,10 @@ pub const Mode = enum(u16) { // Non-exhaustive so that @intToEnum never fails for unsupported modes. _, }; + +/// The device attribute request type (ESC [ c). +pub const DeviceAttributeReq = enum { + primary, // Blank + secondary, // > + tertiary, // = +}; diff --git a/src/terminal/main.zig b/src/terminal/main.zig index f434aeb57..b559e53e3 100644 --- a/src/terminal/main.zig +++ b/src/terminal/main.zig @@ -6,6 +6,7 @@ const sgr = @import("sgr.zig"); pub const Terminal = @import("Terminal.zig"); pub const Parser = @import("Parser.zig"); pub const Stream = stream.Stream; +pub const DeviceAttributeReq = ansi.DeviceAttributeReq; pub const Mode = ansi.Mode; pub const EraseDisplay = csi.EraseDisplay; pub const EraseLine = csi.EraseLine; diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 27df12714..5496da50d 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -235,6 +235,24 @@ pub fn Stream(comptime Handler: type) type { }, ) else log.warn("unimplemented CSI callback: {}", .{action}), + // c - Device Attributes (DA1) + 'c' => if (@hasDecl(T, "deviceAttributes")) { + const req: ansi.DeviceAttributeReq = switch (action.intermediates.len) { + 0 => ansi.DeviceAttributeReq.primary, + 1 => switch (action.intermediates[0]) { + '>' => ansi.DeviceAttributeReq.secondary, + '=' => ansi.DeviceAttributeReq.tertiary, + else => null, + }, + else => @as(?ansi.DeviceAttributeReq, null), + } orelse { + log.warn("invalid device attributes command: {}", .{action}); + return; + }; + + try self.handler.deviceAttributes(req, action.params); + } else log.warn("unimplemented CSI callback: {}", .{action}), + // VPA - Cursor Vertical Position Absolute 'd' => if (@hasDecl(T, "setCursorRow")) try self.handler.setCursorRow( switch (action.params.len) {