feat(NSPasteboard): uses backend when not ssh and on MacOS
- change ID
- txqmlmtwqsptqryuxyxxtztvvrknrltm
- git commit
- 9a377fa209cfecf53343247d197dc8578d6c2a1d
- author
- Grace Yoder <grace_yoder@outlook.com> - 2026-06-30T10:02:29-06:00
- committer
- Grace Yoder <grace_yoder@outlook.com> - 2026-06-30T16:33:26-06:00
- parents
- lopkzsklxzok
- refs
- signature
- Present (not validated)
feat(NSPasteboard): uses backend when not ssh and on MacOS
tree
| type | name | size |
|---|---|---|
| file | .crow/build.yml | 2096 bytes |
| file | .crow/release.yml | 566 bytes |
| file | .gitignore | 354 bytes |
| file | LICENSE | 1073 bytes |
| file | README.md | 632 bytes |
| file | build.zig | 1579 bytes |
| file | build.zig.zon | 247 bytes |
| file | src/Clip/NSPBBridge.m | 382 bytes |
| file | src/Clip/NSPasteboard.zig | 1456 bytes |
| file | src/Clip/OSC52.zig | 4611 bytes |
| file | src/Clip.zig | 1506 bytes |
| file | src/main.zig | 2190 bytes |
diff
src/Clip/NSPasteboard.zig
modified (file → file)
+/// 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");
try std.testing.expectEqualStrings("test_string", std.mem.trim(u8, result.stdout, "\n"));
}
-
src/Clip.zig
modified (file → file)
+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
}
test {
- _ = @import("Clip/NSPasteBoard.zig");
-
inline for (@typeInfo(@This()).@"struct".decls) |decl| {
// Only grab types of structs
const T = @field(@This(), decl.name);
src/main.zig
modified (file → file)
const std = @import("std");
+const builtin = @import("builtin");
+
+const os = builtin.target.os.tag;
const File = std.Io.File;
const Clip = @import("Clip.zig");
// 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();
- try run(io, &clip);
+ 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;
+
+ // 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 {