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
|
const std = @import("std");
const Allocator = std.mem.Allocator;
const renderer = @import("../renderer.zig");
const terminal = @import("../terminal/main.zig");
const termio = @import("../termio.zig");
const MessageData = @import("../datastruct/main.zig").MessageData;
/// The messages that can be sent to an IO thread.
///
/// This is not a tiny structure (~40 bytes at the time of writing this comment),
/// but the number of messages we send to the IO thread is also very small.
/// At the current size we can queue 26,000 messages before consuming a MB of RAM.
pub const Message = union(enum) {
/// Represents a write request. Magic number comes from the largest
/// other union value. It can be upped if we add a larger union member
/// in the future.
pub const WriteReq = MessageData(u8, 38);
/// Request a color scheme report is sent to the PTY.
color_scheme_report: struct {
/// Force write the current color scheme
force: bool,
},
/// Request a visibility report is sent to the PTY.
visibility_report: struct {
/// The visibility state to report.
visible: bool,
/// Send the report even when mode 2033 is disabled.
force: bool,
},
/// Purposely crash the renderer. This is used for testing and debugging.
/// See the "crash" binding action.
crash: void,
/// The derived configuration to update the implementation with. This
/// is allocated via the allocator and is expected to be freed when done.
change_config: struct {
alloc: Allocator,
ptr: *termio.Termio.DerivedConfig,
},
/// Activate or deactivate the inspector.
inspector: bool,
/// Resize the window.
resize: renderer.Size,
/// Request a size report is sent to the PTY ([in-band
/// size report, mode 2048](https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83) and
/// [XTWINOPS](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps;Ps;Ps-t.1EB0)).
size_report: SizeReport,
/// Clear the screen.
clear_screen: struct {
/// Include clearing the history
history: bool,
},
/// Scroll the viewport
scroll_viewport: terminal.Terminal.ScrollViewport,
/// Selection scrolling. If this is set to true then the termio
/// thread starts a timer that will trigger a `selection_scroll_tick`
/// message back to the surface. This ping/pong is because the
/// surface thread doesn't have access to an event loop from libghostty.
selection_scroll: bool,
/// Jump forward/backward n prompts.
jump_to_prompt: isize,
/// Send this when a synchronized output mode is started. This will
/// start the timer so that the output mode is disabled after a
/// period of time so that a bad actor can't hang the terminal.
start_synchronized_output: void,
/// Enable or disable linefeed mode (mode 20).
linefeed_mode: bool,
/// The surface gained or lost focus.
focused: bool,
/// Record a Kitty clipboard protocol session grant for a password
/// so future requests carrying it skip the permission prompt, for
/// reads and writes respectively.
kitty_clipboard_grant_read: KittyClipboardGrant,
kitty_clipboard_grant_write: KittyClipboardGrant,
/// Write where the data fits in the union.
write_small: WriteReq.Small,
/// Write where the data pointer is stable.
write_stable: WriteReq.Stable,
/// Write where the data is allocated and must be freed.
write_alloc: WriteReq.Alloc,
/// The payload of the kitty_clipboard_grant_* messages. The
/// password is allocated and must be freed.
pub const KittyClipboardGrant = struct {
alloc: Allocator,
pw: []const u8,
};
/// Return a write request for the given data. This will use
/// write_small if it fits or write_alloc otherwise. This should NOT
/// be used for stable pointers which can be manually set to write_stable.
pub fn writeReq(alloc: Allocator, data: anytype) !Message {
return switch (try WriteReq.init(alloc, data)) {
.stable => unreachable,
.small => |v| Message{ .write_small = v },
.alloc => |v| Message{ .write_alloc = v },
};
}
/// Free resources owned by a message that will not be processed.
/// The message is invalid after this call.
pub fn deinit(self: *const Message) void {
switch (self.*) {
.change_config => |v| {
v.ptr.deinit();
v.alloc.destroy(v.ptr);
},
.write_alloc => |v| v.alloc.free(v.data),
.kitty_clipboard_grant_read,
.kitty_clipboard_grant_write,
=> |v| v.alloc.free(v.pw),
else => {},
}
}
/// The types of size reports that we support.
pub const SizeReport = terminal.size_report.Style;
};
test {
std.testing.refAllDecls(@This());
}
test {
// Ensure we don't grow our IO message size without explicitly wanting to.
const testing = std.testing;
try testing.expectEqual(@as(usize, 40), @sizeOf(Message));
}
|