Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUACAMOLE-192: Select word in terminal on double click #504

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading