mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-24 12:46:10 +03:00
184 lines
5.7 KiB
Zig
184 lines
5.7 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 = std.debug.assert;
|
|
|
|
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,
|
|
};
|
|
|
|
// 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_uint) {
|
|
new_window,
|
|
};
|
|
|
|
/// 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,
|
|
};
|
|
}
|
|
};
|