Initial selection support - does not yet affect clipboard, nor work properly when scrolled.
This commit is contained in:
parent
2eeb9263c5
commit
0dabb97f01
@ -94,6 +94,11 @@ typedef struct guac_terminal_attributes {
|
||||
*/
|
||||
bool reverse;
|
||||
|
||||
/**
|
||||
* Whether the associated character is selected.
|
||||
*/
|
||||
bool selected;
|
||||
|
||||
/**
|
||||
* Whether to render the character with underscore.
|
||||
*/
|
||||
@ -431,6 +436,31 @@ struct guac_terminal {
|
||||
*/
|
||||
guac_terminal_buffer* buffer;
|
||||
|
||||
/**
|
||||
* Whether text is being selected.
|
||||
*/
|
||||
bool text_selected;
|
||||
|
||||
/**
|
||||
* The row that the selection starts at.
|
||||
*/
|
||||
int selection_start_row;
|
||||
|
||||
/**
|
||||
* The column that the selection starts at.
|
||||
*/
|
||||
int selection_start_column;
|
||||
|
||||
/**
|
||||
* The row that the selection ends at.
|
||||
*/
|
||||
int selection_end_row;
|
||||
|
||||
/**
|
||||
* The column that the selection ends at.
|
||||
*/
|
||||
int selection_end_column;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -621,5 +651,26 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount);
|
||||
*/
|
||||
void guac_terminal_scroll_display_up(guac_terminal* terminal, int amount);
|
||||
|
||||
/**
|
||||
* Marks the start of text selection at the given row and column.
|
||||
*/
|
||||
void guac_terminal_select_start(guac_terminal* terminal, int row, int column);
|
||||
|
||||
/**
|
||||
* Updates the end of text selection at the given row and column.
|
||||
*/
|
||||
void guac_terminal_select_update(guac_terminal* terminal, int row, int column);
|
||||
|
||||
/**
|
||||
* Ends text selection, removing any highlight.
|
||||
*/
|
||||
void guac_terminal_select_end(guac_terminal* terminal);
|
||||
|
||||
/**
|
||||
* Returns a row of character data, whether that data be from the scrollback buffer or the main backing buffer. The length
|
||||
* parameter given here is a pointer to the int variable that should receive the length of the character array returned.
|
||||
*/
|
||||
guac_terminal_char* guac_terminal_get_row(guac_terminal* terminal, int row, int* length);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -147,8 +147,10 @@ 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;
|
||||
guac_terminal* term = client_data->term;
|
||||
|
||||
/* Determine which buttons were just released */
|
||||
int released_mask = client_data->mouse_mask & ~mask;
|
||||
/* Determine which buttons were just released and pressed */
|
||||
int released_mask = client_data->mouse_mask & ~mask;
|
||||
int pressed_mask = ~client_data->mouse_mask & mask;
|
||||
|
||||
client_data->mouse_mask = mask;
|
||||
|
||||
/* Show mouse cursor if not already shown */
|
||||
@ -173,6 +175,34 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
|
||||
|
||||
}
|
||||
|
||||
/* If text selected, change state based on left mouse mouse button */
|
||||
if (term->text_selected) {
|
||||
pthread_mutex_lock(&(term->lock));
|
||||
|
||||
/* If mouse button released, stop selection */
|
||||
if (released_mask & GUAC_CLIENT_MOUSE_LEFT)
|
||||
guac_terminal_select_end(term);
|
||||
|
||||
/* Otherwise, just update */
|
||||
else
|
||||
guac_terminal_select_update(term,
|
||||
y / term->char_height - term->scroll_offset,
|
||||
x / term->char_width);
|
||||
|
||||
pthread_mutex_unlock(&(term->lock));
|
||||
}
|
||||
|
||||
/* Otherwise, if mouse button pressed, start selection */
|
||||
else if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) {
|
||||
pthread_mutex_lock(&(term->lock));
|
||||
|
||||
guac_terminal_select_start(term,
|
||||
y / term->char_height - term->scroll_offset,
|
||||
x / term->char_width);
|
||||
|
||||
pthread_mutex_unlock(&(term->lock));
|
||||
}
|
||||
|
||||
/* Scroll up if wheel moved up */
|
||||
if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) {
|
||||
pthread_mutex_lock(&(term->lock));
|
||||
|
@ -141,6 +141,8 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
||||
term->scroll_start = 0;
|
||||
term->scroll_end = term->term_height - 1;
|
||||
|
||||
term->text_selected = false;
|
||||
|
||||
/* Init scrollback buffer */
|
||||
term->scrollback = guac_terminal_scrollback_buffer_alloc(1000);
|
||||
term->scroll_offset = 0;
|
||||
@ -270,7 +272,7 @@ int __guac_terminal_set_colors(guac_terminal* term,
|
||||
int background, foreground;
|
||||
|
||||
/* Handle reverse video */
|
||||
if (attributes->reverse) {
|
||||
if (attributes->reverse != attributes->selected) {
|
||||
background = attributes->foreground;
|
||||
foreground = attributes->background;
|
||||
}
|
||||
@ -867,7 +869,7 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta,
|
||||
|
||||
/* Color of the rectangle to draw */
|
||||
int color;
|
||||
if (current->character.attributes.reverse)
|
||||
if (current->character.attributes.reverse != current->character.attributes.selected)
|
||||
color = current->character.attributes.foreground;
|
||||
else
|
||||
color = current->character.attributes.background;
|
||||
@ -1221,6 +1223,22 @@ void guac_terminal_scrollback_buffer_append(
|
||||
|
||||
}
|
||||
|
||||
guac_terminal_char* guac_terminal_get_row(guac_terminal* terminal, int row, int* length) {
|
||||
|
||||
/* If row in past, pull from scrollback */
|
||||
if (row < 0) {
|
||||
guac_terminal_scrollback_row* scrollback_row =
|
||||
guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row);
|
||||
|
||||
*length = scrollback_row->length;
|
||||
return scrollback_row->characters;
|
||||
}
|
||||
|
||||
*length = terminal->buffer->width;
|
||||
return &(terminal->buffer->characters[terminal->buffer->width * row]);
|
||||
|
||||
}
|
||||
|
||||
void guac_terminal_scroll_display_down(guac_terminal* terminal,
|
||||
int scroll_amount) {
|
||||
|
||||
@ -1254,34 +1272,12 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal,
|
||||
/* Draw new rows from scrollback */
|
||||
for (row=start_row; row<=end_row; row++) {
|
||||
|
||||
/* If row in past, pull from scrollback */
|
||||
if (row < 0) {
|
||||
int length;
|
||||
guac_terminal_char* current = guac_terminal_get_row(terminal, row, &length);
|
||||
|
||||
/* Get row from scrollback */
|
||||
guac_terminal_scrollback_row* scrollback_row =
|
||||
guac_terminal_scrollback_buffer_get_row(terminal->scrollback,
|
||||
row);
|
||||
|
||||
/* Draw row */
|
||||
/* FIXME: Clear row first */
|
||||
guac_terminal_char* current = scrollback_row->characters;
|
||||
for (column=0; column<scrollback_row->length; column++)
|
||||
guac_terminal_delta_set(terminal->delta, dest_row, column,
|
||||
current++);
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise, pull from buffer */
|
||||
else {
|
||||
|
||||
guac_terminal_char* current = &(terminal->buffer->characters[
|
||||
terminal->buffer->width * row]);
|
||||
|
||||
for (column=0; column<terminal->buffer->width; column++)
|
||||
guac_terminal_delta_set(terminal->delta, dest_row, column,
|
||||
current++);
|
||||
|
||||
}
|
||||
for (column=0; column<length; column++)
|
||||
guac_terminal_delta_set(terminal->delta, dest_row, column,
|
||||
current++);
|
||||
|
||||
/* Next row */
|
||||
dest_row++;
|
||||
@ -1362,3 +1358,173 @@ guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row(
|
||||
|
||||
}
|
||||
|
||||
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->delta->operations[terminal->delta->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_delta_flush(terminal->delta, terminal);
|
||||
guac_socket_flush(terminal->client->socket);
|
||||
|
||||
}
|
||||
|
||||
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->delta->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_delta_flush(terminal->delta, terminal);
|
||||
guac_socket_flush(terminal->client->socket);
|
||||
|
||||
}
|
||||
|
||||
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->delta->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_delta_flush(terminal->delta, terminal);
|
||||
guac_socket_flush(terminal->client->socket);
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user