Skip to content

Commit

Permalink
GUACAMOLE-192: Merge functionality allowing word selection in termina…
Browse files Browse the repository at this point in the history
…l on double click.
  • Loading branch information
jmuehlner authored May 29, 2024
2 parents 4cd10b3 + d00ce0a commit 7d004ce
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/terminal/select.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ 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
|| column >= terminal->selection_end_column + terminal->selection_end_width) {

int width = guac_terminal_find_char(terminal, row, &column);
Expand Down
160 changes: 158 additions & 2 deletions src/terminal/terminal.c
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,10 @@ guac_terminal* guac_terminal_create(guac_client* client,
/* Configure backspace */
term->backspace = options->backspace;

/* Initialize mouse latest click time and counter */
term->click_timer = 0;
term->click_counter = 0;

return term;

}
Expand Down Expand Up @@ -1748,6 +1752,132 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {

}

/**
* Determines if the given character is part of a word.
* Match these chars :[0-9A-Za-z\$\%\&\-\.\/\:\=\?\\_~]
* This allows a path, URL, variable name or IP address to be treated as a word.
*
* @param ascii_char
* The character to check.
*
* @return
* true if match a "word" char,
* false otherwise.
*/
static bool guac_terminal_is_part_of_word(int ascii_char) {
return ((ascii_char >= '0' && ascii_char <= '9') ||
(ascii_char >= 'A' && ascii_char <= 'Z') ||
(ascii_char >= 'a' && ascii_char <= 'z') ||
(ascii_char == '$') ||
(ascii_char == '%') ||
(ascii_char == '&') ||
(ascii_char == '-') ||
(ascii_char == '.') ||
(ascii_char == '/') ||
(ascii_char == ':') ||
(ascii_char == '=') ||
(ascii_char == '?') ||
(ascii_char == '\\') ||
(ascii_char == '_') ||
(ascii_char == '~'));
}

/**
* Determines if the given character is part of blank block.
*
* @param ascii_char
* The character to check.
*
* @return
* true if match space (char 0x20) or NULL (char 0x00),
* false otherwise.
*/
static bool guac_terminal_is_blank(int ascii_char) {
return (ascii_char == '\0' || ascii_char == ' ');
}

/**
* Get the char (int ASCII code) at a specific row/col of the display.
*
* @param terminal
* The terminal on which we want to read a character.
*
* @param row
* The row where to read the character.
*
* @param col
* The column where to read the character.
*
* @return
* The ASCII code of the character at the given row/col.
*/
static int guac_terminal_get_char(guac_terminal* terminal, int row, int col) {
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
guac_terminal_char* ascii_char = &(buffer_row->characters[col]);

return ascii_char->value;
}

/**
* Selection of a word during a double click event.
* - Fetching the character under the mouse cursor.
* - Determining the type of character :
* Letter, digit, acceptable symbol within a word,
* or space/NULL,
* all other chars are treated as single.
* - Calculating the word boundaries.
* - Visual selection of the found word.
* - Adding it to clipboard.
*
* @param terminal
* The terminal that received a double click event.
*
* @param row
* The row where is the mouse at the double click event.
*
* @param col
* The column where is the mouse at the double click event.
*/
static void guac_terminal_double_click(guac_terminal* terminal, int row, int col) {

/* (char)10 behind cursor */
int cursor_char = guac_terminal_get_char(terminal, row, col);

/* Position of the word behind cursor.
* Default = col required to select a char if not a word and not blank. */
int word_head = col;
int word_tail = col;
int flag;

/* The function used to calculate the word borders */
bool (*is_part_of_word)(int) = NULL;

/* If selection is on a word, get its borders */
if (guac_terminal_is_part_of_word(cursor_char))
is_part_of_word = guac_terminal_is_part_of_word;

/* If selection is on a blank, get its borders */
else if (guac_terminal_is_blank(cursor_char))
is_part_of_word = guac_terminal_is_blank;

if (is_part_of_word != NULL) {
/* Get word head*/
do {
flag = guac_terminal_get_char(terminal, row, word_head-1);
} while (is_part_of_word(flag) && (word_head >= 0 && word_head <= terminal->display->width) && word_head--);

/* Get word tail */
do {
flag = guac_terminal_get_char(terminal, row, word_tail+1);
} while (is_part_of_word(flag) && (word_tail >= 0 && word_tail <= terminal->display->width) && word_tail++);
}

/* Select and add to clipboard the "word" */
guac_terminal_select_start(terminal, row, word_head);
guac_terminal_select_update(terminal, row, word_tail);

}

static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
int x, int y, int mask) {

Expand Down Expand Up @@ -1813,8 +1943,34 @@ static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) {
if (term->mod_shift)
guac_terminal_select_resume(term, row, col);
else
guac_terminal_select_start(term, row, col);
else {

/* Reset click counter if last click was 300ms before */
if (guac_timestamp_current() - term->click_timer > 300)
term->click_counter = 0;

/* New click time */
term->click_timer = guac_timestamp_current();

switch (term->click_counter++) {

/* First click = start selection */
case 0:
guac_terminal_select_start(term, row, col);
break;

/* Second click = word selection */
case 1:
guac_terminal_double_click(term, row, col);
break;

/* third click or more = line selection */
default:
guac_terminal_select_start(term, row, 0);
guac_terminal_select_update(term, row, term->display->width);
break;
}
}
}

/* In all other cases, simply update the existing selection as long as
Expand Down
10 changes: 10 additions & 0 deletions src/terminal/terminal/terminal-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,16 @@ struct guac_terminal {
*/
bool disable_copy;

/**
* The time betwen two left clicks.
*/
guac_timestamp click_timer;

/**
* Counter for left clicks.
*/
int click_counter;

};

/**
Expand Down

0 comments on commit 7d004ce

Please sign in to comment.