Compare commits
No commits in common. "70ed8d2b0e50bc936103b2c6ee99bbb15ec7fc04" and "2f75723e16974bd21762fb64d5f6ce53984b115d" have entirely different histories.
70ed8d2b0e
...
2f75723e16
7 changed files with 32 additions and 142 deletions
|
|
@ -7,33 +7,42 @@ clone:
|
||||||
settings:
|
settings:
|
||||||
tags: true
|
tags: true
|
||||||
|
|
||||||
variables:
|
|
||||||
- &zig_image 'ghcr.io/rust-cross/cargo-zigbuild'
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build-linux-x86_64
|
- name: build-linux-x86_64
|
||||||
image: *zig_image
|
image: alpine:3.20
|
||||||
commands:
|
commands:
|
||||||
- zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe --prefix zig-out-linux-x86_64
|
- apk add --no-cache curl xz
|
||||||
- cp zig-out-linux-x86_64/bin/tpb tpb-linux-x86_64
|
- 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
|
- name: build-linux-aarch64
|
||||||
image: *zig_image
|
image: alpine:3.20
|
||||||
commands:
|
commands:
|
||||||
- zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe --prefix zig-out-linux-aarch64
|
- apk add --no-cache curl xz
|
||||||
- cp zig-out-linux-aarch64/bin/tpb tpb-linux-aarch64
|
- 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
|
- name: build-macos-x86_64
|
||||||
image: *zig_image
|
image: alpine:3.20
|
||||||
commands:
|
commands:
|
||||||
- zig build -Dtarget=x86_64-macos -Doptimize=ReleaseSafe --prefix zig-out-macos-x86_64
|
- apk add --no-cache curl xz
|
||||||
- cp zig-out-macos-x86_64/bin/tpb tpb-macos-x86_64
|
- 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
|
- name: build-macos-aarch64
|
||||||
image: *zig_image
|
image: alpine:3.20
|
||||||
commands:
|
commands:
|
||||||
- zig build -Dtarget=aarch64-macos -Doptimize=ReleaseSafe --prefix zig-out-macos-aarch64
|
- apk add --no-cache curl xz
|
||||||
- cp zig-out-macos-aarch64/bin/tpb tpb-macos-aarch64
|
- 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
|
- name: release
|
||||||
|
|
|
||||||
19
build.zig
19
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 test_step = b.step("test", "Run tests");
|
||||||
const run_tests = b.addRunArtifact(tests);
|
const run_tests = b.addRunArtifact(tests);
|
||||||
test_step.dependOn(&run_tests.step);
|
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_step = b.step("run", "Run the app");
|
||||||
|
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
const std = @import("std");
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
pub const OSC52 = @import("Clip/OSC52.zig");
|
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
|
// 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
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
// Simple bridge to objective_c code
|
|
||||||
// https://nathancraddock.com/blog/writing-to-the-clipboard-the-hard-way/
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
NSPasteboard *pboard;
|
|
||||||
|
|
||||||
void initPB() { pboard = [NSPasteboard generalPasteboard]; }
|
|
||||||
|
|
||||||
void sendPB(const char *text) {
|
|
||||||
[pboard clearContents];
|
|
||||||
[pboard setString:[NSString stringWithUTF8String:text]
|
|
||||||
forType:NSPasteboardTypeString];
|
|
||||||
}
|
|
||||||
|
|
@ -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"));
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
pub const OSC52 = @This();
|
pub const OSC52 = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Io = std.Io;
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
io: std.Io,
|
io: std.Io,
|
||||||
|
|
@ -12,10 +13,9 @@ base64buffer: std.ArrayList(u8),
|
||||||
leftover_bytes: [3]u8,
|
leftover_bytes: [3]u8,
|
||||||
leftover_count: u8,
|
leftover_count: u8,
|
||||||
|
|
||||||
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: Io, buf_size: usize) !OSC52 {
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.io = io,
|
.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);
|
const chunkable_length = buf.len - ((buf.len - buf_idx) % 3);
|
||||||
var chunker = std.mem.window(u8, buf[buf_idx..chunkable_length], 3, 3);
|
var chunker = std.mem.window(u8, buf[buf_idx..chunkable_length], 3, 3);
|
||||||
while (chunker.next()) |chunk| {
|
while (chunker.next()) |chunk| {
|
||||||
|
// var temp: [5]u8 = undefined;
|
||||||
s = OSC52.base64encoder.encode(&temp, chunk);
|
s = OSC52.base64encoder.encode(&temp, chunk);
|
||||||
try self.base64buffer.appendSlice(self.alloc, s);
|
try self.base64buffer.appendSlice(self.alloc, s);
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +87,7 @@ pub fn writePasteboard(self: *OSC52) anyerror!void {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write data to stdout
|
// 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, 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, osc_footer);
|
||||||
|
|
|
||||||
35
src/main.zig
35
src/main.zig
|
|
@ -1,7 +1,4 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
const os = builtin.target.os.tag;
|
|
||||||
const File = std.Io.File;
|
const File = std.Io.File;
|
||||||
const Clip = @import("Clip.zig");
|
const Clip = @import("Clip.zig");
|
||||||
|
|
||||||
|
|
@ -10,41 +7,15 @@ pub fn main(init: std.process.Init) !void {
|
||||||
|
|
||||||
// This program should not be run interactively
|
// This program should not be run interactively
|
||||||
if (try File.stdin().isTty(io)) {
|
if (try File.stdin().isTty(io)) {
|
||||||
std.debug.print(
|
std.debug.print("This program is not meant to be run interactively. Please pipe into this program.\n", .{});
|
||||||
\\ This program is not meant to be run interactively.
|
|
||||||
\\ Please pipe into this program.
|
|
||||||
\\
|
|
||||||
, .{});
|
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to avoid doing as many allocations as possible so having raw
|
// We want to avoid doing as many allocations as possible so having raw
|
||||||
// pages with it being larger should be totally fine
|
// pages with it being larger should be totally fine
|
||||||
const alloc = std.heap.page_allocator;
|
var clip = try Clip.OSC52.init(std.heap.page_allocator, init.io, std.heap.pageSize());
|
||||||
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);
|
|
||||||
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