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 <assert.h> 34 : #include <string.h> 35 : #include <stdlib.h> 36 : #include <unistd.h> 37 : #include <typeinfo> 38 : 39 : #include "terminal.h" 40 : 41 : using namespace Terminal; 42 : 43 11654 : Emulator::Emulator( size_t s_width, size_t s_height ) 44 11654 : : fb( s_width, s_height ), dispatch(), user() 45 11654 : {} 46 : 47 28681 : std::string Emulator::read_octets_to_host( void ) 48 : { 49 28681 : std::string ret = dispatch.terminal_to_host; 50 28681 : dispatch.terminal_to_host.clear(); 51 28681 : return ret; 52 : } 53 : 54 47873448 : void Emulator::execute( const Parser::Execute *act ) 55 : { 56 47873448 : dispatch.dispatch( CONTROL, act, &fb ); 57 47873448 : } 58 : 59 24028354 : void Emulator::print( const Parser::Print *act ) 60 : { 61 24028354 : assert( act->char_present ); 62 : 63 24028354 : const wchar_t ch = act->ch; 64 : 65 : /* 66 : * Check for printing ISO 8859-1 first, it's a cheap way to detect 67 : * some common narrow characters. 68 : */ 69 24028354 : const int chwidth = ch == L'\0' ? -1 : ( Cell::isprint_iso8859_1( ch ) ? 1 : wcwidth( ch )); 70 : 71 24028354 : Cell *this_cell = fb.get_mutable_cell(); 72 : 73 24028354 : switch ( chwidth ) { 74 24028342 : case 1: /* normal character */ 75 24028342 : case 2: /* wide character */ 76 24028342 : if ( fb.ds.auto_wrap_mode && fb.ds.next_print_will_wrap ) { 77 292 : fb.get_mutable_row( -1 )->set_wrap( true ); 78 292 : fb.ds.move_col( 0 ); 79 292 : fb.move_rows_autoscroll( 1 ); 80 292 : this_cell = NULL; 81 24028050 : } else if ( fb.ds.auto_wrap_mode 82 24028050 : && (chwidth == 2) 83 24028050 : && (fb.ds.get_cursor_col() == fb.ds.get_width() - 1) ) { 84 : /* wrap 2-cell chars if no room, even without will-wrap flag */ 85 0 : fb.reset_cell( this_cell ); 86 0 : fb.get_mutable_row( -1 )->set_wrap( false ); 87 : /* There doesn't seem to be a consistent way to get the 88 : downstream terminal emulator to set the wrap-around 89 : copy-and-paste flag on a row that ends with an empty cell 90 : because a wide char was wrapped to the next line. */ 91 0 : fb.ds.move_col( 0 ); 92 0 : fb.move_rows_autoscroll( 1 ); 93 0 : this_cell = NULL; 94 : } 95 : 96 24028342 : if ( fb.ds.insert_mode ) { 97 0 : for ( int i = 0; i < chwidth; i++ ) { 98 0 : fb.insert_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() ); 99 : } 100 : this_cell = NULL; 101 : } 102 : 103 24028342 : if (!this_cell) { 104 292 : this_cell = fb.get_mutable_cell(); 105 : } 106 : 107 24028342 : fb.reset_cell( this_cell ); 108 24028342 : this_cell->append( ch ); 109 24028342 : this_cell->set_wide( chwidth == 2 ); /* chwidth had better be 1 or 2 here */ 110 24028342 : fb.apply_renditions_to_cell( this_cell ); 111 : 112 24028342 : if ( chwidth == 2 113 24028342 : && fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { /* erase overlapped cell */ 114 0 : fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) ); 115 : } 116 : 117 24028342 : fb.ds.move_col( chwidth, true, true ); 118 : 119 24028342 : break; 120 12 : case 0: /* combining character */ 121 12 : { 122 12 : Cell *combining_cell = fb.get_combining_cell(); /* can be null if we were resized */ 123 12 : if ( combining_cell == NULL ) { /* character is now offscreen */ 124 : break; 125 : } 126 : 127 12 : if ( combining_cell->empty() ) { 128 : /* cell starts with combining character */ 129 : /* ... but isn't necessarily the target for a new 130 : base character [e.g. start of line], if the 131 : combining character has been cleared with 132 : a sequence like ED ("J") or EL ("K") */ 133 4 : assert( !combining_cell->get_wide() ); 134 4 : combining_cell->set_fallback( true ); 135 4 : fb.ds.move_col( 1, true, true ); 136 : } 137 12 : if ( !combining_cell->full() ) { 138 12 : combining_cell->append( ch ); 139 : } 140 : } 141 : break; 142 : case -1: /* unprintable character */ 143 : break; 144 0 : default: 145 0 : assert( !"unexpected character width from wcwidth()" ); 146 : break; 147 : } 148 24028354 : } 149 : 150 30017 : void Emulator::CSI_dispatch( const Parser::CSI_Dispatch *act ) 151 : { 152 30017 : dispatch.dispatch( CSI, act, &fb ); 153 30017 : } 154 : 155 0 : void Emulator::OSC_end( const Parser::OSC_End *act ) 156 : { 157 0 : dispatch.OSC_dispatch( act, &fb ); 158 0 : } 159 : 160 4 : void Emulator::Esc_dispatch( const Parser::Esc_Dispatch *act ) 161 : { 162 : /* handle 7-bit ESC-encoding of C1 control characters */ 163 8 : if ( (dispatch.get_dispatch_chars().size() == 0) 164 4 : && (0x40 <= act->ch) 165 4 : && (act->ch <= 0x5F) ) { 166 0 : Parser::Esc_Dispatch act2 = *act; 167 0 : act2.ch += 0x40; 168 0 : dispatch.dispatch( CONTROL, &act2, &fb ); 169 0 : } else { 170 4 : dispatch.dispatch( ESCAPE, act, &fb ); 171 : } 172 4 : } 173 : 174 584 : void Emulator::resize( size_t s_width, size_t s_height ) 175 : { 176 584 : fb.resize( s_width, s_height ); 177 584 : } 178 : 179 67291 : bool Emulator::operator==( Emulator const &x ) const 180 : { 181 : /* dispatcher and user are irrelevant for us */ 182 67291 : return fb == x.fb; 183 : }