mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal: move to new modes struct
This commit is contained in:
@ -742,7 +742,7 @@ fn clipboardPaste(
|
|||||||
log.warn("error scrolling to bottom err={}", .{err});
|
log.warn("error scrolling to bottom err={}", .{err});
|
||||||
};
|
};
|
||||||
|
|
||||||
break :bracketed self.io.terminal.modes.bracketed_paste;
|
break :bracketed self.io.terminal.modes.get(.bracketed_paste);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bracketed) {
|
if (bracketed) {
|
||||||
@ -1024,8 +1024,8 @@ pub fn charCallback(
|
|||||||
try self.io.terminal.scrollViewport(.{ .bottom = {} });
|
try self.io.terminal.scrollViewport(.{ .bottom = {} });
|
||||||
|
|
||||||
break :critical .{
|
break :critical .{
|
||||||
.alt_esc_prefix = self.io.terminal.modes.alt_esc_prefix,
|
.alt_esc_prefix = self.io.terminal.modes.get(.alt_esc_prefix),
|
||||||
.modify_other_keys = self.io.terminal.modes.modify_other_keys,
|
.modify_other_keys = self.io.terminal.flags.modify_other_keys_2,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1178,9 +1178,9 @@ pub fn keyCallback(
|
|||||||
|
|
||||||
// We'll need to know these values here on.
|
// We'll need to know these values here on.
|
||||||
self.renderer_state.mutex.lock();
|
self.renderer_state.mutex.lock();
|
||||||
const cursor_key_application = self.io.terminal.modes.cursor_keys;
|
const cursor_key_application = self.io.terminal.modes.get(.cursor_keys);
|
||||||
const keypad_key_application = self.io.terminal.modes.keypad_keys;
|
const keypad_key_application = self.io.terminal.modes.get(.keypad_keys);
|
||||||
const modify_other_keys = self.io.terminal.modes.modify_other_keys;
|
const modify_other_keys = self.io.terminal.flags.modify_other_keys_2;
|
||||||
self.renderer_state.mutex.unlock();
|
self.renderer_state.mutex.unlock();
|
||||||
|
|
||||||
// Check if we're processing a function key.
|
// Check if we're processing a function key.
|
||||||
@ -1355,7 +1355,7 @@ pub fn focusCallback(self: *Surface, focused: bool) !void {
|
|||||||
// Notify the app about focus in/out if it is requesting it
|
// Notify the app about focus in/out if it is requesting it
|
||||||
{
|
{
|
||||||
self.renderer_state.mutex.lock();
|
self.renderer_state.mutex.lock();
|
||||||
const focus_event = self.io.terminal.modes.focus_event;
|
const focus_event = self.io.terminal.modes.get(.focus_event);
|
||||||
self.renderer_state.mutex.unlock();
|
self.renderer_state.mutex.unlock();
|
||||||
|
|
||||||
if (focus_event) {
|
if (focus_event) {
|
||||||
@ -1479,7 +1479,7 @@ pub fn scrollCallback(
|
|||||||
// If we have an active mouse reporting mode, clear the selection.
|
// If we have an active mouse reporting mode, clear the selection.
|
||||||
// The selection can occur if the user uses the shift mod key to
|
// The selection can occur if the user uses the shift mod key to
|
||||||
// override mouse grabbing from the window.
|
// override mouse grabbing from the window.
|
||||||
if (self.io.terminal.modes.mouse_event != .none) {
|
if (self.io.terminal.flags.mouse_event != .none) {
|
||||||
self.setSelection(null);
|
self.setSelection(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1488,8 +1488,8 @@ pub fn scrollCallback(
|
|||||||
// (1) alt screen (2) no explicit mouse reporting and (3) alt
|
// (1) alt screen (2) no explicit mouse reporting and (3) alt
|
||||||
// scroll mode enabled.
|
// scroll mode enabled.
|
||||||
if (self.io.terminal.active_screen == .alternate and
|
if (self.io.terminal.active_screen == .alternate and
|
||||||
self.io.terminal.modes.mouse_event == .none and
|
self.io.terminal.flags.mouse_event == .none and
|
||||||
self.io.terminal.modes.mouse_alternate_scroll)
|
self.io.terminal.modes.get(.mouse_alternate_scroll))
|
||||||
{
|
{
|
||||||
if (y.delta_unsigned > 0) {
|
if (y.delta_unsigned > 0) {
|
||||||
const seq = if (y.delta < 0) "\x1bOA" else "\x1bOB";
|
const seq = if (y.delta < 0) "\x1bOA" else "\x1bOB";
|
||||||
@ -1550,7 +1550,7 @@ fn mouseReport(
|
|||||||
// do we want to not report mouse events at all outside the surface?
|
// do we want to not report mouse events at all outside the surface?
|
||||||
|
|
||||||
// Depending on the event, we may do nothing at all.
|
// Depending on the event, we may do nothing at all.
|
||||||
switch (self.io.terminal.modes.mouse_event) {
|
switch (self.io.terminal.flags.mouse_event) {
|
||||||
.none => return,
|
.none => return,
|
||||||
|
|
||||||
// X10 only reports clicks with mouse button 1, 2, 3. We verify
|
// X10 only reports clicks with mouse button 1, 2, 3. We verify
|
||||||
@ -1585,7 +1585,7 @@ fn mouseReport(
|
|||||||
if (button == null) {
|
if (button == null) {
|
||||||
// Null button means motion without a button pressed
|
// Null button means motion without a button pressed
|
||||||
acc = 3;
|
acc = 3;
|
||||||
} else if (action == .release and self.io.terminal.modes.mouse_format != .sgr) {
|
} else if (action == .release and self.io.terminal.flags.mouse_format != .sgr) {
|
||||||
// Release is 3. It is NOT 3 in SGR mode because SGR can tell
|
// Release is 3. It is NOT 3 in SGR mode because SGR can tell
|
||||||
// the application what button was released.
|
// the application what button was released.
|
||||||
acc = 3;
|
acc = 3;
|
||||||
@ -1601,7 +1601,7 @@ fn mouseReport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// X10 doesn't have modifiers
|
// X10 doesn't have modifiers
|
||||||
if (self.io.terminal.modes.mouse_event != .x10) {
|
if (self.io.terminal.flags.mouse_event != .x10) {
|
||||||
if (mods.shift) acc += 4;
|
if (mods.shift) acc += 4;
|
||||||
if (mods.super) acc += 8;
|
if (mods.super) acc += 8;
|
||||||
if (mods.ctrl) acc += 16;
|
if (mods.ctrl) acc += 16;
|
||||||
@ -1613,7 +1613,7 @@ fn mouseReport(
|
|||||||
break :code acc;
|
break :code acc;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (self.io.terminal.modes.mouse_format) {
|
switch (self.io.terminal.flags.mouse_format) {
|
||||||
.x10 => {
|
.x10 => {
|
||||||
if (viewport_point.x > 222 or viewport_point.y > 222) {
|
if (viewport_point.x > 222 or viewport_point.y > 222) {
|
||||||
log.info("X10 mouse format can only encode X/Y up to 223", .{});
|
log.info("X10 mouse format can only encode X/Y up to 223", .{});
|
||||||
@ -1784,7 +1784,7 @@ pub fn mouseButtonCallback(
|
|||||||
defer self.renderer_state.mutex.unlock();
|
defer self.renderer_state.mutex.unlock();
|
||||||
|
|
||||||
// Report mouse events if enabled
|
// Report mouse events if enabled
|
||||||
if (self.io.terminal.modes.mouse_event != .none) report: {
|
if (self.io.terminal.flags.mouse_event != .none) report: {
|
||||||
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
||||||
if (mods.shift) break :report;
|
if (mods.shift) break :report;
|
||||||
|
|
||||||
@ -1922,7 +1922,7 @@ pub fn cursorPosCallback(
|
|||||||
defer self.renderer_state.mutex.unlock();
|
defer self.renderer_state.mutex.unlock();
|
||||||
|
|
||||||
// Do a mouse report
|
// Do a mouse report
|
||||||
if (self.io.terminal.modes.mouse_event != .none) report: {
|
if (self.io.terminal.flags.mouse_event != .none) report: {
|
||||||
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
// Shift overrides mouse "grabbing" in the window, taken from Kitty.
|
||||||
if (self.mouse.mods.shift) break :report;
|
if (self.mouse.mods.shift) break :report;
|
||||||
|
|
||||||
@ -2224,7 +2224,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !void
|
|||||||
log.warn("error scrolling to bottom err={}", .{err});
|
log.warn("error scrolling to bottom err={}", .{err});
|
||||||
};
|
};
|
||||||
|
|
||||||
break :normal !self.io.terminal.modes.cursor_keys;
|
break :normal !self.io.terminal.modes.get(.cursor_keys);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (normal) {
|
if (normal) {
|
||||||
|
@ -564,7 +564,7 @@ pub fn render(
|
|||||||
self.config.background = bg;
|
self.config.background = bg;
|
||||||
self.config.foreground = fg;
|
self.config.foreground = fg;
|
||||||
}
|
}
|
||||||
if (state.terminal.modes.reverse_colors) {
|
if (state.terminal.modes.get(.reverse_colors)) {
|
||||||
self.config.background = fg;
|
self.config.background = fg;
|
||||||
self.config.foreground = bg;
|
self.config.foreground = bg;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ const assert = std.debug.assert;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const ansi = @import("ansi.zig");
|
const ansi = @import("ansi.zig");
|
||||||
|
const modes = @import("modes.zig");
|
||||||
const charsets = @import("charsets.zig");
|
const charsets = @import("charsets.zig");
|
||||||
const csi = @import("csi.zig");
|
const csi = @import("csi.zig");
|
||||||
const sgr = @import("sgr.zig");
|
const sgr = @import("sgr.zig");
|
||||||
@ -77,45 +78,27 @@ color_palette: color.Palette = color.default,
|
|||||||
/// char CSI (ESC [ <n> b).
|
/// char CSI (ESC [ <n> b).
|
||||||
previous_char: ?u21 = null,
|
previous_char: ?u21 = null,
|
||||||
|
|
||||||
/// Modes - This isn't exhaustive, since some modes (i.e. cursor origin)
|
/// The modes that this terminal currently has active.
|
||||||
/// are applied to the cursor and others aren't boolean yes/no.
|
modes: modes.ModeState = .{},
|
||||||
modes: packed struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
cursor_keys: bool = false, // 1
|
|
||||||
insert: bool = false, // 4
|
|
||||||
reverse_colors: bool = false, // 5,
|
|
||||||
origin: bool = false, // 6
|
|
||||||
autowrap: bool = true, // 7
|
|
||||||
|
|
||||||
deccolm: bool = false, // 3,
|
|
||||||
deccolm_supported: bool = false, // 40
|
|
||||||
keypad_keys: bool = false, // 66
|
|
||||||
alt_esc_prefix: bool = true, // 1036
|
|
||||||
|
|
||||||
focus_event: bool = false, // 1004
|
|
||||||
mouse_alternate_scroll: bool = true, // 1007
|
|
||||||
mouse_event: MouseEvents = .none,
|
|
||||||
mouse_format: MouseFormat = .x10,
|
|
||||||
|
|
||||||
bracketed_paste: bool = false, // 2004
|
|
||||||
|
|
||||||
// This is set via ESC[4;2m. Any other modify key mode just sets
|
|
||||||
// this to false.
|
|
||||||
modify_other_keys: bool = false,
|
|
||||||
|
|
||||||
|
/// These are just a packed set of flags we may set on the terminal.
|
||||||
|
flags: packed struct {
|
||||||
// This isn't a mode, this is set by OSC 133 using the "A" event.
|
// This isn't a mode, this is set by OSC 133 using the "A" event.
|
||||||
// If this is true, it tells us that the shell supports redrawing
|
// If this is true, it tells us that the shell supports redrawing
|
||||||
// the prompt and that when we resize, if the cursor is at a prompt,
|
// the prompt and that when we resize, if the cursor is at a prompt,
|
||||||
// then we should clear the screen below and allow the shell to redraw.
|
// then we should clear the screen below and allow the shell to redraw.
|
||||||
shell_redraws_prompt: bool = false,
|
shell_redraws_prompt: bool = false,
|
||||||
|
|
||||||
test {
|
// This is set via ESC[4;2m. Any other modify key mode just sets
|
||||||
// We have this here so that we explicitly fail when we change the
|
// this to false and we act in mode 1 by default.
|
||||||
// size of modes. The size of modes is NOT particularly important,
|
modify_other_keys_2: bool = false,
|
||||||
// we just want to be mentally aware when it happens.
|
|
||||||
try std.testing.expectEqual(4, @sizeOf(Self));
|
/// The mouse event mode and format. These are set to the last
|
||||||
}
|
/// set mode in modes. You can't get the right event/format to use
|
||||||
|
/// based on modes alone because modes don't show you what order
|
||||||
|
/// this was called so we have to track it separately.
|
||||||
|
mouse_event: MouseEvents = .none,
|
||||||
|
mouse_format: MouseFormat = .x10,
|
||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
/// State required for all charset operations.
|
/// State required for all charset operations.
|
||||||
@ -285,10 +268,10 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void {
|
|||||||
// bit. If the mode "?40" is set, then "?3" (DECCOLM) is supported. This
|
// bit. If the mode "?40" is set, then "?3" (DECCOLM) is supported. This
|
||||||
// doesn't exactly match VT100 semantics but modern terminals no longer
|
// doesn't exactly match VT100 semantics but modern terminals no longer
|
||||||
// blindly accept mode 3 since its so weird in modern practice.
|
// blindly accept mode 3 since its so weird in modern practice.
|
||||||
if (!self.modes.deccolm_supported) return;
|
if (!self.modes.get(.enable_mode_3)) return;
|
||||||
|
|
||||||
// Enable it
|
// Enable it
|
||||||
self.modes.deccolm = mode == .@"132_cols";
|
self.modes.set(.@"132_column", mode == .@"132_cols");
|
||||||
|
|
||||||
// Resize -- we can set cols to 0 because deccolm will force it
|
// Resize -- we can set cols to 0 because deccolm will force it
|
||||||
try self.resize(alloc, 0, self.rows);
|
try self.resize(alloc, 0, self.rows);
|
||||||
@ -300,14 +283,6 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void {
|
|||||||
// TODO: left/right margins
|
// TODO: left/right margins
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows or disallows deccolm.
|
|
||||||
pub fn setDeccolmSupported(self: *Terminal, v: bool) void {
|
|
||||||
const tracy = trace(@src());
|
|
||||||
defer tracy.end();
|
|
||||||
|
|
||||||
self.modes.deccolm_supported = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize the underlying terminal.
|
/// Resize the underlying terminal.
|
||||||
pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !void {
|
pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !void {
|
||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
@ -316,8 +291,8 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
|
|||||||
// If we have deccolm supported then we are fixed at either 80 or 132
|
// If we have deccolm supported then we are fixed at either 80 or 132
|
||||||
// columns depending on if mode 3 is set or not.
|
// columns depending on if mode 3 is set or not.
|
||||||
// TODO: test
|
// TODO: test
|
||||||
const cols: usize = if (self.modes.deccolm_supported)
|
const cols: usize = if (self.modes.get(.enable_mode_3))
|
||||||
if (self.modes.deccolm) 132 else 80
|
if (self.modes.get(.@"132_column")) 132 else 80
|
||||||
else
|
else
|
||||||
cols_req;
|
cols_req;
|
||||||
|
|
||||||
@ -349,13 +324,13 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If modes.shell_redraws_prompt is true and we're on the primary screen,
|
/// If shell_redraws_prompt is true and we're on the primary screen,
|
||||||
/// then this will clear the screen from the cursor down if the cursor is
|
/// then this will clear the screen from the cursor down if the cursor is
|
||||||
/// on a prompt in order to allow the shell to redraw the prompt.
|
/// on a prompt in order to allow the shell to redraw the prompt.
|
||||||
fn clearPromptForResize(self: *Terminal) void {
|
fn clearPromptForResize(self: *Terminal) void {
|
||||||
assert(self.active_screen == .primary);
|
assert(self.active_screen == .primary);
|
||||||
|
|
||||||
if (!self.modes.shell_redraws_prompt) return;
|
if (!self.flags.shell_redraws_prompt) return;
|
||||||
|
|
||||||
// We need to find the first y that is a prompt. If we find any line
|
// We need to find the first y that is a prompt. If we find any line
|
||||||
// that is NOT a prompt (or input -- which is part of a prompt) then
|
// that is NOT a prompt (or input -- which is part of a prompt) then
|
||||||
@ -693,13 +668,16 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
self.previous_char = c;
|
self.previous_char = c;
|
||||||
|
|
||||||
// If we're soft-wrapping, then handle that first.
|
// If we're soft-wrapping, then handle that first.
|
||||||
if (self.screen.cursor.pending_wrap and self.modes.autowrap)
|
if (self.screen.cursor.pending_wrap and self.modes.get(.autowrap))
|
||||||
try self.printWrap();
|
try self.printWrap();
|
||||||
|
|
||||||
// If we have insert mode enabled then we need to handle that. We
|
// If we have insert mode enabled then we need to handle that. We
|
||||||
// only do insert mode if we're not at the end of the line.
|
// only do insert mode if we're not at the end of the line.
|
||||||
if (self.modes.insert and self.screen.cursor.x + width < self.cols)
|
if (self.modes.get(.insert) and
|
||||||
|
self.screen.cursor.x + width < self.cols)
|
||||||
|
{
|
||||||
self.insertBlanks(width);
|
self.insertBlanks(width);
|
||||||
|
}
|
||||||
|
|
||||||
switch (width) {
|
switch (width) {
|
||||||
// Single cell is very easy: just write in the cell
|
// Single cell is very easy: just write in the cell
|
||||||
@ -962,7 +940,7 @@ pub fn setCursorPos(self: *Terminal, row_req: usize, col_req: usize) void {
|
|||||||
y_offset: usize = 0,
|
y_offset: usize = 0,
|
||||||
x_max: usize,
|
x_max: usize,
|
||||||
y_max: usize,
|
y_max: usize,
|
||||||
} = if (self.modes.origin) .{
|
} = if (self.modes.get(.origin)) .{
|
||||||
.x_offset = 0, // TODO: left/right margins
|
.x_offset = 0, // TODO: left/right margins
|
||||||
.y_offset = self.scrolling_region.top,
|
.y_offset = self.scrolling_region.top,
|
||||||
.x_max = self.cols, // TODO: left/right margins
|
.x_max = self.cols, // TODO: left/right margins
|
||||||
@ -994,7 +972,7 @@ pub fn setCursorColAbsolute(self: *Terminal, col_req: usize) void {
|
|||||||
|
|
||||||
// TODO: test
|
// TODO: test
|
||||||
|
|
||||||
assert(!self.modes.origin); // TODO
|
assert(!self.modes.get(.origin)); // TODO
|
||||||
|
|
||||||
if (self.status_display != .main) return; // TODO
|
if (self.status_display != .main) return; // TODO
|
||||||
|
|
||||||
@ -1582,6 +1560,7 @@ pub fn fullReset(self: *Terminal) void {
|
|||||||
self.eraseDisplay(.scrollback);
|
self.eraseDisplay(.scrollback);
|
||||||
self.eraseDisplay(.complete);
|
self.eraseDisplay(.complete);
|
||||||
self.modes = .{};
|
self.modes = .{};
|
||||||
|
self.flags = .{};
|
||||||
self.tabstops.reset(0);
|
self.tabstops.reset(0);
|
||||||
self.screen.cursor = .{};
|
self.screen.cursor = .{};
|
||||||
self.screen.saved_cursor = .{};
|
self.screen.saved_cursor = .{};
|
||||||
@ -1890,7 +1869,7 @@ test "Terminal: setCursorPosition" {
|
|||||||
try testing.expect(!t.screen.cursor.pending_wrap);
|
try testing.expect(!t.screen.cursor.pending_wrap);
|
||||||
|
|
||||||
// Origin mode
|
// Origin mode
|
||||||
t.modes.origin = true;
|
t.modes.set(.origin, true);
|
||||||
|
|
||||||
// No change without a scroll region
|
// No change without a scroll region
|
||||||
t.setCursorPos(81, 81);
|
t.setCursorPos(81, 81);
|
||||||
@ -2340,7 +2319,7 @@ test "Terminal: insert mode with space" {
|
|||||||
|
|
||||||
for ("hello") |c| try t.print(c);
|
for ("hello") |c| try t.print(c);
|
||||||
t.setCursorPos(1, 2);
|
t.setCursorPos(1, 2);
|
||||||
t.modes.insert = true;
|
t.modes.set(.insert, true);
|
||||||
try t.print('X');
|
try t.print('X');
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -2357,7 +2336,7 @@ test "Terminal: insert mode doesn't wrap pushed characters" {
|
|||||||
|
|
||||||
for ("hello") |c| try t.print(c);
|
for ("hello") |c| try t.print(c);
|
||||||
t.setCursorPos(1, 2);
|
t.setCursorPos(1, 2);
|
||||||
t.modes.insert = true;
|
t.modes.set(.insert, true);
|
||||||
try t.print('X');
|
try t.print('X');
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -2373,7 +2352,7 @@ test "Terminal: insert mode does nothing at the end of the line" {
|
|||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
for ("hello") |c| try t.print(c);
|
for ("hello") |c| try t.print(c);
|
||||||
t.modes.insert = true;
|
t.modes.set(.insert, true);
|
||||||
try t.print('X');
|
try t.print('X');
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -2390,7 +2369,7 @@ test "Terminal: insert mode with wide characters" {
|
|||||||
|
|
||||||
for ("hello") |c| try t.print(c);
|
for ("hello") |c| try t.print(c);
|
||||||
t.setCursorPos(1, 2);
|
t.setCursorPos(1, 2);
|
||||||
t.modes.insert = true;
|
t.modes.set(.insert, true);
|
||||||
try t.print('😀'); // 0x1F600
|
try t.print('😀'); // 0x1F600
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -2406,7 +2385,7 @@ test "Terminal: insert mode with wide characters at end" {
|
|||||||
defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
for ("well") |c| try t.print(c);
|
for ("well") |c| try t.print(c);
|
||||||
t.modes.insert = true;
|
t.modes.set(.insert, true);
|
||||||
try t.print('😀'); // 0x1F600
|
try t.print('😀'); // 0x1F600
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -42,106 +42,6 @@ pub const RenditionAspect = enum(u16) {
|
|||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Modes that can be set with with Set Mode (SM) (ESC [ h). The enum
|
|
||||||
/// values correspond to the `?`-prefixed modes, since those are the ones
|
|
||||||
/// of primary interest. The enum value is the mode value.
|
|
||||||
pub const Mode = enum(u16) {
|
|
||||||
/// This control function selects the sequences the arrow keys send.
|
|
||||||
/// You can use the four arrow keys to move the cursor through the current
|
|
||||||
/// page or to send special application commands.
|
|
||||||
///
|
|
||||||
/// If the DECCKM function is set, then the arrow keys send application
|
|
||||||
/// sequences to the host.
|
|
||||||
///
|
|
||||||
/// If the DECCKM function is reset, then the arrow keys send ANSI cursor
|
|
||||||
/// sequences to the host.
|
|
||||||
cursor_keys = 1,
|
|
||||||
|
|
||||||
/// Change terminal wide between 80 and 132 column mode. When set
|
|
||||||
/// (with ?40 set), resizes terminal to 132 columns and keeps it that
|
|
||||||
/// wide. When unset, resizes to 80 columns.
|
|
||||||
@"132_column" = 3,
|
|
||||||
|
|
||||||
/// Insert mode. This mode writes a character and pushes all existing
|
|
||||||
/// characters to the right. The existing contents are never wrapped.
|
|
||||||
insert = 4,
|
|
||||||
|
|
||||||
/// Reverses the foreground and background colors of all cells.
|
|
||||||
reverse_colors = 5,
|
|
||||||
|
|
||||||
/// If set, the origin of the coordinate system is relative to the
|
|
||||||
/// current scroll region. If set the cursor is moved to the top left of
|
|
||||||
/// the current scroll region.
|
|
||||||
origin = 6,
|
|
||||||
|
|
||||||
/// Enable or disable automatic line wrapping.
|
|
||||||
autowrap = 7,
|
|
||||||
|
|
||||||
/// Click-only (press) mouse reporting.
|
|
||||||
mouse_event_x10 = 9,
|
|
||||||
|
|
||||||
/// Set whether the cursor is visible or not.
|
|
||||||
cursor_visible = 25,
|
|
||||||
|
|
||||||
/// Enables or disables mode ?3. If disabled, the terminal will resize
|
|
||||||
/// to the size of the window. If enabled, this will take effect when
|
|
||||||
/// mode ?3 is set or unset.
|
|
||||||
enable_mode_3 = 40,
|
|
||||||
|
|
||||||
/// DECNKM. Sets application keypad mode if enabled.
|
|
||||||
keypad_keys = 66,
|
|
||||||
|
|
||||||
/// "Normal" mouse events: click/release, scroll
|
|
||||||
mouse_event_normal = 1000,
|
|
||||||
|
|
||||||
/// Same as normal mode but also send events for mouse motion
|
|
||||||
/// while the button is pressed when the cell in the grid changes.
|
|
||||||
mouse_event_button = 1002,
|
|
||||||
|
|
||||||
/// Same as button mode but doesn't require a button to be pressed
|
|
||||||
/// to track mouse movement.
|
|
||||||
mouse_event_any = 1003,
|
|
||||||
|
|
||||||
/// Send focus in/out events.
|
|
||||||
focus_event = 1004,
|
|
||||||
|
|
||||||
/// Report mouse position in the utf8 format to support larger screens.
|
|
||||||
mouse_format_utf8 = 1005,
|
|
||||||
|
|
||||||
/// Report mouse position in the SGR format.
|
|
||||||
mouse_format_sgr = 1006,
|
|
||||||
|
|
||||||
/// Report mouse scroll events as cursor up/down keys. Any other mouse
|
|
||||||
/// mode overrides this.
|
|
||||||
mouse_alternate_scroll = 1007,
|
|
||||||
|
|
||||||
/// Report mouse position in the urxvt format.
|
|
||||||
mouse_format_urxvt = 1015,
|
|
||||||
|
|
||||||
/// Report mouse position in the SGR format as pixels, instead of cells.
|
|
||||||
mouse_format_sgr_pixels = 1016,
|
|
||||||
|
|
||||||
/// The alt key sends esc as a prefix before any character. On by default.
|
|
||||||
alt_esc_prefix = 1036,
|
|
||||||
|
|
||||||
/// altSendsEscape xterm (https://invisible-island.net/xterm/manpage/xterm.html)
|
|
||||||
alt_sends_escape = 1039,
|
|
||||||
|
|
||||||
/// Alternate screen mode with save cursor and clear on enter.
|
|
||||||
alt_screen_save_cursor_clear_enter = 1049,
|
|
||||||
|
|
||||||
/// Bracket clipboard paste contents in delimiter sequences.
|
|
||||||
///
|
|
||||||
/// When pasting from the (e.g. system) clipboard add "ESC [ 2 0 0 ~"
|
|
||||||
/// before the clipboard contents and "ESC [ 2 0 1 ~" after the clipboard
|
|
||||||
/// contents. This allows applications to distinguish clipboard contents
|
|
||||||
/// from manually typed text.
|
|
||||||
bracketed_paste = 2004,
|
|
||||||
|
|
||||||
// Non-exhaustive so that @intToEnum never fails for unsupported modes.
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The device attribute request type (ESC [ c).
|
/// The device attribute request type (ESC [ c).
|
||||||
pub const DeviceAttributeReq = enum {
|
pub const DeviceAttributeReq = enum {
|
||||||
primary, // Blank
|
primary, // Blank
|
||||||
|
@ -7,6 +7,7 @@ const csi = @import("csi.zig");
|
|||||||
const sgr = @import("sgr.zig");
|
const sgr = @import("sgr.zig");
|
||||||
pub const point = @import("point.zig");
|
pub const point = @import("point.zig");
|
||||||
pub const color = @import("color.zig");
|
pub const color = @import("color.zig");
|
||||||
|
pub const modes = @import("modes.zig");
|
||||||
pub const parse_table = @import("parse_table.zig");
|
pub const parse_table = @import("parse_table.zig");
|
||||||
|
|
||||||
pub const Charset = charsets.Charset;
|
pub const Charset = charsets.Charset;
|
||||||
@ -20,7 +21,7 @@ pub const Stream = stream.Stream;
|
|||||||
pub const CursorStyle = ansi.CursorStyle;
|
pub const CursorStyle = ansi.CursorStyle;
|
||||||
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
|
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
|
||||||
pub const DeviceStatusReq = ansi.DeviceStatusReq;
|
pub const DeviceStatusReq = ansi.DeviceStatusReq;
|
||||||
pub const Mode = ansi.Mode;
|
pub const Mode = modes.Mode;
|
||||||
pub const ModifyKeyFormat = ansi.ModifyKeyFormat;
|
pub const ModifyKeyFormat = ansi.ModifyKeyFormat;
|
||||||
pub const StatusLineType = ansi.StatusLineType;
|
pub const StatusLineType = ansi.StatusLineType;
|
||||||
pub const StatusDisplay = ansi.StatusDisplay;
|
pub const StatusDisplay = ansi.StatusDisplay;
|
||||||
@ -36,5 +37,4 @@ pub usingnamespace if (builtin.target.isWasm()) struct {
|
|||||||
|
|
||||||
test {
|
test {
|
||||||
@import("std").testing.refAllDecls(@This());
|
@import("std").testing.refAllDecls(@This());
|
||||||
_ = @import("modes.zig");
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ const Parser = @import("Parser.zig");
|
|||||||
const ansi = @import("ansi.zig");
|
const ansi = @import("ansi.zig");
|
||||||
const charsets = @import("charsets.zig");
|
const charsets = @import("charsets.zig");
|
||||||
const csi = @import("csi.zig");
|
const csi = @import("csi.zig");
|
||||||
|
const modes = @import("modes.zig");
|
||||||
const osc = @import("osc.zig");
|
const osc = @import("osc.zig");
|
||||||
const sgr = @import("sgr.zig");
|
const sgr = @import("sgr.zig");
|
||||||
const trace = @import("tracy").trace;
|
const trace = @import("tracy").trace;
|
||||||
@ -426,14 +427,30 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
|
|
||||||
// SM - Set Mode
|
// SM - Set Mode
|
||||||
'h' => if (@hasDecl(T, "setMode")) {
|
'h' => if (@hasDecl(T, "setMode")) {
|
||||||
for (action.params) |mode|
|
for (action.params) |mode| {
|
||||||
try self.handler.setMode(@enumFromInt(mode), true);
|
if (modes.hasSupport(mode)) {
|
||||||
|
try self.handler.setMode(
|
||||||
|
@enumFromInt(mode),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn("unimplemented mode: {}", .{mode});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||||
|
|
||||||
// RM - Reset Mode
|
// RM - Reset Mode
|
||||||
'l' => if (@hasDecl(T, "setMode")) {
|
'l' => if (@hasDecl(T, "setMode")) {
|
||||||
for (action.params) |mode|
|
for (action.params) |mode| {
|
||||||
try self.handler.setMode(@enumFromInt(mode), false);
|
if (modes.hasSupport(mode)) {
|
||||||
|
try self.handler.setMode(
|
||||||
|
@enumFromInt(mode),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn("unimplemented mode: {}", .{mode});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||||
|
|
||||||
// SGR - Select Graphic Rendition
|
// SGR - Select Graphic Rendition
|
||||||
@ -896,20 +913,19 @@ test "stream: cursor right (CUF)" {
|
|||||||
|
|
||||||
test "stream: set mode (SM) and reset mode (RM)" {
|
test "stream: set mode (SM) and reset mode (RM)" {
|
||||||
const H = struct {
|
const H = struct {
|
||||||
mode: ansi.Mode = @as(ansi.Mode, @enumFromInt(0)),
|
mode: modes.Mode = @as(modes.Mode, @enumFromInt(1)),
|
||||||
|
pub fn setMode(self: *@This(), mode: modes.Mode, v: bool) !void {
|
||||||
pub fn setMode(self: *@This(), mode: ansi.Mode, v: bool) !void {
|
self.mode = @as(modes.Mode, @enumFromInt(1));
|
||||||
self.mode = @as(ansi.Mode, @enumFromInt(0));
|
|
||||||
if (v) self.mode = mode;
|
if (v) self.mode = mode;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var s: Stream(H) = .{ .handler = .{} };
|
var s: Stream(H) = .{ .handler = .{} };
|
||||||
try s.nextSlice("\x1B[?6h");
|
try s.nextSlice("\x1B[?6h");
|
||||||
try testing.expectEqual(@as(ansi.Mode, .origin), s.handler.mode);
|
try testing.expectEqual(@as(modes.Mode, .origin), s.handler.mode);
|
||||||
|
|
||||||
try s.nextSlice("\x1B[?6l");
|
try s.nextSlice("\x1B[?6l");
|
||||||
try testing.expectEqual(@as(ansi.Mode, @enumFromInt(0)), s.handler.mode);
|
try testing.expectEqual(@as(modes.Mode, @enumFromInt(1)), s.handler.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "stream: restore mode" {
|
test "stream: restore mode" {
|
||||||
|
@ -1119,7 +1119,7 @@ const StreamHandler = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setCursorRow(self: *StreamHandler, row: u16) !void {
|
pub fn setCursorRow(self: *StreamHandler, row: u16) !void {
|
||||||
if (self.terminal.modes.origin) {
|
if (self.terminal.modes.get(.origin)) {
|
||||||
// TODO
|
// TODO
|
||||||
log.err("setCursorRow: implement origin mode", .{});
|
log.err("setCursorRow: implement origin mode", .{});
|
||||||
unreachable;
|
unreachable;
|
||||||
@ -1184,10 +1184,10 @@ const StreamHandler = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
|
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
|
||||||
self.terminal.modes.modify_other_keys = false;
|
self.terminal.flags.modify_other_keys_2 = false;
|
||||||
switch (format) {
|
switch (format) {
|
||||||
.other_keys => |v| switch (v) {
|
.other_keys => |v| switch (v) {
|
||||||
.numeric => self.terminal.modes.modify_other_keys = true,
|
.numeric => self.terminal.flags.modify_other_keys_2 = true,
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
@ -1199,38 +1199,19 @@ const StreamHandler = struct {
|
|||||||
// terminal locks because it is only called from process() which
|
// terminal locks because it is only called from process() which
|
||||||
// grabs the lock.
|
// grabs the lock.
|
||||||
|
|
||||||
|
// We first always set the raw mode on our mode state.
|
||||||
|
self.terminal.modes.set(mode, enabled);
|
||||||
|
|
||||||
|
// And then some modes require additional processing.
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
.cursor_keys => {
|
// Schedule a render since we changed colors
|
||||||
self.terminal.modes.cursor_keys = enabled;
|
.reverse_colors => try self.queueRender(),
|
||||||
},
|
|
||||||
|
|
||||||
.keypad_keys => {
|
// Origin resets cursor pos
|
||||||
self.terminal.modes.keypad_keys = enabled;
|
.origin => self.terminal.setCursorPos(1, 1),
|
||||||
},
|
|
||||||
|
|
||||||
.insert => {
|
// We need to update our renderer state for this mode
|
||||||
self.terminal.modes.insert = enabled;
|
.cursor_visible => self.ev.renderer_state.cursor.visible = enabled,
|
||||||
},
|
|
||||||
|
|
||||||
.reverse_colors => {
|
|
||||||
self.terminal.modes.reverse_colors = enabled;
|
|
||||||
|
|
||||||
// Schedule a render since we changed colors
|
|
||||||
try self.queueRender();
|
|
||||||
},
|
|
||||||
|
|
||||||
.origin => {
|
|
||||||
self.terminal.modes.origin = enabled;
|
|
||||||
self.terminal.setCursorPos(1, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
.autowrap => {
|
|
||||||
self.terminal.modes.autowrap = enabled;
|
|
||||||
},
|
|
||||||
|
|
||||||
.cursor_visible => {
|
|
||||||
self.ev.renderer_state.cursor.visible = enabled;
|
|
||||||
},
|
|
||||||
|
|
||||||
.alt_screen_save_cursor_clear_enter => {
|
.alt_screen_save_cursor_clear_enter => {
|
||||||
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
||||||
@ -1247,15 +1228,13 @@ const StreamHandler = struct {
|
|||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
},
|
},
|
||||||
|
|
||||||
.bracketed_paste => self.terminal.modes.bracketed_paste = enabled,
|
// Force resize back to the window size
|
||||||
|
.enable_mode_3 => self.terminal.resize(
|
||||||
.enable_mode_3 => {
|
self.alloc,
|
||||||
// Disable deccolm
|
self.grid_size.columns,
|
||||||
self.terminal.setDeccolmSupported(enabled);
|
self.grid_size.rows,
|
||||||
|
) catch |err| {
|
||||||
// Force resize back to the window size
|
log.err("error updating terminal size: {}", .{err});
|
||||||
self.terminal.resize(self.alloc, self.grid_size.columns, self.grid_size.rows) catch |err|
|
|
||||||
log.err("error updating terminal size: {}", .{err});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.@"132_column" => try self.terminal.deccolm(
|
.@"132_column" => try self.terminal.deccolm(
|
||||||
@ -1263,21 +1242,17 @@ const StreamHandler = struct {
|
|||||||
if (enabled) .@"132_cols" else .@"80_cols",
|
if (enabled) .@"132_cols" else .@"80_cols",
|
||||||
),
|
),
|
||||||
|
|
||||||
.mouse_event_x10 => self.terminal.modes.mouse_event = if (enabled) .x10 else .none,
|
.mouse_event_x10 => self.terminal.flags.mouse_event = if (enabled) .x10 else .none,
|
||||||
.mouse_event_normal => self.terminal.modes.mouse_event = if (enabled) .normal else .none,
|
.mouse_event_normal => self.terminal.flags.mouse_event = if (enabled) .normal else .none,
|
||||||
.mouse_event_button => self.terminal.modes.mouse_event = if (enabled) .button else .none,
|
.mouse_event_button => self.terminal.flags.mouse_event = if (enabled) .button else .none,
|
||||||
.mouse_event_any => self.terminal.modes.mouse_event = if (enabled) .any else .none,
|
.mouse_event_any => self.terminal.flags.mouse_event = if (enabled) .any else .none,
|
||||||
|
|
||||||
.mouse_format_utf8 => self.terminal.modes.mouse_format = if (enabled) .utf8 else .x10,
|
.mouse_format_utf8 => self.terminal.flags.mouse_format = if (enabled) .utf8 else .x10,
|
||||||
.mouse_format_sgr => self.terminal.modes.mouse_format = if (enabled) .sgr else .x10,
|
.mouse_format_sgr => self.terminal.flags.mouse_format = if (enabled) .sgr else .x10,
|
||||||
.mouse_format_urxvt => self.terminal.modes.mouse_format = if (enabled) .urxvt else .x10,
|
.mouse_format_urxvt => self.terminal.flags.mouse_format = if (enabled) .urxvt else .x10,
|
||||||
.mouse_format_sgr_pixels => self.terminal.modes.mouse_format = if (enabled) .sgr_pixels else .x10,
|
.mouse_format_sgr_pixels => self.terminal.flags.mouse_format = if (enabled) .sgr_pixels else .x10,
|
||||||
|
|
||||||
.mouse_alternate_scroll => self.terminal.modes.mouse_alternate_scroll = enabled,
|
else => {},
|
||||||
.focus_event => self.terminal.modes.focus_event = enabled,
|
|
||||||
.alt_esc_prefix => self.terminal.modes.alt_esc_prefix = enabled,
|
|
||||||
|
|
||||||
else => if (enabled) log.warn("unimplemented mode: {}", .{mode}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1323,7 +1298,7 @@ const StreamHandler = struct {
|
|||||||
const pos: struct {
|
const pos: struct {
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
} = if (self.terminal.modes.origin) .{
|
} = if (self.terminal.modes.get(.origin)) .{
|
||||||
// TODO: what do we do if cursor is outside scrolling region?
|
// TODO: what do we do if cursor is outside scrolling region?
|
||||||
.x = self.terminal.screen.cursor.x,
|
.x = self.terminal.screen.cursor.x,
|
||||||
.y = self.terminal.screen.cursor.y -| self.terminal.scrolling_region.top,
|
.y = self.terminal.screen.cursor.y -| self.terminal.scrolling_region.top,
|
||||||
@ -1464,7 +1439,7 @@ const StreamHandler = struct {
|
|||||||
pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void {
|
pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void {
|
||||||
_ = aid;
|
_ = aid;
|
||||||
self.terminal.markSemanticPrompt(.prompt);
|
self.terminal.markSemanticPrompt(.prompt);
|
||||||
self.terminal.modes.shell_redraws_prompt = redraw;
|
self.terminal.flags.shell_redraws_prompt = redraw;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn promptEnd(self: *StreamHandler) !void {
|
pub fn promptEnd(self: *StreamHandler) !void {
|
||||||
|
Reference in New Issue
Block a user