/src/mosh/src/terminal/terminalfunctions.cc
Line | Count | Source |
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 <algorithm> |
34 | | #include <cstdio> |
35 | | #include <string> |
36 | | #include <utility> |
37 | | #include <vector> |
38 | | |
39 | | #include <unistd.h> |
40 | | |
41 | | #include "src/terminal/parseraction.h" |
42 | | #include "src/terminal/terminalframebuffer.h" |
43 | | #include "terminaldispatcher.h" |
44 | | |
45 | | using namespace Terminal; |
46 | | |
47 | | /* Terminal functions -- routines activated by CSI, escape or a control char */ |
48 | | |
49 | | static void clearline( Framebuffer* fb, int row, int start, int end ) |
50 | 0 | { |
51 | 0 | for ( int col = start; col <= end; col++ ) { |
52 | 0 | fb->reset_cell( fb->get_mutable_cell( row, col ) ); |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | /* erase in line */ |
57 | | static void CSI_EL( Framebuffer* fb, Dispatcher* dispatch ) |
58 | 0 | { |
59 | 0 | switch ( dispatch->getparam( 0, 0 ) ) { |
60 | 0 | case 0: /* default: active position to end of line, inclusive */ |
61 | 0 | clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); |
62 | 0 | break; |
63 | 0 | case 1: /* start of screen to active position, inclusive */ |
64 | 0 | clearline( fb, -1, 0, fb->ds.get_cursor_col() ); |
65 | 0 | break; |
66 | 0 | case 2: /* all of line */ |
67 | 0 | fb->reset_row( fb->get_mutable_row( -1 ) ); |
68 | 0 | break; |
69 | 0 | default: |
70 | 0 | break; |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | static Function func_CSI_EL( CSI, "K", CSI_EL ); |
75 | | |
76 | | /* erase in display */ |
77 | | static void CSI_ED( Framebuffer* fb, Dispatcher* dispatch ) |
78 | 0 | { |
79 | 0 | switch ( dispatch->getparam( 0, 0 ) ) { |
80 | 0 | case 0: /* active position to end of screen, inclusive */ |
81 | 0 | clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); |
82 | 0 | for ( int y = fb->ds.get_cursor_row() + 1; y < fb->ds.get_height(); y++ ) { |
83 | 0 | fb->reset_row( fb->get_mutable_row( y ) ); |
84 | 0 | } |
85 | 0 | break; |
86 | 0 | case 1: /* start of screen to active position, inclusive */ |
87 | 0 | for ( int y = 0; y < fb->ds.get_cursor_row(); y++ ) { |
88 | 0 | fb->reset_row( fb->get_mutable_row( y ) ); |
89 | 0 | } |
90 | 0 | clearline( fb, -1, 0, fb->ds.get_cursor_col() ); |
91 | 0 | break; |
92 | 0 | case 2: /* entire screen */ |
93 | 0 | for ( int y = 0; y < fb->ds.get_height(); y++ ) { |
94 | 0 | fb->reset_row( fb->get_mutable_row( y ) ); |
95 | 0 | } |
96 | 0 | break; |
97 | 0 | default: |
98 | 0 | break; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | static Function func_CSI_ED( CSI, "J", CSI_ED ); |
103 | | |
104 | | /* cursor movement -- relative and absolute */ |
105 | | static void CSI_cursormove( Framebuffer* fb, Dispatcher* dispatch ) |
106 | 0 | { |
107 | 0 | int num = dispatch->getparam( 0, 1 ); |
108 | |
|
109 | 0 | switch ( dispatch->get_dispatch_chars()[0] ) { |
110 | 0 | case 'A': |
111 | 0 | fb->ds.move_row( -num, true ); |
112 | 0 | break; |
113 | 0 | case 'B': |
114 | 0 | fb->ds.move_row( num, true ); |
115 | 0 | break; |
116 | 0 | case 'C': |
117 | 0 | fb->ds.move_col( num, true ); |
118 | 0 | break; |
119 | 0 | case 'D': |
120 | 0 | fb->ds.move_col( -num, true ); |
121 | 0 | break; |
122 | 0 | case 'H': |
123 | 0 | case 'f': |
124 | 0 | fb->ds.move_row( dispatch->getparam( 0, 1 ) - 1 ); |
125 | 0 | fb->ds.move_col( dispatch->getparam( 1, 1 ) - 1 ); |
126 | 0 | break; |
127 | 0 | default: |
128 | 0 | break; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | static Function func_CSI_cursormove_A( CSI, "A", CSI_cursormove ); |
133 | | static Function func_CSI_cursormove_B( CSI, "B", CSI_cursormove ); |
134 | | static Function func_CSI_cursormove_C( CSI, "C", CSI_cursormove ); |
135 | | static Function func_CSI_cursormove_D( CSI, "D", CSI_cursormove ); |
136 | | static Function func_CSI_cursormove_H( CSI, "H", CSI_cursormove ); |
137 | | static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove ); |
138 | | |
139 | | /* device attributes */ |
140 | | static void CSI_DA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch ) |
141 | 0 | { |
142 | 0 | dispatch->terminal_to_host.append( "\033[?62c" ); /* plain vt220 */ |
143 | 0 | } |
144 | | |
145 | | static Function func_CSI_DA( CSI, "c", CSI_DA ); |
146 | | |
147 | | /* secondary device attributes */ |
148 | | static void CSI_SDA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch ) |
149 | 0 | { |
150 | 0 | dispatch->terminal_to_host.append( "\033[>1;10;0c" ); /* plain vt220 */ |
151 | 0 | } |
152 | | |
153 | | static Function func_CSI_SDA( CSI, ">c", CSI_SDA ); |
154 | | |
155 | | /* screen alignment diagnostic */ |
156 | | static void Esc_DECALN( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
157 | 0 | { |
158 | 0 | for ( int y = 0; y < fb->ds.get_height(); y++ ) { |
159 | 0 | for ( int x = 0; x < fb->ds.get_width(); x++ ) { |
160 | 0 | fb->reset_cell( fb->get_mutable_cell( y, x ) ); |
161 | 0 | fb->get_mutable_cell( y, x )->append( 'E' ); |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN ); |
167 | | |
168 | | /* line feed */ |
169 | | static void Ctrl_LF( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
170 | 0 | { |
171 | 0 | fb->move_rows_autoscroll( 1 ); |
172 | 0 | } |
173 | | |
174 | | /* same procedure for index, vertical tab, and form feed control codes */ |
175 | | static Function func_Ctrl_LF( CONTROL, "\x0a", Ctrl_LF ); |
176 | | static Function func_Ctrl_IND( CONTROL, "\x84", Ctrl_LF ); |
177 | | static Function func_Ctrl_VT( CONTROL, "\x0b", Ctrl_LF ); |
178 | | static Function func_Ctrl_FF( CONTROL, "\x0c", Ctrl_LF ); |
179 | | |
180 | | /* carriage return */ |
181 | | static void Ctrl_CR( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
182 | 0 | { |
183 | 0 | fb->ds.move_col( 0 ); |
184 | 0 | } |
185 | | |
186 | | static Function func_Ctrl_CR( CONTROL, "\x0d", Ctrl_CR ); |
187 | | |
188 | | /* backspace */ |
189 | | static void Ctrl_BS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
190 | 0 | { |
191 | 0 | fb->ds.move_col( -1, true ); |
192 | 0 | } |
193 | | |
194 | | static Function func_Ctrl_BS( CONTROL, "\x08", Ctrl_BS ); |
195 | | |
196 | | /* reverse index -- like a backwards line feed */ |
197 | | static void Ctrl_RI( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
198 | 0 | { |
199 | 0 | fb->move_rows_autoscroll( -1 ); |
200 | 0 | } |
201 | | |
202 | | static Function func_Ctrl_RI( CONTROL, "\x8D", Ctrl_RI ); |
203 | | |
204 | | /* newline */ |
205 | | static void Ctrl_NEL( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
206 | 0 | { |
207 | 0 | fb->ds.move_col( 0 ); |
208 | 0 | fb->move_rows_autoscroll( 1 ); |
209 | 0 | } |
210 | | |
211 | | static Function func_Ctrl_NEL( CONTROL, "\x85", Ctrl_NEL ); |
212 | | |
213 | | /* horizontal tab */ |
214 | | static void HT_n( Framebuffer* fb, size_t count ) |
215 | 0 | { |
216 | 0 | int col = fb->ds.get_next_tab( count ); |
217 | 0 | if ( col == -1 ) { /* no tabs, go to end of line */ |
218 | 0 | col = fb->ds.get_width() - 1; |
219 | 0 | } |
220 | | |
221 | | /* A horizontal tab is the only operation that preserves but |
222 | | does not set the wrap state. It also starts a new grapheme. */ |
223 | |
|
224 | 0 | bool wrap_state_save = fb->ds.next_print_will_wrap; |
225 | 0 | fb->ds.move_col( col, false ); |
226 | 0 | fb->ds.next_print_will_wrap = wrap_state_save; |
227 | 0 | } |
228 | | |
229 | | static void Ctrl_HT( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
230 | 0 | { |
231 | 0 | HT_n( fb, 1 ); |
232 | 0 | } |
233 | | static Function func_Ctrl_HT( CONTROL, "\x09", Ctrl_HT, false ); |
234 | | |
235 | | static void CSI_CxT( Framebuffer* fb, Dispatcher* dispatch ) |
236 | 0 | { |
237 | 0 | int param = dispatch->getparam( 0, 1 ); |
238 | 0 | if ( dispatch->get_dispatch_chars()[0] == 'Z' ) { |
239 | 0 | param = -param; |
240 | 0 | } |
241 | 0 | if ( param == 0 ) { |
242 | 0 | return; |
243 | 0 | } |
244 | 0 | HT_n( fb, param ); |
245 | 0 | } |
246 | | |
247 | | static Function func_CSI_CHT( CSI, "I", CSI_CxT, false ); |
248 | | static Function func_CSI_CBT( CSI, "Z", CSI_CxT, false ); |
249 | | |
250 | | /* horizontal tab set */ |
251 | | static void Ctrl_HTS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
252 | 0 | { |
253 | 0 | fb->ds.set_tab(); |
254 | 0 | } |
255 | | |
256 | | static Function func_Ctrl_HTS( CONTROL, "\x88", Ctrl_HTS ); |
257 | | |
258 | | /* tabulation clear */ |
259 | | static void CSI_TBC( Framebuffer* fb, Dispatcher* dispatch ) |
260 | 0 | { |
261 | 0 | int param = dispatch->getparam( 0, 0 ); |
262 | 0 | switch ( param ) { |
263 | 0 | case 0: /* clear this tab stop */ |
264 | 0 | fb->ds.clear_tab( fb->ds.get_cursor_col() ); |
265 | 0 | break; |
266 | 0 | case 3: /* clear all tab stops */ |
267 | 0 | fb->ds.clear_default_tabs(); |
268 | 0 | for ( int x = 0; x < fb->ds.get_width(); x++ ) { |
269 | 0 | fb->ds.clear_tab( x ); |
270 | 0 | } |
271 | 0 | break; |
272 | 0 | default: |
273 | 0 | break; |
274 | 0 | } |
275 | 0 | } |
276 | | |
277 | | /* TBC preserves wrap state */ |
278 | | static Function func_CSI_TBC( CSI, "g", CSI_TBC, false ); |
279 | | |
280 | | static bool* get_DEC_mode( int param, Framebuffer* fb ) |
281 | 0 | { |
282 | 0 | switch ( param ) { |
283 | 0 | case 1: /* cursor key mode */ |
284 | 0 | return &( fb->ds.application_mode_cursor_keys ); |
285 | 0 | case 3: /* 80/132. Ignore but clear screen. */ |
286 | | /* clear screen */ |
287 | 0 | fb->ds.move_row( 0 ); |
288 | 0 | fb->ds.move_col( 0 ); |
289 | 0 | for ( int y = 0; y < fb->ds.get_height(); y++ ) { |
290 | 0 | fb->reset_row( fb->get_mutable_row( y ) ); |
291 | 0 | } |
292 | 0 | return NULL; |
293 | 0 | case 5: /* reverse video */ |
294 | 0 | return &( fb->ds.reverse_video ); |
295 | 0 | case 6: /* origin */ |
296 | 0 | fb->ds.move_row( 0 ); |
297 | 0 | fb->ds.move_col( 0 ); |
298 | 0 | return &( fb->ds.origin_mode ); |
299 | 0 | case 7: /* auto wrap */ |
300 | 0 | return &( fb->ds.auto_wrap_mode ); |
301 | 0 | case 25: |
302 | 0 | return &( fb->ds.cursor_visible ); |
303 | 0 | case 1004: /* xterm mouse focus event */ |
304 | 0 | return &( fb->ds.mouse_focus_event ); |
305 | 0 | case 1007: /* xterm mouse alternate scroll */ |
306 | 0 | return &( fb->ds.mouse_alternate_scroll ); |
307 | 0 | case 2004: /* bracketed paste */ |
308 | 0 | return &( fb->ds.bracketed_paste ); |
309 | 0 | default: |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | return NULL; |
313 | 0 | } |
314 | | |
315 | | /* helper for CSI_DECSM and CSI_DECRM */ |
316 | | static void set_if_available( bool* mode, bool value ) |
317 | 0 | { |
318 | 0 | if ( mode ) { |
319 | 0 | *mode = value; |
320 | 0 | } |
321 | 0 | } |
322 | | |
323 | | /* set private mode */ |
324 | | static void CSI_DECSM( Framebuffer* fb, Dispatcher* dispatch ) |
325 | 0 | { |
326 | 0 | for ( int i = 0; i < dispatch->param_count(); i++ ) { |
327 | 0 | int param = dispatch->getparam( i, 0 ); |
328 | 0 | if ( param == 9 || ( param >= 1000 && param <= 1003 ) ) { |
329 | 0 | fb->ds.mouse_reporting_mode = (Terminal::DrawState::MouseReportingMode)param; |
330 | 0 | } else if ( param == 1005 || param == 1006 || param == 1015 ) { |
331 | 0 | fb->ds.mouse_encoding_mode = (Terminal::DrawState::MouseEncodingMode)param; |
332 | 0 | } else { |
333 | 0 | set_if_available( get_DEC_mode( param, fb ), true ); |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | /* clear private mode */ |
339 | | static void CSI_DECRM( Framebuffer* fb, Dispatcher* dispatch ) |
340 | 0 | { |
341 | 0 | for ( int i = 0; i < dispatch->param_count(); i++ ) { |
342 | 0 | int param = dispatch->getparam( i, 0 ); |
343 | 0 | if ( param == 9 || ( param >= 1000 && param <= 1003 ) ) { |
344 | 0 | fb->ds.mouse_reporting_mode = Terminal::DrawState::MOUSE_REPORTING_NONE; |
345 | 0 | } else if ( param == 1005 || param == 1006 || param == 1015 ) { |
346 | 0 | fb->ds.mouse_encoding_mode = Terminal::DrawState::MOUSE_ENCODING_DEFAULT; |
347 | 0 | } else { |
348 | 0 | set_if_available( get_DEC_mode( param, fb ), false ); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | /* These functions don't clear wrap state. */ |
354 | | static Function func_CSI_DECSM( CSI, "?h", CSI_DECSM, false ); |
355 | | static Function func_CSI_DECRM( CSI, "?l", CSI_DECRM, false ); |
356 | | |
357 | | static bool* get_ANSI_mode( int param, Framebuffer* fb ) |
358 | 0 | { |
359 | 0 | if ( param == 4 ) { /* insert/replace mode */ |
360 | 0 | return &( fb->ds.insert_mode ); |
361 | 0 | } |
362 | 0 | return NULL; |
363 | 0 | } |
364 | | |
365 | | /* set mode */ |
366 | | static void CSI_SM( Framebuffer* fb, Dispatcher* dispatch ) |
367 | 0 | { |
368 | 0 | for ( int i = 0; i < dispatch->param_count(); i++ ) { |
369 | 0 | bool* mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); |
370 | 0 | if ( mode ) { |
371 | 0 | *mode = true; |
372 | 0 | } |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | /* clear mode */ |
377 | | static void CSI_RM( Framebuffer* fb, Dispatcher* dispatch ) |
378 | 0 | { |
379 | 0 | for ( int i = 0; i < dispatch->param_count(); i++ ) { |
380 | 0 | bool* mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); |
381 | 0 | if ( mode ) { |
382 | 0 | *mode = false; |
383 | 0 | } |
384 | 0 | } |
385 | 0 | } |
386 | | |
387 | | static Function func_CSI_SM( CSI, "h", CSI_SM ); |
388 | | static Function func_CSI_RM( CSI, "l", CSI_RM ); |
389 | | |
390 | | /* set top and bottom margins */ |
391 | | static void CSI_DECSTBM( Framebuffer* fb, Dispatcher* dispatch ) |
392 | 0 | { |
393 | 0 | int top = dispatch->getparam( 0, 1 ); |
394 | 0 | int bottom = dispatch->getparam( 1, fb->ds.get_height() ); |
395 | |
|
396 | 0 | if ( ( bottom <= top ) || ( top > fb->ds.get_height() ) || ( top == 0 && bottom == 1 ) ) { |
397 | 0 | return; /* invalid, xterm ignores */ |
398 | 0 | } |
399 | | |
400 | 0 | fb->ds.set_scrolling_region( top - 1, bottom - 1 ); |
401 | 0 | fb->ds.move_row( 0 ); |
402 | 0 | fb->ds.move_col( 0 ); |
403 | 0 | } |
404 | | |
405 | | static Function func_CSI_DECSTMB( CSI, "r", CSI_DECSTBM ); |
406 | | |
407 | | /* terminal bell */ |
408 | | static void Ctrl_BEL( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
409 | 0 | { |
410 | 0 | fb->ring_bell(); |
411 | 0 | } |
412 | | |
413 | | static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL ); |
414 | | |
415 | | /* select graphics rendition -- e.g., bold, blinking, etc. */ |
416 | | static void CSI_SGR( Framebuffer* fb, Dispatcher* dispatch ) |
417 | 0 | { |
418 | 0 | for ( int i = 0; i < dispatch->param_count(); i++ ) { |
419 | 0 | int rendition = dispatch->getparam( i, 0 ); |
420 | | /* We need to special-case the handling of [34]8 ; 5 ; Ps, |
421 | | because Ps of 0 in that case does not mean reset to default, even |
422 | | though it means that otherwise (as usually renditions are applied |
423 | | in order). */ |
424 | 0 | if ( ( rendition == 38 || rendition == 48 ) && ( dispatch->param_count() - i >= 3 ) |
425 | 0 | && ( dispatch->getparam( i + 1, -1 ) == 5 ) ) { |
426 | 0 | ( rendition == 38 ) ? fb->ds.set_foreground_color( dispatch->getparam( i + 2, 0 ) ) |
427 | 0 | : fb->ds.set_background_color( dispatch->getparam( i + 2, 0 ) ); |
428 | 0 | i += 2; |
429 | 0 | continue; |
430 | 0 | } |
431 | | |
432 | | /* True color support: ESC[ ... [34]8;2;<r>;<g>;<b> ... m */ |
433 | 0 | if ( ( rendition == 38 || rendition == 48 ) && ( dispatch->param_count() - i >= 5 ) |
434 | 0 | && ( dispatch->getparam( i + 1, -1 ) == 2 ) ) { |
435 | 0 | unsigned int red = dispatch->getparam( i + 2, 0 ); |
436 | 0 | unsigned int green = dispatch->getparam( i + 3, 0 ); |
437 | 0 | unsigned int blue = dispatch->getparam( i + 4, 0 ); |
438 | 0 | unsigned int color; |
439 | |
|
440 | 0 | color = Renditions::make_true_color( red, green, blue ); |
441 | |
|
442 | 0 | if ( rendition == 38 ) { |
443 | 0 | fb->ds.set_foreground_color( color ); |
444 | 0 | } else { |
445 | 0 | fb->ds.set_background_color( color ); |
446 | 0 | } |
447 | 0 | i += 4; |
448 | 0 | continue; |
449 | 0 | } |
450 | | |
451 | 0 | fb->ds.add_rendition( rendition ); |
452 | 0 | } |
453 | 0 | } |
454 | | |
455 | | static Function func_CSI_SGR( CSI, "m", CSI_SGR, false ); /* changing renditions doesn't clear wrap flag */ |
456 | | |
457 | | /* save and restore cursor */ |
458 | | static void Esc_DECSC( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
459 | 0 | { |
460 | 0 | fb->ds.save_cursor(); |
461 | 0 | } |
462 | | |
463 | | static void Esc_DECRC( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
464 | 0 | { |
465 | 0 | fb->ds.restore_cursor(); |
466 | 0 | } |
467 | | |
468 | | static Function func_Esc_DECSC( ESCAPE, "7", Esc_DECSC ); |
469 | | static Function func_Esc_DECRC( ESCAPE, "8", Esc_DECRC ); |
470 | | |
471 | | /* device status report -- e.g., cursor position (used by resize) */ |
472 | | static void CSI_DSR( Framebuffer* fb, Dispatcher* dispatch ) |
473 | 0 | { |
474 | 0 | int param = dispatch->getparam( 0, 0 ); |
475 | |
|
476 | 0 | switch ( param ) { |
477 | 0 | case 5: /* device status report requested */ |
478 | 0 | dispatch->terminal_to_host.append( "\033[0n" ); |
479 | 0 | break; |
480 | 0 | case 6: /* report of active position requested */ |
481 | 0 | char cpr[32]; |
482 | 0 | snprintf( cpr, 32, "\033[%d;%dR", fb->ds.get_cursor_row() + 1, fb->ds.get_cursor_col() + 1 ); |
483 | 0 | dispatch->terminal_to_host.append( cpr ); |
484 | 0 | break; |
485 | 0 | default: |
486 | 0 | break; |
487 | 0 | } |
488 | 0 | } |
489 | | |
490 | | static Function func_CSI_DSR( CSI, "n", CSI_DSR ); |
491 | | |
492 | | /* insert line */ |
493 | | static void CSI_IL( Framebuffer* fb, Dispatcher* dispatch ) |
494 | 0 | { |
495 | 0 | int lines = dispatch->getparam( 0, 1 ); |
496 | |
|
497 | 0 | fb->insert_line( fb->ds.get_cursor_row(), lines ); |
498 | | |
499 | | /* vt220 manual and Ecma-48 say to move to first column */ |
500 | | /* but xterm and gnome-terminal don't */ |
501 | 0 | fb->ds.move_col( 0 ); |
502 | 0 | } |
503 | | |
504 | | static Function func_CSI_IL( CSI, "L", CSI_IL ); |
505 | | |
506 | | /* delete line */ |
507 | | static void CSI_DL( Framebuffer* fb, Dispatcher* dispatch ) |
508 | 0 | { |
509 | 0 | int lines = dispatch->getparam( 0, 1 ); |
510 | |
|
511 | 0 | fb->delete_line( fb->ds.get_cursor_row(), lines ); |
512 | | |
513 | | /* same story -- xterm and gnome-terminal don't |
514 | | move to first column */ |
515 | 0 | fb->ds.move_col( 0 ); |
516 | 0 | } |
517 | | |
518 | | static Function func_CSI_DL( CSI, "M", CSI_DL ); |
519 | | |
520 | | /* insert characters */ |
521 | | static void CSI_ICH( Framebuffer* fb, Dispatcher* dispatch ) |
522 | 0 | { |
523 | 0 | int cells = dispatch->getparam( 0, 1 ); |
524 | |
|
525 | 0 | for ( int i = 0; i < cells; i++ ) { |
526 | 0 | fb->insert_cell( fb->ds.get_cursor_row(), fb->ds.get_cursor_col() ); |
527 | 0 | } |
528 | 0 | } |
529 | | |
530 | | static Function func_CSI_ICH( CSI, "@", CSI_ICH ); |
531 | | |
532 | | /* delete character */ |
533 | | static void CSI_DCH( Framebuffer* fb, Dispatcher* dispatch ) |
534 | 0 | { |
535 | 0 | int cells = dispatch->getparam( 0, 1 ); |
536 | |
|
537 | 0 | for ( int i = 0; i < cells; i++ ) { |
538 | 0 | fb->delete_cell( fb->ds.get_cursor_row(), fb->ds.get_cursor_col() ); |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | | static Function func_CSI_DCH( CSI, "P", CSI_DCH ); |
543 | | |
544 | | /* line position absolute */ |
545 | | static void CSI_VPA( Framebuffer* fb, Dispatcher* dispatch ) |
546 | 0 | { |
547 | 0 | int row = dispatch->getparam( 0, 1 ); |
548 | 0 | fb->ds.move_row( row - 1 ); |
549 | 0 | } |
550 | | |
551 | | static Function func_CSI_VPA( CSI, "d", CSI_VPA ); |
552 | | |
553 | | /* character position absolute */ |
554 | | static void CSI_HPA( Framebuffer* fb, Dispatcher* dispatch ) |
555 | 0 | { |
556 | 0 | int col = dispatch->getparam( 0, 1 ); |
557 | 0 | fb->ds.move_col( col - 1 ); |
558 | 0 | } |
559 | | |
560 | | static Function func_CSI_CHA( CSI, "G", CSI_HPA ); /* ECMA-48 name: CHA */ |
561 | | static Function func_CSI_HPA( CSI, "\x60", CSI_HPA ); /* ECMA-48 name: HPA */ |
562 | | |
563 | | /* erase character */ |
564 | | static void CSI_ECH( Framebuffer* fb, Dispatcher* dispatch ) |
565 | 0 | { |
566 | 0 | int num = dispatch->getparam( 0, 1 ); |
567 | 0 | int limit = fb->ds.get_cursor_col() + num - 1; |
568 | 0 | if ( limit >= fb->ds.get_width() ) { |
569 | 0 | limit = fb->ds.get_width() - 1; |
570 | 0 | } |
571 | |
|
572 | 0 | clearline( fb, -1, fb->ds.get_cursor_col(), limit ); |
573 | 0 | } |
574 | | |
575 | | static Function func_CSI_ECH( CSI, "X", CSI_ECH ); |
576 | | |
577 | | /* reset to initial state */ |
578 | | static void Esc_RIS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
579 | 0 | { |
580 | 0 | fb->reset(); |
581 | 0 | } |
582 | | |
583 | | static Function func_Esc_RIS( ESCAPE, "c", Esc_RIS ); |
584 | | |
585 | | /* soft reset */ |
586 | | static void CSI_DECSTR( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) ) |
587 | 0 | { |
588 | 0 | fb->soft_reset(); |
589 | 0 | } |
590 | | |
591 | | static Function func_CSI_DECSTR( CSI, "!p", CSI_DECSTR ); |
592 | | |
593 | | static bool Parse_OSC_8( const std::vector<wchar_t>& osc8_vector, std::string& osc8_str ) |
594 | 0 | { |
595 | 0 | osc8_str.reserve( osc8_vector.size() ); |
596 | 0 | for ( wchar_t wide_char : osc8_vector ) { |
597 | | // Valid char range is 32-126, per |
598 | | // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#encodings |
599 | 0 | if ( wide_char < 32 || wide_char > 126 ) { |
600 | 0 | return false; |
601 | 0 | } |
602 | 0 | osc8_str.append( 1, static_cast<char>( wide_char ) ); |
603 | 0 | } |
604 | 0 | return true; |
605 | 0 | } |
606 | | |
607 | | static void OSC_8( const std::string& OSC_string, Framebuffer* fb ) |
608 | 0 | { |
609 | | // OSC of the form "\033]8;params;url\007" |
610 | 0 | assert( OSC_string[0] == '8' ); |
611 | 0 | if ( OSC_string.size() <= 2 || OSC_string[1] != ';' ) { |
612 | | // Bail early if the string is malformed. |
613 | 0 | return; |
614 | 0 | } |
615 | | |
616 | 0 | size_t second_semicolon = OSC_string.find_first_of( ';', 2 ); |
617 | 0 | if ( second_semicolon == std::string::npos ) { |
618 | | // Missing the second semicolon, malformed. |
619 | 0 | return; |
620 | 0 | } |
621 | | |
622 | 0 | std::string url = OSC_string.substr( second_semicolon + 1 ); |
623 | 0 | fb->ds.set_hyperlink( Hyperlink( OSC_string.substr( 2, second_semicolon - 2 ), std::move( url ) ) ); |
624 | 0 | } |
625 | | |
626 | | /* xterm uses an Operating System Command to set the window title */ |
627 | | void Dispatcher::OSC_dispatch( const Parser::OSC_End* act __attribute( ( unused ) ), Framebuffer* fb ) |
628 | 0 | { |
629 | | /* handle osc copy clipboard sequence 52;c; */ |
630 | 0 | if ( OSC_string.size() >= 5 && OSC_string[0] == L'5' && OSC_string[1] == L'2' && OSC_string[2] == L';' |
631 | 0 | && OSC_string[3] == L'c' && OSC_string[4] == L';' ) { |
632 | 0 | Terminal::Framebuffer::title_type clipboard( OSC_string.begin() + 5, OSC_string.end() ); |
633 | 0 | fb->set_clipboard( clipboard ); |
634 | | /* handle osc terminal title sequence */ |
635 | 0 | } else if ( OSC_string.size() >= 1 ) { |
636 | 0 | long cmd_num = -1; |
637 | 0 | int offset = 0; |
638 | 0 | if ( OSC_string[0] == L';' ) { |
639 | | /* OSC of the form "\033];<title>\007" */ |
640 | 0 | cmd_num = 0; /* treat it as as a zero */ |
641 | 0 | offset = 1; |
642 | 0 | } else if ( ( OSC_string.size() >= 2 ) && ( OSC_string[1] == L';' ) ) { |
643 | | /* OSC of the form "\033]X;<title>\007" where X can be: |
644 | | * 0: set icon name and window title |
645 | | * 1: set icon name |
646 | | * 2: set window title */ |
647 | 0 | cmd_num = OSC_string[0] - L'0'; |
648 | 0 | offset = 2; |
649 | 0 | } |
650 | 0 | if ( cmd_num == 8 ) { |
651 | | // Handle OSC8 hyperlinks separately |
652 | 0 | std::string osc_8_str; |
653 | 0 | if ( !Parse_OSC_8( OSC_string, osc_8_str ) ) { |
654 | | // |
655 | 0 | return; |
656 | 0 | } |
657 | 0 | OSC_8( osc_8_str, fb ); |
658 | 0 | return; |
659 | 0 | } |
660 | 0 | bool set_icon = cmd_num == 0 || cmd_num == 1; |
661 | 0 | bool set_title = cmd_num == 0 || cmd_num == 2; |
662 | 0 | if ( set_icon || set_title ) { |
663 | 0 | fb->set_title_initialized(); |
664 | 0 | int title_length = std::min( OSC_string.size(), (size_t)256 ); |
665 | 0 | Terminal::Framebuffer::title_type newtitle( OSC_string.begin() + offset, OSC_string.begin() + title_length ); |
666 | 0 | if ( set_icon ) { |
667 | 0 | fb->set_icon_name( newtitle ); |
668 | 0 | } |
669 | 0 | if ( set_title ) { |
670 | 0 | fb->set_window_title( newtitle ); |
671 | 0 | } |
672 | 0 | } |
673 | 0 | } |
674 | 0 | } |
675 | | |
676 | | /* scroll down or terminfo indn */ |
677 | | static void CSI_SD( Framebuffer* fb, Dispatcher* dispatch ) |
678 | 0 | { |
679 | 0 | fb->scroll( dispatch->getparam( 0, 1 ) ); |
680 | 0 | } |
681 | | |
682 | | static Function func_CSI_SD( CSI, "S", CSI_SD ); |
683 | | |
684 | | /* scroll up or terminfo rin */ |
685 | | static void CSI_SU( Framebuffer* fb, Dispatcher* dispatch ) |
686 | 0 | { |
687 | 0 | fb->scroll( -dispatch->getparam( 0, 1 ) ); |
688 | 0 | } |
689 | | |
690 | | static Function func_CSI_SU( CSI, "T", CSI_SU ); |