Commit 805ffcfd for guacamole.apache.org
commit 805ffcfd9c4477c4fb982052b9d25b5cb3acd167
Merge: e775052a 889ad1cd
Author: Virtually Nick <vnick@apache.org>
Date: Thu Jun 18 15:47:58 2026 -0400
Merge patch branch changes to main.
diff --cc src/protocols/kubernetes/kubernetes.c
index c18f1518,af2005f7..fc142abc
--- a/src/protocols/kubernetes/kubernetes.c
+++ b/src/protocols/kubernetes/kubernetes.c
@@@ -254,7 -287,7 +289,8 @@@ void* guac_kubernetes_client_thread(voi
options->font_size = settings->font_size;
options->color_scheme = settings->color_scheme;
options->backspace = settings->backspace;
+ options->func_keys_and_keypad = settings->func_keys_and_keypad;
+ options->linux_console_keys = strcmp(settings->terminal_type, "linux") == 0;
/* Create terminal */
kubernetes_client->term = guac_terminal_create(client, options);
diff --cc src/protocols/kubernetes/settings.c
index 710f2cc4,f77c6766..2b39da4f
--- a/src/protocols/kubernetes/settings.c
+++ b/src/protocols/kubernetes/settings.c
@@@ -58,10 -57,9 +58,11 @@@ const char* GUAC_KUBERNETES_CLIENT_ARGS
"read-only",
"backspace",
"scrollback",
+ "func-keys-and-keypad",
+ "clipboard-buffer-size",
"disable-copy",
"disable-paste",
+ "terminal-type",
NULL
};
diff --cc src/protocols/kubernetes/settings.h
index 01db2420,50e80612..fed7f584
--- a/src/protocols/kubernetes/settings.h
+++ b/src/protocols/kubernetes/settings.h
@@@ -268,12 -259,12 +274,18 @@@ typedef struct guac_kubernetes_setting
*/
int backspace;
+ /**
+ * The family of codes (e.g. vt100) which will be used when you push
+ * the function and keypad keys.
+ */
+ char* func_keys_and_keypad;
+
+ /**
+ * The terminal emulator type that is connected to this server (e.g.
+ * "xterm" or "xterm-256color"). "linux" is used if unspecified.
+ */
+ char* terminal_type;
+
} guac_kubernetes_settings;
/**
diff --cc src/protocols/ssh/ssh.c
index 5bf38633,cd91bd9f..4f1631c9
--- a/src/protocols/ssh/ssh.c
+++ b/src/protocols/ssh/ssh.c
@@@ -308,7 -315,7 +317,8 @@@ void* ssh_client_thread(void* data)
options->font_size = settings->font_size;
options->color_scheme = settings->color_scheme;
options->backspace = settings->backspace;
+ options->func_keys_and_keypad = settings->func_keys_and_keypad;
+ options->linux_console_keys = (strcmp(settings->terminal_type, "linux") == 0);
/* Create terminal */
ssh_client->term = guac_terminal_create(client, options);
diff --cc src/protocols/telnet/telnet.c
index 5ffc683a,948cdc4a..bbcd740a
--- a/src/protocols/telnet/telnet.c
+++ b/src/protocols/telnet/telnet.c
@@@ -564,7 -576,7 +578,8 @@@ void* guac_telnet_client_thread(void* d
options->font_size = settings->font_size;
options->color_scheme = settings->color_scheme;
options->backspace = settings->backspace;
+ options->func_keys_and_keypad = settings->func_keys_and_keypad;
+ options->linux_console_keys = (strcmp(settings->terminal_type, "linux") == 0);
/* Create terminal */
telnet_client->term = guac_terminal_create(client, options);
diff --cc src/terminal/terminal.c
index 5cfa6917,a8ab2306..cc8b4b6b
--- a/src/terminal/terminal.c
+++ b/src/terminal/terminal.c
@@@ -423,8 -567,9 +569,9 @@@ guac_terminal* guac_terminal_create(gua
/* Init terminal state */
term->current_attributes = default_char.attributes;
term->default_char = default_char;
- term->clipboard = guac_common_clipboard_alloc();
+ term->clipboard = guac_common_clipboard_alloc(options->clipboard_buffer_size);
term->disable_copy = options->disable_copy;
+ term->linux_console_keys = options->linux_console_keys;
/* Calculate available text display area by character size */
int rows, columns;
@@@ -1451,16 -1807,26 +1819,35 @@@ static int __guac_terminal_send_key(gua
guac_terminal_notify(term);
}
+ /* Track modifiers */
+ if (keysym == GUAC_TERMINAL_KEY_CTRL_L || keysym == GUAC_TERMINAL_KEY_CTRL_R)
+ term->mod_ctrl = pressed;
+ else if (keysym == GUAC_TERMINAL_KEY_META_L || keysym == GUAC_TERMINAL_KEY_META_R)
+ term->mod_meta = pressed;
+ else if (keysym == GUAC_TERMINAL_KEY_ALT_L || keysym == GUAC_TERMINAL_KEY_ALT_R)
+ term->mod_alt = pressed;
+ else if (keysym == GUAC_TERMINAL_KEY_SHIFT_L || keysym == GUAC_TERMINAL_KEY_SHIFT_R)
++
+ /*
+ * Super (Windows/Command) and Hyper are treated as Meta since terminals don't
+ * have separate modifier bits for them.
+ *
+ * MODE_SWITCH and the ISO level selectors are intentionally not treated as
+ * Alt. On international layouts these are often used to produce printable
+ * third-/fifth-level characters, and folding them into Alt would inject an
+ * unwanted ESC prefix into normal text input.
+ */
+ if (keysym == GUAC_KEYSYM_CTRL_L || keysym == GUAC_KEYSYM_CTRL_R)
+ term->mod_ctrl = pressed;
+ else if (keysym == GUAC_KEYSYM_META_L || keysym == GUAC_KEYSYM_META_R
+ || keysym == GUAC_KEYSYM_SUPER_L || keysym == GUAC_KEYSYM_SUPER_R
+ || keysym == GUAC_KEYSYM_HYPER_L || keysym == GUAC_KEYSYM_HYPER_R)
+ term->mod_meta = pressed;
+ else if (keysym == GUAC_KEYSYM_ALT_L || keysym == GUAC_KEYSYM_ALT_R)
+ term->mod_alt = pressed;
+ else if (keysym == GUAC_KEYSYM_SHIFT_L || keysym == GUAC_KEYSYM_SHIFT_R)
term->mod_shift = pressed;
-
+
/* If key pressed */
else if (pressed) {
@@@ -1527,11 -1869,18 +1914,11 @@@
if (term->scroll_offset != 0)
guac_terminal_scroll_display_down(term, term->scroll_offset);
- /*
- * Modified arrows, function keys, and cursor editing keys encode Alt
- * and Meta directly in CSI (below). For all other keys, Alt/Meta are
- * represented by prefixing ESC.
- */
- if ((term->mod_alt || term->mod_meta)
- && !__guac_terminal_is_arrow_keysym(keysym)
- && !__guac_terminal_is_function_keysym(keysym)
- && !__guac_terminal_is_editing_keysym(keysym))
- guac_terminal_send_string(term, "\x1B");
+ /* If alt being held, also send escape character */
+ if (term->mod_alt)
+ guac_terminal_send_string(term, GUAC_TERMINAL_ASCII_ESCAPE);
- /* Translate Ctrl+letter to control code */
+ /* Translate Ctrl+letter to control code */
if (term->mod_ctrl) {
char data;
@@@ -1556,25 -1905,16 +1943,26 @@@
else if (keysym >= '3' && keysym <= '7')
data = (char) (keysym - '3' + 0x1B);
- else {
- /* No C0 mapping: encode as CSI modifier sequence if applicable */
- if (__guac_terminal_is_arrow_keysym(keysym))
- return __guac_terminal_send_modified_arrow(term, keysym);
- if (__guac_terminal_is_function_keysym(keysym))
- return __guac_terminal_send_modified_function(term, keysym);
- if (__guac_terminal_is_editing_keysym(keysym))
- return __guac_terminal_send_modified_editing(term, keysym);
+ /* CTRL+Left: return to previous word */
+ else if (keysym == GUAC_TERMINAL_KEY_LEFT || keysym == GUAC_TERMINAL_KEY_KP_LEFT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_PREV_WORD);
+
+ /* CTRL+Right: go to next word */
+ else if (keysym == GUAC_TERMINAL_KEY_RIGHT || keysym == GUAC_TERMINAL_KEY_KP_RIGHT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_NEXT_WORD);
+
+ /* CTRL+Backspace: remove word (map to CTRL+w) */
+ else if (keysym == GUAC_TERMINAL_KEY_BACKSPACE)
+ data = (char) 23;
+
+ /* CTRL+Supr: remove word to right */
+ else if (keysym == GUAC_TERMINAL_KEY_DELETE || keysym == GUAC_TERMINAL_KEY_KP_DELETE)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_DELETE_WORD);
+
+ /* Otherwise ignore */
+ else
return 0;
+ }
return guac_terminal_send_data(term, &data, 1);
@@@ -1645,144 -1947,73 +2034,154 @@@
char backspace_str[] = { term->backspace, '\0' };
return guac_terminal_send_string(term, backspace_str);
}
- if (keysym == GUAC_KEYSYM_TAB || keysym == GUAC_KEYSYM_KP_TAB) return guac_terminal_send_string(term, "\x09");
- if (keysym == GUAC_KEYSYM_LINE_FEED) return guac_terminal_send_string(term, "\x0A");
- if (keysym == GUAC_KEYSYM_ENTER || keysym == GUAC_KEYSYM_KP_ENTER) return guac_terminal_send_string(term, "\x0D");
- if (keysym == GUAC_KEYSYM_ESCAPE) return guac_terminal_send_string(term, "\x1B");
- /* Cursor editing keys (Home, End, Insert, Delete, Page Up, Page Down) w/ modifiers */
- if (__guac_terminal_any_modifier(term) && __guac_terminal_is_editing_keysym(keysym))
- return __guac_terminal_send_modified_editing(term, keysym);
+ if (keysym == 0xFF09 || keysym == 0xFF89) return guac_terminal_send_string(term, "\x09"); /* Tab */
+ if (keysym == 0xFF0A) return guac_terminal_send_string(term, "\x0A"); /* Line Feed */
+ if (keysym == 0xFF0D || keysym == 0xFF8D) return guac_terminal_send_string(term, "\x0D"); /* Enter */
+ if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */
+
+ /* Tab */
+ if (keysym == GUAC_TERMINAL_KEY_TAB || keysym == GUAC_TERMINAL_KEY_KP_TAB)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ASCII_TAB);
+
+ /* Enter */
+ if (keysym == GUAC_TERMINAL_KEY_ENTER || keysym == GUAC_TERMINAL_KEY_KP_ENTER)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ASCII_CR);
+
+ /* Esc */
+ if (keysym == GUAC_TERMINAL_KEY_ESCAPE)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ASCII_ESCAPE);
+
+ /* Home */
+ if (keysym == GUAC_TERMINAL_KEY_HOME || keysym == GUAC_TERMINAL_KEY_KP_HOME)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_HOME);
- /* Arrow keys w/ application cursor */
- if (term->application_cursor_keys) {
+ if (keysym == GUAC_KEYSYM_HOME || keysym == GUAC_KEYSYM_KP_HOME)
+ return guac_terminal_send_string(term, term->linux_console_keys ? "\x1B[1~" : "\x1B[H");
+
+ if (keysym == GUAC_KEYSYM_END || keysym == GUAC_KEYSYM_KP_END)
+ return guac_terminal_send_string(term, term->linux_console_keys ? "\x1B[4~" : "\x1B[F");
+
+ /* Arrow keys w/ modifiers */
+ if (__guac_terminal_any_modifier(term) && __guac_terminal_is_arrow_keysym(keysym))
+ return __guac_terminal_send_modified_arrow(term, keysym);
+ /* Application cursor mode (DECCKM) affects only arrow keys.
+ * Home and End use their own sequences above and are unaffected. */
+ if (term->application_cursor_keys) {
- if (keysym == GUAC_KEYSYM_LEFT || keysym == GUAC_KEYSYM_KP_LEFT) return guac_terminal_send_string(term, "\x1BOD");
- if (keysym == GUAC_KEYSYM_UP || keysym == GUAC_KEYSYM_KP_UP) return guac_terminal_send_string(term, "\x1BOA");
- if (keysym == GUAC_KEYSYM_RIGHT || keysym == GUAC_KEYSYM_KP_RIGHT) return guac_terminal_send_string(term, "\x1BOC");
- if (keysym == GUAC_KEYSYM_DOWN || keysym == GUAC_KEYSYM_KP_DOWN) return guac_terminal_send_string(term, "\x1BOB");
+ /* Left */
+ if (keysym == GUAC_TERMINAL_KEY_LEFT || keysym == GUAC_TERMINAL_KEY_KP_LEFT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_APP_CURSOR_LEFT);
+
+ /* Up */
+ if (keysym == GUAC_TERMINAL_KEY_UP || keysym == GUAC_TERMINAL_KEY_KP_UP)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_APP_CURSOR_UP);
+
+ /* Right */
+ if (keysym == GUAC_TERMINAL_KEY_RIGHT || keysym == GUAC_TERMINAL_KEY_KP_RIGHT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_APP_CURSOR_RIGHT);
+
+ /* Down */
+ if (keysym == GUAC_TERMINAL_KEY_DOWN || keysym == GUAC_TERMINAL_KEY_KP_DOWN)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_APP_CURSOR_DOWN);
}
else {
- if (keysym == GUAC_KEYSYM_LEFT || keysym == GUAC_KEYSYM_KP_LEFT) return guac_terminal_send_string(term, "\x1B[D");
- if (keysym == GUAC_KEYSYM_UP || keysym == GUAC_KEYSYM_KP_UP) return guac_terminal_send_string(term, "\x1B[A");
- if (keysym == GUAC_KEYSYM_RIGHT || keysym == GUAC_KEYSYM_KP_RIGHT) return guac_terminal_send_string(term, "\x1B[C");
- if (keysym == GUAC_KEYSYM_DOWN || keysym == GUAC_KEYSYM_KP_DOWN) return guac_terminal_send_string(term, "\x1B[B");
- }
- if (keysym == GUAC_KEYSYM_PAGE_UP || keysym == GUAC_KEYSYM_KP_PAGE_UP) return guac_terminal_send_string(term, "\x1B[5~");
- if (keysym == GUAC_KEYSYM_PAGE_DOWN || keysym == GUAC_KEYSYM_KP_PAGE_DOWN) return guac_terminal_send_string(term, "\x1B[6~");
+ /* Left */
+ if (keysym == GUAC_TERMINAL_KEY_LEFT || keysym == GUAC_TERMINAL_KEY_KP_LEFT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_CURSOR_LEFT);
- if (keysym == GUAC_KEYSYM_INSERT || keysym == GUAC_KEYSYM_KP_INSERT) return guac_terminal_send_string(term, "\x1B[2~");
+ /* Up */
+ if (keysym == GUAC_TERMINAL_KEY_UP || keysym == GUAC_TERMINAL_KEY_KP_UP)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_CURSOR_UP);
- if (__guac_terminal_any_modifier(term) && __guac_terminal_is_function_keysym(keysym))
- return __guac_terminal_send_modified_function(term, keysym);
+ /* Right */
+ if (keysym == GUAC_TERMINAL_KEY_RIGHT || keysym == GUAC_TERMINAL_KEY_KP_RIGHT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_CURSOR_RIGHT);
- /* F1-F5: Linux console uses ESC [ [ A-E; xterm/VT220 uses SS3 and CSI tilde */
- if (term->linux_console_keys) {
- if (keysym == GUAC_KEYSYM_F1 || keysym == GUAC_KEYSYM_KP_F1) return guac_terminal_send_string(term, "\x1B[[A");
- if (keysym == GUAC_KEYSYM_F2 || keysym == GUAC_KEYSYM_KP_F2) return guac_terminal_send_string(term, "\x1B[[B");
- if (keysym == GUAC_KEYSYM_F3 || keysym == GUAC_KEYSYM_KP_F3) return guac_terminal_send_string(term, "\x1B[[C");
- if (keysym == GUAC_KEYSYM_F4 || keysym == GUAC_KEYSYM_KP_F4) return guac_terminal_send_string(term, "\x1B[[D");
- if (keysym == GUAC_KEYSYM_F5) return guac_terminal_send_string(term, "\x1B[[E");
+ /* Down */
+ if (keysym == GUAC_TERMINAL_KEY_DOWN || keysym == GUAC_TERMINAL_KEY_KP_DOWN)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_CURSOR_DOWN);
+ }
+
+ /* Page up */
+ if (keysym == GUAC_TERMINAL_KEY_PAGEUP || keysym == GUAC_TERMINAL_KEY_KP_PAGEUP)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_PAGEUP);
+
+ /* Page down */
+ if (keysym == GUAC_TERMINAL_KEY_PAGEDOWN || keysym == GUAC_TERMINAL_KEY_KP_PAGEDOWN)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_PAGEDOWN);
+
+ /* End */
+ if (keysym == GUAC_TERMINAL_KEY_END || keysym == GUAC_TERMINAL_KEY_KP_END)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_END);
+
+ /* Insert */
+ if (keysym == GUAC_TERMINAL_KEY_INSERT || keysym == GUAC_TERMINAL_KEY_KP_INSERT)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_INSERT);
+
+ /* F1 */
+ if (term->func_keys_and_keypad == GUAC_TERMINAL_FUNC_KEYS_AND_KEYPAD_VT100) {
+ /* https://vt100.net/docs/vt100-ug/chapter3.html */
+ if (keysym == 0xFFBE || keysym == 0xFF91) return guac_terminal_send_string(term, "\x1BOP"); /* F1 */
+ if (keysym == 0xFFBF || keysym == 0xFF92) return guac_terminal_send_string(term, "\x1BOQ"); /* F2 */
+ if (keysym == 0xFFC0 || keysym == 0xFF93) return guac_terminal_send_string(term, "\x1BOR"); /* F3 */
+ if (keysym == 0xFFC1 || keysym == 0xFF94) return guac_terminal_send_string(term, "\x1BOS"); /* F4 */
+ /* Send this escape code, although the original VT100 did not have F5 */
+ if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[15~"); /* F5 */
}
else {
- if (keysym == GUAC_KEYSYM_F1 || keysym == GUAC_KEYSYM_KP_F1) return guac_terminal_send_string(term, "\x1BOP");
- if (keysym == GUAC_KEYSYM_F2 || keysym == GUAC_KEYSYM_KP_F2) return guac_terminal_send_string(term, "\x1BOQ");
- if (keysym == GUAC_KEYSYM_F3 || keysym == GUAC_KEYSYM_KP_F3) return guac_terminal_send_string(term, "\x1BOR");
- if (keysym == GUAC_KEYSYM_F4 || keysym == GUAC_KEYSYM_KP_F4) return guac_terminal_send_string(term, "\x1BOS");
- if (keysym == GUAC_KEYSYM_F5) return guac_terminal_send_string(term, "\x1B[15~");
+ if (keysym == GUAC_TERMINAL_KEY_F1 || keysym == GUAC_TERMINAL_KEY_KP_F1)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F1);
+
+ /* F2 */
+ if (keysym == GUAC_TERMINAL_KEY_F2 || keysym == GUAC_TERMINAL_KEY_KP_F2)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F2);
+
+ /* F3 */
+ if (keysym == GUAC_TERMINAL_KEY_F3 || keysym == GUAC_TERMINAL_KEY_KP_F3)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F3);
+
+ /* F4 */
+ if (keysym == GUAC_TERMINAL_KEY_F4 || keysym == GUAC_TERMINAL_KEY_KP_F4)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F4);
+
+ /* F5 */
+ if (keysym == GUAC_TERMINAL_KEY_F5)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F5);
+
}
+ /* F6 */
+ if (keysym == GUAC_TERMINAL_KEY_F6)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F6);
+
+ /* F7 */
+ if (keysym == GUAC_TERMINAL_KEY_F7)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F7);
- if (keysym == GUAC_KEYSYM_F6) return guac_terminal_send_string(term, "\x1B[17~");
- if (keysym == GUAC_KEYSYM_F7) return guac_terminal_send_string(term, "\x1B[18~");
- if (keysym == GUAC_KEYSYM_F8) return guac_terminal_send_string(term, "\x1B[19~");
- if (keysym == GUAC_KEYSYM_F9) return guac_terminal_send_string(term, "\x1B[20~");
- if (keysym == GUAC_KEYSYM_F10) return guac_terminal_send_string(term, "\x1B[21~");
- if (keysym == GUAC_KEYSYM_F11) return guac_terminal_send_string(term, "\x1B[23~");
- if (keysym == GUAC_KEYSYM_F12) return guac_terminal_send_string(term, "\x1B[24~");
+ /* F8 */
+ if (keysym == GUAC_TERMINAL_KEY_F8)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F8);
- if (keysym == GUAC_KEYSYM_DELETE || keysym == GUAC_KEYSYM_KP_DELETE) return guac_terminal_send_string(term, "\x1B[3~");
+ /* F9 */
+ if (keysym == GUAC_TERMINAL_KEY_F9)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F9);
+
+ /* F10 */
+ if (keysym == GUAC_TERMINAL_KEY_F10)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F10);
+
+ /* F11 */
+ if (keysym == GUAC_TERMINAL_KEY_F11)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F11);
+
+ /* F12 */
+ if (keysym == GUAC_TERMINAL_KEY_F12)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_F12);
+
+ /* Delete */
+ if (keysym == GUAC_TERMINAL_KEY_DELETE || keysym == GUAC_TERMINAL_KEY_KP_DELETE)
+ return guac_terminal_send_string(term, GUAC_TERMINAL_ESC_SEQ_DELETE);
/* Ignore unknown keys */
guac_client_log(term->client, GUAC_LOG_DEBUG,
@@@ -1820,12 -2051,12 +2219,12 @@@ int guac_terminal_send_key(guac_termina
* 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') ||
+ return ((ascii_char >= '0' && ascii_char <= '9') ||
+ (ascii_char >= 'A' && ascii_char <= 'Z') ||
(ascii_char >= 'a' && ascii_char <= 'z') ||
+ (ascii_char >= GUAC_TERMINAL_LATIN1_CAPITAL_AGRAVE &&
+ ascii_char <= GUAC_TERMINAL_LATIN1_Y_UMLAUT) ||
(ascii_char == '$') ||
- (ascii_char == '%') ||
- (ascii_char == '&') ||
(ascii_char == '-') ||
(ascii_char == '.') ||
(ascii_char == '/') ||
@@@ -2103,182 -2098,58 +2502,182 @@@ static void guac_terminal_get_word_boun
*
* @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.
+ *
+ * @param hold
+ * True when user hold left click, false otherwise.
*/
-static void guac_terminal_double_click(guac_terminal* terminal, int row, int col) {
+static void guac_terminal_double_click(guac_terminal* terminal, int row, int col, bool hold) {
+ /* To store buffer row characters */
guac_terminal_char* characters;
- int length = guac_terminal_buffer_get_columns(terminal->current_buffer, &characters, NULL, row);
+ /* To get wrapped buffer row status */
+ bool is_wrapped;
+ /* Length of the buffer row */
+ int length;
+ /* Character read at a position */
+ int current_char;
+
+ /* Position of the detected word. Default = col/row required to select
+ * a char if not a word and not blank */
+ int word_col_head = col;
+ int word_col_tail = col;
+ int word_row_head = row;
+ int word_row_tail = row;
+
+ /* Position of the detected URI */
+ int uri_col_head;
+ int uri_col_tail;
+ int uri_row_head;
+ int uri_row_tail;
+
+ /* User holds left click: update default selection boundaries */
+ if (hold)
+ guac_terminal_word_initial_position(terminal, col, row, &word_col_head,
+ &word_col_tail, &word_row_head, &word_row_tail);
+
+ /* Try to get boundaries of a word */
+ guac_terminal_get_word_bounds(terminal->current_buffer, guac_terminal_is_part_of_word,
+ &word_col_head, &word_col_tail, &word_row_head, &word_row_tail);
+
+ /* Search for URI only when user don't hold left click
+ * to unconditionally extend selection to word pattern */
+ if (!hold) {
+
+ /* Begin uri search on previously found word position,
+ * this avoids going through the same characters twice */
+ uri_col_head = word_col_head;
+ uri_col_tail = word_col_tail;
+ uri_row_head = word_row_head;
+ uri_row_tail = word_row_tail;
+
+ /* Get boundaries of potential URI */
+ guac_terminal_get_word_bounds(terminal->current_buffer, guac_terminal_is_part_of_uri,
+ &uri_col_head, &uri_col_tail, &uri_row_head, &uri_row_tail);
+
+ /* Check if uri dected */
+ if ((uri_col_head != word_col_head || uri_col_tail != word_col_tail ||
+ uri_row_head != word_row_head || uri_row_tail != word_row_tail)) {
+
+ /* Temp vars to avoid overwrite uri_row_head and uri_col_head values */
+ int tmp_row = uri_row_head;
+ int tmp_col = uri_col_head;
+
+ /* Check for the presence of a uri scheme like /^[a-z]*\:\/{2}/ */
+ do {
+
+ /* Get first char of first row */
+ length = guac_terminal_buffer_get_columns(terminal->current_buffer,
+ &characters, &is_wrapped, tmp_row);
+ current_char = characters[tmp_col].value;
+
+ /* [a-z]+ part */
+ if (current_char >= 'a' && current_char <= 'z') {
+
+ /* Go to next col on current row */
+ if (tmp_col != length-1) {
+ tmp_col++;
+ continue;
+ }
+
+ /* Go to first col of next row */
+ tmp_col = 0;
+ tmp_row++;
+ continue;
- if (col >= length)
- return;
+ }
- /* (char)10 behind cursor */
- int current_char = characters[col].value;
+ /* Search for URI scheme delimiter `://` */
+ if (current_char == ':' &&
+ tmp_col + 2 < length &&
+ characters[tmp_col + 1].value == '/' &&
+ characters[tmp_col + 2].value == '/') {
+
+ /* Use URI limits instead of word limits */
+ word_col_head = uri_col_head;
+ word_col_tail = uri_col_tail;
+ word_row_head = uri_row_head;
+ word_row_tail = uri_row_tail;
+ }
- /* Position of the word behind cursor.
- * Default = col required to select a char if not a word and not blank. */
+ /* Always exit after non-letter char */
+ break;
- /* The function used to calculate the word borders */
- bool (*is_part_of_word)(int) = NULL;
+ } while (tmp_row < uri_row_tail || tmp_col < uri_col_tail);
+ }
+ }
- /* If selection is on a word, get its borders */
- if (guac_terminal_is_part_of_word(current_char))
- is_part_of_word = guac_terminal_is_part_of_word;
+ /* Select and add to clipboard the "word" */
+ guac_terminal_select_start(terminal, word_row_head, word_col_head, GUAC_TERMINAL_COLUMN_SIDE_LEFT);
+ guac_terminal_select_update(terminal, word_row_tail, word_col_tail, GUAC_TERMINAL_COLUMN_SIDE_RIGHT);
- /* If selection is on a blank, get its borders */
- else if (guac_terminal_is_blank(current_char))
- is_part_of_word = guac_terminal_is_blank;
+}
+
+/**
+ * Selection of a line during a triple click event.
+ * - Get buffer row boundaries if it has been wrapped.
+ * - Visual selection of the line.
+ * - Adding it to clipboard.
+ *
+ * @param terminal
+ * The terminal that received a triple click event.
+ *
+ * @param row
+ * The row where is the mouse at the triple click event.
+ *
+ * @param hold
+ * True when user hold left click.
+ */
+static void guac_terminal_triple_click(guac_terminal* terminal, int row, bool hold) {
- int word_head = col;
- int word_tail = col;
+ /* Temporarily reading previous and next lines */
+ guac_terminal_char* characters;
+ bool is_wrapped;
+ int length;
- if (is_part_of_word != NULL) {
+ /* Final boundary rows */
+ int top_row = row;
+ int bottom_row = row;
- /* Get word head*/
- for (; word_head - 1 >= 0; word_head--) {
- if (!is_part_of_word(characters[word_head - 1].value))
- break;
- }
+ /* User holds left click */
+ if (hold) {
- /* Get word tail */
- for (; word_tail + 1 < terminal->display->width && word_tail + 1 < length; word_tail++) {
- if (!is_part_of_word(characters[word_tail + 1].value))
- break;
- }
+ /* Use initial row as bottom of the selection and go up */
+ if (row <= terminal->selection_initial_row)
+ bottom_row = terminal->selection_initial_row;
+ /* Use initial row as top of the selection and go down */
+ if (row > terminal->selection_initial_row)
+ top_row = terminal->selection_initial_row;
}
- /* Select and add to clipboard the "word" */
- guac_terminal_select_start(terminal, row, word_head, GUAC_TERMINAL_COLUMN_SIDE_LEFT);
- guac_terminal_select_update(terminal, row, word_tail, GUAC_TERMINAL_COLUMN_SIDE_RIGHT);
+ /* Get top boundary */
+ do {
+
+ /* Read previous buffer row */
+ length = guac_terminal_buffer_get_columns(terminal->current_buffer,
+ &characters, &is_wrapped, top_row - 1);
+ /* Go to the previous row if it is wrapped */
+ } while (is_wrapped && top_row--);
+
+ /* Get bottom boundary */
+ do {
+
+ /* Read current buffer row */
+ length = guac_terminal_buffer_get_columns(terminal->current_buffer,
+ &characters, &is_wrapped, bottom_row);
+
+ /* Go to the next row if current row is wrapped */
+ } while (is_wrapped && bottom_row++);
+
+ /* Start selection on first col of top_row */
+ guac_terminal_select_start(terminal, top_row, 0, GUAC_TERMINAL_COLUMN_SIDE_LEFT);
+
+ /* End selection on last col of bottom_row */
+ guac_terminal_select_update(terminal, bottom_row, length - 1, GUAC_TERMINAL_COLUMN_SIDE_RIGHT);
}
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
@@@ -2364,21 -2235,12 +2763,21 @@@
/* First click = start selection */
case 0:
+ /* The rectangular selection is requested by pressing
+ * the ALT key at the start of the selection */
+ term->rectangle_selection = term->mod_alt;
+
+ /* Save initial mouse position */
+ term->selection_initial_row = row;
+ term->selection_initial_column = col;
+
+ /* Start selection */
guac_terminal_select_start(term, row, col, side);
break;
-
+
/* Second click = word selection */
case 1:
- guac_terminal_double_click(term, row, col);
+ guac_terminal_double_click(term, row, col, false);
break;
/* third click or more = line selection */
diff --cc src/terminal/terminal/terminal.h
index 399be145,dbb6be19..d1a0bae6
--- a/src/terminal/terminal/terminal.h
+++ b/src/terminal/terminal/terminal.h
@@@ -681,12 -238,13 +681,19 @@@ typedef struct guac_terminal_options
*/
int backspace;
+ /**
+ * The family of codes (e.g. vt100) which will be used when you push
+ * the function and keypad keys.
+ */
+ char* func_keys_and_keypad;
+
+ /**
+ * Whether to use Linux terminal type compatibility mode for key encoding.
+ * When true, key sequences match those expected by a Linux console
+ * ($TERM=linux). When false, standard xterm/VT220 sequences are used.
+ */
+ bool linux_console_keys;
+
} guac_terminal_options;
/**