diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index bb51d0a6..2a38c1c6 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -69,6 +69,11 @@ typedef struct ssh_guac_client_data { */ char* clipboard_data; + /** + * Whether the alt key is currently being held down. + */ + int mod_alt; + /** * Whether the control key is currently being held down. */ diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 805ce684..ac50b616 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -228,6 +228,11 @@ struct guac_terminal { */ bool automatic_carriage_return; + /** + * Whether insert mode is enabled (DECIM). + */ + bool insert_mode; + }; /** @@ -352,5 +357,21 @@ void guac_terminal_resize(guac_terminal* term, int width, int height); */ void guac_terminal_flush(guac_terminal* terminal); +/** + * Sends the given string as if typed by the user. + */ +int guac_terminal_send_data(guac_terminal* term, const char* data, int length); + +/** + * Sends the given string as if typed by the user. + */ +int guac_terminal_send_string(guac_terminal* term, const char* data); + +/** + * Sends data through STDIN as if typed by the user, using the format + * string given and any args (similar to printf). + */ +int guac_terminal_sendf(guac_terminal* term, const char* format, ...); + #endif diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 8332049d..8f680ac0 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -72,6 +72,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Init client data */ client->data = client_data; client_data->term = term; + client_data->mod_alt = 0; client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; client_data->term_channel = NULL; diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 24be1caa..99896887 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -158,7 +158,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int length = strlen(client_data->clipboard_data); if (length) - return write(term->stdin_pipe_fd[1], + return guac_terminal_send_data(term, client_data->clipboard_data, length); } @@ -228,9 +228,6 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_terminal* term = client_data->term; - /* Get write end of STDIN pipe */ - int fd = term->stdin_pipe_fd[1]; - /* Hide mouse cursor if not already hidden */ if (client_data->current_cursor != client_data->blank_cursor) { pthread_mutex_lock(&(term->lock)); @@ -243,9 +240,10 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { } /* Track modifiers */ - if (keysym == 0xFFE3) { + if (keysym == 0xFFE3) client_data->mod_ctrl = pressed; - } + else if (keysym == 0xFFE9) + client_data->mod_alt = pressed; /* If key pressed */ else if (pressed) { @@ -257,6 +255,10 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { pthread_mutex_unlock(&(term->lock)); } + /* If alt being held, also send escape character */ + if (client_data->mod_alt) + return guac_terminal_send_string(term, "\x1B"); + /* Translate Ctrl+letter to control code */ if (client_data->mod_ctrl) { @@ -272,7 +274,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else return 0; - return write(fd, &data, 1); + return guac_terminal_send_data(term, &data, 1); } @@ -283,50 +285,57 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { char data[5]; length = guac_terminal_encode_utf8(keysym & 0xFFFF, data); - return write(fd, data, length); + return guac_terminal_send_data(term, data, length); } + /* Non-printable keys */ else { - int length = 0; - const char* data = NULL; + if (keysym == 0xFF08) return guac_terminal_send_string(term, "\x7F"); /* Backspace */ + if (keysym == 0xFF09) return guac_terminal_send_string(term, "\x09"); /* Tab */ + if (keysym == 0xFF0D) return guac_terminal_send_string(term, "\x0D"); /* Enter */ + if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */ - if (keysym == 0xFF08) { data = "\x08"; length = 1; } - else if (keysym == 0xFF09) { data = "\x09"; length = 1; } - else if (keysym == 0xFF0D) { data = "\x0D"; length = 1; } - else if (keysym == 0xFF1B) { data = "\x1B"; length = 1; } + if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1BOH"); /* Home */ - /* Arrow keys */ - else if (keysym == 0xFF52) { - if (term->application_cursor_keys) data = "\x1BOA"; - else data = "\x1B[A"; - length = 3; + /* Arrow keys w/ application cursor */ + if (term->application_cursor_keys) { + if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1BOD"); /* Left */ + if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1BOA"); /* Up */ + if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1BOC"); /* Right */ + if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1BOB"); /* Down */ + } + else { + if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1B[D"); /* Left */ + if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1B[A"); /* Up */ + if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1B[C"); /* Right */ + if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1B[B"); /* Down */ } - else if (keysym == 0xFF54) { - if (term->application_cursor_keys) data = "\x1BOB"; - else data = "\x1B[B"; - length = 3; - } + if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5;3~"); /* Page up */ + if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6;3~"); /* Page down */ + if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1BOF"); /* End */ - else if (keysym == 0xFF53) { - if (term->application_cursor_keys) data = "\x1BOC"; - else data = "\x1B[C"; - length = 3; - } + if (keysym == 0xFF63) return guac_terminal_send_string(term, "\x1B[2~"); /* Insert */ - else if (keysym == 0xFF51) { - if (term->application_cursor_keys) data = "\x1BOD"; - else data = "\x1B[D"; - length = 3; - } + if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1BOP"); /* F1 */ + if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1BOQ"); /* F2 */ + if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1BOR"); /* F3 */ + if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1BOS"); /* F4 */ + if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[15~"); /* F5 */ + if (keysym == 0xFFC3) return guac_terminal_send_string(term, "\x1B[17~"); /* F6 */ + if (keysym == 0xFFC4) return guac_terminal_send_string(term, "\x1B[18~"); /* F7 */ + if (keysym == 0xFFC5) return guac_terminal_send_string(term, "\x1B[19~"); /* F8 */ + if (keysym == 0xFFC6) return guac_terminal_send_string(term, "\x1B[20~"); /* F9 */ + if (keysym == 0xFFC7) return guac_terminal_send_string(term, "\x1B[21~"); /* F10 */ + if (keysym == 0xFFC8) return guac_terminal_send_string(term, "\x1B[22~"); /* F11 */ + if (keysym == 0xFFC9) return guac_terminal_send_string(term, "\x1B[23~"); /* F12 */ - /* Ignore other keys */ - else return 0; + if (keysym == 0xFFFF) return guac_terminal_send_string(term, "\x1B[3~"); /* Delete */ - return write(fd, data, length); + /* Ignore unknown keys */ } } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 76d74623..f7bd7fc0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -37,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include #include @@ -80,6 +81,7 @@ void guac_terminal_reset(guac_terminal* term) { term->text_selected = false; term->application_cursor_keys = false; term->automatic_carriage_return = false; + term->insert_mode = false; /* Clear terminal */ for (row=0; rowterm_height; row++) @@ -755,3 +757,32 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { } +int guac_terminal_send_data(guac_terminal* term, const char* data, int length) { + return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length); +} + +int guac_terminal_send_string(guac_terminal* term, const char* data) { + return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data)); +} + +int guac_terminal_sendf(guac_terminal* term, const char* format, ...) { + + int written; + + va_list ap; + char buffer[1024]; + + /* Print to buffer */ + va_start(ap, format); + written = vsnprintf(buffer, sizeof(buffer)-1, format, ap); + va_end(ap); + + if (written < 0) + return written; + + /* Write to STDIN */ + return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written); + +} + + diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index b8fa4bb2..002f7981 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -36,7 +36,6 @@ * ***** END LICENSE BLOCK ***** */ #include -#include #include "common.h" #include "terminal.h" @@ -58,31 +57,6 @@ */ #define GUAC_TERMINAL_OK "\x1B[0n" - -/** - * Sends data through STDIN as if typed by the user, using the format - * string given and any args (similar to printf). - */ -static int guac_terminal_respond(guac_terminal* term, const char* format, ...) { - - int written; - - va_list ap; - char buffer[1024]; - - /* Print to buffer */ - va_start(ap, format); - written = vsnprintf(buffer, sizeof(buffer)-1, format, ap); - va_end(ap); - - if (written < 0) - return written; - - /* Write to STDIN */ - return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written); - -} - int guac_terminal_echo(guac_terminal* term, char c) { static int bytes_remaining = 0; @@ -140,7 +114,7 @@ int guac_terminal_echo(guac_terminal* term, char c) { /* Enquiry */ case 0x05: - guac_terminal_respond(term, "%s", GUAC_TERMINAL_ANSWERBACK); + guac_terminal_send_string(term, GUAC_TERMINAL_ANSWERBACK); break; /* Bell */ @@ -345,7 +319,7 @@ int guac_terminal_escape(guac_terminal* term, char c) { /* DEC Identify */ case 'Z': - guac_terminal_respond(term, "%s", GUAC_TERMINAL_VT102_ID); + guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID); term->char_handler = guac_terminal_echo; break; @@ -413,6 +387,7 @@ static bool* __guac_terminal_get_flag(guac_terminal* term, int num, char private else if (private_mode == 0) { switch (num) { + case 4: return &(term->insert_mode); /* DECIM */ case 20: return &(term->automatic_carriage_return); /* LF/NL */ } } @@ -696,7 +671,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* c: Identify */ case 'c': if (argv[0] == 0 && private_mode_character == 0) - guac_terminal_respond(term, "%s", GUAC_TERMINAL_VT102_ID); + guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID); break; /* d: Move cursor, current col */ @@ -810,11 +785,11 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Device status report */ if (argv[0] == 5 && private_mode_character == 0) - guac_terminal_respond(term, "%s", GUAC_TERMINAL_OK); + guac_terminal_send_string(term, GUAC_TERMINAL_OK); /* Cursor position report */ else if (argv[0] == 6 && private_mode_character == 0) - guac_terminal_respond(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1); + guac_terminal_sendf(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1); break;