aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/apprt/ipc.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/apprt/ipc.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/apprt/ipc.zig')
-rw-r--r--vendor/libghostty-vt/src/apprt/ipc.zig192
1 files changed, 192 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/apprt/ipc.zig b/vendor/libghostty-vt/src/apprt/ipc.zig
new file mode 100644
index 00000000..b37647e0
--- /dev/null
+++ b/vendor/libghostty-vt/src/apprt/ipc.zig
@@ -0,0 +1,192 @@
+//! Inter-process Communication to a running Ghostty instance from a separate
+//! process.
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const assert = @import("../quirks.zig").inlineAssert;
+const lib = @import("../lib/main.zig");
+
+pub const Errors = error{
+ /// The IPC failed. If a function returns this error, it's expected that
+ /// an a more specific error message will have been written to stderr (or
+ /// otherwise shown to the user in an appropriate way).
+ IPCFailed,
+};
+
+pub const Target = union(Key) {
+ /// Open up a new window in a custom instance of Ghostty.
+ class: [:0]const u8,
+
+ /// Detect which instance to open a new window in.
+ detect,
+
+ // Sync with: ghostty_ipc_target_tag_e
+ pub const Key = enum(c_int) {
+ class,
+ detect,
+
+ test "ghostty.h Target.Key" {
+ try lib.checkGhosttyHEnum(Key, "GHOSTTY_IPC_TARGET_");
+ }
+ };
+
+ // Sync with: ghostty_ipc_target_u
+ pub const CValue = extern union {
+ class: [*:0]const u8,
+ detect: void,
+ };
+
+ // Sync with: ghostty_ipc_target_s
+ pub const C = extern struct {
+ key: Key,
+ value: CValue,
+ };
+
+ /// Convert to ghostty_ipc_target_s.
+ pub fn cval(self: Target) C {
+ return .{
+ .key = @as(Key, self),
+ .value = switch (self) {
+ .class => |class| .{ .class = class.ptr },
+ .detect => .{ .detect = {} },
+ },
+ };
+ }
+};
+
+pub const Action = union(enum) {
+ // A GUIDE TO ADDING NEW ACTIONS:
+ //
+ // 1. Add the action to the `Key` enum. The order of the enum matters
+ // because it maps directly to the libghostty C enum. For ABI
+ // compatibility, new actions should be added to the end of the enum.
+ //
+ // 2. Add the action and optional value to the Action union.
+ //
+ // 3. If the value type is not void, ensure the value is C ABI
+ // compatible (extern). If it is not, add a `C` decl to the value
+ // and a `cval` function to convert to the C ABI compatible value.
+ //
+ // 4. Update `include/ghostty.h`: add the new key, value, and union
+ // entry. If the value type is void then only the key needs to be
+ // added. Ensure the order matches exactly with the Zig code.
+
+ /// The arguments to pass to Ghostty as the command.
+ new_window: NewWindow,
+
+ pub const NewWindow = struct {
+ /// A list of command arguments to launch in the new window. If this is
+ /// `null` the command configured in the config or the user's default
+ /// shell should be launched.
+ ///
+ /// It is an error for this to be non-`null`, but zero length.
+ arguments: ?[][:0]const u8,
+
+ pub const C = extern struct {
+ /// null terminated list of arguments
+ /// it will be null itself if there are no arguments
+ arguments: ?[*]?[*:0]const u8,
+
+ pub fn deinit(self: *NewWindow.C, alloc: Allocator) void {
+ if (self.arguments) |arguments| alloc.free(arguments);
+ }
+ };
+
+ pub fn cval(self: *NewWindow, alloc: Allocator) Allocator.Error!NewWindow.C {
+ var result: NewWindow.C = undefined;
+
+ if (self.arguments) |arguments| {
+ result.arguments = try alloc.alloc([*:0]const u8, arguments.len + 1);
+
+ for (arguments, 0..) |argument, i|
+ result.arguments[i] = argument.ptr;
+
+ // add null terminator
+ result.arguments[arguments.len] = null;
+ } else {
+ result.arguments = null;
+ }
+
+ return result;
+ }
+ };
+
+ /// Sync with: ghostty_ipc_action_tag_e
+ pub const Key = enum(c_int) {
+ new_window,
+
+ test "ghostty.h Action.Key" {
+ try lib.checkGhosttyHEnum(Key, "GHOSTTY_IPC_ACTION_");
+ }
+ };
+
+ /// Sync with: ghostty_ipc_action_u
+ pub const CValue = cvalue: {
+ const key_fields = @typeInfo(Key).@"enum".fields;
+ var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined;
+ for (key_fields, 0..) |field, i| {
+ const action = @unionInit(Action, field.name, undefined);
+ const Type = t: {
+ const Type = @TypeOf(@field(action, field.name));
+ // Types can provide custom types for their CValue.
+ if (Type != void and @hasDecl(Type, "C")) break :t Type.C;
+ break :t Type;
+ };
+
+ union_fields[i] = .{
+ .name = field.name,
+ .type = Type,
+ .alignment = @alignOf(Type),
+ };
+ }
+
+ break :cvalue @Type(.{ .@"union" = .{
+ .layout = .@"extern",
+ .tag_type = null,
+ .fields = &union_fields,
+ .decls = &.{},
+ } });
+ };
+
+ /// Sync with: ghostty_ipc_action_s
+ pub const C = extern struct {
+ key: Key,
+ value: CValue,
+ };
+
+ comptime {
+ // For ABI compatibility, we expect that this is our union size.
+ // At the time of writing, we don't promise ABI compatibility
+ // so we can change this but I want to be aware of it.
+ assert(@sizeOf(CValue) == switch (@sizeOf(usize)) {
+ 4 => 4,
+ 8 => 8,
+ else => unreachable,
+ });
+ }
+
+ /// Returns the value type for the given key.
+ pub fn Value(comptime key: Key) type {
+ inline for (@typeInfo(Action).@"union".fields) |field| {
+ const field_key = @field(Key, field.name);
+ if (field_key == key) return field.type;
+ }
+
+ unreachable;
+ }
+
+ /// Convert to ghostty_ipc_action_s.
+ pub fn cval(self: Action, alloc: Allocator) C {
+ const value: CValue = switch (self) {
+ inline else => |v, tag| @unionInit(
+ CValue,
+ @tagName(tag),
+ if (@TypeOf(v) != void and @hasDecl(@TypeOf(v), "cval")) v.cval(alloc) else v,
+ ),
+ };
+
+ return .{
+ .key = @as(Key, self),
+ .value = value,
+ };
+ }
+};