Initial scrollback implementation (missing buffer redraw and several necessary graphical ops).

This commit is contained in:
Michael Jumper 2013-04-05 01:32:33 -07:00
parent 06fb3b5a2e
commit f7143be78b
3 changed files with 338 additions and 54 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);
}