diff --git a/protocols/ssh/include/buffer.h b/protocols/ssh/include/buffer.h index 7a4299e7..83c05cce 100644 --- a/protocols/ssh/include/buffer.h +++ b/protocols/ssh/include/buffer.h @@ -112,6 +112,12 @@ void guac_terminal_buffer_free(guac_terminal_buffer* buffer); */ guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row); +/** + * Ensures the given row has at least the given number of character spaces available. If new characters + * must be added, they are initialized with the given fill character. + */ +void guac_terminal_buffer_prepare_row(guac_terminal_buffer_row* row, int width, guac_terminal_char* fill); + /** * Copies the given range of columns to a new location, offset from * the original by the given number of columns. @@ -123,7 +129,7 @@ void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int src_row, int rows, +void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int start_row, int end_row, int offset); /** diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 227b6792..7c01825e 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -204,7 +204,7 @@ void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_display_copy_rows(guac_terminal_display* display, int src_row, int rows, +void guac_terminal_display_copy_rows(guac_terminal_display* display, int start_row, int end_row, int offset); /** diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 6b9ecae8..993be802 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -118,10 +118,11 @@ struct guac_terminal { guac_terminal_attributes current_attributes; /** - * The attributes which will be applied to characters by default, unless - * other attributes are explicitly specified. + * The character whose attributes dictate the default attributes + * of all characters. When new screen space is allocated, this + * character fills the gaps. */ - guac_terminal_attributes default_attributes; + guac_terminal_char default_char; /** * Handler which will receive all printed characters, updating the terminal @@ -192,11 +193,10 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size); int guac_terminal_set(guac_terminal* term, int row, int col, char c); /** - * Clears a rectangular region of characters, replacing them with the - * current background color and attributes. + * Clears the given region within a single row. */ -int guac_terminal_clear(guac_terminal* term, - int row, int col, int rows, int cols); +int guac_terminal_clear_columns(guac_terminal* term, + int row, int start_col, int end_col); /** * Clears the given region from right-to-left, top-to-bottom, replacing @@ -265,7 +265,7 @@ void guac_terminal_copy_columns(guac_terminal* terminal, int row, * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_copy_rows(guac_terminal* terminal, int src_row, int rows, +void guac_terminal_copy_rows(guac_terminal* terminal, int start_row, int end_row, int offset); /** diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index f6df30e9..bfc3951c 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -101,13 +101,36 @@ guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buf } +void guac_terminal_buffer_prepare_row(guac_terminal_buffer_row* row, int width, guac_terminal_char* fill) { + + int i; + guac_terminal_char* first; + + /* If already wide enough, nothing to do. */ + if (width < row->length) + return; + + /* Expand if necessary */ + if (width > row->available) { + row->available = width*2; + row->characters = realloc(row->characters, sizeof(guac_terminal_char) * row->available); + } + + /* Initialize new part of row */ + first = &(row->characters[row->length]); + for (i=row->length; ilength = width; + +} void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, int start_column, int end_column, int offset) { /* STUB */ } -void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int src_row, int rows, +void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int start_row, int end_row, int offset) { /* STUB */ } diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index e1f24aaa..91561b90 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -344,16 +344,25 @@ void guac_terminal_display_free(guac_terminal_display* display) { void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, int start_column, int end_column, int offset) { /* STUB */ + guac_client_log_info(display->client, + "display_copy_columns: row=%i, start=%i, end=%i, offset=%i", + row, start_column, end_column, offset); } -void guac_terminal_display_copy_rows(guac_terminal_display* display, int src_row, int rows, +void guac_terminal_display_copy_rows(guac_terminal_display* display, int start_row, int end_row, int offset) { /* STUB */ + guac_client_log_info(display->client, + "display_copy_rows: start=%i, end=%i, offset=%i", + start_row, end_row, offset); } void guac_terminal_display_set_columns(guac_terminal_display* display, int row, int start_column, int end_column, guac_terminal_char* character) { /* STUB */ + guac_client_log_info(display->client, + "display_set_columns: row=%i, start=%i, end=%i, char='%c'", + row, start_column, end_column, character->value); } void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 0651ce19..0ce482ef 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -56,13 +56,15 @@ guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { - guac_terminal_attributes default_attributes = { - .foreground = 7, - .background = 0, - .bold = false, - .reverse = false, - .underscore = false - }; + guac_terminal_char default_char = { + .value = ' ', + .attributes = { + .foreground = 7, + .background = 0, + .bold = false, + .reverse = false, + .underscore = false + }}; guac_terminal* term = malloc(sizeof(guac_terminal)); term->client = client; @@ -74,12 +76,12 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Init display */ term->display = guac_terminal_display_alloc(client, term->term_width, term->term_height, - default_attributes.foreground, - default_attributes.background); + default_char.attributes.foreground, + default_char.attributes.background); /* Init terminal state */ - term->current_attributes = - term->default_attributes = default_attributes; + term->current_attributes = default_char.attributes; + term->default_char = default_char; term->cursor_row = 0; term->cursor_col = 0; @@ -93,10 +95,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->text_selected = false; - /* Clear with background color */ - guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width); - /* Init terminal lock */ pthread_mutex_init(&(term->lock), NULL); @@ -116,19 +114,12 @@ void guac_terminal_free(guac_terminal* term) { int guac_terminal_set(guac_terminal* term, int row, int col, char c) { - int scrolled_row = row + term->scroll_offset; - /* Build character with current attributes */ guac_terminal_char guac_char; guac_char.value = c; guac_char.attributes = term->current_attributes; - /* Set display */ - if (scrolled_row < term->display->height) - guac_terminal_display_set_columns(term->display, scrolled_row, col, col, &guac_char); - - /* Set buffer */ - guac_terminal_buffer_set_columns(term->buffer, row, col, col, &guac_char); + guac_terminal_set_columns(term, row, col, col, &guac_char); return 0; @@ -136,18 +127,20 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { + guac_terminal_char* guac_char; int scrolled_row = row + term->scroll_offset; /* Get character from buffer */ - guac_terminal_char* guac_char = - &(term->buffer->characters[row*term->buffer->width + col]); + guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(term->buffer, row); + guac_terminal_buffer_prepare_row(buffer_row, col+1, &term->default_char); /* Toggle reverse */ + guac_char = &(buffer_row->characters[col]); guac_char->attributes.reverse = !(guac_char->attributes.reverse); /* Set display */ if (scrolled_row < term->display->height) - guac_terminal_display_set(term->display, scrolled_row, col, guac_char); + guac_terminal_display_set_columns(term->display, scrolled_row, col, col, guac_char); return 0; @@ -164,120 +157,31 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) { } -int guac_terminal_copy(guac_terminal* term, - int src_row, int src_col, int rows, int cols, - int dst_row, int dst_col) { - - int scrolled_src_row = src_row + term->scroll_offset; - int scrolled_dst_row = dst_row + term->scroll_offset; - - int scrolled_rows = rows; - - /* FIXME: If source (but not dest) is partially scrolled out of view, then - * the display will not be updated properly. We need to pull the data - * from the buffer in such a case. - */ - - if (scrolled_src_row < term->display->height && - scrolled_dst_row < term->display->height) { - - /* Adjust display rect height if scrolled out of view */ - if (scrolled_src_row + scrolled_rows > term->display->height) - scrolled_rows = term->display->height - scrolled_src_row; - - if (scrolled_dst_row + scrolled_rows > term->display->height) - scrolled_rows = term->display->height - scrolled_dst_row; - - /* Update display */ - guac_terminal_display_copy(term->display, - scrolled_dst_row, dst_col, - scrolled_src_row, src_col, - cols, rows); - - } - - /* Update buffer */ - guac_terminal_buffer_copy(term->buffer, - dst_row, dst_col, - src_row, src_col, - cols, rows); - - return 0; - -} - - -int guac_terminal_clear(guac_terminal* term, - int row, int col, int rows, int cols) { - - int scrolled_row = row + term->scroll_offset; - int scrolled_rows = rows; - - /* Build space */ - guac_terminal_char character; - character.value = ' '; - character.attributes = term->current_attributes; - - if (scrolled_row < term->display->height) { - - /* Adjust display rect height if scrolled out of view */ - if (scrolled_row + scrolled_rows > term->display->height) - scrolled_rows = term->display->height - scrolled_row; - - /* Fill with color */ - guac_terminal_display_set_rect(term->display, - scrolled_row, col, cols, scrolled_rows, &character); - - } - - guac_terminal_buffer_set_rect(term->buffer, - row, col, cols, rows, &character); - - return 0; - -} - int guac_terminal_scroll_up(guac_terminal* term, int start_row, int end_row, int amount) { - - /* Calculate height of scroll region */ - int height = end_row - start_row + 1; - - /* If scroll region is entire screen, push rows into scrollback */ - if (start_row == 0 && end_row == term->term_height-1) - guac_terminal_scrollback_buffer_append(term->scrollback, term, amount); - - return - - /* Move rows within scroll region up by the given amount */ - guac_terminal_copy(term, - start_row + amount, 0, - height - amount, term->term_width, - start_row, 0) - - /* Fill new rows with background */ - || guac_terminal_clear(term, - end_row - amount + 1, 0, amount, term->term_width); - + /* STUB */ + return 0; } int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount) { + /* STUB */ + return 0; +} - /* Calculate height of scroll region */ - int height = end_row - start_row + 1; - - return +int guac_terminal_clear_columns(guac_terminal* term, + int row, int start_col, int end_col) { - /* Move rows within scroll region down by the given amount */ - guac_terminal_copy(term, - start_row, 0, - height - amount, term->term_width, - start_row + amount, 0) + /* Build space */ + guac_terminal_char blank; + blank.value = ' '; + blank.attributes = term->current_attributes; - /* Fill new rows with background */ - || guac_terminal_clear(term, - start_row, 0, amount, term->term_width); + /* Clear */ + guac_terminal_set_columns(term, + row, start_col, end_col, &blank); + + return 0; } @@ -289,9 +193,8 @@ int guac_terminal_clear_range(guac_terminal* term, if (start_col > 0) { /* Clear from start_col to far right */ - if (guac_terminal_clear(term, - start_row, start_col, 1, term->term_width - start_col)) - return 1; + guac_terminal_clear_columns(term, + start_row, start_col, term->term_width - 1); /* One less row to clear */ start_row++; @@ -301,9 +204,7 @@ int guac_terminal_clear_range(guac_terminal* term, if (end_col < term->term_width - 1) { /* Clear from far left to end_col */ - if (guac_terminal_clear(term, - end_row, 0, 1, end_col + 1)) - return 1; + guac_terminal_clear_columns(term, end_row, 0, end_col); /* One less row to clear */ end_row--; @@ -313,9 +214,13 @@ int guac_terminal_clear_range(guac_terminal* term, /* Remaining region now guaranteed rectangular. Clear, if possible */ if (start_row <= end_row) { - if (guac_terminal_clear(term, - start_row, 0, end_row - start_row + 1, term->term_width)) - return 1; + int row; + for (row=start_row; row<=end_row; row++) { + + /* Clear entire row */ + guac_terminal_clear_columns(term, row, 0, term->term_width - 1); + + } } @@ -455,7 +360,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Get row from scrollback */ guac_terminal_buffer_row* buffer_row = - guac_terminal_buffer_get_row(terminal, row); + guac_terminal_buffer_get_row(terminal->buffer, row); /* Draw row */ /* FIXME: Clear row first */ @@ -470,7 +375,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, } /* FIXME: Should flush somewhere more sensible */ - guac_terminal_display_flush(terminal->display, terminal); + guac_terminal_display_flush(terminal->display); guac_socket_flush(terminal->client->socket); } @@ -484,8 +389,8 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* Limit scroll amount by size of scrollback buffer */ - if (terminal->scroll_offset + scroll_amount > terminal->scrollback->length) - scroll_amount = terminal->scrollback->length - terminal->scroll_offset; + if (terminal->scroll_offset + scroll_amount > terminal->buffer->length) + scroll_amount = terminal->buffer->length - terminal->scroll_offset; /* If not scrolling at all, don't bother trying */ if (scroll_amount == 0) @@ -526,178 +431,44 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } /* FIXME: Should flush somewhere more sensible */ - guac_terminal_display_flush(terminal->display, terminal); + guac_terminal_display_flush(terminal->display); guac_socket_flush(terminal->client->socket); } void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { - - guac_terminal_char* guac_char; - guac_terminal_operation* guac_operation; - - /* Update selection coordinates */ - terminal->selection_start_row = - terminal->selection_end_row = row; - terminal->selection_start_column = - terminal->selection_end_column = column; - terminal->text_selected = true; - - /* Get char and operation */ - guac_char = &(terminal->buffer->characters[terminal->buffer->width * row + column]); - guac_operation = &(terminal->display->operations[terminal->display->width * row + column]); - - /* Set character as selected */ - guac_char->attributes.selected = true; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - guac_terminal_display_flush(terminal->display, terminal); - guac_socket_flush(terminal->client->socket); - + /* STUB */ } void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { - - int start_index = terminal->selection_start_row * terminal->buffer->width - + terminal->selection_start_column; - - int old_end_index = terminal->selection_end_row * terminal->buffer->width - + terminal->selection_end_column; - - int new_end_index = row * terminal->buffer->width + column; - - int old_index_a, old_index_b; - int new_index_a, new_index_b; - - int search_index_a, search_index_b; - - int i; - guac_terminal_char* guac_char; - guac_terminal_operation* guac_operation; - - /* If unchanged, do nothing */ - if (old_end_index == new_end_index) return; - - /* Calculate old selection range */ - if (start_index < old_end_index) { - old_index_a = start_index; - old_index_b = old_end_index; - } - else { - old_index_a = old_end_index; - old_index_b = start_index; - } - - /* Calculate new selection range */ - if (start_index < new_end_index) { - new_index_a = start_index; - new_index_b = new_end_index; - } - else { - new_index_a = new_end_index; - new_index_b = start_index; - } - - if (new_index_a < old_index_a) - search_index_a = new_index_a; - else - search_index_a = old_index_a; - - if (new_index_b > old_index_b) - search_index_b = new_index_b; - else - search_index_b = old_index_b; - - /* Get first character */ - guac_char = &(terminal->buffer->characters[search_index_a]); - guac_operation = &(terminal->display->operations[search_index_a]); - - /* Invert modified area */ - for (i=search_index_a; i<=search_index_b; i++) { - - /* If now selected, mark as such */ - if (i >= new_index_a && i <= new_index_b && - (i < old_index_a || i > old_index_b)) { - - guac_char->attributes.selected = true; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - } - - /* If now unselected, mark as such */ - else if (i >= old_index_a && i <= old_index_b && - (i < new_index_a || i > new_index_b)) { - - guac_char->attributes.selected = false; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - } - - /* Next char */ - guac_char++; - guac_operation++; - - } - - terminal->selection_end_row = row; - terminal->selection_end_column = column; - - guac_terminal_display_flush(terminal->display, terminal); - guac_socket_flush(terminal->client->socket); - + /* STUB */ } void guac_terminal_select_end(guac_terminal* terminal) { - - int index_a = terminal->selection_end_row * terminal->buffer->width - + terminal->selection_end_column; - - int index_b = terminal->selection_start_row * terminal->buffer->width - + terminal->selection_start_column; - - int i; - guac_terminal_char* guac_char; - guac_terminal_operation* guac_operation; - - /* The start and end indices of all characters in selection */ - int start_index; - int end_index; - - /* Order indices such that end is after start */ - if (index_a > index_b) { - start_index = index_b; - end_index = index_a; - } - else { - start_index = index_a; - end_index = index_b; - } - - /* Get first character */ - guac_char = &(terminal->buffer->characters[start_index]); - guac_operation = &(terminal->display->operations[start_index]); - - /* Restore state from buffer */ - for (i=start_index; i<=end_index; i++) { - - /* Restore state */ - guac_char->attributes.selected = false; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - /* Next char */ - guac_char++; - guac_operation++; - - } - - terminal->text_selected = false; - - guac_terminal_display_flush(terminal->display, terminal); - guac_socket_flush(terminal->client->socket); - + /* STUB */ +} + +void guac_terminal_copy_columns(guac_terminal* terminal, int row, + int start_column, int end_column, int offset) { + /* STUB */ + guac_client_log_info(terminal->client, + "terminal_copy_columns: row=%i, start=%i, end=%i, offset=%i", + row, start_column, end_column, offset); +} + +void guac_terminal_copy_rows(guac_terminal* terminal, + int start_row, int end_row, int offset) { + /* STUB */ + guac_client_log_info(terminal->client, + "terminal_copy_rows: start=%i, end=%i, offset=%i", + start_row, end_row, offset); +} + +void guac_terminal_set_columns(guac_terminal* terminal, int row, + int start_column, int end_column, guac_terminal_char* character) { + /* STUB */ + guac_client_log_info(terminal->client, + "terminal_set_columns: row=%i, start=%i, end=%i, char='%c'", + row, start_column, end_column, character->value); } diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 2acb74b3..619e1c89 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -297,7 +297,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Reset attributes */ if (value == 0) - term->current_attributes = term->default_attributes; + term->current_attributes = term->default_char.attributes; /* Bold */ else if (value == 1) @@ -319,20 +319,20 @@ int guac_terminal_csi(guac_terminal* term, char c) { else if (value == 38) { term->current_attributes.underscore = true; term->current_attributes.foreground = - term->default_attributes.foreground; + term->default_char.attributes.foreground; } /* Underscore off, default foreground */ else if (value == 39) { term->current_attributes.underscore = false; term->current_attributes.foreground = - term->default_attributes.foreground; + term->default_char.attributes.foreground; } /* Reset background */ else if (value == 49) term->current_attributes.background = - term->default_attributes.background; + term->default_char.attributes.background; /* Reverse video */ else if (value == 7) @@ -400,8 +400,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Entire screen */ else if (argv[0] == 2) - guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width); + guac_terminal_clear_range(term, + 0, 0, term->term_height - 1, term->term_width - 1); break; @@ -410,22 +410,18 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Erase from cursor to end of line */ if (argv[0] == 0) - guac_terminal_clear(term, - term->cursor_row, term->cursor_col, - 1, term->term_width - term->cursor_col); - + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->term_width - 1); /* Erase from start to cursor */ else if (argv[0] == 1) - guac_terminal_clear(term, - term->cursor_row, 0, - 1, term->cursor_col + 1); + guac_terminal_clear_columns(term, term->cursor_row, + 0, term->cursor_col); /* Erase line */ else if (argv[0] == 2) - guac_terminal_clear(term, - term->cursor_row, 0, - 1, term->term_width); + guac_terminal_clear_columns(term, term->cursor_row, + 0, term->term_width - 1); break; @@ -459,16 +455,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Scroll left by amount */ if (term->cursor_col + amount < term->term_width) - guac_terminal_copy(term, - term->cursor_row, term->cursor_col + amount, - 1, - term->term_width - term->cursor_col - amount, - term->cursor_row, term->cursor_col); + guac_terminal_copy_columns(term, term->cursor_row, + term->cursor_col + amount, term->term_width - 1, + -amount); /* Clear right */ - guac_terminal_clear(term, - term->cursor_row, term->term_width - amount, - 1, amount); + guac_terminal_clear_columns(term, term->cursor_row, + term->term_width - amount, term->term_width - 1); break; @@ -479,9 +472,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { if (amount == 0) amount = 1; /* Clear characters */ - guac_terminal_clear(term, - term->cursor_row, term->cursor_col, - 1, amount); + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->cursor_col + amount - 1); break; @@ -493,15 +485,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Scroll right by amount */ if (term->cursor_col + amount < term->term_width) - guac_terminal_copy(term, - term->cursor_row, term->cursor_col, - 1, term->term_width - term->cursor_col - amount, - term->cursor_row, term->cursor_col + amount); + guac_terminal_copy_columns(term, term->cursor_row, + term->cursor_col, term->term_width - amount - 1, + amount); /* Clear left */ - guac_terminal_clear(term, - term->cursor_row, term->cursor_col, - 1, amount); + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->cursor_col + amount - 1); break;