Compare commits
3 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2178c4d839 | |||
| 130e668dd5 | |||
| ec524f039c |
3 changed files with 79 additions and 32 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
.{
|
.{
|
||||||
.name = .tpb,
|
.name = .tpb,
|
||||||
.version = "0.1.1",
|
.version = "0.1.2",
|
||||||
.fingerprint = 0x203a601c46df0265,
|
.fingerprint = 0x203a601c46df0265,
|
||||||
.minimum_zig_version = "0.16.0",
|
.minimum_zig_version = "0.16.0",
|
||||||
.paths = .{
|
.paths = .{
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,29 @@ base64buffer: std.ArrayList(u8),
|
||||||
leftover_bytes: [3]u8,
|
leftover_bytes: [3]u8,
|
||||||
leftover_count: u8,
|
leftover_count: u8,
|
||||||
|
|
||||||
|
/// This is to wrap the OSC buffer in the tmux passthrough sequence. This lets
|
||||||
|
/// OSC52 clipboard requests go directly to the host instead of going to a tmux
|
||||||
|
/// buffer.
|
||||||
|
/// https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it
|
||||||
|
/// Since tmux v3.7, there is a `get-clipboard` option and when it is set to
|
||||||
|
/// `request` any OSC52 query that comes to it will cause the tmux buffer to
|
||||||
|
/// update before sending the information.
|
||||||
|
/// https://raw.githubusercontent.com/tmux/tmux/3.7b/CHANGES
|
||||||
|
/// This still wraps the tmux session in an escape whenever one is detected
|
||||||
|
/// since this option is still incredibly new.
|
||||||
|
is_tmux: bool,
|
||||||
|
|
||||||
const base64encoder =
|
const base64encoder =
|
||||||
std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '=');
|
std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '=');
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, io: std.Io, buf_size: usize) !OSC52 {
|
pub fn init(alloc: std.mem.Allocator, io: std.Io, buf_size: usize, is_tmux: bool) !OSC52 {
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.io = io,
|
.io = io,
|
||||||
.base64buffer = try std.ArrayList(u8).initCapacity(alloc, buf_size),
|
.base64buffer = try std.ArrayList(u8).initCapacity(alloc, buf_size),
|
||||||
.leftover_bytes = undefined,
|
.leftover_bytes = undefined,
|
||||||
.leftover_count = 0,
|
.leftover_count = 0,
|
||||||
|
.is_tmux = is_tmux,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,29 +84,58 @@ pub fn writePasteboard(self: *OSC52) anyerror!void {
|
||||||
try self.encodeRemaining();
|
try self.encodeRemaining();
|
||||||
|
|
||||||
// https://ghostty.org/docs/vt/osc/52
|
// https://ghostty.org/docs/vt/osc/52
|
||||||
const osc_header = &[_]u8{
|
|
||||||
0x1b, // ESC
|
|
||||||
0x5d, // ']'
|
|
||||||
0x35, 0x32, // Code 52
|
|
||||||
0x3b, // ';'
|
|
||||||
// 0x70, // 'p' Primary Clipboard
|
|
||||||
0x63, // 'c' Standard Clipboard
|
|
||||||
0x3b, // ';'
|
|
||||||
};
|
|
||||||
const osc_footer = &[_]u8{
|
|
||||||
0x1b, // ESC
|
|
||||||
0x5c, // '\'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write data to stdout
|
// Write data to stdout
|
||||||
const stdout = std.Io.File.stdout();
|
const stdout = std.Io.File.stdout();
|
||||||
try stdout.writeStreamingAll(self.io, osc_header);
|
try stdout.writeStreamingAll(self.io, self.osc_header());
|
||||||
try stdout.writeStreamingAll(self.io, self.base64buffer.items);
|
try stdout.writeStreamingAll(self.io, self.base64buffer.items);
|
||||||
try stdout.writeStreamingAll(self.io, osc_footer);
|
try stdout.writeStreamingAll(self.io, self.osc_footer());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn osc_header(self: *OSC52) []const u8 {
|
||||||
|
if (self.is_tmux) {
|
||||||
|
return &[_]u8{
|
||||||
|
0x1b, // ESC
|
||||||
|
'P', 't', 'm', 'u', 'x', ';', // tmux escape
|
||||||
|
0x1b, 0x1b, // ESC ESC
|
||||||
|
0x5d, // ']'
|
||||||
|
0x35, 0x32, // Code 52
|
||||||
|
0x3b, // ';'
|
||||||
|
// 0x70, // 'p' Primary Clipboard
|
||||||
|
0x63, // 'c' Standard Clipboard
|
||||||
|
0x3b, // ';'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return &[_]u8{
|
||||||
|
0x1b, // ESC
|
||||||
|
0x5d, // ']'
|
||||||
|
0x35, 0x32, // Code 52
|
||||||
|
0x3b, // ';'
|
||||||
|
// 0x70, // 'p' Primary Clipboard
|
||||||
|
0x63, // 'c' Standard Clipboard
|
||||||
|
0x3b, // ';'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn osc_footer(self: *OSC52) []const u8 {
|
||||||
|
if (self.is_tmux) {
|
||||||
|
return &[_]u8{
|
||||||
|
0x1b, 0x1b, // ESC ESC
|
||||||
|
0x5c, // '\'
|
||||||
|
0x1b, // ESC
|
||||||
|
0x5c, // '\'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return &[_]u8{
|
||||||
|
0x1b, // ESC
|
||||||
|
0x5c, // '\'
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "basic base64 encode" {
|
test "basic base64 encode" {
|
||||||
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
|
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024, false);
|
||||||
defer osc.free();
|
defer osc.free();
|
||||||
try osc.writeCopyBuffer("part1");
|
try osc.writeCopyBuffer("part1");
|
||||||
try osc.encodeRemaining();
|
try osc.encodeRemaining();
|
||||||
|
|
@ -101,7 +143,7 @@ test "basic base64 encode" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "multi part base64 encode" {
|
test "multi part base64 encode" {
|
||||||
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
|
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024, false);
|
||||||
defer osc.free();
|
defer osc.free();
|
||||||
try osc.writeCopyBuffer("part1");
|
try osc.writeCopyBuffer("part1");
|
||||||
try osc.writeCopyBuffer("part2");
|
try osc.writeCopyBuffer("part2");
|
||||||
|
|
@ -110,7 +152,7 @@ test "multi part base64 encode" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "tiny buffers base64 encode" {
|
test "tiny buffers base64 encode" {
|
||||||
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
|
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024, false);
|
||||||
defer osc.free();
|
defer osc.free();
|
||||||
try osc.writeCopyBuffer("part1");
|
try osc.writeCopyBuffer("part1");
|
||||||
try osc.writeCopyBuffer("part2");
|
try osc.writeCopyBuffer("part2");
|
||||||
|
|
@ -126,7 +168,7 @@ test "tiny buffers base64 encode" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "big buffers base64 encode" {
|
test "big buffers base64 encode" {
|
||||||
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
|
var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024, false);
|
||||||
defer osc.free();
|
defer osc.free();
|
||||||
try osc.writeCopyBuffer("part1part1part1part1part1part1part1part1part1|");
|
try osc.writeCopyBuffer("part1part1part1part1part1part1part1part1part1|");
|
||||||
try osc.writeCopyBuffer("part2part2part2part2part2part2part2part2part2");
|
try osc.writeCopyBuffer("part2part2part2part2part2part2part2part2part2");
|
||||||
|
|
|
||||||
27
src/main.zig
27
src/main.zig
|
|
@ -28,8 +28,11 @@ pub fn main(init: std.process.Init) !void {
|
||||||
init.environ_map.get("SSH_CLIENT") != null or
|
init.environ_map.get("SSH_CLIENT") != null or
|
||||||
init.environ_map.get("SSH_TTY") != null;
|
init.environ_map.get("SSH_TTY") != null;
|
||||||
|
|
||||||
|
const is_tmux =
|
||||||
|
init.environ_map.get("TMUX") != null;
|
||||||
|
|
||||||
if (is_ssh) {
|
if (is_ssh) {
|
||||||
var clip = try Clip.OSC52.init(alloc, io, size);
|
var clip = try Clip.OSC52.init(alloc, io, size, is_tmux);
|
||||||
defer clip.free();
|
defer clip.free();
|
||||||
try run(io, &clip);
|
try run(io, &clip);
|
||||||
return;
|
return;
|
||||||
|
|
@ -45,21 +48,23 @@ pub fn main(init: std.process.Init) !void {
|
||||||
// I don't like this but it will stay until I review how dispatch works
|
// I don't like this but it will stay until I review how dispatch works
|
||||||
// with zig since I will probably be refactoring there
|
// with zig since I will probably be refactoring there
|
||||||
linux: {
|
linux: {
|
||||||
const xdg_type = init.environ_map.get("XDG_SESSION_TYPE") orelse "";
|
if (comptime os == .linux) {
|
||||||
|
const xdg_type = init.environ_map.get("XDG_SESSION_TYPE") orelse "";
|
||||||
|
|
||||||
if (std.mem.eql(u8, xdg_type, "wayland")) {
|
if (std.mem.eql(u8, xdg_type, "wayland")) {
|
||||||
var clip = Clip.ForkOut.init(io, &[_][]const u8{"wl-copy"}) catch break :linux;
|
var clip = Clip.ForkOut.init(io, &[_][]const u8{"wl-copy"}) catch break :linux;
|
||||||
try run(io, &clip);
|
try run(io, &clip);
|
||||||
return;
|
return;
|
||||||
} else if (std.mem.eql(u8, xdg_type, "x11")) {
|
} else if (std.mem.eql(u8, xdg_type, "x11")) {
|
||||||
var clip = Clip.ForkOut.init(io, &[_][]const u8{ "xclip", "-selection", "clipboard" }) catch break :linux;
|
var clip = Clip.ForkOut.init(io, &[_][]const u8{ "xclip", "-selection", "clipboard" }) catch break :linux;
|
||||||
try run(io, &clip);
|
try run(io, &clip);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback Clipboard
|
// Fallback Clipboard
|
||||||
var clip = try Clip.OSC52.init(alloc, io, size);
|
var clip = try Clip.OSC52.init(alloc, io, size, is_tmux);
|
||||||
defer clip.free();
|
defer clip.free();
|
||||||
try run(io, &clip);
|
try run(io, &clip);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue