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