aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/pkg/macos/foundation
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg/macos/foundation
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg/macos/foundation')
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/array.zig176
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig103
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/base.zig35
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/character_set.zig49
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/data.zig38
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig125
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/number.zig79
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/string.zig174
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/type.zig2
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/url.zig104
11 files changed, 886 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/array.zig b/vendor/libghostty-vt/pkg/macos/foundation/array.zig
new file mode 100644
index 00000000..7b580eb0
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/array.zig
@@ -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.*);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig b/vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig
new file mode 100644
index 00000000..c7f27d7d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig
@@ -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);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/base.zig b/vendor/libghostty-vt/pkg/macos/foundation/base.zig
new file mode 100644
index 00000000..b926523f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/base.zig
@@ -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 };
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/c.zig b/vendor/libghostty-vt/pkg/macos/foundation/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/character_set.zig b/vendor/libghostty-vt/pkg/macos/foundation/character_set.zig
new file mode 100644
index 00000000..d80ca45d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/character_set.zig
@@ -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();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/data.zig b/vendor/libghostty-vt/pkg/macos/foundation/data.zig
new file mode 100644
index 00000000..c06da65b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/data.zig
@@ -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();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig b/vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig
new file mode 100644
index 00000000..a529442a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig
@@ -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);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/number.zig b/vendor/libghostty-vt/pkg/macos/foundation/number.zig
new file mode 100644
index 00000000..88bf9edf
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/number.zig
@@ -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);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/string.zig b/vendor/libghostty-vt/pkg/macos/foundation/string.zig
new file mode 100644
index 00000000..2f679fef
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/string.zig
@@ -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));
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/type.zig b/vendor/libghostty-vt/pkg/macos/foundation/type.zig
new file mode 100644
index 00000000..45bd0905
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/type.zig
@@ -0,0 +1,2 @@
+pub extern "c" fn CFRelease(*anyopaque) void;
+pub extern "c" fn CFRetain(*anyopaque) void;
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/url.zig b/vendor/libghostty-vt/pkg/macos/foundation/url.zig
new file mode 100644
index 00000000..b48a7181
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/url.zig
@@ -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);
+ }
+}