From ce778fefaf406b077740cfa650a5646685ac28d7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 21 Nov 2022 15:06:25 -0800 Subject: [PATCH] OSC 52 clipboard write --- src/Window.zig | 33 +++++++++++++++++++++++++++++++-- src/termio/Exec.zig | 8 +++++++- src/window/message.zig | 8 ++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index b5ec4c470..f5bf490e5 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -594,6 +594,15 @@ pub fn handleMessage(self: *Window, msg: Message) !void { .cell_size => |size| try self.setCellSize(size), .clipboard_read => |kind| try self.clipboardRead(kind), + + .clipboard_write => |req| switch (req) { + .small => |v| try self.clipboardWrite(v.data[0..v.len]), + .stable => |v| try self.clipboardWrite(v), + .alloc => |v| { + defer v.alloc.free(v.data); + try self.clipboardWrite(v.data); + }, + }, } } @@ -608,13 +617,14 @@ fn clipboardRead(self: *const Window, kind: u8) !void { // This must hold the base64 encoded data PLUS the OSC code surrounding it. const enc = std.base64.standard.Encoder; const size = enc.calcSize(data.len); - var buf = try self.alloc.alloc(u8, size + 8); // 8 for OSC + var buf = try self.alloc.alloc(u8, size + 9); // const for OSC defer self.alloc.free(buf); // Wrap our data with the OSC code const prefix = try std.fmt.bufPrint(buf, "\x1b]52;{c};", .{kind}); assert(prefix.len == 7); - buf[buf.len - 1] = '\x1b'; + buf[buf.len - 2] = '\x1b'; + buf[buf.len - 1] = '\\'; // Do the base64 encoding const encoded = enc.encode(buf[prefix.len..], data); @@ -627,6 +637,25 @@ fn clipboardRead(self: *const Window, kind: u8) !void { self.io_thread.wakeup.send() catch {}; } +fn clipboardWrite(self: *const Window, data: []const u8) !void { + const dec = std.base64.standard.Decoder; + + // Build buffer + const size = try dec.calcSizeForSlice(data); + var buf = try self.alloc.allocSentinel(u8, size, 0); + defer self.alloc.free(buf); + buf[buf.len] = 0; + + // Decode + try dec.decode(buf, data); + assert(buf[buf.len] == 0); + + glfw.setClipboardString(buf) catch |err| { + log.err("error setting clipboard string err={}", .{err}); + return; + }; +} + /// Change the cell size for the terminal grid. This can happen as /// a result of changing the font size at runtime. fn setCellSize(self: *Window, size: renderer.CellSize) !void { diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index fc7be7c28..6bb59501a 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -829,6 +829,12 @@ const StreamHandler = struct { return; } - unreachable; + // Write clipboard contents + _ = self.window_mailbox.push(.{ + .clipboard_write = try Window.Message.WriteReq.init( + self.alloc, + data, + ), + }, .{ .forever = {} }); } }; diff --git a/src/window/message.zig b/src/window/message.zig index a6b49f753..159b6e92a 100644 --- a/src/window/message.zig +++ b/src/window/message.zig @@ -1,9 +1,14 @@ const App = @import("../App.zig"); const Window = @import("../Window.zig"); const renderer = @import("../renderer.zig"); +const termio = @import("../termio.zig"); /// The message types that can be sent to a single window. pub const Message = union(enum) { + /// Represents a write request. Magic number comes from the max size + /// we want this union to be. + pub const WriteReq = termio.MessageData(u8, 256); + /// Set the title of the window. /// TODO: we should change this to a "WriteReq" style structure in /// the termio message so that we can more efficiently send strings @@ -15,6 +20,9 @@ pub const Message = union(enum) { /// Read the clipboard and write to the pty. clipboard_read: u8, + + /// Write the clipboard contents. + clipboard_write: WriteReq, }; /// A window mailbox.