Skip to content

Commit 7d004ce

Browse files
authored
GUACAMOLE-192: Merge functionality allowing word selection in terminal on double click.
2 parents 4cd10b3 + d00ce0a commit 7d004ce

File tree

3 files changed

+169
-3
lines changed

3 files changed

+169
-3
lines changed

src/terminal/select.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
194194

195195
/* Only update if selection has changed */
196196
if (row != terminal->selection_end_row
197-
|| column < terminal->selection_end_column
197+
|| column <= terminal->selection_end_column
198198
|| column >= terminal->selection_end_column + terminal->selection_end_width) {
199199

200200
int width = guac_terminal_find_char(terminal, row, &column);

src/terminal/terminal.c

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,10 @@ guac_terminal* guac_terminal_create(guac_client* client,
590590
/* Configure backspace */
591591
term->backspace = options->backspace;
592592

593+
/* Initialize mouse latest click time and counter */
594+
term->click_timer = 0;
595+
term->click_counter = 0;
596+
593597
return term;
594598

595599
}
@@ -1748,6 +1752,132 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
17481752

17491753
}
17501754

1755+
/**
1756+
* Determines if the given character is part of a word.
1757+
* Match these chars :[0-9A-Za-z\$\%\&\-\.\/\:\=\?\\_~]
1758+
* This allows a path, URL, variable name or IP address to be treated as a word.
1759+
*
1760+
* @param ascii_char
1761+
* The character to check.
1762+
*
1763+
* @return
1764+
* true if match a "word" char,
1765+
* false otherwise.
1766+
*/
1767+
static bool guac_terminal_is_part_of_word(int ascii_char) {
1768+
return ((ascii_char >= '0' && ascii_char <= '9') ||
1769+
(ascii_char >= 'A' && ascii_char <= 'Z') ||
1770+
(ascii_char >= 'a' && ascii_char <= 'z') ||
1771+
(ascii_char == '$') ||
1772+
(ascii_char == '%') ||
1773+
(ascii_char == '&') ||
1774+
(ascii_char == '-') ||
1775+
(ascii_char == '.') ||
1776+
(ascii_char == '/') ||
1777+
(ascii_char == ':') ||
1778+
(ascii_char == '=') ||
1779+
(ascii_char == '?') ||
1780+
(ascii_char == '\\') ||
1781+
(ascii_char == '_') ||
1782+
(ascii_char == '~'));
1783+
}
1784+
1785+
/**
1786+
* Determines if the given character is part of blank block.
1787+
*
1788+
* @param ascii_char
1789+
* The character to check.
1790+
*
1791+
* @return
1792+
* true if match space (char 0x20) or NULL (char 0x00),
1793+
* false otherwise.
1794+
*/
1795+
static bool guac_terminal_is_blank(int ascii_char) {
1796+
return (ascii_char == '\0' || ascii_char == ' ');
1797+
}
1798+
1799+
/**
1800+
* Get the char (int ASCII code) at a specific row/col of the display.
1801+
*
1802+
* @param terminal
1803+
* The terminal on which we want to read a character.
1804+
*
1805+
* @param row
1806+
* The row where to read the character.
1807+
*
1808+
* @param col
1809+
* The column where to read the character.
1810+
*
1811+
* @return
1812+
* The ASCII code of the character at the given row/col.
1813+
*/
1814+
static int guac_terminal_get_char(guac_terminal* terminal, int row, int col) {
1815+
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
1816+
guac_terminal_char* ascii_char = &(buffer_row->characters[col]);
1817+
1818+
return ascii_char->value;
1819+
}
1820+
1821+
/**
1822+
* Selection of a word during a double click event.
1823+
* - Fetching the character under the mouse cursor.
1824+
* - Determining the type of character :
1825+
* Letter, digit, acceptable symbol within a word,
1826+
* or space/NULL,
1827+
* all other chars are treated as single.
1828+
* - Calculating the word boundaries.
1829+
* - Visual selection of the found word.
1830+
* - Adding it to clipboard.
1831+
*
1832+
* @param terminal
1833+
* The terminal that received a double click event.
1834+
*
1835+
* @param row
1836+
* The row where is the mouse at the double click event.
1837+
*
1838+
* @param col
1839+
* The column where is the mouse at the double click event.
1840+
*/
1841+
static void guac_terminal_double_click(guac_terminal* terminal, int row, int col) {
1842+
1843+
/* (char)10 behind cursor */
1844+
int cursor_char = guac_terminal_get_char(terminal, row, col);
1845+
1846+
/* Position of the word behind cursor.
1847+
* Default = col required to select a char if not a word and not blank. */
1848+
int word_head = col;
1849+
int word_tail = col;
1850+
int flag;
1851+
1852+
/* The function used to calculate the word borders */
1853+
bool (*is_part_of_word)(int) = NULL;
1854+
1855+
/* If selection is on a word, get its borders */
1856+
if (guac_terminal_is_part_of_word(cursor_char))
1857+
is_part_of_word = guac_terminal_is_part_of_word;
1858+
1859+
/* If selection is on a blank, get its borders */
1860+
else if (guac_terminal_is_blank(cursor_char))
1861+
is_part_of_word = guac_terminal_is_blank;
1862+
1863+
if (is_part_of_word != NULL) {
1864+
/* Get word head*/
1865+
do {
1866+
flag = guac_terminal_get_char(terminal, row, word_head-1);
1867+
} while (is_part_of_word(flag) && (word_head >= 0 && word_head <= terminal->display->width) && word_head--);
1868+
1869+
/* Get word tail */
1870+
do {
1871+
flag = guac_terminal_get_char(terminal, row, word_tail+1);
1872+
} while (is_part_of_word(flag) && (word_tail >= 0 && word_tail <= terminal->display->width) && word_tail++);
1873+
}
1874+
1875+
/* Select and add to clipboard the "word" */
1876+
guac_terminal_select_start(terminal, row, word_head);
1877+
guac_terminal_select_update(terminal, row, word_tail);
1878+
1879+
}
1880+
17511881
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
17521882
int x, int y, int mask) {
17531883

@@ -1813,8 +1943,34 @@ static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
18131943
if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) {
18141944
if (term->mod_shift)
18151945
guac_terminal_select_resume(term, row, col);
1816-
else
1817-
guac_terminal_select_start(term, row, col);
1946+
else {
1947+
1948+
/* Reset click counter if last click was 300ms before */
1949+
if (guac_timestamp_current() - term->click_timer > 300)
1950+
term->click_counter = 0;
1951+
1952+
/* New click time */
1953+
term->click_timer = guac_timestamp_current();
1954+
1955+
switch (term->click_counter++) {
1956+
1957+
/* First click = start selection */
1958+
case 0:
1959+
guac_terminal_select_start(term, row, col);
1960+
break;
1961+
1962+
/* Second click = word selection */
1963+
case 1:
1964+
guac_terminal_double_click(term, row, col);
1965+
break;
1966+
1967+
/* third click or more = line selection */
1968+
default:
1969+
guac_terminal_select_start(term, row, 0);
1970+
guac_terminal_select_update(term, row, term->display->width);
1971+
break;
1972+
}
1973+
}
18181974
}
18191975

18201976
/* In all other cases, simply update the existing selection as long as

src/terminal/terminal/terminal-priv.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,16 @@ struct guac_terminal {
465465
*/
466466
bool disable_copy;
467467

468+
/**
469+
* The time betwen two left clicks.
470+
*/
471+
guac_timestamp click_timer;
472+
473+
/**
474+
* Counter for left clicks.
475+
*/
476+
int click_counter;
477+
468478
};
469479

470480
/**

0 commit comments

Comments
 (0)