aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrace Yoder <grace_yoder@outlook.com>2026-06-30 10:02:29 -0600
committerGrace Yoder <grace_yoder@outlook.com>2026-06-30 16:33:26 -0600
commit9a377fa209cfecf53343247d197dc8578d6c2a1d (patch)
tree363b610d822ef6b855cf31873dfa097dd436d38f
parent565174c4b8c73e9e28524709060335a0b17ee3fb (diff)
feat(NSPasteboard): uses backend when not ssh and on MacOS
-rw-r--r--src/Clip.zig9
-rw-r--r--src/Clip/NSPasteboard.zig37
-rw-r--r--src/main.zig29
3 files changed, 68 insertions, 7 deletions
diff --git a/src/Clip.zig b/src/Clip.zig
index 1adeeb0..b4e1187 100644
--- a/src/Clip.zig
+++ b/src/Clip.zig
@@ -1,5 +1,10 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
pub const OSC52 = @import("Clip/OSC52.zig");
-// pub const PBCopy = if (@import("builtin").os.tag == .macos) @import("Clip/OSC52.zig") else null;
+// Ideally we would make else be `@compileError` but that
+// messes up with the interface checking
+pub const NSPasteboard = if (builtin.target.os.tag == .macos) @import("Clip/NSPasteboard.zig") else void;
// 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
@@ -16,8 +21,6 @@ fn assertFn(comptime T: type, comptime name: []const u8, comptime Sig: type) voi
}
test {
- _ = @import("Clip/NSPasteBoard.zig");
-
inline for (@typeInfo(@This()).@"struct".decls) |decl| {
// Only grab types of structs
const T = @field(@This(), decl.name);
diff --git a/src/Clip/NSPasteboard.zig b/src/Clip/NSPasteboard.zig
index 92c3403..acb895a 100644
--- a/src/Clip/NSPasteboard.zig
+++ b/src/Clip/NSPasteboard.zig
@@ -1,8 +1,44 @@
+/// NSPasteBoard Clipboard backend
+/// Fulfills `Clip` interface
+pub const NSPasteboard = @This();
+
const std = @import("std");
extern fn sendPB(text: [*:0]const u8) void;
extern fn initPB() void;
+alloc: std.mem.Allocator,
+io: std.Io,
+
+buffer: std.ArrayList(u8),
+
+pub fn init(alloc: std.mem.Allocator, io: std.Io, buf_size: usize) !NSPasteboard {
+ return .{
+ .alloc = alloc,
+ .io = io,
+ .buffer = try std.ArrayList(u8).initCapacity(alloc, buf_size),
+ };
+}
+
+pub fn free(self: *NSPasteboard) void {
+ self.buffer.clearAndFree(self.alloc);
+}
+
+pub fn writeCopyBuffer(self: *NSPasteboard, buf: []const u8) anyerror!void {
+ try self.buffer.appendSlice(self.alloc, buf);
+}
+
+pub fn writePasteboard(self: *NSPasteboard) anyerror!void {
+ // Null Terminate string for C function Call
+ if (self.buffer.items[self.buffer.items.len - 1] == '\n') {
+ self.buffer.items[self.buffer.items.len - 1] = '\x00';
+ } else {
+ try self.buffer.append(self.alloc, '\x00');
+ }
+ initPB();
+ sendPB(@ptrCast(self.buffer.items.ptr));
+}
+
test "objc bridge" {
initPB();
sendPB("test_string");
@@ -15,4 +51,3 @@ test "objc bridge" {
try std.testing.expectEqualStrings("test_string", std.mem.trim(u8, result.stdout, "\n"));
}
-
diff --git a/src/main.zig b/src/main.zig
index 4f45742..0725ae7 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,4 +1,7 @@
const std = @import("std");
+const builtin = @import("builtin");
+
+const os = builtin.target.os.tag;
const File = std.Io.File;
const Clip = @import("Clip.zig");
@@ -13,10 +16,30 @@ pub fn main(init: std.process.Init) !void {
// We want to avoid doing as many allocations as possible so having raw
// pages with it being larger should be totally fine
- var clip = try Clip.OSC52.init(std.heap.page_allocator, init.io, std.heap.pageSize());
- defer clip.free();
+ const alloc = std.heap.page_allocator;
+ const size = std.heap.pageSize();
+
+ const is_ssh = init.environ_map.get("SSH_CONNECTION") != null or
+ init.environ_map.get("SSH_CLIENT") != null or
+ init.environ_map.get("SSH_TTY") != null;
- try run(io, &clip);
+ // This can be cleaned up
+ if (is_ssh) {
+ var clip = try Clip.OSC52.init(alloc, io, size);
+ defer clip.free();
+ try run(io, &clip);
+ } else {
+ if (comptime os == .macos) {
+ var clip = try Clip.NSPasteboard.init(alloc, io, size);
+ defer clip.free();
+ try run(io, &clip);
+ } else {
+ // No Linux Clipboards yet
+ var clip = try Clip.OSC52.init(alloc, io, size);
+ defer clip.free();
+ try run(io, &clip);
+ }
+ }
}
pub fn run(io: std.Io, clip: anytype) !void {