From 2ad3a7c5ccca9992944735cc52f5e157518f1149 Mon Sep 17 00:00:00 2001 From: corentin-soriano Date: Thu, 30 May 2024 18:42:27 +0200 Subject: [PATCH] GUACAMOLE-1633: Add support of alternate screen buffer --- src/terminal/terminal-handlers.c | 13 +++++ src/terminal/terminal.c | 73 ++++++++++++++++++++++++++- src/terminal/terminal/terminal-priv.h | 45 +++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/terminal/terminal-handlers.c b/src/terminal/terminal-handlers.c index 1cff2d8d6..e6cfacaa4 100644 --- a/src/terminal/terminal-handlers.c +++ b/src/terminal/terminal-handlers.c @@ -50,6 +50,11 @@ */ #define GUAC_TERMINAL_OK "\x1B[0n" +/** + * Alternative buffer CSI sequence. + */ +#define GUAC_TERMINAL_ALT_BUFFER 1049 + /** * Advances the cursor to the next row, scrolling if the cursor would otherwise * leave the scrolling region. If the cursor is already outside the scrolling @@ -885,6 +890,10 @@ int guac_terminal_csi(guac_terminal* term, unsigned char c) { if (flag != NULL) *flag = true; + /* Open alternate screen buffer */ + if (argv[0] == GUAC_TERMINAL_ALT_BUFFER) + guac_terminal_switch_buffers(term, true); + break; /* l: Reset Mode */ @@ -894,6 +903,10 @@ int guac_terminal_csi(guac_terminal* term, unsigned char c) { flag = __guac_terminal_get_flag(term, argv[0], private_mode_character); if (flag != NULL) *flag = false; + + /* Close alternate screen buffer */ + if (argv[0] == GUAC_TERMINAL_ALT_BUFFER) + guac_terminal_switch_buffers(term, false); break; diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 83ac15dfd..d9f811804 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -482,9 +482,11 @@ guac_terminal* guac_terminal_create(guac_client* client, if (initial_scrollback < GUAC_TERMINAL_MAX_ROWS) initial_scrollback = GUAC_TERMINAL_MAX_ROWS; - /* Init buffer */ + /* Init current and alternate buffer */ term->buffer = guac_terminal_buffer_alloc(initial_scrollback, &default_char); + term->buffer_alt = NULL; + term->buffer_switched = false; /* Init display */ term->display = guac_terminal_display_alloc(client, @@ -633,8 +635,10 @@ void guac_terminal_free(guac_terminal* term) { /* Free display */ guac_terminal_display_free(term->display); - /* Free buffer */ + /* Free buffers */ guac_terminal_buffer_free(term->buffer); + if (term->buffer_alt != NULL) + guac_terminal_buffer_free(term->buffer_alt); /* Free scrollbar */ guac_terminal_scrollbar_free(term->scrollbar); @@ -2384,3 +2388,68 @@ void guac_terminal_remove_user(guac_terminal* terminal, guac_user* user) { /* Remove the user from the terminal cursor */ guac_common_cursor_remove_user(terminal->cursor, user); } + +void guac_terminal_switch_buffers(guac_terminal* terminal, bool to_alt) { + + /* Already on requested buffer */ + if (terminal->buffer_switched == to_alt) + return; + + /* Allocate alternate buffer */ + if (terminal->buffer_alt == NULL) + terminal->buffer_alt = guac_terminal_buffer_alloc( + terminal->display->height, &terminal->default_char); + + /* Keep new buffer state */ + terminal->buffer_switched = to_alt; + + /* Inversion of buffers pointers to switch to alternate */ + guac_terminal_buffer* temp_buffer = terminal->buffer; + terminal->buffer = terminal->buffer_alt; + terminal->buffer_alt = temp_buffer; + + /* Switch to alternate buffer */ + if (to_alt) { + + /* Backup cursor position before switching alternate buffer */ + terminal->visible_cursor_col_alt = terminal->visible_cursor_col; + terminal->visible_cursor_row_alt = terminal->visible_cursor_row; + terminal->cursor_col_alt = terminal->cursor_col; + terminal->cursor_row_alt = terminal->cursor_row; + + /* Clear screen content and selection */ + guac_terminal_reset(terminal); + + } + + /* Switch to normal buffer */ + else { + + /* Restore cursor position before switching normal buffer */ + terminal->visible_cursor_col = terminal->visible_cursor_col_alt; + terminal->visible_cursor_row = terminal->visible_cursor_row_alt; + terminal->cursor_col = terminal->cursor_col_alt; + terminal->cursor_row = terminal->cursor_row_alt; + + /* Repaint and resize overall display */ + guac_terminal_repaint_default_layer(terminal, terminal->client->socket); + __guac_terminal_redraw_rect(terminal, 0, 0, + terminal->term_height - 1, + terminal->term_width - 1); + + /* Restore scrollbar state */ + guac_terminal_scrollbar_set_bounds(terminal->scrollbar, + -guac_terminal_get_available_scroll(terminal), 0); + + /* Clear selection */ + terminal->text_selected = false; + terminal->selection_committed = false; + guac_terminal_notify(terminal); + + /* Free alternate buffer when unused */ + guac_terminal_buffer_free(terminal->buffer_alt); + terminal->buffer_alt = NULL; + + } + +} diff --git a/src/terminal/terminal/terminal-priv.h b/src/terminal/terminal/terminal-priv.h index 1098c141a..cf9eb14c8 100644 --- a/src/terminal/terminal/terminal-priv.h +++ b/src/terminal/terminal/terminal-priv.h @@ -243,6 +243,11 @@ struct guac_terminal { */ int cursor_row; + /** + * Backup of cursor_row when using alternate buffer. + */ + int cursor_row_alt; + /** * The current column location of the cursor. Note that while most * terminal operations will clip the cursor location within the bounds @@ -252,6 +257,11 @@ struct guac_terminal { */ int cursor_col; + /** + * Backup of cursor_col when using alternate buffer. + */ + int cursor_col_alt; + /** * The desired visibility state of the cursor. */ @@ -263,12 +273,22 @@ struct guac_terminal { */ int visible_cursor_row; + /** + * Backup of visible_cursor_row when using alternate buffer. + */ + int visible_cursor_row_alt; + /** * The column of the rendered cursor. * Will be set to -1 if the cursor is not visible. */ int visible_cursor_col; + /** + * Backup of visible_cursor_col when using alternate buffer. + */ + int visible_cursor_col_alt; + /** * The row of the saved cursor (ESC 7). */ @@ -311,6 +331,18 @@ struct guac_terminal { */ guac_terminal_buffer* buffer; + /** + * Alternate buffer. + */ + guac_terminal_buffer* buffer_alt; + + /** + * Actual state of the buffer: + * - true if switched to alternate buffer. + * - false if normal buffer. + */ + bool buffer_switched; + /** * Automatically place a tabstop every N characters. If zero, then no * tabstops exist automatically. @@ -666,5 +698,18 @@ void guac_terminal_copy_rows(guac_terminal* terminal, */ void guac_terminal_flush(guac_terminal* terminal); +/** + * Swith betwen normal and alternate buffer. + * + * @param terminal + * Terminal on which we switch buffers. + * + * @param to_alt + * Direction of buffer inversion. + * True if normal to alternate buffer. + * False if alternate to normal buffer. + */ +void guac_terminal_switch_buffers(guac_terminal* terminal, bool to_alt); + #endif