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