1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
//! 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.
/// Create a new window.
new_window: NewWindow,
/// Create a new tab.
new_tab: NewTab,
/// Toggle the quick terminal.
toggle_quick_terminal: void,
pub const NewWindow = struct {
/// A list of configuration entries that will override the configuration
/// of the first surface of the new window. If this is `null` there
/// are no overrides. Note that not all configuration entries may be
/// overridden.
///
/// It is an error for this to be non-`null`, but zero length.
arguments: ?[][:0]const u8,
pub const C = extern struct {
/// A list of configuration entries that will override the
/// configuration of the first surface of the new tab. If this is
/// `null` there are no overrides. Note that not all configuration
/// entries may be overridden.
///
/// It is an error for this to be non-`null`, but zero length.
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;
}
};
pub const NewTab = struct {
/// The unique ID of a surface. If the ID is zero the currently focused
/// surface is assumed. This will be used to identify the window to add
/// the tab to.
surface_id: u64,
/// A list of configuration entries that will override the configuration
/// of the first surface of the new tab. If this is `null` there
/// are no overrides. Note that not all configuration entries may be
/// overridden.
///
/// It is an error for this to be non-`null`, but zero length.
arguments: ?[][:0]const u8,
pub const C = extern struct {
/// The unique ID of a surface. If the ID is zero the currently
/// focused surface is assumed. This will be used to identify the
/// window to add the tab to.
surface_id: u64,
/// A list of configuration entries that will override the
/// configuration of the first surface of the new tab. If this is
/// `null` there are no overrides. Note that not all configuration
/// entries may be overridden.
///
/// It is an error for this to be non-`null`, but zero length.
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,
new_tab,
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 => 12,
8 => 16,
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,
};
}
};
|