Files
ghostty/src/apprt/ipc.zig
Chris Marchesi e8525c0fd9 Update to Zig 0.16.0
This commit represents the majority of the work necessary to upgrade
Ghostty to use Zig 0.16.0.

Key parts:

* In addition to its previous responsibilities, the global state now
  houses state for global I/O implementations and the process
  environment. It is now also utilized in the main application along
  with the C library. Where necessary, global state is isolated from key
  parts of the implementation (e.g., in libghostty subsystems), and it's
  expected that this list will grow.

* We currently manage our own C translation layer where necessary. In
  these cases, cImport has been removed in favor of the new external
  translate-c package. Due to fixes that have needed be made to properly
  translate the dependencies that were swapped out, as mentioned, we
  have had to backport fixes from the current translate-c package (and
  the upstream Arocc dependency). We will host this ourselves until Zig
  0.17.0 is released with these fixes.

* Where necessary (only a small number of cases), some stdlib code from
  0.15.2 (and even from 0.17.0) has been taken, adopted, and vendored in
  lib/compat.

Co-authored-by: Leah Amelia Chen <hi@pluie.me>
2026-07-21 12:35:05 -07:00

193 lines
6.1 KiB
Zig

//! 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,
/// Toggle the quick terminal.
toggle_quick_terminal: void,
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,
toggle_quick_terminal,
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 names: [key_fields.len][]const u8 = undefined;
var types: [key_fields.len]type = undefined;
var attrs: [key_fields.len]std.builtin.Type.UnionField.Attributes = undefined;
for (key_fields, &names, &types, &attrs) |field, *name, *ty, *attr| {
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;
};
name.* = field.name;
ty.* = Type;
attr.* = .{ .@"align" = @alignOf(Type) };
}
break :cvalue @Union(.@"extern", null, &names, &types, &attrs);
};
/// 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,
};
}
};