mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00

These sequences were implemented: CSI 14 t - report the text area size in pixels CSI 16 t - report the cell size in pixels CSI 18 t - report the text area size in cells CSI 21 t - report the window title These sequences were not implemented because they manuipulate the window state in ways that we do not want. CSI 1 t CSI 2 t CSI 3 ; x ; y t CSI 4 ; height ; width ; t CSI 5 t CSI 6 t CSI 7 t CSI 8 ; height ; width ; t CSI 9 ; 0 t CSI 9 ; 1 t CSI 9 ; 2 t CSI 9 ; 3 t CSI 10 ; 0 t CSI 10 ; 1 t CSI 10 ; 2 t CSI 24 t These sequences were not implemented because they do not make sense in a Wayland context: CSI 11 t CSI 13 t CSI 14 ; 2 t These sequences were not implemented because they provide information about the screen that is unnecessary. CSI 15 t CSI 19 t These sequences were not implemeted because Ghostty does not maintain an icon title for windows. CSI 20 t CSI 22 ; 0 t CSI 22 ; 1 t CSI 23 ; 0 t CSI 23 ; 1 t These sequences were not implemented because of the additional complexity of maintaining a stack of window titles. CSI 22 ; 2 t CSI 23 ; 2 t
238 lines
7.8 KiB
Zig
238 lines
7.8 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const Allocator = std.mem.Allocator;
|
|
const renderer = @import("../renderer.zig");
|
|
const terminal = @import("../terminal/main.zig");
|
|
const termio = @import("../termio.zig");
|
|
|
|
/// 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 messages are IO thread sends are also very few. 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);
|
|
|
|
pub const Resize = struct {
|
|
/// The grid size for the given screen size with padding applied.
|
|
grid_size: renderer.GridSize,
|
|
|
|
/// The full screen (drawable) size. This does NOT include padding.
|
|
/// This should be sent on to the renderer.
|
|
screen_size: renderer.ScreenSize,
|
|
|
|
/// The padding, so that the terminal implementation can subtract
|
|
/// this to send to the pty.
|
|
padding: renderer.Padding,
|
|
};
|
|
|
|
/// 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: Resize,
|
|
|
|
/// 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.htmli#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,
|
|
|
|
/// 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 child exited abnormally. The termio state is marked
|
|
/// as process exited but the surface hasn't been notified to
|
|
/// close because termio can use this to update the terminal
|
|
/// with an error message.
|
|
child_exited_abnormally: struct {
|
|
exit_code: u32,
|
|
runtime_ms: u64,
|
|
},
|
|
|
|
/// 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,
|
|
|
|
/// 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 },
|
|
};
|
|
}
|
|
|
|
/// The types of size reports that we support
|
|
pub const SizeReport = enum {
|
|
mode_2048,
|
|
csi_14_t,
|
|
csi_16_t,
|
|
csi_18_t,
|
|
};
|
|
};
|
|
|
|
/// Creates a union that can be used to accommodate data that fit within an array,
|
|
/// are a stable pointer, or require deallocation. This is helpful for thread
|
|
/// messaging utilities.
|
|
pub fn MessageData(comptime Elem: type, comptime small_size: comptime_int) type {
|
|
return union(enum) {
|
|
pub const Self = @This();
|
|
|
|
pub const Small = struct {
|
|
pub const Max = small_size;
|
|
pub const Array = [Max]Elem;
|
|
pub const Len = std.math.IntFittingRange(0, small_size);
|
|
data: Array = undefined,
|
|
len: Len = 0,
|
|
};
|
|
|
|
pub const Alloc = struct {
|
|
alloc: Allocator,
|
|
data: []Elem,
|
|
};
|
|
|
|
pub const Stable = []const Elem;
|
|
|
|
/// A small write where the data fits into this union size.
|
|
small: Small,
|
|
|
|
/// A stable pointer so we can just pass the slice directly through.
|
|
/// This is useful i.e. for const data.
|
|
stable: Stable,
|
|
|
|
/// Allocated and must be freed with the provided allocator. This
|
|
/// should be rarely used.
|
|
alloc: Alloc,
|
|
|
|
/// Initializes the union for a given data type. This will
|
|
/// attempt to fit into a small value if possible, otherwise
|
|
/// will allocate and put into alloc.
|
|
///
|
|
/// This can't and will never detect stable pointers.
|
|
pub fn init(alloc: Allocator, data: anytype) !Self {
|
|
switch (@typeInfo(@TypeOf(data))) {
|
|
.Pointer => |info| {
|
|
assert(info.size == .Slice);
|
|
assert(info.child == Elem);
|
|
|
|
// If it fits in our small request, do that.
|
|
if (data.len <= Small.Max) {
|
|
var buf: Small.Array = undefined;
|
|
@memcpy(buf[0..data.len], data);
|
|
return Self{
|
|
.small = .{
|
|
.data = buf,
|
|
.len = @intCast(data.len),
|
|
},
|
|
};
|
|
}
|
|
|
|
// Otherwise, allocate
|
|
const buf = try alloc.dupe(Elem, data);
|
|
errdefer alloc.free(buf);
|
|
return Self{
|
|
.alloc = .{
|
|
.alloc = alloc,
|
|
.data = buf,
|
|
},
|
|
};
|
|
},
|
|
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: Self) void {
|
|
switch (self) {
|
|
.small, .stable => {},
|
|
.alloc => |v| v.alloc.free(v.alloc.data),
|
|
}
|
|
}
|
|
|
|
/// Returns a const slice of the data pointed to by this request.
|
|
pub fn slice(self: Self) []const Elem {
|
|
return switch (self) {
|
|
.small => |v| v.data[0..v.len],
|
|
.stable => |v| v,
|
|
.alloc => |v| v.data,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
test "MessageData init small" {
|
|
const testing = std.testing;
|
|
const alloc = testing.allocator;
|
|
|
|
const Data = MessageData(u8, 10);
|
|
const input = "hello!";
|
|
const io = try Data.init(alloc, @as([]const u8, input));
|
|
try testing.expect(io == .small);
|
|
}
|
|
|
|
test "MessageData init alloc" {
|
|
const testing = std.testing;
|
|
const alloc = testing.allocator;
|
|
|
|
const Data = MessageData(u8, 10);
|
|
const input = "hello! " ** 100;
|
|
const io = try Data.init(alloc, @as([]const u8, input));
|
|
try testing.expect(io == .alloc);
|
|
io.alloc.alloc.free(io.alloc.data);
|
|
}
|
|
|
|
test "MessageData small fits non-u8 sized data" {
|
|
const testing = std.testing;
|
|
const alloc = testing.allocator;
|
|
|
|
const len = 500;
|
|
const Data = MessageData(u8, len);
|
|
const input: []const u8 = "X" ** len;
|
|
const io = try Data.init(alloc, input);
|
|
try testing.expect(io == .small);
|
|
}
|