diff --git a/.crow/build.yml b/.crow/build.yml index 44c059a..e6945f6 100644 --- a/.crow/build.yml +++ b/.crow/build.yml @@ -7,33 +7,42 @@ clone: settings: tags: true -variables: - - &zig_image 'ghcr.io/rust-cross/cargo-zigbuild' - steps: - name: build-linux-x86_64 - image: *zig_image + image: alpine:3.20 commands: - - zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe --prefix zig-out-linux-x86_64 - - cp zig-out-linux-x86_64/bin/tpb tpb-linux-x86_64 + - apk add --no-cache curl xz + - curl -sSL https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz | tar -xJ -C /usr/local + - ln -s /usr/local/zig-x86_64-linux-0.16.0/zig /usr/local/bin/zig + - zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe + - cp zig-out/bin/tpb tpb-linux-x86_64 - name: build-linux-aarch64 - image: *zig_image + image: alpine:3.20 commands: - - zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe --prefix zig-out-linux-aarch64 - - cp zig-out-linux-aarch64/bin/tpb tpb-linux-aarch64 + - apk add --no-cache curl xz + - curl -sSL https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz | tar -xJ -C /usr/local + - ln -s /usr/local/zig-x86_64-linux-0.16.0/zig /usr/local/bin/zig + - zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe + - cp zig-out/bin/tpb tpb-linux-aarch64 - name: build-macos-x86_64 - image: *zig_image + image: alpine:3.20 commands: - - zig build -Dtarget=x86_64-macos -Doptimize=ReleaseSafe --prefix zig-out-macos-x86_64 - - cp zig-out-macos-x86_64/bin/tpb tpb-macos-x86_64 + - apk add --no-cache curl xz + - curl -sSL https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz | tar -xJ -C /usr/local + - ln -s /usr/local/zig-x86_64-linux-0.16.0/zig /usr/local/bin/zig + - zig build -Dtarget=x86_64-macos -Doptimize=ReleaseSafe + - cp zig-out/bin/tpb tpb-macos-x86_64 - name: build-macos-aarch64 - image: *zig_image + image: alpine:3.20 commands: - - zig build -Dtarget=aarch64-macos -Doptimize=ReleaseSafe --prefix zig-out-macos-aarch64 - - cp zig-out-macos-aarch64/bin/tpb tpb-macos-aarch64 + - apk add --no-cache curl xz + - curl -sSL https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz | tar -xJ -C /usr/local + - ln -s /usr/local/zig-x86_64-linux-0.16.0/zig /usr/local/bin/zig + - zig build -Dtarget=aarch64-macos -Doptimize=ReleaseSafe + - cp zig-out/bin/tpb tpb-macos-aarch64 - name: release diff --git a/build.zig b/build.zig index 59c2f26..242a70c 100644 --- a/build.zig +++ b/build.zig @@ -23,29 +23,10 @@ pub fn build(b: *std.Build) void { }), }); - if (target.result.os.tag == .macos) { - exe.root_module.addCSourceFiles(.{ - .files = &.{"src/Clip/NSPBBridge.m"}, - .language = .objective_c, - .flags = &.{"-fobjc-arc"}, - }); - exe.root_module.linkFramework("Cocoa", .{}); - tests.root_module.addCSourceFiles(.{ - .files = &.{"src/Clip/NSPBBridge.m"}, - .language = .objective_c, - .flags = &.{"-fobjc-arc"}, - }); - tests.root_module.linkFramework("Cocoa", .{}); - } - const test_step = b.step("test", "Run tests"); const run_tests = b.addRunArtifact(tests); test_step.dependOn(&run_tests.step); - const interface_step = b.step("interface", "Check interface (compile tests only)"); - const check_interface = b.addInstallArtifact(tests, .{}); - interface_step.dependOn(&check_interface.step); - const run_step = b.step("run", "Run the app"); const run_cmd = b.addRunArtifact(exe); diff --git a/src/Clip.zig b/src/Clip.zig index 299d4c6..8d264c8 100644 --- a/src/Clip.zig +++ b/src/Clip.zig @@ -1,11 +1,4 @@ -const std = @import("std"); -const builtin = @import("builtin"); - pub const OSC52 = @import("Clip/OSC52.zig"); -// 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 diff --git a/src/Clip/NSPBBridge.m b/src/Clip/NSPBBridge.m deleted file mode 100644 index e736c3f..0000000 --- a/src/Clip/NSPBBridge.m +++ /dev/null @@ -1,12 +0,0 @@ -// Simple bridge to objective_c code -// https://nathancraddock.com/blog/writing-to-the-clipboard-the-hard-way/ -#import -NSPasteboard *pboard; - -void initPB() { pboard = [NSPasteboard generalPasteboard]; } - -void sendPB(const char *text) { - [pboard clearContents]; - [pboard setString:[NSString stringWithUTF8String:text] - forType:NSPasteboardTypeString]; -} diff --git a/src/Clip/NSPasteboard.zig b/src/Clip/NSPasteboard.zig deleted file mode 100644 index acb895a..0000000 --- a/src/Clip/NSPasteboard.zig +++ /dev/null @@ -1,53 +0,0 @@ -/// 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"); - - const result = try std.process.run(std.testing.allocator, std.testing.io, .{ - .argv = &.{"pbpaste"}, - }); - defer std.testing.allocator.free(result.stdout); - defer std.testing.allocator.free(result.stderr); - - try std.testing.expectEqualStrings("test_string", std.mem.trim(u8, result.stdout, "\n")); -} diff --git a/src/Clip/OSC52.zig b/src/Clip/OSC52.zig index 8b751f5..d95fca1 100644 --- a/src/Clip/OSC52.zig +++ b/src/Clip/OSC52.zig @@ -3,6 +3,7 @@ pub const OSC52 = @This(); const std = @import("std"); +const Io = std.Io; alloc: std.mem.Allocator, io: std.Io, @@ -12,10 +13,9 @@ base64buffer: std.ArrayList(u8), leftover_bytes: [3]u8, leftover_count: u8, -const base64encoder = - std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '='); +const base64encoder = 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: Io, buf_size: usize) !OSC52 { return .{ .alloc = alloc, .io = io, @@ -51,6 +51,7 @@ pub fn writeCopyBuffer(self: *OSC52, buf: []const u8) anyerror!void { const chunkable_length = buf.len - ((buf.len - buf_idx) % 3); var chunker = std.mem.window(u8, buf[buf_idx..chunkable_length], 3, 3); while (chunker.next()) |chunk| { + // var temp: [5]u8 = undefined; s = OSC52.base64encoder.encode(&temp, chunk); try self.base64buffer.appendSlice(self.alloc, s); } @@ -86,7 +87,7 @@ pub fn writePasteboard(self: *OSC52) anyerror!void { }; // Write data to stdout - const stdout = std.Io.File.stdout(); + const stdout = Io.File.stdout(); try stdout.writeStreamingAll(self.io, osc_header); try stdout.writeStreamingAll(self.io, self.base64buffer.items); try stdout.writeStreamingAll(self.io, osc_footer); diff --git a/src/main.zig b/src/main.zig index bb0aa14..4f45742 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); - -const os = builtin.target.os.tag; const File = std.Io.File; const Clip = @import("Clip.zig"); @@ -10,41 +7,15 @@ pub fn main(init: std.process.Init) !void { // This program should not be run interactively if (try File.stdin().isTty(io)) { - std.debug.print( - \\ This program is not meant to be run interactively. - \\ Please pipe into this program. - \\ - , .{}); + std.debug.print("This program is not meant to be run interactively. Please pipe into this program.\n", .{}); std.process.exit(1); } // We want to avoid doing as many allocations as possible so having raw // pages with it being larger should be totally fine - 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; - - if (is_ssh) { - var clip = try Clip.OSC52.init(alloc, io, size); - defer clip.free(); - try run(io, &clip); - return; - } - - if (comptime os == .macos) { - var clip = try Clip.NSPasteboard.init(alloc, io, size); - defer clip.free(); - try run(io, &clip); - return; - } - - // Fallback Clipboard - var clip = try Clip.OSC52.init(alloc, io, size); + var clip = try Clip.OSC52.init(std.heap.page_allocator, init.io, std.heap.pageSize()); defer clip.free(); + try run(io, &clip); }