Compare commits
No commits in common. "b57611439f160c10987f55ee0d2043a201b91606" and "874356bd3f84970edddc3ae390ac61d664609166" have entirely different histories.
b57611439f
...
874356bd3f
29
.github/workflows/zig-build.yaml
vendored
29
.github/workflows/zig-build.yaml
vendored
|
@ -1,29 +0,0 @@
|
||||||
name: Generic zig build
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '*'
|
|
||||||
- '!zig-develop*'
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: elerch/setup-zig@v3
|
|
||||||
with:
|
|
||||||
version: 0.12.0
|
|
||||||
- uses: elerch/zig-action-cache@v1.1.6
|
|
||||||
- name: Build project
|
|
||||||
run: zig build --summary all
|
|
||||||
- name: Run tests
|
|
||||||
run: zig build test --summary all
|
|
||||||
- name: Notify
|
|
||||||
uses: elerch/action-notify-ntfy@v2.github
|
|
||||||
if: always() && env.GITEA_ACTIONS == 'true'
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.NTFY_HOST }}
|
|
||||||
topic: ${{ secrets.NTFY_TOPIC }}
|
|
||||||
status: ${{ job.status }}
|
|
||||||
user: ${{ secrets.NTFY_USER }}
|
|
||||||
password: ${{ secrets.NTFY_PASSWORD }}
|
|
206
GitRepoStep.zig
Normal file
206
GitRepoStep.zig
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
//! Publish Date: 2021_10_17
|
||||||
|
//! This file is hosted at github.com/marler8997/zig-build-repos and is meant to be copied
|
||||||
|
//! to projects that use it.
|
||||||
|
const std = @import("std");
|
||||||
|
const GitRepoStep = @This();
|
||||||
|
|
||||||
|
pub const ShaCheck = enum {
|
||||||
|
none,
|
||||||
|
warn,
|
||||||
|
err,
|
||||||
|
|
||||||
|
pub fn reportFail(self: ShaCheck, comptime fmt: []const u8, args: anytype) void {
|
||||||
|
switch (self) {
|
||||||
|
.none => unreachable,
|
||||||
|
.warn => std.log.warn(fmt, args),
|
||||||
|
.err => {
|
||||||
|
std.log.err(fmt, args);
|
||||||
|
std.os.exit(0xff);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
step: std.build.Step,
|
||||||
|
builder: *std.build.Builder,
|
||||||
|
url: []const u8,
|
||||||
|
name: []const u8,
|
||||||
|
branch: ?[]const u8 = null,
|
||||||
|
sha: []const u8,
|
||||||
|
path: []const u8 = null,
|
||||||
|
sha_check: ShaCheck = .warn,
|
||||||
|
fetch_enabled: bool,
|
||||||
|
|
||||||
|
var cached_default_fetch_option: ?bool = null;
|
||||||
|
pub fn defaultFetchOption(b: *std.build.Builder) bool {
|
||||||
|
if (cached_default_fetch_option) |_| {} else {
|
||||||
|
cached_default_fetch_option = if (b.option(bool, "fetch", "automatically fetch network resources")) |o| o else false;
|
||||||
|
}
|
||||||
|
return cached_default_fetch_option.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(b: *std.build.Builder, opt: struct {
|
||||||
|
url: []const u8,
|
||||||
|
branch: ?[]const u8 = null,
|
||||||
|
sha: []const u8,
|
||||||
|
path: ?[]const u8 = null,
|
||||||
|
sha_check: ShaCheck = .warn,
|
||||||
|
fetch_enabled: ?bool = null,
|
||||||
|
}) *GitRepoStep {
|
||||||
|
var result = b.allocator.create(GitRepoStep) catch @panic("memory");
|
||||||
|
const name = std.fs.path.basename(opt.url);
|
||||||
|
result.* = GitRepoStep{
|
||||||
|
.step = std.build.Step.init(.custom, "clone a git repository", b.allocator, make),
|
||||||
|
.builder = b,
|
||||||
|
.url = opt.url,
|
||||||
|
.name = name,
|
||||||
|
.branch = opt.branch,
|
||||||
|
.sha = opt.sha,
|
||||||
|
.path = if (opt.path) |p| (b.allocator.dupe(u8, p) catch @panic("memory")) else (std.fs.path.resolve(b.allocator, &[_][]const u8{
|
||||||
|
b.build_root,
|
||||||
|
"libs",
|
||||||
|
name,
|
||||||
|
})) catch @panic("memory"),
|
||||||
|
.sha_check = opt.sha_check,
|
||||||
|
.fetch_enabled = if (opt.fetch_enabled) |fe| fe else defaultFetchOption(b),
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should be included in std.build, it helps find bugs in build files
|
||||||
|
fn hasDependency(step: *const std.build.Step, dep_candidate: *const std.build.Step) bool {
|
||||||
|
for (step.dependencies.items) |dep| {
|
||||||
|
// TODO: should probably use step.loop_flag to prevent infinite recursion
|
||||||
|
// when a circular reference is encountered, or maybe keep track of
|
||||||
|
// the steps encounterd with a hash set
|
||||||
|
if (dep == dep_candidate or hasDependency(dep, dep_candidate))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make(step: *std.build.Step) !void {
|
||||||
|
const self = @fieldParentPtr(GitRepoStep, "step", step);
|
||||||
|
|
||||||
|
std.fs.accessAbsolute(self.path, std.fs.File.OpenFlags{ .read = true }) catch {
|
||||||
|
const branch_args = if (self.branch) |b| &[2][]const u8{ " -b ", b } else &[2][]const u8{ "", "" };
|
||||||
|
if (!self.fetch_enabled) {
|
||||||
|
std.debug.print("Error: git repository '{s}' does not exist\n", .{self.path});
|
||||||
|
std.debug.print(" Use -Dfetch to download it automatically, or run the following to clone it:\n", .{});
|
||||||
|
std.debug.print(" git clone {s}{s}{s} {s} && git -C {3s} checkout {s} -b for_ziget\n", .{ self.url, branch_args[0], branch_args[1], self.path, self.sha });
|
||||||
|
std.os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var args = std.ArrayList([]const u8).init(self.builder.allocator);
|
||||||
|
defer args.deinit();
|
||||||
|
try args.append("git");
|
||||||
|
try args.append("clone");
|
||||||
|
try args.append("--recurse-submodules");
|
||||||
|
try args.append(self.url);
|
||||||
|
// TODO: clone it to a temporary location in case of failure
|
||||||
|
// also, remove that temporary location before running
|
||||||
|
try args.append(self.path);
|
||||||
|
if (self.branch) |branch| {
|
||||||
|
try args.append("-b");
|
||||||
|
try args.append(branch);
|
||||||
|
}
|
||||||
|
try run(self.builder, args.items);
|
||||||
|
}
|
||||||
|
try run(self.builder, &[_][]const u8{
|
||||||
|
"git",
|
||||||
|
"-C",
|
||||||
|
self.path,
|
||||||
|
"checkout",
|
||||||
|
self.sha,
|
||||||
|
"-b",
|
||||||
|
"fordep",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.checkSha();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkSha(self: GitRepoStep) !void {
|
||||||
|
if (self.sha_check == .none)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const result: union(enum) { failed: anyerror, output: []const u8 } = blk: {
|
||||||
|
const result = std.ChildProcess.exec(.{
|
||||||
|
.allocator = self.builder.allocator,
|
||||||
|
.argv = &[_][]const u8{
|
||||||
|
"git",
|
||||||
|
"-C",
|
||||||
|
self.path,
|
||||||
|
"rev-parse",
|
||||||
|
"HEAD",
|
||||||
|
},
|
||||||
|
.cwd = self.builder.build_root,
|
||||||
|
.env_map = self.builder.env_map,
|
||||||
|
}) catch |e| break :blk .{ .failed = e };
|
||||||
|
try std.io.getStdErr().writer().writeAll(result.stderr);
|
||||||
|
switch (result.term) {
|
||||||
|
.Exited => |code| {
|
||||||
|
if (code == 0) break :blk .{ .output = result.stdout };
|
||||||
|
break :blk .{ .failed = error.GitProcessNonZeroExit };
|
||||||
|
},
|
||||||
|
.Signal => break :blk .{ .failed = error.GitProcessFailedWithSignal },
|
||||||
|
.Stopped => break :blk .{ .failed = error.GitProcessWasStopped },
|
||||||
|
.Unknown => break :blk .{ .failed = error.GitProcessFailed },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (result) {
|
||||||
|
.failed => |err| {
|
||||||
|
return self.sha_check.reportFail("failed to retreive sha for repository '{s}': {s}", .{ self.name, @errorName(err) });
|
||||||
|
},
|
||||||
|
.output => |output| {
|
||||||
|
if (!std.mem.eql(u8, std.mem.trimRight(u8, output, "\n\r"), self.sha)) {
|
||||||
|
return self.sha_check.reportFail("repository '{s}' sha does not match\nexpected: {s}\nactual : {s}\n", .{ self.name, self.sha, output });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(builder: *std.build.Builder, argv: []const []const u8) !void {
|
||||||
|
{
|
||||||
|
var msg = std.ArrayList(u8).init(builder.allocator);
|
||||||
|
defer msg.deinit();
|
||||||
|
const writer = msg.writer();
|
||||||
|
var prefix: []const u8 = "";
|
||||||
|
for (argv) |arg| {
|
||||||
|
try writer.print("{s}\"{s}\"", .{ prefix, arg });
|
||||||
|
prefix = " ";
|
||||||
|
}
|
||||||
|
std.log.info("[RUN] {s}", .{msg.items});
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = try std.ChildProcess.init(argv, builder.allocator);
|
||||||
|
defer child.deinit();
|
||||||
|
|
||||||
|
child.stdin_behavior = .Ignore;
|
||||||
|
child.stdout_behavior = .Inherit;
|
||||||
|
child.stderr_behavior = .Inherit;
|
||||||
|
child.cwd = builder.build_root;
|
||||||
|
child.env_map = builder.env_map;
|
||||||
|
|
||||||
|
try child.spawn();
|
||||||
|
const result = try child.wait();
|
||||||
|
switch (result) {
|
||||||
|
.Exited => |code| if (code != 0) {
|
||||||
|
std.log.err("git clone failed with exit code {}", .{code});
|
||||||
|
std.os.exit(0xff);
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.log.err("git clone failed with: {}", .{result});
|
||||||
|
std.os.exit(0xff);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get's the repository path and also verifies that the step requesting the path
|
||||||
|
// is dependent on this step.
|
||||||
|
pub fn getPath(self: *const GitRepoStep, who_wants_to_know: *const std.build.Step) []const u8 {
|
||||||
|
if (!hasDependency(who_wants_to_know, &self.step))
|
||||||
|
@panic("a step called GitRepoStep.getPath but has not added it as a dependency");
|
||||||
|
return self.path;
|
||||||
|
}
|
136
build.zig
136
build.zig
|
@ -1,6 +1,12 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const GitRepoStep = @import("GitRepoStep.zig");
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.build.Builder) void {
|
||||||
|
const zfetch_repo = GitRepoStep.create(b, .{
|
||||||
|
.url = "https://github.com/truemedian/zfetch",
|
||||||
|
// .branch = "0.1.10", // branch also takes tags. Tag 0.1.10 isn't quite new enough
|
||||||
|
.sha = "271cab5da4d12c8f08e67aa0cd5268da100e52f1",
|
||||||
|
});
|
||||||
// Standard target options allows the person running `zig build` to choose
|
// Standard target options allows the person running `zig build` to choose
|
||||||
// what target to build for. Here we do not override the defaults, which
|
// what target to build for. Here we do not override the defaults, which
|
||||||
// means any target is allowed, and the default is native. Other options
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
@ -9,12 +15,12 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
// Standard release options allow the person running `zig build` to select
|
// Standard release options allow the person running `zig build` to select
|
||||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
const uploadexe = switch (target.result.os.tag) {
|
const uploadexe = switch (target.getOs().tag) {
|
||||||
.linux => b.addExecutable(.{ .name = "clipboard-upload", .root_source_file = b.path("src/main-linux.zig"), .target = target }),
|
.linux => b.addExecutable("clipboard-upload", "src/main-linux.zig"),
|
||||||
.windows => b.addExecutable(.{ .name = "clipboard-upload", .root_source_file = b.path("src/main-windows.zig"), .target = target }),
|
.windows => b.addExecutable("clipboard-upload", "src/main-windows.zig"),
|
||||||
else => std.process.exit(1),
|
else => std.os.exit(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
const path = if (b.option(bool, "curl", "use external curl command") orelse false)
|
const path = if (b.option(bool, "curl", "use external curl command") orelse false)
|
||||||
|
@ -24,22 +30,19 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
const enc_path = blk: {
|
const enc_path = blk: {
|
||||||
if (b.option(bool, "seperate-encryption", "use external encryption command") orelse false) {
|
if (b.option(bool, "seperate-encryption", "use external encryption command") orelse false) {
|
||||||
const encryptionexe = b.addExecutable(.{
|
const encryptionexe = b.addExecutable("encrypt", "src/encrypt.zig");
|
||||||
.name = "encrypt",
|
encryptionexe.setTarget(target);
|
||||||
.root_source_file = b.path("src/encrypt.zig"),
|
encryptionexe.setBuildMode(mode);
|
||||||
.target = target,
|
encryptionexe.install();
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
b.getInstallStep().dependOn(&b.addInstallArtifact(encryptionexe, .{}).step);
|
|
||||||
break :blk "config/external_encryption.zig";
|
break :blk "config/external_encryption.zig";
|
||||||
} else {
|
} else {
|
||||||
break :blk "config/sane_encryption.zig";
|
break :blk "config/sane_encryption.zig";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
configureExe(uploadexe, b, target, optimize, path, enc_path);
|
configureExe(uploadexe, b, target, mode, zfetch_repo, path, enc_path);
|
||||||
|
|
||||||
const run_cmd = b.addRunArtifact(uploadexe);
|
const run_cmd = uploadexe.run();
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
run_cmd.addArgs(args);
|
run_cmd.addArgs(args);
|
||||||
|
@ -48,11 +51,11 @@ pub fn build(b: *std.Build) void {
|
||||||
const run_step = b.step("run", "Run the app (uplaods clipboard contents)");
|
const run_step = b.step("run", "Run the app (uplaods clipboard contents)");
|
||||||
run_step.dependOn(&run_cmd.step);
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
const downloadexe = b.addExecutable(.{ .name = "clipboard-download", .root_source_file = b.path("src/download.zig"), .target = target });
|
const downloadexe = b.addExecutable("clipboard-download", "src/download.zig");
|
||||||
|
|
||||||
configureExe(downloadexe, b, target, optimize, path, enc_path);
|
configureExe(downloadexe, b, target, mode, zfetch_repo, path, enc_path);
|
||||||
|
|
||||||
const run_download_cmd = b.addRunArtifact(downloadexe);
|
const run_download_cmd = downloadexe.run();
|
||||||
run_download_cmd.step.dependOn(b.getInstallStep());
|
run_download_cmd.step.dependOn(b.getInstallStep());
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
run_download_cmd.addArgs(args);
|
run_download_cmd.addArgs(args);
|
||||||
|
@ -62,9 +65,10 @@ pub fn build(b: *std.Build) void {
|
||||||
run_download_step.dependOn(&run_download_cmd.step);
|
run_download_step.dependOn(&run_download_cmd.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configureExe(exe: *std.Build.Step.Compile, b: *std.Build, target: std.Build.ResolvedTarget, mode: std.builtin.OptimizeMode, config_path: []const u8, enc_config_path: []const u8) void {
|
fn configureExe(exe: *std.build.LibExeObjStep, b: *std.build.Builder, target: std.zig.CrossTarget, mode: std.builtin.Mode, zfetch_repo: anytype, config_path: []const u8, enc_config_path: []const u8) void {
|
||||||
exe.root_module.optimize = mode;
|
exe.setTarget(target);
|
||||||
if (target.result.os.tag == .linux) {
|
exe.setBuildMode(mode);
|
||||||
|
if (target.getOs().tag == .linux) {
|
||||||
exe.linkLibC();
|
exe.linkLibC();
|
||||||
// LibX11 1.7.2: https://gitlab.freedesktop.org/xorg/lib/libx11/-/archive/libX11-1.7.2/libx11-libX11-1.7.2.tar.gz
|
// LibX11 1.7.2: https://gitlab.freedesktop.org/xorg/lib/libx11/-/archive/libX11-1.7.2/libx11-libX11-1.7.2.tar.gz
|
||||||
// LibXfixes 5.0.3: https://gitlab.freedesktop.org/xorg/lib/libxfixes/-/archive/libXfixes-5.0.3/libxfixes-libXfixes-5.0.3.tar.gz
|
// LibXfixes 5.0.3: https://gitlab.freedesktop.org/xorg/lib/libxfixes/-/archive/libXfixes-5.0.3/libxfixes-libXfixes-5.0.3.tar.gz
|
||||||
|
@ -72,39 +76,93 @@ fn configureExe(exe: *std.Build.Step.Compile, b: *std.Build, target: std.Build.R
|
||||||
// We can download the above by taking each url and processing in a
|
// We can download the above by taking each url and processing in a
|
||||||
// command e.g.:
|
// command e.g.:
|
||||||
// curl <url> | tar xz --wildcards '*.h'
|
// curl <url> | tar xz --wildcards '*.h'
|
||||||
exe.addIncludePath(b.path("libx11-libX11-1.7.2/include"));
|
exe.addIncludeDir("libx11-libX11-1.7.2/include");
|
||||||
exe.addIncludePath(b.path("libxfixes-libXfixes-5.0.3/include"));
|
exe.addIncludeDir("libxfixes-libXfixes-5.0.3/include");
|
||||||
exe.addIncludePath(b.path("xorgproto-xorgproto-2021.5/include"));
|
exe.addIncludeDir("xorgproto-xorgproto-2021.5/include");
|
||||||
|
|
||||||
// More than a little messy. We're grabbing libX11 and libXfixes from
|
// More than a little messy. We're grabbing libX11 and libXfixes from
|
||||||
// the host, while using downloaded headers. This assumes debian
|
// the host, while using downloaded headers. This assumes debian
|
||||||
// bullseye host, and also means you can't cross-compile from Windows.
|
// bullseye host, and also means you can't cross-compile from Windows.
|
||||||
// TODO: Make this better
|
// TODO: Make this better
|
||||||
// const dependent_objects = .{
|
|
||||||
// "/usr/lib/x86_64-linux-gnu/libX11.so.6",
|
|
||||||
// "/usr/lib/x86_64-linux-gnu/libXfixes.so.3",
|
|
||||||
// };
|
|
||||||
const dependent_objects = .{
|
const dependent_objects = .{
|
||||||
"/usr/lib/x86_64-linux-gnu/libX11.so.6",
|
"/usr/lib/x86_64-linux-gnu/libX11.so.6",
|
||||||
"/nix/store/wd7b3f9sqgs0flgicx213iiyzjb4jxqm-libXfixes-6.0.1/lib/libXfixes.so.3",
|
"/usr/lib/x86_64-linux-gnu/libXfixes.so.3",
|
||||||
};
|
};
|
||||||
inline for (dependent_objects) |obj|
|
inline for (dependent_objects) |obj|
|
||||||
exe.addObjectFile(.{ .cwd_relative = obj });
|
exe.addObjectFile(obj);
|
||||||
// exe.addObjectFile(b.path(obj));
|
|
||||||
}
|
}
|
||||||
if (target.result.os.tag == .windows) {
|
if (target.getOs().tag == .windows) {
|
||||||
// woah...we don't actually need libc!
|
// woah...we don't actually need libc!
|
||||||
exe.linkSystemLibrary("user32");
|
exe.linkSystemLibrary("user32");
|
||||||
exe.linkSystemLibrary("kernel32");
|
exe.linkSystemLibrary("kernel32");
|
||||||
exe.linkSystemLibrary("shell32");
|
exe.linkSystemLibrary("shell32");
|
||||||
}
|
}
|
||||||
|
const copy_deps = std.build.RunStep.create(b, "copy zfetch deps");
|
||||||
|
copy_deps.addArgs(&[_][]const u8{
|
||||||
|
"cp",
|
||||||
|
"zfetch_deps.zig",
|
||||||
|
"libs/zfetch/deps.zig",
|
||||||
|
});
|
||||||
|
copy_deps.step.dependOn(&zfetch_repo.step);
|
||||||
|
|
||||||
exe.root_module.addImport("config", b.createModule(.{
|
// exe.step.dependOn(&zfetch_repo.step);
|
||||||
.root_source_file = b.path(config_path),
|
exe.step.dependOn(©_deps.step);
|
||||||
}));
|
|
||||||
exe.root_module.addImport("encryptionconfig", b.createModule(.{
|
|
||||||
.root_source_file = b.path(enc_config_path),
|
|
||||||
}));
|
|
||||||
|
|
||||||
b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{}).step);
|
// This import won't work unless we're already cloned. The way around
|
||||||
|
// this is to have a multi-stage build process, but that's a lot of work.
|
||||||
|
// Instead, I've copied the addPackage and tweaked it for the build prefix
|
||||||
|
// so we'll have to keep that in sync with upstream
|
||||||
|
// const zfetch = @import("libs/zfetch/build.zig");
|
||||||
|
const zpkg = getZfetchPackage(b, "libs/zfetch") catch unreachable;
|
||||||
|
exe.addPackage(zpkg);
|
||||||
|
exe.addPackage(.{
|
||||||
|
.name = "iguanaTLS",
|
||||||
|
.path = .{ .path = "libs/zfetch/libs/iguanaTLS/src/main.zig" },
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.addPackage(.{
|
||||||
|
.name = "config",
|
||||||
|
.path = .{ .path = config_path },
|
||||||
|
});
|
||||||
|
exe.addPackage(.{
|
||||||
|
.name = "encryptionconfig",
|
||||||
|
.path = .{ .path = enc_config_path },
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getDependency(comptime lib_prefix: []const u8, comptime name: []const u8, comptime root: []const u8) !std.build.Pkg {
|
||||||
|
const path = lib_prefix ++ "/libs/" ++ name ++ "/" ++ root;
|
||||||
|
|
||||||
|
// We don't actually care if the dependency has been checked out, as
|
||||||
|
// GitRepoStep will handle that for us
|
||||||
|
// Make sure that the dependency has been checked out.
|
||||||
|
// std.fs.cwd().access(path, .{}) catch |err| switch (err) {
|
||||||
|
// error.FileNotFound => {
|
||||||
|
// std.log.err("zfetch: dependency '{s}' not checked out", .{name});
|
||||||
|
//
|
||||||
|
// return err;
|
||||||
|
// },
|
||||||
|
// else => return err,
|
||||||
|
// };
|
||||||
|
|
||||||
|
return std.build.Pkg{
|
||||||
|
.name = name,
|
||||||
|
.path = .{ .path = path },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn getZfetchPackage(b: *std.build.Builder, comptime lib_prefix: []const u8) !std.build.Pkg {
|
||||||
|
var dependencies = b.allocator.alloc(std.build.Pkg, 4) catch unreachable;
|
||||||
|
|
||||||
|
dependencies[0] = try getDependency(lib_prefix, "iguanaTLS", "src/main.zig");
|
||||||
|
dependencies[1] = try getDependency(lib_prefix, "network", "network.zig");
|
||||||
|
dependencies[2] = try getDependency(lib_prefix, "uri", "uri.zig");
|
||||||
|
dependencies[3] = try getDependency(lib_prefix, "hzzp", "src/main.zig");
|
||||||
|
|
||||||
|
return std.build.Pkg{
|
||||||
|
.name = "zfetch",
|
||||||
|
.path = .{ .path = lib_prefix ++ "/src/main.zig" },
|
||||||
|
.dependencies = dependencies,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const zfetch = @import("zfetch");
|
||||||
const crypt = @import("crypt.zig");
|
const crypt = @import("crypt.zig");
|
||||||
const config = @import("config");
|
const config = @import("config");
|
||||||
const encryptionconfig = @import("encryptionconfig");
|
const encryptionconfig = @import("encryptionconfig");
|
||||||
|
@ -12,7 +13,7 @@ const encryptionconfig = @import("encryptionconfig");
|
||||||
// iguanaTLS to support tls 1.3
|
// iguanaTLS to support tls 1.3
|
||||||
// iguanaTLS to support something else, like ECDHE-ECDSA-CHACHA20-POLY1305
|
// iguanaTLS to support something else, like ECDHE-ECDSA-CHACHA20-POLY1305
|
||||||
// In the meantime, I've allowed use of http, since we're encrypting anyway
|
// In the meantime, I've allowed use of http, since we're encrypting anyway
|
||||||
const clipboard_url = "https://clippy.lerch.org/work2";
|
const clipboard_url = "http://clippy.lerch.org/work2";
|
||||||
// const clipboard_url = "https://httpbin.org/post";
|
// const clipboard_url = "https://httpbin.org/post";
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
@ -58,8 +59,9 @@ pub fn clipboardChanged(self: *Self, contents: []const u8) !void {
|
||||||
defer aa.free(clip_contents);
|
defer aa.free(clip_contents);
|
||||||
|
|
||||||
// Ugh - it's the encryption that Crowdstrike doesn't like.. :(
|
// Ugh - it's the encryption that Crowdstrike doesn't like.. :(
|
||||||
const buf: []u8 = try aa.dupe(u8, contents);
|
var buf: []u8 = try aa.alloc(u8, contents.len);
|
||||||
defer aa.free(buf);
|
defer aa.free(buf);
|
||||||
|
std.mem.copy(u8, buf, contents);
|
||||||
const encrypted = encrypt(aa, self.key.*, buf) catch |e| {
|
const encrypted = encrypt(aa, self.key.*, buf) catch |e| {
|
||||||
std.log.err("Could not encrypt clipboard contents: {}", .{e});
|
std.log.err("Could not encrypt clipboard contents: {}", .{e});
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
|
@ -125,20 +127,19 @@ fn get(allocator: std.mem.Allocator) ![]const u8 {
|
||||||
// @embedFile("/etc/ssl/certs/ca-certificates.crt"),
|
// @embedFile("/etc/ssl/certs/ca-certificates.crt"),
|
||||||
// ).reader();
|
// ).reader();
|
||||||
// const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader);
|
// const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader);
|
||||||
|
try zfetch.init();
|
||||||
|
defer zfetch.deinit();
|
||||||
|
|
||||||
|
var headers = zfetch.Headers.init(allocator);
|
||||||
|
defer headers.deinit();
|
||||||
|
|
||||||
// try headers.appendValue("Accept", "application/json");
|
// try headers.appendValue("Accept", "application/json");
|
||||||
// try headers.appendValue("Content-Type", "text/plain");
|
// try headers.appendValue("Content-Type", "text/plain");
|
||||||
|
|
||||||
var data = std.ArrayList(u8).init(allocator);
|
var req = try zfetch.Request.init(allocator, clipboard_url, null);
|
||||||
defer data.deinit();
|
defer req.deinit();
|
||||||
var client = std.http.Client{ .allocator = allocator };
|
|
||||||
defer client.deinit();
|
|
||||||
|
|
||||||
const res = try client.fetch(.{
|
try req.do(.GET, headers, null);
|
||||||
.location = .{ .url = clipboard_url },
|
|
||||||
.response_storage = .{ .dynamic = &data },
|
|
||||||
});
|
|
||||||
_ = res; // TODO: Check res.status
|
|
||||||
|
|
||||||
// Printf debugging
|
// Printf debugging
|
||||||
// const stdout = std.io.getStdOut().writer();
|
// const stdout = std.io.getStdOut().writer();
|
||||||
|
@ -149,6 +150,18 @@ fn get(allocator: std.mem.Allocator) ![]const u8 {
|
||||||
// }
|
// }
|
||||||
// try stdout.print("body:\n", .{});
|
// try stdout.print("body:\n", .{});
|
||||||
//
|
//
|
||||||
|
const reader = req.reader();
|
||||||
|
|
||||||
|
var data = std.ArrayList(u8).init(allocator);
|
||||||
|
defer data.deinit();
|
||||||
|
const data_writer = data.writer();
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
while (true) {
|
||||||
|
const read = try reader.read(&buf);
|
||||||
|
if (read == 0) break;
|
||||||
|
|
||||||
|
try data_writer.writeAll(buf[0..read]);
|
||||||
|
}
|
||||||
return data.toOwnedSlice();
|
return data.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,20 +270,42 @@ fn put(allocator: std.mem.Allocator, data: []const u8) !void {
|
||||||
// @embedFile("/etc/ssl/certs/ca-certificates.crt"),
|
// @embedFile("/etc/ssl/certs/ca-certificates.crt"),
|
||||||
// ).reader();
|
// ).reader();
|
||||||
// const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader);
|
// const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader);
|
||||||
var client = std.http.Client{ .allocator = allocator };
|
try zfetch.init();
|
||||||
defer client.deinit();
|
defer zfetch.deinit();
|
||||||
|
|
||||||
const res = try client.fetch(.{
|
var headers = zfetch.Headers.init(allocator);
|
||||||
.location = .{ .url = clipboard_url },
|
defer headers.deinit();
|
||||||
.method = .PUT,
|
|
||||||
.payload = data,
|
|
||||||
});
|
|
||||||
_ = res; // TODO: check status
|
|
||||||
|
|
||||||
|
// try headers.appendValue("Accept", "application/json");
|
||||||
|
// try headers.appendValue("Content-Type", "text/plain");
|
||||||
|
|
||||||
|
var req = try zfetch.Request.init(allocator, clipboard_url, null);
|
||||||
|
defer req.deinit();
|
||||||
|
|
||||||
|
try req.do(.PUT, headers, data);
|
||||||
|
|
||||||
|
// Printf debugging
|
||||||
|
// const stdout = std.io.getStdOut().writer();
|
||||||
|
// try stdout.print("status: {d} {s}\n", .{ req.status.code, req.status.reason });
|
||||||
|
// try stdout.print("headers:\n", .{});
|
||||||
|
// for (req.headers.list.items) |header| {
|
||||||
|
// try stdout.print(" {s}: {s}\n", .{ header.name, header.value });
|
||||||
|
// }
|
||||||
|
// try stdout.print("body:\n", .{});
|
||||||
|
//
|
||||||
|
// const reader = req.reader();
|
||||||
|
//
|
||||||
|
// var buf: [1024]u8 = undefined;
|
||||||
|
// while (true) {
|
||||||
|
// const read = try reader.read(&buf);
|
||||||
|
// if (read == 0) break;
|
||||||
|
//
|
||||||
|
// try stdout.writeAll(buf[0..read]);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
test "full integration" {
|
test "full integration" {
|
||||||
const allocator = std.testing.allocator;
|
var allocator = std.testing.allocator;
|
||||||
var watcher = init(allocator);
|
var watcher = init(allocator);
|
||||||
defer watcher.deinit();
|
defer watcher.deinit();
|
||||||
try watcher.clipboardChanged("hello world");
|
try watcher.clipboardChanged("hello world");
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub fn encryptWithKey(allocator: std.mem.Allocator, key: [key_size]u8, data: []u
|
||||||
var encrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks + 1));
|
var encrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks + 1));
|
||||||
defer encrypted.deinit();
|
defer encrypted.deinit();
|
||||||
while (current_block < total_blocks) {
|
while (current_block < total_blocks) {
|
||||||
in = @ptrCast(data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
|
in = @ptrCast(*[block_size]u8, data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
|
||||||
ctx.encrypt(out[0..], in.*[0..]);
|
ctx.encrypt(out[0..], in.*[0..]);
|
||||||
encrypted.appendSliceAssumeCapacity(out[0..]);
|
encrypted.appendSliceAssumeCapacity(out[0..]);
|
||||||
current_block += 1;
|
current_block += 1;
|
||||||
|
@ -56,7 +56,7 @@ pub fn encryptWithKey(allocator: std.mem.Allocator, key: [key_size]u8, data: []u
|
||||||
// We can't just ptrcast into data as we are likely to run over the end
|
// We can't just ptrcast into data as we are likely to run over the end
|
||||||
// of the data memory. We'll declare a new input buffer
|
// of the data memory. We'll declare a new input buffer
|
||||||
var in_last: [block_size]u8 = undefined;
|
var in_last: [block_size]u8 = undefined;
|
||||||
const padding: u8 = @intCast(block_size - (data.len % block_size));
|
const padding: u8 = @intCast(u8, block_size - (data.len % block_size));
|
||||||
var inx: u8 = 0;
|
var inx: u8 = 0;
|
||||||
for (data[(total_blocks * block_size)..]) |b| {
|
for (data[(total_blocks * block_size)..]) |b| {
|
||||||
in_last[inx] = b;
|
in_last[inx] = b;
|
||||||
|
@ -99,14 +99,14 @@ pub fn decryptWithKey(allocator: std.mem.Allocator, key: [key_size]u8, data: []c
|
||||||
var decrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks - 1));
|
var decrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks - 1));
|
||||||
defer decrypted.deinit();
|
defer decrypted.deinit();
|
||||||
while (current_block < total_blocks - 1) {
|
while (current_block < total_blocks - 1) {
|
||||||
in = @ptrCast(data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
|
in = @ptrCast(*const [block_size]u8, data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
|
||||||
ctx.decrypt(out[0..], in.*[0..]);
|
ctx.decrypt(out[0..], in.*[0..]);
|
||||||
decrypted.appendSliceAssumeCapacity(out[0..]);
|
decrypted.appendSliceAssumeCapacity(out[0..]);
|
||||||
current_block += 1;
|
current_block += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// deal with final block, PKCS#7 padding
|
// deal with final block, PKCS#7 padding
|
||||||
in = @ptrCast(data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
|
in = @ptrCast(*const [block_size]u8, data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
|
||||||
ctx.decrypt(out[0..], in.*[0..]);
|
ctx.decrypt(out[0..], in.*[0..]);
|
||||||
// Assertion was triggering when data was plain text
|
// Assertion was triggering when data was plain text
|
||||||
if (out[block_size - 1] > block_size) {
|
if (out[block_size - 1] > block_size) {
|
||||||
|
@ -131,7 +131,7 @@ test "correct size, even block" {
|
||||||
const str = "0123456789ABCDEF";
|
const str = "0123456789ABCDEF";
|
||||||
var allocator = std.testing.allocator;
|
var allocator = std.testing.allocator;
|
||||||
var buf: [str.len]u8 = undefined;
|
var buf: [str.len]u8 = undefined;
|
||||||
const data = try std.fmt.bufPrint(&buf, str, .{});
|
var data = try std.fmt.bufPrint(&buf, str, .{});
|
||||||
const encrypted = try encryptWithKey(allocator, test_key, data);
|
const encrypted = try encryptWithKey(allocator, test_key, data);
|
||||||
defer allocator.free(encrypted);
|
defer allocator.free(encrypted);
|
||||||
const size: usize = 32;
|
const size: usize = 32;
|
||||||
|
@ -142,7 +142,7 @@ test "round trip, even block" {
|
||||||
const str = "0123456789ABCDEF";
|
const str = "0123456789ABCDEF";
|
||||||
var allocator = std.testing.allocator;
|
var allocator = std.testing.allocator;
|
||||||
var buf: [str.len]u8 = undefined;
|
var buf: [str.len]u8 = undefined;
|
||||||
const data = try std.fmt.bufPrint(&buf, str, .{});
|
var data = try std.fmt.bufPrint(&buf, str, .{});
|
||||||
const encrypted = try encryptWithKey(allocator, test_key, data);
|
const encrypted = try encryptWithKey(allocator, test_key, data);
|
||||||
defer allocator.free(encrypted);
|
defer allocator.free(encrypted);
|
||||||
const decrypted = try decryptWithKey(allocator, test_key, encrypted);
|
const decrypted = try decryptWithKey(allocator, test_key, encrypted);
|
||||||
|
@ -155,7 +155,7 @@ test "round trip, uneven block" {
|
||||||
const str = "0123456789ABCDEFG";
|
const str = "0123456789ABCDEFG";
|
||||||
var allocator = std.testing.allocator;
|
var allocator = std.testing.allocator;
|
||||||
var buf: [str.len]u8 = undefined;
|
var buf: [str.len]u8 = undefined;
|
||||||
const data = try std.fmt.bufPrint(&buf, str, .{});
|
var data = try std.fmt.bufPrint(&buf, str, .{});
|
||||||
const encrypted = try encryptWithKey(allocator, test_key, data);
|
const encrypted = try encryptWithKey(allocator, test_key, data);
|
||||||
defer allocator.free(encrypted);
|
defer allocator.free(encrypted);
|
||||||
const decrypted = try decryptWithKey(allocator, test_key, encrypted);
|
const decrypted = try decryptWithKey(allocator, test_key, encrypted);
|
||||||
|
@ -167,8 +167,8 @@ test "round trip, uneven block, using password" {
|
||||||
const str = "0123456789ABCDEFG";
|
const str = "0123456789ABCDEFG";
|
||||||
var allocator = std.testing.allocator;
|
var allocator = std.testing.allocator;
|
||||||
var buf: [str.len]u8 = undefined;
|
var buf: [str.len]u8 = undefined;
|
||||||
const data = try std.fmt.bufPrint(&buf, str, .{});
|
var data = try std.fmt.bufPrint(&buf, str, .{});
|
||||||
const key = try keyFromPassword(allocator, "foo", "");
|
var key = try keyFromPassword(allocator, "foo", "");
|
||||||
defer allocator.free(key);
|
defer allocator.free(key);
|
||||||
const encrypted = try encryptWithKey(allocator, key.*, data);
|
const encrypted = try encryptWithKey(allocator, key.*, data);
|
||||||
defer allocator.free(encrypted);
|
defer allocator.free(encrypted);
|
||||||
|
|
|
@ -42,9 +42,11 @@ pub fn main() !u8 {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
var args = try std.process.argsWithAllocator(allocator);
|
var args = std.process.args();
|
||||||
defer args.deinit();
|
defer args.deinit();
|
||||||
while (args.next()) |arg| {
|
while (args.next(allocator)) |arg_or_err| {
|
||||||
|
const arg = try arg_or_err;
|
||||||
|
defer allocator.free(arg);
|
||||||
if (std.mem.eql(u8, arg, "-w")) {
|
if (std.mem.eql(u8, arg, "-w")) {
|
||||||
watch = true;
|
watch = true;
|
||||||
break;
|
break;
|
||||||
|
@ -99,16 +101,7 @@ fn addToClipboard(allocator: std.mem.Allocator, data: []const u8) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Term = struct {
|
fn execLinux(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult {
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
stderr: []const u8,
|
|
||||||
term: std.process.Child.Term,
|
|
||||||
|
|
||||||
pub fn deinit(self: *Term) void {
|
|
||||||
self.allocator.free(self.stderr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fn execLinux(allocator: std.mem.Allocator, data: []const u8) !Term {
|
|
||||||
// we need to do this (maybe) because there is no real way to close stdin cleanly, which is what will
|
// we need to do this (maybe) because there is no real way to close stdin cleanly, which is what will
|
||||||
// kill xclip.
|
// kill xclip.
|
||||||
//
|
//
|
||||||
|
@ -121,21 +114,15 @@ fn execLinux(allocator: std.mem.Allocator, data: []const u8) !Term {
|
||||||
defer allocator.free(xclip_cmd);
|
defer allocator.free(xclip_cmd);
|
||||||
std.log.debug("cmd: {s}", .{xclip_cmd});
|
std.log.debug("cmd: {s}", .{xclip_cmd});
|
||||||
|
|
||||||
var child = std.process.Child.init(
|
return std.ChildProcess.exec(.{
|
||||||
&[_][]const u8{
|
.allocator = allocator,
|
||||||
|
.argv = &[_][]const u8{
|
||||||
"/usr/bin/env",
|
"/usr/bin/env",
|
||||||
"sh",
|
"sh",
|
||||||
"-c",
|
"-c",
|
||||||
xclip_cmd,
|
xclip_cmd,
|
||||||
},
|
},
|
||||||
allocator,
|
});
|
||||||
);
|
|
||||||
const term = try std.process.Child.spawnAndWait(&child);
|
|
||||||
return .{
|
|
||||||
.term = term,
|
|
||||||
.allocator = allocator,
|
|
||||||
.stderr = if (child.stderr) |e| try e.readToEndAlloc(allocator, 10 * 1024 * 1024) else try allocator.dupe(u8, ""),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execWindows(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult {
|
fn execWindows(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult {
|
||||||
|
|
|
@ -12,9 +12,11 @@ pub fn main() !u8 {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
var args = try std.process.argsWithAllocator(allocator);
|
var args = std.process.args();
|
||||||
defer args.deinit();
|
defer args.deinit();
|
||||||
while (args.next()) |arg| {
|
while (args.next(allocator)) |arg_or_err| {
|
||||||
|
const arg = try arg_or_err;
|
||||||
|
defer allocator.free(arg);
|
||||||
if (std.mem.eql(u8, arg, "-w")) {
|
if (std.mem.eql(u8, arg, "-w")) {
|
||||||
watch = true;
|
watch = true;
|
||||||
break;
|
break;
|
||||||
|
@ -27,15 +29,15 @@ pub fn main() !u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clipboardAction(allocator: std.mem.Allocator, watch: bool, clip: *Clipboard) !void {
|
fn clipboardAction(allocator: std.mem.Allocator, watch: bool, clip: *Clipboard) !void {
|
||||||
const display: *c.Display = c.XOpenDisplay(null).?;
|
var display: *c.Display = c.XOpenDisplay(null).?;
|
||||||
defer _ = c.XCloseDisplay(display);
|
defer _ = c.XCloseDisplay(display);
|
||||||
const default_screen = c.XDefaultScreen(display);
|
const default_screen = c.XDefaultScreen(display);
|
||||||
const color = c.XBlackPixel(display, default_screen);
|
var color = c.XBlackPixel(display, default_screen);
|
||||||
const window = c.XCreateSimpleWindow(display, c.XDefaultRootWindow(display), 0, 0, 1, 1, 0, color, color);
|
var window = c.XCreateSimpleWindow(display, c.XDefaultRootWindow(display), 0, 0, 1, 1, 0, color, color);
|
||||||
defer _ = c.XDestroyWindow(display, window);
|
defer _ = c.XDestroyWindow(display, window);
|
||||||
// Watch will not return
|
// Watch will not return
|
||||||
if (watch) try watchClip(allocator, display, window, "CLIPBOARD", clip);
|
if (watch) try watchClip(allocator, display, window, "CLIPBOARD", clip);
|
||||||
const result = (try printSelection(allocator, display, window, "CLIPBOARD", "UTF8_STRING", clip)) or
|
var result = (try printSelection(allocator, display, window, "CLIPBOARD", "UTF8_STRING", clip)) or
|
||||||
(try printSelection(allocator, display, window, "CLIPBOARD", "STRING", clip));
|
(try printSelection(allocator, display, window, "CLIPBOARD", "STRING", clip));
|
||||||
if (!result) return error.ClipboardActionFailed;
|
if (!result) return error.ClipboardActionFailed;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +46,7 @@ fn watchClip(allocator: std.mem.Allocator, display: *c.Display, window: c.Window
|
||||||
var event_base: c_int = undefined;
|
var event_base: c_int = undefined;
|
||||||
var error_base: c_int = undefined;
|
var error_base: c_int = undefined;
|
||||||
var event: c.XEvent = undefined;
|
var event: c.XEvent = undefined;
|
||||||
const bufid = c.XInternAtom(display, bufname, c.False);
|
var bufid = c.XInternAtom(display, bufname, c.False);
|
||||||
|
|
||||||
_ = c.XFixesQueryExtension(display, &event_base, &error_base);
|
_ = c.XFixesQueryExtension(display, &event_base, &error_base);
|
||||||
_ = c.XFixesSelectSelectionInput(display, c.XDefaultRootWindow(display), bufid, c.XFixesSetSelectionOwnerNotifyMask);
|
_ = c.XFixesSelectSelectionInput(display, c.XDefaultRootWindow(display), bufid, c.XFixesSetSelectionOwnerNotifyMask);
|
||||||
|
@ -72,10 +74,10 @@ fn printSelection(
|
||||||
var ressize: c_ulong = undefined;
|
var ressize: c_ulong = undefined;
|
||||||
var restail: c_ulong = undefined;
|
var restail: c_ulong = undefined;
|
||||||
var resbits: c_int = undefined;
|
var resbits: c_int = undefined;
|
||||||
const bufid = c.XInternAtom(display, bufname, c.False);
|
var bufid = c.XInternAtom(display, bufname, c.False);
|
||||||
var fmtid = c.XInternAtom(display, fmtname, c.False);
|
var fmtid = c.XInternAtom(display, fmtname, c.False);
|
||||||
const propid = c.XInternAtom(display, "XSEL_DATA", c.False);
|
var propid = c.XInternAtom(display, "XSEL_DATA", c.False);
|
||||||
const incrid = c.XInternAtom(display, "INCR", c.False);
|
var incrid = c.XInternAtom(display, "INCR", c.False);
|
||||||
var event: c.XEvent = undefined;
|
var event: c.XEvent = undefined;
|
||||||
|
|
||||||
_ = c.XSelectInput(display, window, c.PropertyChangeMask);
|
_ = c.XSelectInput(display, window, c.PropertyChangeMask);
|
||||||
|
@ -89,13 +91,13 @@ fn printSelection(
|
||||||
_ = c.XGetWindowProperty(display, window, propid, 0, c.LONG_MAX / 4, c.True, c.AnyPropertyType, &fmtid, &resbits, &ressize, &restail, &result);
|
_ = c.XGetWindowProperty(display, window, propid, 0, c.LONG_MAX / 4, c.True, c.AnyPropertyType, &fmtid, &resbits, &ressize, &restail, &result);
|
||||||
defer _ = c.XFree(result);
|
defer _ = c.XFree(result);
|
||||||
if (fmtid != incrid)
|
if (fmtid != incrid)
|
||||||
_ = try clip.clipboardChanged(std.mem.span(result)); // TODO: Ensure we don't start queueing these things up
|
_ = async clip.clipboardChanged(std.mem.span(result)); // TODO: Ensure we don't start queueing these things up
|
||||||
|
|
||||||
if (fmtid == incrid) {
|
if (fmtid == incrid) {
|
||||||
ressize = 1;
|
ressize = 1;
|
||||||
var arena_allocator = std.heap.ArenaAllocator.init(allocator);
|
var arena_allocator = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena_allocator.deinit();
|
defer arena_allocator.deinit();
|
||||||
const local_allocator = arena_allocator.allocator();
|
var local_allocator = arena_allocator.allocator();
|
||||||
var buffer = std.ArrayList(u8).init(local_allocator);
|
var buffer = std.ArrayList(u8).init(local_allocator);
|
||||||
defer buffer.deinit();
|
defer buffer.deinit();
|
||||||
while (ressize > 0) {
|
while (ressize > 0) {
|
||||||
|
@ -108,7 +110,7 @@ fn printSelection(
|
||||||
//defer _ = c.XFree(result); // Creates double free error, but not sure why
|
//defer _ = c.XFree(result); // Creates double free error, but not sure why
|
||||||
try buffer.appendSlice(try std.fmt.allocPrint(local_allocator, "{s}", .{result}));
|
try buffer.appendSlice(try std.fmt.allocPrint(local_allocator, "{s}", .{result}));
|
||||||
}
|
}
|
||||||
_ = try clip.clipboardChanged(buffer.items);
|
_ = async clip.clipboardChanged(buffer.items);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else // request failed, e.g. owner can't convert to the target format
|
} else // request failed, e.g. owner can't convert to the target format
|
||||||
|
|
|
@ -1191,7 +1191,7 @@ fn UpdateContents(hwnd: w.HWND) void {
|
||||||
// Get a lock on the clipboard
|
// Get a lock on the clipboard
|
||||||
if (OpenClipboard(hwnd) > 0) {
|
if (OpenClipboard(hwnd) > 0) {
|
||||||
defer _ = CloseClipboard();
|
defer _ = CloseClipboard();
|
||||||
var hglb = GetClipboardData(uFormat) orelse return;
|
var hglb = GetClipboardData(uFormat).?;
|
||||||
const hnd = @intCast(isize, @ptrToInt(hglb));
|
const hnd = @intCast(isize, @ptrToInt(hglb));
|
||||||
var lpstr = GlobalLock(hnd).?;
|
var lpstr = GlobalLock(hnd).?;
|
||||||
defer _ = GlobalUnlock(hnd);
|
defer _ = GlobalUnlock(hnd);
|
||||||
|
|
1
zfetch_deps.zig
Normal file
1
zfetch_deps.zig
Normal file
|
@ -0,0 +1 @@
|
||||||
|
const use_submodules = 1;
|
Loading…
Reference in New Issue
Block a user