Initial scrollback implementation (missing buffer redraw and several necessary graphical ops).
This commit is contained in:
parent
06fb3b5a2e
commit
f7143be78b
@ -207,6 +207,63 @@ typedef struct guac_terminal_delta {
|
|||||||
|
|
||||||
} guac_terminal_delta;
|
} guac_terminal_delta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single variable-length row of terminal data.
|
||||||
|
*/
|
||||||
|
typedef struct guac_terminal_scrollback_row {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of guac_terminal_char representing the contents of the row.
|
||||||
|
*/
|
||||||
|
guac_terminal_char* characters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of this row in characters. This is the number of initialized
|
||||||
|
* characters in the buffer, usually equal to the number of characters
|
||||||
|
* in the screen width at the time this row was created.
|
||||||
|
*/
|
||||||
|
int length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of elements in the characters array. After the length
|
||||||
|
* equals this value, the array must be resized.
|
||||||
|
*/
|
||||||
|
int available;
|
||||||
|
|
||||||
|
} guac_terminal_scrollback_row;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scrollback buffer containing a constant number of arbitrary-length rows.
|
||||||
|
* New rows can be appended to the buffer, with the oldest row replaced with
|
||||||
|
* the new row.
|
||||||
|
*/
|
||||||
|
typedef struct guac_terminal_scrollback_buffer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of scrollback buffer rows. This array functions as a ring buffer.
|
||||||
|
* When a new row needs to be appended, the top reference is moved down
|
||||||
|
* and the old top row is replaced.
|
||||||
|
*/
|
||||||
|
guac_terminal_scrollback_row* scrollback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of rows in the scrollback buffer. This is the total capacity
|
||||||
|
* of the buffer.
|
||||||
|
*/
|
||||||
|
int rows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The row to replace when adding a new row to the scrollback.
|
||||||
|
*/
|
||||||
|
int top;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of rows currently stored in the scrollback buffer.
|
||||||
|
*/
|
||||||
|
int length;
|
||||||
|
|
||||||
|
} guac_terminal_scrollback_buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamically-resizable character buffer.
|
* Dynamically-resizable character buffer.
|
||||||
*/
|
*/
|
||||||
@ -278,10 +335,16 @@ struct guac_terminal {
|
|||||||
guac_layer* filled_glyphs;
|
guac_layer* filled_glyphs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of scrollback buffer rows, where each row is an array of
|
* The scrollback buffer.
|
||||||
* characters.
|
|
||||||
*/
|
*/
|
||||||
guac_terminal_char** scrollback;
|
guac_terminal_scrollback_buffer* scrollback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The relative offset of the display. A positive value indicates that
|
||||||
|
* many rows have been scrolled into view, zero indicates that no
|
||||||
|
* scrolling has occurred. Negative values are illegal.
|
||||||
|
*/
|
||||||
|
int scroll_offset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The width of each character, in pixels.
|
* The width of each character, in pixels.
|
||||||
@ -501,5 +564,37 @@ void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer,
|
|||||||
*/
|
*/
|
||||||
void guac_terminal_buffer_free(guac_terminal_buffer* buffer);
|
void guac_terminal_buffer_free(guac_terminal_buffer* buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates a new scrollback buffer having the given number of rows.
|
||||||
|
*/
|
||||||
|
guac_terminal_scrollback_buffer*
|
||||||
|
guac_terminal_scrollback_buffer_alloc(int rows);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the given scrollback buffer.
|
||||||
|
*/
|
||||||
|
void guac_terminal_scrollback_buffer_free(
|
||||||
|
guac_terminal_scrollback_buffer* buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the given number of rows into the scrollback, maintaining display
|
||||||
|
* position within the scrollback as possible.
|
||||||
|
*/
|
||||||
|
void guac_terminal_scrollback_buffer_append(
|
||||||
|
guac_terminal_scrollback_buffer* buffer,
|
||||||
|
guac_terminal* terminal, int rows);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll down the display by one row, replacing the new space with data from
|
||||||
|
* the scrollback.
|
||||||
|
*/
|
||||||
|
void guac_terminal_scroll_display_down(guac_terminal* terminal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll up the display by one row, replacing the new space with data from
|
||||||
|
* either the scrollback or the terminal buffer.
|
||||||
|
*/
|
||||||
|
void guac_terminal_scroll_display_up(guac_terminal* terminal);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -138,6 +138,7 @@ int ssh_guac_client_clipboard_handler(guac_client* client, char* data) {
|
|||||||
int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
|
int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
||||||
|
guac_terminal* term = client_data->term;
|
||||||
|
|
||||||
/* Determine which buttons were just released */
|
/* Determine which buttons were just released */
|
||||||
int released_mask = client_data->mouse_mask & ~mask;
|
int released_mask = client_data->mouse_mask & ~mask;
|
||||||
@ -156,14 +157,12 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
|
|||||||
|
|
||||||
/* Scroll up if wheel moved up */
|
/* Scroll up if wheel moved up */
|
||||||
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) {
|
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) {
|
||||||
/* STUB */
|
guac_terminal_scroll_display_up(term);
|
||||||
guac_client_log_info(client, "stub: scroll up");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scroll down if wheel moved down */
|
/* Scroll down if wheel moved down */
|
||||||
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) {
|
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) {
|
||||||
/* STUB */
|
guac_terminal_scroll_display_down(term);
|
||||||
guac_client_log_info(client, "stub: scroll down");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -84,8 +84,6 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
|||||||
.underscore = false
|
.underscore = false
|
||||||
};
|
};
|
||||||
|
|
||||||
int row, col;
|
|
||||||
|
|
||||||
PangoFontMap* font_map;
|
PangoFontMap* font_map;
|
||||||
PangoFont* font;
|
PangoFont* font;
|
||||||
PangoFontMetrics* metrics;
|
PangoFontMetrics* metrics;
|
||||||
@ -142,26 +140,9 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
|||||||
term->scroll_start = 0;
|
term->scroll_start = 0;
|
||||||
term->scroll_end = term->term_height - 1;
|
term->scroll_end = term->term_height - 1;
|
||||||
|
|
||||||
/* Create scrollback buffer */
|
/* Init scrollback buffer */
|
||||||
term->scrollback = malloc(term->term_height * sizeof(guac_terminal_char*));
|
term->scrollback = guac_terminal_scrollback_buffer_alloc(1000);
|
||||||
|
term->scroll_offset = 0;
|
||||||
/* Init buffer */
|
|
||||||
for (row = 0; row < term->term_height; row++) {
|
|
||||||
|
|
||||||
/* Create row */
|
|
||||||
guac_terminal_char* current_row =
|
|
||||||
term->scrollback[row] = malloc(term->term_width * sizeof(guac_terminal_char));
|
|
||||||
|
|
||||||
/* Init row */
|
|
||||||
for (col = 0; col < term->term_width; col++) {
|
|
||||||
|
|
||||||
/* Empty character, default colors */
|
|
||||||
current_row[col].value = '\0';
|
|
||||||
current_row[col].attributes = term->default_attributes;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Init delta */
|
/* Init delta */
|
||||||
term->delta = guac_terminal_delta_alloc(term->term_width,
|
term->delta = guac_terminal_delta_alloc(term->term_width,
|
||||||
@ -182,10 +163,7 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
|||||||
void guac_terminal_free(guac_terminal* term) {
|
void guac_terminal_free(guac_terminal* term) {
|
||||||
|
|
||||||
/* Free scrollback buffer */
|
/* Free scrollback buffer */
|
||||||
for (int row = 0; row < term->term_height; row++)
|
guac_terminal_scrollback_buffer_free(term->scrollback);
|
||||||
free(term->scrollback[row]);
|
|
||||||
|
|
||||||
free(term->scrollback);
|
|
||||||
|
|
||||||
/* Free delta */
|
/* Free delta */
|
||||||
guac_terminal_delta_free(term->delta);
|
guac_terminal_delta_free(term->delta);
|
||||||
@ -467,26 +445,8 @@ int guac_terminal_scroll_up(guac_terminal* term,
|
|||||||
int height = end_row - start_row + 1;
|
int height = end_row - start_row + 1;
|
||||||
|
|
||||||
/* If scroll region is entire screen, push rows into scrollback */
|
/* If scroll region is entire screen, push rows into scrollback */
|
||||||
if (start_row == 0 && end_row == term->term_height-1) {
|
if (start_row == 0 && end_row == term->term_height-1)
|
||||||
|
guac_terminal_scrollback_buffer_append(term->scrollback, term, amount);
|
||||||
/* STUB: Test, for sake of logging */
|
|
||||||
char test_str[1024];
|
|
||||||
int column;
|
|
||||||
|
|
||||||
/* Generate test string */
|
|
||||||
guac_terminal_char* current = term->buffer->characters;
|
|
||||||
for (column=0; column < term->buffer->width; column++) {
|
|
||||||
test_str[column] = current->value;
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
test_str[column] = 0;
|
|
||||||
|
|
||||||
/* Log string version of row that WOULD have been scrolled into the
|
|
||||||
* scrollback */
|
|
||||||
guac_client_log_info(term->client,
|
|
||||||
"scroll: %s", test_str);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -630,11 +590,16 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta,
|
|||||||
|
|
||||||
/* FIXME: Handle intersections between src and dst rects */
|
/* FIXME: Handle intersections between src and dst rects */
|
||||||
|
|
||||||
|
guac_terminal_operation* src_copy = malloc(
|
||||||
|
sizeof(guac_terminal_operation) * delta->width * delta->height);
|
||||||
|
memcpy(src_copy, delta->operations,
|
||||||
|
sizeof(guac_terminal_operation) * delta->width * delta->height);
|
||||||
|
|
||||||
guac_terminal_operation* current_row =
|
guac_terminal_operation* current_row =
|
||||||
&(delta->operations[dst_row*delta->width + dst_column]);
|
&(delta->operations[dst_row*delta->width + dst_column]);
|
||||||
|
|
||||||
guac_terminal_operation* src_current_row =
|
guac_terminal_operation* src_current_row =
|
||||||
&(delta->operations[src_row*delta->width + src_column]);
|
&(src_copy[src_row*delta->width + src_column]);
|
||||||
|
|
||||||
/* Set rectangle to copy operations */
|
/* Set rectangle to copy operations */
|
||||||
for (row=0; row<h; row++) {
|
for (row=0; row<h; row++) {
|
||||||
@ -1117,3 +1082,228 @@ void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guac_terminal_scrollback_buffer*
|
||||||
|
guac_terminal_scrollback_buffer_alloc(int rows) {
|
||||||
|
|
||||||
|
/* Allocate scrollback */
|
||||||
|
guac_terminal_scrollback_buffer* buffer =
|
||||||
|
malloc(sizeof(guac_terminal_scrollback_buffer));
|
||||||
|
|
||||||
|
int i;
|
||||||
|
guac_terminal_scrollback_row* row;
|
||||||
|
|
||||||
|
/* Init scrollback data */
|
||||||
|
buffer->rows = rows;
|
||||||
|
buffer->top = 0;
|
||||||
|
buffer->length = 0;
|
||||||
|
buffer->scrollback = malloc(sizeof(guac_terminal_scrollback_row) *
|
||||||
|
buffer->rows);
|
||||||
|
|
||||||
|
/* Init scrollback rows */
|
||||||
|
row = buffer->scrollback;
|
||||||
|
for (i=0; i<rows; i++) {
|
||||||
|
|
||||||
|
/* Allocate row */
|
||||||
|
row->available = 256;
|
||||||
|
row->length = 0;
|
||||||
|
row->characters = malloc(sizeof(guac_terminal_char) * row->available);
|
||||||
|
|
||||||
|
/* Next row */
|
||||||
|
row++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_scrollback_buffer_free(
|
||||||
|
guac_terminal_scrollback_buffer* buffer) {
|
||||||
|
|
||||||
|
int i;
|
||||||
|
guac_terminal_scrollback_row* row = buffer->scrollback;
|
||||||
|
|
||||||
|
/* Free all rows */
|
||||||
|
for (i=0; i<buffer->rows; i++) {
|
||||||
|
free(row->characters);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free actual buffer */
|
||||||
|
free(buffer->scrollback);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_scrollback_buffer_append(
|
||||||
|
guac_terminal_scrollback_buffer* buffer,
|
||||||
|
guac_terminal* terminal, int rows) {
|
||||||
|
|
||||||
|
int row, column;
|
||||||
|
|
||||||
|
/* Copy data into scrollback */
|
||||||
|
guac_terminal_scrollback_row* scrollback_row =
|
||||||
|
&(buffer->scrollback[buffer->top]);
|
||||||
|
guac_terminal_char* current = terminal->buffer->characters;
|
||||||
|
|
||||||
|
for (row=0; row<rows; row++) {
|
||||||
|
|
||||||
|
/* FIXME: Assumes scrollback row large enough */
|
||||||
|
|
||||||
|
/* Copy character data for row */
|
||||||
|
guac_terminal_char* dest = scrollback_row->characters;
|
||||||
|
for (column=0; column < terminal->buffer->width; column++)
|
||||||
|
*(dest++) = *(current++);
|
||||||
|
|
||||||
|
scrollback_row->length = terminal->buffer->width;
|
||||||
|
|
||||||
|
/* Next scrollback row */
|
||||||
|
scrollback_row++;
|
||||||
|
buffer->top++;
|
||||||
|
|
||||||
|
/* Wrap around when bottom reached */
|
||||||
|
if (buffer->top == buffer->rows) {
|
||||||
|
buffer->top = 0;
|
||||||
|
scrollback_row = buffer->scrollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end for each row */
|
||||||
|
|
||||||
|
/* Increment row count */
|
||||||
|
buffer->length += rows;
|
||||||
|
if (buffer->length > buffer->rows)
|
||||||
|
buffer->length = buffer->rows;
|
||||||
|
|
||||||
|
/* Log string version of row that WOULD have been scrolled into the
|
||||||
|
* scrollback */
|
||||||
|
guac_client_log_info(terminal->client,
|
||||||
|
"scrollback->top=%i (length=%i/%i)", buffer->top, buffer->length, buffer->rows);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_scroll_display_down(guac_terminal* terminal) {
|
||||||
|
|
||||||
|
int scroll_amount = 3;
|
||||||
|
|
||||||
|
int row, column;
|
||||||
|
|
||||||
|
int scrollback_row_index;
|
||||||
|
guac_terminal_scrollback_row* scrollback_row;
|
||||||
|
|
||||||
|
/* Limit scroll amount by size of scrollback buffer */
|
||||||
|
if (scroll_amount > terminal->scroll_offset)
|
||||||
|
scroll_amount = terminal->scroll_offset;
|
||||||
|
|
||||||
|
/* If not scrolling at all, don't bother trying */
|
||||||
|
if (scroll_amount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Shift screen up */
|
||||||
|
if (terminal->term_height > scroll_amount)
|
||||||
|
guac_terminal_delta_copy(terminal->delta,
|
||||||
|
0, 0, /* Destination row, col */
|
||||||
|
scroll_amount, 0, /* source row,col */
|
||||||
|
terminal->term_width, terminal->term_height - scroll_amount);
|
||||||
|
|
||||||
|
/* Advance by scroll amount */
|
||||||
|
terminal->scroll_offset -= scroll_amount;
|
||||||
|
guac_client_log_info(terminal->client, "scrolling to %i", terminal->scroll_offset);
|
||||||
|
|
||||||
|
/* Get corresponding scrollback row */
|
||||||
|
scrollback_row_index = terminal->scrollback->top - terminal->scroll_offset + terminal->term_height - 1;
|
||||||
|
scrollback_row = &(terminal->scrollback->scrollback[scrollback_row_index]);
|
||||||
|
|
||||||
|
/* Draw new rows from scrollback */
|
||||||
|
for (row=terminal->term_height - scroll_amount; row < terminal->term_height; row++) {
|
||||||
|
|
||||||
|
/* FIXME: Clear row first */
|
||||||
|
|
||||||
|
/* Draw row */
|
||||||
|
guac_terminal_char* current = scrollback_row->characters;
|
||||||
|
for (column=0; column<scrollback_row->length; column++)
|
||||||
|
guac_terminal_delta_set(terminal->delta, row, column, current++);
|
||||||
|
|
||||||
|
/* Next row */
|
||||||
|
scrollback_row_index++;
|
||||||
|
|
||||||
|
/* Wrap if at end of scrollback */
|
||||||
|
if (scrollback_row_index == terminal->scrollback->rows) {
|
||||||
|
scrollback_row_index = 0;
|
||||||
|
scrollback_row = terminal->scrollback->scrollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, just advance to next row */
|
||||||
|
else
|
||||||
|
scrollback_row++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Should flush somewhere more sensible */
|
||||||
|
guac_terminal_delta_flush(terminal->delta, terminal);
|
||||||
|
guac_socket_flush(terminal->client->socket);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_scroll_display_up(guac_terminal* terminal) {
|
||||||
|
|
||||||
|
int scroll_amount = 3;
|
||||||
|
|
||||||
|
int row, column;
|
||||||
|
|
||||||
|
int scrollback_row_index;
|
||||||
|
guac_terminal_scrollback_row* scrollback_row;
|
||||||
|
|
||||||
|
/* 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 not scrolling at all, don't bother trying */
|
||||||
|
if (scroll_amount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Shift screen down */
|
||||||
|
if (terminal->term_height > scroll_amount)
|
||||||
|
guac_terminal_delta_copy(terminal->delta,
|
||||||
|
scroll_amount, 0, /* Destination row,col */
|
||||||
|
0, 0, /* Source row, col */
|
||||||
|
terminal->term_width, terminal->term_height - scroll_amount);
|
||||||
|
|
||||||
|
/* Advance by scroll amount */
|
||||||
|
terminal->scroll_offset += scroll_amount;
|
||||||
|
guac_client_log_info(terminal->client, "scrolling to %i", terminal->scroll_offset);
|
||||||
|
|
||||||
|
/* Get corresponding scrollback row */
|
||||||
|
scrollback_row_index = terminal->scrollback->top - terminal->scroll_offset;
|
||||||
|
scrollback_row = &(terminal->scrollback->scrollback[scrollback_row_index]);
|
||||||
|
|
||||||
|
/* Draw new rows from scrollback */
|
||||||
|
for (row=0; row < scroll_amount; row++) {
|
||||||
|
|
||||||
|
/* FIXME: Clear row first */
|
||||||
|
|
||||||
|
/* Draw row */
|
||||||
|
guac_terminal_char* current = scrollback_row->characters;
|
||||||
|
for (column=0; column<scrollback_row->length; column++)
|
||||||
|
guac_terminal_delta_set(terminal->delta, row, column, current++);
|
||||||
|
|
||||||
|
/* Next row */
|
||||||
|
scrollback_row_index++;
|
||||||
|
|
||||||
|
/* Wrap if at end of scrollback */
|
||||||
|
if (scrollback_row_index == terminal->scrollback->rows) {
|
||||||
|
scrollback_row_index = 0;
|
||||||
|
scrollback_row = terminal->scrollback->scrollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, just advance to next row */
|
||||||
|
else
|
||||||
|
scrollback_row++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Should flush somewhere more sensible */
|
||||||
|
guac_terminal_delta_flush(terminal->delta, terminal);
|
||||||
|
guac_socket_flush(terminal->client->socket);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user