chore: import upstream snapshot with attribution
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
pub const c = @import("animation/c.zig").c;
|
||||
|
||||
/// https://developer.apple.com/documentation/quartzcore/calayer/contents_gravity_values?language=objc
|
||||
pub extern "c" const kCAGravityTopLeft: *anyopaque;
|
||||
pub extern "c" const kCAGravityBottomLeft: *anyopaque;
|
||||
pub extern "c" const kCAGravityCenter: *anyopaque;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,82 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const module = b.addModule("macos", .{
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const lib = b.addLibrary(.{
|
||||
.name = "macos",
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
.linkage = .static,
|
||||
});
|
||||
|
||||
lib.addCSourceFile(.{
|
||||
.file = b.path("os/zig_macos.c"),
|
||||
.flags = &.{"-std=c99"},
|
||||
});
|
||||
lib.addCSourceFile(.{
|
||||
.file = b.path("text/ext.c"),
|
||||
});
|
||||
lib.linkFramework("CoreFoundation");
|
||||
lib.linkFramework("CoreGraphics");
|
||||
lib.linkFramework("CoreText");
|
||||
lib.linkFramework("CoreVideo");
|
||||
lib.linkFramework("QuartzCore");
|
||||
lib.linkFramework("IOSurface");
|
||||
if (target.result.os.tag == .macos) {
|
||||
lib.linkFramework("Carbon");
|
||||
module.linkFramework("Carbon", .{});
|
||||
}
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
module.linkFramework("CoreFoundation", .{});
|
||||
module.linkFramework("CoreGraphics", .{});
|
||||
module.linkFramework("CoreText", .{});
|
||||
module.linkFramework("CoreVideo", .{});
|
||||
module.linkFramework("QuartzCore", .{});
|
||||
module.linkFramework("IOSurface", .{});
|
||||
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
}
|
||||
b.installArtifact(lib);
|
||||
|
||||
{
|
||||
const test_exe = b.addTest(.{
|
||||
.name = "test",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
try apple_sdk.addPaths(b, test_exe);
|
||||
}
|
||||
test_exe.linkLibrary(lib);
|
||||
|
||||
var it = module.import_table.iterator();
|
||||
while (it.next()) |entry| {
|
||||
test_exe.root_module.addImport(
|
||||
entry.key_ptr.*,
|
||||
entry.value_ptr.*,
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(test_exe);
|
||||
|
||||
const tests_run = b.addRunArtifact(test_exe);
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&tests_run.step);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.{
|
||||
.name = .macos,
|
||||
.version = "0.1.0",
|
||||
.fingerprint = 0x45e2f6107d5b2b2c,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub const c = @import("carbon/c.zig").c;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,18 @@
|
||||
pub const c = @import("dispatch/c.zig").c;
|
||||
pub const data = @import("dispatch/data.zig");
|
||||
pub const queue = @import("dispatch/queue.zig");
|
||||
pub const Data = data.Data;
|
||||
|
||||
pub extern "c" fn dispatch_sync(
|
||||
queue: *anyopaque,
|
||||
block: *anyopaque,
|
||||
) void;
|
||||
|
||||
pub extern "c" fn dispatch_async(
|
||||
queue: *anyopaque,
|
||||
block: *anyopaque,
|
||||
) void;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,31 @@
|
||||
const std = @import("std");
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Data = opaque {
|
||||
pub const DESTRUCTOR_DEFAULT = c.DISPATCH_DATA_DESTRUCTOR_DEFAULT;
|
||||
|
||||
pub fn create(
|
||||
data: []const u8,
|
||||
queue: ?*anyopaque,
|
||||
destructor: ?*anyopaque,
|
||||
) !*Data {
|
||||
return dispatch_data_create(
|
||||
data.ptr,
|
||||
data.len,
|
||||
queue,
|
||||
destructor,
|
||||
) orelse return error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(data: *Data) void {
|
||||
foundation.c.CFRelease(data);
|
||||
}
|
||||
};
|
||||
|
||||
extern "c" fn dispatch_data_create(
|
||||
data: [*]const u8,
|
||||
len: usize,
|
||||
queue: ?*anyopaque,
|
||||
destructor: ?*anyopaque,
|
||||
) ?*Data;
|
||||
@@ -0,0 +1,8 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Queue = *anyopaque; // dispatch_queue_t
|
||||
|
||||
pub fn getMain() Queue {
|
||||
return c.dispatch_get_main_queue().?;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
const array = @import("foundation/array.zig");
|
||||
const attributed_string = @import("foundation/attributed_string.zig");
|
||||
const base = @import("foundation/base.zig");
|
||||
const character_set = @import("foundation/character_set.zig");
|
||||
const data = @import("foundation/data.zig");
|
||||
const dictionary = @import("foundation/dictionary.zig");
|
||||
const number = @import("foundation/number.zig");
|
||||
const string = @import("foundation/string.zig");
|
||||
const typepkg = @import("foundation/type.zig");
|
||||
const url = @import("foundation/url.zig");
|
||||
|
||||
pub const c = @import("foundation/c.zig").c;
|
||||
pub const Array = array.Array;
|
||||
pub const MutableArray = array.MutableArray;
|
||||
pub const AttributedString = attributed_string.AttributedString;
|
||||
pub const MutableAttributedString = attributed_string.MutableAttributedString;
|
||||
pub const ComparisonResult = base.ComparisonResult;
|
||||
pub const Range = base.Range;
|
||||
pub const FourCharCode = base.FourCharCode;
|
||||
pub const CharacterSet = character_set.CharacterSet;
|
||||
pub const Data = data.Data;
|
||||
pub const Dictionary = dictionary.Dictionary;
|
||||
pub const MutableDictionary = dictionary.MutableDictionary;
|
||||
pub const Number = number.Number;
|
||||
pub const String = string.String;
|
||||
pub const MutableString = string.MutableString;
|
||||
pub const StringComparison = string.StringComparison;
|
||||
pub const StringEncoding = string.StringEncoding;
|
||||
pub const stringGetSurrogatePairForLongCharacter = string.stringGetSurrogatePairForLongCharacter;
|
||||
pub const URL = url.URL;
|
||||
pub const URLPathStyle = url.URLPathStyle;
|
||||
pub const CFRelease = typepkg.CFRelease;
|
||||
pub const CFRetain = typepkg.CFRetain;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const base = @import("base.zig");
|
||||
const c = @import("c.zig").c;
|
||||
const cftype = @import("type.zig");
|
||||
const ComparisonResult = base.ComparisonResult;
|
||||
const Range = base.Range;
|
||||
|
||||
pub const Array = opaque {
|
||||
pub fn create(comptime T: type, values: []*const T) Allocator.Error!*Array {
|
||||
return CFArrayCreate(
|
||||
null,
|
||||
@ptrCast(values.ptr),
|
||||
@intCast(values.len),
|
||||
null,
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Array) void {
|
||||
cftype.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getCount(self: *Array) usize {
|
||||
return CFArrayGetCount(self);
|
||||
}
|
||||
|
||||
/// Note the return type is actually a `*const T` but we strip the
|
||||
/// constness so that further API calls work correctly. The Foundation
|
||||
/// API doesn't properly mark things const/non-const.
|
||||
pub fn getValueAtIndex(self: *Array, comptime T: type, idx: usize) *T {
|
||||
return @ptrCast(@alignCast(CFArrayGetValueAtIndex(self, idx)));
|
||||
}
|
||||
|
||||
pub extern "c" fn CFArrayCreate(
|
||||
allocator: ?*anyopaque,
|
||||
values: [*]*const anyopaque,
|
||||
num_values: usize,
|
||||
callbacks: ?*const anyopaque,
|
||||
) ?*Array;
|
||||
pub extern "c" fn CFArrayGetCount(*Array) usize;
|
||||
pub extern "c" fn CFArrayGetValueAtIndex(*Array, usize) *anyopaque;
|
||||
extern "c" var kCFTypeArrayCallBacks: anyopaque;
|
||||
};
|
||||
|
||||
pub const MutableArray = opaque {
|
||||
pub fn create() Allocator.Error!*MutableArray {
|
||||
return CFArrayCreateMutable(
|
||||
null,
|
||||
0,
|
||||
&c.kCFTypeArrayCallBacks,
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createCopy(array: *Array) Allocator.Error!*MutableArray {
|
||||
return CFArrayCreateMutableCopy(
|
||||
null,
|
||||
0,
|
||||
array,
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *MutableArray) void {
|
||||
cftype.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn appendValue(
|
||||
self: *MutableArray,
|
||||
comptime Elem: type,
|
||||
value: *const Elem,
|
||||
) void {
|
||||
CFArrayAppendValue(self, @ptrCast(@constCast(value)));
|
||||
}
|
||||
|
||||
pub fn removeValue(self: *MutableArray, idx: usize) void {
|
||||
CFArrayRemoveValueAtIndex(self, idx);
|
||||
}
|
||||
|
||||
pub fn sortValues(
|
||||
self: *MutableArray,
|
||||
comptime Elem: type,
|
||||
comptime Context: type,
|
||||
context: ?*Context,
|
||||
comptime comparator: ?*const fn (
|
||||
a: *const Elem,
|
||||
b: *const Elem,
|
||||
context: ?*Context,
|
||||
) callconv(.c) ComparisonResult,
|
||||
) void {
|
||||
CFArraySortValues(
|
||||
self,
|
||||
Range.init(0, Array.CFArrayGetCount(@ptrCast(self))),
|
||||
comparator,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
extern "c" fn CFArrayCreateMutable(
|
||||
allocator: ?*anyopaque,
|
||||
capacity: usize,
|
||||
callbacks: ?*const anyopaque,
|
||||
) ?*MutableArray;
|
||||
extern "c" fn CFArrayCreateMutableCopy(
|
||||
allocator: ?*anyopaque,
|
||||
capacity: usize,
|
||||
array: *Array,
|
||||
) ?*MutableArray;
|
||||
extern "c" fn CFArrayAppendValue(
|
||||
*MutableArray,
|
||||
*anyopaque,
|
||||
) void;
|
||||
extern "c" fn CFArrayRemoveValueAtIndex(
|
||||
*MutableArray,
|
||||
usize,
|
||||
) void;
|
||||
extern "c" fn CFArraySortValues(
|
||||
array: *MutableArray,
|
||||
range: Range,
|
||||
comparator: ?*const anyopaque,
|
||||
context: ?*anyopaque,
|
||||
) void;
|
||||
};
|
||||
|
||||
test "array" {
|
||||
const testing = std.testing;
|
||||
|
||||
const str = "hello";
|
||||
var values = [_]*const u8{ &str[0], &str[1] };
|
||||
const arr = try Array.create(u8, &values);
|
||||
defer arr.release();
|
||||
|
||||
try testing.expectEqual(@as(usize, 2), arr.getCount());
|
||||
|
||||
{
|
||||
const ch = arr.getValueAtIndex(u8, 0);
|
||||
try testing.expectEqual(@as(u8, 'h'), ch.*);
|
||||
}
|
||||
|
||||
// Can make it mutable
|
||||
var mut = try MutableArray.createCopy(arr);
|
||||
defer mut.release();
|
||||
}
|
||||
|
||||
test "array sorting" {
|
||||
const testing = std.testing;
|
||||
|
||||
const str = "hello";
|
||||
var values = [_]*const u8{ &str[0], &str[1] };
|
||||
const arr = try Array.create(u8, &values);
|
||||
defer arr.release();
|
||||
const mut = try MutableArray.createCopy(arr);
|
||||
defer mut.release();
|
||||
|
||||
mut.sortValues(
|
||||
u8,
|
||||
void,
|
||||
null,
|
||||
struct {
|
||||
fn compare(a: *const u8, b: *const u8, _: ?*void) callconv(.c) ComparisonResult {
|
||||
if (a.* > b.*) return .greater;
|
||||
if (a.* == b.*) return .equal;
|
||||
return .less;
|
||||
}
|
||||
}.compare,
|
||||
);
|
||||
|
||||
{
|
||||
const mutarr: *Array = @ptrCast(mut);
|
||||
const ch = mutarr.getValueAtIndex(u8, 0);
|
||||
try testing.expectEqual(@as(u8, 'e'), ch.*);
|
||||
}
|
||||
{
|
||||
const mutarr: *Array = @ptrCast(mut);
|
||||
const ch = mutarr.getValueAtIndex(u8, 1);
|
||||
try testing.expectEqual(@as(u8, 'h'), ch.*);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const AttributedString = opaque {
|
||||
pub fn create(
|
||||
str: *foundation.String,
|
||||
attributes: *foundation.Dictionary,
|
||||
) Allocator.Error!*AttributedString {
|
||||
return @ptrCast(@constCast(c.CFAttributedStringCreate(
|
||||
null,
|
||||
@ptrCast(str),
|
||||
@ptrCast(attributes),
|
||||
) orelse return Allocator.Error.OutOfMemory));
|
||||
}
|
||||
|
||||
pub fn release(self: *AttributedString) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getLength(self: *AttributedString) usize {
|
||||
return @intCast(c.CFAttributedStringGetLength(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getString(self: *AttributedString) *foundation.String {
|
||||
return @ptrFromInt(@intFromPtr(
|
||||
c.CFAttributedStringGetString(@ptrCast(self)),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
pub const MutableAttributedString = opaque {
|
||||
pub fn create(cap: usize) Allocator.Error!*MutableAttributedString {
|
||||
return @as(
|
||||
?*MutableAttributedString,
|
||||
@ptrFromInt(@intFromPtr(c.CFAttributedStringCreateMutable(
|
||||
null,
|
||||
@intCast(cap),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *MutableAttributedString) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn replaceString(
|
||||
self: *MutableAttributedString,
|
||||
range: foundation.Range,
|
||||
replacement: *foundation.String,
|
||||
) void {
|
||||
c.CFAttributedStringReplaceString(
|
||||
@ptrCast(self),
|
||||
@bitCast(range),
|
||||
@ptrCast(replacement),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setAttribute(
|
||||
self: *MutableAttributedString,
|
||||
range: foundation.Range,
|
||||
key: anytype,
|
||||
value: ?*anyopaque,
|
||||
) void {
|
||||
const T = @TypeOf(key);
|
||||
const info = @typeInfo(T);
|
||||
const Key = if (info != .pointer) T else info.pointer.child;
|
||||
const key_arg = if (@hasDecl(Key, "key"))
|
||||
key.key()
|
||||
else
|
||||
key;
|
||||
|
||||
c.CFAttributedStringSetAttribute(
|
||||
@ptrCast(self),
|
||||
@bitCast(range),
|
||||
@ptrCast(key_arg),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getLength(self: *MutableAttributedString) usize {
|
||||
return @intCast(c.CFAttributedStringGetLength(@ptrCast(self)));
|
||||
}
|
||||
};
|
||||
|
||||
test "mutable attributed string" {
|
||||
//const testing = std.testing;
|
||||
|
||||
const str = try MutableAttributedString.create(0);
|
||||
defer str.release();
|
||||
|
||||
{
|
||||
const rep = try foundation.String.createWithBytes("hello", .utf8, false);
|
||||
defer rep.release();
|
||||
str.replaceString(foundation.Range.init(0, 0), rep);
|
||||
}
|
||||
|
||||
str.setAttribute(foundation.Range.init(0, 0), text.FontAttribute.url, null);
|
||||
str.setAttribute(foundation.Range.init(0, 0), text.FontAttribute.name.key(), null);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const ComparisonResult = enum(c_int) {
|
||||
less = -1,
|
||||
equal = 0,
|
||||
greater = 1,
|
||||
};
|
||||
|
||||
pub const Range = extern struct {
|
||||
location: c.CFIndex,
|
||||
length: c.CFIndex,
|
||||
|
||||
pub fn init(loc: usize, len: usize) Range {
|
||||
return @bitCast(c.CFRangeMake(@intCast(loc), @intCast(len)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const FourCharCode = packed struct(u32) {
|
||||
d: u8,
|
||||
c: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
|
||||
pub fn init(v: *const [4]u8) FourCharCode {
|
||||
return .{ .a = v[0], .b = v[1], .c = v[2], .d = v[3] };
|
||||
}
|
||||
|
||||
/// Converts the ID to a string. The return value is only valid
|
||||
/// for the lifetime of the self pointer.
|
||||
pub fn str(self: FourCharCode) [4]u8 {
|
||||
return .{ self.a, self.b, self.c, self.d };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,49 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const CharacterSet = opaque {
|
||||
pub fn createWithCharactersInString(
|
||||
str: *foundation.String,
|
||||
) Allocator.Error!*CharacterSet {
|
||||
return @as(?*CharacterSet, @ptrFromInt(@intFromPtr(c.CFCharacterSetCreateWithCharactersInString(
|
||||
null,
|
||||
@ptrCast(str),
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createWithCharactersInRange(
|
||||
range: foundation.Range,
|
||||
) Allocator.Error!*CharacterSet {
|
||||
return @as(?*CharacterSet, @ptrFromInt(@intFromPtr(c.CFCharacterSetCreateWithCharactersInRange(
|
||||
null,
|
||||
@bitCast(range),
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *CharacterSet) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
};
|
||||
|
||||
test "character set" {
|
||||
//const testing = std.testing;
|
||||
|
||||
const str = try foundation.String.createWithBytes("hello world", .ascii, false);
|
||||
defer str.release();
|
||||
|
||||
const cs = try CharacterSet.createWithCharactersInString(str);
|
||||
defer cs.release();
|
||||
}
|
||||
|
||||
test "character set range" {
|
||||
//const testing = std.testing;
|
||||
|
||||
const cs = try CharacterSet.createWithCharactersInRange(.{
|
||||
.location = 'A',
|
||||
.length = 1,
|
||||
});
|
||||
defer cs.release();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Data = opaque {
|
||||
pub fn createWithBytesNoCopy(data: []const u8) Allocator.Error!*Data {
|
||||
return @as(
|
||||
?*Data,
|
||||
@ptrFromInt(@intFromPtr(c.CFDataCreateWithBytesNoCopy(
|
||||
null,
|
||||
data.ptr,
|
||||
@intCast(data.len),
|
||||
c.kCFAllocatorNull,
|
||||
))),
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Data) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getPointer(self: *Data) [*]const u8 {
|
||||
return @ptrCast(c.CFDataGetBytePtr(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getLength(self: *Data) usize {
|
||||
return @intCast(c.CFDataGetLength(@ptrCast(self)));
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
//const testing = std.testing;
|
||||
|
||||
const raw = "hello world";
|
||||
const data = try Data.createWithBytesNoCopy(raw);
|
||||
defer data.release();
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Dictionary = opaque {
|
||||
pub fn create(
|
||||
keys: ?[]const ?*const anyopaque,
|
||||
values: ?[]const ?*const anyopaque,
|
||||
) Allocator.Error!*Dictionary {
|
||||
if (keys != null or values != null) {
|
||||
assert(keys != null);
|
||||
assert(values != null);
|
||||
assert(keys.?.len == values.?.len);
|
||||
}
|
||||
|
||||
return @as(?*Dictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreate(
|
||||
null,
|
||||
@ptrCast(@constCast(if (keys) |slice| slice.ptr else null)),
|
||||
@ptrCast(@constCast(if (values) |slice| slice.ptr else null)),
|
||||
@intCast(if (keys) |slice| slice.len else 0),
|
||||
&c.kCFTypeDictionaryKeyCallBacks,
|
||||
&c.kCFTypeDictionaryValueCallBacks,
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Dictionary) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getCount(self: *Dictionary) usize {
|
||||
return @intCast(c.CFDictionaryGetCount(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getValue(self: *Dictionary, comptime V: type, key: ?*const anyopaque) ?*V {
|
||||
return @ptrFromInt(@intFromPtr(c.CFDictionaryGetValue(
|
||||
@ptrCast(self),
|
||||
key,
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn getKeysAndValues(self: *Dictionary, alloc: Allocator) !struct {
|
||||
keys: []?*const anyopaque,
|
||||
values: []?*const anyopaque,
|
||||
} {
|
||||
const count = self.getCount();
|
||||
const keys = try alloc.alloc(?*const anyopaque, count);
|
||||
errdefer alloc.free(keys);
|
||||
const values = try alloc.alloc(?*const anyopaque, count);
|
||||
errdefer alloc.free(values);
|
||||
c.CFDictionaryGetKeysAndValues(
|
||||
@ptrCast(self),
|
||||
@ptrCast(keys.ptr),
|
||||
@ptrCast(values.ptr),
|
||||
);
|
||||
return .{ .keys = keys, .values = values };
|
||||
}
|
||||
};
|
||||
|
||||
pub const MutableDictionary = opaque {
|
||||
pub fn create(cap: usize) Allocator.Error!*MutableDictionary {
|
||||
return @as(?*MutableDictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreateMutable(
|
||||
null,
|
||||
@intCast(cap),
|
||||
&c.kCFTypeDictionaryKeyCallBacks,
|
||||
&c.kCFTypeDictionaryValueCallBacks,
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createMutableCopy(cap: usize, src: *Dictionary) Allocator.Error!*MutableDictionary {
|
||||
return @as(?*MutableDictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreateMutableCopy(
|
||||
null,
|
||||
@intCast(cap),
|
||||
@ptrCast(src),
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *MutableDictionary) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn setValue(self: *MutableDictionary, key: ?*const anyopaque, value: ?*const anyopaque) void {
|
||||
c.CFDictionarySetValue(
|
||||
@ptrCast(self),
|
||||
key,
|
||||
value,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
test "dictionary" {
|
||||
const testing = std.testing;
|
||||
|
||||
const str = try foundation.String.createWithBytes("hello", .unicode, false);
|
||||
defer str.release();
|
||||
|
||||
var keys = [_]?*const anyopaque{c.kCFURLIsPurgeableKey};
|
||||
var values = [_]?*const anyopaque{str};
|
||||
const dict = try Dictionary.create(&keys, &values);
|
||||
defer dict.release();
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), dict.getCount());
|
||||
try testing.expect(dict.getValue(foundation.String, c.kCFURLIsPurgeableKey) != null);
|
||||
try testing.expect(dict.getValue(foundation.String, c.kCFURLIsVolumeKey) == null);
|
||||
}
|
||||
|
||||
test "mutable dictionary" {
|
||||
const testing = std.testing;
|
||||
|
||||
const dict = try MutableDictionary.create(0);
|
||||
defer dict.release();
|
||||
|
||||
const str = try foundation.String.createWithBytes("hello", .unicode, false);
|
||||
defer str.release();
|
||||
|
||||
dict.setValue(c.kCFURLIsPurgeableKey, str);
|
||||
|
||||
{
|
||||
const imm = @as(*Dictionary, @ptrCast(dict));
|
||||
try testing.expectEqual(@as(usize, 1), imm.getCount());
|
||||
try testing.expect(imm.getValue(foundation.String, c.kCFURLIsPurgeableKey) != null);
|
||||
try testing.expect(imm.getValue(foundation.String, c.kCFURLIsVolumeKey) == null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Number = opaque {
|
||||
pub fn create(
|
||||
comptime type_: NumberType,
|
||||
value: *const type_.ValueType(),
|
||||
) Allocator.Error!*Number {
|
||||
return @as(?*Number, @ptrFromInt(@intFromPtr(c.CFNumberCreate(
|
||||
null,
|
||||
@intFromEnum(type_),
|
||||
value,
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn getValue(self: *const Number, comptime t: NumberType, ptr: *t.ValueType()) bool {
|
||||
return c.CFNumberGetValue(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(t),
|
||||
ptr,
|
||||
) == 1;
|
||||
}
|
||||
|
||||
pub fn release(self: *Number) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const NumberType = enum(c.CFNumberType) {
|
||||
sint8 = c.kCFNumberSInt8Type,
|
||||
sint16 = c.kCFNumberSInt16Type,
|
||||
sint32 = c.kCFNumberSInt32Type,
|
||||
sint64 = c.kCFNumberSInt64Type,
|
||||
float32 = c.kCFNumberFloat32Type,
|
||||
float64 = c.kCFNumberFloat64Type,
|
||||
char = c.kCFNumberCharType,
|
||||
short = c.kCFNumberShortType,
|
||||
int = c.kCFNumberIntType,
|
||||
long = c.kCFNumberLongType,
|
||||
long_long = c.kCFNumberLongLongType,
|
||||
float = c.kCFNumberFloatType,
|
||||
double = c.kCFNumberDoubleType,
|
||||
cf_index = c.kCFNumberCFIndexType,
|
||||
ns_integer = c.kCFNumberNSIntegerType,
|
||||
cg_float = c.kCFNumberCGFloatType,
|
||||
|
||||
pub fn ValueType(comptime self: NumberType) type {
|
||||
return switch (self) {
|
||||
.sint8 => i8,
|
||||
.sint16 => i16,
|
||||
.sint32 => i32,
|
||||
.sint64 => i64,
|
||||
.float32 => f32,
|
||||
.float64 => f64,
|
||||
.char => u8,
|
||||
.short => c_short,
|
||||
.int => c_int,
|
||||
.long => c_long,
|
||||
.long_long => c_longlong,
|
||||
.float => f32,
|
||||
.double => f64,
|
||||
else => unreachable, // TODO
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
|
||||
const inner: i8 = 42;
|
||||
const v = try Number.create(.sint8, &inner);
|
||||
defer v.release();
|
||||
|
||||
var result: i8 = undefined;
|
||||
try testing.expect(v.getValue(.sint8, &result));
|
||||
try testing.expectEqual(result, inner);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const String = opaque {
|
||||
pub fn createWithBytes(
|
||||
bs: []const u8,
|
||||
encoding: StringEncoding,
|
||||
external: bool,
|
||||
) Allocator.Error!*String {
|
||||
return @as(?*String, @ptrFromInt(@intFromPtr(c.CFStringCreateWithBytes(
|
||||
null,
|
||||
bs.ptr,
|
||||
@intCast(bs.len),
|
||||
@intFromEnum(encoding),
|
||||
@intFromBool(external),
|
||||
)))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createWithCharactersNoCopy(
|
||||
unichars: []const u16,
|
||||
) *String {
|
||||
return @as(*String, @ptrFromInt(@intFromPtr(c.CFStringCreateWithCharactersNoCopy(
|
||||
null,
|
||||
@ptrCast(unichars.ptr),
|
||||
@intCast(unichars.len),
|
||||
foundation.c.kCFAllocatorNull,
|
||||
))));
|
||||
}
|
||||
|
||||
pub fn release(self: *String) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getLength(self: *String) usize {
|
||||
return @intCast(c.CFStringGetLength(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn hasPrefix(self: *String, prefix: *String) bool {
|
||||
return c.CFStringHasPrefix(
|
||||
@ptrCast(self),
|
||||
@ptrCast(prefix),
|
||||
) == 1;
|
||||
}
|
||||
|
||||
pub fn compare(
|
||||
self: *String,
|
||||
other: *String,
|
||||
options: StringComparison,
|
||||
) foundation.ComparisonResult {
|
||||
return @enumFromInt(c.CFStringCompare(
|
||||
@ptrCast(self),
|
||||
@ptrCast(other),
|
||||
@intCast(@as(c_int, @bitCast(options))),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn cstring(self: *String, buf: []u8, encoding: StringEncoding) ?[]const u8 {
|
||||
if (c.CFStringGetCString(
|
||||
@ptrCast(self),
|
||||
buf.ptr,
|
||||
@intCast(buf.len),
|
||||
@intFromEnum(encoding),
|
||||
) == 0) return null;
|
||||
return std.mem.sliceTo(buf, 0);
|
||||
}
|
||||
|
||||
pub fn cstringPtr(self: *String, encoding: StringEncoding) ?[:0]const u8 {
|
||||
const ptr = c.CFStringGetCStringPtr(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(encoding),
|
||||
);
|
||||
if (ptr == null) return null;
|
||||
return std.mem.sliceTo(ptr, 0);
|
||||
}
|
||||
};
|
||||
|
||||
pub const MutableString = opaque {
|
||||
pub fn create(cap: usize) !*MutableString {
|
||||
return @ptrCast(c.CFStringCreateMutable(
|
||||
null,
|
||||
@intCast(cap),
|
||||
) orelse return Allocator.Error.OutOfMemory);
|
||||
}
|
||||
|
||||
pub fn release(self: *MutableString) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn string(self: *MutableString) *String {
|
||||
return @ptrCast(self);
|
||||
}
|
||||
|
||||
pub fn appendCharacters(self: *MutableString, chars: []const u16) void {
|
||||
c.CFStringAppendCharacters(
|
||||
@ptrCast(self),
|
||||
chars.ptr,
|
||||
@intCast(chars.len),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pub const StringComparison = packed struct {
|
||||
case_insensitive: bool = false,
|
||||
_unused_2: bool = false,
|
||||
backwards: bool = false,
|
||||
anchored: bool = false,
|
||||
nonliteral: bool = false,
|
||||
localized: bool = false,
|
||||
numerically: bool = false,
|
||||
diacritic_insensitive: bool = false,
|
||||
width_insensitive: bool = false,
|
||||
forced_ordering: bool = false,
|
||||
_padding: u22 = 0,
|
||||
|
||||
test {
|
||||
try std.testing.expectEqual(@bitSizeOf(c_int), @bitSizeOf(StringComparison));
|
||||
}
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/corefoundation/cfstringencoding?language=objc
|
||||
pub const StringEncoding = enum(u32) {
|
||||
invalid = 0xffffffff,
|
||||
mac_roman = 0,
|
||||
windows_latin1 = 0x0500,
|
||||
iso_latin1 = 0x0201,
|
||||
nextstep_latin = 0x0B01,
|
||||
ascii = 0x0600,
|
||||
unicode = 0x0100,
|
||||
utf8 = 0x08000100,
|
||||
non_lossy_ascii = 0x0BFF,
|
||||
utf16_be = 0x10000100,
|
||||
utf16_le = 0x14000100,
|
||||
utf32 = 0x0c000100,
|
||||
utf32_be = 0x18000100,
|
||||
utf32_le = 0x1c000100,
|
||||
};
|
||||
|
||||
pub fn stringGetSurrogatePairForLongCharacter(
|
||||
ch: u32,
|
||||
surrogates: []u16,
|
||||
) bool {
|
||||
assert(surrogates.len >= 2);
|
||||
return c.CFStringGetSurrogatePairForLongCharacter(ch, surrogates.ptr) == 1;
|
||||
}
|
||||
|
||||
test "string" {
|
||||
const testing = std.testing;
|
||||
|
||||
const str = try String.createWithBytes("hello world", .ascii, false);
|
||||
defer str.release();
|
||||
|
||||
const prefix = try String.createWithBytes("hello", .ascii, false);
|
||||
defer prefix.release();
|
||||
|
||||
try testing.expect(str.hasPrefix(prefix));
|
||||
try testing.expectEqual(foundation.ComparisonResult.equal, str.compare(str, .{}));
|
||||
try testing.expectEqualStrings("hello world", str.cstringPtr(.ascii).?);
|
||||
|
||||
{
|
||||
var buf: [128]u8 = undefined;
|
||||
const cstr = str.cstring(&buf, .ascii).?;
|
||||
try testing.expectEqualStrings("hello world", cstr);
|
||||
}
|
||||
}
|
||||
|
||||
test "unichar" {
|
||||
const testing = std.testing;
|
||||
|
||||
var unichars: [2]u16 = undefined;
|
||||
try testing.expect(!stringGetSurrogatePairForLongCharacter('A', &unichars));
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub extern "c" fn CFRelease(*anyopaque) void;
|
||||
pub extern "c" fn CFRetain(*anyopaque) void;
|
||||
@@ -0,0 +1,104 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const URL = opaque {
|
||||
pub fn createWithString(str: *foundation.String, base: ?*URL) Allocator.Error!*URL {
|
||||
return CFURLCreateWithString(
|
||||
null,
|
||||
str,
|
||||
base,
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createWithFileSystemPath(
|
||||
path: *foundation.String,
|
||||
style: URLPathStyle,
|
||||
dir: bool,
|
||||
) Allocator.Error!*URL {
|
||||
return @as(
|
||||
?*URL,
|
||||
@ptrFromInt(@intFromPtr(c.CFURLCreateWithFileSystemPath(
|
||||
null,
|
||||
@ptrCast(path),
|
||||
@intFromEnum(style),
|
||||
if (dir) 1 else 0,
|
||||
))),
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createStringByReplacingPercentEscapes(
|
||||
str: *foundation.String,
|
||||
escape: *foundation.String,
|
||||
) Allocator.Error!*foundation.String {
|
||||
return CFURLCreateStringByReplacingPercentEscapes(
|
||||
null,
|
||||
str,
|
||||
escape,
|
||||
) orelse return error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *URL) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn copyPath(self: *URL) ?*foundation.String {
|
||||
return CFURLCopyPath(self);
|
||||
}
|
||||
|
||||
pub extern "c" fn CFURLCreateWithString(
|
||||
allocator: ?*anyopaque,
|
||||
url_string: *const anyopaque,
|
||||
base_url: ?*const anyopaque,
|
||||
) ?*URL;
|
||||
pub extern "c" fn CFURLCopyPath(*URL) ?*foundation.String;
|
||||
pub extern "c" fn CFURLCreateStringByReplacingPercentEscapes(
|
||||
allocator: ?*anyopaque,
|
||||
original: *const anyopaque,
|
||||
escape: *const anyopaque,
|
||||
) ?*foundation.String;
|
||||
};
|
||||
|
||||
pub const URLPathStyle = enum(c_int) {
|
||||
posix = c.kCFURLPOSIXPathStyle,
|
||||
windows = c.kCFURLWindowsPathStyle,
|
||||
};
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
|
||||
const str = try foundation.String.createWithBytes("http://www.example.com/foo", .utf8, false);
|
||||
defer str.release();
|
||||
|
||||
const url = try URL.createWithString(str, null);
|
||||
defer url.release();
|
||||
|
||||
{
|
||||
const path = url.copyPath().?;
|
||||
defer path.release();
|
||||
|
||||
var buf: [128]u8 = undefined;
|
||||
const cstr = path.cstring(&buf, .utf8).?;
|
||||
try testing.expectEqualStrings("/foo", cstr);
|
||||
}
|
||||
}
|
||||
|
||||
test "path" {
|
||||
const testing = std.testing;
|
||||
|
||||
const str = try foundation.String.createWithBytes("foo/bar.ttf", .utf8, false);
|
||||
defer str.release();
|
||||
|
||||
const url = try URL.createWithFileSystemPath(str, .posix, false);
|
||||
defer url.release();
|
||||
|
||||
{
|
||||
const path = url.copyPath().?;
|
||||
defer path.release();
|
||||
|
||||
var buf: [128]u8 = undefined;
|
||||
const cstr = path.cstring(&buf, .utf8).?;
|
||||
try testing.expectEqualStrings("foo/bar.ttf", cstr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
const affine_transform = @import("graphics/affine_transform.zig");
|
||||
const bitmap_context = @import("graphics/bitmap_context.zig");
|
||||
const color_space = @import("graphics/color_space.zig");
|
||||
const font = @import("graphics/font.zig");
|
||||
const geometry = @import("graphics/geometry.zig");
|
||||
const image = @import("graphics/image.zig");
|
||||
const path = @import("graphics/path.zig");
|
||||
|
||||
pub const c = @import("graphics/c.zig").c;
|
||||
pub const AffineTransform = affine_transform.AffineTransform;
|
||||
pub const BitmapContext = bitmap_context.BitmapContext;
|
||||
pub const ColorSpace = color_space.ColorSpace;
|
||||
pub const Glyph = font.Glyph;
|
||||
pub const Point = geometry.Point;
|
||||
pub const Rect = geometry.Rect;
|
||||
pub const Size = geometry.Size;
|
||||
pub const ImageAlphaInfo = image.ImageAlphaInfo;
|
||||
pub const BitmapInfo = image.BitmapInfo;
|
||||
pub const Path = path.Path;
|
||||
pub const MutablePath = path.MutablePath;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const AffineTransform = extern struct {
|
||||
a: c.CGFloat,
|
||||
b: c.CGFloat,
|
||||
c: c.CGFloat,
|
||||
d: c.CGFloat,
|
||||
tx: c.CGFloat,
|
||||
ty: c.CGFloat,
|
||||
|
||||
pub fn identity() AffineTransform {
|
||||
return @bitCast(c.CGAffineTransformIdentity);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const graphics = @import("../graphics.zig");
|
||||
const Context = @import("context.zig").Context;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const BitmapContext = opaque {
|
||||
pub const context = Context(BitmapContext);
|
||||
|
||||
pub fn create(
|
||||
data: ?[]u8,
|
||||
width: usize,
|
||||
height: usize,
|
||||
bits_per_component: usize,
|
||||
bytes_per_row: usize,
|
||||
space: *graphics.ColorSpace,
|
||||
opts: c_uint,
|
||||
) Allocator.Error!*BitmapContext {
|
||||
return @as(
|
||||
?*BitmapContext,
|
||||
@ptrFromInt(@intFromPtr(c.CGBitmapContextCreate(
|
||||
@ptrCast(if (data) |d| d.ptr else null),
|
||||
width,
|
||||
height,
|
||||
bits_per_component,
|
||||
bytes_per_row,
|
||||
@ptrCast(space),
|
||||
opts,
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
//const testing = std.testing;
|
||||
|
||||
const cs = try graphics.ColorSpace.createDeviceGray();
|
||||
defer cs.release();
|
||||
const ctx = try BitmapContext.create(null, 80, 80, 8, 80, cs, 0);
|
||||
const context = BitmapContext.context;
|
||||
defer context.release(ctx);
|
||||
context.setShouldAntialias(ctx, true);
|
||||
context.setShouldSmoothFonts(ctx, false);
|
||||
context.setGrayFillColor(ctx, 1, 1);
|
||||
context.setGrayStrokeColor(ctx, 1, 1);
|
||||
context.setTextDrawingMode(ctx, .fill);
|
||||
context.setTextMatrix(ctx, graphics.AffineTransform.identity());
|
||||
context.setTextPosition(ctx, 0, 0);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,99 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const ColorSpace = opaque {
|
||||
pub fn createDeviceGray() Allocator.Error!*ColorSpace {
|
||||
return @as(
|
||||
?*ColorSpace,
|
||||
@ptrFromInt(@intFromPtr(c.CGColorSpaceCreateDeviceGray())),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createDeviceRGB() Allocator.Error!*ColorSpace {
|
||||
return @as(
|
||||
?*ColorSpace,
|
||||
@ptrFromInt(@intFromPtr(c.CGColorSpaceCreateDeviceRGB())),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createNamed(name: Name) Allocator.Error!*ColorSpace {
|
||||
return @as(
|
||||
?*ColorSpace,
|
||||
@ptrFromInt(@intFromPtr(c.CGColorSpaceCreateWithName(name.cfstring()))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *ColorSpace) void {
|
||||
c.CGColorSpaceRelease(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub const Name = enum {
|
||||
/// This color space uses the DCI P3 primaries, a D65 white point, and
|
||||
/// the sRGB transfer function.
|
||||
displayP3,
|
||||
/// The Display P3 color space with a linear transfer function and
|
||||
/// extended-range values.
|
||||
extendedLinearDisplayP3,
|
||||
/// The sRGB colorimetry and non-linear transfer function are specified
|
||||
/// in IEC 61966-2-1.
|
||||
sRGB,
|
||||
/// This color space has the same colorimetry as `sRGB`, but uses a
|
||||
/// linear transfer function.
|
||||
linearSRGB,
|
||||
/// This color space has the same colorimetry as `sRGB`, but you can
|
||||
/// encode component values below `0.0` and above `1.0`. Negative values
|
||||
/// are encoded as the signed reflection of the original encoding
|
||||
/// function, as shown in the formula below:
|
||||
/// ```
|
||||
/// extendedTransferFunction(x) = sign(x) * sRGBTransferFunction(abs(x))
|
||||
/// ```
|
||||
extendedSRGB,
|
||||
/// This color space has the same colorimetry as `sRGB`; in addition,
|
||||
/// you may encode component values below `0.0` and above `1.0`.
|
||||
extendedLinearSRGB,
|
||||
/// ...
|
||||
genericGrayGamma2_2,
|
||||
/// ...
|
||||
linearGray,
|
||||
/// This color space has the same colorimetry as `genericGrayGamma2_2`,
|
||||
/// but you can encode component values below `0.0` and above `1.0`.
|
||||
/// Negative values are encoded as the signed reflection of the
|
||||
/// original encoding function, as shown in the formula below:
|
||||
/// ```
|
||||
/// extendedGrayTransferFunction(x) = sign(x) * gamma22Function(abs(x))
|
||||
/// ```
|
||||
extendedGray,
|
||||
/// This color space has the same colorimetry as `linearGray`; in
|
||||
/// addition, you may encode component values below `0.0` and above `1.0`.
|
||||
extendedLinearGray,
|
||||
|
||||
fn cfstring(self: Name) c.CFStringRef {
|
||||
return switch (self) {
|
||||
.displayP3 => c.kCGColorSpaceDisplayP3,
|
||||
.extendedLinearDisplayP3 => c.kCGColorSpaceExtendedLinearDisplayP3,
|
||||
.sRGB => c.kCGColorSpaceSRGB,
|
||||
.extendedSRGB => c.kCGColorSpaceExtendedSRGB,
|
||||
.linearSRGB => c.kCGColorSpaceLinearSRGB,
|
||||
.extendedLinearSRGB => c.kCGColorSpaceExtendedLinearSRGB,
|
||||
.genericGrayGamma2_2 => c.kCGColorSpaceGenericGrayGamma2_2,
|
||||
.extendedGray => c.kCGColorSpaceExtendedGray,
|
||||
.linearGray => c.kCGColorSpaceLinearGray,
|
||||
.extendedLinearGray => c.kCGColorSpaceExtendedLinearGray,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
test {
|
||||
//const testing = std.testing;
|
||||
|
||||
const space = try ColorSpace.createDeviceGray();
|
||||
defer space.release();
|
||||
}
|
||||
|
||||
test {
|
||||
const space = try ColorSpace.createDeviceRGB();
|
||||
defer space.release();
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const graphics = @import("../graphics.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// Returns a struct that has all the shared context functions for the
|
||||
/// given type.
|
||||
pub fn Context(comptime T: type) type {
|
||||
return struct {
|
||||
value: *T,
|
||||
|
||||
pub fn release(self: *T) void {
|
||||
c.CGContextRelease(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn setLineWidth(self: *T, width: f64) void {
|
||||
c.CGContextSetLineWidth(
|
||||
@ptrCast(self),
|
||||
width,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setAllowsAntialiasing(self: *T, v: bool) void {
|
||||
c.CGContextSetAllowsAntialiasing(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setAllowsFontSmoothing(self: *T, v: bool) void {
|
||||
c.CGContextSetAllowsFontSmoothing(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setAllowsFontSubpixelPositioning(self: *T, v: bool) void {
|
||||
c.CGContextSetAllowsFontSubpixelPositioning(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setAllowsFontSubpixelQuantization(self: *T, v: bool) void {
|
||||
c.CGContextSetAllowsFontSubpixelQuantization(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setShouldAntialias(self: *T, v: bool) void {
|
||||
c.CGContextSetShouldAntialias(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setShouldSmoothFonts(self: *T, v: bool) void {
|
||||
c.CGContextSetShouldSmoothFonts(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setShouldSubpixelPositionFonts(self: *T, v: bool) void {
|
||||
c.CGContextSetShouldSubpixelPositionFonts(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setShouldSubpixelQuantizeFonts(self: *T, v: bool) void {
|
||||
c.CGContextSetShouldSubpixelQuantizeFonts(
|
||||
@ptrCast(self),
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setGrayFillColor(self: *T, gray: f64, alpha: f64) void {
|
||||
c.CGContextSetGrayFillColor(
|
||||
@ptrCast(self),
|
||||
gray,
|
||||
alpha,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setGrayStrokeColor(self: *T, gray: f64, alpha: f64) void {
|
||||
c.CGContextSetGrayStrokeColor(
|
||||
@ptrCast(self),
|
||||
gray,
|
||||
alpha,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setRGBFillColor(self: *T, r: f64, g: f64, b: f64, alpha: f64) void {
|
||||
c.CGContextSetRGBFillColor(
|
||||
@ptrCast(self),
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
alpha,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setRGBStrokeColor(self: *T, r: f64, g: f64, b: f64, alpha: f64) void {
|
||||
c.CGContextSetRGBStrokeColor(
|
||||
@ptrCast(self),
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
alpha,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setTextDrawingMode(self: *T, mode: TextDrawingMode) void {
|
||||
c.CGContextSetTextDrawingMode(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(mode),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setTextMatrix(self: *T, matrix: graphics.AffineTransform) void {
|
||||
c.CGContextSetTextMatrix(
|
||||
@ptrCast(self),
|
||||
@bitCast(matrix),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setTextPosition(self: *T, x: f64, y: f64) void {
|
||||
c.CGContextSetTextPosition(
|
||||
@ptrCast(self),
|
||||
x,
|
||||
y,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn fillRect(self: *T, rect: graphics.Rect) void {
|
||||
c.CGContextFillRect(
|
||||
@ptrCast(self),
|
||||
@bitCast(rect),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn scaleCTM(self: *T, sx: c.CGFloat, sy: c.CGFloat) void {
|
||||
c.CGContextScaleCTM(
|
||||
@ptrCast(self),
|
||||
sx,
|
||||
sy,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn translateCTM(self: *T, tx: c.CGFloat, ty: c.CGFloat) void {
|
||||
c.CGContextTranslateCTM(
|
||||
@ptrCast(self),
|
||||
tx,
|
||||
ty,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const TextDrawingMode = enum(c_int) {
|
||||
fill = c.kCGTextFill,
|
||||
stroke = c.kCGTextStroke,
|
||||
fill_stroke = c.kCGTextFillStroke,
|
||||
invisible = c.kCGTextInvisible,
|
||||
fill_clip = c.kCGTextFillClip,
|
||||
stroke_clip = c.kCGTextStrokeClip,
|
||||
fill_stroke_clip = c.kCGTextFillStrokeClip,
|
||||
clip = c.kCGTextClip,
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Glyph = c.CGGlyph;
|
||||
@@ -0,0 +1,34 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Point = extern struct {
|
||||
x: c.CGFloat,
|
||||
y: c.CGFloat,
|
||||
};
|
||||
|
||||
pub const Rect = extern struct {
|
||||
origin: Point,
|
||||
size: Size,
|
||||
|
||||
pub fn init(x: f64, y: f64, width: f64, height: f64) Rect {
|
||||
return @bitCast(c.CGRectMake(x, y, width, height));
|
||||
}
|
||||
|
||||
pub fn isNull(self: Rect) bool {
|
||||
return c.CGRectIsNull(@bitCast(self));
|
||||
}
|
||||
|
||||
pub fn getHeight(self: Rect) c.CGFloat {
|
||||
return c.CGRectGetHeight(@bitCast(self));
|
||||
}
|
||||
|
||||
pub fn getWidth(self: Rect) c.CGFloat {
|
||||
return c.CGRectGetWidth(@bitCast(self));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Size = extern struct {
|
||||
width: c.CGFloat,
|
||||
height: c.CGFloat,
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const graphics = @import("../graphics.zig");
|
||||
const context = @import("context.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const ImageAlphaInfo = enum(c_uint) {
|
||||
none = c.kCGImageAlphaNone,
|
||||
premultiplied_last = c.kCGImageAlphaPremultipliedLast,
|
||||
premultiplied_first = c.kCGImageAlphaPremultipliedFirst,
|
||||
last = c.kCGImageAlphaLast,
|
||||
first = c.kCGImageAlphaFirst,
|
||||
none_skip_last = c.kCGImageAlphaNoneSkipLast,
|
||||
none_skip_first = c.kCGImageAlphaNoneSkipFirst,
|
||||
only = c.kCGImageAlphaOnly,
|
||||
};
|
||||
|
||||
pub const BitmapInfo = enum(c_uint) {
|
||||
alpha_mask = c.kCGBitmapAlphaInfoMask,
|
||||
float_mask = c.kCGBitmapFloatInfoMask,
|
||||
float_components = c.kCGBitmapFloatComponents,
|
||||
byte_order_mask = c.kCGBitmapByteOrderMask,
|
||||
byte_order_default = c.kCGBitmapByteOrderDefault,
|
||||
byte_order_16_little = c.kCGBitmapByteOrder16Little,
|
||||
byte_order_32_little = c.kCGBitmapByteOrder32Little,
|
||||
byte_order_16_big = c.kCGBitmapByteOrder16Big,
|
||||
byte_order_32_big = c.kCGBitmapByteOrder32Big,
|
||||
|
||||
_,
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Path = opaque {
|
||||
pub fn createWithRect(
|
||||
rect: graphics.Rect,
|
||||
transform: ?*const graphics.AffineTransform,
|
||||
) Allocator.Error!*Path {
|
||||
return @as(
|
||||
?*Path,
|
||||
@ptrFromInt(@intFromPtr(c.CGPathCreateWithRect(
|
||||
@bitCast(rect),
|
||||
@ptrCast(transform),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Path) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const MutablePath = opaque {
|
||||
pub fn create() Allocator.Error!*MutablePath {
|
||||
return @as(
|
||||
?*MutablePath,
|
||||
@ptrFromInt(@intFromPtr(c.CGPathCreateMutable())),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *MutablePath) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn addRect(
|
||||
self: *MutablePath,
|
||||
transform: ?*const graphics.AffineTransform,
|
||||
rect: graphics.Rect,
|
||||
) void {
|
||||
c.CGPathAddRect(
|
||||
@ptrCast(self),
|
||||
@ptrCast(transform),
|
||||
@bitCast(rect),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getBoundingBox(self: *MutablePath) graphics.Rect {
|
||||
return @bitCast(c.CGPathGetBoundingBox(@ptrCast(self)));
|
||||
}
|
||||
};
|
||||
|
||||
test "mutable path" {
|
||||
//const testing = std.testing;
|
||||
|
||||
const path = try MutablePath.create();
|
||||
defer path.release();
|
||||
|
||||
path.addRect(null, graphics.Rect.init(0, 0, 100, 200));
|
||||
}
|
||||
|
||||
test "path from rect" {
|
||||
const path = try Path.createWithRect(graphics.Rect.init(0, 0, 100, 200), null);
|
||||
defer path.release();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
const iosurface = @import("iosurface/iosurface.zig");
|
||||
|
||||
pub const c = @import("iosurface/c.zig").c;
|
||||
pub const IOSurface = iosurface.IOSurface;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,136 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const c = @import("c.zig").c;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const video = @import("../video.zig");
|
||||
|
||||
pub const IOSurface = opaque {
|
||||
pub const Error = error{
|
||||
InvalidOperation,
|
||||
};
|
||||
|
||||
pub const Properties = struct {
|
||||
width: c_int,
|
||||
height: c_int,
|
||||
pixel_format: video.PixelFormat,
|
||||
bytes_per_element: c_int,
|
||||
colorspace: ?*graphics.ColorSpace,
|
||||
};
|
||||
|
||||
pub fn init(properties: Properties) Allocator.Error!*IOSurface {
|
||||
var w = try foundation.Number.create(.int, &properties.width);
|
||||
defer w.release();
|
||||
var h = try foundation.Number.create(.int, &properties.height);
|
||||
defer h.release();
|
||||
var pf = try foundation.Number.create(.int, &@as(c_int, @intFromEnum(properties.pixel_format)));
|
||||
defer pf.release();
|
||||
var bpe = try foundation.Number.create(.int, &properties.bytes_per_element);
|
||||
defer bpe.release();
|
||||
|
||||
var properties_dict = try foundation.Dictionary.create(
|
||||
&[_]?*const anyopaque{
|
||||
c.kIOSurfaceWidth,
|
||||
c.kIOSurfaceHeight,
|
||||
c.kIOSurfacePixelFormat,
|
||||
c.kIOSurfaceBytesPerElement,
|
||||
},
|
||||
&[_]?*const anyopaque{ w, h, pf, bpe },
|
||||
);
|
||||
defer properties_dict.release();
|
||||
|
||||
var surface = @as(?*IOSurface, @ptrFromInt(@intFromPtr(
|
||||
c.IOSurfaceCreate(@ptrCast(properties_dict)),
|
||||
))) orelse return error.OutOfMemory;
|
||||
|
||||
if (properties.colorspace) |space| {
|
||||
surface.setColorSpace(space);
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *IOSurface) void {
|
||||
// We mark it purgeable so that it is immediately unloaded, so that we
|
||||
// don't have to wait for CoreFoundation garbage collection to trigger.
|
||||
_ = c.IOSurfaceSetPurgeable(
|
||||
@ptrCast(self),
|
||||
c.kIOSurfacePurgeableEmpty,
|
||||
null,
|
||||
);
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn retain(self: *IOSurface) void {
|
||||
foundation.CFRetain(self);
|
||||
}
|
||||
|
||||
pub fn release(self: *IOSurface) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn setColorSpace(self: *IOSurface, colorspace: *graphics.ColorSpace) void {
|
||||
const serialized_colorspace = graphics.c.CGColorSpaceCopyPropertyList(
|
||||
@ptrCast(colorspace),
|
||||
).?;
|
||||
defer foundation.CFRelease(@constCast(serialized_colorspace));
|
||||
|
||||
c.IOSurfaceSetValue(
|
||||
@ptrCast(self),
|
||||
c.kIOSurfaceColorSpace,
|
||||
@ptrCast(serialized_colorspace),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn lock(self: *IOSurface) void {
|
||||
c.IOSurfaceLock(
|
||||
@ptrCast(self),
|
||||
0,
|
||||
null,
|
||||
);
|
||||
}
|
||||
pub inline fn unlock(self: *IOSurface) void {
|
||||
c.IOSurfaceUnlock(
|
||||
@ptrCast(self),
|
||||
0,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn getAllocSize(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetAllocSize(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getWidth(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetWidth(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getHeight(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetHeight(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getBytesPerElement(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetBytesPerElement(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getBytesPerRow(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetBytesPerRow(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getBaseAddress(self: *IOSurface) ?[*]u8 {
|
||||
return @ptrCast(c.IOSurfaceGetBaseAddress(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub inline fn getElementWidth(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetElementWidth(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getElementHeight(self: *IOSurface) usize {
|
||||
return c.IOSurfaceGetElementHeight(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub inline fn getPixelFormat(self: *IOSurface) video.PixelFormat {
|
||||
return @enumFromInt(c.IOSurfaceGetPixelFormat(@ptrCast(self)));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const carbon = @import("carbon.zig");
|
||||
pub const foundation = @import("foundation.zig");
|
||||
pub const animation = @import("animation.zig");
|
||||
pub const dispatch = @import("dispatch.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");
|
||||
pub const iosurface = @import("iosurface.zig");
|
||||
|
||||
// All of our C imports consolidated into one place. We used to
|
||||
// import them one by one in each package but Zig 0.14 has some
|
||||
// kind of issue with that I wasn't able to minimize.
|
||||
pub const c = @cImport({
|
||||
@cInclude("CoreFoundation/CoreFoundation.h");
|
||||
@cInclude("CoreGraphics/CoreGraphics.h");
|
||||
@cInclude("CoreText/CoreText.h");
|
||||
@cInclude("CoreVideo/CoreVideo.h");
|
||||
@cInclude("CoreVideo/CVPixelBuffer.h");
|
||||
@cInclude("QuartzCore/CALayer.h");
|
||||
@cInclude("IOSurface/IOSurfaceRef.h");
|
||||
@cInclude("dispatch/dispatch.h");
|
||||
@cInclude("os/log.h");
|
||||
@cInclude("os/signpost.h");
|
||||
|
||||
if (builtin.os.tag == .macos) {
|
||||
@cInclude("Carbon/Carbon.h");
|
||||
}
|
||||
});
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
const log = @import("os/log.zig");
|
||||
|
||||
pub const c = @import("os/c.zig");
|
||||
pub const signpost = @import("os/signpost.zig");
|
||||
pub const Log = log.Log;
|
||||
pub const LogType = log.LogType;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,65 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Log = opaque {
|
||||
pub fn create(
|
||||
subsystem: [:0]const u8,
|
||||
category: [:0]const u8,
|
||||
) *Log {
|
||||
return @ptrCast(c.os_log_create(
|
||||
subsystem.ptr,
|
||||
category.ptr,
|
||||
).?);
|
||||
}
|
||||
|
||||
pub fn release(self: *Log) void {
|
||||
c.os_release(self);
|
||||
}
|
||||
|
||||
pub fn typeEnabled(self: *Log, typ: LogType) bool {
|
||||
return c.os_log_type_enabled(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(typ),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn log(
|
||||
self: *Log,
|
||||
alloc: Allocator,
|
||||
typ: LogType,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const str = nosuspend std.fmt.allocPrintSentinel(
|
||||
alloc,
|
||||
format,
|
||||
args,
|
||||
0,
|
||||
) catch return;
|
||||
defer alloc.free(str);
|
||||
zig_os_log_with_type(self, typ, str.ptr);
|
||||
}
|
||||
|
||||
extern "c" fn zig_os_log_with_type(*Log, LogType, [*c]const u8) void;
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/os/os_log_type_t?language=objc
|
||||
pub const LogType = enum(c.os_log_type_t) {
|
||||
default = c.OS_LOG_TYPE_DEFAULT,
|
||||
debug = c.OS_LOG_TYPE_DEBUG,
|
||||
info = c.OS_LOG_TYPE_INFO,
|
||||
err = c.OS_LOG_TYPE_ERROR,
|
||||
fault = c.OS_LOG_TYPE_FAULT,
|
||||
};
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
|
||||
const log = Log.create("com.mitchellh.ghostty", "test");
|
||||
defer log.release();
|
||||
|
||||
try testing.expect(log.typeEnabled(.fault));
|
||||
log.log(testing.allocator, .default, "hello {d}", .{12});
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const c = @import("c.zig").c;
|
||||
const logpkg = @import("log.zig");
|
||||
const Log = logpkg.Log;
|
||||
|
||||
/// This should be called once at the start of the program to intialize
|
||||
/// some required state for signpost logging.
|
||||
///
|
||||
/// This is all to workaround a Zig bug:
|
||||
/// https://github.com/ziglang/zig/issues/24370
|
||||
pub fn init() void {
|
||||
if (__dso_handle != null) return;
|
||||
|
||||
const sym = comptime sym: {
|
||||
const root = @import("root");
|
||||
|
||||
// If we have a main function, use that as the symbol.
|
||||
if (@hasDecl(root, "main")) break :sym root.main;
|
||||
|
||||
// Otherwise, we're in a library, so we just use the first
|
||||
// function in our root module. I actually don't know if this is
|
||||
// all required or if we can just use the real `__dso_handle` symbol,
|
||||
// but this seems to work for now.
|
||||
for (@typeInfo(root).@"struct".decls) |decl_info| {
|
||||
const decl = @field(root, decl_info.name);
|
||||
if (@typeInfo(@TypeOf(decl)) == .@"fn") break :sym decl;
|
||||
}
|
||||
|
||||
@compileError("no functions found in root module");
|
||||
};
|
||||
|
||||
// Since __dso_handle is not automatically populated by the linker,
|
||||
// we populate it by looking up the main function's module address
|
||||
// which should be a mach-o header.
|
||||
var info: DlInfo = undefined;
|
||||
const result = dladdr(sym, &info);
|
||||
assert(result != 0);
|
||||
__dso_handle = @ptrCast(@alignCast(info.dli_fbase));
|
||||
}
|
||||
|
||||
/// This should REALLY be an extern var that is populated by the linker,
|
||||
/// but there is a Zig bug: https://github.com/ziglang/zig/issues/24370
|
||||
var __dso_handle: ?*c.mach_header = null;
|
||||
|
||||
// Import the necessary C functions and types
|
||||
extern "c" fn dladdr(addr: ?*const anyopaque, info: *DlInfo) c_int;
|
||||
|
||||
// Define the Dl_info structure
|
||||
const DlInfo = extern struct {
|
||||
dli_fname: [*:0]const u8, // Pathname of shared object
|
||||
dli_fbase: ?*anyopaque, // Base address of shared object
|
||||
dli_sname: [*:0]const u8, // Name of nearest symbol
|
||||
dli_saddr: ?*anyopaque, // Address of nearest symbol
|
||||
};
|
||||
|
||||
/// Checks whether signpost logging is enabled for the given log handle.
|
||||
/// Returns true if signposts will be recorded for this log, false otherwise.
|
||||
/// This can be used to avoid expensive operations when signpost logging is disabled.
|
||||
///
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_enabled?language=objc
|
||||
pub fn enabled(log: *Log) bool {
|
||||
return c.os_signpost_enabled(@ptrCast(log));
|
||||
}
|
||||
|
||||
/// Emits a signpost event - a single point in time marker.
|
||||
/// Events are useful for marking when specific actions occur, such as
|
||||
/// user interactions, state changes, or other discrete occurrences.
|
||||
/// The event will appear as a vertical line in Instruments.
|
||||
///
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_event_emit?language=objc
|
||||
pub fn emitEvent(
|
||||
log: *Log,
|
||||
id: Id,
|
||||
comptime name: [:0]const u8,
|
||||
) void {
|
||||
emitWithName(log, id, .event, name);
|
||||
}
|
||||
|
||||
/// Marks the beginning of a time interval.
|
||||
/// Use this with intervalEnd to measure the duration of operations.
|
||||
/// The same ID must be used for both the begin and end calls.
|
||||
/// Intervals appear as horizontal bars in Instruments timeline.
|
||||
///
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_interval_begin?language=objc
|
||||
pub fn intervalBegin(log: *Log, id: Id, comptime name: [:0]const u8) void {
|
||||
emitWithName(log, id, .interval_begin, name);
|
||||
}
|
||||
|
||||
/// Marks the end of a time interval.
|
||||
/// Must be paired with a prior intervalBegin call using the same ID.
|
||||
/// The name should match the name used in intervalBegin.
|
||||
/// Instruments will calculate and display the duration between begin and end.
|
||||
///
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_interval_end?language=objc
|
||||
pub fn intervalEnd(log: *Log, id: Id, comptime name: [:0]const u8) void {
|
||||
emitWithName(log, id, .interval_end, name);
|
||||
}
|
||||
|
||||
/// The internal function to emit a signpost with a specific name.
|
||||
fn emitWithName(
|
||||
log: *Log,
|
||||
id: Id,
|
||||
typ: Type,
|
||||
comptime name: [:0]const u8,
|
||||
) void {
|
||||
// Init must be called by this point.
|
||||
assert(__dso_handle != null);
|
||||
|
||||
var buf: [2]u8 = @splat(0);
|
||||
c._os_signpost_emit_with_name_impl(
|
||||
__dso_handle,
|
||||
@ptrCast(log),
|
||||
@intFromEnum(typ),
|
||||
@intFromEnum(id),
|
||||
name.ptr,
|
||||
"".ptr,
|
||||
&buf,
|
||||
buf.len,
|
||||
);
|
||||
}
|
||||
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_id_t?language=objc
|
||||
pub const Id = enum(u64) {
|
||||
null = 0, // OS_SIGNPOST_ID_NULL
|
||||
invalid = 0xFFFFFFFFFFFFFFFF, // OS_SIGNPOST_ID_INVALID
|
||||
exclusive = 0xEEEEB0B5B2B2EEEE, // OS_SIGNPOST_ID_EXCLUSIVE
|
||||
_,
|
||||
|
||||
/// Generates a new signpost ID for use with signpost operations.
|
||||
/// The ID is unique for the given log handle and can be used to track
|
||||
/// asynchronous operations or mark specific points of interest in the code.
|
||||
/// Returns a unique signpost ID that can be used with os_signpost functions.
|
||||
///
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_id_generate?language=objc
|
||||
pub fn generate(log: *Log) Id {
|
||||
return @enumFromInt(c.os_signpost_id_generate(@ptrCast(log)));
|
||||
}
|
||||
|
||||
/// Creates a signpost ID based on a pointer value.
|
||||
/// This is useful for tracking operations associated with a specific object
|
||||
/// or memory location. The same pointer will always generate the same ID
|
||||
/// for a given log handle, allowing correlation of signpost events.
|
||||
/// Pass null to get the null signpost ID.
|
||||
///
|
||||
/// https://developer.apple.com/documentation/os/os_signpost_id_for_pointer?language=objc
|
||||
pub fn forPointer(log: *Log, ptr: ?*anyopaque) Id {
|
||||
return @enumFromInt(c.os_signpost_id_make_with_pointer(
|
||||
@ptrCast(log),
|
||||
@ptrCast(ptr),
|
||||
));
|
||||
}
|
||||
|
||||
test "generate ID" {
|
||||
// We can't really test the return value because it may return null
|
||||
// if signposts are disabled.
|
||||
const id: Id = .generate(Log.create("com.mitchellh.ghostty", "test"));
|
||||
try std.testing.expect(id != .invalid);
|
||||
}
|
||||
|
||||
test "generate ID for pointer" {
|
||||
var foo: usize = 0x1234;
|
||||
const id: Id = .forPointer(Log.create("com.mitchellh.ghostty", "test"), &foo);
|
||||
try std.testing.expect(id != .null);
|
||||
}
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/os/ossignposttype?language=objc
|
||||
pub const Type = enum(u8) {
|
||||
event = 0, // OS_SIGNPOST_EVENT
|
||||
interval_begin = 1, // OS_SIGNPOST_INTERVAL_BEGIN
|
||||
interval_end = 2, // OS_SIGNPOST_INTERVAL_END
|
||||
|
||||
pub const mask: u8 = 0x03; // OS_SIGNPOST_TYPE_MASK
|
||||
};
|
||||
|
||||
/// Special os_log category values that surface in Instruments and other
|
||||
/// tooling.
|
||||
pub const Category = struct {
|
||||
/// Points of Interest appear as a dedicated track in Instruments.
|
||||
/// Use this for high-level application events that help understand
|
||||
/// the flow of your application.
|
||||
pub const points_of_interest: [:0]const u8 = "PointsOfInterest";
|
||||
|
||||
/// Dynamic Tracing category enables runtime-configurable logging.
|
||||
/// Signposts in this category can be enabled/disabled dynamically
|
||||
/// without recompiling.
|
||||
pub const dynamic_tracing: [:0]const u8 = "DynamicTracing";
|
||||
|
||||
/// Dynamic Stack Tracing category captures call stacks at signpost
|
||||
/// events. This provides deeper debugging information but has higher
|
||||
/// performance overhead.
|
||||
pub const dynamic_stack_tracing: [:0]const u8 = "DynamicStackTracing";
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Id;
|
||||
}
|
||||
|
||||
test enabled {
|
||||
_ = enabled(Log.create("com.mitchellh.ghostty", "test"));
|
||||
}
|
||||
|
||||
test "intervals" {
|
||||
init();
|
||||
|
||||
const log = Log.create("com.mitchellh.ghostty", "test");
|
||||
defer log.release();
|
||||
|
||||
// Test that we can begin and end an interval
|
||||
const id = Id.generate(log);
|
||||
intervalBegin(log, id, "Test Interval");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <os/log.h>
|
||||
#include <os/signpost.h>
|
||||
|
||||
// A wrapper so we can use the os_log_with_type macro.
|
||||
void zig_os_log_with_type(
|
||||
os_log_t log,
|
||||
os_log_type_t type,
|
||||
const char *message
|
||||
) {
|
||||
os_log_with_type(log, type, "%{public}s", message);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
const font = @import("text/font.zig");
|
||||
const font_collection = @import("text/font_collection.zig");
|
||||
const font_descriptor = @import("text/font_descriptor.zig");
|
||||
const font_manager = @import("text/font_manager.zig");
|
||||
const frame = @import("text/frame.zig");
|
||||
const framesetter = @import("text/framesetter.zig");
|
||||
const typesetter = @import("text/typesetter.zig");
|
||||
const line = @import("text/line.zig");
|
||||
const paragraph_style = @import("text/paragraph_style.zig");
|
||||
const run = @import("text/run.zig");
|
||||
const stylized_strings = @import("text/stylized_strings.zig");
|
||||
|
||||
pub const c = @import("text/c.zig").c;
|
||||
pub const Font = font.Font;
|
||||
pub const FontTableTag = font.FontTableTag;
|
||||
pub const FontCollection = font_collection.FontCollection;
|
||||
pub const FontDescriptor = font_descriptor.FontDescriptor;
|
||||
pub const FontAttribute = font_descriptor.FontAttribute;
|
||||
pub const FontTraitKey = font_descriptor.FontTraitKey;
|
||||
pub const FontVariationAxisKey = font_descriptor.FontVariationAxisKey;
|
||||
pub const FontSymbolicTraits = font_descriptor.FontSymbolicTraits;
|
||||
pub const createFontDescriptorsFromURL = font_manager.createFontDescriptorsFromURL;
|
||||
pub const createFontDescriptorsFromData = font_manager.createFontDescriptorsFromData;
|
||||
pub const createFontDescriptorFromData = font_manager.createFontDescriptorFromData;
|
||||
pub const Frame = frame.Frame;
|
||||
pub const Framesetter = framesetter.Framesetter;
|
||||
pub const Typesetter = typesetter.Typesetter;
|
||||
pub const Line = line.Line;
|
||||
pub const ParagraphStyle = paragraph_style.ParagraphStyle;
|
||||
pub const ParagraphStyleSetting = paragraph_style.ParagraphStyleSetting;
|
||||
pub const ParagraphStyleSpecifier = paragraph_style.ParagraphStyleSpecifier;
|
||||
pub const WritingDirection = paragraph_style.WritingDirection;
|
||||
pub const Run = run.Run;
|
||||
pub const StringAttribute = stylized_strings.StringAttribute;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <CoreText/CoreText.h>
|
||||
|
||||
// A wrapper to fix a Zig C ABI issue.
|
||||
void zig_cabi_CTLineGetBoundsWithOptions(
|
||||
CTLineRef line,
|
||||
CTLineBoundsOptions options,
|
||||
CGRect *result
|
||||
) {
|
||||
*result = CTLineGetBoundsWithOptions(line, options);
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Font = opaque {
|
||||
pub fn createWithFontDescriptor(desc: *const text.FontDescriptor, size: f32) Allocator.Error!*Font {
|
||||
return @as(
|
||||
?*Font,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontCreateWithFontDescriptor(
|
||||
@ptrCast(desc),
|
||||
size,
|
||||
null,
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createForString(
|
||||
self: *Font,
|
||||
str: *foundation.String,
|
||||
range: foundation.Range,
|
||||
) ?*Font {
|
||||
return @ptrCast(@constCast(c.CTFontCreateForString(
|
||||
@ptrCast(self),
|
||||
@ptrCast(str),
|
||||
@bitCast(range),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn copyWithAttributes(
|
||||
self: *Font,
|
||||
size: f32,
|
||||
matrix: ?*const graphics.AffineTransform,
|
||||
attrs: ?*text.FontDescriptor,
|
||||
) Allocator.Error!*Font {
|
||||
return @as(
|
||||
?*Font,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontCreateCopyWithAttributes(
|
||||
@ptrCast(self),
|
||||
size,
|
||||
@ptrCast(matrix),
|
||||
@ptrCast(attrs),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Font) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn retain(self: *Font) void {
|
||||
_ = c.CFRetain(self);
|
||||
}
|
||||
|
||||
pub fn copyDescriptor(self: *Font) *text.FontDescriptor {
|
||||
return @ptrCast(@constCast(c.CTFontCopyFontDescriptor(@ptrCast(self))));
|
||||
}
|
||||
|
||||
pub fn copyFeatures(self: *Font) *foundation.Array {
|
||||
return @ptrCast(@constCast(c.CTFontCopyFeatures(@ptrCast(self))));
|
||||
}
|
||||
|
||||
pub fn copyDefaultCascadeListForLanguages(self: *Font) *foundation.Array {
|
||||
return @ptrCast(@constCast(c.CTFontCopyDefaultCascadeListForLanguages(@ptrCast(self), null)));
|
||||
}
|
||||
|
||||
pub fn copyTable(self: *Font, tag: FontTableTag) ?*foundation.Data {
|
||||
return @ptrCast(@constCast(c.CTFontCopyTable(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(tag),
|
||||
c.kCTFontTableOptionNoOptions,
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn getGlyphCount(self: *Font) usize {
|
||||
return @intCast(c.CTFontGetGlyphCount(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getGlyphsForCharacters(self: *Font, chars: []const u16, glyphs: []graphics.Glyph) bool {
|
||||
assert(chars.len == glyphs.len);
|
||||
return c.CTFontGetGlyphsForCharacters(
|
||||
@ptrCast(self),
|
||||
chars.ptr,
|
||||
glyphs.ptr,
|
||||
@intCast(chars.len),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn createPathForGlyph(self: *Font, glyph: graphics.Glyph) ?*graphics.Path {
|
||||
return @ptrCast(@constCast(c.CTFontCreatePathForGlyph(
|
||||
@ptrCast(self),
|
||||
glyph,
|
||||
null,
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn drawGlyphs(
|
||||
self: *Font,
|
||||
glyphs: []const graphics.Glyph,
|
||||
positions: []const graphics.Point,
|
||||
context: anytype, // Must be some context type from graphics
|
||||
) void {
|
||||
assert(positions.len == glyphs.len);
|
||||
c.CTFontDrawGlyphs(
|
||||
@ptrCast(self),
|
||||
glyphs.ptr,
|
||||
@ptrCast(positions.ptr),
|
||||
glyphs.len,
|
||||
@ptrCast(context),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getBoundingRectsForGlyphs(
|
||||
self: *Font,
|
||||
orientation: FontOrientation,
|
||||
glyphs: []const graphics.Glyph,
|
||||
rects: ?[]graphics.Rect,
|
||||
) graphics.Rect {
|
||||
if (rects) |s| assert(glyphs.len == s.len);
|
||||
return @bitCast(c.CTFontGetBoundingRectsForGlyphs(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(orientation),
|
||||
glyphs.ptr,
|
||||
@ptrCast(if (rects) |s| s.ptr else null),
|
||||
@intCast(glyphs.len),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn getAdvancesForGlyphs(
|
||||
self: *Font,
|
||||
orientation: FontOrientation,
|
||||
glyphs: []const graphics.Glyph,
|
||||
advances: ?[]graphics.Size,
|
||||
) f64 {
|
||||
if (advances) |s| assert(glyphs.len == s.len);
|
||||
return c.CTFontGetAdvancesForGlyphs(
|
||||
@ptrCast(self),
|
||||
@intFromEnum(orientation),
|
||||
glyphs.ptr,
|
||||
@ptrCast(if (advances) |s| s.ptr else null),
|
||||
@intCast(glyphs.len),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn copyAttribute(self: *Font, comptime attr: text.FontAttribute) ?attr.Value() {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontCopyAttribute(
|
||||
@ptrCast(self),
|
||||
@ptrCast(attr.key()),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn copyFamilyName(self: *Font) *foundation.String {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontCopyFamilyName(@ptrCast(self))));
|
||||
}
|
||||
|
||||
pub fn copyDisplayName(self: *Font) *foundation.String {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontCopyDisplayName(@ptrCast(self))));
|
||||
}
|
||||
|
||||
pub fn copyPostScriptName(self: *Font) *foundation.String {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontCopyPostScriptName(@ptrCast(self))));
|
||||
}
|
||||
|
||||
pub fn getSymbolicTraits(self: *Font) text.FontSymbolicTraits {
|
||||
return @bitCast(c.CTFontGetSymbolicTraits(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getAscent(self: *Font) f64 {
|
||||
return c.CTFontGetAscent(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getDescent(self: *Font) f64 {
|
||||
return c.CTFontGetDescent(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getLeading(self: *Font) f64 {
|
||||
return c.CTFontGetLeading(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getBoundingBox(self: *Font) graphics.Rect {
|
||||
return @bitCast(c.CTFontGetBoundingBox(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getUnderlinePosition(self: *Font) f64 {
|
||||
return c.CTFontGetUnderlinePosition(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getUnderlineThickness(self: *Font) f64 {
|
||||
return c.CTFontGetUnderlineThickness(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getCapHeight(self: *Font) f64 {
|
||||
return c.CTFontGetCapHeight(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getXHeight(self: *Font) f64 {
|
||||
return c.CTFontGetXHeight(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getUnitsPerEm(self: *Font) u32 {
|
||||
return c.CTFontGetUnitsPerEm(@ptrCast(self));
|
||||
}
|
||||
|
||||
pub fn getSize(self: *Font) f64 {
|
||||
return c.CTFontGetSize(@ptrCast(self));
|
||||
}
|
||||
};
|
||||
|
||||
pub const FontOrientation = enum(c_uint) {
|
||||
default = c.kCTFontOrientationDefault,
|
||||
horizontal = c.kCTFontOrientationHorizontal,
|
||||
vertical = c.kCTFontOrientationVertical,
|
||||
};
|
||||
|
||||
pub const FontTableTag = enum(u32) {
|
||||
svg = c.kCTFontTableSVG,
|
||||
os2 = c.kCTFontTableOS2,
|
||||
head = c.kCTFontTableHead,
|
||||
hhea = c.kCTFontTableHhea,
|
||||
post = c.kCTFontTablePost,
|
||||
_,
|
||||
|
||||
pub fn init(v: *const [4]u8) FontTableTag {
|
||||
const raw: u32 = @bitCast(foundation.FourCharCode.init(v));
|
||||
return @enumFromInt(raw);
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
|
||||
const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
|
||||
defer name.release();
|
||||
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer desc.release();
|
||||
|
||||
const font = try Font.createWithFontDescriptor(desc, 12);
|
||||
defer font.release();
|
||||
|
||||
// Traits
|
||||
{
|
||||
const traits = font.getSymbolicTraits();
|
||||
try testing.expect(!traits.color_glyphs);
|
||||
}
|
||||
|
||||
var glyphs = [1]graphics.Glyph{0};
|
||||
try testing.expect(font.getGlyphsForCharacters(
|
||||
&[_]u16{'A'},
|
||||
&glyphs,
|
||||
));
|
||||
try testing.expect(glyphs[0] > 0);
|
||||
|
||||
// Bounding rect
|
||||
{
|
||||
var rect = font.getBoundingRectsForGlyphs(.horizontal, &glyphs, null);
|
||||
try testing.expect(rect.size.width > 0);
|
||||
|
||||
var singles: [1]graphics.Rect = undefined;
|
||||
rect = font.getBoundingRectsForGlyphs(.horizontal, &glyphs, &singles);
|
||||
try testing.expect(rect.size.width > 0);
|
||||
try testing.expect(singles[0].size.width > 0);
|
||||
}
|
||||
|
||||
// Advances
|
||||
{
|
||||
var advance = font.getAdvancesForGlyphs(.horizontal, &glyphs, null);
|
||||
try testing.expect(advance > 0);
|
||||
|
||||
var singles: [1]graphics.Size = undefined;
|
||||
advance = font.getAdvancesForGlyphs(.horizontal, &glyphs, &singles);
|
||||
try testing.expect(advance > 0);
|
||||
try testing.expect(singles[0].width > 0);
|
||||
}
|
||||
|
||||
// Draw
|
||||
{
|
||||
const cs = try graphics.ColorSpace.createDeviceGray();
|
||||
defer cs.release();
|
||||
const ctx = try graphics.BitmapContext.create(null, 80, 80, 8, 80, cs, 0);
|
||||
const context = graphics.BitmapContext.context;
|
||||
defer context.release(ctx);
|
||||
|
||||
var pos = [_]graphics.Point{.{ .x = 0, .y = 0 }};
|
||||
font.drawGlyphs(
|
||||
&glyphs,
|
||||
&pos,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test "copy" {
|
||||
const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
|
||||
defer name.release();
|
||||
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer desc.release();
|
||||
|
||||
const font = try Font.createWithFontDescriptor(desc, 12);
|
||||
defer font.release();
|
||||
|
||||
const f2 = try font.copyWithAttributes(14, null, null);
|
||||
defer f2.release();
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const FontCollection = opaque {
|
||||
pub fn createFromAvailableFonts() Allocator.Error!*FontCollection {
|
||||
return @as(
|
||||
?*FontCollection,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontCollectionCreateFromAvailableFonts(null))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createWithFontDescriptors(descs: *foundation.Array) Allocator.Error!*FontCollection {
|
||||
return @as(
|
||||
?*FontCollection,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontCollectionCreateWithFontDescriptors(
|
||||
@ptrCast(descs),
|
||||
null,
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *FontCollection) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn createMatchingFontDescriptors(self: *FontCollection) *foundation.Array {
|
||||
const result = c.CTFontCollectionCreateMatchingFontDescriptors(@ptrCast(self));
|
||||
if (result) |ptr| return @ptrFromInt(@intFromPtr(ptr));
|
||||
|
||||
// If we have no results, we create an empty array. This is not
|
||||
// exactly matching the Mac API. We can fix this later if we want
|
||||
// but I chose this to make it slightly more Zig-like at the cost
|
||||
// of some memory in the rare case.
|
||||
return foundation.Array.create(anyopaque, &[_]*const anyopaque{}) catch unreachable;
|
||||
}
|
||||
};
|
||||
|
||||
fn debugDumpList(list: *foundation.Array) !void {
|
||||
var i: usize = 0;
|
||||
while (i < list.getCount()) : (i += 1) {
|
||||
const desc = list.getValueAtIndex(text.FontDescriptor, i);
|
||||
{
|
||||
var buf: [128]u8 = undefined;
|
||||
const name = desc.copyAttribute(.name);
|
||||
defer name.release();
|
||||
const cstr = name.cstring(&buf, .utf8).?;
|
||||
|
||||
var family_buf: [128]u8 = undefined;
|
||||
const family = desc.copyAttribute(.family_name);
|
||||
defer family.release();
|
||||
const family_cstr = family.cstring(&family_buf, .utf8).?;
|
||||
|
||||
var buf2: [128]u8 = undefined;
|
||||
const url = desc.copyAttribute(.url);
|
||||
defer url.release();
|
||||
const path = path: {
|
||||
const blank = try foundation.String.createWithBytes("", .utf8, false);
|
||||
defer blank.release();
|
||||
|
||||
const path = url.copyPath() orelse break :path "<no path>";
|
||||
defer path.release();
|
||||
|
||||
const decoded = try foundation.URL.createStringByReplacingPercentEscapes(
|
||||
path,
|
||||
blank,
|
||||
);
|
||||
defer decoded.release();
|
||||
|
||||
break :path decoded.cstring(&buf2, .utf8) orelse
|
||||
"<path cannot be converted to string>";
|
||||
};
|
||||
|
||||
std.log.warn("i={d} name={s} family={s} path={s}", .{ i, cstr, family_cstr, path });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "collection" {
|
||||
const testing = std.testing;
|
||||
|
||||
const v = try FontCollection.createFromAvailableFonts();
|
||||
defer v.release();
|
||||
|
||||
const list = v.createMatchingFontDescriptors();
|
||||
defer list.release();
|
||||
|
||||
try testing.expect(list.getCount() > 0);
|
||||
}
|
||||
|
||||
test "from descriptors" {
|
||||
const testing = std.testing;
|
||||
|
||||
const name = try foundation.String.createWithBytes("AppleColorEmoji", .utf8, false);
|
||||
defer name.release();
|
||||
|
||||
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer desc.release();
|
||||
|
||||
var desc_arr = [_]*const text.FontDescriptor{desc};
|
||||
const arr = try foundation.Array.create(text.FontDescriptor, &desc_arr);
|
||||
defer arr.release();
|
||||
|
||||
const v = try FontCollection.createWithFontDescriptors(arr);
|
||||
defer v.release();
|
||||
|
||||
const list = v.createMatchingFontDescriptors();
|
||||
defer list.release();
|
||||
|
||||
try testing.expect(list.getCount() > 0);
|
||||
|
||||
// try debugDumpList(list);
|
||||
}
|
||||
|
||||
test "from descriptors no match" {
|
||||
const testing = std.testing;
|
||||
|
||||
const name = try foundation.String.createWithBytes("ThisShouldNeverExist", .utf8, false);
|
||||
defer name.release();
|
||||
|
||||
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer desc.release();
|
||||
|
||||
var desc_arr = [_]*const text.FontDescriptor{desc};
|
||||
const arr = try foundation.Array.create(text.FontDescriptor, &desc_arr);
|
||||
defer arr.release();
|
||||
|
||||
const v = try FontCollection.createWithFontDescriptors(arr);
|
||||
defer v.release();
|
||||
|
||||
const list = v.createMatchingFontDescriptors();
|
||||
defer list.release();
|
||||
|
||||
try testing.expect(list.getCount() == 0);
|
||||
|
||||
//try debugDumpList(list);
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const FontDescriptor = opaque {
|
||||
pub fn createWithNameAndSize(name: *foundation.String, size: f64) Allocator.Error!*FontDescriptor {
|
||||
return @as(
|
||||
?*FontDescriptor,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontDescriptorCreateWithNameAndSize(@ptrCast(name), size))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createWithAttributes(dict: *foundation.Dictionary) Allocator.Error!*FontDescriptor {
|
||||
return @as(
|
||||
?*FontDescriptor,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontDescriptorCreateWithAttributes(@ptrCast(dict)))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createCopyWithAttributes(
|
||||
original: *FontDescriptor,
|
||||
dict: *foundation.Dictionary,
|
||||
) Allocator.Error!*FontDescriptor {
|
||||
return @as(
|
||||
?*FontDescriptor,
|
||||
@ptrFromInt(@intFromPtr(c.CTFontDescriptorCreateCopyWithAttributes(
|
||||
@ptrCast(original),
|
||||
@ptrCast(dict),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createCopyWithVariation(
|
||||
original: *FontDescriptor,
|
||||
id: *foundation.Number,
|
||||
value: f64,
|
||||
) Allocator.Error!*FontDescriptor {
|
||||
return @as(
|
||||
?*FontDescriptor,
|
||||
@ptrCast(@constCast(c.CTFontDescriptorCreateCopyWithVariation(
|
||||
@ptrCast(original),
|
||||
@ptrCast(id),
|
||||
value,
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn retain(self: *FontDescriptor) void {
|
||||
_ = c.CFRetain(self);
|
||||
}
|
||||
|
||||
pub fn release(self: *FontDescriptor) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn copyAttribute(self: *const FontDescriptor, comptime attr: FontAttribute) ?attr.Value() {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontDescriptorCopyAttribute(
|
||||
@ptrCast(self),
|
||||
@ptrCast(attr.key()),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn copyAttributes(self: *const FontDescriptor) *foundation.Dictionary {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontDescriptorCopyAttributes(
|
||||
@ptrCast(self),
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const FontAttribute = enum {
|
||||
url,
|
||||
name,
|
||||
display_name,
|
||||
family_name,
|
||||
style_name,
|
||||
traits,
|
||||
variation,
|
||||
size,
|
||||
matrix,
|
||||
cascade_list,
|
||||
character_set,
|
||||
languages,
|
||||
baseline_adjust,
|
||||
macintosh_encodings,
|
||||
features,
|
||||
feature_settings,
|
||||
fixed_advance,
|
||||
orientation,
|
||||
format,
|
||||
registration_scope,
|
||||
priority,
|
||||
enabled,
|
||||
downloadable,
|
||||
downloaded,
|
||||
|
||||
// https://developer.apple.com/documentation/coretext/core_text_constants?language=objc
|
||||
variation_axes,
|
||||
|
||||
pub fn key(self: FontAttribute) *foundation.String {
|
||||
return @as(*foundation.String, @ptrFromInt(@intFromPtr(switch (self) {
|
||||
.url => c.kCTFontURLAttribute,
|
||||
.name => c.kCTFontNameAttribute,
|
||||
.display_name => c.kCTFontDisplayNameAttribute,
|
||||
.family_name => c.kCTFontFamilyNameAttribute,
|
||||
.style_name => c.kCTFontStyleNameAttribute,
|
||||
.traits => c.kCTFontTraitsAttribute,
|
||||
.variation => c.kCTFontVariationAttribute,
|
||||
.size => c.kCTFontSizeAttribute,
|
||||
.matrix => c.kCTFontMatrixAttribute,
|
||||
.cascade_list => c.kCTFontCascadeListAttribute,
|
||||
.character_set => c.kCTFontCharacterSetAttribute,
|
||||
.languages => c.kCTFontLanguagesAttribute,
|
||||
.baseline_adjust => c.kCTFontBaselineAdjustAttribute,
|
||||
.macintosh_encodings => c.kCTFontMacintoshEncodingsAttribute,
|
||||
.features => c.kCTFontFeaturesAttribute,
|
||||
.feature_settings => c.kCTFontFeatureSettingsAttribute,
|
||||
.fixed_advance => c.kCTFontFixedAdvanceAttribute,
|
||||
.orientation => c.kCTFontOrientationAttribute,
|
||||
.format => c.kCTFontFormatAttribute,
|
||||
.registration_scope => c.kCTFontRegistrationScopeAttribute,
|
||||
.priority => c.kCTFontPriorityAttribute,
|
||||
.enabled => c.kCTFontEnabledAttribute,
|
||||
.downloadable => c.kCTFontDownloadableAttribute,
|
||||
.downloaded => c.kCTFontDownloadedAttribute,
|
||||
.variation_axes => c.kCTFontVariationAxesAttribute,
|
||||
})));
|
||||
}
|
||||
|
||||
pub fn Value(comptime self: FontAttribute) type {
|
||||
return switch (self) {
|
||||
.url => *foundation.URL,
|
||||
.name => *foundation.String,
|
||||
.display_name => *foundation.String,
|
||||
.family_name => *foundation.String,
|
||||
.style_name => *foundation.String,
|
||||
.traits => *foundation.Dictionary,
|
||||
.variation => *foundation.Dictionary,
|
||||
.size => *foundation.Number,
|
||||
.matrix => *anyopaque, // CFDataRef
|
||||
.cascade_list => *foundation.Array,
|
||||
.character_set => *anyopaque, // CFCharacterSetRef
|
||||
.languages => *foundation.Array,
|
||||
.baseline_adjust => *foundation.Number,
|
||||
.macintosh_encodings => *foundation.Number,
|
||||
.features => *foundation.Array,
|
||||
.feature_settings => *foundation.Array,
|
||||
.fixed_advance => *foundation.Number,
|
||||
.orientation => *foundation.Number,
|
||||
.format => *foundation.Number,
|
||||
.registration_scope => *foundation.Number,
|
||||
.priority => *foundation.Number,
|
||||
.enabled => *foundation.Number,
|
||||
.downloadable => *anyopaque, // CFBoolean
|
||||
.downloaded => *anyopaque, // CFBoolean
|
||||
.variation_axes => *foundation.Array,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const FontTraitKey = enum {
|
||||
symbolic,
|
||||
weight,
|
||||
width,
|
||||
slant,
|
||||
|
||||
pub fn key(self: FontTraitKey) *foundation.String {
|
||||
return @as(*foundation.String, @ptrFromInt(@intFromPtr(switch (self) {
|
||||
.symbolic => c.kCTFontSymbolicTrait,
|
||||
.weight => c.kCTFontWeightTrait,
|
||||
.width => c.kCTFontWidthTrait,
|
||||
.slant => c.kCTFontSlantTrait,
|
||||
})));
|
||||
}
|
||||
|
||||
pub fn Value(self: FontTraitKey) type {
|
||||
return switch (self) {
|
||||
.symbolic => *foundation.Number,
|
||||
.weight => *foundation.Number,
|
||||
.width => *foundation.Number,
|
||||
.slant => *foundation.Number,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// https://developer.apple.com/documentation/coretext/ctfont/font_variation_axis_dictionary_keys?language=objc
|
||||
pub const FontVariationAxisKey = enum {
|
||||
identifier,
|
||||
minimum_value,
|
||||
maximum_value,
|
||||
default_value,
|
||||
name,
|
||||
hidden,
|
||||
|
||||
pub fn key(self: FontVariationAxisKey) *foundation.String {
|
||||
return @as(*foundation.String, @ptrFromInt(@intFromPtr(switch (self) {
|
||||
.identifier => c.kCTFontVariationAxisIdentifierKey,
|
||||
.minimum_value => c.kCTFontVariationAxisMinimumValueKey,
|
||||
.maximum_value => c.kCTFontVariationAxisMaximumValueKey,
|
||||
.default_value => c.kCTFontVariationAxisDefaultValueKey,
|
||||
.name => c.kCTFontVariationAxisNameKey,
|
||||
.hidden => c.kCTFontVariationAxisHiddenKey,
|
||||
})));
|
||||
}
|
||||
|
||||
pub fn Value(comptime self: FontVariationAxisKey) type {
|
||||
return switch (self) {
|
||||
.identifier => foundation.Number,
|
||||
.minimum_value => foundation.Number,
|
||||
.maximum_value => foundation.Number,
|
||||
.default_value => foundation.Number,
|
||||
.name => foundation.String,
|
||||
.hidden => foundation.Number,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const FontSymbolicTraits = packed struct(u32) {
|
||||
italic: bool = false,
|
||||
bold: bool = false,
|
||||
_unused1: u3 = 0,
|
||||
expanded: bool = false,
|
||||
condensed: bool = false,
|
||||
_unused2: u3 = 0,
|
||||
monospace: bool = false,
|
||||
vertical: bool = false,
|
||||
ui_optimized: bool = false,
|
||||
color_glyphs: bool = false,
|
||||
composite: bool = false,
|
||||
_padding: u17 = 0,
|
||||
|
||||
pub fn init(num: *foundation.Number) FontSymbolicTraits {
|
||||
var raw: i32 = undefined;
|
||||
_ = num.getValue(.sint32, &raw);
|
||||
return @as(FontSymbolicTraits, @bitCast(raw));
|
||||
}
|
||||
|
||||
test {
|
||||
try std.testing.expectEqual(
|
||||
@bitSizeOf(c.CTFontSymbolicTraits),
|
||||
@bitSizeOf(FontSymbolicTraits),
|
||||
);
|
||||
}
|
||||
|
||||
test "bitcast" {
|
||||
const actual: c.CTFontSymbolicTraits = c.kCTFontTraitMonoSpace | c.kCTFontTraitExpanded;
|
||||
const expected: FontSymbolicTraits = .{
|
||||
.monospace = true,
|
||||
.expanded = true,
|
||||
};
|
||||
|
||||
try std.testing.expectEqual(actual, @as(c.CTFontSymbolicTraits, @bitCast(expected)));
|
||||
}
|
||||
|
||||
test "number" {
|
||||
const raw: i32 = c.kCTFontTraitMonoSpace | c.kCTFontTraitExpanded;
|
||||
const num = try foundation.Number.create(.sint32, &raw);
|
||||
defer num.release();
|
||||
|
||||
const expected: FontSymbolicTraits = .{ .monospace = true, .expanded = true };
|
||||
const actual = FontSymbolicTraits.init(num);
|
||||
try std.testing.expect(std.meta.eql(expected, actual));
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
test "descriptor" {
|
||||
const testing = std.testing;
|
||||
|
||||
const name = try foundation.String.createWithBytes("foo", .utf8, false);
|
||||
defer name.release();
|
||||
|
||||
const v = try FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer v.release();
|
||||
|
||||
const copy_name = v.copyAttribute(.name).?;
|
||||
defer copy_name.release();
|
||||
|
||||
{
|
||||
var buf: [128]u8 = undefined;
|
||||
const cstr = copy_name.cstring(&buf, .utf8).?;
|
||||
try testing.expectEqualStrings("foo", cstr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const FontDescriptor = @import("./font_descriptor.zig").FontDescriptor;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub fn createFontDescriptorsFromURL(url: *foundation.URL) ?*foundation.Array {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontManagerCreateFontDescriptorsFromURL(
|
||||
@ptrCast(url),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn createFontDescriptorsFromData(data: *foundation.Data) ?*foundation.Array {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontManagerCreateFontDescriptorsFromData(
|
||||
@ptrCast(data),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn createFontDescriptorFromData(data: *foundation.Data) ?*FontDescriptor {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFontManagerCreateFontDescriptorFromData(
|
||||
@ptrCast(data),
|
||||
)));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Frame = opaque {
|
||||
pub fn release(self: *Frame) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getLineOrigins(
|
||||
self: *Frame,
|
||||
range: foundation.Range,
|
||||
points: []graphics.Point,
|
||||
) void {
|
||||
c.CTFrameGetLineOrigins(
|
||||
@ptrCast(self),
|
||||
@bitCast(range),
|
||||
@ptrCast(points.ptr),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getLines(self: *Frame) *foundation.Array {
|
||||
return @ptrFromInt(@intFromPtr(c.CTFrameGetLines(@ptrCast(self))));
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
// See framesetter tests...
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Framesetter = opaque {
|
||||
pub fn createWithAttributedString(str: *foundation.AttributedString) Allocator.Error!*Framesetter {
|
||||
return @as(
|
||||
?*Framesetter,
|
||||
@ptrFromInt(@intFromPtr(c.CTFramesetterCreateWithAttributedString(
|
||||
@ptrCast(str),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Framesetter) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn createFrame(
|
||||
self: *Framesetter,
|
||||
range: foundation.Range,
|
||||
path: *graphics.Path,
|
||||
attrs: ?*foundation.Dictionary,
|
||||
) !*text.Frame {
|
||||
return @as(
|
||||
?*text.Frame,
|
||||
@ptrFromInt(@intFromPtr(c.CTFramesetterCreateFrame(
|
||||
@ptrCast(self),
|
||||
@bitCast(range),
|
||||
@ptrCast(path),
|
||||
@ptrCast(attrs),
|
||||
))),
|
||||
) orelse error.FrameCreateFailed;
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
const str = try foundation.MutableAttributedString.create(0);
|
||||
defer str.release();
|
||||
{
|
||||
const rep = try foundation.String.createWithBytes("hello", .utf8, false);
|
||||
defer rep.release();
|
||||
str.replaceString(foundation.Range.init(0, 0), rep);
|
||||
}
|
||||
|
||||
const fs = try Framesetter.createWithAttributedString(@ptrCast(str));
|
||||
defer fs.release();
|
||||
|
||||
const path = try graphics.Path.createWithRect(graphics.Rect.init(0, 0, 100, 200), null);
|
||||
defer path.release();
|
||||
const frame = try fs.createFrame(
|
||||
foundation.Range.init(0, 0),
|
||||
path,
|
||||
null,
|
||||
);
|
||||
defer frame.release();
|
||||
|
||||
{
|
||||
var points: [1]graphics.Point = undefined;
|
||||
frame.getLineOrigins(foundation.Range.init(0, 1), &points);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Line = opaque {
|
||||
pub fn createWithAttributedString(str: *foundation.AttributedString) Allocator.Error!*Line {
|
||||
return @as(
|
||||
?*Line,
|
||||
@ptrFromInt(@intFromPtr(c.CTLineCreateWithAttributedString(
|
||||
@ptrCast(str),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Line) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getGlyphCount(self: *Line) usize {
|
||||
return @intCast(c.CTLineGetGlyphCount(
|
||||
@ptrCast(self),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn getBoundsWithOptions(
|
||||
self: *Line,
|
||||
opts: LineBoundsOptions,
|
||||
) graphics.Rect {
|
||||
return @bitCast(c.CTLineGetBoundsWithOptions(
|
||||
@ptrCast(self),
|
||||
@bitCast(opts),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn getTypographicBounds(
|
||||
self: *Line,
|
||||
ascent: ?*f64,
|
||||
descent: ?*f64,
|
||||
leading: ?*f64,
|
||||
) f64 {
|
||||
return c.CTLineGetTypographicBounds(
|
||||
@ptrCast(self),
|
||||
ascent,
|
||||
descent,
|
||||
leading,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getGlyphRuns(self: *Line) *foundation.Array {
|
||||
return @ptrCast(@constCast(c.CTLineGetGlyphRuns(@ptrCast(self))));
|
||||
}
|
||||
};
|
||||
|
||||
pub const LineBoundsOptions = packed struct {
|
||||
exclude_leading: bool = false,
|
||||
exclude_shifts: bool = false,
|
||||
hanging_punctuation: bool = false,
|
||||
glyph_path_bounds: bool = false,
|
||||
use_optical_bounds: bool = false,
|
||||
language_extents: bool = false,
|
||||
_padding: u58 = 0,
|
||||
|
||||
test {
|
||||
try std.testing.expectEqual(
|
||||
@bitSizeOf(c.CTLineBoundsOptions),
|
||||
@bitSizeOf(LineBoundsOptions),
|
||||
);
|
||||
}
|
||||
|
||||
test "bitcast" {
|
||||
const actual: c.CTLineBoundsOptions = c.kCTLineBoundsExcludeTypographicShifts |
|
||||
c.kCTLineBoundsUseOpticalBounds;
|
||||
const expected: LineBoundsOptions = .{
|
||||
.exclude_shifts = true,
|
||||
.use_optical_bounds = true,
|
||||
};
|
||||
|
||||
try std.testing.expectEqual(actual, @as(c.CTLineBoundsOptions, @bitCast(expected)));
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
test "line" {
|
||||
const testing = std.testing;
|
||||
|
||||
const font = font: {
|
||||
const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
|
||||
defer name.release();
|
||||
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer desc.release();
|
||||
|
||||
break :font try text.Font.createWithFontDescriptor(desc, 12);
|
||||
};
|
||||
defer font.release();
|
||||
|
||||
const rep = try foundation.String.createWithBytes("hello", .utf8, false);
|
||||
defer rep.release();
|
||||
const str = try foundation.MutableAttributedString.create(rep.getLength());
|
||||
defer str.release();
|
||||
str.replaceString(foundation.Range.init(0, 0), rep);
|
||||
str.setAttribute(
|
||||
foundation.Range.init(0, rep.getLength()),
|
||||
text.StringAttribute.font,
|
||||
font,
|
||||
);
|
||||
|
||||
const line = try Line.createWithAttributedString(@as(*foundation.AttributedString, @ptrCast(str)));
|
||||
defer line.release();
|
||||
|
||||
try testing.expectEqual(@as(usize, 5), line.getGlyphCount());
|
||||
|
||||
// TODO: this is a garbage value but should work...
|
||||
const bounds = line.getBoundsWithOptions(.{});
|
||||
_ = bounds;
|
||||
//std.log.warn("bounds={}", .{bounds});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
// https://developer.apple.com/documentation/coretext/ctparagraphstyle?language=objc
|
||||
pub const ParagraphStyle = opaque {
|
||||
pub fn create(
|
||||
settings: []const ParagraphStyleSetting,
|
||||
) Allocator.Error!*ParagraphStyle {
|
||||
return @ptrCast(@constCast(c.CTParagraphStyleCreate(
|
||||
@ptrCast(settings.ptr),
|
||||
settings.len,
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn release(self: *ParagraphStyle) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/coretext/ctparagraphstylesetting?language=objc
|
||||
pub const ParagraphStyleSetting = extern struct {
|
||||
spec: ParagraphStyleSpecifier,
|
||||
value_size: usize,
|
||||
value: *const anyopaque,
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/coretext/ctparagraphstylespecifier?language=objc
|
||||
pub const ParagraphStyleSpecifier = enum(c_uint) {
|
||||
base_writing_direction = 13,
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/uikit/nswritingdirectionattributename?language=objc
|
||||
pub const WritingDirection = enum(c_int) {
|
||||
natural = -1,
|
||||
ltr = 0,
|
||||
rtl = 1,
|
||||
lro = 2,
|
||||
rlo = 3,
|
||||
};
|
||||
|
||||
test ParagraphStyle {
|
||||
const p = try ParagraphStyle.create(&.{});
|
||||
defer p.release();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Run = opaque {
|
||||
pub fn release(self: *Run) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn getGlyphCount(self: *Run) usize {
|
||||
return @intCast(c.CTRunGetGlyphCount(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn getGlyphsPtr(self: *Run) ?[]const graphics.Glyph {
|
||||
const len = self.getGlyphCount();
|
||||
if (len == 0) return &.{};
|
||||
const ptr: [*c]const graphics.Glyph = @ptrCast(
|
||||
c.CTRunGetGlyphsPtr(@ptrCast(self)),
|
||||
);
|
||||
if (ptr == null) return null;
|
||||
return ptr[0..len];
|
||||
}
|
||||
|
||||
pub fn getGlyphs(self: *Run, alloc: Allocator) ![]const graphics.Glyph {
|
||||
const len = self.getGlyphCount();
|
||||
const ptr = try alloc.alloc(graphics.Glyph, len);
|
||||
errdefer alloc.free(ptr);
|
||||
c.CTRunGetGlyphs(
|
||||
@ptrCast(self),
|
||||
.{ .location = 0, .length = 0 },
|
||||
@ptrCast(ptr.ptr),
|
||||
);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pub fn getPositionsPtr(self: *Run) ?[]const graphics.Point {
|
||||
const len = self.getGlyphCount();
|
||||
if (len == 0) return &.{};
|
||||
const ptr: [*c]const graphics.Point = @ptrCast(
|
||||
c.CTRunGetPositionsPtr(@ptrCast(self)),
|
||||
);
|
||||
if (ptr == null) return null;
|
||||
return ptr[0..len];
|
||||
}
|
||||
|
||||
pub fn getPositions(self: *Run, alloc: Allocator) ![]const graphics.Point {
|
||||
const len = self.getGlyphCount();
|
||||
const ptr = try alloc.alloc(graphics.Point, len);
|
||||
errdefer alloc.free(ptr);
|
||||
c.CTRunGetPositions(
|
||||
@ptrCast(self),
|
||||
.{ .location = 0, .length = 0 },
|
||||
@ptrCast(ptr.ptr),
|
||||
);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pub fn getAdvancesPtr(self: *Run) ?[]const graphics.Size {
|
||||
const len = self.getGlyphCount();
|
||||
if (len == 0) return &.{};
|
||||
const ptr: [*c]const graphics.Size = @ptrCast(
|
||||
c.CTRunGetAdvancesPtr(@ptrCast(self)),
|
||||
);
|
||||
if (ptr == null) return null;
|
||||
return ptr[0..len];
|
||||
}
|
||||
|
||||
pub fn getAdvances(self: *Run, alloc: Allocator) ![]const graphics.Size {
|
||||
const len = self.getGlyphCount();
|
||||
const ptr = try alloc.alloc(graphics.Size, len);
|
||||
errdefer alloc.free(ptr);
|
||||
c.CTRunGetAdvances(
|
||||
@ptrCast(self),
|
||||
.{ .location = 0, .length = 0 },
|
||||
@ptrCast(ptr.ptr),
|
||||
);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pub fn getStringIndicesPtr(self: *Run) ?[]const usize {
|
||||
const len = self.getGlyphCount();
|
||||
if (len == 0) return &.{};
|
||||
const ptr: [*c]const usize = @ptrCast(
|
||||
c.CTRunGetStringIndicesPtr(@ptrCast(self)),
|
||||
);
|
||||
if (ptr == null) return null;
|
||||
return ptr[0..len];
|
||||
}
|
||||
|
||||
pub fn getStringIndices(self: *Run, alloc: Allocator) ![]const usize {
|
||||
const len = self.getGlyphCount();
|
||||
const ptr = try alloc.alloc(usize, len);
|
||||
errdefer alloc.free(ptr);
|
||||
c.CTRunGetStringIndices(
|
||||
@ptrCast(self),
|
||||
.{ .location = 0, .length = 0 },
|
||||
@ptrCast(ptr.ptr),
|
||||
);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pub fn getStatus(self: *Run) Status {
|
||||
return @bitCast(c.CTRunGetStatus(@ptrCast(self)));
|
||||
}
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/coretext/ctrunstatus?language=objc
|
||||
pub const Status = packed struct(u32) {
|
||||
right_to_left: bool,
|
||||
non_monotonic: bool,
|
||||
has_non_identity_matrix: bool,
|
||||
_pad: u29 = 0,
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
const foundation = @import("../foundation.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const StringAttribute = enum {
|
||||
font,
|
||||
paragraph_style,
|
||||
writing_direction,
|
||||
|
||||
pub fn key(self: StringAttribute) *foundation.String {
|
||||
return @ptrFromInt(@intFromPtr(switch (self) {
|
||||
.font => c.kCTFontAttributeName,
|
||||
.paragraph_style => c.kCTParagraphStyleAttributeName,
|
||||
.writing_direction => c.kCTWritingDirectionAttributeName,
|
||||
}));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const foundation = @import("../foundation.zig");
|
||||
const graphics = @import("../graphics.zig");
|
||||
const text = @import("../text.zig");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Typesetter = opaque {
|
||||
pub fn createWithAttributedStringAndOptions(
|
||||
str: *foundation.AttributedString,
|
||||
opts: *foundation.Dictionary,
|
||||
) Allocator.Error!*Typesetter {
|
||||
return @as(
|
||||
?*Typesetter,
|
||||
@ptrFromInt(@intFromPtr(c.CTTypesetterCreateWithAttributedStringAndOptions(
|
||||
@ptrCast(str),
|
||||
@ptrCast(opts),
|
||||
))),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *Typesetter) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
||||
pub fn createLine(
|
||||
self: *Typesetter,
|
||||
range: foundation.c.CFRange,
|
||||
) *text.Line {
|
||||
return @ptrFromInt(@intFromPtr(c.CTTypesetterCreateLine(
|
||||
@ptrCast(self),
|
||||
range,
|
||||
)));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
const display_link = @import("video/display_link.zig");
|
||||
const pixel_format = @import("video/pixel_format.zig");
|
||||
|
||||
pub const c = @import("video/c.zig").c;
|
||||
pub const DisplayLink = display_link.DisplayLink;
|
||||
pub const PixelFormat = pixel_format.PixelFormat;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub const c = @import("../main.zig").c;
|
||||
@@ -0,0 +1,86 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn setCurrentCGDisplay(
|
||||
self: *DisplayLink,
|
||||
display_id: c.CGDirectDisplayID,
|
||||
) Error!void {
|
||||
if (c.CVDisplayLinkSetCurrentCGDisplay(
|
||||
@ptrCast(self),
|
||||
display_id,
|
||||
) != c.kCVReturnSuccess)
|
||||
return error.InvalidOperation;
|
||||
}
|
||||
|
||||
// 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 Userdata: type,
|
||||
comptime callbackFn: *const fn (*DisplayLink, ?*Userdata) void,
|
||||
userinfo: ?*Userdata,
|
||||
) 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,
|
||||
@ptrCast(@alignCast(inner_userinfo)),
|
||||
);
|
||||
return c.kCVReturnSuccess;
|
||||
}
|
||||
}).callback),
|
||||
userinfo,
|
||||
) != c.kCVReturnSuccess)
|
||||
return error.InvalidOperation;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,171 @@
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const PixelFormat = enum(c_int) {
|
||||
/// 1 bit indexed
|
||||
@"1Monochrome" = c.kCVPixelFormatType_1Monochrome,
|
||||
/// 2 bit indexed
|
||||
@"2Indexed" = c.kCVPixelFormatType_2Indexed,
|
||||
/// 4 bit indexed
|
||||
@"4Indexed" = c.kCVPixelFormatType_4Indexed,
|
||||
/// 8 bit indexed
|
||||
@"8Indexed" = c.kCVPixelFormatType_8Indexed,
|
||||
/// 1 bit indexed gray, white is zero
|
||||
@"1IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_1IndexedGray_WhiteIsZero,
|
||||
/// 2 bit indexed gray, white is zero
|
||||
@"2IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_2IndexedGray_WhiteIsZero,
|
||||
/// 4 bit indexed gray, white is zero
|
||||
@"4IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_4IndexedGray_WhiteIsZero,
|
||||
/// 8 bit indexed gray, white is zero
|
||||
@"8IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_8IndexedGray_WhiteIsZero,
|
||||
/// 16 bit BE RGB 555
|
||||
@"16BE555" = c.kCVPixelFormatType_16BE555,
|
||||
/// 16 bit LE RGB 555
|
||||
@"16LE555" = c.kCVPixelFormatType_16LE555,
|
||||
/// 16 bit LE RGB 5551
|
||||
@"16LE5551" = c.kCVPixelFormatType_16LE5551,
|
||||
/// 16 bit BE RGB 565
|
||||
@"16BE565" = c.kCVPixelFormatType_16BE565,
|
||||
/// 16 bit LE RGB 565
|
||||
@"16LE565" = c.kCVPixelFormatType_16LE565,
|
||||
/// 24 bit RGB
|
||||
@"24RGB" = c.kCVPixelFormatType_24RGB,
|
||||
/// 24 bit BGR
|
||||
@"24BGR" = c.kCVPixelFormatType_24BGR,
|
||||
/// 32 bit ARGB
|
||||
@"32ARGB" = c.kCVPixelFormatType_32ARGB,
|
||||
/// 32 bit BGRA
|
||||
@"32BGRA" = c.kCVPixelFormatType_32BGRA,
|
||||
/// 32 bit ABGR
|
||||
@"32ABGR" = c.kCVPixelFormatType_32ABGR,
|
||||
/// 32 bit RGBA
|
||||
@"32RGBA" = c.kCVPixelFormatType_32RGBA,
|
||||
/// 64 bit ARGB, 16-bit big-endian samples
|
||||
@"64ARGB" = c.kCVPixelFormatType_64ARGB,
|
||||
/// 64 bit RGBA, 16-bit little-endian full-range (0-65535) samples
|
||||
@"64RGBALE" = c.kCVPixelFormatType_64RGBALE,
|
||||
/// 48 bit RGB, 16-bit big-endian samples
|
||||
@"48RGB" = c.kCVPixelFormatType_48RGB,
|
||||
/// 32 bit AlphaGray, 16-bit big-endian samples, black is zero
|
||||
@"32AlphaGray" = c.kCVPixelFormatType_32AlphaGray,
|
||||
/// 16 bit Grayscale, 16-bit big-endian samples, black is zero
|
||||
@"16Gray" = c.kCVPixelFormatType_16Gray,
|
||||
/// 30 bit RGB, 10-bit big-endian samples, 2 unused padding bits (at least significant end).
|
||||
@"30RGB" = c.kCVPixelFormatType_30RGB,
|
||||
/// 30 bit RGB, 10-bit big-endian samples, 2 unused padding bits (at most significant end), video-range (64-940).
|
||||
@"30RGB_r210" = c.kCVPixelFormatType_30RGB_r210,
|
||||
/// Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1
|
||||
@"422YpCbCr8" = c.kCVPixelFormatType_422YpCbCr8,
|
||||
/// Component Y'CbCrA 8-bit 4:4:4:4, ordered Cb Y' Cr A
|
||||
@"4444YpCbCrA8" = c.kCVPixelFormatType_4444YpCbCrA8,
|
||||
/// Component Y'CbCrA 8-bit 4:4:4:4, rendering format. full range alpha, zero biased YUV, ordered A Y' Cb Cr
|
||||
@"4444YpCbCrA8R" = c.kCVPixelFormatType_4444YpCbCrA8R,
|
||||
/// Component Y'CbCrA 8-bit 4:4:4:4, ordered A Y' Cb Cr, full range alpha, video range Y'CbCr.
|
||||
@"4444AYpCbCr8" = c.kCVPixelFormatType_4444AYpCbCr8,
|
||||
/// Component Y'CbCrA 16-bit 4:4:4:4, ordered A Y' Cb Cr, full range alpha, video range Y'CbCr, 16-bit little-endian samples.
|
||||
@"4444AYpCbCr16" = c.kCVPixelFormatType_4444AYpCbCr16,
|
||||
/// Component AY'CbCr single precision floating-point 4:4:4:4
|
||||
@"4444AYpCbCrFloat" = c.kCVPixelFormatType_4444AYpCbCrFloat,
|
||||
/// Component Y'CbCr 8-bit 4:4:4, ordered Cr Y' Cb, video range Y'CbCr
|
||||
@"444YpCbCr8" = c.kCVPixelFormatType_444YpCbCr8,
|
||||
/// Component Y'CbCr 10,12,14,16-bit 4:2:2
|
||||
@"422YpCbCr16" = c.kCVPixelFormatType_422YpCbCr16,
|
||||
/// Component Y'CbCr 10-bit 4:2:2
|
||||
@"422YpCbCr10" = c.kCVPixelFormatType_422YpCbCr10,
|
||||
/// Component Y'CbCr 10-bit 4:4:4
|
||||
@"444YpCbCr10" = c.kCVPixelFormatType_444YpCbCr10,
|
||||
/// Planar Component Y'CbCr 8-bit 4:2:0. baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct
|
||||
@"420YpCbCr8Planar" = c.kCVPixelFormatType_420YpCbCr8Planar,
|
||||
/// Planar Component Y'CbCr 8-bit 4:2:0, full range. baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct
|
||||
@"420YpCbCr8PlanarFullRange" = c.kCVPixelFormatType_420YpCbCr8PlanarFullRange,
|
||||
/// First plane: Video-range Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1; second plane: alpha 8-bit 0-255
|
||||
@"422YpCbCr_4A_8BiPlanar" = c.kCVPixelFormatType_422YpCbCr_4A_8BiPlanar,
|
||||
/// Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
|
||||
@"420YpCbCr8BiPlanarVideoRange" = c.kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
|
||||
/// Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
|
||||
@"420YpCbCr8BiPlanarFullRange" = c.kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
/// Bi-Planar Component Y'CbCr 8-bit 4:2:2, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
|
||||
@"422YpCbCr8BiPlanarVideoRange" = c.kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange,
|
||||
/// Bi-Planar Component Y'CbCr 8-bit 4:2:2, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
|
||||
@"422YpCbCr8BiPlanarFullRange" = c.kCVPixelFormatType_422YpCbCr8BiPlanarFullRange,
|
||||
/// Bi-Planar Component Y'CbCr 8-bit 4:4:4, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
|
||||
@"444YpCbCr8BiPlanarVideoRange" = c.kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange,
|
||||
/// Bi-Planar Component Y'CbCr 8-bit 4:4:4, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
|
||||
@"444YpCbCr8BiPlanarFullRange" = c.kCVPixelFormatType_444YpCbCr8BiPlanarFullRange,
|
||||
/// Component Y'CbCr 8-bit 4:2:2, ordered Y'0 Cb Y'1 Cr
|
||||
@"422YpCbCr8_yuvs" = c.kCVPixelFormatType_422YpCbCr8_yuvs,
|
||||
/// Component Y'CbCr 8-bit 4:2:2, full range, ordered Y'0 Cb Y'1 Cr
|
||||
@"422YpCbCr8FullRange" = c.kCVPixelFormatType_422YpCbCr8FullRange,
|
||||
/// 8 bit one component, black is zero
|
||||
OneComponent8 = c.kCVPixelFormatType_OneComponent8,
|
||||
/// 8 bit two component, black is zero
|
||||
TwoComponent8 = c.kCVPixelFormatType_TwoComponent8,
|
||||
/// little-endian RGB101010, 2 MSB are ignored, wide-gamut (384-895)
|
||||
@"30RGBLEPackedWideGamut" = c.kCVPixelFormatType_30RGBLEPackedWideGamut,
|
||||
/// little-endian ARGB2101010 full-range ARGB
|
||||
ARGB2101010LEPacked = c.kCVPixelFormatType_ARGB2101010LEPacked,
|
||||
/// little-endian ARGB10101010, each 10 bits in the MSBs of 16bits, wide-gamut (384-895, including alpha)
|
||||
@"40ARGBLEWideGamut" = c.kCVPixelFormatType_40ARGBLEWideGamut,
|
||||
/// little-endian ARGB10101010, each 10 bits in the MSBs of 16bits, wide-gamut (384-895, including alpha). Alpha premultiplied
|
||||
@"40ARGBLEWideGamutPremultiplied" = c.kCVPixelFormatType_40ARGBLEWideGamutPremultiplied,
|
||||
/// 10 bit little-endian one component, stored as 10 MSBs of 16 bits, black is zero
|
||||
OneComponent10 = c.kCVPixelFormatType_OneComponent10,
|
||||
/// 12 bit little-endian one component, stored as 12 MSBs of 16 bits, black is zero
|
||||
OneComponent12 = c.kCVPixelFormatType_OneComponent12,
|
||||
/// 16 bit little-endian one component, black is zero
|
||||
OneComponent16 = c.kCVPixelFormatType_OneComponent16,
|
||||
/// 16 bit little-endian two component, black is zero
|
||||
TwoComponent16 = c.kCVPixelFormatType_TwoComponent16,
|
||||
/// 16 bit one component IEEE half-precision float, 16-bit little-endian samples
|
||||
OneComponent16Half = c.kCVPixelFormatType_OneComponent16Half,
|
||||
/// 32 bit one component IEEE float, 32-bit little-endian samples
|
||||
OneComponent32Float = c.kCVPixelFormatType_OneComponent32Float,
|
||||
/// 16 bit two component IEEE half-precision float, 16-bit little-endian samples
|
||||
TwoComponent16Half = c.kCVPixelFormatType_TwoComponent16Half,
|
||||
/// 32 bit two component IEEE float, 32-bit little-endian samples
|
||||
TwoComponent32Float = c.kCVPixelFormatType_TwoComponent32Float,
|
||||
/// 64 bit RGBA IEEE half-precision float, 16-bit little-endian samples
|
||||
@"64RGBAHalf" = c.kCVPixelFormatType_64RGBAHalf,
|
||||
/// 128 bit RGBA IEEE float, 32-bit little-endian samples
|
||||
@"128RGBAFloat" = c.kCVPixelFormatType_128RGBAFloat,
|
||||
/// Bayer 14-bit Little-Endian, packed in 16-bits, ordered G R G R... alternating with B G B G...
|
||||
@"14Bayer_GRBG" = c.kCVPixelFormatType_14Bayer_GRBG,
|
||||
/// Bayer 14-bit Little-Endian, packed in 16-bits, ordered R G R G... alternating with G B G B...
|
||||
@"14Bayer_RGGB" = c.kCVPixelFormatType_14Bayer_RGGB,
|
||||
/// Bayer 14-bit Little-Endian, packed in 16-bits, ordered B G B G... alternating with G R G R...
|
||||
@"14Bayer_BGGR" = c.kCVPixelFormatType_14Bayer_BGGR,
|
||||
/// Bayer 14-bit Little-Endian, packed in 16-bits, ordered G B G B... alternating with R G R G...
|
||||
@"14Bayer_GBRG" = c.kCVPixelFormatType_14Bayer_GBRG,
|
||||
/// IEEE754-2008 binary16 (half float), describing the normalized shift when comparing two images. Units are 1/meters: ( pixelShift / (pixelFocalLength * baselineInMeters) )
|
||||
DisparityFloat16 = c.kCVPixelFormatType_DisparityFloat16,
|
||||
/// IEEE754-2008 binary32 float, describing the normalized shift when comparing two images. Units are 1/meters: ( pixelShift / (pixelFocalLength * baselineInMeters) )
|
||||
DisparityFloat32 = c.kCVPixelFormatType_DisparityFloat32,
|
||||
/// IEEE754-2008 binary16 (half float), describing the depth (distance to an object) in meters
|
||||
DepthFloat16 = c.kCVPixelFormatType_DepthFloat16,
|
||||
/// IEEE754-2008 binary32 float, describing the depth (distance to an object) in meters
|
||||
DepthFloat32 = c.kCVPixelFormatType_DepthFloat32,
|
||||
/// 2 plane YCbCr10 4:2:0, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960])
|
||||
@"420YpCbCr10BiPlanarVideoRange" = c.kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange,
|
||||
/// 2 plane YCbCr10 4:2:2, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960])
|
||||
@"422YpCbCr10BiPlanarVideoRange" = c.kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange,
|
||||
/// 2 plane YCbCr10 4:4:4, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960])
|
||||
@"444YpCbCr10BiPlanarVideoRange" = c.kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange,
|
||||
/// 2 plane YCbCr10 4:2:0, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023)
|
||||
@"420YpCbCr10BiPlanarFullRange" = c.kCVPixelFormatType_420YpCbCr10BiPlanarFullRange,
|
||||
/// 2 plane YCbCr10 4:2:2, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023)
|
||||
@"422YpCbCr10BiPlanarFullRange" = c.kCVPixelFormatType_422YpCbCr10BiPlanarFullRange,
|
||||
/// 2 plane YCbCr10 4:4:4, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023)
|
||||
@"444YpCbCr10BiPlanarFullRange" = c.kCVPixelFormatType_444YpCbCr10BiPlanarFullRange,
|
||||
/// first and second planes as per 420YpCbCr8BiPlanarVideoRange (420v), alpha 8 bits in third plane full-range. No CVPlanarPixelBufferInfo struct.
|
||||
@"420YpCbCr8VideoRange_8A_TriPlanar" = c.kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar,
|
||||
/// Single plane Bayer 16-bit little-endian sensor element ("sensel".*) samples from full-size decoding of ProRes RAW images; Bayer pattern (sensel ordering) and other raw conversion information is described via buffer attachments
|
||||
@"16VersatileBayer" = c.kCVPixelFormatType_16VersatileBayer,
|
||||
/// Single plane 64-bit RGBA (16-bit little-endian samples) from downscaled decoding of ProRes RAW images; components--which may not be co-sited with one another--are sensel values and require raw conversion, information for which is described via buffer attachments
|
||||
@"64RGBA_DownscaledProResRAW" = c.kCVPixelFormatType_64RGBA_DownscaledProResRAW,
|
||||
/// 2 plane YCbCr16 4:2:2, video-range (luma=[4096,60160] chroma=[4096,61440])
|
||||
@"422YpCbCr16BiPlanarVideoRange" = c.kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange,
|
||||
/// 2 plane YCbCr16 4:4:4, video-range (luma=[4096,60160] chroma=[4096,61440])
|
||||
@"444YpCbCr16BiPlanarVideoRange" = c.kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange,
|
||||
/// 3 plane video-range YCbCr16 4:4:4 with 16-bit full-range alpha (luma=[4096,60160] chroma=[4096,61440] alpha=[0,65535]). No CVPlanarPixelBufferInfo struct.
|
||||
@"444YpCbCr16VideoRange_16A_TriPlanar" = c.kCVPixelFormatType_444YpCbCr16VideoRange_16A_TriPlanar,
|
||||
_,
|
||||
};
|
||||
Reference in New Issue
Block a user