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.
This commit is contained in:
Timo J. Rinne
2013-05-16 18:09:15 +00:00
committed by Keith Winstein
parent 4792992afa
commit f960a8bcf5
2 changed files with 89 additions and 12 deletions
+79 -11
View File
@@ -121,6 +121,72 @@ void STMClient::init( void )
overlays.set_title_prefix( wstring( L"[mosh] " ) ); 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 ]; wchar_t tmp[ 128 ];
swprintf( tmp, 128, L"Nothing received from server on UDP port %d.", port ); swprintf( tmp, 128, L"Nothing received from server on UDP port %d.", port );
connecting_notification = wstring( tmp ); 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 ); 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 ( quit_sequence_started ) {
if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */ if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */
if ( network->has_remote_addr() && (!network->shutdown_in_progress()) ) { if ( network->has_remote_addr() && (!network->shutdown_in_progress()) ) {
@@ -266,7 +330,7 @@ bool STMClient::process_user_input( int fd )
} else { } else {
return false; 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 */ /* Restore terminal and terminal-driver state */
swrite( STDOUT_FILENO, display.close().c_str() ); swrite( STDOUT_FILENO, display.close().c_str() );
@@ -283,30 +347,34 @@ bool STMClient::process_user_input( int fd )
kill( 0, SIGSTOP ); kill( 0, SIGSTOP );
resume(); resume();
} else if ( the_byte == '^' ) { } else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) {
/* Emulation sequence to type Ctrl-^ is Ctrl-^ ^ */ /* Emulation sequence to type escape_key is escape_key +
network->get_current_state().push_back( Parser::UserByte( 0x1E ) ); escape_pass_key (that is escape key without Ctrl) */
network->get_current_state().push_back( Parser::UserByte( escape_key ) );
} else { } else {
/* Ctrl-^ followed by anything other than . and ^ gets sent literally */ /* Escape key followed by anything other than . and ^ gets sent literally */
network->get_current_state().push_back( Parser::UserByte( 0x1E ) ); network->get_current_state().push_back( Parser::UserByte( escape_key ) );
network->get_current_state().push_back( Parser::UserByte( the_byte ) ); network->get_current_state().push_back( Parser::UserByte( the_byte ) );
} }
quit_sequence_started = false; 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"" ); overlays.get_notification_engine().set_notification_string( L"" );
} }
continue; 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 ) { 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; continue;
} }
lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */
if ( the_byte == 0x0C ) { /* Ctrl-L */ if ( the_byte == 0x0C ) { /* Ctrl-L */
repaint_requested = true; repaint_requested = true;
} }
+10 -1
View File
@@ -48,6 +48,12 @@ private:
int port; int port;
std::string key; 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 termios saved_termios, raw_termios;
struct winsize window_size; struct winsize window_size;
@@ -58,7 +64,7 @@ private:
Terminal::Display display; Terminal::Display display;
std::wstring connecting_notification; std::wstring connecting_notification;
bool repaint_requested, quit_sequence_started; bool repaint_requested, lf_entered, quit_sequence_started;
bool clean_shutdown; bool clean_shutdown;
void main_init( void ); void main_init( void );
@@ -79,6 +85,8 @@ private:
public: public:
STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode ) 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 ), : 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(), saved_termios(), raw_termios(),
window_size(), window_size(),
local_framebuffer( NULL ), local_framebuffer( NULL ),
@@ -88,6 +96,7 @@ public:
display( true ), /* use TERM environment var to initialize display */ display( true ), /* use TERM environment var to initialize display */
connecting_notification(), connecting_notification(),
repaint_requested( false ), repaint_requested( false ),
lf_entered( false ),
quit_sequence_started( false ), quit_sequence_started( false ),
clean_shutdown( false ) clean_shutdown( false )
{ {