Line data Source code
1 : /* 2 : Mosh: the mobile shell 3 : Copyright 2012 Keith Winstein 4 : 5 : This program is free software: you can redistribute it and/or modify 6 : it under the terms of the GNU General Public License as published by 7 : the Free Software Foundation, either version 3 of the License, or 8 : (at your option) any later version. 9 : 10 : This program is distributed in the hope that it will be useful, 11 : but WITHOUT ANY WARRANTY; without even the implied warranty of 12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 : GNU General Public License for more details. 14 : 15 : You should have received a copy of the GNU General Public License 16 : along with this program. If not, see <http://www.gnu.org/licenses/>. 17 : 18 : In addition, as a special exception, the copyright holders give 19 : permission to link the code of portions of this program with the 20 : OpenSSL library under certain conditions as described in each 21 : individual source file, and distribute linked combinations including 22 : the two. 23 : 24 : You must obey the GNU General Public License in all respects for all 25 : of the code used other than OpenSSL. If you modify file(s) with this 26 : exception, you may extend this exception to your version of the 27 : file(s), but you are not obligated to do so. If you do not wish to do 28 : so, delete this exception statement from your version. If you delete 29 : this exception statement from all source files in the program, then 30 : also delete it here. 31 : */ 32 : 33 : #include "completeterminal.h" 34 : #include "fatal_assert.h" 35 : 36 : #include "hostinput.pb.h" 37 : 38 : #include <limits.h> 39 : 40 : using namespace std; 41 : using namespace Parser; 42 : using namespace Terminal; 43 : using namespace HostBuffers; 44 : 45 26916 : string Complete::act( const string &str ) 46 : { 47 72216454 : for ( unsigned int i = 0; i < str.size(); i++ ) { 48 : /* parse octet into up to three actions */ 49 72189538 : parser.input( str[ i ], actions ); 50 : 51 : /* apply actions to terminal and delete them */ 52 144377982 : for ( Actions::iterator it = actions.begin(); 53 144377982 : it != actions.end(); 54 144377982 : it++ ) { 55 72188444 : Action &act = **it; 56 72188444 : act.act_on_terminal( &terminal ); 57 : } 58 72189538 : actions.clear(); 59 : } 60 : 61 26916 : return terminal.read_octets_to_host(); 62 : } 63 : 64 1765 : string Complete::act( const Action &act ) 65 : { 66 : /* apply action to terminal */ 67 1765 : act.act_on_terminal( &terminal ); 68 1765 : return terminal.read_octets_to_host(); 69 : } 70 : 71 : /* interface for Network::Transport */ 72 18191 : string Complete::diff_from( const Complete &existing ) const 73 : { 74 18191 : HostBuffers::HostMessage output; 75 : 76 18191 : if ( existing.get_echo_ack() != get_echo_ack() ) { 77 9479 : assert( get_echo_ack() >= existing.get_echo_ack() ); 78 9479 : Instruction *new_echo = output.add_instruction(); 79 9479 : new_echo->MutableExtension( echoack )->set_echo_ack_num( get_echo_ack() ); 80 : } 81 : 82 18191 : if ( !(existing.get_fb() == get_fb()) ) { 83 17215 : if ( (existing.get_fb().ds.get_width() != terminal.get_fb().ds.get_width()) 84 17215 : || (existing.get_fb().ds.get_height() != terminal.get_fb().ds.get_height()) ) { 85 88 : Instruction *new_res = output.add_instruction(); 86 88 : new_res->MutableExtension( resize )->set_width( terminal.get_fb().ds.get_width() ); 87 88 : new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() ); 88 : } 89 17215 : string update = display.new_frame( true, existing.get_fb(), terminal.get_fb() ); 90 17215 : if ( !update.empty() ) { 91 13302 : Instruction *new_inst = output.add_instruction(); 92 30517 : new_inst->MutableExtension( hostbytes )->set_hoststring( update ); 93 : } 94 17215 : } 95 : 96 36382 : return output.SerializeAsString(); 97 18191 : } 98 : 99 10754 : string Complete::init_diff( void ) const 100 : { 101 10754 : return diff_from( Complete( get_fb().ds.get_width(), get_fb().ds.get_height() )); 102 : } 103 : 104 8078 : void Complete::apply_string( const string & diff ) 105 : { 106 8078 : HostBuffers::HostMessage input; 107 8078 : fatal_assert( input.ParseFromString( diff ) ); 108 : 109 14381 : for ( int i = 0; i < input.instruction_size(); i++ ) { 110 6303 : if ( input.instruction( i ).HasExtension( hostbytes ) ) { 111 5460 : string terminal_to_host = act( input.instruction( i ).GetExtension( hostbytes ).hoststring() ); 112 5460 : assert( terminal_to_host.empty() ); /* server never interrogates client terminal */ 113 6303 : } else if ( input.instruction( i ).HasExtension( resize ) ) { 114 264 : act( Resize( input.instruction( i ).GetExtension( resize ).width(), 115 88 : input.instruction( i ).GetExtension( resize ).height() ) ); 116 755 : } else if ( input.instruction( i ).HasExtension( echoack ) ) { 117 755 : uint64_t inst_echo_ack_num = input.instruction( i ).GetExtension( echoack ).echo_ack_num(); 118 755 : assert( inst_echo_ack_num >= echo_ack ); 119 755 : echo_ack = inst_echo_ack_num; 120 : } 121 : } 122 8078 : } 123 : 124 67291 : bool Complete::operator==( Complete const &x ) const 125 : { 126 : // assert( parser == x.parser ); /* parser state is irrelevant for us */ 127 67291 : return (terminal == x.terminal) && (echo_ack == x.echo_ack); 128 : } 129 : 130 26592 : bool Complete::set_echo_ack( uint64_t now ) 131 : { 132 26592 : bool ret = false; 133 26592 : uint64_t newest_echo_ack = 0; 134 : 135 55006 : for ( input_history_type::const_iterator i = input_history.begin(); 136 55006 : i != input_history.end(); 137 55006 : i++ ) { 138 28414 : if ( i->second <= now - ECHO_TIMEOUT ) { 139 25148 : newest_echo_ack = i->first; 140 : } 141 : } 142 : 143 : for ( input_history_type::iterator i = input_history.begin(); 144 55006 : i != input_history.end(); ) { 145 28414 : input_history_type::iterator i_next = i; 146 28414 : i_next++; 147 28414 : if ( i->first < newest_echo_ack ) { 148 308 : input_history.erase( i ); 149 : } 150 : i = i_next; 151 : } 152 : 153 26592 : if ( echo_ack != newest_echo_ack ) { 154 734 : ret = true; 155 : } 156 : 157 26592 : echo_ack = newest_echo_ack; 158 : 159 26592 : return ret; 160 : } 161 : 162 958 : void Complete::register_input_frame( uint64_t n, uint64_t now ) 163 : { 164 958 : input_history.push_back( std::make_pair( n, now ) ); 165 958 : } 166 : 167 27044 : int Complete::wait_time( uint64_t now ) const 168 : { 169 27044 : if ( input_history.size() < 2 ) { 170 : return INT_MAX; 171 : } 172 : 173 1508 : input_history_type::const_iterator it = input_history.begin(); 174 1508 : it++; 175 : 176 1508 : uint64_t next_echo_ack_time = it->second + ECHO_TIMEOUT; 177 1508 : if ( next_echo_ack_time <= now ) { 178 : return 0; 179 : } 180 1508 : return next_echo_ack_time - now; 181 : } 182 : 183 5377 : bool Complete::compare( const Complete &other ) const 184 : { 185 5377 : bool ret = false; 186 5377 : const Framebuffer &fb = terminal.get_fb(); 187 5377 : const Framebuffer &other_fb = other.terminal.get_fb(); 188 5377 : const int height = fb.ds.get_height(); 189 5377 : const int other_height = other_fb.ds.get_height(); 190 5377 : const int width = fb.ds.get_width(); 191 5377 : const int other_width = other_fb.ds.get_width(); 192 : 193 5377 : if ( height != other_height || width != other_width ) { 194 0 : fprintf( stderr, "Framebuffer size (%dx%d, %dx%d) differs.\n", width, height, other_width, other_height ); 195 0 : return true; 196 : } 197 : 198 134105 : for ( int y = 0; y < height; y++ ) { 199 10384728 : for ( int x = 0; x < width; x++ ) { 200 10256000 : if ( fb.get_cell( y, x )->compare( *other_fb.get_cell( y, x ) ) ) { 201 0 : fprintf( stderr, "Cell (%d, %d) differs.\n", y, x ); 202 0 : ret = true; 203 : } 204 : } 205 : } 206 : 207 5377 : if ( (fb.ds.get_cursor_row() != other_fb.ds.get_cursor_row()) 208 5377 : || (fb.ds.get_cursor_col() != other_fb.ds.get_cursor_col()) ) { 209 0 : fprintf( stderr, "Cursor mismatch: (%d, %d) vs. (%d, %d).\n", 210 : fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), 211 : other_fb.ds.get_cursor_row(), other_fb.ds.get_cursor_col() ); 212 0 : ret = true; 213 : } 214 : /* XXX should compare other terminal state too (mouse mode, bell. etc.) */ 215 : 216 : return ret; 217 : }