GUACAMOLE-573: Merge allow selection of text while scrolling.
This commit is contained in:
commit
f3d9c2f610
@ -30,6 +30,7 @@ noinst_HEADERS = \
|
|||||||
terminal/named-colors.h \
|
terminal/named-colors.h \
|
||||||
terminal/palette.h \
|
terminal/palette.h \
|
||||||
terminal/scrollbar.h \
|
terminal/scrollbar.h \
|
||||||
|
terminal/select.h \
|
||||||
terminal/terminal.h \
|
terminal/terminal.h \
|
||||||
terminal/terminal_handlers.h \
|
terminal/terminal_handlers.h \
|
||||||
terminal/types.h \
|
terminal/types.h \
|
||||||
@ -44,6 +45,7 @@ libguac_terminal_la_SOURCES = \
|
|||||||
named-colors.c \
|
named-colors.c \
|
||||||
palette.c \
|
palette.c \
|
||||||
scrollbar.c \
|
scrollbar.c \
|
||||||
|
select.c \
|
||||||
terminal.c \
|
terminal.c \
|
||||||
terminal_handlers.c \
|
terminal_handlers.c \
|
||||||
typescript.c \
|
typescript.c \
|
||||||
|
@ -37,54 +37,6 @@
|
|||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the currently-selected region, removing the highlight.
|
|
||||||
*/
|
|
||||||
static void __guac_terminal_display_clear_select(guac_terminal_display* display) {
|
|
||||||
|
|
||||||
guac_socket* socket = display->client->socket;
|
|
||||||
guac_layer* select_layer = display->select_layer;
|
|
||||||
|
|
||||||
guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1);
|
|
||||||
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
|
|
||||||
0x00, 0x00, 0x00, 0x00);
|
|
||||||
|
|
||||||
guac_client_end_frame(display->client);
|
|
||||||
guac_socket_flush(socket);
|
|
||||||
|
|
||||||
/* Text is no longer selected */
|
|
||||||
display->text_selected =
|
|
||||||
display->selection_committed = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether at least one character within the given range is selected.
|
|
||||||
*/
|
|
||||||
static bool __guac_terminal_display_selected_contains(guac_terminal_display* display,
|
|
||||||
int start_row, int start_column, int end_row, int end_column) {
|
|
||||||
|
|
||||||
/* If test range starts after highlight ends, does not intersect */
|
|
||||||
if (start_row > display->selection_end_row)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (start_row == display->selection_end_row
|
|
||||||
&& start_column > display->selection_end_column)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* If test range ends before highlight starts, does not intersect */
|
|
||||||
if (end_row < display->selection_start_row)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (end_row == display->selection_start_row
|
|
||||||
&& end_column < display->selection_start_column)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Otherwise, does intersect */
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Maps any codepoint onto a number between 0 and 511 inclusive */
|
/* Maps any codepoint onto a number between 0 and 511 inclusive */
|
||||||
int __guac_terminal_hash_codepoint(int codepoint) {
|
int __guac_terminal_hash_codepoint(int codepoint) {
|
||||||
|
|
||||||
@ -310,8 +262,7 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
|
|||||||
display->operations = NULL;
|
display->operations = NULL;
|
||||||
|
|
||||||
/* Initially nothing selected */
|
/* Initially nothing selected */
|
||||||
display->text_selected =
|
display->text_selected = false;
|
||||||
display->selection_committed = false;
|
|
||||||
|
|
||||||
return display;
|
return display;
|
||||||
|
|
||||||
@ -413,11 +364,6 @@ void guac_terminal_display_copy_columns(guac_terminal_display* display, int row,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If selection visible and committed, clear if update touches selection */
|
|
||||||
if (display->text_selected && display->selection_committed &&
|
|
||||||
__guac_terminal_display_selected_contains(display, row, start_column, row, end_column))
|
|
||||||
__guac_terminal_display_clear_select(display);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_display_copy_rows(guac_terminal_display* display,
|
void guac_terminal_display_copy_rows(guac_terminal_display* display,
|
||||||
@ -463,11 +409,6 @@ void guac_terminal_display_copy_rows(guac_terminal_display* display,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If selection visible and committed, clear if update touches selection */
|
|
||||||
if (display->text_selected && display->selection_committed &&
|
|
||||||
__guac_terminal_display_selected_contains(display, start_row, 0, end_row, display->width - 1))
|
|
||||||
__guac_terminal_display_clear_select(display);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_display_set_columns(guac_terminal_display* display, int row,
|
void guac_terminal_display_set_columns(guac_terminal_display* display, int row,
|
||||||
@ -502,11 +443,6 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If selection visible and committed, clear if update touches selection */
|
|
||||||
if (display->text_selected && display->selection_committed &&
|
|
||||||
__guac_terminal_display_selected_contains(display, row, start_column, row, end_column))
|
|
||||||
__guac_terminal_display_clear_select(display);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_display_resize(guac_terminal_display* display, int width, int height) {
|
void guac_terminal_display_resize(guac_terminal_display* display, int width, int height) {
|
||||||
@ -570,10 +506,6 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int
|
|||||||
display->char_width * width,
|
display->char_width * width,
|
||||||
display->char_height * height);
|
display->char_height * height);
|
||||||
|
|
||||||
/* If selection visible and committed, clear */
|
|
||||||
if (display->text_selected && display->selection_committed)
|
|
||||||
__guac_terminal_display_clear_select(display);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __guac_terminal_display_flush_copy(guac_terminal_display* display) {
|
void __guac_terminal_display_flush_copy(guac_terminal_display* display) {
|
||||||
@ -899,16 +831,20 @@ void guac_terminal_display_dup(guac_terminal_display* display, guac_user* user,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_display_commit_select(guac_terminal_display* display) {
|
|
||||||
display->selection_committed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_terminal_display_select(guac_terminal_display* display,
|
void guac_terminal_display_select(guac_terminal_display* display,
|
||||||
int start_row, int start_col, int end_row, int end_col) {
|
int start_row, int start_col, int end_row, int end_col) {
|
||||||
|
|
||||||
guac_socket* socket = display->client->socket;
|
guac_socket* socket = display->client->socket;
|
||||||
guac_layer* select_layer = display->select_layer;
|
guac_layer* select_layer = display->select_layer;
|
||||||
|
|
||||||
|
/* Do nothing if selection is unchanged */
|
||||||
|
if (display->text_selected
|
||||||
|
&& display->selection_start_row == start_row
|
||||||
|
&& display->selection_start_column == start_col
|
||||||
|
&& display->selection_end_row == end_row
|
||||||
|
&& display->selection_end_column == end_col)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Text is now selected */
|
/* Text is now selected */
|
||||||
display->text_selected = true;
|
display->text_selected = true;
|
||||||
|
|
||||||
@ -989,8 +925,24 @@ void guac_terminal_display_select(guac_terminal_display* display,
|
|||||||
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
|
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
|
||||||
0x00, 0x80, 0xFF, 0x60);
|
0x00, 0x80, 0xFF, 0x60);
|
||||||
|
|
||||||
guac_client_end_frame(display->client);
|
}
|
||||||
guac_socket_flush(socket);
|
|
||||||
|
void guac_terminal_display_clear_select(guac_terminal_display* display) {
|
||||||
|
|
||||||
|
/* Do nothing if nothing is selected */
|
||||||
|
if (!display->text_selected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
guac_socket* socket = display->client->socket;
|
||||||
|
guac_layer* select_layer = display->select_layer;
|
||||||
|
|
||||||
|
guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1);
|
||||||
|
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
|
||||||
|
0x00, 0x00, 0x00, 0x00);
|
||||||
|
|
||||||
|
/* Text is no longer selected */
|
||||||
|
display->text_selected = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
452
src/terminal/select.c
Normal file
452
src/terminal/select.c
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "common/clipboard.h"
|
||||||
|
#include "terminal/buffer.h"
|
||||||
|
#include "terminal/display.h"
|
||||||
|
#include "terminal/select.h"
|
||||||
|
#include "terminal/terminal.h"
|
||||||
|
#include "terminal/types.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/unicode.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the coordinates for the currently-selected range of text within the
|
||||||
|
* given terminal, normalized such that the start coordinate is before the end
|
||||||
|
* coordinate and the end coordinate takes into account character width. If no
|
||||||
|
* text is currently selected, the behavior of this function is undefined.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance whose selected text coordinates should be
|
||||||
|
* retrieved in normalized form.
|
||||||
|
*
|
||||||
|
* @param start_row
|
||||||
|
* A pointer to an int which should receive the row number of the first
|
||||||
|
* character of text selected within the terminal, where the first
|
||||||
|
* (top-most) row in the terminal is row 0. Rows within the scrollback
|
||||||
|
* buffer (above the top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param start_col
|
||||||
|
* A pointer to an int which should receive the column number of the first
|
||||||
|
* character of text selected within terminal, where 0 is the first
|
||||||
|
* (left-most) column within the row.
|
||||||
|
*
|
||||||
|
* @param end_row
|
||||||
|
* A pointer to an int which should receive the row number of the last
|
||||||
|
* character of text selected within the terminal, where the first
|
||||||
|
* (top-most) row in the terminal is row 0. Rows within the scrollback
|
||||||
|
* buffer (above the top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param end_col
|
||||||
|
* A pointer to an int which should receive the column number of the first
|
||||||
|
* character of text selected within terminal, taking into account the
|
||||||
|
* width of that character, where 0 is the first (left-most) column within
|
||||||
|
* the row.
|
||||||
|
*/
|
||||||
|
static void guac_terminal_select_normalized_range(guac_terminal* terminal,
|
||||||
|
int* start_row, int* start_col, int* end_row, int* end_col) {
|
||||||
|
|
||||||
|
/* Pass through start/end coordinates if they are already in the expected
|
||||||
|
* order, adjusting only for final character width */
|
||||||
|
if (terminal->selection_start_row < terminal->selection_end_row
|
||||||
|
|| (terminal->selection_start_row == terminal->selection_end_row
|
||||||
|
&& terminal->selection_start_column < terminal->selection_end_column)) {
|
||||||
|
|
||||||
|
*start_row = terminal->selection_start_row;
|
||||||
|
*start_col = terminal->selection_start_column;
|
||||||
|
*end_row = terminal->selection_end_row;
|
||||||
|
*end_col = terminal->selection_end_column + terminal->selection_end_width - 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Coordinates must otherwise be swapped in addition to adjusting for
|
||||||
|
* final character width */
|
||||||
|
else {
|
||||||
|
*end_row = terminal->selection_start_row;
|
||||||
|
*end_col = terminal->selection_start_column + terminal->selection_start_width - 1;
|
||||||
|
*start_row = terminal->selection_end_row;
|
||||||
|
*start_col = terminal->selection_end_column;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_select_redraw(guac_terminal* terminal) {
|
||||||
|
|
||||||
|
/* Update the selected region of the display if text is currently
|
||||||
|
* selected */
|
||||||
|
if (terminal->text_selected) {
|
||||||
|
|
||||||
|
int start_row = terminal->selection_start_row + terminal->scroll_offset;
|
||||||
|
int start_column = terminal->selection_start_column;
|
||||||
|
|
||||||
|
int end_row = terminal->selection_end_row + terminal->scroll_offset;
|
||||||
|
int end_column = terminal->selection_end_column;
|
||||||
|
|
||||||
|
/* Update start/end columns to include character width */
|
||||||
|
if (start_row > end_row || (start_row == end_row && start_column > end_column))
|
||||||
|
start_column += terminal->selection_start_width - 1;
|
||||||
|
else
|
||||||
|
end_column += terminal->selection_end_width - 1;
|
||||||
|
|
||||||
|
guac_terminal_display_select(terminal->display, start_row, start_column, end_row, end_column);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the display selection if no text is currently selected */
|
||||||
|
else
|
||||||
|
guac_terminal_display_clear_select(terminal->display);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the beginning of the character at the given row and column, updating
|
||||||
|
* the column to the starting column of that character. The width, if available,
|
||||||
|
* is returned. If the character has no defined width, 1 is returned.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal in which the character should be located.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The row number of the desired character, where the first (top-most) row
|
||||||
|
* in the terminal is row 0. Rows within the scrollback buffer (above the
|
||||||
|
* top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param column
|
||||||
|
* A pointer to an int containing the column number of the desired
|
||||||
|
* character, where 0 is the first (left-most) column within the row. If
|
||||||
|
* the character is a multi-column character, the value of this int will be
|
||||||
|
* adjusted as necessary such that it contains the column number of the
|
||||||
|
* first column containing the character.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The width of the specified character, in columns, or 1 if the character
|
||||||
|
* has no defined width.
|
||||||
|
*/
|
||||||
|
static int guac_terminal_find_char(guac_terminal* terminal,
|
||||||
|
int row, int* column) {
|
||||||
|
|
||||||
|
int start_column = *column;
|
||||||
|
|
||||||
|
/* If requested row is outside the bounds of the current terminal or
|
||||||
|
* scrollback, assume the character is 1 column wide */
|
||||||
|
if (row >= terminal->term_height
|
||||||
|
|| row < terminal->term_height - terminal->buffer->length)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
||||||
|
if (start_column < buffer_row->length) {
|
||||||
|
|
||||||
|
/* Find beginning of character */
|
||||||
|
guac_terminal_char* start_char = &(buffer_row->characters[start_column]);
|
||||||
|
while (start_column > 0 && start_char->value == GUAC_CHAR_CONTINUATION) {
|
||||||
|
start_char--;
|
||||||
|
start_column--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use width, if available */
|
||||||
|
if (start_char->value != GUAC_CHAR_CONTINUATION) {
|
||||||
|
*column = start_column;
|
||||||
|
return start_char->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default to one column wide */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_select_start(guac_terminal* terminal, int row, int column) {
|
||||||
|
|
||||||
|
int width = guac_terminal_find_char(terminal, row, &column);
|
||||||
|
|
||||||
|
terminal->selection_start_row =
|
||||||
|
terminal->selection_end_row = row;
|
||||||
|
|
||||||
|
terminal->selection_start_column =
|
||||||
|
terminal->selection_end_column = column;
|
||||||
|
|
||||||
|
terminal->selection_start_width =
|
||||||
|
terminal->selection_end_width = width;
|
||||||
|
|
||||||
|
terminal->text_selected = false;
|
||||||
|
terminal->selection_committed = false;
|
||||||
|
guac_terminal_notify(terminal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
|
||||||
|
|
||||||
|
/* Only update if selection has changed */
|
||||||
|
if (row != terminal->selection_end_row
|
||||||
|
|| column < terminal->selection_end_column
|
||||||
|
|| column >= terminal->selection_end_column + terminal->selection_end_width) {
|
||||||
|
|
||||||
|
int width = guac_terminal_find_char(terminal, row, &column);
|
||||||
|
|
||||||
|
terminal->selection_end_row = row;
|
||||||
|
terminal->selection_end_column = column;
|
||||||
|
terminal->selection_end_width = width;
|
||||||
|
terminal->text_selected = true;
|
||||||
|
|
||||||
|
guac_terminal_notify(terminal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_select_resume(guac_terminal* terminal, int row, int column) {
|
||||||
|
|
||||||
|
int selection_start_row;
|
||||||
|
int selection_start_column;
|
||||||
|
int selection_end_row;
|
||||||
|
int selection_end_column;
|
||||||
|
|
||||||
|
/* No need to test coordinates if no text is selected at all */
|
||||||
|
if (!terminal->text_selected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Use normalized coordinates for sake of simple comparison */
|
||||||
|
guac_terminal_select_normalized_range(terminal,
|
||||||
|
&selection_start_row, &selection_start_column,
|
||||||
|
&selection_end_row, &selection_end_column);
|
||||||
|
|
||||||
|
/* Prefer to expand from start, such that attempting to resume a selection
|
||||||
|
* within the existing selection preserves the top-most portion of the
|
||||||
|
* selection */
|
||||||
|
if (row > selection_start_row ||
|
||||||
|
(row == selection_start_row && column > selection_start_column)) {
|
||||||
|
terminal->selection_start_row = selection_start_row;
|
||||||
|
terminal->selection_start_column = selection_start_column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand from bottom-most portion of selection if doing otherwise would
|
||||||
|
* reduce the size of the selection */
|
||||||
|
else {
|
||||||
|
terminal->selection_start_row = selection_end_row;
|
||||||
|
terminal->selection_start_column = selection_end_column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection is again in-progress */
|
||||||
|
terminal->selection_committed = false;
|
||||||
|
|
||||||
|
/* Update selection to contain given character */
|
||||||
|
guac_terminal_select_update(terminal, row, column);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the text within the given subsection of a terminal row to the
|
||||||
|
* clipboard. The provided coordinates are considered inclusiveley (the
|
||||||
|
* characters at the start and end column are included in the copied
|
||||||
|
* text). Any out-of-bounds coordinates will be automatically clipped within
|
||||||
|
* the bounds of the given row.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the buffer containing the
|
||||||
|
* text being copied and the clipboard receiving the copied text.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The row number of the text within the terminal to be copied into the
|
||||||
|
* clipboard, where the first (top-most) row in the terminal is row 0. Rows
|
||||||
|
* within the scrollback buffer (above the top-most row of the terminal)
|
||||||
|
* will be negative.
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* The first column of the text to be copied from the given row into the
|
||||||
|
* clipboard associated with the given terminal, where 0 is the first
|
||||||
|
* (left-most) column within the row.
|
||||||
|
*
|
||||||
|
* @param end
|
||||||
|
* The last column of the text to be copied from the given row into the
|
||||||
|
* clipboard associated with the given terminal, where 0 is the first
|
||||||
|
* (left-most) column within the row, or a negative value to denote that
|
||||||
|
* the last column in the row should be used.
|
||||||
|
*/
|
||||||
|
static void guac_terminal_clipboard_append_row(guac_terminal* terminal,
|
||||||
|
int row, int start, int end) {
|
||||||
|
|
||||||
|
char buffer[1024];
|
||||||
|
int i = start;
|
||||||
|
|
||||||
|
/* If requested row is outside the bounds of the current terminal or
|
||||||
|
* scrollback, do nothing */
|
||||||
|
if (row >= terminal->term_height
|
||||||
|
|| row < terminal->term_height - terminal->buffer->length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
guac_terminal_buffer_row* buffer_row =
|
||||||
|
guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
||||||
|
|
||||||
|
/* If selection is entirely outside the bounds of the row, then there is
|
||||||
|
* nothing to append */
|
||||||
|
if (start < 0 || start > buffer_row->length - 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Clip given range to actual bounds of row */
|
||||||
|
if (end < 0 || end > buffer_row->length - 1)
|
||||||
|
end = buffer_row->length - 1;
|
||||||
|
|
||||||
|
/* Repeatedly convert chunks of terminal buffer rows until entire specified
|
||||||
|
* region has been appended to clipboard */
|
||||||
|
while (i <= end) {
|
||||||
|
|
||||||
|
int remaining = sizeof(buffer);
|
||||||
|
char* current = buffer;
|
||||||
|
|
||||||
|
/* Convert as many codepoints within the given range as possible */
|
||||||
|
for (i = start; i <= end; i++) {
|
||||||
|
|
||||||
|
int codepoint = buffer_row->characters[i].value;
|
||||||
|
|
||||||
|
/* Ignore null (blank) characters */
|
||||||
|
if (codepoint == 0 || codepoint == GUAC_CHAR_CONTINUATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Encode current codepoint as UTF-8 */
|
||||||
|
int bytes = guac_utf8_write(codepoint, current, remaining);
|
||||||
|
if (bytes == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
current += bytes;
|
||||||
|
remaining -= bytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append converted buffer to clipboard */
|
||||||
|
guac_common_clipboard_append(terminal->clipboard, buffer, current - buffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_select_end(guac_terminal* terminal) {
|
||||||
|
|
||||||
|
guac_client* client = terminal->client;
|
||||||
|
guac_socket* socket = client->socket;
|
||||||
|
|
||||||
|
/* If no text is selected, nothing to do */
|
||||||
|
if (!terminal->text_selected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Selection is now committed */
|
||||||
|
terminal->selection_committed = true;
|
||||||
|
|
||||||
|
/* Reset current clipboard contents */
|
||||||
|
guac_common_clipboard_reset(terminal->clipboard, "text/plain");
|
||||||
|
|
||||||
|
int start_row, start_col;
|
||||||
|
int end_row, end_col;
|
||||||
|
|
||||||
|
/* Ensure proper ordering of start and end coords */
|
||||||
|
guac_terminal_select_normalized_range(terminal,
|
||||||
|
&start_row, &start_col, &end_row, &end_col);
|
||||||
|
|
||||||
|
/* If only one row, simply copy */
|
||||||
|
if (end_row == start_row)
|
||||||
|
guac_terminal_clipboard_append_row(terminal, start_row, start_col, end_col);
|
||||||
|
|
||||||
|
/* Otherwise, copy multiple rows */
|
||||||
|
else {
|
||||||
|
|
||||||
|
/* Store first row */
|
||||||
|
guac_terminal_clipboard_append_row(terminal, start_row, start_col, -1);
|
||||||
|
|
||||||
|
/* Store all middle rows */
|
||||||
|
for (int row = start_row + 1; row < end_row; row++) {
|
||||||
|
guac_common_clipboard_append(terminal->clipboard, "\n", 1);
|
||||||
|
guac_terminal_clipboard_append_row(terminal, row, 0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store last row */
|
||||||
|
guac_common_clipboard_append(terminal->clipboard, "\n", 1);
|
||||||
|
guac_terminal_clipboard_append_row(terminal, end_row, 0, end_col);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send data */
|
||||||
|
guac_common_clipboard_send(terminal->clipboard, client);
|
||||||
|
guac_socket_flush(socket);
|
||||||
|
|
||||||
|
guac_terminal_notify(terminal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool guac_terminal_select_contains(guac_terminal* terminal,
|
||||||
|
int start_row, int start_column, int end_row, int end_column) {
|
||||||
|
|
||||||
|
int selection_start_row;
|
||||||
|
int selection_start_column;
|
||||||
|
int selection_end_row;
|
||||||
|
int selection_end_column;
|
||||||
|
|
||||||
|
/* No need to test coordinates if no text is selected at all */
|
||||||
|
if (!terminal->text_selected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Use normalized coordinates for sake of simple comparison */
|
||||||
|
guac_terminal_select_normalized_range(terminal,
|
||||||
|
&selection_start_row, &selection_start_column,
|
||||||
|
&selection_end_row, &selection_end_column);
|
||||||
|
|
||||||
|
/* If test range starts after highlight ends, does not intersect */
|
||||||
|
if (start_row > selection_end_row)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (start_row == selection_end_row && start_column > selection_end_column)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* If test range ends before highlight starts, does not intersect */
|
||||||
|
if (end_row < selection_start_row)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (end_row == selection_start_row && end_column < selection_start_column)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Otherwise, does intersect */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_select_touch(guac_terminal* terminal,
|
||||||
|
int start_row, int start_column, int end_row, int end_column) {
|
||||||
|
|
||||||
|
/* Only clear selection if selection is committed */
|
||||||
|
if (!terminal->selection_committed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Clear selection if it contains any characters within the given region */
|
||||||
|
if (guac_terminal_select_contains(terminal, start_row, start_column,
|
||||||
|
end_row, end_column)) {
|
||||||
|
|
||||||
|
/* Text is no longer selected */
|
||||||
|
terminal->text_selected = false;
|
||||||
|
terminal->selection_committed = false;
|
||||||
|
guac_terminal_notify(terminal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@
|
|||||||
#include "terminal/common.h"
|
#include "terminal/common.h"
|
||||||
#include "terminal/display.h"
|
#include "terminal/display.h"
|
||||||
#include "terminal/palette.h"
|
#include "terminal/palette.h"
|
||||||
|
#include "terminal/select.h"
|
||||||
#include "terminal/terminal.h"
|
#include "terminal/terminal.h"
|
||||||
#include "terminal/terminal_handlers.h"
|
#include "terminal/terminal_handlers.h"
|
||||||
#include "terminal/types.h"
|
#include "terminal/types.h"
|
||||||
@ -60,6 +61,9 @@ static void __guac_terminal_set_columns(guac_terminal* terminal, int row,
|
|||||||
guac_terminal_buffer_set_columns(terminal->buffer, row,
|
guac_terminal_buffer_set_columns(terminal->buffer, row,
|
||||||
start_column, end_column, character);
|
start_column, end_column, character);
|
||||||
|
|
||||||
|
/* Clear selection if region is modified */
|
||||||
|
guac_terminal_select_touch(terminal, row, start_column, row, end_column);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,6 +176,7 @@ void guac_terminal_reset(guac_terminal* term) {
|
|||||||
|
|
||||||
/* Reset flags */
|
/* Reset flags */
|
||||||
term->text_selected = false;
|
term->text_selected = false;
|
||||||
|
term->selection_committed = false;
|
||||||
term->application_cursor_keys = false;
|
term->application_cursor_keys = false;
|
||||||
term->automatic_carriage_return = false;
|
term->automatic_carriage_return = false;
|
||||||
term->insert_mode = false;
|
term->insert_mode = false;
|
||||||
@ -1018,6 +1023,12 @@ int guac_terminal_scroll_up(guac_terminal* term,
|
|||||||
term->visible_cursor_row <= end_row)
|
term->visible_cursor_row <= end_row)
|
||||||
term->visible_cursor_row -= amount;
|
term->visible_cursor_row -= amount;
|
||||||
|
|
||||||
|
/* Update selected region */
|
||||||
|
if (term->text_selected) {
|
||||||
|
term->selection_start_row -= amount;
|
||||||
|
term->selection_end_row -= amount;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, just copy row data upwards */
|
/* Otherwise, just copy row data upwards */
|
||||||
@ -1269,184 +1280,6 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_select_redraw(guac_terminal* terminal) {
|
|
||||||
|
|
||||||
int start_row = terminal->selection_start_row + terminal->scroll_offset;
|
|
||||||
int start_column = terminal->selection_start_column;
|
|
||||||
|
|
||||||
int end_row = terminal->selection_end_row + terminal->scroll_offset;
|
|
||||||
int end_column = terminal->selection_end_column;
|
|
||||||
|
|
||||||
/* Update start/end columns to include character width */
|
|
||||||
if (start_row > end_row || (start_row == end_row && start_column > end_column))
|
|
||||||
start_column += terminal->selection_start_width - 1;
|
|
||||||
else
|
|
||||||
end_column += terminal->selection_end_width - 1;
|
|
||||||
|
|
||||||
guac_terminal_display_select(terminal->display, start_row, start_column, end_row, end_column);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locates the beginning of the character at the given row and column, updating
|
|
||||||
* the column to the starting column of that character. The width, if available,
|
|
||||||
* is returned. If the character has no defined width, 1 is returned.
|
|
||||||
*/
|
|
||||||
static int __guac_terminal_find_char(guac_terminal* terminal, int row, int* column) {
|
|
||||||
|
|
||||||
int start_column = *column;
|
|
||||||
|
|
||||||
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
|
||||||
if (start_column < buffer_row->length) {
|
|
||||||
|
|
||||||
/* Find beginning of character */
|
|
||||||
guac_terminal_char* start_char = &(buffer_row->characters[start_column]);
|
|
||||||
while (start_column > 0 && start_char->value == GUAC_CHAR_CONTINUATION) {
|
|
||||||
start_char--;
|
|
||||||
start_column--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use width, if available */
|
|
||||||
if (start_char->value != GUAC_CHAR_CONTINUATION) {
|
|
||||||
*column = start_column;
|
|
||||||
return start_char->width;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default to one column wide */
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_terminal_select_start(guac_terminal* terminal, int row, int column) {
|
|
||||||
|
|
||||||
int width = __guac_terminal_find_char(terminal, row, &column);
|
|
||||||
|
|
||||||
terminal->selection_start_row =
|
|
||||||
terminal->selection_end_row = row;
|
|
||||||
|
|
||||||
terminal->selection_start_column =
|
|
||||||
terminal->selection_end_column = column;
|
|
||||||
|
|
||||||
terminal->selection_start_width =
|
|
||||||
terminal->selection_end_width = width;
|
|
||||||
|
|
||||||
terminal->text_selected = true;
|
|
||||||
|
|
||||||
guac_terminal_select_redraw(terminal);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
|
|
||||||
|
|
||||||
/* Only update if selection has changed */
|
|
||||||
if (row != terminal->selection_end_row
|
|
||||||
|| column < terminal->selection_end_column
|
|
||||||
|| column >= terminal->selection_end_column + terminal->selection_end_width) {
|
|
||||||
|
|
||||||
int width = __guac_terminal_find_char(terminal, row, &column);
|
|
||||||
|
|
||||||
terminal->selection_end_row = row;
|
|
||||||
terminal->selection_end_column = column;
|
|
||||||
terminal->selection_end_width = width;
|
|
||||||
|
|
||||||
guac_terminal_select_redraw(terminal);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) {
|
|
||||||
|
|
||||||
int length = 0;
|
|
||||||
int i;
|
|
||||||
for (i=start; i<=end; i++) {
|
|
||||||
|
|
||||||
int codepoint = row->characters[i].value;
|
|
||||||
|
|
||||||
/* If not null (blank), add to string */
|
|
||||||
if (codepoint != 0 && codepoint != GUAC_CHAR_CONTINUATION) {
|
|
||||||
int bytes = guac_terminal_encode_utf8(codepoint, string);
|
|
||||||
string += bytes;
|
|
||||||
length += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return length;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_terminal_select_end(guac_terminal* terminal, char* string) {
|
|
||||||
|
|
||||||
/* Deselect */
|
|
||||||
terminal->text_selected = false;
|
|
||||||
guac_terminal_display_commit_select(terminal->display);
|
|
||||||
|
|
||||||
guac_terminal_buffer_row* buffer_row;
|
|
||||||
|
|
||||||
int row;
|
|
||||||
|
|
||||||
int start_row, start_col;
|
|
||||||
int end_row, end_col;
|
|
||||||
|
|
||||||
/* Ensure proper ordering of start and end coords */
|
|
||||||
if (terminal->selection_start_row < terminal->selection_end_row
|
|
||||||
|| (terminal->selection_start_row == terminal->selection_end_row
|
|
||||||
&& terminal->selection_start_column < terminal->selection_end_column)) {
|
|
||||||
|
|
||||||
start_row = terminal->selection_start_row;
|
|
||||||
start_col = terminal->selection_start_column;
|
|
||||||
end_row = terminal->selection_end_row;
|
|
||||||
end_col = terminal->selection_end_column + terminal->selection_end_width - 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
end_row = terminal->selection_start_row;
|
|
||||||
end_col = terminal->selection_start_column + terminal->selection_start_width - 1;
|
|
||||||
start_row = terminal->selection_end_row;
|
|
||||||
start_col = terminal->selection_end_column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If only one row, simply copy */
|
|
||||||
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0);
|
|
||||||
if (end_row == start_row) {
|
|
||||||
if (buffer_row->length - 1 < end_col)
|
|
||||||
end_col = buffer_row->length - 1;
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, start_col, end_col, string);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, copy multiple rows */
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* Store first row */
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string);
|
|
||||||
|
|
||||||
/* Store all middle rows */
|
|
||||||
for (row=start_row+1; row<end_row; row++) {
|
|
||||||
|
|
||||||
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
|
||||||
|
|
||||||
*(string++) = '\n';
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store last row */
|
|
||||||
buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0);
|
|
||||||
if (buffer_row->length - 1 < end_col)
|
|
||||||
end_col = buffer_row->length - 1;
|
|
||||||
|
|
||||||
*(string++) = '\n';
|
|
||||||
string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Null terminator */
|
|
||||||
*string = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_terminal_copy_columns(guac_terminal* terminal, int row,
|
void guac_terminal_copy_columns(guac_terminal* terminal, int row,
|
||||||
int start_column, int end_column, int offset) {
|
int start_column, int end_column, int offset) {
|
||||||
|
|
||||||
@ -1456,6 +1289,9 @@ void guac_terminal_copy_columns(guac_terminal* terminal, int row,
|
|||||||
guac_terminal_buffer_copy_columns(terminal->buffer, row,
|
guac_terminal_buffer_copy_columns(terminal->buffer, row,
|
||||||
start_column, end_column, offset);
|
start_column, end_column, offset);
|
||||||
|
|
||||||
|
/* Clear selection if region is modified */
|
||||||
|
guac_terminal_select_touch(terminal, row, start_column, row, end_column);
|
||||||
|
|
||||||
/* Update cursor location if within region */
|
/* Update cursor location if within region */
|
||||||
if (row == terminal->visible_cursor_row &&
|
if (row == terminal->visible_cursor_row &&
|
||||||
terminal->visible_cursor_col >= start_column &&
|
terminal->visible_cursor_col >= start_column &&
|
||||||
@ -1477,6 +1313,10 @@ void guac_terminal_copy_rows(guac_terminal* terminal,
|
|||||||
guac_terminal_buffer_copy_rows(terminal->buffer,
|
guac_terminal_buffer_copy_rows(terminal->buffer,
|
||||||
start_row, end_row, offset);
|
start_row, end_row, offset);
|
||||||
|
|
||||||
|
/* Clear selection if region is modified */
|
||||||
|
guac_terminal_select_touch(terminal, start_row, 0, end_row,
|
||||||
|
terminal->term_width);
|
||||||
|
|
||||||
/* Update cursor location if within region */
|
/* Update cursor location if within region */
|
||||||
if (terminal->visible_cursor_row >= start_row &&
|
if (terminal->visible_cursor_row >= start_row &&
|
||||||
terminal->visible_cursor_row <= end_row)
|
terminal->visible_cursor_row <= end_row)
|
||||||
@ -1709,6 +1549,7 @@ void guac_terminal_flush(guac_terminal* terminal) {
|
|||||||
guac_terminal_typescript_flush(terminal->typescript);
|
guac_terminal_typescript_flush(terminal->typescript);
|
||||||
|
|
||||||
/* Flush display state */
|
/* Flush display state */
|
||||||
|
guac_terminal_select_redraw(terminal);
|
||||||
guac_terminal_commit_cursor(terminal);
|
guac_terminal_commit_cursor(terminal);
|
||||||
guac_terminal_display_flush(terminal->display);
|
guac_terminal_display_flush(terminal->display);
|
||||||
guac_terminal_scrollbar_flush(terminal->scrollbar);
|
guac_terminal_scrollbar_flush(terminal->scrollbar);
|
||||||
@ -1906,9 +1747,6 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
|
|||||||
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
||||||
int x, int y, int mask) {
|
int x, int y, int mask) {
|
||||||
|
|
||||||
guac_client* client = term->client;
|
|
||||||
guac_socket* socket = client->socket;
|
|
||||||
|
|
||||||
/* Determine which buttons were just released and pressed */
|
/* Determine which buttons were just released and pressed */
|
||||||
int released_mask = term->mouse_mask & ~mask;
|
int released_mask = term->mouse_mask & ~mask;
|
||||||
int pressed_mask = ~term->mouse_mask & mask;
|
int pressed_mask = ~term->mouse_mask & mask;
|
||||||
@ -1944,46 +1782,32 @@ static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
|||||||
if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) || (released_mask & GUAC_CLIENT_MOUSE_MIDDLE))
|
if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) || (released_mask & GUAC_CLIENT_MOUSE_MIDDLE))
|
||||||
return guac_terminal_send_data(term, term->clipboard->buffer, term->clipboard->length);
|
return guac_terminal_send_data(term, term->clipboard->buffer, term->clipboard->length);
|
||||||
|
|
||||||
/* If text selected, change state based on left mouse mouse button */
|
/* If left mouse button was just released, stop selection */
|
||||||
if (term->text_selected) {
|
if (released_mask & GUAC_CLIENT_MOUSE_LEFT)
|
||||||
|
guac_terminal_select_end(term);
|
||||||
|
|
||||||
/* If mouse button released, stop selection */
|
/* Update selection state contextually while the left mouse button is
|
||||||
if (released_mask & GUAC_CLIENT_MOUSE_LEFT) {
|
* pressed */
|
||||||
|
else if (mask & GUAC_CLIENT_MOUSE_LEFT) {
|
||||||
|
|
||||||
int selected_length;
|
int row = y / term->display->char_height - term->scroll_offset;
|
||||||
|
int col = x / term->display->char_width;
|
||||||
|
|
||||||
/* End selection and get selected text */
|
/* If mouse button was already just pressed, start a new selection or
|
||||||
int selectable_size = term->term_width * term->term_height * sizeof(char);
|
* resume the existing selection depending on whether shift is held */
|
||||||
char* string = malloc(selectable_size);
|
if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) {
|
||||||
guac_terminal_select_end(term, string);
|
if (term->mod_shift)
|
||||||
|
guac_terminal_select_resume(term, row, col);
|
||||||
selected_length = strnlen(string, selectable_size);
|
|
||||||
|
|
||||||
/* Store new data */
|
|
||||||
guac_common_clipboard_reset(term->clipboard, "text/plain");
|
|
||||||
guac_common_clipboard_append(term->clipboard, string, selected_length);
|
|
||||||
free(string);
|
|
||||||
|
|
||||||
/* Send data */
|
|
||||||
guac_common_clipboard_send(term->clipboard, client);
|
|
||||||
guac_socket_flush(socket);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, just update */
|
|
||||||
else
|
else
|
||||||
guac_terminal_select_update(term,
|
guac_terminal_select_start(term, row, col);
|
||||||
y / term->display->char_height - term->scroll_offset,
|
|
||||||
x / term->display->char_width);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, if mouse button pressed AND moved, start selection */
|
/* In all other cases, simply update the existing selection as long as
|
||||||
else if (!(pressed_mask & GUAC_CLIENT_MOUSE_LEFT) &&
|
* the mouse button is pressed */
|
||||||
mask & GUAC_CLIENT_MOUSE_LEFT)
|
else
|
||||||
guac_terminal_select_start(term,
|
guac_terminal_select_update(term, row, col);
|
||||||
y / term->display->char_height - term->scroll_offset,
|
|
||||||
x / term->display->char_width);
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
|
@ -182,17 +182,10 @@ typedef struct guac_terminal_display {
|
|||||||
guac_layer* select_layer;
|
guac_layer* select_layer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether text is being selected.
|
* Whether text is currently selected.
|
||||||
*/
|
*/
|
||||||
bool text_selected;
|
bool text_selected;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the selection is finished, and will no longer be modified. A
|
|
||||||
* committed selection remains highlighted for reference, but the
|
|
||||||
* highlight will be removed when the display changes.
|
|
||||||
*/
|
|
||||||
bool selection_committed;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The row that the selection starts at.
|
* The row that the selection starts at.
|
||||||
*/
|
*/
|
||||||
@ -333,10 +326,13 @@ void guac_terminal_display_select(guac_terminal_display* display,
|
|||||||
int start_row, int start_col, int end_row, int end_col);
|
int start_row, int start_col, int end_row, int end_col);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits the select rectangle, allowing the display to clear it when
|
* Clears the currently-selected region, removing the highlight.
|
||||||
* necessary.
|
*
|
||||||
|
* @param display
|
||||||
|
* The guac_terminal_display whose currently-selected region should be
|
||||||
|
* cleared.
|
||||||
*/
|
*/
|
||||||
void guac_terminal_display_commit_select(guac_terminal_display* display);
|
void guac_terminal_display_clear_select(guac_terminal_display* display);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
179
src/terminal/terminal/select.h
Normal file
179
src/terminal/terminal/select.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUAC_TERMINAL_SELECT_H
|
||||||
|
#define GUAC_TERMINAL_SELECT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the visible portion of the text selection rectangle to the
|
||||||
|
* underlying terminal display, requesting that it be redrawn. If no
|
||||||
|
* visible change would result from redrawing the selection rectangle,
|
||||||
|
* this function may have no effect.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal whose text selection rectangle should be
|
||||||
|
* redrawn.
|
||||||
|
*/
|
||||||
|
void guac_terminal_select_redraw(guac_terminal* terminal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the start of text selection at the given row and column. Any existing
|
||||||
|
* selection is cleared. This function should only be invoked while the
|
||||||
|
* guac_terminal is locked through a call to guac_terminal_lock().
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the text being selected.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The row number of the character at the start of the text selection,
|
||||||
|
* where the first (top-most) row in the terminal is row 0. Rows within
|
||||||
|
* the scrollback buffer (above the top-most row of the terminal) will be
|
||||||
|
* negative.
|
||||||
|
*
|
||||||
|
* @param column
|
||||||
|
* The column number of the character at the start of the text selection,
|
||||||
|
* where the first (left-most) column in the terminal is column 0.
|
||||||
|
*/
|
||||||
|
void guac_terminal_select_start(guac_terminal* terminal, int row, int column);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the end of text selection at the given row and column. This function
|
||||||
|
* should only be invoked while the guac_terminal is locked through a call to
|
||||||
|
* guac_terminal_lock().
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the text being selected.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The row number of the character at the current end of the text
|
||||||
|
* selection, where the first (top-most) row in the terminal is row 0. Rows
|
||||||
|
* within the scrollback buffer (above the top-most row of the terminal)
|
||||||
|
* will be negative.
|
||||||
|
*
|
||||||
|
* @param column
|
||||||
|
* The column number of the character at the current end of the text
|
||||||
|
* selection, where the first (left-most) column in the terminal is
|
||||||
|
* column 0.
|
||||||
|
*/
|
||||||
|
void guac_terminal_select_update(guac_terminal* terminal, int row, int column);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes selecting text, expanding the existing selected region from the
|
||||||
|
* closest end to additionally contain the given character. This function
|
||||||
|
* should only be invoked while the guac_terminal is locked through a call to
|
||||||
|
* guac_terminal_lock().
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the text being selected.
|
||||||
|
*
|
||||||
|
* @param row
|
||||||
|
* The row number of the character to include within the text selection,
|
||||||
|
* where the first (top-most) row in the terminal is row 0. Rows within the
|
||||||
|
* scrollback buffer (above the top-most row of the terminal) will be
|
||||||
|
* negative.
|
||||||
|
*
|
||||||
|
* @param column
|
||||||
|
* The column number of the character to include within the text selection,
|
||||||
|
* where the first (left-most) column in the terminal is column 0.
|
||||||
|
*/
|
||||||
|
void guac_terminal_select_resume(guac_terminal* terminal, int row, int column);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends text selection, removing any highlight and storing the selected
|
||||||
|
* character data within the clipboard associated with the given terminal. If
|
||||||
|
* more text is selected than can fit within the clipboard, text at the end of
|
||||||
|
* the selected area will be dropped as necessary. This function should only be
|
||||||
|
* invoked while the guac_terminal is locked through a call to
|
||||||
|
* guac_terminal_lock().
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the text being selected.
|
||||||
|
*/
|
||||||
|
void guac_terminal_select_end(guac_terminal* terminal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether at least one character within the given range is currently
|
||||||
|
* selected.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the text being selected.
|
||||||
|
*
|
||||||
|
* @param start_row
|
||||||
|
* The first row of the region to test, inclusive, where the first
|
||||||
|
* (top-most) row in the terminal is row 0. Rows within the scrollback
|
||||||
|
* buffer (above the top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param start_column
|
||||||
|
* The first column of the region to test, inclusive, where the first
|
||||||
|
* (left-most) column in the terminal is column 0.
|
||||||
|
*
|
||||||
|
* @param end_row
|
||||||
|
* The last row of the region to test, inclusive, where the first
|
||||||
|
* (top-most) row in the terminal is row 0. Rows within the scrollback
|
||||||
|
* buffer (above the top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param end_column
|
||||||
|
* The last column of the region to test, inclusive, where the first
|
||||||
|
* (left-most) column in the terminal is column 0.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if at least one character within the given range is currently
|
||||||
|
* selected, false otherwise.
|
||||||
|
*/
|
||||||
|
bool guac_terminal_select_contains(guac_terminal* terminal,
|
||||||
|
int start_row, int start_column, int end_row, int end_column);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current selection if it contains at least one character within
|
||||||
|
* the given region. If no text is currently selected, the selection has not
|
||||||
|
* yet been committed, or the region does not contain at least one selected
|
||||||
|
* character, this function has no effect.
|
||||||
|
*
|
||||||
|
* @param terminal
|
||||||
|
* The guac_terminal instance associated with the text being selected.
|
||||||
|
*
|
||||||
|
* @param start_row
|
||||||
|
* The first row of the region, inclusive, where the first (top-most) row
|
||||||
|
* in the terminal is row 0. Rows within the scrollback buffer (above the
|
||||||
|
* top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param start_column
|
||||||
|
* The first column of the region, inclusive, where the first (left-most)
|
||||||
|
* column in the terminal is column 0.
|
||||||
|
*
|
||||||
|
* @param end_row
|
||||||
|
* The last row of the region, inclusive, where the first (top-most) row in
|
||||||
|
* the terminal is row 0. Rows within the scrollback buffer (above the
|
||||||
|
* top-most row of the terminal) will be negative.
|
||||||
|
*
|
||||||
|
* @param end_column
|
||||||
|
* The last column of the region, inclusive, where the first (left-most)
|
||||||
|
* column in the terminal is column 0.
|
||||||
|
*/
|
||||||
|
void guac_terminal_select_touch(guac_terminal* terminal,
|
||||||
|
int start_row, int start_column, int end_row, int end_column);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -367,10 +367,18 @@ struct guac_terminal {
|
|||||||
int active_char_set;
|
int active_char_set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether text is being selected.
|
* Whether text is currently selected.
|
||||||
*/
|
*/
|
||||||
bool text_selected;
|
bool text_selected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the selection is finished, and will no longer be modified. A
|
||||||
|
* committed selection remains highlighted for reference, but the
|
||||||
|
* highlight will be removed if characters within the selected region are
|
||||||
|
* modified.
|
||||||
|
*/
|
||||||
|
bool selection_committed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The row that the selection starts at.
|
* The row that the selection starts at.
|
||||||
*/
|
*/
|
||||||
@ -697,23 +705,6 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount);
|
|||||||
*/
|
*/
|
||||||
void guac_terminal_scroll_display_up(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. Character data is stored in the
|
|
||||||
* string buffer provided.
|
|
||||||
*/
|
|
||||||
void guac_terminal_select_end(guac_terminal* terminal, char* string);
|
|
||||||
|
|
||||||
|
|
||||||
/* LOW-LEVEL TERMINAL OPERATIONS */
|
/* LOW-LEVEL TERMINAL OPERATIONS */
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user