Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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.
  • Loading branch information...
commit f960a8bcf56294ca7aafe3c14fc8c8c4c3941535 1 parent 4792992
@rinne rinne authored committed
Showing with 89 additions and 12 deletions.
  1. +79 −11 src/frontend/stmclient.cc
  2. +10 −1 src/frontend/stmclient.h
View
90 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;
}
View
11 src/frontend/stmclient.h
@@ -48,6 +48,12 @@ class STMClient {
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 @@ class STMClient {
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 @@ class STMClient {
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 @@ class STMClient {
display( true ), /* use TERM environment var to initialize display */
connecting_notification(),
repaint_requested( false ),
+ lf_entered( false ),
quit_sequence_started( false ),
clean_shutdown( false )
{
Please sign in to comment.
Something went wrong with that request. Please try again.