Compare commits

..

No commits in common. "main" and "0.1.0" have entirely different histories.
main ... 0.1.0

5 changed files with 23 additions and 112 deletions

View file

@ -1,6 +1,6 @@
.{ .{
.name = .tpb, .name = .tpb,
.version = "0.1.2", .version = "0.1.0",
.fingerprint = 0x203a601c46df0265, .fingerprint = 0x203a601c46df0265,
.minimum_zig_version = "0.16.0", .minimum_zig_version = "0.16.0",
.paths = .{ .paths = .{

View file

@ -7,8 +7,6 @@ pub const OSC52 = @import("Clip/OSC52.zig");
pub const NSPasteboard = pub const NSPasteboard =
if (builtin.target.os.tag == .macos) @import("Clip/NSPasteboard.zig") else void; if (builtin.target.os.tag == .macos) @import("Clip/NSPasteboard.zig") else void;
pub const ForkOut = @import("Clip/ForkOut.zig");
// I am not sure if this is super idiomatic Zig or if this is bad practice, but // I am not sure if this is super idiomatic Zig or if this is bad practice, but
// by ensuring that all Clip backends implement the same methods, we are able // by ensuring that all Clip backends implement the same methods, we are able
// to use the `anytype` type and we are able to have an interface that is able // to use the `anytype` type and we are able to have an interface that is able

View file

@ -1,24 +0,0 @@
/// ForkOut Clipboard backend
/// Fulfills `Clip` interface
pub const ForkOut = @This();
const std = @import("std");
extern fn sendPB(text: [*:0]const u8) void;
extern fn initPB() void;
io: std.Io,
child: std.process.Child,
pub fn init(io: std.Io, argv: []const []const u8) !ForkOut {
return .{ .child = try std.process.spawn(io, .{ .argv = argv, .stdin = .pipe }), .io = io };
}
pub fn writeCopyBuffer(self: *ForkOut, buf: []const u8) anyerror!void {
try self.child.stdin.?.writeStreamingAll(self.io, buf);
}
pub fn writePasteboard(self: *ForkOut) anyerror!void {
self.child.stdin.?.close(self.io);
}

View file

@ -12,29 +12,16 @@ 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, is_tmux: bool) !OSC52 { pub fn init(alloc: std.mem.Allocator, io: std.Io, buf_size: usize) !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,
}; };
} }
@ -84,58 +71,29 @@ 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, self.osc_header()); try stdout.writeStreamingAll(self.io, osc_header);
try stdout.writeStreamingAll(self.io, self.base64buffer.items); try stdout.writeStreamingAll(self.io, self.base64buffer.items);
try stdout.writeStreamingAll(self.io, self.osc_footer()); try stdout.writeStreamingAll(self.io, 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, false); var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
defer osc.free(); defer osc.free();
try osc.writeCopyBuffer("part1"); try osc.writeCopyBuffer("part1");
try osc.encodeRemaining(); try osc.encodeRemaining();
@ -143,7 +101,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, false); var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
defer osc.free(); defer osc.free();
try osc.writeCopyBuffer("part1"); try osc.writeCopyBuffer("part1");
try osc.writeCopyBuffer("part2"); try osc.writeCopyBuffer("part2");
@ -152,7 +110,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, false); var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
defer osc.free(); defer osc.free();
try osc.writeCopyBuffer("part1"); try osc.writeCopyBuffer("part1");
try osc.writeCopyBuffer("part2"); try osc.writeCopyBuffer("part2");
@ -168,7 +126,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, false); var osc = try OSC52.init(std.testing.allocator, std.testing.io, 1024);
defer osc.free(); defer osc.free();
try osc.writeCopyBuffer("part1part1part1part1part1part1part1part1part1|"); try osc.writeCopyBuffer("part1part1part1part1part1part1part1part1part1|");
try osc.writeCopyBuffer("part2part2part2part2part2part2part2part2part2"); try osc.writeCopyBuffer("part2part2part2part2part2part2part2part2part2");

View file

@ -28,11 +28,8 @@ 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, is_tmux); var clip = try Clip.OSC52.init(alloc, io, size);
defer clip.free(); defer clip.free();
try run(io, &clip); try run(io, &clip);
return; return;
@ -45,26 +42,8 @@ pub fn main(init: std.process.Init) !void {
return; return;
} }
// I don't like this but it will stay until I review how dispatch works
// with zig since I will probably be refactoring there
linux: {
if (comptime os == .linux) {
const xdg_type = init.environ_map.get("XDG_SESSION_TYPE") orelse "";
if (std.mem.eql(u8, xdg_type, "wayland")) {
var clip = Clip.ForkOut.init(io, &[_][]const u8{"wl-copy"}) catch break :linux;
try run(io, &clip);
return;
} else if (std.mem.eql(u8, xdg_type, "x11")) {
var clip = Clip.ForkOut.init(io, &[_][]const u8{ "xclip", "-selection", "clipboard" }) catch break :linux;
try run(io, &clip);
return;
}
}
}
// Fallback Clipboard // Fallback Clipboard
var clip = try Clip.OSC52.init(alloc, io, size, is_tmux); var clip = try Clip.OSC52.init(alloc, io, size);
defer clip.free(); defer clip.free();
try run(io, &clip); try run(io, &clip);
} }