mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
alternate screen buffer (mode 1049) now supported
This commit is contained in:
@ -775,6 +775,21 @@ pub fn setMode(self: *Window, mode: terminal.Mode, enabled: bool) !void {
|
|||||||
self.terminal.modes.autowrap = @boolToInt(enabled);
|
self.terminal.modes.autowrap = @boolToInt(enabled);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.alt_screen_save_cursor_clear_enter => {
|
||||||
|
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
||||||
|
.cursor_save = true,
|
||||||
|
.clear_on_enter = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
self.terminal.alternateScreen(opts)
|
||||||
|
else
|
||||||
|
self.terminal.primaryScreen(opts);
|
||||||
|
|
||||||
|
// Schedule a render since we changed screens
|
||||||
|
try self.render_timer.schedule();
|
||||||
|
},
|
||||||
|
|
||||||
.bracketed_paste => self.bracketed_paste = true,
|
.bracketed_paste => self.bracketed_paste = true,
|
||||||
|
|
||||||
.enable_mode_3 => self.terminal.modes.enable_mode_3 = @boolToInt(enabled),
|
.enable_mode_3 => self.terminal.modes.enable_mode_3 = @boolToInt(enabled),
|
||||||
|
@ -22,8 +22,18 @@ const log = std.log.scoped(.terminal);
|
|||||||
/// Default tabstop interval
|
/// Default tabstop interval
|
||||||
const TABSTOP_INTERVAL = 8;
|
const TABSTOP_INTERVAL = 8;
|
||||||
|
|
||||||
/// Screen is the current screen state.
|
/// Screen type is an enum that tracks whether a screen is primary or alternate.
|
||||||
|
const ScreenType = enum {
|
||||||
|
primary,
|
||||||
|
alternate,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Screen is the current screen state. The "active_screen" field says what
|
||||||
|
/// the current screen is. The backup screen is the opposite of the active
|
||||||
|
/// screen.
|
||||||
|
active_screen: ScreenType,
|
||||||
screen: Screen,
|
screen: Screen,
|
||||||
|
secondary_screen: Screen,
|
||||||
|
|
||||||
/// Where the tabstops are.
|
/// Where the tabstops are.
|
||||||
tabstops: Tabstops,
|
tabstops: Tabstops,
|
||||||
@ -60,8 +70,11 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
|
|||||||
return Terminal{
|
return Terminal{
|
||||||
.cols = cols,
|
.cols = cols,
|
||||||
.rows = rows,
|
.rows = rows,
|
||||||
|
.active_screen = .primary,
|
||||||
// TODO: configurable scrollback
|
// TODO: configurable scrollback
|
||||||
.screen = try Screen.init(alloc, rows, cols, 1000),
|
.screen = try Screen.init(alloc, rows, cols, 1000),
|
||||||
|
// No scrollback for the alternate screen
|
||||||
|
.secondary_screen = try Screen.init(alloc, rows, cols, 0),
|
||||||
.tabstops = try Tabstops.init(alloc, cols, TABSTOP_INTERVAL),
|
.tabstops = try Tabstops.init(alloc, cols, TABSTOP_INTERVAL),
|
||||||
.scrolling_region = .{
|
.scrolling_region = .{
|
||||||
.top = 0,
|
.top = 0,
|
||||||
@ -73,9 +86,62 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
|
|||||||
pub fn deinit(self: *Terminal, alloc: Allocator) void {
|
pub fn deinit(self: *Terminal, alloc: Allocator) void {
|
||||||
self.tabstops.deinit(alloc);
|
self.tabstops.deinit(alloc);
|
||||||
self.screen.deinit(alloc);
|
self.screen.deinit(alloc);
|
||||||
|
self.secondary_screen.deinit(alloc);
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options for switching to the alternate screen.
|
||||||
|
pub const AlternateScreenOptions = struct {
|
||||||
|
cursor_save: bool = false,
|
||||||
|
clear_on_enter: bool = false,
|
||||||
|
clear_on_exit: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Switch to the alternate screen buffer.
|
||||||
|
///
|
||||||
|
/// The alternate screen buffer:
|
||||||
|
/// * has its own grid
|
||||||
|
/// * has its own cursor state (included saved cursor)
|
||||||
|
/// * does not support scrollback
|
||||||
|
///
|
||||||
|
pub fn alternateScreen(self: *Terminal, options: AlternateScreenOptions) void {
|
||||||
|
// TODO: test
|
||||||
|
// TODO(mitchellh): what happens if we enter alternate screen multiple times?
|
||||||
|
// for now, we ignore...
|
||||||
|
if (self.active_screen == .alternate) return;
|
||||||
|
|
||||||
|
// If we requested cursor save, we save the cursor in the primary screen
|
||||||
|
if (options.cursor_save) self.saveCursor();
|
||||||
|
|
||||||
|
// Switch the screens
|
||||||
|
const old = self.screen;
|
||||||
|
self.screen = self.secondary_screen;
|
||||||
|
self.secondary_screen = old;
|
||||||
|
self.active_screen = .alternate;
|
||||||
|
|
||||||
|
if (options.clear_on_enter) {
|
||||||
|
self.eraseDisplay(.complete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switch back to the primary screen (reset alternate screen mode).
|
||||||
|
pub fn primaryScreen(self: *Terminal, options: AlternateScreenOptions) void {
|
||||||
|
// TODO: test
|
||||||
|
// TODO(mitchellh): what happens if we enter alternate screen multiple times?
|
||||||
|
if (self.active_screen == .primary) return;
|
||||||
|
|
||||||
|
if (options.clear_on_exit) self.eraseDisplay(.complete);
|
||||||
|
|
||||||
|
// Switch the screens
|
||||||
|
const old = self.screen;
|
||||||
|
self.screen = self.secondary_screen;
|
||||||
|
self.secondary_screen = old;
|
||||||
|
self.active_screen = .primary;
|
||||||
|
|
||||||
|
// Restore the cursor from the primary screen
|
||||||
|
if (options.cursor_save) self.restoreCursor();
|
||||||
|
}
|
||||||
|
|
||||||
/// Resize the underlying terminal.
|
/// Resize the underlying terminal.
|
||||||
pub fn resize(self: *Terminal, alloc: Allocator, cols: usize, rows: usize) !void {
|
pub fn resize(self: *Terminal, alloc: Allocator, cols: usize, rows: usize) !void {
|
||||||
// TODO: test, wrapping, etc.
|
// TODO: test, wrapping, etc.
|
||||||
|
@ -63,6 +63,9 @@ pub const Mode = enum(u16) {
|
|||||||
/// mode ?3 is set or unset.
|
/// mode ?3 is set or unset.
|
||||||
enable_mode_3 = 40,
|
enable_mode_3 = 40,
|
||||||
|
|
||||||
|
/// Alternate screen mode with save cursor and clear on enter.
|
||||||
|
alt_screen_save_cursor_clear_enter = 1049,
|
||||||
|
|
||||||
/// Bracket clipboard paste contents in delimiter sequences.
|
/// Bracket clipboard paste contents in delimiter sequences.
|
||||||
///
|
///
|
||||||
/// When pasting from the (e.g. system) clipboard add "ESC [ 2 0 0 ~"
|
/// When pasting from the (e.g. system) clipboard add "ESC [ 2 0 0 ~"
|
||||||
|
@ -48,11 +48,11 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
//log.debug("char: {x}", .{c});
|
//log.debug("char: {x}", .{c});
|
||||||
const actions = self.parser.next(c);
|
const actions = self.parser.next(c);
|
||||||
for (actions) |action_opt| {
|
for (actions) |action_opt| {
|
||||||
if (action_opt) |action| {
|
// if (action_opt) |action| {
|
||||||
if (action != .print) {
|
// if (action != .print) {
|
||||||
log.info("action: {}", .{action});
|
// log.info("action: {}", .{action});
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
switch (action_opt orelse continue) {
|
switch (action_opt orelse continue) {
|
||||||
.print => |p| if (@hasDecl(T, "print")) try self.handler.print(p),
|
.print => |p| if (@hasDecl(T, "print")) try self.handler.print(p),
|
||||||
.execute => |code| try self.execute(code),
|
.execute => |code| try self.execute(code),
|
||||||
|
Reference in New Issue
Block a user