Use TERM env var / terminfo to decide whether to send ECH. Fixes tmux bug.

Closes #28 github issue.
This commit is contained in:
Keith Winstein
2012-03-10 05:41:03 -05:00
parent ac16f9127c
commit b8457dd308
13 changed files with 114 additions and 23 deletions
+1
View File
@@ -88,6 +88,7 @@ Getting Mosh
* [GNU Autotools][] * [GNU Autotools][]
* the [Protocol Buffers][] library and compiler * the [Protocol Buffers][] library and compiler
* [Boost][] * [Boost][]
* `ncurses`
* `libutempter` * `libutempter`
* `zlib` * `zlib`
* the Perl module [IO::Pty][] * the Perl module [IO::Pty][]
+3 -1
View File
@@ -75,7 +75,7 @@ AS_IF([test x"$with_skalibs" != xno],
AC_SUBST([STDDJB_LDFLAGS], ["$SKALIBS_LDFLAGS -lstddjb"])]) AC_SUBST([STDDJB_LDFLAGS], ["$SKALIBS_LDFLAGS -lstddjb"])])
# Checks for header files. # Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h langinfo.h limits.h locale.h netinet/in.h pty.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h termios.h unistd.h util.h wchar.h wctype.h]) AC_CHECK_HEADERS([arpa/inet.h curses.h fcntl.h langinfo.h limits.h locale.h netinet/in.h pty.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h term.h termios.h unistd.h util.h wchar.h wctype.h])
# Checks for typedefs, structures, and compiler characteristics. # Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL AC_HEADER_STDBOOL
@@ -99,6 +99,8 @@ AC_CHECK_FUNCS([gettimeofday inet_ntoa iswprint memchr memset nl_langinfo setenv
AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define if clock_gettime is available.])]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define if clock_gettime is available.])])
AC_CHECK_LIB([ncurses], [setupterm])
AC_CHECK_DECL([mach_absolute_time], AC_CHECK_DECL([mach_absolute_time],
[AC_DEFINE([HAVE_MACH_ABSOLUTE_TIME], [1], [AC_DEFINE([HAVE_MACH_ABSOLUTE_TIME], [1],
[Define if mach_absolute_time is available.])], [Define if mach_absolute_time is available.])],
+1 -1
View File
@@ -2,7 +2,7 @@ Source: mosh
Section: net Section: net
Priority: optional Priority: optional
Maintainer: Keith Winstein <keithw@mit.edu> Maintainer: Keith Winstein <keithw@mit.edu>
Build-Depends: debhelper (>= 7.0.50), autotools-dev, protobuf-compiler, libprotobuf-dev, dh-autoreconf, pkg-config, libutempter-dev, libboost-dev, zlib1g-dev, skalibs-dev Build-Depends: debhelper (>= 7.0.50), autotools-dev, protobuf-compiler, libprotobuf-dev, dh-autoreconf, pkg-config, libutempter-dev, libboost-dev, zlib1g-dev, skalibs-dev, libncurses5-dev
Standards-Version: 3.9.3 Standards-Version: 3.9.3
Homepage: http://mosh.mit.edu Homepage: http://mosh.mit.edu
Vcs-Git: git://github.com/keithw/mosh.git Vcs-Git: git://github.com/keithw/mosh.git
+8 -4
View File
@@ -148,7 +148,8 @@ int main( void )
} }
/* Print a frame if the last frame was more than 1/50 seconds ago */ /* Print a frame if the last frame was more than 1/50 seconds ago */
bool tick( Terminal::Framebuffer &state, const Terminal::Framebuffer &new_frame ) bool tick( Terminal::Framebuffer &state, const Terminal::Framebuffer &new_frame,
const Terminal::Display &display )
{ {
static bool initialized = false; static bool initialized = false;
static struct timeval last_time; static struct timeval last_time;
@@ -164,7 +165,7 @@ bool tick( Terminal::Framebuffer &state, const Terminal::Framebuffer &new_frame
if ( (!initialized) if ( (!initialized)
|| (diff >= 0.02) ) { || (diff >= 0.02) ) {
std::string update = Terminal::Display::new_frame( initialized, state, new_frame ); std::string update = display.new_frame( initialized, state, new_frame );
swrite( STDOUT_FILENO, update.c_str() ); swrite( STDOUT_FILENO, update.c_str() );
state = new_frame; state = new_frame;
@@ -222,6 +223,9 @@ void emulate_terminal( int fd )
Terminal::Complete complete( window_size.ws_col, window_size.ws_row ); Terminal::Complete complete( window_size.ws_col, window_size.ws_row );
Terminal::Framebuffer state( window_size.ws_col, window_size.ws_row ); Terminal::Framebuffer state( window_size.ws_col, window_size.ws_row );
/* open display */
Terminal::Display display( true ); /* use TERM to initialize */
struct pollfd pollfds[ 3 ]; struct pollfd pollfds[ 3 ];
pollfds[ 0 ].fd = STDIN_FILENO; pollfds[ 0 ].fd = STDIN_FILENO;
@@ -310,14 +314,14 @@ void emulate_terminal( int fd )
break; break;
} }
if ( tick( state, complete.get_fb()) ) { /* there was a frame */ if ( tick( state, complete.get_fb(), display ) ) { /* there was a frame */
poll_timeout = -1; poll_timeout = -1;
} else { } else {
poll_timeout = 20; poll_timeout = 20;
} }
} }
std::string update = Terminal::Display::new_frame( true, state, complete.get_fb() ); std::string update = display.new_frame( true, state, complete.get_fb() );
swrite( STDOUT_FILENO, update.c_str() ); swrite( STDOUT_FILENO, update.c_str() );
swrite( STDOUT_FILENO, Terminal::Emulator::close().c_str() ); swrite( STDOUT_FILENO, Terminal::Emulator::close().c_str() );
+2
View File
@@ -89,6 +89,8 @@ int main( int argc, char *argv[] )
} catch ( Crypto::CryptoException e ) { } catch ( Crypto::CryptoException e ) {
fprintf( stderr, "Crypto exception: %s\r\n", fprintf( stderr, "Crypto exception: %s\r\n",
e.text.c_str() ); e.text.c_str() );
} catch ( std::string s ) {
fprintf( stderr, "Error: %s\r\n", s.c_str() );
} }
printf( "\n[mosh is exiting.]\n" ); printf( "\n[mosh is exiting.]\n" );
+4 -4
View File
@@ -130,7 +130,7 @@ void STMClient::main_init( void )
local_framebuffer = new Terminal::Framebuffer( window_size.ws_col, window_size.ws_row ); local_framebuffer = new Terminal::Framebuffer( window_size.ws_col, window_size.ws_row );
/* initialize screen */ /* initialize screen */
string init = Terminal::Display::new_frame( false, *local_framebuffer, *local_framebuffer ); string init = display.new_frame( false, *local_framebuffer, *local_framebuffer );
swrite( STDOUT_FILENO, init.data(), init.size() ); swrite( STDOUT_FILENO, init.data(), init.size() );
/* open network */ /* open network */
@@ -158,9 +158,9 @@ void STMClient::output_new_frame( void )
overlays.apply( new_state ); overlays.apply( new_state );
/* calculate minimal difference from where we are */ /* calculate minimal difference from where we are */
const string diff( Terminal::Display::new_frame( !repaint_requested, const string diff( display.new_frame( !repaint_requested,
*local_framebuffer, *local_framebuffer,
new_state ) ); new_state ) );
swrite( STDOUT_FILENO, diff.data(), diff.size() ); swrite( STDOUT_FILENO, diff.data(), diff.size() );
*local_framebuffer = new_state; *local_framebuffer = new_state;
+2
View File
@@ -42,6 +42,7 @@ private:
Terminal::Framebuffer *local_framebuffer; Terminal::Framebuffer *local_framebuffer;
Overlay::OverlayManager overlays; Overlay::OverlayManager overlays;
Network::Transport< Network::UserStream, Terminal::Complete > *network; Network::Transport< Network::UserStream, Terminal::Complete > *network;
Terminal::Display display;
bool repaint_requested, quit_sequence_started; bool repaint_requested, quit_sequence_started;
@@ -61,6 +62,7 @@ public:
local_framebuffer( NULL ), local_framebuffer( NULL ),
overlays(), overlays(),
network( NULL ), network( NULL ),
display( true ), /* use TERM environment var to initialize display */
repaint_requested( false ), repaint_requested( false ),
quit_sequence_started( false ) quit_sequence_started( false )
{ {
+1 -1
View File
@@ -75,7 +75,7 @@ string Complete::diff_from( const Complete &existing ) const
new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() ); new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() );
} }
Instruction *new_inst = output.add_instruction(); Instruction *new_inst = output.add_instruction();
new_inst->MutableExtension( hostbytes )->set_hoststring( Terminal::Display::new_frame( true, existing.get_fb(), terminal.get_fb() ) ); new_inst->MutableExtension( hostbytes )->set_hoststring( display.new_frame( true, existing.get_fb(), terminal.get_fb() ) );
} }
return output.SerializeAsString(); return output.SerializeAsString();
+2 -1
View File
@@ -32,6 +32,7 @@ namespace Terminal {
private: private:
Parser::UTF8Parser parser; Parser::UTF8Parser parser;
Terminal::Emulator terminal; Terminal::Emulator terminal;
Terminal::Display display;
std::list< std::pair<uint64_t, uint64_t> > input_history; std::list< std::pair<uint64_t, uint64_t> > input_history;
uint64_t echo_ack; uint64_t echo_ack;
@@ -39,7 +40,7 @@ namespace Terminal {
static const int ECHO_TIMEOUT = 50; /* for late ack */ static const int ECHO_TIMEOUT = 50; /* for late ack */
public: public:
Complete( size_t width, size_t height ) : parser(), terminal( width, height ), Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ),
input_history(), echo_ack( 0 ) {} input_history(), echo_ack( 0 ) {}
std::string act( const std::string &str ); std::string act( const std::string &str );
+1 -2
View File
@@ -3,5 +3,4 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) -fno-default-inline -pipe
noinst_LIBRARIES = libmoshterminal.a noinst_LIBRARIES = libmoshterminal.a
libmoshterminal_a_SOURCES = parseraction.cc parseraction.h parser.cc parser.h parserstate.cc parserstatefamily.h parserstate.h parsertransition.h terminal.cc terminaldispatcher.cc terminaldispatcher.h terminaldisplay.cc terminaldisplay.h terminalframebuffer.cc terminalframebuffer.h terminalfunctions.cc terminal.h terminaluserinput.cc terminaluserinput.h libmoshterminal_a_SOURCES = parseraction.cc parseraction.h parser.cc parser.h parserstate.cc parserstatefamily.h parserstate.h parsertransition.h terminal.cc terminaldispatcher.cc terminaldispatcher.h terminaldisplay.cc terminaldisplayinit.cc terminaldisplay.h terminalframebuffer.cc terminalframebuffer.h terminalfunctions.cc terminal.h terminaluserinput.cc terminaluserinput.h
+21 -6
View File
@@ -25,7 +25,7 @@ using namespace Terminal;
/* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */ /* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */
std::string Display::new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) std::string Display::new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const
{ {
FrameState frame( last ); FrameState frame( last );
@@ -201,7 +201,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
return frame.str; return frame.str;
} }
void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) const
{ {
char tmp[ 64 ]; char tmp[ 64 ];
@@ -238,13 +238,28 @@ void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &
} }
} }
frame.x += clear_count; assert( frame.x + clear_count <= f.ds.get_width() );
if ( frame.x >= f.ds.get_width() ) {
/* can we go to the end of the line? */
if ( frame.x + clear_count == f.ds.get_width() ) {
snprintf( tmp, 64, "\033[K" ); snprintf( tmp, 64, "\033[K" );
frame.append( tmp );
frame.x += clear_count;
} else { } else {
snprintf( tmp, 64, "\033[%dX", clear_count ); if ( has_ech ) {
if ( clear_count == 1 ) {
frame.append( "\033[X" );
} else {
snprintf( tmp, 64, "\033[%dX", clear_count );
frame.append( tmp );
}
frame.x += clear_count;
} else { /* no ECH, so just print a space */
frame.append( " " );
frame.cursor_x++;
frame.x++;
}
} }
frame.append( tmp );
return; return;
} }
+7 -2
View File
@@ -49,10 +49,15 @@ namespace Terminal {
class Display { class Display {
private: private:
static void put_cell( bool initialized, FrameState &frame, const Framebuffer &f ); bool has_ech; /* erase character is part of vt200 but not supported by tmux
(or by "screen" terminfo entry, which is what tmux advertises) */
void put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) const;
public: public:
static std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ); std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const;
Display( bool use_environment );
}; };
} }
+60
View File
@@ -0,0 +1,60 @@
/*
Mosh: the mobile shell
Copyright 2012 Keith Winstein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This is in its own file because otherwise the ncurses #defines
alias our own variable names. */
#include "terminaldisplay.h"
#include <string>
#include <curses.h>
#include <term.h>
using namespace Terminal;
Display::Display( bool use_environment )
: has_ech( true )
{
if ( use_environment ) {
int errret = -2;
int ret = setupterm( (char *)0, 1, &errret );
if ( ret != OK ) {
switch ( errret ) {
case 1:
throw std::string( "Terminal is hardcopy and cannot be used by curses applications." );
break;
case 0:
throw std::string( "Unknown terminal type." );
break;
case -1:
throw std::string( "Terminfo database could not be found." );
break;
default:
throw std::string( "Unknown terminfo error." );
break;
}
}
char *val = tigetstr( "ech" );
if ( val <= 0 ) {
has_ech = false;
}
}
}