From 0230222c0dbb2b9a1a8dcc3a91a225703fde3b9f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 14 Nov 2023 14:03:38 -0800 Subject: [PATCH] pkg/macos: add CoreVide for DisplayLink --- pkg/macos/build.zig | 1 + pkg/macos/main.zig | 1 + pkg/macos/video.zig | 6 +++ pkg/macos/video/c.zig | 3 ++ pkg/macos/video/display_link.zig | 71 ++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 pkg/macos/video.zig create mode 100644 pkg/macos/video/c.zig create mode 100644 pkg/macos/video/display_link.zig diff --git a/pkg/macos/build.zig b/pkg/macos/build.zig index 942100e45..cf8b4a541 100644 --- a/pkg/macos/build.zig +++ b/pkg/macos/build.zig @@ -28,6 +28,7 @@ pub fn build(b: *std.Build) !void { lib.linkFramework("CoreFoundation"); lib.linkFramework("CoreGraphics"); lib.linkFramework("CoreText"); + lib.linkFramework("CoreVideo"); if (!target.isNative()) try apple_sdk.addPaths(b, lib); b.installArtifact(lib); diff --git a/pkg/macos/main.zig b/pkg/macos/main.zig index 73bc5d3f6..46071d55b 100644 --- a/pkg/macos/main.zig +++ b/pkg/macos/main.zig @@ -2,6 +2,7 @@ pub const foundation = @import("foundation.zig"); pub const graphics = @import("graphics.zig"); pub const os = @import("os.zig"); pub const text = @import("text.zig"); +pub const video = @import("video.zig"); test { @import("std").testing.refAllDecls(@This()); diff --git a/pkg/macos/video.zig b/pkg/macos/video.zig new file mode 100644 index 000000000..c04f5cf27 --- /dev/null +++ b/pkg/macos/video.zig @@ -0,0 +1,6 @@ +pub const c = @import("video/c.zig"); +pub usingnamespace @import("video/display_link.zig"); + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/pkg/macos/video/c.zig b/pkg/macos/video/c.zig new file mode 100644 index 000000000..46c5d9ef3 --- /dev/null +++ b/pkg/macos/video/c.zig @@ -0,0 +1,3 @@ +pub usingnamespace @cImport({ + @cInclude("CoreVideo/CoreVideo.h"); +}); diff --git a/pkg/macos/video/display_link.zig b/pkg/macos/video/display_link.zig new file mode 100644 index 000000000..e7f4844a0 --- /dev/null +++ b/pkg/macos/video/display_link.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const c = @import("c.zig"); + +pub const DisplayLink = opaque { + pub const Error = error{ + InvalidOperation, + }; + + pub fn createWithActiveCGDisplays() Allocator.Error!*DisplayLink { + var result: ?*DisplayLink = null; + if (c.CVDisplayLinkCreateWithActiveCGDisplays( + @ptrCast(&result), + ) != c.kCVReturnSuccess) + return error.OutOfMemory; + + return result orelse error.OutOfMemory; + } + + pub fn release(self: *DisplayLink) void { + c.CVDisplayLinkRelease(@ptrCast(self)); + } + + pub fn start(self: *DisplayLink) Error!void { + if (c.CVDisplayLinkStart(@ptrCast(self)) != c.kCVReturnSuccess) + return error.InvalidOperation; + } + + pub fn stop(self: *DisplayLink) Error!void { + if (c.CVDisplayLinkStop(@ptrCast(self)) != c.kCVReturnSuccess) + return error.InvalidOperation; + } + + pub fn isRunning(self: *DisplayLink) bool { + return c.CVDisplayLinkIsRunning(@ptrCast(self)) != 0; + } + + // Note: this purposely throws away a ton of arguments I didn't need. + // It would be trivial to refactor this into Zig types and properly + // pass this through. + pub fn setOutputCallback( + self: *DisplayLink, + comptime callbackFn: *const fn (*DisplayLink, ?*anyopaque) void, + userinfo: ?*anyopaque, + ) Error!void { + if (c.CVDisplayLinkSetOutputCallback( + @ptrCast(self), + @ptrCast(&(struct { + fn callback( + displayLink: *DisplayLink, + inNow: *const c.CVTimeStamp, + inOutputTime: *const c.CVTimeStamp, + flagsIn: c.CVOptionFlags, + flagsOut: *c.CVOptionFlags, + inner_userinfo: ?*anyopaque, + ) callconv(.C) c.CVReturn { + _ = inNow; + _ = inOutputTime; + _ = flagsIn; + _ = flagsOut; + + callbackFn(displayLink, inner_userinfo); + return c.kCVReturnSuccess; + } + }).callback), + userinfo, + ) != c.kCVReturnSuccess) + return error.InvalidOperation; + } +};