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);
|
||||
},
|
||||
|
||||
.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,
|
||||
|
||||
.enable_mode_3 => self.terminal.modes.enable_mode_3 = @boolToInt(enabled),
|
||||
|
@ -22,8 +22,18 @@ const log = std.log.scoped(.terminal);
|
||||
/// Default tabstop interval
|
||||
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,
|
||||
secondary_screen: Screen,
|
||||
|
||||
/// Where the tabstops are.
|
||||
tabstops: Tabstops,
|
||||
@ -60,8 +70,11 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
|
||||
return Terminal{
|
||||
.cols = cols,
|
||||
.rows = rows,
|
||||
.active_screen = .primary,
|
||||
// TODO: configurable scrollback
|
||||
.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),
|
||||
.scrolling_region = .{
|
||||
.top = 0,
|
||||
@ -73,9 +86,62 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
|
||||
pub fn deinit(self: *Terminal, alloc: Allocator) void {
|
||||
self.tabstops.deinit(alloc);
|
||||
self.screen.deinit(alloc);
|
||||
self.secondary_screen.deinit(alloc);
|
||||
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.
|
||||
pub fn resize(self: *Terminal, alloc: Allocator, cols: usize, rows: usize) !void {
|
||||
// TODO: test, wrapping, etc.
|
||||
|
@ -63,6 +63,9 @@ pub const Mode = enum(u16) {
|
||||
/// mode ?3 is set or unset.
|
||||
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.
|
||||
///
|
||||
/// 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});
|
||||
const actions = self.parser.next(c);
|
||||
for (actions) |action_opt| {
|
||||
if (action_opt) |action| {
|
||||
if (action != .print) {
|
||||
log.info("action: {}", .{action});
|
||||
}
|
||||
}
|
||||
// if (action_opt) |action| {
|
||||
// if (action != .print) {
|
||||
// log.info("action: {}", .{action});
|
||||
// }
|
||||
// }
|
||||
switch (action_opt orelse continue) {
|
||||
.print => |p| if (@hasDecl(T, "print")) try self.handler.print(p),
|
||||
.execute => |code| try self.execute(code),
|
||||
|
Reference in New Issue
Block a user