chiark / gitweb /
terminal: remove an unused initialization
[elogind.git] / src / libsystemd-terminal / term-screen.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 /*
23  * Terminal Screens
24  * The term_screen layer implements the terminal-side. It handles all commands
25  * returned by the seq-parser and applies them to its own pages.
26  *
27  * While there are a lot of legacy control-sequences, we only support a small
28  * subset. There is no reason to implement unused codes like horizontal
29  * scrolling.
30  * If you implement new commands, make sure to document them properly.
31  *
32  * Standards:
33  *   ECMA-48
34  *   ANSI X3.64
35  *   ISO/IEC 6429
36  * References:
37  *   http://www.vt100.net/emu/ctrlseq_dec.html
38  *   http://www.vt100.net/docs/vt100-ug/chapter3.html
39  *   http://www.vt100.net/docs/vt510-rm/chapter4
40  *   http://www.vt100.net/docs/vt510-rm/contents
41  *   http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
42  *   ASCII
43  *   http://en.wikipedia.org/wiki/C0_and_C1_control_codes
44  *   https://en.wikipedia.org/wiki/ANSI_color
45  */
46
47 #include <stdbool.h>
48 #include <stdint.h>
49 #include <stdlib.h>
50 #include <xkbcommon/xkbcommon-keysyms.h>
51 #include "macro.h"
52 #include "term-internal.h"
53 #include "util.h"
54
55 int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data) {
56         _cleanup_(term_screen_unrefp) term_screen *screen = NULL;
57         int r;
58
59         assert_return(out, -EINVAL);
60
61         screen = new0(term_screen, 1);
62         if (!screen)
63                 return -ENOMEM;
64
65         screen->ref = 1;
66         screen->age = 1;
67         screen->write_fn = write_fn;
68         screen->write_fn_data = write_fn_data;
69         screen->cmd_fn = cmd_fn;
70         screen->cmd_fn_data = cmd_fn_data;
71         screen->flags = TERM_FLAG_7BIT_MODE;
72         screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
73         screen->g0 = &term_unicode_lower;
74         screen->g1 = &term_unicode_upper;
75         screen->g2 = &term_unicode_lower;
76         screen->g3 = &term_unicode_upper;
77         screen->state.gl = &screen->g0;
78         screen->state.gr = &screen->g1;
79         screen->saved = screen->state;
80         screen->saved_alt = screen->saved;
81
82         r = term_page_new(&screen->page_main);
83         if (r < 0)
84                 return r;
85
86         r = term_page_new(&screen->page_alt);
87         if (r < 0)
88                 return r;
89
90         r = term_parser_new(&screen->parser, false);
91         if (r < 0)
92                 return r;
93
94         r = term_history_new(&screen->history_main);
95         if (r < 0)
96                 return r;
97
98         screen->page = screen->page_main;
99         screen->history = screen->history_main;
100
101         *out = screen;
102         screen = NULL;
103         return 0;
104 }
105
106 term_screen *term_screen_ref(term_screen *screen) {
107         if (!screen)
108                 return NULL;
109
110         assert_return(screen->ref > 0, NULL);
111
112         ++screen->ref;
113         return screen;
114 }
115
116 term_screen *term_screen_unref(term_screen *screen) {
117         if (!screen)
118                 return NULL;
119
120         assert_return(screen->ref > 0, NULL);
121
122         if (--screen->ref)
123                 return NULL;
124
125         free(screen->answerback);
126         free(screen->tabs);
127         term_history_free(screen->history_main);
128         term_page_free(screen->page_alt);
129         term_page_free(screen->page_main);
130         term_parser_free(screen->parser);
131         free(screen);
132
133         return NULL;
134 }
135
136 /*
137  * Write-Helpers
138  * Unfortunately, 7bit/8bit compat mode requires us to send C1 controls encoded
139  * as 7bit if asked by the application. This is really used in the wild, so we
140  * cannot fall back to "always 7bit".
141  * screen_write() is the underlying backend which forwards any writes to the
142  * users's callback. It's the users responsibility to buffer these and write
143  * them out once their call to term_screen_feed_*() returns.
144  * The SEQ_WRITE() and SEQ_WRITE_KEY() macros allow constructing C0/C1 sequences
145  * directly in the code-base without requiring any intermediate buffer during
146  * runtime.
147  */
148
149 #define C0_CSI "\e["
150 #define C1_CSI "\x9b"
151
152 #define SEQ(_screen, _prefix_esc, _c0, _c1, _seq) \
153                 (((_screen)->flags & TERM_FLAG_7BIT_MODE) ? \
154                         ((_prefix_esc) ? ("\e" _c0 _seq) : (_c0 _seq)) : \
155                         ((_prefix_esc) ? ("\e" _c1 _seq) : (_c1 _seq)))
156
157 #define SEQ_SIZE(_screen, _prefix_esc, _c0, _c1, _seq) \
158                 (((_screen)->flags & TERM_FLAG_7BIT_MODE) ? \
159                         ((_prefix_esc) ? sizeof("\e" _c0 _seq) : sizeof(_c0 _seq)) : \
160                         ((_prefix_esc) ? sizeof("\e" _c1 _seq) : sizeof(_c1 _seq)))
161
162 #define SEQ_WRITE_KEY(_screen, _prefix_esc, _c0, _c1, _seq) \
163                 screen_write((_screen), \
164                              SEQ((_screen), (_prefix_esc), \
165                                  _c0, _c1, _seq), \
166                              SEQ_SIZE((_screen), (_prefix_esc), \
167                                      _c0, _c1, _seq) - 1)
168
169 #define SEQ_WRITE(_screen, _c0, _c1, _seq) \
170                 SEQ_WRITE_KEY((_screen), false, _c0, _c1, _seq)
171
172 static int screen_write(term_screen *screen, const void *buf, size_t len) {
173         if (len < 1 || !screen->write_fn)
174                 return 0;
175
176         return screen->write_fn(screen, screen->write_fn_data, buf, len);
177 }
178
179 /*
180  * Command Forwarding
181  * Some commands cannot be handled by the screen-layer directly. Those are
182  * forwarded to the command-handler of the caller. This is rarely used and can
183  * safely be set to NULL.
184  */
185
186 static int screen_forward(term_screen *screen, unsigned int cmd, const term_seq *seq) {
187         if (!screen->cmd_fn)
188                 return 0;
189
190         return screen->cmd_fn(screen, screen->cmd_fn_data, cmd, seq);
191 }
192
193 /*
194  * Screen Helpers
195  * These helpers implement common-operations like cursor-handler and more, which
196  * are used by several command dispatchers.
197  */
198
199 static unsigned int screen_clamp_x(term_screen *screen, unsigned int x) {
200         if (x >= screen->page->width)
201                 return (screen->page->width > 0) ? screen->page->width - 1 : 0;
202
203         return x;
204 }
205
206 static unsigned int screen_clamp_y(term_screen *screen, unsigned int y) {
207         if (y >= screen->page->height)
208                 return (screen->page->height > 0) ? screen->page->height - 1 : 0;
209
210         return y;
211 }
212
213 static bool screen_tab_is_set(term_screen *screen, unsigned int pos) {
214         if (pos >= screen->page->width)
215                 return false;
216
217         return screen->tabs[pos / 8] & (1 << (pos % 8));
218 }
219
220 static inline void screen_age_cursor(term_screen *screen) {
221         term_cell *cell;
222
223         cell = term_page_get_cell(screen->page, screen->state.cursor_x, screen->state.cursor_y);
224         if (cell)
225                 cell->age = screen->age;
226 }
227
228 static void screen_cursor_clear_wrap(term_screen *screen) {
229         screen->flags &= ~TERM_FLAG_PENDING_WRAP;
230 }
231
232 static void screen_cursor_set(term_screen *screen, unsigned int x, unsigned int y) {
233         x = screen_clamp_x(screen, x);
234         y = screen_clamp_y(screen, y);
235
236         if (x == screen->state.cursor_x && y == screen->state.cursor_y)
237                 return;
238
239         if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
240                 screen_age_cursor(screen);
241
242         screen->state.cursor_x = x;
243         screen->state.cursor_y = y;
244
245         if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
246                 screen_age_cursor(screen);
247 }
248
249 static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned int y) {
250         if (screen->state.origin_mode) {
251                 x = screen_clamp_x(screen, x);
252                 y = screen_clamp_x(screen, y) + screen->page->scroll_idx;
253
254                 if (y >= screen->page->scroll_idx + screen->page->scroll_num) {
255                         y = screen->page->scroll_idx + screen->page->scroll_num;
256                         if (screen->page->scroll_num > 0)
257                                 y -= 1;
258                 }
259         }
260
261         screen_cursor_set(screen, x, y);
262 }
263
264 static void screen_cursor_left(term_screen *screen, unsigned int num) {
265         if (num > screen->state.cursor_x)
266                 num = screen->state.cursor_x;
267
268         screen_cursor_set(screen, screen->state.cursor_x - num, screen->state.cursor_y);
269 }
270
271 static void screen_cursor_left_tab(term_screen *screen, unsigned int num) {
272         unsigned int i;
273
274         i = screen->state.cursor_x;
275         while (i > 0 && num > 0) {
276                 if (screen_tab_is_set(screen, --i))
277                         --num;
278         }
279
280         screen_cursor_set(screen, i, screen->state.cursor_y);
281 }
282
283 static void screen_cursor_right(term_screen *screen, unsigned int num) {
284         if (num > screen->page->width)
285                 num = screen->page->width;
286
287         screen_cursor_set(screen, screen->state.cursor_x + num, screen->state.cursor_y);
288 }
289
290 static void screen_cursor_right_tab(term_screen *screen, unsigned int num) {
291         unsigned int i;
292
293         i = screen->state.cursor_x;
294         while (i + 1 < screen->page->width && num > 0) {
295                 if (screen_tab_is_set(screen, ++i))
296                         --num;
297         }
298
299         screen_cursor_set(screen, i, screen->state.cursor_y);
300 }
301
302 static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) {
303         unsigned int max;
304
305         if (screen->state.cursor_y < screen->page->scroll_idx) {
306                 if (num > screen->state.cursor_y)
307                         num = screen->state.cursor_y;
308
309                 screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
310         } else {
311                 max = screen->state.cursor_y - screen->page->scroll_idx;
312                 if (num > max) {
313                         if (num < 1)
314                                 return;
315
316                         if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
317                                 screen_age_cursor(screen);
318
319                         if (scroll)
320                                 term_page_scroll_down(screen->page, num - max, &screen->state.attr, screen->age, NULL);
321
322                         screen->state.cursor_y = screen->page->scroll_idx;
323
324                         if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
325                                 screen_age_cursor(screen);
326                 } else {
327                         screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
328                 }
329         }
330 }
331
332 static void screen_cursor_down(term_screen *screen, unsigned int num, bool scroll) {
333         unsigned int max;
334
335         if (screen->state.cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) {
336                 if (num > screen->page->height)
337                         num = screen->page->height;
338
339                 screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
340         } else {
341                 max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->state.cursor_y;
342                 if (num > max) {
343                         if (num < 1)
344                                 return;
345
346                         if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
347                                 screen_age_cursor(screen);
348
349                         if (scroll)
350                                 term_page_scroll_up(screen->page, num - max, &screen->state.attr, screen->age, screen->history);
351
352                         screen->state.cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1;
353
354                         if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
355                                 screen_age_cursor(screen);
356                 } else {
357                         screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y + num);
358                 }
359         }
360 }
361
362 static void screen_save_state(term_screen *screen, term_state *where) {
363         *where = screen->state;
364 }
365
366 static void screen_restore_state(term_screen *screen, term_state *from) {
367         screen_cursor_set(screen, from->cursor_x, from->cursor_y);
368         screen->state = *from;
369 }
370
371 static void screen_reset_page(term_screen *screen, term_page *page) {
372         term_page_set_scroll_region(page, 0, page->height);
373         term_page_erase(page, 0, 0, page->width, page->height, &screen->state.attr, screen->age, false);
374 }
375
376 static void screen_change_alt(term_screen *screen, bool set) {
377         if (set) {
378                 screen->page = screen->page_alt;
379                 screen->history = NULL;
380         } else {
381                 screen->page = screen->page_main;
382                 screen->history = screen->history_main;
383         }
384
385         screen->page->age = screen->age;
386 }
387
388 static inline void set_reset(term_screen *screen, unsigned int flag, bool set) {
389         if (set)
390                 screen->flags |= flag;
391         else
392                 screen->flags &= ~flag;
393 }
394
395 static void screen_mode_change(term_screen *screen, unsigned int mode, bool dec, bool set) {
396         switch (mode) {
397         case 1:
398                 if (dec) {
399                         /*
400                          * DECCKM: cursor-keys
401                          * TODO
402                          */
403                         set_reset(screen, TERM_FLAG_CURSOR_KEYS, set);
404                 }
405
406                 break;
407         case 6:
408                 if (dec) {
409                         /*
410                          * DECOM: origin-mode
411                          * TODO
412                          */
413                         screen->state.origin_mode = set;
414                 }
415
416                 break;
417         case 7:
418                 if (dec) {
419                         /*
420                          * DECAWN: auto-wrap mode
421                          * TODO
422                          */
423                         screen->state.auto_wrap = set;
424                 }
425
426                 break;
427         case 20:
428                 if (!dec) {
429                         /*
430                          * LNM: line-feed/new-line mode
431                          * TODO
432                          */
433                         set_reset(screen, TERM_FLAG_NEWLINE_MODE, set);
434                 }
435
436                 break;
437         case 25:
438                 if (dec) {
439                         /*
440                          * DECTCEM: text-cursor-enable
441                          * TODO
442                          */
443                         set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set);
444                         screen_age_cursor(screen);
445                 }
446
447                 break;
448         case 47:
449                 if (dec) {
450                         /*
451                          * XTERM-ASB: alternate-screen-buffer
452                          * This enables/disables the alternate screen-buffer.
453                          * It effectively saves the current page content and
454                          * allows you to restore it when changing to the
455                          * original screen-buffer again.
456                          */
457                         screen_change_alt(screen, set);
458                 }
459
460                 break;
461         case 1047:
462                 if (dec) {
463                         /*
464                          * XTERM-ASBPE: alternate-screen-buffer-post-erase
465                          * This is the same as XTERM-ASB but erases the
466                          * alternate screen-buffer before switching back to the
467                          * original buffer. Use it to discard any data on the
468                          * alternate screen buffer when done.
469                          */
470                         if (!set)
471                                 screen_reset_page(screen, screen->page_alt);
472
473                         screen_change_alt(screen, set);
474                 }
475
476                 break;
477         case 1048:
478                 if (dec) {
479                         /*
480                          * XTERM-ASBCS: alternate-screen-buffer-cursor-state
481                          * This has the same effect as DECSC/DECRC, but uses a
482                          * separate state buffer. It is usually used in
483                          * combination with alternate screen buffers to provide
484                          * stacked state storage.
485                          */
486                         if (set)
487                                 screen_save_state(screen, &screen->saved_alt);
488                         else
489                                 screen_restore_state(screen, &screen->saved_alt);
490                 }
491
492                 break;
493         case 1049:
494                 if (dec) {
495                         /*
496                          * XTERM-ASBX: alternate-screen-buffer-extended
497                          * This combines XTERM-ASBPE and XTERM-ASBCS somewhat.
498                          * When enabling, state is saved, alternate screen
499                          * buffer is activated and cleared.
500                          * When disabled, alternate screen buffer is cleared,
501                          * then normal screen buffer is enabled and state is
502                          * restored.
503                          */
504                         if (set)
505                                 screen_save_state(screen, &screen->saved_alt);
506
507                         screen_reset_page(screen, screen->page_alt);
508                         screen_change_alt(screen, set);
509
510                         if (!set)
511                                 screen_restore_state(screen, &screen->saved_alt);
512                 }
513
514                 break;
515         }
516 }
517
518 /* map a character according to current GL and GR maps */
519 static uint32_t screen_map(term_screen *screen, uint32_t val) {
520         uint32_t nval = -1U;
521
522         /* 32 and 127 always map to identity. 160 and 255 map to identity iff a
523          * 96 character set is loaded into GR. Values above 255 always map to
524          * identity. */
525         switch (val) {
526         case 33 ... 126:
527                 if (screen->state.glt) {
528                         nval = (**screen->state.glt)[val - 32];
529                         screen->state.glt = NULL;
530                 } else {
531                         nval = (**screen->state.gl)[val - 32];
532                 }
533                 break;
534         case 160 ... 255:
535                 if (screen->state.grt) {
536                         nval = (**screen->state.grt)[val - 160];
537                         screen->state.grt = NULL;
538                 } else {
539                         nval = (**screen->state.gr)[val - 160];
540                 }
541                 break;
542         }
543
544         return (nval == -1U) ? val : nval;
545 }
546
547 /*
548  * Command Handlers
549  * This is the unofficial documentation of all the TERM_CMD_* definitions. Each
550  * handled command has a separate function with an extensive comment on the
551  * semantics of the command.
552  * Note that many semantics are unknown and need to be verified. This is mostly
553  * about error-handling, though. Applications rarely rely on those features.
554  */
555
556 static int screen_DA1(term_screen *screen, const term_seq *seq);
557 static int screen_LF(term_screen *screen, const term_seq *seq);
558
559 static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) {
560         term_char_t ch = TERM_CHAR_NULL;
561         uint32_t c;
562
563         if (screen->state.cursor_x + 1 == screen->page->width
564             && screen->flags & TERM_FLAG_PENDING_WRAP
565             && screen->state.auto_wrap) {
566                 screen_cursor_down(screen, 1, true);
567                 screen_cursor_set(screen, 0, screen->state.cursor_y);
568         }
569
570         screen_cursor_clear_wrap(screen);
571
572         c = screen_map(screen, seq->terminator);
573         ch = term_char_merge(ch, screen_map(screen, c));
574         term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false);
575
576         if (screen->state.cursor_x + 1 == screen->page->width)
577                 screen->flags |= TERM_FLAG_PENDING_WRAP;
578         else
579                 screen_cursor_right(screen, 1);
580
581         return 0;
582 }
583
584 static int screen_BEL(term_screen *screen, const term_seq *seq) {
585         /*
586          * BEL - sound bell tone
587          * This command should trigger an acoustic bell. Usually, this is
588          * forwarded directly to the pcspkr. However, bells have become quite
589          * uncommon and annoying, so we're not implementing them here. Instead,
590          * it's one of the commands we forward to the caller.
591          */
592
593         return screen_forward(screen, TERM_CMD_BEL, seq);
594 }
595
596 static int screen_BS(term_screen *screen, const term_seq *seq) {
597         /*
598          * BS - backspace
599          * Move cursor one cell to the left. If already at the left margin,
600          * nothing happens.
601          */
602
603         screen_cursor_clear_wrap(screen);
604         screen_cursor_left(screen, 1);
605         return 0;
606 }
607
608 static int screen_CBT(term_screen *screen, const term_seq *seq) {
609         /*
610          * CBT - cursor-backward-tabulation
611          * Move the cursor @args[0] tabs backwards (to the left). The
612          * current cursor cell, in case it's a tab, is not counted.
613          * Furthermore, the cursor cannot be moved beyond position 0 and
614          * it will stop there.
615          *
616          * Defaults:
617          *   args[0]: 1
618          */
619
620         unsigned int num = 1;
621
622         if (seq->args[0] > 0)
623                 num = seq->args[0];
624
625         screen_cursor_clear_wrap(screen);
626         screen_cursor_left_tab(screen, num);
627
628         return 0;
629 }
630
631 static int screen_CHA(term_screen *screen, const term_seq *seq) {
632         /*
633          * CHA - cursor-horizontal-absolute
634          * Move the cursor to position @args[0] in the current line. The
635          * cursor cannot be moved beyond the rightmost cell and will stop
636          * there.
637          *
638          * Defaults:
639          *   args[0]: 1
640          */
641
642         unsigned int pos = 1;
643
644         if (seq->args[0] > 0)
645                 pos = seq->args[0];
646
647         screen_cursor_clear_wrap(screen);
648         screen_cursor_set(screen, pos - 1, screen->state.cursor_y);
649
650         return 0;
651 }
652
653 static int screen_CHT(term_screen *screen, const term_seq *seq) {
654         /*
655          * CHT - cursor-horizontal-forward-tabulation
656          * Move the cursor @args[0] tabs forward (to the right). The
657          * current cursor cell, in case it's a tab, is not counted.
658          * Furthermore, the cursor cannot be moved beyond the rightmost cell
659          * and will stop there.
660          *
661          * Defaults:
662          *   args[0]: 1
663          */
664
665         unsigned int num = 1;
666
667         if (seq->args[0] > 0)
668                 num = seq->args[0];
669
670         screen_cursor_clear_wrap(screen);
671         screen_cursor_right_tab(screen, num);
672
673         return 0;
674 }
675
676 static int screen_CNL(term_screen *screen, const term_seq *seq) {
677         /*
678          * CNL - cursor-next-line
679          * Move the cursor @args[0] lines down.
680          *
681          * TODO: Does this stop at the bottom or cause a scroll-up?
682          *
683          * Defaults:
684          *   args[0]: 1
685          */
686
687         unsigned int num = 1;
688
689         if (seq->args[0] > 0)
690                 num = seq->args[0];
691
692         screen_cursor_clear_wrap(screen);
693         screen_cursor_down(screen, num, false);
694
695         return 0;
696 }
697
698 static int screen_CPL(term_screen *screen, const term_seq *seq) {
699         /*
700          * CPL - cursor-preceding-line
701          * Move the cursor @args[0] lines up.
702          *
703          * TODO: Does this stop at the top or cause a scroll-up?
704          *
705          * Defaults:
706          *   args[0]: 1
707          */
708
709         unsigned int num = 1;
710
711         if (seq->args[0] > 0)
712                 num = seq->args[0];
713
714         screen_cursor_clear_wrap(screen);
715         screen_cursor_up(screen, num, false);
716
717         return 0;
718 }
719
720 static int screen_CR(term_screen *screen, const term_seq *seq) {
721         /*
722          * CR - carriage-return
723          * Move the cursor to the left margin on the current line.
724          */
725
726         screen_cursor_clear_wrap(screen);
727         screen_cursor_set(screen, 0, screen->state.cursor_y);
728
729         return 0;
730 }
731
732 static int screen_CUB(term_screen *screen, const term_seq *seq) {
733         /*
734          * CUB - cursor-backward
735          * Move the cursor @args[0] positions to the left. The cursor stops
736          * at the left-most position.
737          *
738          * Defaults:
739          *   args[0]: 1
740          */
741
742         unsigned int num = 1;
743
744         if (seq->args[0] > 0)
745                 num = seq->args[0];
746
747         screen_cursor_clear_wrap(screen);
748         screen_cursor_left(screen, num);
749
750         return 0;
751 }
752
753 static int screen_CUD(term_screen *screen, const term_seq *seq) {
754         /*
755          * CUD - cursor-down
756          * Move the cursor @args[0] positions down. The cursor stops at the
757          * bottom margin. If it was already moved further, it stops at the
758          * bottom line.
759          *
760          * Defaults:
761          *   args[0]: 1
762          */
763
764         unsigned int num = 1;
765
766         if (seq->args[0] > 0)
767                 num = seq->args[0];
768
769         screen_cursor_clear_wrap(screen);
770         screen_cursor_down(screen, num, false);
771
772         return 0;
773 }
774
775 static int screen_CUF(term_screen *screen, const term_seq *seq) {
776         /*
777          * CUF -cursor-forward
778          * Move the cursor @args[0] positions to the right. The cursor stops
779          * at the right-most position.
780          *
781          * Defaults:
782          *   args[0]: 1
783          */
784
785         unsigned int num = 1;
786
787         if (seq->args[0] > 0)
788                 num = seq->args[0];
789
790         screen_cursor_clear_wrap(screen);
791         screen_cursor_right(screen, num);
792
793         return 0;
794 }
795
796 static int screen_CUP(term_screen *screen, const term_seq *seq) {
797         /*
798          * CUP - cursor-position
799          * Moves the cursor to position @args[1] x @args[0]. If either is 0, it
800          * is treated as 1. The positions are subject to the origin-mode and
801          * clamped to the addressable with/height.
802          *
803          * Defaults:
804          *   args[0]: 1
805          *   args[1]: 1
806          */
807
808         unsigned int x = 1, y = 1;
809
810         if (seq->args[0] > 0)
811                 y = seq->args[0];
812         if (seq->args[1] > 0)
813                 x = seq->args[1];
814
815         screen_cursor_clear_wrap(screen);
816         screen_cursor_set_rel(screen, x - 1, y - 1);
817
818         return 0;
819 }
820
821 static int screen_CUU(term_screen *screen, const term_seq *seq) {
822         /*
823          * CUU - cursor-up
824          * Move the cursor @args[0] positions up. The cursor stops at the
825          * top margin. If it was already moved further, it stops at the
826          * top line.
827          *
828          * Defaults:
829          *   args[0]: 1
830          *
831          */
832
833         unsigned int num = 1;
834
835         if (seq->args[0] > 0)
836                 num = seq->args[0];
837
838         screen_cursor_clear_wrap(screen);
839         screen_cursor_up(screen, num, false);
840
841         return 0;
842 }
843
844 static int screen_DA1(term_screen *screen, const term_seq *seq) {
845         /*
846          * DA1 - primary-device-attributes
847          * The primary DA asks for basic terminal features. We simply return
848          * a hard-coded list of features we implement.
849          * Note that the primary DA asks for supported features, not currently
850          * enabled features.
851          *
852          * The terminal's answer is:
853          *   ^[ ? 64 ; ARGS c
854          * The first argument, 64, is fixed and denotes a VT420, the last
855          * DEC-term that extended this number.
856          * All following arguments denote supported features. Note
857          * that at most 15 features can be sent (max CSI args). It is safe to
858          * send more, but clients might not be able to parse them. This is a
859          * client's problem and we shouldn't care. There is no other way to
860          * send those feature lists, so we have to extend them beyond 15 in
861          * those cases.
862          *
863          * Known modes:
864          *    1: 132 column mode
865          *       The 132 column mode is supported by the terminal.
866          *    2: printer port
867          *       A priner-port is supported and can be addressed via
868          *       control-codes.
869          *    3: ReGIS graphics
870          *       Support for ReGIS graphics is available. The ReGIS routines
871          *       provide the "remote graphics instruction set" and allow basic
872          *       vector-rendering.
873          *    4: sixel
874          *       Support of Sixel graphics is available. This provides access
875          *       to the sixel bitmap routines.
876          *    6: selective erase
877          *       The terminal supports DECSCA and related selective-erase
878          *       functions. This allows to protect specific cells from being
879          *       erased, if specified.
880          *    7: soft character set (DRCS)
881          *       TODO: ?
882          *    8: user-defined keys (UDKs)
883          *       TODO: ?
884          *    9: national-replacement character sets (NRCS)
885          *       National-replacement character-sets are available.
886          *   12: Yugoslavian (SCS)
887          *       TODO: ?
888          *   15: technical character set
889          *       The DEC technical-character-set is available.
890          *   18: windowing capability
891          *       TODO: ?
892          *   21: horizontal scrolling
893          *       TODO: ?
894          *   22: ANSII color
895          *       TODO: ?
896          *   23: Greek
897          *       TODO: ?
898          *   24: Turkish
899          *       TODO: ?
900          *   29: ANSI text locator
901          *       TODO: ?
902          *   42: ISO Latin-2 character set
903          *       TODO: ?
904          *   44: PCTerm
905          *       TODO: ?
906          *   45: soft keymap
907          *       TODO: ?
908          *   46: ASCII emulation
909          *       TODO: ?
910          */
911
912         return SEQ_WRITE(screen, C0_CSI, C1_CSI, "?64;1;6;9;15c");
913 }
914
915 static int screen_DA2(term_screen *screen, const term_seq *seq) {
916         /*
917          * DA2 - secondary-device-attributes
918          * The secondary DA asks for the terminal-ID, firmware versions and
919          * other non-primary attributes. All these values are
920          * informational-only and should not be used by the host to detect
921          * terminal features.
922          *
923          * The terminal's response is:
924          *   ^[ > 61 ; FIRMWARE ; KEYBOARD c
925          * whereas 65 is fixed for VT525 terminals, the last terminal-line that
926          * increased this number. FIRMWARE is the firmware
927          * version encoded as major/minor (20 == 2.0) and KEYBOARD is 0 for STD
928          * keyboard and 1 for PC keyboards.
929          *
930          * We replace the firmware-version with the systemd-version so clients
931          * can decode it again.
932          */
933
934         return SEQ_WRITE(screen, C0_CSI, C1_CSI, ">65;" PACKAGE_VERSION ";1c");
935 }
936
937 static int screen_DA3(term_screen *screen, const term_seq *seq) {
938         /*
939          * DA3 - tertiary-device-attributes
940          * The tertiary DA is used to query the terminal-ID.
941          *
942          * The terminal's response is:
943          *   ^P ! | XX AA BB CC ^\
944          * whereas all four parameters are hexadecimal-encoded pairs. XX
945          * denotes the manufacturing site, AA BB CC is the terminal's ID.
946          */
947
948         /* we do not support tertiary DAs */
949         return 0;
950 }
951
952 static int screen_DC1(term_screen *screen, const term_seq *seq) {
953         /*
954          * DC1 - device-control-1 or XON
955          * This clears any previous XOFF and resumes terminal-transmission.
956          */
957
958         /* we do not support XON */
959         return 0;
960 }
961
962 static int screen_DC3(term_screen *screen, const term_seq *seq) {
963         /*
964          * DC3 - device-control-3 or XOFF
965          * Stops terminal transmission. No further characters are sent until
966          * an XON is received.
967          */
968
969         /* we do not support XOFF */
970         return 0;
971 }
972
973 static int screen_DCH(term_screen *screen, const term_seq *seq) {
974         /*
975          * DCH - delete-character
976          * This deletes @argv[0] characters at the current cursor position. As
977          * characters are deleted, the remaining characters between the cursor
978          * and right margin move to the left. Character attributes move with the
979          * characters. The terminal adds blank spaces with no visual character
980          * attributes at the right margin. DCH has no effect outside the
981          * scrolling margins.
982          *
983          * Defaults:
984          *   args[0]: 1
985          */
986
987         unsigned int num = 1;
988
989         if (seq->args[0] > 0)
990                 num = seq->args[0];
991
992         screen_cursor_clear_wrap(screen);
993         term_page_delete_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
994
995         return 0;
996 }
997
998 static int screen_DECALN(term_screen *screen, const term_seq *seq) {
999         /*
1000          * DECALN - screen-alignment-pattern
1001          *
1002          * Probably not worth implementing.
1003          */
1004
1005         return 0;
1006 }
1007
1008 static int screen_DECANM(term_screen *screen, const term_seq *seq) {
1009         /*
1010          * DECANM - ansi-mode
1011          * Set the terminal into VT52 compatibility mode. Control sequences
1012          * overlap with regular sequences so we have to detect them early before
1013          * dispatching them.
1014          *
1015          * Probably not worth implementing.
1016          */
1017
1018         return 0;
1019 }
1020
1021 static int screen_DECBI(term_screen *screen, const term_seq *seq) {
1022         /*
1023          * DECBI - back-index
1024          * This control function moves the cursor backward one column. If the
1025          * cursor is at the left margin, then all screen data within the margin
1026          * moves one column to the right. The column that shifted past the right
1027          * margin is lost.
1028          * DECBI adds a new column at the left margin with no visual attributes.
1029          * DECBI does not affect the margins. If the cursor is beyond the
1030          * left-margin at the left border, then the terminal ignores DECBI.
1031          *
1032          * Probably not worth implementing.
1033          */
1034
1035         return 0;
1036 }
1037
1038 static int screen_DECCARA(term_screen *screen, const term_seq *seq) {
1039         /*
1040          * DECCARA - change-attributes-in-rectangular-area
1041          *
1042          * Probably not worth implementing.
1043          */
1044
1045         return 0;
1046 }
1047
1048 static int screen_DECCRA(term_screen *screen, const term_seq *seq) {
1049         /*
1050          * DECCRA - copy-rectangular-area
1051          *
1052          * Probably not worth implementing.
1053          */
1054
1055         return 0;
1056 }
1057
1058 static int screen_DECDC(term_screen *screen, const term_seq *seq) {
1059         /*
1060          * DECDC - delete-column
1061          *
1062          * Probably not worth implementing.
1063          */
1064
1065         return 0;
1066 }
1067
1068 static int screen_DECDHL_BH(term_screen *screen, const term_seq *seq) {
1069         /*
1070          * DECDHL_BH - double-width-double-height-line: bottom half
1071          *
1072          * Probably not worth implementing.
1073          */
1074
1075         return 0;
1076 }
1077
1078 static int screen_DECDHL_TH(term_screen *screen, const term_seq *seq) {
1079         /*
1080          * DECDHL_TH - double-width-double-height-line: top half
1081          *
1082          * Probably not worth implementing.
1083          */
1084
1085         return 0;
1086 }
1087
1088 static int screen_DECDWL(term_screen *screen, const term_seq *seq) {
1089         /*
1090          * DECDWL - double-width-single-height-line
1091          *
1092          * Probably not worth implementing.
1093          */
1094
1095         return 0;
1096 }
1097
1098 static int screen_DECEFR(term_screen *screen, const term_seq *seq) {
1099         /*
1100          * DECEFR - enable-filter-rectangle
1101          * Defines the coordinates of a filter rectangle (top, left, bottom,
1102          * right as @args[0] to @args[3]) and activates it.
1103          * Anytime the locator is detected outside of the filter rectangle, an
1104          * outside rectangle event is generated and the rectangle is disabled.
1105          * Filter rectangles are always treated as "one-shot" events. Any
1106          * parameters that are omitted default to the current locator position.
1107          * If all parameters are omitted, any locator motion will be reported.
1108          * DECELR always cancels any prevous rectangle definition.
1109          *
1110          * The locator is usually associated with the mouse-cursor, but based
1111          * on cells instead of pixels. See DECELR how to initialize and enable
1112          * it. DECELR can also enable pixel-mode instead of cell-mode.
1113          *
1114          * TODO: implement
1115          */
1116
1117         return 0;
1118 }
1119
1120 static int screen_DECELF(term_screen *screen, const term_seq *seq) {
1121         /*
1122          * DECELF - enable-local-functions
1123          *
1124          * Probably not worth implementing.
1125          */
1126
1127         return 0;
1128 }
1129
1130 static int screen_DECELR(term_screen *screen, const term_seq *seq) {
1131         /*
1132          * DECELR - enable-locator-reporting
1133          * This changes the locator-reporting mode. @args[0] specifies the mode
1134          * to set, 0 disables locator-reporting, 1 enables it continuously, 2
1135          * enables it for a single report. @args[1] specifies the
1136          * precision-mode. 0 and 2 set the reporting to cell-precision, 1 sets
1137          * pixel-precision.
1138          *
1139          * Defaults:
1140          *   args[0]: 0
1141          *   args[1]: 0
1142          *
1143          * TODO: implement
1144          */
1145
1146         return 0;
1147 }
1148
1149 static int screen_DECERA(term_screen *screen, const term_seq *seq) {
1150         /*
1151          * DECERA - erase-rectangular-area
1152          *
1153          * Probably not worth implementing.
1154          */
1155
1156         return 0;
1157 }
1158
1159 static int screen_DECFI(term_screen *screen, const term_seq *seq) {
1160         /*
1161          * DECFI - forward-index
1162          * This control function moves the cursor forward one column. If the
1163          * cursor is at the right margin, then all screen data within the
1164          * margins moves one column to the left. The column shifted past the
1165          * left margin is lost.
1166          * DECFI adds a new column at the right margin, with no visual
1167          * attributes. DECFI does not affect margins. If the cursor is beyond
1168          * the right margin at the border of the page when the terminal
1169          * receives DECFI, then the terminal ignores DECFI.
1170          *
1171          * Probably not worth implementing.
1172          */
1173
1174         return 0;
1175 }
1176
1177 static int screen_DECFRA(term_screen *screen, const term_seq *seq) {
1178         /*
1179          * DECFRA - fill-rectangular-area
1180          *
1181          * Probably not worth implementing.
1182          */
1183
1184         return 0;
1185 }
1186
1187 static int screen_DECIC(term_screen *screen, const term_seq *seq) {
1188         /*
1189          * DECIC - insert-column
1190          *
1191          * Probably not worth implementing.
1192          */
1193
1194         return 0;
1195 }
1196
1197 static int screen_DECID(term_screen *screen, const term_seq *seq) {
1198         /*
1199          * DECID - return-terminal-id
1200          * This is an obsolete form of TERM_CMD_DA1.
1201          */
1202
1203         return screen_DA1(screen, seq);
1204 }
1205
1206 static int screen_DECINVM(term_screen *screen, const term_seq *seq) {
1207         /*
1208          * DECINVM - invoke-macro
1209          *
1210          * Probably not worth implementing.
1211          */
1212
1213         return 0;
1214 }
1215
1216 static int screen_DECKBD(term_screen *screen, const term_seq *seq) {
1217         /*
1218          * DECKBD - keyboard-language-selection
1219          *
1220          * Probably not worth implementing.
1221          */
1222
1223         return 0;
1224 }
1225
1226 static int screen_DECKPAM(term_screen *screen, const term_seq *seq) {
1227         /*
1228          * DECKPAM - keypad-application-mode
1229          * Enables the keypad-application mode. If enabled, the keypad sends
1230          * special characters instead of the printed characters. This way,
1231          * applications can detect whether a numeric key was pressed on the
1232          * top-row or on the keypad.
1233          * Default is keypad-numeric-mode.
1234          */
1235
1236         screen->flags |= TERM_FLAG_KEYPAD_MODE;
1237
1238         return 0;
1239 }
1240
1241 static int screen_DECKPNM(term_screen *screen, const term_seq *seq) {
1242         /*
1243          * DECKPNM - keypad-numeric-mode
1244          * This disables the keypad-application-mode (DECKPAM) and returns to
1245          * the keypad-numeric-mode. Keypresses on the keypad generate the same
1246          * sequences as corresponding keypresses on the main keyboard.
1247          * Default is keypad-numeric-mode.
1248          */
1249
1250         screen->flags &= ~TERM_FLAG_KEYPAD_MODE;
1251
1252         return 0;
1253 }
1254
1255 static int screen_DECLFKC(term_screen *screen, const term_seq *seq) {
1256         /*
1257          * DECLFKC - local-function-key-control
1258          *
1259          * Probably not worth implementing.
1260          */
1261
1262         return 0;
1263 }
1264
1265 static int screen_DECLL(term_screen *screen, const term_seq *seq) {
1266         /*
1267          * DECLL - load-leds
1268          *
1269          * Probably not worth implementing.
1270          */
1271
1272         return 0;
1273 }
1274
1275 static int screen_DECLTOD(term_screen *screen, const term_seq *seq) {
1276         /*
1277          * DECLTOD - load-time-of-day
1278          *
1279          * Probably not worth implementing.
1280          */
1281
1282         return 0;
1283 }
1284
1285 static int screen_DECPCTERM(term_screen *screen, const term_seq *seq) {
1286         /*
1287          * DECPCTERM - pcterm-mode
1288          * This enters/exits the PCTerm mode. Default mode is VT-mode. It can
1289          * also select parameters for scancode/keycode mappings in SCO mode.
1290          *
1291          * Definitely not worth implementing. Lets kill PCTerm/SCO modes!
1292          */
1293
1294         return 0;
1295 }
1296
1297 static int screen_DECPKA(term_screen *screen, const term_seq *seq) {
1298         /*
1299          * DECPKA - program-key-action
1300          *
1301          * Probably not worth implementing.
1302          */
1303
1304         return 0;
1305 }
1306
1307 static int screen_DECPKFMR(term_screen *screen, const term_seq *seq) {
1308         /*
1309          * DECPKFMR - program-key-free-memory-report
1310          *
1311          * Probably not worth implementing.
1312          */
1313
1314         return 0;
1315 }
1316
1317 static int screen_DECRARA(term_screen *screen, const term_seq *seq) {
1318         /*
1319          * DECRARA - reverse-attributes-in-rectangular-area
1320          *
1321          * Probably not worth implementing.
1322          */
1323
1324         return 0;
1325 }
1326
1327 static int screen_DECRC(term_screen *screen, const term_seq *seq) {
1328         /*
1329          * DECRC - restore-cursor
1330          * Restores the terminal to the state saved by the save cursor (DECSC)
1331          * function. This includes more than just the cursor-position.
1332          *
1333          * If nothing was saved by DECSC, then DECRC performs the following
1334          * actions:
1335          *   * Moves the cursor to the home position (upper left of screen).
1336          *   * Resets origin mode (DECOM).
1337          *   * Turns all character attributes off (normal setting).
1338          *   * Maps the ASCII character set into GL, and the DEC Supplemental
1339          *     Graphic set into GR.
1340          *
1341          * The terminal maintains a separate DECSC buffer for the main display
1342          * and the status line. This feature lets you save a separate operating
1343          * state for the main display and the status line.
1344          */
1345
1346         screen_restore_state(screen, &screen->saved);
1347
1348         return 0;
1349 }
1350
1351 static int screen_DECREQTPARM(term_screen *screen, const term_seq *seq) {
1352         /*
1353          * DECREQTPARM - request-terminal-parameters
1354          * The sequence DECREPTPARM is sent by the terminal controller to notify
1355          * the host of the status of selected terminal parameters. The status
1356          * sequence may be sent when requested by the host or at the terminal's
1357          * discretion. DECREPTPARM is sent upon receipt of a DECREQTPARM.
1358          *
1359          * If @args[0] is 0, this marks a request and the terminal is allowed
1360          * to send DECREPTPARM messages without request. If it is 1, the same
1361          * applies but the terminal should no longer send DECREPTPARM
1362          * unrequested.
1363          * 2 and 3 mark a report, but 3 is only used if the terminal answers as
1364          * an explicit request with @args[0] == 1.
1365          *
1366          * The other arguments are ignored in requests, but have the following
1367          * meaning in responses:
1368          *   args[1]: 1=no-parity-set 4=parity-set-and-odd 5=parity-set-and-even
1369          *   args[2]: 1=8bits-per-char 2=7bits-per-char
1370          *   args[3]: transmission-speed
1371          *   args[4]: receive-speed
1372          *   args[5]: 1=bit-rate-multiplier-is-16
1373          *   args[6]: This value communicates the four switch values in block 5
1374          *            of SETUP B, which are only visible to the user when an STP
1375          *            option is installed. These bits may be assigned for an STP
1376          *            device. The four bits are a decimal-encoded binary number.
1377          *            Value between 0-15.
1378          *
1379          * The transmission/receive speeds have mappings for number => bits/s
1380          * which are quite weird. Examples are: 96->3600, 112->9600, 120->19200
1381          *
1382          * Defaults:
1383          *   args[0]: 0
1384          */
1385
1386         if (seq->n_args < 1 || seq->args[0] == 0) {
1387                 screen->flags &= ~TERM_FLAG_INHIBIT_TPARM;
1388                 return SEQ_WRITE(screen, C0_CSI, C1_CSI, "2;1;1;120;120;1;0x");
1389         } else if (seq->args[0] == 1) {
1390                 screen->flags |= TERM_FLAG_INHIBIT_TPARM;
1391                 return SEQ_WRITE(screen, C0_CSI, C1_CSI, "3;1;1;120;120;1;0x");
1392         } else {
1393                 return 0;
1394         }
1395 }
1396
1397 static int screen_DECRPKT(term_screen *screen, const term_seq *seq) {
1398         /*
1399          * DECRPKT - report-key-type
1400          * Response to DECRQKT, we can safely ignore it as we're the one sending
1401          * it to the host.
1402          */
1403
1404         return 0;
1405 }
1406
1407 static int screen_DECRQCRA(term_screen *screen, const term_seq *seq) {
1408         /*
1409          * DECRQCRA - request-checksum-of-rectangular-area
1410          *
1411          * Probably not worth implementing.
1412          */
1413
1414         return 0;
1415 }
1416
1417 static int screen_DECRQDE(term_screen *screen, const term_seq *seq) {
1418         /*
1419          * DECRQDE - request-display-extent
1420          *
1421          * Probably not worth implementing.
1422          */
1423
1424         return 0;
1425 }
1426
1427 static int screen_DECRQKT(term_screen *screen, const term_seq *seq) {
1428         /*
1429          * DECRQKT - request-key-type
1430          *
1431          * Probably not worth implementing.
1432          */
1433
1434         return 0;
1435 }
1436
1437 static int screen_DECRQLP(term_screen *screen, const term_seq *seq) {
1438         /*
1439          * DECRQLP - request-locator-position
1440          * See DECELR for locator-information.
1441          *
1442          * TODO: document and implement
1443          */
1444
1445         return 0;
1446 }
1447
1448 static int screen_DECRQM_ANSI(term_screen *screen, const term_seq *seq) {
1449         /*
1450          * DECRQM_ANSI - request-mode-ansi
1451          * The host sends this control function to find out if a particular mode
1452          * is set or reset. The terminal responds with a report mode function.
1453          * @args[0] contains the mode to query.
1454          *
1455          * Response is DECRPM with the first argument set to the mode that was
1456          * queried, second argument is 0 if mode is invalid, 1 if mode is set,
1457          * 2 if mode is not set (reset), 3 if mode is permanently set and 4 if
1458          * mode is permanently not set (reset):
1459          *   ANSI: ^[ MODE ; VALUE $ y
1460          *   DEC:  ^[ ? MODE ; VALUE $ y
1461          *
1462          * TODO: implement
1463          */
1464
1465         return 0;
1466 }
1467
1468 static int screen_DECRQM_DEC(term_screen *screen, const term_seq *seq) {
1469         /*
1470          * DECRQM_DEC - request-mode-dec
1471          * Same as DECRQM_ANSI but for DEC modes.
1472          *
1473          * TODO: implement
1474          */
1475
1476         return 0;
1477 }
1478
1479 static int screen_DECRQPKFM(term_screen *screen, const term_seq *seq) {
1480         /*
1481          * DECRQPKFM - request-program-key-free-memory
1482          *
1483          * Probably not worth implementing.
1484          */
1485
1486         return 0;
1487 }
1488
1489 static int screen_DECRQPSR(term_screen *screen, const term_seq *seq) {
1490         /*
1491          * DECRQPSR - request-presentation-state-report
1492          *
1493          * Probably not worth implementing.
1494          */
1495
1496         return 0;
1497 }
1498
1499 static int screen_DECRQTSR(term_screen *screen, const term_seq *seq) {
1500         /*
1501          * DECRQTSR - request-terminal-state-report
1502          *
1503          * Probably not worth implementing.
1504          */
1505
1506         return 0;
1507 }
1508
1509 static int screen_DECRQUPSS(term_screen *screen, const term_seq *seq) {
1510         /*
1511          * DECRQUPSS - request-user-preferred-supplemental-set
1512          *
1513          * Probably not worth implementing.
1514          */
1515
1516         return 0;
1517 }
1518
1519 static int screen_DECSACE(term_screen *screen, const term_seq *seq) {
1520         /*
1521          * DECSACE - select-attribute-change-extent
1522          *
1523          * Probably not worth implementing.
1524          */
1525
1526         return 0;
1527 }
1528
1529 static int screen_DECSASD(term_screen *screen, const term_seq *seq) {
1530         /*
1531          * DECSASD - select-active-status-display
1532          *
1533          * Probably not worth implementing.
1534          */
1535
1536         return 0;
1537 }
1538
1539 static int screen_DECSC(term_screen *screen, const term_seq *seq) {
1540         /*
1541          * DECSC - save-cursor
1542          * Save cursor and terminal state so it can be restored later on.
1543          * Saves the following items in the terminal's memory:
1544          *   * Cursor position
1545          *   * Character attributes set by the SGR command
1546          *   * Character sets (G0, G1, G2, or G3) currently in GL and GR
1547          *   * Wrap flag (autowrap or no autowrap)
1548          *   * State of origin mode (DECOM)
1549          *   * Selective erase attribute
1550          *   * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent
1551          */
1552
1553         screen_save_state(screen, &screen->saved);
1554
1555         return 0;
1556 }
1557
1558 static int screen_DECSCA(term_screen *screen, const term_seq *seq) {
1559         /*
1560          * DECSCA - select-character-protection-attribute
1561          * Defines the characters that come after it as erasable or not erasable
1562          * from the screen. The selective erase control functions (DECSED and
1563          * DECSEL) can only erase characters defined as erasable.
1564          *
1565          * @args[0] specifies the new mode. 0 and 2 mark any following character
1566          * as erasable, 1 marks it as not erasable.
1567          *
1568          * Defaults:
1569          *   args[0]: 0
1570          */
1571
1572         unsigned int mode = 0;
1573
1574         if (seq->args[0] > 0)
1575                 mode = seq->args[0];
1576
1577         switch (mode) {
1578         case 0:
1579         case 2:
1580                 screen->state.attr.protect = 0;
1581                 break;
1582         case 1:
1583                 screen->state.attr.protect = 1;
1584                 break;
1585         }
1586
1587         return 0;
1588 }
1589
1590 static int screen_DECSCL(term_screen *screen, const term_seq *seq) {
1591         /*
1592          * DECSCL - select-conformance-level
1593          * Select the terminal's operating level. The factory default is
1594          * level 4 (VT Level 4 mode, 7-bit controls).
1595          * When you change the conformance level, the terminal performs a hard
1596          * reset (RIS).
1597          *
1598          * @args[0] defines the conformance-level, valid values are:
1599          *   61: Level 1 (VT100)
1600          *   62: Level 2 (VT200)
1601          *   63: Level 3 (VT300)
1602          *   64: Level 4 (VT400)
1603          * @args[1] defines the 8bit-mode, valid values are:
1604          *    0: 8-bit controls
1605          *    1: 7-bit controls
1606          *    2: 8-bit controls (same as 0)
1607          *
1608          * If @args[0] is 61, then @args[1] is ignored and 7bit controls are
1609          * enforced.
1610          *
1611          * Defaults:
1612          *   args[0]: 64
1613          *   args[1]: 0
1614          */
1615
1616         unsigned int level = 64, bit = 0;
1617
1618         if (seq->n_args > 0) {
1619                 level = seq->args[0];
1620                 if (seq->n_args > 1)
1621                         bit = seq->args[1];
1622         }
1623
1624         term_screen_hard_reset(screen);
1625
1626         switch (level) {
1627         case 61:
1628                 screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT100;
1629                 screen->flags |= TERM_FLAG_7BIT_MODE;
1630                 break;
1631         case 62 ... 69:
1632                 screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
1633                 if (bit == 1)
1634                         screen->flags |= TERM_FLAG_7BIT_MODE;
1635                 else
1636                         screen->flags &= ~TERM_FLAG_7BIT_MODE;
1637                 break;
1638         }
1639
1640         return 0;
1641 }
1642
1643 static int screen_DECSCP(term_screen *screen, const term_seq *seq) {
1644         /*
1645          * DECSCP - select-communication-port
1646          *
1647          * Probably not worth implementing.
1648          */
1649
1650         return 0;
1651 }
1652
1653 static int screen_DECSCPP(term_screen *screen, const term_seq *seq) {
1654         /*
1655          * DECSCPP - select-columns-per-page
1656          * Select columns per page. The number of rows is unaffected by this.
1657          * @args[0] selectes the number of columns (width), DEC only defines 80
1658          * and 132, but we allow any integer here. 0 is equivalent to 80.
1659          * Page content is *not* cleared and the cursor is left untouched.
1660          * However, if the page is reduced in width and the cursor would be
1661          * outside the visible region, it's set to the right border. Newly added
1662          * cells are cleared. No data is retained outside the visible region.
1663          *
1664          * Defaults:
1665          *   args[0]: 0
1666          *
1667          * TODO: implement
1668          */
1669
1670         return 0;
1671 }
1672
1673 static int screen_DECSCS(term_screen *screen, const term_seq *seq) {
1674         /*
1675          * DECSCS - select-communication-speed
1676          *
1677          * Probably not worth implementing.
1678          */
1679
1680         return 0;
1681 }
1682
1683 static int screen_DECSCUSR(term_screen *screen, const term_seq *seq) {
1684         /*
1685          * DECSCUSR - set-cursor-style
1686          * This changes the style of the cursor. @args[0] can be one of:
1687          *   0, 1: blinking block
1688          *      2: steady block
1689          *      3: blinking underline
1690          *      4: steady underline
1691          * Changing this setting does _not_ affect the cursor visibility itself.
1692          * Use DECTCEM for that.
1693          *
1694          * Defaults:
1695          *   args[0]: 0
1696          *
1697          * TODO: implement
1698          */
1699
1700         return 0;
1701 }
1702
1703 static int screen_DECSDDT(term_screen *screen, const term_seq *seq) {
1704         /*
1705          * DECSDDT - select-disconnect-delay-time
1706          *
1707          * Probably not worth implementing.
1708          */
1709
1710         return 0;
1711 }
1712
1713 static int screen_DECSDPT(term_screen *screen, const term_seq *seq) {
1714         /*
1715          * DECSDPT - select-digital-printed-data-type
1716          *
1717          * Probably not worth implementing.
1718          */
1719
1720         return 0;
1721 }
1722
1723 static int screen_DECSED(term_screen *screen, const term_seq *seq) {
1724         /*
1725          * DECSED - selective-erase-in-display
1726          * This control function erases some or all of the erasable characters
1727          * in the display. DECSED can only erase characters defined as erasable
1728          * by the DECSCA control function. DECSED works inside or outside the
1729          * scrolling margins.
1730          *
1731          * @args[0] defines which regions are erased. If it is 0, all cells from
1732          * the cursor (inclusive) till the end of the display are erase. If it
1733          * is 1, all cells from the start of the display till the cursor
1734          * (inclusive) are erased. If it is 2, all cells are erased.
1735          *
1736          * Defaults:
1737          *   args[0]: 0
1738          */
1739
1740         unsigned int mode = 0;
1741
1742         if (seq->args[0] > 0)
1743                 mode = seq->args[0];
1744
1745         switch (mode) {
1746         case 0:
1747                 term_page_erase(screen->page,
1748                                 screen->state.cursor_x, screen->state.cursor_y,
1749                                 screen->page->width, screen->page->height,
1750                                 &screen->state.attr, screen->age, true);
1751                 break;
1752         case 1:
1753                 term_page_erase(screen->page,
1754                                 0, 0,
1755                                 screen->state.cursor_x, screen->state.cursor_y,
1756                                 &screen->state.attr, screen->age, true);
1757                 break;
1758         case 2:
1759                 term_page_erase(screen->page,
1760                                 0, 0,
1761                                 screen->page->width, screen->page->height,
1762                                 &screen->state.attr, screen->age, true);
1763                 break;
1764         }
1765
1766         return 0;
1767 }
1768
1769 static int screen_DECSEL(term_screen *screen, const term_seq *seq) {
1770         /*
1771          * DECSEL - selective-erase-in-line
1772          * This control function erases some or all of the erasable characters
1773          * in a single line of text. DECSEL erases only those characters defined
1774          * as erasable by the DECSCA control function. DECSEL works inside or
1775          * outside the scrolling margins.
1776          *
1777          * @args[0] defines the region to be erased. If it is 0, all cells from
1778          * the cursor (inclusive) till the end of the line are erase. If it is
1779          * 1, all cells from the start of the line till the cursor (inclusive)
1780          * are erased. If it is 2, the whole line of the cursor is erased.
1781          *
1782          * Defaults:
1783          *   args[0]: 0
1784          */
1785
1786         unsigned int mode = 0;
1787
1788         if (seq->args[0] > 0)
1789                 mode = seq->args[0];
1790
1791         switch (mode) {
1792         case 0:
1793                 term_page_erase(screen->page,
1794                                 screen->state.cursor_x, screen->state.cursor_y,
1795                                 screen->page->width, screen->state.cursor_y,
1796                                 &screen->state.attr, screen->age, true);
1797                 break;
1798         case 1:
1799                 term_page_erase(screen->page,
1800                                 0, screen->state.cursor_y,
1801                                 screen->state.cursor_x, screen->state.cursor_y,
1802                                 &screen->state.attr, screen->age, true);
1803                 break;
1804         case 2:
1805                 term_page_erase(screen->page,
1806                                 0, screen->state.cursor_y,
1807                                 screen->page->width, screen->state.cursor_y,
1808                                 &screen->state.attr, screen->age, true);
1809                 break;
1810         }
1811
1812         return 0;
1813 }
1814
1815 static int screen_DECSERA(term_screen *screen, const term_seq *seq) {
1816         /*
1817          * DECSERA - selective-erase-rectangular-area
1818          *
1819          * Probably not worth implementing.
1820          */
1821
1822         return 0;
1823 }
1824
1825 static int screen_DECSFC(term_screen *screen, const term_seq *seq) {
1826         /*
1827          * DECSFC - select-flow-control
1828          *
1829          * Probably not worth implementing.
1830          */
1831
1832         return 0;
1833 }
1834
1835 static int screen_DECSKCV(term_screen *screen, const term_seq *seq) {
1836         /*
1837          * DECSKCV - set-key-click-volume
1838          *
1839          * Probably not worth implementing.
1840          */
1841
1842         return 0;
1843 }
1844
1845 static int screen_DECSLCK(term_screen *screen, const term_seq *seq) {
1846         /*
1847          * DECSLCK - set-lock-key-style
1848          *
1849          * Probably not worth implementing.
1850          */
1851
1852         return 0;
1853 }
1854
1855 static int screen_DECSLE(term_screen *screen, const term_seq *seq) {
1856         /*
1857          * DECSLE - select-locator-events
1858          *
1859          * TODO: implement
1860          */
1861
1862         return 0;
1863 }
1864
1865 static int screen_DECSLPP(term_screen *screen, const term_seq *seq) {
1866         /*
1867          * DECSLPP - set-lines-per-page
1868          * Set the number of lines used for the page. @args[0] specifies the
1869          * number of lines to be used. DEC only allows a limited number of
1870          * choices, however, we allow all integers. 0 is equivalent to 24.
1871          *
1872          * Defaults:
1873          *   args[0]: 0
1874          *
1875          * TODO: implement
1876          */
1877
1878         return 0;
1879 }
1880
1881 static int screen_DECSLRM_OR_SC(term_screen *screen, const term_seq *seq) {
1882         /*
1883          * DECSLRM_OR_SC - set-left-and-right-margins or save-cursor
1884          *
1885          * TODO: Detect save-cursor and run it. DECSLRM is not worth
1886          *       implementing.
1887          */
1888
1889         return 0;
1890 }
1891
1892 static int screen_DECSMBV(term_screen *screen, const term_seq *seq) {
1893         /*
1894          * DECSMBV - set-margin-bell-volume
1895          *
1896          * Probably not worth implementing.
1897          */
1898
1899         return 0;
1900 }
1901
1902 static int screen_DECSMKR(term_screen *screen, const term_seq *seq) {
1903         /*
1904          * DECSMKR - select-modifier-key-reporting
1905          *
1906          * Probably not worth implementing.
1907          */
1908
1909         return 0;
1910 }
1911
1912 static int screen_DECSNLS(term_screen *screen, const term_seq *seq) {
1913         /*
1914          * DECSNLS - set-lines-per-screen
1915          *
1916          * Probably not worth implementing.
1917          */
1918
1919         return 0;
1920 }
1921
1922 static int screen_DECSPP(term_screen *screen, const term_seq *seq) {
1923         /*
1924          * DECSPP - set-port-parameter
1925          *
1926          * Probably not worth implementing.
1927          */
1928
1929         return 0;
1930 }
1931
1932 static int screen_DECSPPCS(term_screen *screen, const term_seq *seq) {
1933         /*
1934          * DECSPPCS - select-pro-printer-character-set
1935          *
1936          * Probably not worth implementing.
1937          */
1938
1939         return 0;
1940 }
1941
1942 static int screen_DECSPRTT(term_screen *screen, const term_seq *seq) {
1943         /*
1944          * DECSPRTT - select-printer-type
1945          *
1946          * Probably not worth implementing.
1947          */
1948
1949         return 0;
1950 }
1951
1952 static int screen_DECSR(term_screen *screen, const term_seq *seq) {
1953         /*
1954          * DECSR - secure-reset
1955          *
1956          * Probably not worth implementing.
1957          */
1958
1959         return 0;
1960 }
1961
1962 static int screen_DECSRFR(term_screen *screen, const term_seq *seq) {
1963         /*
1964          * DECSRFR - select-refresh-rate
1965          *
1966          * Probably not worth implementing.
1967          */
1968
1969         return 0;
1970 }
1971
1972 static int screen_DECSSCLS(term_screen *screen, const term_seq *seq) {
1973         /*
1974          * DECSSCLS - set-scroll-speed
1975          *
1976          * Probably not worth implementing.
1977          */
1978
1979         return 0;
1980 }
1981
1982 static int screen_DECSSDT(term_screen *screen, const term_seq *seq) {
1983         /*
1984          * DECSSDT - select-status-display-line-type
1985          *
1986          * Probably not worth implementing.
1987          */
1988
1989         return 0;
1990 }
1991
1992 static int screen_DECSSL(term_screen *screen, const term_seq *seq) {
1993         /*
1994          * DECSSL - select-setup-language
1995          *
1996          * Probably not worth implementing.
1997          */
1998
1999         return 0;
2000 }
2001
2002 static int screen_DECST8C(term_screen *screen, const term_seq *seq) {
2003         /*
2004          * DECST8C - set-tab-at-every-8-columns
2005          * Clear the tab-ruler and reset it to a tab at every 8th column,
2006          * starting at 9 (though, setting a tab at 1 is fine as it has no
2007          * effect).
2008          */
2009
2010         unsigned int i;
2011
2012         for (i = 0; i < screen->page->width; i += 8)
2013                 screen->tabs[i / 8] = 0x1;
2014
2015         return 0;
2016 }
2017
2018 static int screen_DECSTBM(term_screen *screen, const term_seq *seq) {
2019         /*
2020          * DECSTBM - set-top-and-bottom-margins
2021          * This control function sets the top and bottom margins for the current
2022          * page. You cannot perform scrolling outside the margins.
2023          *
2024          * @args[0] defines the top margin, @args[1] defines the bottom margin.
2025          * The bottom margin must be lower than the top-margin.
2026          *
2027          * This call resets the cursor position to 0/0 of the page.
2028          *
2029          * Defaults:
2030          *   args[0]: 1
2031          *   args[1]: last page-line
2032          */
2033
2034         unsigned int top, bottom;
2035
2036         top = 1;
2037         bottom = screen->page->height;
2038
2039         if (seq->args[0] > 0)
2040                 top = seq->args[0];
2041         if (seq->args[1] > 0)
2042                 bottom = seq->args[1];
2043
2044         if (top > screen->page->height)
2045                 top = screen->page->height;
2046         if (bottom > screen->page->height)
2047                 bottom = screen->page->height;
2048
2049         if (top >= bottom || top > screen->page->height || bottom > screen->page->height) {
2050                 top = 1;
2051                 bottom = screen->page->height;
2052         }
2053
2054         term_page_set_scroll_region(screen->page, top - 1, bottom - top + 1);
2055         screen_cursor_clear_wrap(screen);
2056         screen_cursor_set(screen, 0, 0);
2057
2058         return 0;
2059 }
2060
2061 static int screen_DECSTR(term_screen *screen, const term_seq *seq) {
2062         /*
2063          * DECSTR - soft-terminal-reset
2064          * Perform a soft reset to the default values.
2065          */
2066
2067         term_screen_soft_reset(screen);
2068
2069         return 0;
2070 }
2071
2072 static int screen_DECSTRL(term_screen *screen, const term_seq *seq) {
2073         /*
2074          * DECSTRL - set-transmit-rate-limit
2075          *
2076          * Probably not worth implementing.
2077          */
2078
2079         return 0;
2080 }
2081
2082 static int screen_DECSWBV(term_screen *screen, const term_seq *seq) {
2083         /*
2084          * DECSWBV - set-warning-bell-volume
2085          *
2086          * Probably not worth implementing.
2087          */
2088
2089         return 0;
2090 }
2091
2092 static int screen_DECSWL(term_screen *screen, const term_seq *seq) {
2093         /*
2094          * DECSWL - single-width-single-height-line
2095          *
2096          * Probably not worth implementing.
2097          */
2098
2099         return 0;
2100 }
2101
2102 static int screen_DECTID(term_screen *screen, const term_seq *seq) {
2103         /*
2104          * DECTID - select-terminal-id
2105          *
2106          * Probably not worth implementing.
2107          */
2108
2109         return 0;
2110 }
2111
2112 static int screen_DECTME(term_screen *screen, const term_seq *seq) {
2113         /*
2114          * DECTME - terminal-mode-emulation
2115          *
2116          * Probably not worth implementing.
2117          */
2118
2119         return 0;
2120 }
2121
2122 static int screen_DECTST(term_screen *screen, const term_seq *seq) {
2123         /*
2124          * DECTST - invoke-confidence-test
2125          *
2126          * Probably not worth implementing.
2127          */
2128
2129         return 0;
2130 }
2131
2132 static int screen_DL(term_screen *screen, const term_seq *seq) {
2133         /*
2134          * DL - delete-line
2135          * This control function deletes one or more lines in the scrolling
2136          * region, starting with the line that has the cursor. @args[0] defines
2137          * the number of lines to delete. 0 is treated the same as 1.
2138          * As lines are deleted, lines below the cursor and in the scrolling
2139          * region move up. The terminal adds blank lines with no visual
2140          * character attributes at the bottom of the scrolling region. If it is
2141          * greater than the number of lines remaining on the page, DL deletes
2142          * only the remaining lines. DL has no effect outside the scrolling
2143          * margins.
2144          *
2145          * Defaults:
2146          *   args[0]: 1
2147          */
2148
2149         unsigned int num = 1;
2150
2151         if (seq->args[0] > 0)
2152                 num = seq->args[0];
2153
2154         term_page_delete_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
2155
2156         return 0;
2157 }
2158
2159 static int screen_DSR_ANSI(term_screen *screen, const term_seq *seq) {
2160         /*
2161          * DSR_ANSI - device-status-report-ansi
2162          *
2163          * TODO: implement
2164          */
2165
2166         return 0;
2167 }
2168
2169 static int screen_DSR_DEC(term_screen *screen, const term_seq *seq) {
2170         /*
2171          * DSR_DEC - device-status-report-dec
2172          *
2173          * TODO: implement
2174          */
2175
2176         return 0;
2177 }
2178
2179 static int screen_ECH(term_screen *screen, const term_seq *seq) {
2180         /*
2181          * ECH - erase-character
2182          * This control function erases one or more characters, from the cursor
2183          * position to the right. ECH clears character attributes from erased
2184          * character positions. ECH works inside or outside the scrolling
2185          * margins.
2186          * @args[0] defines the number of characters to erase. 0 is treated the
2187          * same as 1.
2188          *
2189          * Defaults:
2190          *   args[0]: 1
2191          */
2192
2193         unsigned int num = 1;
2194
2195         if (seq->args[0] > 0)
2196                 num = seq->args[0];
2197
2198         term_page_erase(screen->page,
2199                         screen->state.cursor_x, screen->state.cursor_y,
2200                         screen->state.cursor_x + num, screen->state.cursor_y,
2201                         &screen->state.attr, screen->age, false);
2202
2203         return 0;
2204 }
2205
2206 static int screen_ED(term_screen *screen, const term_seq *seq) {
2207         /*
2208          * ED - erase-in-display
2209          * This control function erases characters from part or all of the
2210          * display. When you erase complete lines, they become single-height,
2211          * single-width lines, with all visual character attributes cleared. ED
2212          * works inside or outside the scrolling margins.
2213          *
2214          * @args[0] defines the region to erase. 0 means from cursor (inclusive)
2215          * till the end of the screen. 1 means from the start of the screen till
2216          * the cursor (inclusive) and 2 means the whole screen.
2217          *
2218          * Defaults:
2219          *   args[0]: 0
2220          */
2221
2222         unsigned int mode = 0;
2223
2224         if (seq->args[0] > 0)
2225                 mode = seq->args[0];
2226
2227         switch (mode) {
2228         case 0:
2229                 term_page_erase(screen->page,
2230                                 screen->state.cursor_x, screen->state.cursor_y,
2231                                 screen->page->width, screen->page->height,
2232                                 &screen->state.attr, screen->age, false);
2233                 break;
2234         case 1:
2235                 term_page_erase(screen->page,
2236                                 0, 0,
2237                                 screen->state.cursor_x, screen->state.cursor_y,
2238                                 &screen->state.attr, screen->age, false);
2239                 break;
2240         case 2:
2241                 term_page_erase(screen->page,
2242                                 0, 0,
2243                                 screen->page->width, screen->page->height,
2244                                 &screen->state.attr, screen->age, false);
2245                 break;
2246         }
2247
2248         return 0;
2249 }
2250
2251 static int screen_EL(term_screen *screen, const term_seq *seq) {
2252         /*
2253          * EL - erase-in-line
2254          * This control function erases characters on the line that has the
2255          * cursor. EL clears all character attributes from erased character
2256          * positions. EL works inside or outside the scrolling margins.
2257          *
2258          * @args[0] defines the region to erase. 0 means from cursor (inclusive)
2259          * till the end of the line. 1 means from the start of the line till the
2260          * cursor (inclusive) and 2 means the whole line.
2261          *
2262          * Defaults:
2263          *   args[0]: 0
2264          */
2265
2266         unsigned int mode = 0;
2267
2268         if (seq->args[0] > 0)
2269                 mode = seq->args[0];
2270
2271         switch (mode) {
2272         case 0:
2273                 term_page_erase(screen->page,
2274                                 screen->state.cursor_x, screen->state.cursor_y,
2275                                 screen->page->width, screen->state.cursor_y,
2276                                 &screen->state.attr, screen->age, false);
2277                 break;
2278         case 1:
2279                 term_page_erase(screen->page,
2280                                 0, screen->state.cursor_y,
2281                                 screen->state.cursor_x, screen->state.cursor_y,
2282                                 &screen->state.attr, screen->age, false);
2283                 break;
2284         case 2:
2285                 term_page_erase(screen->page,
2286                                 0, screen->state.cursor_y,
2287                                 screen->page->width, screen->state.cursor_y,
2288                                 &screen->state.attr, screen->age, false);
2289                 break;
2290         }
2291
2292         return 0;
2293 }
2294
2295 static int screen_ENQ(term_screen *screen, const term_seq *seq) {
2296         /*
2297          * ENQ - enquiry
2298          * Transmit the answerback-string. If none is set, do nothing.
2299          */
2300
2301         if (screen->answerback)
2302                 return screen_write(screen, screen->answerback, strlen(screen->answerback));
2303
2304         return 0;
2305 }
2306
2307 static int screen_EPA(term_screen *screen, const term_seq *seq) {
2308         /*
2309          * EPA - end-of-guarded-area
2310          *
2311          * TODO: What is this?
2312          */
2313
2314         return 0;
2315 }
2316
2317 static int screen_FF(term_screen *screen, const term_seq *seq) {
2318         /*
2319          * FF - form-feed
2320          * This causes the cursor to jump to the next line. It is treated the
2321          * same as LF.
2322          */
2323
2324         return screen_LF(screen, seq);
2325 }
2326
2327 static int screen_HPA(term_screen *screen, const term_seq *seq) {
2328         /*
2329          * HPA - horizontal-position-absolute
2330          * HPA causes the active position to be moved to the n-th horizontal
2331          * position of the active line. If an attempt is made to move the active
2332          * position past the last position on the line, then the active position
2333          * stops at the last position on the line.
2334          *
2335          * @args[0] defines the horizontal position. 0 is treated as 1.
2336          *
2337          * Defaults:
2338          *   args[0]: 1
2339          */
2340
2341         unsigned int num = 1;
2342
2343         if (seq->args[0] > 0)
2344                 num = seq->args[0];
2345
2346         screen_cursor_clear_wrap(screen);
2347         screen_cursor_set(screen, num - 1, screen->state.cursor_y);
2348
2349         return 0;
2350 }
2351
2352 static int screen_HPR(term_screen *screen, const term_seq *seq) {
2353         /*
2354          * HPR - horizontal-position-relative
2355          * HPR causes the active position to be moved to the n-th following
2356          * horizontal position of the active line. If an attempt is made to move
2357          * the active position past the last position on the line, then the
2358          * active position stops at the last position on the line.
2359          *
2360          * @args[0] defines the horizontal position. 0 is treated as 1.
2361          *
2362          * Defaults:
2363          *   args[0]: 1
2364          */
2365
2366         unsigned int num = 1;
2367
2368         if (seq->args[0] > 0)
2369                 num = seq->args[0];
2370
2371         screen_cursor_clear_wrap(screen);
2372         screen_cursor_right(screen, num);
2373
2374         return 0;
2375 }
2376
2377 static int screen_HT(term_screen *screen, const term_seq *seq) {
2378         /*
2379          * HT - horizontal-tab
2380          * Moves the cursor to the next tab stop. If there are no more tab
2381          * stops, the cursor moves to the right margin. HT does not cause text
2382          * to auto wrap.
2383          */
2384
2385         screen_cursor_clear_wrap(screen);
2386         screen_cursor_right_tab(screen, 1);
2387
2388         return 0;
2389 }
2390
2391 static int screen_HTS(term_screen *screen, const term_seq *seq) {
2392         /*
2393          * HTS - horizontal-tab-set
2394          * HTS sets a horizontal tab stop at the column position indicated by
2395          * the value of the active column when the terminal receives an HTS.
2396          *
2397          * Executing an HTS does not effect the other horizontal tab stop
2398          * settings.
2399          */
2400
2401         unsigned int pos;
2402
2403         pos = screen->state.cursor_x;
2404         if (screen->page->width > 0)
2405                 screen->tabs[pos / 8] |= 1U << (pos % 8);
2406
2407         return 0;
2408 }
2409
2410 static int screen_HVP(term_screen *screen, const term_seq *seq) {
2411         /*
2412          * HVP - horizontal-and-vertical-position
2413          * This control function works the same as the cursor position (CUP)
2414          * function. Origin mode (DECOM) selects line numbering and the ability
2415          * to move the cursor into margins.
2416          *
2417          * Defaults:
2418          *   args[0]: 1
2419          *   args[1]: 1
2420          */
2421
2422         return screen_CUP(screen, seq);
2423 }
2424
2425 static int screen_ICH(term_screen *screen, const term_seq *seq) {
2426         /*
2427          * ICH - insert-character
2428          * This control function inserts one or more space (SP) characters
2429          * starting at the cursor position. @args[0] is the number of characters
2430          * to insert. 0 is treated as 1.
2431          *
2432          * The ICH sequence inserts blank characters with the normal
2433          * character attribute. The cursor remains at the beginning of the blank
2434          * characters. Text between the cursor and right margin moves to the
2435          * right. Characters scrolled past the right margin are lost. ICH has no
2436          * effect outside the scrolling margins.
2437          *
2438          * Defaults:
2439          *   args[0]: 1
2440          */
2441
2442         unsigned int num = 1;
2443
2444         if (seq->args[0] > 0)
2445                 num = seq->args[0];
2446
2447         screen_cursor_clear_wrap(screen);
2448         term_page_insert_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
2449
2450         return 0;
2451 }
2452
2453 static int screen_IL(term_screen *screen, const term_seq *seq) {
2454         /*
2455          * IL - insert-line
2456          * This control function inserts one or more blank lines, starting at
2457          * the cursor. @args[0] is the number of lines to insert. 0 is treated
2458          * as 1.
2459          *
2460          * As lines are inserted, lines below the cursor and in the scrolling
2461          * region move down. Lines scrolled off the page are lost. IL has no
2462          * effect outside the page margins.
2463          *
2464          * Defaults:
2465          *   args[0]: 1
2466          */
2467
2468         unsigned int num = 1;
2469
2470         if (seq->args[0] > 0)
2471                 num = seq->args[0];
2472
2473         screen_cursor_clear_wrap(screen);
2474         term_page_insert_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
2475
2476         return 0;
2477 }
2478
2479 static int screen_IND(term_screen *screen, const term_seq *seq) {
2480         /*
2481          * IND - index
2482          * IND moves the cursor down one line in the same column. If the cursor
2483          * is at the bottom margin, then the screen performs a scroll-up.
2484          */
2485
2486         screen_cursor_down(screen, 1, true);
2487
2488         return 0;
2489 }
2490
2491 static int screen_LF(term_screen *screen, const term_seq *seq) {
2492         /*
2493          * LF - line-feed
2494          * Causes a line feed or a new line operation, depending on the setting
2495          * of line feed/new line mode.
2496          */
2497
2498         screen_cursor_down(screen, 1, true);
2499         if (screen->flags & TERM_FLAG_NEWLINE_MODE)
2500                 screen_cursor_left(screen, screen->state.cursor_x);
2501
2502         return 0;
2503 }
2504
2505 static int screen_LS1R(term_screen *screen, const term_seq *seq) {
2506         /*
2507          * LS1R - locking-shift-1-right
2508          * Map G1 into GR.
2509          */
2510
2511         screen->state.gr = &screen->g1;
2512
2513         return 0;
2514 }
2515
2516 static int screen_LS2(term_screen *screen, const term_seq *seq) {
2517         /*
2518          * LS2 - locking-shift-2
2519          * Map G2 into GL.
2520          */
2521
2522         screen->state.gl = &screen->g2;
2523
2524         return 0;
2525 }
2526
2527 static int screen_LS2R(term_screen *screen, const term_seq *seq) {
2528         /*
2529          * LS2R - locking-shift-2-right
2530          * Map G2 into GR.
2531          */
2532
2533         screen->state.gr = &screen->g2;
2534
2535         return 0;
2536 }
2537
2538 static int screen_LS3(term_screen *screen, const term_seq *seq) {
2539         /*
2540          * LS3 - locking-shift-3
2541          * Map G3 into GL.
2542          */
2543
2544         screen->state.gl = &screen->g3;
2545
2546         return 0;
2547 }
2548
2549 static int screen_LS3R(term_screen *screen, const term_seq *seq) {
2550         /*
2551          * LS3R - locking-shift-3-right
2552          * Map G3 into GR.
2553          */
2554
2555         screen->state.gr = &screen->g3;
2556
2557         return 0;
2558 }
2559
2560 static int screen_MC_ANSI(term_screen *screen, const term_seq *seq) {
2561         /*
2562          * MC_ANSI - media-copy-ansi
2563          *
2564          * Probably not worth implementing.
2565          */
2566
2567         return 0;
2568 }
2569
2570 static int screen_MC_DEC(term_screen *screen, const term_seq *seq) {
2571         /*
2572          * MC_DEC - media-copy-dec
2573          *
2574          * Probably not worth implementing.
2575          */
2576
2577         return 0;
2578 }
2579
2580 static int screen_NEL(term_screen *screen, const term_seq *seq) {
2581         /*
2582          * NEL - next-line
2583          * Moves cursor to first position on next line. If cursor is at bottom
2584          * margin, then screen performs a scroll-up.
2585          */
2586
2587         screen_cursor_clear_wrap(screen);
2588         screen_cursor_down(screen, 1, true);
2589         screen_cursor_set(screen, 0, screen->state.cursor_y);
2590
2591         return 0;
2592 }
2593
2594 static int screen_NP(term_screen *screen, const term_seq *seq) {
2595         /*
2596          * NP - next-page
2597          * This control function moves the cursor forward to the home position
2598          * on one of the following pages in page memory. If there is only one
2599          * page, then the terminal ignores NP.
2600          * If NP tries to move the cursor past the last page in memory, then the
2601          * cursor stops at the last page.
2602          *
2603          * @args[0] defines the number of pages to forward. 0 is treated as 1.
2604          *
2605          * Defaults:
2606          *   args[0]: 1
2607          *
2608          * Probably not worth implementing. We only support a single page.
2609          */
2610
2611         return 0;
2612 }
2613
2614 static int screen_NULL(term_screen *screen, const term_seq *seq) {
2615         /*
2616          * NULL - null
2617          * The NULL operation does nothing. ASCII NULL is always ignored.
2618          */
2619
2620         return 0;
2621 }
2622
2623 static int screen_PP(term_screen *screen, const term_seq *seq) {
2624         /*
2625          * PP - preceding-page
2626          * This control function moves the cursor backward to the home position
2627          * on one of the preceding pages in page memory. If there is only one
2628          * page, then the terminal ignores PP.
2629          * If PP tries to move the cursor back farther than the first page in
2630          * memory, then the cursor stops at the first page.
2631          *
2632          * @args[0] defines the number of pages to go backwards. 0 is treated
2633          * as 1.
2634          *
2635          * Defaults:
2636          *   args[0]: 1
2637          *
2638          * Probably not worth implementing. We only support a single page.
2639          */
2640
2641         return 0;
2642 }
2643
2644 static int screen_PPA(term_screen *screen, const term_seq *seq) {
2645         /*
2646          * PPA - page-position-absolute
2647          * This control function can move the cursor to the corresponding row
2648          * and column on any page in page memory. You select the page by its
2649          * number. If there is only one page, then the terminal ignores PPA.
2650          *
2651          * @args[0] is the number of the page to move the cursor to. If it is
2652          * greater than the number of the last page in memory, then the cursor
2653          * stops at the last page. If it is less than the number of the first
2654          * page, then the cursor stops at the first page.
2655          *
2656          * Defaults:
2657          *   args[0]: 1
2658          *
2659          * Probably not worth implementing. We only support a single page.
2660          */
2661
2662         return 0;
2663 }
2664
2665 static int screen_PPB(term_screen *screen, const term_seq *seq) {
2666         /*
2667          * PPB - page-position-backward
2668          * This control function moves the cursor backward to the corresponding
2669          * row and column on one of the preceding pages in page memory. If there
2670          * is only one page, then the terminal ignores PPB.
2671          *
2672          * @args[0] indicates the number of pages to move the cursor backward.
2673          * If it tries to move the cursor back farther than the first page in
2674          * memory, then the cursor stops at the first page. 0 is treated as 1.
2675          *
2676          * Defaults:
2677          *   args[0]: 1
2678          *
2679          * Probably not worth implementing. We only support a single page.
2680          */
2681
2682         return 0;
2683 }
2684
2685 static int screen_PPR(term_screen *screen, const term_seq *seq) {
2686         /*
2687          * PPR - page-position-relative
2688          * This control function moves the cursor forward to the corresponding
2689          * row and column on one of the following pages in page memory. If there
2690          * is only one page, then the terminal ignores PPR.
2691          *
2692          * @args[0] indicates how many pages to move the cursor forward. If it
2693          * tries to move the cursor beyond the last page in memory, then the
2694          * cursor stops at the last page. 0 is treated as 1.
2695          *
2696          * Defaults:
2697          *   args[0]: 1
2698          *
2699          * Probably not worth implementing. We only support a single page.
2700          */
2701
2702         return 0;
2703 }
2704
2705 static int screen_RC(term_screen *screen, const term_seq *seq) {
2706         /*
2707          * RC - restore-cursor
2708          */
2709
2710         return screen_DECRC(screen, seq);
2711 }
2712
2713 static int screen_REP(term_screen *screen, const term_seq *seq) {
2714         /*
2715          * REP - repeat
2716          * Repeat the preceding graphics-character the given number of times.
2717          * @args[0] specifies how often it shall be repeated. 0 is treated as 1.
2718          *
2719          * Defaults:
2720          *   args[0]: 1
2721          *
2722          * Probably not worth implementing.
2723          */
2724
2725         return 0;
2726 }
2727
2728 static int screen_RI(term_screen *screen, const term_seq *seq) {
2729         /*
2730          * RI - reverse-index
2731          * Moves the cursor up one line in the same column. If the cursor is at
2732          * the top margin, the page scrolls down.
2733          */
2734
2735         screen_cursor_up(screen, 1, true);
2736
2737         return 0;
2738 }
2739
2740 static int screen_RIS(term_screen *screen, const term_seq *seq) {
2741         /*
2742          * RIS - reset-to-initial-state
2743          * This control function causes a nonvolatile memory (NVR) recall to
2744          * occur. RIS replaces all set-up features with their saved settings.
2745          *
2746          * The terminal stores these saved settings in NVR memory. The saved
2747          * setting for a feature is the same as the factory-default setting,
2748          * unless you saved a new setting.
2749          */
2750
2751         term_screen_hard_reset(screen);
2752
2753         return 0;
2754 }
2755
2756 static int screen_RM_ANSI(term_screen *screen, const term_seq *seq) {
2757         /*
2758          * RM_ANSI - reset-mode-ansi
2759          *
2760          * TODO: implement (see VT510rm manual)
2761          */
2762
2763         unsigned int i;
2764
2765         for (i = 0; i < seq->n_args; ++i)
2766                 screen_mode_change(screen, seq->args[i], false, false);
2767
2768         return 0;
2769 }
2770
2771 static int screen_RM_DEC(term_screen *screen, const term_seq *seq) {
2772         /*
2773          * RM_DEC - reset-mode-dec
2774          * This is the same as RM_ANSI but for DEC modes.
2775          */
2776
2777         unsigned int i;
2778
2779         for (i = 0; i < seq->n_args; ++i)
2780                 screen_mode_change(screen, seq->args[i], true, false);
2781
2782         return 0;
2783 }
2784
2785 static int screen_S7C1T(term_screen *screen, const term_seq *seq) {
2786         /*
2787          * S7C1T - set-7bit-c1-terminal
2788          * This causes the terminal to start sending C1 controls as 7bit
2789          * sequences instead of 8bit C1 controls.
2790          * This is ignored if the terminal is below level-2 emulation mode
2791          * (VT100 and below), the terminal already sends 7bit controls then.
2792          */
2793
2794         if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
2795                 screen->flags |= TERM_FLAG_7BIT_MODE;
2796
2797         return 0;
2798 }
2799
2800 static int screen_S8C1T(term_screen *screen, const term_seq *seq) {
2801         /*
2802          * S8C1T - set-8bit-c1-terminal
2803          * This causes the terminal to start sending C1 controls as 8bit C1
2804          * control instead of 7bit sequences.
2805          * This is ignored if the terminal is below level-2 emulation mode
2806          * (VT100 and below). The terminal always sends 7bit controls in those
2807          * modes.
2808          */
2809
2810         if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
2811                 screen->flags &= ~TERM_FLAG_7BIT_MODE;
2812
2813         return 0;
2814 }
2815
2816 static int screen_SCS(term_screen *screen, const term_seq *seq) {
2817         /*
2818          * SCS - select-character-set
2819          * Designate character sets to G-sets. The mapping from intermediates
2820          * and terminal characters in the escape sequence to G-sets and
2821          * character-sets is non-trivial and implemented separately. See there
2822          * for more information.
2823          * This call simply sets the selected G-set to the desired
2824          * character-set.
2825          */
2826
2827         term_charset *cs = NULL;
2828
2829         /* TODO: support more of them? */
2830         switch (seq->charset) {
2831         case TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL:
2832         case TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL:
2833         case TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL:
2834         case TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL:
2835         case TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL:
2836         case TERM_CHARSET_ISO_LATIN_CYRILLIC:
2837                 break;
2838
2839         case TERM_CHARSET_DEC_SPECIAL_GRAPHIC:
2840                 cs = &term_dec_special_graphics;
2841                 break;
2842         case TERM_CHARSET_DEC_SUPPLEMENTAL:
2843                 cs = &term_dec_supplemental_graphics;
2844                 break;
2845         case TERM_CHARSET_DEC_TECHNICAL:
2846         case TERM_CHARSET_CYRILLIC_DEC:
2847         case TERM_CHARSET_DUTCH_NRCS:
2848         case TERM_CHARSET_FINNISH_NRCS:
2849         case TERM_CHARSET_FRENCH_NRCS:
2850         case TERM_CHARSET_FRENCH_CANADIAN_NRCS:
2851         case TERM_CHARSET_GERMAN_NRCS:
2852         case TERM_CHARSET_GREEK_DEC:
2853         case TERM_CHARSET_GREEK_NRCS:
2854         case TERM_CHARSET_HEBREW_DEC:
2855         case TERM_CHARSET_HEBREW_NRCS:
2856         case TERM_CHARSET_ITALIAN_NRCS:
2857         case TERM_CHARSET_NORWEGIAN_DANISH_NRCS:
2858         case TERM_CHARSET_PORTUGUESE_NRCS:
2859         case TERM_CHARSET_RUSSIAN_NRCS:
2860         case TERM_CHARSET_SCS_NRCS:
2861         case TERM_CHARSET_SPANISH_NRCS:
2862         case TERM_CHARSET_SWEDISH_NRCS:
2863         case TERM_CHARSET_SWISS_NRCS:
2864         case TERM_CHARSET_TURKISH_DEC:
2865         case TERM_CHARSET_TURKISH_NRCS:
2866                 break;
2867
2868         case TERM_CHARSET_USERPREF_SUPPLEMENTAL:
2869                 break;
2870         }
2871
2872         if (seq->intermediates & TERM_SEQ_FLAG_POPEN)
2873                 screen->g0 = cs ? : &term_unicode_lower;
2874         else if (seq->intermediates & TERM_SEQ_FLAG_PCLOSE)
2875                 screen->g1 = cs ? : &term_unicode_upper;
2876         else if (seq->intermediates & TERM_SEQ_FLAG_MULT)
2877                 screen->g2 = cs ? : &term_unicode_lower;
2878         else if (seq->intermediates & TERM_SEQ_FLAG_PLUS)
2879                 screen->g3 = cs ? : &term_unicode_upper;
2880         else if (seq->intermediates & TERM_SEQ_FLAG_MINUS)
2881                 screen->g1 = cs ? : &term_unicode_upper;
2882         else if (seq->intermediates & TERM_SEQ_FLAG_DOT)
2883                 screen->g2 = cs ? : &term_unicode_lower;
2884         else if (seq->intermediates & TERM_SEQ_FLAG_SLASH)
2885                 screen->g3 = cs ? : &term_unicode_upper;
2886
2887         return 0;
2888 }
2889
2890 static int screen_SD(term_screen *screen, const term_seq *seq) {
2891         /*
2892          * SD - scroll-down
2893          * This control function moves the user window down a specified number
2894          * of lines in page memory.
2895          * @args[0] is the number of lines to move the
2896          * user window up in page memory. New lines appear at the top of the
2897          * display. Old lines disappear at the bottom of the display. You
2898          * cannot pan past the top margin of the current page. 0 is treated
2899          * as 1.
2900          *
2901          * Defaults:
2902          *   args[0]: 1
2903          */
2904
2905         unsigned int num = 1;
2906
2907         if (seq->args[0] > 0)
2908                 num = seq->args[0];
2909
2910         term_page_scroll_down(screen->page, num, &screen->state.attr, screen->age, NULL);
2911
2912         return 0;
2913 }
2914
2915 static int screen_SGR(term_screen *screen, const term_seq *seq) {
2916         /*
2917          * SGR - select-graphics-rendition
2918          */
2919
2920         term_color *dst;
2921         unsigned int i, code;
2922         int v;
2923
2924         if (seq->n_args < 1) {
2925                 zero(screen->state.attr);
2926                 return 0;
2927         }
2928
2929         for (i = 0; i < seq->n_args; ++i) {
2930                 v = seq->args[i];
2931                 switch (v) {
2932                 case 1:
2933                         screen->state.attr.bold = 1;
2934                         break;
2935                 case 3:
2936                         screen->state.attr.italic = 1;
2937                         break;
2938                 case 4:
2939                         screen->state.attr.underline = 1;
2940                         break;
2941                 case 5:
2942                         screen->state.attr.blink = 1;
2943                         break;
2944                 case 7:
2945                         screen->state.attr.inverse = 1;
2946                         break;
2947                 case 8:
2948                         screen->state.attr.hidden = 1;
2949                         break;
2950                 case 22:
2951                         screen->state.attr.bold = 0;
2952                         break;
2953                 case 23:
2954                         screen->state.attr.italic = 0;
2955                         break;
2956                 case 24:
2957                         screen->state.attr.underline = 0;
2958                         break;
2959                 case 25:
2960                         screen->state.attr.blink = 0;
2961                         break;
2962                 case 27:
2963                         screen->state.attr.inverse = 0;
2964                         break;
2965                 case 28:
2966                         screen->state.attr.hidden = 0;
2967                         break;
2968                 case 30 ... 37:
2969                         screen->state.attr.fg.ccode = v - 30 + TERM_CCODE_BLACK;
2970                         break;
2971                 case 39:
2972                         screen->state.attr.fg.ccode = 0;
2973                         break;
2974                 case 40 ... 47:
2975                         screen->state.attr.bg.ccode = v - 40 + TERM_CCODE_BLACK;
2976                         break;
2977                 case 49:
2978                         screen->state.attr.bg.ccode = 0;
2979                         break;
2980                 case 90 ... 97:
2981                         screen->state.attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK;
2982                         break;
2983                 case 100 ... 107:
2984                         screen->state.attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK;
2985                         break;
2986                 case 38:
2987                         /* fallthrough */
2988                 case 48:
2989
2990                         if (v == 38)
2991                                 dst = &screen->state.attr.fg;
2992                         else
2993                                 dst = &screen->state.attr.bg;
2994
2995                         ++i;
2996                         if (i >= seq->n_args)
2997                                 break;
2998
2999                         switch (seq->args[i]) {
3000                         case 2:
3001                                 /* 24bit-color support */
3002
3003                                 i += 3;
3004                                 if (i >= seq->n_args)
3005                                         break;
3006
3007                                 dst->ccode = TERM_CCODE_RGB;
3008                                 dst->red = (seq->args[i - 2] >= 0) ? seq->args[i - 2] : 0;
3009                                 dst->green = (seq->args[i - 1] >= 0) ? seq->args[i - 1] : 0;
3010                                 dst->blue = (seq->args[i] >= 0) ? seq->args[i] : 0;
3011
3012                                 break;
3013                         case 5:
3014                                 /* 256-color support */
3015
3016                                 ++i;
3017                                 if (i >= seq->n_args || seq->args[i] < 0)
3018                                         break;
3019
3020                                 dst->ccode = TERM_CCODE_256;
3021                                 code = seq->args[i];
3022                                 dst->c256 = code < 256 ? code : 0;
3023
3024                                 break;
3025                         }
3026
3027                         break;
3028                 case -1:
3029                         /* fallthrough */
3030                 case 0:
3031                         zero(screen->state.attr);
3032                         break;
3033                 }
3034         }
3035
3036         return 0;
3037 }
3038
3039 static int screen_SI(term_screen *screen, const term_seq *seq) {
3040         /*
3041          * SI - shift-in
3042          * Map G0 into GL.
3043          */
3044
3045         screen->state.gl = &screen->g0;
3046
3047         return 0;
3048 }
3049
3050 static int screen_SM_ANSI(term_screen *screen, const term_seq *seq) {
3051         /*
3052          * SM_ANSI - set-mode-ansi
3053          *
3054          * TODO: implement
3055          */
3056
3057         unsigned int i;
3058
3059         for (i = 0; i < seq->n_args; ++i)
3060                 screen_mode_change(screen, seq->args[i], false, true);
3061
3062         return 0;
3063 }
3064
3065 static int screen_SM_DEC(term_screen *screen, const term_seq *seq) {
3066         /*
3067          * SM_DEC - set-mode-dec
3068          * This is the same as SM_ANSI but for DEC modes.
3069          */
3070
3071         unsigned int i;
3072
3073         for (i = 0; i < seq->n_args; ++i)
3074                 screen_mode_change(screen, seq->args[i], true, true);
3075
3076         return 0;
3077 }
3078
3079 static int screen_SO(term_screen *screen, const term_seq *seq) {
3080         /*
3081          * SO - shift-out
3082          * Map G1 into GL.
3083          */
3084
3085         screen->state.gl = &screen->g1;
3086
3087         return 0;
3088 }
3089
3090 static int screen_SPA(term_screen *screen, const term_seq *seq) {
3091         /*
3092          * SPA - start-of-protected-area
3093          *
3094          * TODO: What is this?
3095          */
3096
3097         return 0;
3098 }
3099
3100 static int screen_SS2(term_screen *screen, const term_seq *seq) {
3101         /*
3102          * SS2 - single-shift-2
3103          * Temporarily map G2 into GL for the next graphics character.
3104          */
3105
3106         screen->state.glt = &screen->g2;
3107
3108         return 0;
3109 }
3110
3111 static int screen_SS3(term_screen *screen, const term_seq *seq) {
3112         /*
3113          * SS3 - single-shift-3
3114          * Temporarily map G3 into GL for the next graphics character
3115          */
3116
3117         screen->state.glt = &screen->g3;
3118
3119         return 0;
3120 }
3121
3122 static int screen_ST(term_screen *screen, const term_seq *seq) {
3123         /*
3124          * ST - string-terminator
3125          * The string-terminator is usually part of control-sequences and
3126          * handled by the parser. In all other situations it is silently
3127          * ignored.
3128          */
3129
3130         return 0;
3131 }
3132
3133 static int screen_SU(term_screen *screen, const term_seq *seq) {
3134         /*
3135          * SU - scroll-up
3136          * This control function moves the user window up a specified number of
3137          * lines in page memory.
3138          * @args[0] is the number of lines to move the
3139          * user window down in page memory. New lines appear at the bottom of
3140          * the display. Old lines disappear at the top of the display. You
3141          * cannot pan past the bottom margin of the current page. 0 is treated
3142          * as 1.
3143          *
3144          * Defaults:
3145          *   args[0]: 1
3146          */
3147
3148         unsigned int num = 1;
3149
3150         if (seq->args[0] > 0)
3151                 num = seq->args[0];
3152
3153         term_page_scroll_up(screen->page, num, &screen->state.attr, screen->age, screen->history);
3154
3155         return 0;
3156 }
3157
3158 static int screen_SUB(term_screen *screen, const term_seq *seq) {
3159         /*
3160          * SUB - substitute
3161          * Cancel the current control-sequence and print a replacement
3162          * character. Our parser already handles this so all we have to do is
3163          * print the replacement character.
3164          */
3165
3166         static const term_seq rep = {
3167                 .type = TERM_SEQ_GRAPHIC,
3168                 .command = TERM_CMD_GRAPHIC,
3169                 .terminator = 0xfffd,
3170         };
3171
3172         return screen_GRAPHIC(screen, &rep);
3173 }
3174
3175 static int screen_TBC(term_screen *screen, const term_seq *seq) {
3176         /*
3177          * TBC - tab-clear
3178          * This clears tab-stops. If @args[0] is 0, the tab-stop at the current
3179          * cursor position is cleared. If it is 3, all tab stops are cleared.
3180          *
3181          * Defaults:
3182          *   args[0]: 0
3183          */
3184
3185         unsigned int mode = 0, pos;
3186
3187         if (seq->args[0] > 0)
3188                 mode = seq->args[0];
3189
3190         switch (mode) {
3191         case 0:
3192                 pos = screen->state.cursor_x;
3193                 if (screen->page->width > 0)
3194                         screen->tabs[pos / 8] &= ~(1U << (pos % 8));
3195                 break;
3196         case 3:
3197                 if (screen->page->width > 0)
3198                         memset(screen->tabs, 0, (screen->page->width + 7) / 8);
3199                 break;
3200         }
3201
3202         return 0;
3203 }
3204
3205 static int screen_VPA(term_screen *screen, const term_seq *seq) {
3206         /*