diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg/macos/foundation | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (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.zig | 176 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig | 103 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/base.zig | 35 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/c.zig | 1 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/character_set.zig | 49 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/data.zig | 38 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig | 125 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/number.zig | 79 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/string.zig | 174 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/type.zig | 2 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/macos/foundation/url.zig | 104 |
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); + } +} |
