diff options
author | Timo J. Rinne <tri@iki.fi> | 2013-05-16 18:09:15 +0000 |
---|---|---|
committer | Keith Winstein <keithw@mit.edu> | 2013-05-17 15:05:33 -0400 |
commit | f960a8bcf56294ca7aafe3c14fc8c8c4c3941535 (patch) | |
tree | dd31c8ab27cd1c0b0db1d0d03181a9adb1a5aea6 | |
parent | 4792992afa78d606fff35346bcce5a819fc72b69 (diff) |
mosh-client: Make terminal escape character configurable
Uses environment variable MOSH_ESCAPE_KEY. Defaults to current Ctrl-^
which is somewhat problematic for many non-US keyboards.
Signed-off-by: Timo J. Rinne <tri@iki.fi>
Closes #425. Closes #215.
-rw-r--r-- | src/frontend/stmclient.cc | 90 | ||||
-rw-r--r-- | src/frontend/stmclient.h | 11 |
2 files changed, 89 insertions, 12 deletions
diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc index 7d68ab1..c2ff2a5 100644 --- a/src/frontend/stmclient.cc +++ b/src/frontend/stmclient.cc @@ -121,6 +121,72 @@ void STMClient::init( void ) overlays.set_title_prefix( wstring( L"[mosh] " ) ); } + /* Set terminal escape key. */ + const char *escape_key_env; + if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) { + if ( strlen( escape_key_env ) == 1 ) { + escape_key = (int)escape_key_env[0]; + if ( (escape_key > 0) || (escape_key < 128) ) { + if ( escape_key < 32 ) { + /* If escape is ctrl-something, pass it with repeating the key without ctrl. */ + escape_pass_key = escape_key + (int)'@'; + } else { + /* If escape is something else, pass it with repeating the key itself. */ + escape_pass_key = escape_key; + } + if ( escape_pass_key >= 'A' && escape_pass_key <= 'Z' ) { + /* If escape pass is an upper case character, define optional version + as lower case of the same. */ + escape_pass_key2 = escape_pass_key + (int)'a' - (int)'A'; + } else { + escape_pass_key2 = escape_pass_key; + } + } else { + escape_key = 0x1E; + escape_pass_key = '^'; + escape_pass_key2 = '^'; + } + } else if ( strlen( escape_key_env ) == 0 ) { + escape_key = -1; + } else { + escape_key = 0x1E; + escape_pass_key = '^'; + escape_pass_key2 = '^'; + } + } else { + escape_key = 0x1E; + escape_pass_key = '^'; + escape_pass_key2 = '^'; + } + + /* There are so many better ways to shoot oneself into leg than + setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn + that we just won't allow that. */ + if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C || escape_key == 0x0D ) { + escape_key = 0x1E; + escape_pass_key = '^'; + escape_pass_key2 = '^'; + } + + /* Adjust escape help differently if escape is a control character. */ + if ( escape_key > 0 ) { + char escape_pass_name_buf[16]; + char escape_key_name_buf[16]; + sprintf(escape_pass_name_buf, "\"%c\"", escape_pass_key); + if (escape_key < 32) { + sprintf(escape_key_name_buf, "Ctrl-%c", escape_pass_key); + escape_requires_lf = false; + } else { + sprintf(escape_key_name_buf, "\"%c\"", escape_key); + escape_requires_lf = true; + } + string tmp; + tmp = string( escape_pass_name_buf ); + wstring escape_pass_name = std::wstring(tmp.begin(), tmp.end()); + tmp = string( escape_key_name_buf ); + wstring escape_key_name = std::wstring(tmp.begin(), tmp.end()); + escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name; + } wchar_t tmp[ 128 ]; swprintf( tmp, 128, L"Nothing received from server on UDP port %d.", port ); connecting_notification = wstring( tmp ); @@ -255,8 +321,6 @@ bool STMClient::process_user_input( int fd ) overlays.get_prediction_engine().new_user_byte( the_byte, *local_framebuffer ); - const static wstring help_message( L"Commands: Ctrl-Z suspends, \".\" quits, \"^\" gives literal Ctrl-^" ); - if ( quit_sequence_started ) { if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */ if ( network->has_remote_addr() && (!network->shutdown_in_progress()) ) { @@ -266,7 +330,7 @@ bool STMClient::process_user_input( int fd ) } else { return false; } - } else if ( the_byte == 0x1a ) { /* Suspend sequence is Ctrl-^ Ctrl-Z */ + } else if ( the_byte == 0x1a ) { /* Suspend sequence is escape_key Ctrl-Z */ /* Restore terminal and terminal-driver state */ swrite( STDOUT_FILENO, display.close().c_str() ); @@ -283,30 +347,34 @@ bool STMClient::process_user_input( int fd ) kill( 0, SIGSTOP ); resume(); - } else if ( the_byte == '^' ) { - /* Emulation sequence to type Ctrl-^ is Ctrl-^ ^ */ - network->get_current_state().push_back( Parser::UserByte( 0x1E ) ); + } else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) { + /* Emulation sequence to type escape_key is escape_key + + escape_pass_key (that is escape key without Ctrl) */ + network->get_current_state().push_back( Parser::UserByte( escape_key ) ); } else { - /* Ctrl-^ followed by anything other than . and ^ gets sent literally */ - network->get_current_state().push_back( Parser::UserByte( 0x1E ) ); + /* Escape key followed by anything other than . and ^ gets sent literally */ + network->get_current_state().push_back( Parser::UserByte( escape_key ) ); network->get_current_state().push_back( Parser::UserByte( the_byte ) ); } quit_sequence_started = false; - if ( overlays.get_notification_engine().get_notification_string() == help_message ) { + if ( overlays.get_notification_engine().get_notification_string() == escape_key_help ) { overlays.get_notification_engine().set_notification_string( L"" ); } continue; } - quit_sequence_started = (the_byte == 0x1E); + quit_sequence_started = (escape_key > 0) && (the_byte == escape_key) && (lf_entered || (! escape_requires_lf)); if ( quit_sequence_started ) { - overlays.get_notification_engine().set_notification_string( help_message, true, false ); + lf_entered = false; + overlays.get_notification_engine().set_notification_string( escape_key_help, true, false ); continue; } + lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */ + if ( the_byte == 0x0C ) { /* Ctrl-L */ repaint_requested = true; } diff --git a/src/frontend/stmclient.h b/src/frontend/stmclient.h index 51150c6..e4bab9b 100644 --- a/src/frontend/stmclient.h +++ b/src/frontend/stmclient.h @@ -48,6 +48,12 @@ private: int port; std::string key; + int escape_key; + int escape_pass_key; + int escape_pass_key2; + bool escape_requires_lf; + std::wstring escape_key_help; + struct termios saved_termios, raw_termios; struct winsize window_size; @@ -58,7 +64,7 @@ private: Terminal::Display display; std::wstring connecting_notification; - bool repaint_requested, quit_sequence_started; + bool repaint_requested, lf_entered, quit_sequence_started; bool clean_shutdown; void main_init( void ); @@ -79,6 +85,8 @@ private: public: STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode ) : ip( s_ip ), port( s_port ), key( s_key ), + escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ), + escape_requires_lf( false ), escape_key_help( L"?" ), saved_termios(), raw_termios(), window_size(), local_framebuffer( NULL ), @@ -88,6 +96,7 @@ public: display( true ), /* use TERM environment var to initialize display */ connecting_notification(), repaint_requested( false ), + lf_entered( false ), quit_sequence_started( false ), clean_shutdown( false ) { |