aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/os/args.zig
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/src/os/args.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/os/args.zig')
-rw-r--r--vendor/libghostty-vt/src/os/args.zig130
1 files changed, 130 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/os/args.zig b/vendor/libghostty-vt/src/os/args.zig
new file mode 100644
index 00000000..9ef5bba4
--- /dev/null
+++ b/vendor/libghostty-vt/src/os/args.zig
@@ -0,0 +1,130 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const Allocator = std.mem.Allocator;
+const objc = @import("objc");
+const macos = @import("macos");
+
+/// Returns an iterator over the command line arguments. This may or may
+/// not allocate depending on the platform.
+///
+/// For Zig-aware readers: this is the same as std.process.argsWithAllocator
+/// but handles macOS using NSProcessInfo instead of libc argc/argv.
+pub fn iterator(allocator: Allocator) ArgIterator.InitError!ArgIterator {
+ //if (true) return try std.process.argsWithAllocator(allocator);
+ return .initWithAllocator(allocator);
+}
+
+/// Duck-typed to std.process.ArgIterator
+pub const ArgIterator = switch (builtin.os.tag) {
+ .macos => IteratorMacOS,
+ else => std.process.ArgIterator,
+};
+
+/// This is an ArgIterator (duck-typed for std.process.ArgIterator) for
+/// NSApplicationMain-based applications on macOS. It uses NSProcessInfo to
+/// get the command line arguments since libc argc/argv pointers are not
+/// valid.
+///
+/// I believe this should work for all macOS applications even if
+/// NSApplicationMain is not used, but I haven't tested that so I'm not
+/// sure. If/when libghostty is ever used outside of NSApplicationMain
+/// then we can revisit this.
+const IteratorMacOS = struct {
+ alloc: Allocator,
+ index: usize,
+ count: usize,
+ buf: [:0]u8,
+ args: objc.Object,
+
+ pub const InitError = Allocator.Error;
+
+ pub fn initWithAllocator(alloc: Allocator) InitError!IteratorMacOS {
+ const NSProcessInfo = objc.getClass("NSProcessInfo").?;
+ const info = NSProcessInfo.msgSend(objc.Object, objc.sel("processInfo"), .{});
+ const args = info.getProperty(objc.Object, "arguments");
+ errdefer args.release();
+
+ // Determine our maximum length so we can allocate the buffer to
+ // fit all values.
+ var max: usize = 0;
+ const count: usize = @intCast(args.getProperty(c_ulong, "count"));
+ for (0..count) |i| {
+ const nsstr = args.msgSend(
+ objc.Object,
+ objc.sel("objectAtIndex:"),
+ .{@as(c_ulong, @intCast(i))},
+ );
+
+ const maxlen: usize = @intCast(nsstr.msgSend(
+ c_ulong,
+ objc.sel("maximumLengthOfBytesUsingEncoding:"),
+ .{@as(c_ulong, 4)},
+ ));
+
+ max = @max(max, maxlen);
+ }
+
+ // Allocate our buffer. We add 1 for the null terminator.
+ const buf = try alloc.allocSentinel(u8, max, 0);
+ errdefer alloc.free(buf);
+
+ return .{
+ .alloc = alloc,
+ .index = 0,
+ .count = count,
+ .buf = buf,
+ .args = args,
+ };
+ }
+
+ pub fn deinit(self: *IteratorMacOS) void {
+ self.alloc.free(self.buf);
+
+ // Note: we don't release self.args because it is a pointer copy
+ // not a retained object.
+ }
+
+ pub fn next(self: *IteratorMacOS) ?[:0]const u8 {
+ if (self.index == self.count) return null;
+
+ // NSString. No release because not a copy.
+ const nsstr = self.args.msgSend(
+ objc.Object,
+ objc.sel("objectAtIndex:"),
+ .{@as(c_ulong, @intCast(self.index))},
+ );
+ self.index += 1;
+
+ // Convert to string using getCString. Our buffer should always
+ // be big enough because we precomputed the maximum length.
+ if (!nsstr.msgSend(
+ bool,
+ objc.sel("getCString:maxLength:encoding:"),
+ .{
+ @as([*]u8, @ptrCast(self.buf.ptr)),
+ @as(c_ulong, @intCast(self.buf.len)),
+ @as(c_ulong, 4), // NSUTF8StringEncoding
+ },
+ )) {
+ // This should never happen... if it does, we just return empty.
+ return "";
+ }
+
+ return std.mem.sliceTo(self.buf, 0);
+ }
+
+ pub fn skip(self: *IteratorMacOS) bool {
+ if (self.index == self.count) return false;
+ self.index += 1;
+ return true;
+ }
+};
+
+test "args" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+
+ var iter = try iterator(alloc);
+ defer iter.deinit();
+ try testing.expect(iter.next().?.len > 0);
+}