]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-terminal/term-screen.c
update TODO
[thirdparty/systemd.git] / src / libsystemd-terminal / term-screen.c
CommitLineData
e432f9e8
DH
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>
f8958c34 50#include <xkbcommon/xkbcommon-keysyms.h>
e432f9e8
DH
51#include "macro.h"
52#include "term-internal.h"
53#include "util.h"
54
55int 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;
e432f9e8
DH
73 screen->g0 = &term_unicode_lower;
74 screen->g1 = &term_unicode_upper;
75 screen->g2 = &term_unicode_lower;
76 screen->g3 = &term_unicode_upper;
3ae49a8f
DH
77 screen->state.gl = &screen->g0;
78 screen->state.gr = &screen->g1;
79 screen->saved = screen->state;
c7afe4f3 80 screen->saved_alt = screen->saved;
e432f9e8
DH
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
106term_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
116term_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
172static 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
186static 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
199static 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
206static 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
213static 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
220static inline void screen_age_cursor(term_screen *screen) {
221 term_cell *cell;
222
3ae49a8f 223 cell = term_page_get_cell(screen->page, screen->state.cursor_x, screen->state.cursor_y);
e432f9e8
DH
224 if (cell)
225 cell->age = screen->age;
226}
227
228static void screen_cursor_clear_wrap(term_screen *screen) {
229 screen->flags &= ~TERM_FLAG_PENDING_WRAP;
230}
231
232static 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
3ae49a8f 236 if (x == screen->state.cursor_x && y == screen->state.cursor_y)
e432f9e8
DH
237 return;
238
239 if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
240 screen_age_cursor(screen);
241
3ae49a8f
DH
242 screen->state.cursor_x = x;
243 screen->state.cursor_y = y;
e432f9e8
DH
244
245 if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
246 screen_age_cursor(screen);
247}
248
249static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned int y) {
3ae49a8f 250 if (screen->state.origin_mode) {
e432f9e8
DH
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
264static void screen_cursor_left(term_screen *screen, unsigned int num) {
3ae49a8f
DH
265 if (num > screen->state.cursor_x)
266 num = screen->state.cursor_x;
e432f9e8 267
3ae49a8f 268 screen_cursor_set(screen, screen->state.cursor_x - num, screen->state.cursor_y);
e432f9e8
DH
269}
270
271static void screen_cursor_left_tab(term_screen *screen, unsigned int num) {
272 unsigned int i;
273
3ae49a8f 274 i = screen->state.cursor_x;
e432f9e8
DH
275 while (i > 0 && num > 0) {
276 if (screen_tab_is_set(screen, --i))
277 --num;
278 }
279
3ae49a8f 280 screen_cursor_set(screen, i, screen->state.cursor_y);
e432f9e8
DH
281}
282
283static void screen_cursor_right(term_screen *screen, unsigned int num) {
284 if (num > screen->page->width)
285 num = screen->page->width;
286
3ae49a8f 287 screen_cursor_set(screen, screen->state.cursor_x + num, screen->state.cursor_y);
e432f9e8
DH
288}
289
290static void screen_cursor_right_tab(term_screen *screen, unsigned int num) {
291 unsigned int i;
292
3ae49a8f 293 i = screen->state.cursor_x;
e432f9e8
DH
294 while (i + 1 < screen->page->width && num > 0) {
295 if (screen_tab_is_set(screen, ++i))
296 --num;
297 }
298
3ae49a8f 299 screen_cursor_set(screen, i, screen->state.cursor_y);
e432f9e8
DH
300}
301
302static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) {
303 unsigned int max;
304
3ae49a8f
DH
305 if (screen->state.cursor_y < screen->page->scroll_idx) {
306 if (num > screen->state.cursor_y)
307 num = screen->state.cursor_y;
e432f9e8 308
3ae49a8f 309 screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
e432f9e8 310 } else {
3ae49a8f 311 max = screen->state.cursor_y - screen->page->scroll_idx;
e432f9e8
DH
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)
3ae49a8f 320 term_page_scroll_down(screen->page, num - max, &screen->state.attr, screen->age, NULL);
e432f9e8 321
3ae49a8f 322 screen->state.cursor_y = screen->page->scroll_idx;
e432f9e8
DH
323
324 if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
325 screen_age_cursor(screen);
326 } else {
3ae49a8f 327 screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
e432f9e8
DH
328 }
329 }
330}
331
332static void screen_cursor_down(term_screen *screen, unsigned int num, bool scroll) {
333 unsigned int max;
334
3ae49a8f 335 if (screen->state.cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) {
e432f9e8
DH
336 if (num > screen->page->height)
337 num = screen->page->height;
338
3ae49a8f 339 screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
e432f9e8 340 } else {
3ae49a8f 341 max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->state.cursor_y;
e432f9e8
DH
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)
3ae49a8f 350 term_page_scroll_up(screen->page, num - max, &screen->state.attr, screen->age, screen->history);
e432f9e8 351
3ae49a8f 352 screen->state.cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1;
e432f9e8
DH
353
354 if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
355 screen_age_cursor(screen);
356 } else {
3ae49a8f 357 screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y + num);
e432f9e8
DH
358 }
359 }
360}
361
3ae49a8f
DH
362static void screen_save_state(term_screen *screen, term_state *where) {
363 *where = screen->state;
364}
365
366static 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
c7afe4f3
TG
371static 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
376static 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
e432f9e8
DH
388static 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
7ee738ec
TG
395static void screen_mode_change_ansi(term_screen *screen, unsigned mode, bool set) {
396 switch (mode) {
397 case 20:
398 /*
399 * LNM: line-feed/new-line mode
400 * TODO
401 */
402 set_reset(screen, TERM_FLAG_NEWLINE_MODE, set);
403
404 break;
9536cf93
TG
405 default:
406 log_debug("terminal: failed to %s unknown ANSI mode %u", set ? "set" : "unset", mode);
7ee738ec
TG
407 }
408}
409
410static void screen_mode_change_dec(term_screen *screen, unsigned int mode, bool set) {
e432f9e8
DH
411 switch (mode) {
412 case 1:
7ee738ec
TG
413 /*
414 * DECCKM: cursor-keys
415 * TODO
416 */
417 set_reset(screen, TERM_FLAG_CURSOR_KEYS, set);
e432f9e8
DH
418
419 break;
420 case 6:
7ee738ec
TG
421 /*
422 * DECOM: origin-mode
423 * TODO
424 */
425 screen->state.origin_mode = set;
e432f9e8
DH
426
427 break;
428 case 7:
7ee738ec
TG
429 /*
430 * DECAWN: auto-wrap mode
431 * TODO
432 */
433 screen->state.auto_wrap = set;
e432f9e8
DH
434
435 break;
436 case 25:
7ee738ec
TG
437 /*
438 * DECTCEM: text-cursor-enable
439 * TODO
440 */
441 set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set);
442 screen_age_cursor(screen);
e432f9e8 443
c7afe4f3
TG
444 break;
445 case 47:
7ee738ec
TG
446 /*
447 * XTERM-ASB: alternate-screen-buffer
448 * This enables/disables the alternate screen-buffer.
449 * It effectively saves the current page content and
450 * allows you to restore it when changing to the
451 * original screen-buffer again.
452 */
453 screen_change_alt(screen, set);
c7afe4f3
TG
454
455 break;
456 case 1047:
7ee738ec
TG
457 /*
458 * XTERM-ASBPE: alternate-screen-buffer-post-erase
459 * This is the same as XTERM-ASB but erases the
460 * alternate screen-buffer before switching back to the
461 * original buffer. Use it to discard any data on the
462 * alternate screen buffer when done.
463 */
464 if (!set)
465 screen_reset_page(screen, screen->page_alt);
466
467 screen_change_alt(screen, set);
c7afe4f3
TG
468
469 break;
470 case 1048:
7ee738ec
TG
471 /*
472 * XTERM-ASBCS: alternate-screen-buffer-cursor-state
473 * This has the same effect as DECSC/DECRC, but uses a
474 * separate state buffer. It is usually used in
475 * combination with alternate screen buffers to provide
476 * stacked state storage.
477 */
478 if (set)
479 screen_save_state(screen, &screen->saved_alt);
480 else
481 screen_restore_state(screen, &screen->saved_alt);
c7afe4f3
TG
482
483 break;
484 case 1049:
7ee738ec
TG
485 /*
486 * XTERM-ASBX: alternate-screen-buffer-extended
487 * This combines XTERM-ASBPE and XTERM-ASBCS somewhat.
488 * When enabling, state is saved, alternate screen
489 * buffer is activated and cleared.
490 * When disabled, alternate screen buffer is cleared,
491 * then normal screen buffer is enabled and state is
492 * restored.
493 */
494 if (set)
495 screen_save_state(screen, &screen->saved_alt);
496
497 screen_reset_page(screen, screen->page_alt);
498 screen_change_alt(screen, set);
499
500 if (!set)
501 screen_restore_state(screen, &screen->saved_alt);
c7afe4f3 502
e432f9e8 503 break;
9536cf93
TG
504 default:
505 log_debug("terminal: failed to %s unknown DEC mode %u", set ? "set" : "unset", mode);
e432f9e8
DH
506 }
507}
508
509/* map a character according to current GL and GR maps */
510static uint32_t screen_map(term_screen *screen, uint32_t val) {
511 uint32_t nval = -1U;
512
513 /* 32 and 127 always map to identity. 160 and 255 map to identity iff a
514 * 96 character set is loaded into GR. Values above 255 always map to
515 * identity. */
516 switch (val) {
517 case 33 ... 126:
3ae49a8f
DH
518 if (screen->state.glt) {
519 nval = (**screen->state.glt)[val - 32];
520 screen->state.glt = NULL;
e432f9e8 521 } else {
3ae49a8f 522 nval = (**screen->state.gl)[val - 32];
e432f9e8
DH
523 }
524 break;
525 case 160 ... 255:
3ae49a8f
DH
526 if (screen->state.grt) {
527 nval = (**screen->state.grt)[val - 160];
528 screen->state.grt = NULL;
e432f9e8 529 } else {
3ae49a8f 530 nval = (**screen->state.gr)[val - 160];
e432f9e8
DH
531 }
532 break;
533 }
534
535 return (nval == -1U) ? val : nval;
536}
537
538/*
539 * Command Handlers
06b643e7 540 * This is the unofficial documentation of all the TERM_CMD_* definitions. Each
e432f9e8
DH
541 * handled command has a separate function with an extensive comment on the
542 * semantics of the command.
543 * Note that many semantics are unknown and need to be verified. This is mostly
544 * about error-handling, though. Applications rarely rely on those features.
545 */
546
547static int screen_DA1(term_screen *screen, const term_seq *seq);
548static int screen_LF(term_screen *screen, const term_seq *seq);
549
550static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) {
551 term_char_t ch = TERM_CHAR_NULL;
552 uint32_t c;
553
3ae49a8f 554 if (screen->state.cursor_x + 1 == screen->page->width
e432f9e8 555 && screen->flags & TERM_FLAG_PENDING_WRAP
3ae49a8f 556 && screen->state.auto_wrap) {
e432f9e8 557 screen_cursor_down(screen, 1, true);
3ae49a8f 558 screen_cursor_set(screen, 0, screen->state.cursor_y);
e432f9e8
DH
559 }
560
561 screen_cursor_clear_wrap(screen);
562
563 c = screen_map(screen, seq->terminator);
564 ch = term_char_merge(ch, screen_map(screen, c));
3ae49a8f 565 term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false);
e432f9e8 566
3ae49a8f 567 if (screen->state.cursor_x + 1 == screen->page->width)
e432f9e8
DH
568 screen->flags |= TERM_FLAG_PENDING_WRAP;
569 else
570 screen_cursor_right(screen, 1);
571
572 return 0;
573}
574
575static int screen_BEL(term_screen *screen, const term_seq *seq) {
576 /*
577 * BEL - sound bell tone
578 * This command should trigger an acoustic bell. Usually, this is
579 * forwarded directly to the pcspkr. However, bells have become quite
580 * uncommon and annoying, so we're not implementing them here. Instead,
581 * it's one of the commands we forward to the caller.
582 */
583
584 return screen_forward(screen, TERM_CMD_BEL, seq);
585}
586
587static int screen_BS(term_screen *screen, const term_seq *seq) {
588 /*
589 * BS - backspace
590 * Move cursor one cell to the left. If already at the left margin,
591 * nothing happens.
592 */
593
594 screen_cursor_clear_wrap(screen);
595 screen_cursor_left(screen, 1);
596 return 0;
597}
598
599static int screen_CBT(term_screen *screen, const term_seq *seq) {
600 /*
601 * CBT - cursor-backward-tabulation
602 * Move the cursor @args[0] tabs backwards (to the left). The
603 * current cursor cell, in case it's a tab, is not counted.
604 * Furthermore, the cursor cannot be moved beyond position 0 and
605 * it will stop there.
606 *
607 * Defaults:
608 * args[0]: 1
609 */
610
611 unsigned int num = 1;
612
613 if (seq->args[0] > 0)
614 num = seq->args[0];
615
616 screen_cursor_clear_wrap(screen);
617 screen_cursor_left_tab(screen, num);
618
619 return 0;
620}
621
622static int screen_CHA(term_screen *screen, const term_seq *seq) {
623 /*
624 * CHA - cursor-horizontal-absolute
625 * Move the cursor to position @args[0] in the current line. The
626 * cursor cannot be moved beyond the rightmost cell and will stop
627 * there.
628 *
629 * Defaults:
630 * args[0]: 1
631 */
632
633 unsigned int pos = 1;
634
635 if (seq->args[0] > 0)
636 pos = seq->args[0];
637
638 screen_cursor_clear_wrap(screen);
3ae49a8f 639 screen_cursor_set(screen, pos - 1, screen->state.cursor_y);
e432f9e8
DH
640
641 return 0;
642}
643
644static int screen_CHT(term_screen *screen, const term_seq *seq) {
645 /*
646 * CHT - cursor-horizontal-forward-tabulation
647 * Move the cursor @args[0] tabs forward (to the right). The
648 * current cursor cell, in case it's a tab, is not counted.
649 * Furthermore, the cursor cannot be moved beyond the rightmost cell
650 * and will stop there.
651 *
652 * Defaults:
653 * args[0]: 1
654 */
655
656 unsigned int num = 1;
657
658 if (seq->args[0] > 0)
659 num = seq->args[0];
660
661 screen_cursor_clear_wrap(screen);
662 screen_cursor_right_tab(screen, num);
663
664 return 0;
665}
666
667static int screen_CNL(term_screen *screen, const term_seq *seq) {
668 /*
669 * CNL - cursor-next-line
670 * Move the cursor @args[0] lines down.
671 *
672 * TODO: Does this stop at the bottom or cause a scroll-up?
673 *
674 * Defaults:
675 * args[0]: 1
676 */
677
678 unsigned int num = 1;
679
680 if (seq->args[0] > 0)
681 num = seq->args[0];
682
683 screen_cursor_clear_wrap(screen);
684 screen_cursor_down(screen, num, false);
685
686 return 0;
687}
688
689static int screen_CPL(term_screen *screen, const term_seq *seq) {
690 /*
691 * CPL - cursor-preceding-line
692 * Move the cursor @args[0] lines up.
693 *
694 * TODO: Does this stop at the top or cause a scroll-up?
695 *
696 * Defaults:
697 * args[0]: 1
698 */
699
700 unsigned int num = 1;
701
702 if (seq->args[0] > 0)
703 num = seq->args[0];
704
705 screen_cursor_clear_wrap(screen);
706 screen_cursor_up(screen, num, false);
707
708 return 0;
709}
710
711static int screen_CR(term_screen *screen, const term_seq *seq) {
712 /*
713 * CR - carriage-return
714 * Move the cursor to the left margin on the current line.
715 */
716
717 screen_cursor_clear_wrap(screen);
3ae49a8f 718 screen_cursor_set(screen, 0, screen->state.cursor_y);
e432f9e8
DH
719
720 return 0;
721}
722
723static int screen_CUB(term_screen *screen, const term_seq *seq) {
724 /*
725 * CUB - cursor-backward
726 * Move the cursor @args[0] positions to the left. The cursor stops
727 * at the left-most position.
728 *
729 * Defaults:
730 * args[0]: 1
731 */
732
733 unsigned int num = 1;
734
735 if (seq->args[0] > 0)
736 num = seq->args[0];
737
738 screen_cursor_clear_wrap(screen);
739 screen_cursor_left(screen, num);
740
741 return 0;
742}
743
744static int screen_CUD(term_screen *screen, const term_seq *seq) {
745 /*
746 * CUD - cursor-down
747 * Move the cursor @args[0] positions down. The cursor stops at the
748 * bottom margin. If it was already moved further, it stops at the
749 * bottom line.
750 *
751 * Defaults:
752 * args[0]: 1
753 */
754
755 unsigned int num = 1;
756
757 if (seq->args[0] > 0)
758 num = seq->args[0];
759
760 screen_cursor_clear_wrap(screen);
761 screen_cursor_down(screen, num, false);
762
763 return 0;
764}
765
766static int screen_CUF(term_screen *screen, const term_seq *seq) {
767 /*
768 * CUF -cursor-forward
769 * Move the cursor @args[0] positions to the right. The cursor stops
770 * at the right-most position.
771 *
772 * Defaults:
773 * args[0]: 1
774 */
775
776 unsigned int num = 1;
777
778 if (seq->args[0] > 0)
779 num = seq->args[0];
780
781 screen_cursor_clear_wrap(screen);
782 screen_cursor_right(screen, num);
783
784 return 0;
785}
786
787static int screen_CUP(term_screen *screen, const term_seq *seq) {
788 /*
789 * CUP - cursor-position
790 * Moves the cursor to position @args[1] x @args[0]. If either is 0, it
791 * is treated as 1. The positions are subject to the origin-mode and
792 * clamped to the addressable with/height.
793 *
794 * Defaults:
795 * args[0]: 1
796 * args[1]: 1
797 */
798
799 unsigned int x = 1, y = 1;
800
801 if (seq->args[0] > 0)
802 y = seq->args[0];
803 if (seq->args[1] > 0)
804 x = seq->args[1];
805
806 screen_cursor_clear_wrap(screen);
807 screen_cursor_set_rel(screen, x - 1, y - 1);
808
809 return 0;
810}
811
812static int screen_CUU(term_screen *screen, const term_seq *seq) {
813 /*
814 * CUU - cursor-up
815 * Move the cursor @args[0] positions up. The cursor stops at the
816 * top margin. If it was already moved further, it stops at the
817 * top line.
818 *
819 * Defaults:
820 * args[0]: 1
821 *
822 */
823
824 unsigned int num = 1;
825
826 if (seq->args[0] > 0)
827 num = seq->args[0];
828
829 screen_cursor_clear_wrap(screen);
830 screen_cursor_up(screen, num, false);
831
832 return 0;
833}
834
835static int screen_DA1(term_screen *screen, const term_seq *seq) {
836 /*
837 * DA1 - primary-device-attributes
838 * The primary DA asks for basic terminal features. We simply return
839 * a hard-coded list of features we implement.
840 * Note that the primary DA asks for supported features, not currently
841 * enabled features.
842 *
843 * The terminal's answer is:
844 * ^[ ? 64 ; ARGS c
845 * The first argument, 64, is fixed and denotes a VT420, the last
846 * DEC-term that extended this number.
847 * All following arguments denote supported features. Note
848 * that at most 15 features can be sent (max CSI args). It is safe to
849 * send more, but clients might not be able to parse them. This is a
850 * client's problem and we shouldn't care. There is no other way to
851 * send those feature lists, so we have to extend them beyond 15 in
852 * those cases.
853 *
854 * Known modes:
855 * 1: 132 column mode
856 * The 132 column mode is supported by the terminal.
857 * 2: printer port
858 * A priner-port is supported and can be addressed via
859 * control-codes.
860 * 3: ReGIS graphics
861 * Support for ReGIS graphics is available. The ReGIS routines
862 * provide the "remote graphics instruction set" and allow basic
863 * vector-rendering.
864 * 4: sixel
865 * Support of Sixel graphics is available. This provides access
866 * to the sixel bitmap routines.
867 * 6: selective erase
868 * The terminal supports DECSCA and related selective-erase
869 * functions. This allows to protect specific cells from being
870 * erased, if specified.
871 * 7: soft character set (DRCS)
872 * TODO: ?
873 * 8: user-defined keys (UDKs)
874 * TODO: ?
875 * 9: national-replacement character sets (NRCS)
876 * National-replacement character-sets are available.
877 * 12: Yugoslavian (SCS)
878 * TODO: ?
879 * 15: technical character set
880 * The DEC technical-character-set is available.
881 * 18: windowing capability
882 * TODO: ?
883 * 21: horizontal scrolling
884 * TODO: ?
885 * 22: ANSII color
886 * TODO: ?
887 * 23: Greek
888 * TODO: ?
889 * 24: Turkish
890 * TODO: ?
891 * 29: ANSI text locator
892 * TODO: ?
893 * 42: ISO Latin-2 character set
894 * TODO: ?
895 * 44: PCTerm
896 * TODO: ?
897 * 45: soft keymap
898 * TODO: ?
899 * 46: ASCII emulation
900 * TODO: ?
901 */
902
903 return SEQ_WRITE(screen, C0_CSI, C1_CSI, "?64;1;6;9;15c");
904}
905
906static int screen_DA2(term_screen *screen, const term_seq *seq) {
907 /*
908 * DA2 - secondary-device-attributes
909 * The secondary DA asks for the terminal-ID, firmware versions and
910 * other non-primary attributes. All these values are
911 * informational-only and should not be used by the host to detect
912 * terminal features.
913 *
914 * The terminal's response is:
915 * ^[ > 61 ; FIRMWARE ; KEYBOARD c
916 * whereas 65 is fixed for VT525 terminals, the last terminal-line that
917 * increased this number. FIRMWARE is the firmware
918 * version encoded as major/minor (20 == 2.0) and KEYBOARD is 0 for STD
919 * keyboard and 1 for PC keyboards.
920 *
921 * We replace the firmware-version with the systemd-version so clients
922 * can decode it again.
923 */
924
925 return SEQ_WRITE(screen, C0_CSI, C1_CSI, ">65;" PACKAGE_VERSION ";1c");
926}
927
928static int screen_DA3(term_screen *screen, const term_seq *seq) {
929 /*
930 * DA3 - tertiary-device-attributes
931 * The tertiary DA is used to query the terminal-ID.
932 *
933 * The terminal's response is:
934 * ^P ! | XX AA BB CC ^\
935 * whereas all four parameters are hexadecimal-encoded pairs. XX
936 * denotes the manufacturing site, AA BB CC is the terminal's ID.
937 */
938
939 /* we do not support tertiary DAs */
940 return 0;
941}
942
943static int screen_DC1(term_screen *screen, const term_seq *seq) {
944 /*
945 * DC1 - device-control-1 or XON
946 * This clears any previous XOFF and resumes terminal-transmission.
947 */
948
949 /* we do not support XON */
950 return 0;
951}
952
953static int screen_DC3(term_screen *screen, const term_seq *seq) {
954 /*
955 * DC3 - device-control-3 or XOFF
956 * Stops terminal transmission. No further characters are sent until
957 * an XON is received.
958 */
959
960 /* we do not support XOFF */
961 return 0;
962}
963
964static int screen_DCH(term_screen *screen, const term_seq *seq) {
965 /*
966 * DCH - delete-character
967 * This deletes @argv[0] characters at the current cursor position. As
968 * characters are deleted, the remaining characters between the cursor
969 * and right margin move to the left. Character attributes move with the
970 * characters. The terminal adds blank spaces with no visual character
971 * attributes at the right margin. DCH has no effect outside the
972 * scrolling margins.
973 *
974 * Defaults:
975 * args[0]: 1
976 */
977
978 unsigned int num = 1;
979
980 if (seq->args[0] > 0)
981 num = seq->args[0];
982
983 screen_cursor_clear_wrap(screen);
3ae49a8f 984 term_page_delete_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
e432f9e8
DH
985
986 return 0;
987}
988
989static int screen_DECALN(term_screen *screen, const term_seq *seq) {
990 /*
991 * DECALN - screen-alignment-pattern
992 *
993 * Probably not worth implementing.
994 */
995
996 return 0;
997}
998
999static int screen_DECANM(term_screen *screen, const term_seq *seq) {
1000 /*
1001 * DECANM - ansi-mode
1002 * Set the terminal into VT52 compatibility mode. Control sequences
1003 * overlap with regular sequences so we have to detect them early before
1004 * dispatching them.
1005 *
1006 * Probably not worth implementing.
1007 */
1008
1009 return 0;
1010}
1011
1012static int screen_DECBI(term_screen *screen, const term_seq *seq) {
1013 /*
1014 * DECBI - back-index
1015 * This control function moves the cursor backward one column. If the
1016 * cursor is at the left margin, then all screen data within the margin
1017 * moves one column to the right. The column that shifted past the right
1018 * margin is lost.
1019 * DECBI adds a new column at the left margin with no visual attributes.
1020 * DECBI does not affect the margins. If the cursor is beyond the
1021 * left-margin at the left border, then the terminal ignores DECBI.
1022 *
1023 * Probably not worth implementing.
1024 */
1025
1026 return 0;
1027}
1028
1029static int screen_DECCARA(term_screen *screen, const term_seq *seq) {
1030 /*
1031 * DECCARA - change-attributes-in-rectangular-area
1032 *
1033 * Probably not worth implementing.
1034 */
1035
1036 return 0;
1037}
1038
1039static int screen_DECCRA(term_screen *screen, const term_seq *seq) {
1040 /*
1041 * DECCRA - copy-rectangular-area
1042 *
1043 * Probably not worth implementing.
1044 */
1045
1046 return 0;
1047}
1048
1049static int screen_DECDC(term_screen *screen, const term_seq *seq) {
1050 /*
1051 * DECDC - delete-column
1052 *
1053 * Probably not worth implementing.
1054 */
1055
1056 return 0;
1057}
1058
1059static int screen_DECDHL_BH(term_screen *screen, const term_seq *seq) {
1060 /*
1061 * DECDHL_BH - double-width-double-height-line: bottom half
1062 *
1063 * Probably not worth implementing.
1064 */
1065
1066 return 0;
1067}
1068
1069static int screen_DECDHL_TH(term_screen *screen, const term_seq *seq) {
1070 /*
1071 * DECDHL_TH - double-width-double-height-line: top half
1072 *
1073 * Probably not worth implementing.
1074 */
1075
1076 return 0;
1077}
1078
1079static int screen_DECDWL(term_screen *screen, const term_seq *seq) {
1080 /*
1081 * DECDWL - double-width-single-height-line
1082 *
1083 * Probably not worth implementing.
1084 */
1085
1086 return 0;
1087}
1088
1089static int screen_DECEFR(term_screen *screen, const term_seq *seq) {
1090 /*
1091 * DECEFR - enable-filter-rectangle
1092 * Defines the coordinates of a filter rectangle (top, left, bottom,
1093 * right as @args[0] to @args[3]) and activates it.
1094 * Anytime the locator is detected outside of the filter rectangle, an
1095 * outside rectangle event is generated and the rectangle is disabled.
1096 * Filter rectangles are always treated as "one-shot" events. Any
1097 * parameters that are omitted default to the current locator position.
1098 * If all parameters are omitted, any locator motion will be reported.
1099 * DECELR always cancels any prevous rectangle definition.
1100 *
1101 * The locator is usually associated with the mouse-cursor, but based
1102 * on cells instead of pixels. See DECELR how to initialize and enable
1103 * it. DECELR can also enable pixel-mode instead of cell-mode.
1104 *
1105 * TODO: implement
1106 */
1107
1108 return 0;
1109}
1110
1111static int screen_DECELF(term_screen *screen, const term_seq *seq) {
1112 /*
1113 * DECELF - enable-local-functions
1114 *
1115 * Probably not worth implementing.
1116 */
1117
1118 return 0;
1119}
1120
1121static int screen_DECELR(term_screen *screen, const term_seq *seq) {
1122 /*
1123 * DECELR - enable-locator-reporting
1124 * This changes the locator-reporting mode. @args[0] specifies the mode
06b643e7 1125 * to set, 0 disables locator-reporting, 1 enables it continuously, 2
e432f9e8
DH
1126 * enables it for a single report. @args[1] specifies the
1127 * precision-mode. 0 and 2 set the reporting to cell-precision, 1 sets
1128 * pixel-precision.
1129 *
1130 * Defaults:
1131 * args[0]: 0
1132 * args[1]: 0
1133 *
1134 * TODO: implement
1135 */
1136
1137 return 0;
1138}
1139
1140static int screen_DECERA(term_screen *screen, const term_seq *seq) {
1141 /*
1142 * DECERA - erase-rectangular-area
1143 *
1144 * Probably not worth implementing.
1145 */
1146
1147 return 0;
1148}
1149
1150static int screen_DECFI(term_screen *screen, const term_seq *seq) {
1151 /*
1152 * DECFI - forward-index
1153 * This control function moves the cursor forward one column. If the
1154 * cursor is at the right margin, then all screen data within the
1155 * margins moves one column to the left. The column shifted past the
1156 * left margin is lost.
1157 * DECFI adds a new column at the right margin, with no visual
1158 * attributes. DECFI does not affect margins. If the cursor is beyond
1159 * the right margin at the border of the page when the terminal
1160 * receives DECFI, then the terminal ignores DECFI.
1161 *
1162 * Probably not worth implementing.
1163 */
1164
1165 return 0;
1166}
1167
1168static int screen_DECFRA(term_screen *screen, const term_seq *seq) {
1169 /*
1170 * DECFRA - fill-rectangular-area
1171 *
1172 * Probably not worth implementing.
1173 */
1174
1175 return 0;
1176}
1177
1178static int screen_DECIC(term_screen *screen, const term_seq *seq) {
1179 /*
1180 * DECIC - insert-column
1181 *
1182 * Probably not worth implementing.
1183 */
1184
1185 return 0;
1186}
1187
1188static int screen_DECID(term_screen *screen, const term_seq *seq) {
1189 /*
1190 * DECID - return-terminal-id
1191 * This is an obsolete form of TERM_CMD_DA1.
1192 */
1193
1194 return screen_DA1(screen, seq);
1195}
1196
1197static int screen_DECINVM(term_screen *screen, const term_seq *seq) {
1198 /*
1199 * DECINVM - invoke-macro
1200 *
1201 * Probably not worth implementing.
1202 */
1203
1204 return 0;
1205}
1206
1207static int screen_DECKBD(term_screen *screen, const term_seq *seq) {
1208 /*
1209 * DECKBD - keyboard-language-selection
1210 *
1211 * Probably not worth implementing.
1212 */
1213
1214 return 0;
1215}
1216
1217static int screen_DECKPAM(term_screen *screen, const term_seq *seq) {
1218 /*
1219 * DECKPAM - keypad-application-mode
1220 * Enables the keypad-application mode. If enabled, the keypad sends
1221 * special characters instead of the printed characters. This way,
1222 * applications can detect whether a numeric key was pressed on the
1223 * top-row or on the keypad.
1224 * Default is keypad-numeric-mode.
1225 */
1226
1227 screen->flags |= TERM_FLAG_KEYPAD_MODE;
1228
1229 return 0;
1230}
1231
1232static int screen_DECKPNM(term_screen *screen, const term_seq *seq) {
1233 /*
1234 * DECKPNM - keypad-numeric-mode
1235 * This disables the keypad-application-mode (DECKPAM) and returns to
1236 * the keypad-numeric-mode. Keypresses on the keypad generate the same
1237 * sequences as corresponding keypresses on the main keyboard.
1238 * Default is keypad-numeric-mode.
1239 */
1240
1241 screen->flags &= ~TERM_FLAG_KEYPAD_MODE;
1242
1243 return 0;
1244}
1245
1246static int screen_DECLFKC(term_screen *screen, const term_seq *seq) {
1247 /*
1248 * DECLFKC - local-function-key-control
1249 *
1250 * Probably not worth implementing.
1251 */
1252
1253 return 0;
1254}
1255
1256static int screen_DECLL(term_screen *screen, const term_seq *seq) {
1257 /*
1258 * DECLL - load-leds
1259 *
1260 * Probably not worth implementing.
1261 */
1262
1263 return 0;
1264}
1265
1266static int screen_DECLTOD(term_screen *screen, const term_seq *seq) {
1267 /*
1268 * DECLTOD - load-time-of-day
1269 *
1270 * Probably not worth implementing.
1271 */
1272
1273 return 0;
1274}
1275
1276static int screen_DECPCTERM(term_screen *screen, const term_seq *seq) {
1277 /*
1278 * DECPCTERM - pcterm-mode
1279 * This enters/exits the PCTerm mode. Default mode is VT-mode. It can
1280 * also select parameters for scancode/keycode mappings in SCO mode.
1281 *
1282 * Definitely not worth implementing. Lets kill PCTerm/SCO modes!
1283 */
1284
1285 return 0;
1286}
1287
1288static int screen_DECPKA(term_screen *screen, const term_seq *seq) {
1289 /*
1290 * DECPKA - program-key-action
1291 *
1292 * Probably not worth implementing.
1293 */
1294
1295 return 0;
1296}
1297
1298static int screen_DECPKFMR(term_screen *screen, const term_seq *seq) {
1299 /*
1300 * DECPKFMR - program-key-free-memory-report
1301 *
1302 * Probably not worth implementing.
1303 */
1304
1305 return 0;
1306}
1307
1308static int screen_DECRARA(term_screen *screen, const term_seq *seq) {
1309 /*
1310 * DECRARA - reverse-attributes-in-rectangular-area
1311 *
1312 * Probably not worth implementing.
1313 */
1314
1315 return 0;
1316}
1317
1318static int screen_DECRC(term_screen *screen, const term_seq *seq) {
1319 /*
1320 * DECRC - restore-cursor
1321 * Restores the terminal to the state saved by the save cursor (DECSC)
1322 * function. This includes more than just the cursor-position.
1323 *
1324 * If nothing was saved by DECSC, then DECRC performs the following
1325 * actions:
1326 * * Moves the cursor to the home position (upper left of screen).
1327 * * Resets origin mode (DECOM).
1328 * * Turns all character attributes off (normal setting).
1329 * * Maps the ASCII character set into GL, and the DEC Supplemental
1330 * Graphic set into GR.
1331 *
1332 * The terminal maintains a separate DECSC buffer for the main display
1333 * and the status line. This feature lets you save a separate operating
1334 * state for the main display and the status line.
1335 */
1336
3ae49a8f 1337 screen_restore_state(screen, &screen->saved);
e432f9e8
DH
1338
1339 return 0;
1340}
1341
1342static int screen_DECREQTPARM(term_screen *screen, const term_seq *seq) {
1343 /*
1344 * DECREQTPARM - request-terminal-parameters
1345 * The sequence DECREPTPARM is sent by the terminal controller to notify
1346 * the host of the status of selected terminal parameters. The status
1347 * sequence may be sent when requested by the host or at the terminal's
1348 * discretion. DECREPTPARM is sent upon receipt of a DECREQTPARM.
1349 *
1350 * If @args[0] is 0, this marks a request and the terminal is allowed
1351 * to send DECREPTPARM messages without request. If it is 1, the same
1352 * applies but the terminal should no longer send DECREPTPARM
1353 * unrequested.
1354 * 2 and 3 mark a report, but 3 is only used if the terminal answers as
1355 * an explicit request with @args[0] == 1.
1356 *
1357 * The other arguments are ignored in requests, but have the following
1358 * meaning in responses:
1359 * args[1]: 1=no-parity-set 4=parity-set-and-odd 5=parity-set-and-even
1360 * args[2]: 1=8bits-per-char 2=7bits-per-char
1361 * args[3]: transmission-speed
1362 * args[4]: receive-speed
1363 * args[5]: 1=bit-rate-multiplier-is-16
1364 * args[6]: This value communicates the four switch values in block 5
1365 * of SETUP B, which are only visible to the user when an STP
1366 * option is installed. These bits may be assigned for an STP
1367 * device. The four bits are a decimal-encoded binary number.
1368 * Value between 0-15.
1369 *
1370 * The transmission/receive speeds have mappings for number => bits/s
1371 * which are quite weird. Examples are: 96->3600, 112->9600, 120->19200
1372 *
1373 * Defaults:
1374 * args[0]: 0
1375 */
1376
1377 if (seq->n_args < 1 || seq->args[0] == 0) {
1378 screen->flags &= ~TERM_FLAG_INHIBIT_TPARM;
1379 return SEQ_WRITE(screen, C0_CSI, C1_CSI, "2;1;1;120;120;1;0x");
1380 } else if (seq->args[0] == 1) {
1381 screen->flags |= TERM_FLAG_INHIBIT_TPARM;
1382 return SEQ_WRITE(screen, C0_CSI, C1_CSI, "3;1;1;120;120;1;0x");
1383 } else {
1384 return 0;
1385 }
1386}
1387
1388static int screen_DECRPKT(term_screen *screen, const term_seq *seq) {
1389 /*
1390 * DECRPKT - report-key-type
1391 * Response to DECRQKT, we can safely ignore it as we're the one sending
1392 * it to the host.
1393 */
1394
1395 return 0;
1396}
1397
1398static int screen_DECRQCRA(term_screen *screen, const term_seq *seq) {
1399 /*
1400 * DECRQCRA - request-checksum-of-rectangular-area
1401 *
1402 * Probably not worth implementing.
1403 */
1404
1405 return 0;
1406}
1407
1408static int screen_DECRQDE(term_screen *screen, const term_seq *seq) {
1409 /*
1410 * DECRQDE - request-display-extent
1411 *
1412 * Probably not worth implementing.
1413 */
1414
1415 return 0;
1416}
1417
1418static int screen_DECRQKT(term_screen *screen, const term_seq *seq) {
1419 /*
1420 * DECRQKT - request-key-type
1421 *
1422 * Probably not worth implementing.
1423 */
1424
1425 return 0;
1426}
1427
1428static int screen_DECRQLP(term_screen *screen, const term_seq *seq) {
1429 /*
1430 * DECRQLP - request-locator-position
1431 * See DECELR for locator-information.
1432 *
1433 * TODO: document and implement
1434 */
1435
1436 return 0;
1437}
1438
1439static int screen_DECRQM_ANSI(term_screen *screen, const term_seq *seq) {
1440 /*
1441 * DECRQM_ANSI - request-mode-ansi
1442 * The host sends this control function to find out if a particular mode
1443 * is set or reset. The terminal responds with a report mode function.
1444 * @args[0] contains the mode to query.
1445 *
1446 * Response is DECRPM with the first argument set to the mode that was
1447 * queried, second argument is 0 if mode is invalid, 1 if mode is set,
1448 * 2 if mode is not set (reset), 3 if mode is permanently set and 4 if
1449 * mode is permanently not set (reset):
1450 * ANSI: ^[ MODE ; VALUE $ y
1451 * DEC: ^[ ? MODE ; VALUE $ y
1452 *
1453 * TODO: implement
1454 */
1455
1456 return 0;
1457}
1458
1459static int screen_DECRQM_DEC(term_screen *screen, const term_seq *seq) {
1460 /*
1461 * DECRQM_DEC - request-mode-dec
1462 * Same as DECRQM_ANSI but for DEC modes.
1463 *
1464 * TODO: implement
1465 */
1466
1467 return 0;
1468}
1469
1470static int screen_DECRQPKFM(term_screen *screen, const term_seq *seq) {
1471 /*
1472 * DECRQPKFM - request-program-key-free-memory
1473 *
1474 * Probably not worth implementing.
1475 */
1476
1477 return 0;
1478}
1479
1480static int screen_DECRQPSR(term_screen *screen, const term_seq *seq) {
1481 /*
1482 * DECRQPSR - request-presentation-state-report
1483 *
1484 * Probably not worth implementing.
1485 */
1486
1487 return 0;
1488}
1489
1490static int screen_DECRQTSR(term_screen *screen, const term_seq *seq) {
1491 /*
1492 * DECRQTSR - request-terminal-state-report
1493 *
1494 * Probably not worth implementing.
1495 */
1496
1497 return 0;
1498}
1499
1500static int screen_DECRQUPSS(term_screen *screen, const term_seq *seq) {
1501 /*
1502 * DECRQUPSS - request-user-preferred-supplemental-set
1503 *
1504 * Probably not worth implementing.
1505 */
1506
1507 return 0;
1508}
1509
1510static int screen_DECSACE(term_screen *screen, const term_seq *seq) {
1511 /*
1512 * DECSACE - select-attribute-change-extent
1513 *
1514 * Probably not worth implementing.
1515 */
1516
1517 return 0;
1518}
1519
1520static int screen_DECSASD(term_screen *screen, const term_seq *seq) {
1521 /*
1522 * DECSASD - select-active-status-display
1523 *
1524 * Probably not worth implementing.
1525 */
1526
1527 return 0;
1528}
1529
1530static int screen_DECSC(term_screen *screen, const term_seq *seq) {
1531 /*
1532 * DECSC - save-cursor
1533 * Save cursor and terminal state so it can be restored later on.
1534 * Saves the following items in the terminal's memory:
1535 * * Cursor position
1536 * * Character attributes set by the SGR command
1537 * * Character sets (G0, G1, G2, or G3) currently in GL and GR
1538 * * Wrap flag (autowrap or no autowrap)
1539 * * State of origin mode (DECOM)
1540 * * Selective erase attribute
1541 * * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent
1542 */
1543
3ae49a8f 1544 screen_save_state(screen, &screen->saved);
e432f9e8
DH
1545
1546 return 0;
1547}
1548
1549static int screen_DECSCA(term_screen *screen, const term_seq *seq) {
1550 /*
1551 * DECSCA - select-character-protection-attribute
1552 * Defines the characters that come after it as erasable or not erasable
1553 * from the screen. The selective erase control functions (DECSED and
1554 * DECSEL) can only erase characters defined as erasable.
1555 *
1556 * @args[0] specifies the new mode. 0 and 2 mark any following character
1557 * as erasable, 1 marks it as not erasable.
1558 *
1559 * Defaults:
1560 * args[0]: 0
1561 */
1562
1563 unsigned int mode = 0;
1564
1565 if (seq->args[0] > 0)
1566 mode = seq->args[0];
1567
1568 switch (mode) {
1569 case 0:
1570 case 2:
3ae49a8f 1571 screen->state.attr.protect = 0;
e432f9e8
DH
1572 break;
1573 case 1:
3ae49a8f 1574 screen->state.attr.protect = 1;
e432f9e8
DH
1575 break;
1576 }
1577
1578 return 0;
1579}
1580
1581static int screen_DECSCL(term_screen *screen, const term_seq *seq) {
1582 /*
1583 * DECSCL - select-conformance-level
1584 * Select the terminal's operating level. The factory default is
1585 * level 4 (VT Level 4 mode, 7-bit controls).
1586 * When you change the conformance level, the terminal performs a hard
1587 * reset (RIS).
1588 *
1589 * @args[0] defines the conformance-level, valid values are:
1590 * 61: Level 1 (VT100)
1591 * 62: Level 2 (VT200)
1592 * 63: Level 3 (VT300)
1593 * 64: Level 4 (VT400)
1594 * @args[1] defines the 8bit-mode, valid values are:
1595 * 0: 8-bit controls
1596 * 1: 7-bit controls
1597 * 2: 8-bit controls (same as 0)
1598 *
1599 * If @args[0] is 61, then @args[1] is ignored and 7bit controls are
1600 * enforced.
1601 *
1602 * Defaults:
1603 * args[0]: 64
1604 * args[1]: 0
1605 */
1606
1607 unsigned int level = 64, bit = 0;
1608
1609 if (seq->n_args > 0) {
1610 level = seq->args[0];
1611 if (seq->n_args > 1)
1612 bit = seq->args[1];
1613 }
1614
1615 term_screen_hard_reset(screen);
1616
1617 switch (level) {
1618 case 61:
1619 screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT100;
1620 screen->flags |= TERM_FLAG_7BIT_MODE;
1621 break;
1622 case 62 ... 69:
1623 screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
1624 if (bit == 1)
1625 screen->flags |= TERM_FLAG_7BIT_MODE;
1626 else
1627 screen->flags &= ~TERM_FLAG_7BIT_MODE;
1628 break;
1629 }
1630
1631 return 0;
1632}
1633
1634static int screen_DECSCP(term_screen *screen, const term_seq *seq) {
1635 /*
1636 * DECSCP - select-communication-port
1637 *
1638 * Probably not worth implementing.
1639 */
1640
1641 return 0;
1642}
1643
1644static int screen_DECSCPP(term_screen *screen, const term_seq *seq) {
1645 /*
1646 * DECSCPP - select-columns-per-page
1647 * Select columns per page. The number of rows is unaffected by this.
1648 * @args[0] selectes the number of columns (width), DEC only defines 80
1649 * and 132, but we allow any integer here. 0 is equivalent to 80.
1650 * Page content is *not* cleared and the cursor is left untouched.
1651 * However, if the page is reduced in width and the cursor would be
1652 * outside the visible region, it's set to the right border. Newly added
1653 * cells are cleared. No data is retained outside the visible region.
1654 *
1655 * Defaults:
1656 * args[0]: 0
1657 *
1658 * TODO: implement
1659 */
1660
1661 return 0;
1662}
1663
1664static int screen_DECSCS(term_screen *screen, const term_seq *seq) {
1665 /*
1666 * DECSCS - select-communication-speed
1667 *
1668 * Probably not worth implementing.
1669 */
1670
1671 return 0;
1672}
1673
1674static int screen_DECSCUSR(term_screen *screen, const term_seq *seq) {
1675 /*
1676 * DECSCUSR - set-cursor-style
1677 * This changes the style of the cursor. @args[0] can be one of:
1678 * 0, 1: blinking block
1679 * 2: steady block
1680 * 3: blinking underline
1681 * 4: steady underline
1682 * Changing this setting does _not_ affect the cursor visibility itself.
1683 * Use DECTCEM for that.
1684 *
1685 * Defaults:
1686 * args[0]: 0
1687 *
1688 * TODO: implement
1689 */
1690
1691 return 0;
1692}
1693
1694static int screen_DECSDDT(term_screen *screen, const term_seq *seq) {
1695 /*
1696 * DECSDDT - select-disconnect-delay-time
1697 *
1698 * Probably not worth implementing.
1699 */
1700
1701 return 0;
1702}
1703
1704static int screen_DECSDPT(term_screen *screen, const term_seq *seq) {
1705 /*
1706 * DECSDPT - select-digital-printed-data-type
1707 *
1708 * Probably not worth implementing.
1709 */
1710
1711 return 0;
1712}
1713
1714static int screen_DECSED(term_screen *screen, const term_seq *seq) {
1715 /*
1716 * DECSED - selective-erase-in-display
1717 * This control function erases some or all of the erasable characters
1718 * in the display. DECSED can only erase characters defined as erasable
1719 * by the DECSCA control function. DECSED works inside or outside the
1720 * scrolling margins.
1721 *
1722 * @args[0] defines which regions are erased. If it is 0, all cells from
1723 * the cursor (inclusive) till the end of the display are erase. If it
1724 * is 1, all cells from the start of the display till the cursor
1725 * (inclusive) are erased. If it is 2, all cells are erased.
1726 *
1727 * Defaults:
1728 * args[0]: 0
1729 */
1730
1731 unsigned int mode = 0;
1732
1733 if (seq->args[0] > 0)
1734 mode = seq->args[0];
1735
1736 switch (mode) {
1737 case 0:
1738 term_page_erase(screen->page,
3ae49a8f 1739 screen->state.cursor_x, screen->state.cursor_y,
e432f9e8 1740 screen->page->width, screen->page->height,
3ae49a8f 1741 &screen->state.attr, screen->age, true);
e432f9e8
DH
1742 break;
1743 case 1:
1744 term_page_erase(screen->page,
1745 0, 0,
3ae49a8f
DH
1746 screen->state.cursor_x, screen->state.cursor_y,
1747 &screen->state.attr, screen->age, true);
e432f9e8
DH
1748 break;
1749 case 2:
1750 term_page_erase(screen->page,
1751 0, 0,
1752 screen->page->width, screen->page->height,
3ae49a8f 1753 &screen->state.attr, screen->age, true);
e432f9e8
DH
1754 break;
1755 }
1756
1757 return 0;
1758}
1759
1760static int screen_DECSEL(term_screen *screen, const term_seq *seq) {
1761 /*
1762 * DECSEL - selective-erase-in-line
1763 * This control function erases some or all of the erasable characters
1764 * in a single line of text. DECSEL erases only those characters defined
1765 * as erasable by the DECSCA control function. DECSEL works inside or
1766 * outside the scrolling margins.
1767 *
1768 * @args[0] defines the region to be erased. If it is 0, all cells from
1769 * the cursor (inclusive) till the end of the line are erase. If it is
1770 * 1, all cells from the start of the line till the cursor (inclusive)
1771 * are erased. If it is 2, the whole line of the cursor is erased.
1772 *
1773 * Defaults:
1774 * args[0]: 0
1775 */
1776
1777 unsigned int mode = 0;
1778
1779 if (seq->args[0] > 0)
1780 mode = seq->args[0];
1781
1782 switch (mode) {
1783 case 0:
1784 term_page_erase(screen->page,
3ae49a8f
DH
1785 screen->state.cursor_x, screen->state.cursor_y,
1786 screen->page->width, screen->state.cursor_y,
1787 &screen->state.attr, screen->age, true);
e432f9e8
DH
1788 break;
1789 case 1:
1790 term_page_erase(screen->page,
3ae49a8f
DH
1791 0, screen->state.cursor_y,
1792 screen->state.cursor_x, screen->state.cursor_y,
1793 &screen->state.attr, screen->age, true);
e432f9e8
DH
1794 break;
1795 case 2:
1796 term_page_erase(screen->page,
3ae49a8f
DH
1797 0, screen->state.cursor_y,
1798 screen->page->width, screen->state.cursor_y,
1799 &screen->state.attr, screen->age, true);
e432f9e8
DH
1800 break;
1801 }
1802
1803 return 0;
1804}
1805
1806static int screen_DECSERA(term_screen *screen, const term_seq *seq) {
1807 /*
1808 * DECSERA - selective-erase-rectangular-area
1809 *
1810 * Probably not worth implementing.
1811 */
1812
1813 return 0;
1814}
1815
1816static int screen_DECSFC(term_screen *screen, const term_seq *seq) {
1817 /*
1818 * DECSFC - select-flow-control
1819 *
1820 * Probably not worth implementing.
1821 */
1822
1823 return 0;
1824}
1825
1826static int screen_DECSKCV(term_screen *screen, const term_seq *seq) {
1827 /*
1828 * DECSKCV - set-key-click-volume
1829 *
1830 * Probably not worth implementing.
1831 */
1832
1833 return 0;
1834}
1835
1836static int screen_DECSLCK(term_screen *screen, const term_seq *seq) {
1837 /*
1838 * DECSLCK - set-lock-key-style
1839 *
1840 * Probably not worth implementing.
1841 */
1842
1843 return 0;
1844}
1845
1846static int screen_DECSLE(term_screen *screen, const term_seq *seq) {
1847 /*
1848 * DECSLE - select-locator-events
1849 *
1850 * TODO: implement
1851 */
1852
1853 return 0;
1854}
1855
1856static int screen_DECSLPP(term_screen *screen, const term_seq *seq) {
1857 /*
1858 * DECSLPP - set-lines-per-page
1859 * Set the number of lines used for the page. @args[0] specifies the
1860 * number of lines to be used. DEC only allows a limited number of
1861 * choices, however, we allow all integers. 0 is equivalent to 24.
1862 *
1863 * Defaults:
1864 * args[0]: 0
1865 *
1866 * TODO: implement
1867 */
1868
1869 return 0;
1870}
1871
1872static int screen_DECSLRM_OR_SC(term_screen *screen, const term_seq *seq) {
1873 /*
1874 * DECSLRM_OR_SC - set-left-and-right-margins or save-cursor
1875 *
1876 * TODO: Detect save-cursor and run it. DECSLRM is not worth
1877 * implementing.
1878 */
1879
1880 return 0;
1881}
1882
1883static int screen_DECSMBV(term_screen *screen, const term_seq *seq) {
1884 /*
1885 * DECSMBV - set-margin-bell-volume
1886 *
1887 * Probably not worth implementing.
1888 */
1889
1890 return 0;
1891}
1892
1893static int screen_DECSMKR(term_screen *screen, const term_seq *seq) {
1894 /*
1895 * DECSMKR - select-modifier-key-reporting
1896 *
1897 * Probably not worth implementing.
1898 */
1899
1900 return 0;
1901}
1902
1903static int screen_DECSNLS(term_screen *screen, const term_seq *seq) {
1904 /*
1905 * DECSNLS - set-lines-per-screen
1906 *
1907 * Probably not worth implementing.
1908 */
1909
1910 return 0;
1911}
1912
1913static int screen_DECSPP(term_screen *screen, const term_seq *seq) {
1914 /*
1915 * DECSPP - set-port-parameter
1916 *
1917 * Probably not worth implementing.
1918 */
1919
1920 return 0;
1921}
1922
1923static int screen_DECSPPCS(term_screen *screen, const term_seq *seq) {
1924 /*
1925 * DECSPPCS - select-pro-printer-character-set
1926 *
1927 * Probably not worth implementing.
1928 */
1929
1930 return 0;
1931}
1932
1933static int screen_DECSPRTT(term_screen *screen, const term_seq *seq) {
1934 /*
1935 * DECSPRTT - select-printer-type
1936 *
1937 * Probably not worth implementing.
1938 */
1939
1940 return 0;
1941}
1942
1943static int screen_DECSR(term_screen *screen, const term_seq *seq) {
1944 /*
1945 * DECSR - secure-reset
1946 *
1947 * Probably not worth implementing.
1948 */
1949
1950 return 0;
1951}
1952
1953static int screen_DECSRFR(term_screen *screen, const term_seq *seq) {
1954 /*
1955 * DECSRFR - select-refresh-rate
1956 *
1957 * Probably not worth implementing.
1958 */
1959
1960 return 0;
1961}
1962
1963static int screen_DECSSCLS(term_screen *screen, const term_seq *seq) {
1964 /*
1965 * DECSSCLS - set-scroll-speed
1966 *
1967 * Probably not worth implementing.
1968 */
1969
1970 return 0;
1971}
1972
1973static int screen_DECSSDT(term_screen *screen, const term_seq *seq) {
1974 /*
1975 * DECSSDT - select-status-display-line-type
1976 *
1977 * Probably not worth implementing.
1978 */
1979
1980 return 0;
1981}
1982
1983static int screen_DECSSL(term_screen *screen, const term_seq *seq) {
1984 /*
1985 * DECSSL - select-setup-language
1986 *
1987 * Probably not worth implementing.
1988 */
1989
1990 return 0;
1991}
1992
1993static int screen_DECST8C(term_screen *screen, const term_seq *seq) {
1994 /*
1995 * DECST8C - set-tab-at-every-8-columns
1996 * Clear the tab-ruler and reset it to a tab at every 8th column,
1997 * starting at 9 (though, setting a tab at 1 is fine as it has no
1998 * effect).
1999 */
2000
2001 unsigned int i;
2002
2003 for (i = 0; i < screen->page->width; i += 8)
2004 screen->tabs[i / 8] = 0x1;
2005
2006 return 0;
2007}
2008
2009static int screen_DECSTBM(term_screen *screen, const term_seq *seq) {
2010 /*
2011 * DECSTBM - set-top-and-bottom-margins
2012 * This control function sets the top and bottom margins for the current
2013 * page. You cannot perform scrolling outside the margins.
2014 *
2015 * @args[0] defines the top margin, @args[1] defines the bottom margin.
2016 * The bottom margin must be lower than the top-margin.
2017 *
2018 * This call resets the cursor position to 0/0 of the page.
2019 *
2020 * Defaults:
2021 * args[0]: 1
2022 * args[1]: last page-line
2023 */
2024
2025 unsigned int top, bottom;
2026
2027 top = 1;
2028 bottom = screen->page->height;
2029
2030 if (seq->args[0] > 0)
2031 top = seq->args[0];
2032 if (seq->args[1] > 0)
2033 bottom = seq->args[1];
2034
2035 if (top > screen->page->height)
2036 top = screen->page->height;
2037 if (bottom > screen->page->height)
2038 bottom = screen->page->height;
2039
2040 if (top >= bottom || top > screen->page->height || bottom > screen->page->height) {
2041 top = 1;
2042 bottom = screen->page->height;
2043 }
2044
c7afe4f3 2045 term_page_set_scroll_region(screen->page, top - 1, bottom - top + 1);
e432f9e8
DH
2046 screen_cursor_clear_wrap(screen);
2047 screen_cursor_set(screen, 0, 0);
2048
2049 return 0;
2050}
2051
2052static int screen_DECSTR(term_screen *screen, const term_seq *seq) {
2053 /*
2054 * DECSTR - soft-terminal-reset
2055 * Perform a soft reset to the default values.
2056 */
2057
2058 term_screen_soft_reset(screen);
2059
2060 return 0;
2061}
2062
2063static int screen_DECSTRL(term_screen *screen, const term_seq *seq) {
2064 /*
2065 * DECSTRL - set-transmit-rate-limit
2066 *
2067 * Probably not worth implementing.
2068 */
2069
2070 return 0;
2071}
2072
2073static int screen_DECSWBV(term_screen *screen, const term_seq *seq) {
2074 /*
2075 * DECSWBV - set-warning-bell-volume
2076 *
2077 * Probably not worth implementing.
2078 */
2079
2080 return 0;
2081}
2082
2083static int screen_DECSWL(term_screen *screen, const term_seq *seq) {
2084 /*
2085 * DECSWL - single-width-single-height-line
2086 *
2087 * Probably not worth implementing.
2088 */
2089
2090 return 0;
2091}
2092
2093static int screen_DECTID(term_screen *screen, const term_seq *seq) {
2094 /*
2095 * DECTID - select-terminal-id
2096 *
2097 * Probably not worth implementing.
2098 */
2099
2100 return 0;
2101}
2102
2103static int screen_DECTME(term_screen *screen, const term_seq *seq) {
2104 /*
2105 * DECTME - terminal-mode-emulation
2106 *
2107 * Probably not worth implementing.
2108 */
2109
2110 return 0;
2111}
2112
2113static int screen_DECTST(term_screen *screen, const term_seq *seq) {
2114 /*
2115 * DECTST - invoke-confidence-test
2116 *
2117 * Probably not worth implementing.
2118 */
2119
2120 return 0;
2121}
2122
2123static int screen_DL(term_screen *screen, const term_seq *seq) {
2124 /*
2125 * DL - delete-line
2126 * This control function deletes one or more lines in the scrolling
2127 * region, starting with the line that has the cursor. @args[0] defines
2128 * the number of lines to delete. 0 is treated the same as 1.
2129 * As lines are deleted, lines below the cursor and in the scrolling
2130 * region move up. The terminal adds blank lines with no visual
2131 * character attributes at the bottom of the scrolling region. If it is
2132 * greater than the number of lines remaining on the page, DL deletes
2133 * only the remaining lines. DL has no effect outside the scrolling
2134 * margins.
2135 *
2136 * Defaults:
2137 * args[0]: 1
2138 */
2139
2140 unsigned int num = 1;
2141
2142 if (seq->args[0] > 0)
2143 num = seq->args[0];
2144
3ae49a8f 2145 term_page_delete_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
e432f9e8
DH
2146
2147 return 0;
2148}
2149
2150static int screen_DSR_ANSI(term_screen *screen, const term_seq *seq) {
2151 /*
2152 * DSR_ANSI - device-status-report-ansi
2153 *
2154 * TODO: implement
2155 */
2156
2157 return 0;
2158}
2159
2160static int screen_DSR_DEC(term_screen *screen, const term_seq *seq) {
2161 /*
2162 * DSR_DEC - device-status-report-dec
2163 *
2164 * TODO: implement
2165 */
2166
2167 return 0;
2168}
2169
2170static int screen_ECH(term_screen *screen, const term_seq *seq) {
2171 /*
2172 * ECH - erase-character
2173 * This control function erases one or more characters, from the cursor
2174 * position to the right. ECH clears character attributes from erased
2175 * character positions. ECH works inside or outside the scrolling
2176 * margins.
2177 * @args[0] defines the number of characters to erase. 0 is treated the
2178 * same as 1.
2179 *
2180 * Defaults:
2181 * args[0]: 1
2182 */
2183
2184 unsigned int num = 1;
2185
2186 if (seq->args[0] > 0)
2187 num = seq->args[0];
2188
2189 term_page_erase(screen->page,
3ae49a8f
DH
2190 screen->state.cursor_x, screen->state.cursor_y,
2191 screen->state.cursor_x + num, screen->state.cursor_y,
2192 &screen->state.attr, screen->age, false);
e432f9e8
DH
2193
2194 return 0;
2195}
2196
2197static int screen_ED(term_screen *screen, const term_seq *seq) {
2198 /*
2199 * ED - erase-in-display
2200 * This control function erases characters from part or all of the
2201 * display. When you erase complete lines, they become single-height,
2202 * single-width lines, with all visual character attributes cleared. ED
2203 * works inside or outside the scrolling margins.
2204 *
2205 * @args[0] defines the region to erase. 0 means from cursor (inclusive)
2206 * till the end of the screen. 1 means from the start of the screen till
2207 * the cursor (inclusive) and 2 means the whole screen.
2208 *
2209 * Defaults:
2210 * args[0]: 0
2211 */
2212
2213 unsigned int mode = 0;
2214
2215 if (seq->args[0] > 0)
2216 mode = seq->args[0];
2217
2218 switch (mode) {
2219 case 0:
2220 term_page_erase(screen->page,
3ae49a8f 2221 screen->state.cursor_x, screen->state.cursor_y,
e432f9e8 2222 screen->page->width, screen->page->height,
3ae49a8f 2223 &screen->state.attr, screen->age, false);
e432f9e8
DH
2224 break;
2225 case 1:
2226 term_page_erase(screen->page,
2227 0, 0,
3ae49a8f
DH
2228 screen->state.cursor_x, screen->state.cursor_y,
2229 &screen->state.attr, screen->age, false);
e432f9e8
DH
2230 break;
2231 case 2:
2232 term_page_erase(screen->page,
2233 0, 0,
2234 screen->page->width, screen->page->height,
3ae49a8f 2235 &screen->state.attr, screen->age, false);
e432f9e8
DH
2236 break;
2237 }
2238
2239 return 0;
2240}
2241
2242static int screen_EL(term_screen *screen, const term_seq *seq) {
2243 /*
2244 * EL - erase-in-line
2245 * This control function erases characters on the line that has the
2246 * cursor. EL clears all character attributes from erased character
2247 * positions. EL works inside or outside the scrolling margins.
2248 *
2249 * @args[0] defines the region to erase. 0 means from cursor (inclusive)
2250 * till the end of the line. 1 means from the start of the line till the
2251 * cursor (inclusive) and 2 means the whole line.
2252 *
2253 * Defaults:
2254 * args[0]: 0
2255 */
2256
2257 unsigned int mode = 0;
2258
2259 if (seq->args[0] > 0)
2260 mode = seq->args[0];
2261
2262 switch (mode) {
2263 case 0:
2264 term_page_erase(screen->page,
3ae49a8f
DH
2265 screen->state.cursor_x, screen->state.cursor_y,
2266 screen->page->width, screen->state.cursor_y,
2267 &screen->state.attr, screen->age, false);
e432f9e8
DH
2268 break;
2269 case 1:
2270 term_page_erase(screen->page,
3ae49a8f
DH
2271 0, screen->state.cursor_y,
2272 screen->state.cursor_x, screen->state.cursor_y,
2273 &screen->state.attr, screen->age, false);
e432f9e8
DH
2274 break;
2275 case 2:
2276 term_page_erase(screen->page,
3ae49a8f
DH
2277 0, screen->state.cursor_y,
2278 screen->page->width, screen->state.cursor_y,
2279 &screen->state.attr, screen->age, false);
e432f9e8
DH
2280 break;
2281 }
2282
2283 return 0;
2284}
2285
2286static int screen_ENQ(term_screen *screen, const term_seq *seq) {
2287 /*
2288 * ENQ - enquiry
2289 * Transmit the answerback-string. If none is set, do nothing.
2290 */
2291
2292 if (screen->answerback)
2293 return screen_write(screen, screen->answerback, strlen(screen->answerback));
2294
2295 return 0;
2296}
2297
2298static int screen_EPA(term_screen *screen, const term_seq *seq) {
2299 /*
2300 * EPA - end-of-guarded-area
2301 *
2302 * TODO: What is this?
2303 */
2304
2305 return 0;
2306}
2307
2308static int screen_FF(term_screen *screen, const term_seq *seq) {
2309 /*
2310 * FF - form-feed
2311 * This causes the cursor to jump to the next line. It is treated the
2312 * same as LF.
2313 */
2314
2315 return screen_LF(screen, seq);
2316}
2317
2318static int screen_HPA(term_screen *screen, const term_seq *seq) {
2319 /*
2320 * HPA - horizontal-position-absolute
2321 * HPA causes the active position to be moved to the n-th horizontal
2322 * position of the active line. If an attempt is made to move the active
2323 * position past the last position on the line, then the active position
2324 * stops at the last position on the line.
2325 *
2326 * @args[0] defines the horizontal position. 0 is treated as 1.
2327 *
2328 * Defaults:
2329 * args[0]: 1
2330 */
2331
2332 unsigned int num = 1;
2333
2334 if (seq->args[0] > 0)
2335 num = seq->args[0];
2336
2337 screen_cursor_clear_wrap(screen);
3ae49a8f 2338 screen_cursor_set(screen, num - 1, screen->state.cursor_y);
e432f9e8
DH
2339
2340 return 0;
2341}
2342
2343static int screen_HPR(term_screen *screen, const term_seq *seq) {
2344 /*
2345 * HPR - horizontal-position-relative
2346 * HPR causes the active position to be moved to the n-th following
2347 * horizontal position of the active line. If an attempt is made to move
2348 * the active position past the last position on the line, then the
2349 * active position stops at the last position on the line.
2350 *
2351 * @args[0] defines the horizontal position. 0 is treated as 1.
2352 *
2353 * Defaults:
2354 * args[0]: 1
2355 */
2356
2357 unsigned int num = 1;
2358
2359 if (seq->args[0] > 0)
2360 num = seq->args[0];
2361
2362 screen_cursor_clear_wrap(screen);
2363 screen_cursor_right(screen, num);
2364
2365 return 0;
2366}
2367
2368static int screen_HT(term_screen *screen, const term_seq *seq) {
2369 /*
2370 * HT - horizontal-tab
2371 * Moves the cursor to the next tab stop. If there are no more tab
2372 * stops, the cursor moves to the right margin. HT does not cause text
2373 * to auto wrap.
2374 */
2375
2376 screen_cursor_clear_wrap(screen);
2377 screen_cursor_right_tab(screen, 1);
2378
2379 return 0;
2380}
2381
2382static int screen_HTS(term_screen *screen, const term_seq *seq) {
2383 /*
2384 * HTS - horizontal-tab-set
2385 * HTS sets a horizontal tab stop at the column position indicated by
2386 * the value of the active column when the terminal receives an HTS.
2387 *
2388 * Executing an HTS does not effect the other horizontal tab stop
2389 * settings.
2390 */
2391
2392 unsigned int pos;
2393
3ae49a8f 2394 pos = screen->state.cursor_x;
e432f9e8
DH
2395 if (screen->page->width > 0)
2396 screen->tabs[pos / 8] |= 1U << (pos % 8);
2397
2398 return 0;
2399}
2400
2401static int screen_HVP(term_screen *screen, const term_seq *seq) {
2402 /*
2403 * HVP - horizontal-and-vertical-position
2404 * This control function works the same as the cursor position (CUP)
2405 * function. Origin mode (DECOM) selects line numbering and the ability
2406 * to move the cursor into margins.
2407 *
2408 * Defaults:
2409 * args[0]: 1
2410 * args[1]: 1
2411 */
2412
2413 return screen_CUP(screen, seq);
2414}
2415
2416static int screen_ICH(term_screen *screen, const term_seq *seq) {
2417 /*
2418 * ICH - insert-character
2419 * This control function inserts one or more space (SP) characters
2420 * starting at the cursor position. @args[0] is the number of characters
2421 * to insert. 0 is treated as 1.
2422 *
2423 * The ICH sequence inserts blank characters with the normal
2424 * character attribute. The cursor remains at the beginning of the blank
2425 * characters. Text between the cursor and right margin moves to the
2426 * right. Characters scrolled past the right margin are lost. ICH has no
2427 * effect outside the scrolling margins.
2428 *
2429 * Defaults:
2430 * args[0]: 1
2431 */
2432
2433 unsigned int num = 1;
2434
2435 if (seq->args[0] > 0)
2436 num = seq->args[0];
2437
2438 screen_cursor_clear_wrap(screen);
3ae49a8f 2439 term_page_insert_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
e432f9e8
DH
2440
2441 return 0;
2442}
2443
2444static int screen_IL(term_screen *screen, const term_seq *seq) {
2445 /*
2446 * IL - insert-line
2447 * This control function inserts one or more blank lines, starting at
2448 * the cursor. @args[0] is the number of lines to insert. 0 is treated
2449 * as 1.
2450 *
2451 * As lines are inserted, lines below the cursor and in the scrolling
2452 * region move down. Lines scrolled off the page are lost. IL has no
2453 * effect outside the page margins.
2454 *
2455 * Defaults:
2456 * args[0]: 1
2457 */
2458
2459 unsigned int num = 1;
2460
2461 if (seq->args[0] > 0)
2462 num = seq->args[0];
2463
2464 screen_cursor_clear_wrap(screen);
3ae49a8f 2465 term_page_insert_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
e432f9e8
DH
2466
2467 return 0;
2468}
2469
2470static int screen_IND(term_screen *screen, const term_seq *seq) {
2471 /*
2472 * IND - index
2473 * IND moves the cursor down one line in the same column. If the cursor
2474 * is at the bottom margin, then the screen performs a scroll-up.
2475 */
2476
2477 screen_cursor_down(screen, 1, true);
2478
2479 return 0;
2480}
2481
2482static int screen_LF(term_screen *screen, const term_seq *seq) {
2483 /*
2484 * LF - line-feed
2485 * Causes a line feed or a new line operation, depending on the setting
2486 * of line feed/new line mode.
2487 */
2488
2489 screen_cursor_down(screen, 1, true);
2490 if (screen->flags & TERM_FLAG_NEWLINE_MODE)
3ae49a8f 2491 screen_cursor_left(screen, screen->state.cursor_x);
e432f9e8
DH
2492
2493 return 0;
2494}
2495
2496static int screen_LS1R(term_screen *screen, const term_seq *seq) {
2497 /*
2498 * LS1R - locking-shift-1-right
2499 * Map G1 into GR.
2500 */
2501
3ae49a8f 2502 screen->state.gr = &screen->g1;
e432f9e8
DH
2503
2504 return 0;
2505}
2506
2507static int screen_LS2(term_screen *screen, const term_seq *seq) {
2508 /*
2509 * LS2 - locking-shift-2
2510 * Map G2 into GL.
2511 */
2512
3ae49a8f 2513 screen->state.gl = &screen->g2;
e432f9e8
DH
2514
2515 return 0;
2516}
2517
2518static int screen_LS2R(term_screen *screen, const term_seq *seq) {
2519 /*
2520 * LS2R - locking-shift-2-right
2521 * Map G2 into GR.
2522 */
2523
3ae49a8f 2524 screen->state.gr = &screen->g2;
e432f9e8
DH
2525
2526 return 0;
2527}
2528
2529static int screen_LS3(term_screen *screen, const term_seq *seq) {
2530 /*
2531 * LS3 - locking-shift-3
2532 * Map G3 into GL.
2533 */
2534
3ae49a8f 2535 screen->state.gl = &screen->g3;
e432f9e8
DH
2536
2537 return 0;
2538}
2539
2540static int screen_LS3R(term_screen *screen, const term_seq *seq) {
2541 /*
2542 * LS3R - locking-shift-3-right
2543 * Map G3 into GR.
2544 */
2545
3ae49a8f 2546 screen->state.gr = &screen->g3;
e432f9e8
DH
2547
2548 return 0;
2549}
2550
2551static int screen_MC_ANSI(term_screen *screen, const term_seq *seq) {
2552 /*
2553 * MC_ANSI - media-copy-ansi
2554 *
2555 * Probably not worth implementing.
2556 */
2557
2558 return 0;
2559}
2560
2561static int screen_MC_DEC(term_screen *screen, const term_seq *seq) {
2562 /*
2563 * MC_DEC - media-copy-dec
2564 *
2565 * Probably not worth implementing.
2566 */
2567
2568 return 0;
2569}
2570
2571static int screen_NEL(term_screen *screen, const term_seq *seq) {
2572 /*
2573 * NEL - next-line
2574 * Moves cursor to first position on next line. If cursor is at bottom
2575 * margin, then screen performs a scroll-up.
2576 */
2577
2578 screen_cursor_clear_wrap(screen);
2579 screen_cursor_down(screen, 1, true);
3ae49a8f 2580 screen_cursor_set(screen, 0, screen->state.cursor_y);
e432f9e8
DH
2581
2582 return 0;
2583}
2584
2585static int screen_NP(term_screen *screen, const term_seq *seq) {
2586 /*
2587 * NP - next-page
2588 * This control function moves the cursor forward to the home position
2589 * on one of the following pages in page memory. If there is only one
2590 * page, then the terminal ignores NP.
2591 * If NP tries to move the cursor past the last page in memory, then the
2592 * cursor stops at the last page.
2593 *
2594 * @args[0] defines the number of pages to forward. 0 is treated as 1.
2595 *
2596 * Defaults:
2597 * args[0]: 1
2598 *
2599 * Probably not worth implementing. We only support a single page.
2600 */
2601
2602 return 0;
2603}
2604
2605static int screen_NULL(term_screen *screen, const term_seq *seq) {
2606 /*
2607 * NULL - null
2608 * The NULL operation does nothing. ASCII NULL is always ignored.
2609 */
2610
2611 return 0;
2612}
2613
2614static int screen_PP(term_screen *screen, const term_seq *seq) {
2615 /*
2616 * PP - preceding-page
2617 * This control function moves the cursor backward to the home position
2618 * on one of the preceding pages in page memory. If there is only one
2619 * page, then the terminal ignores PP.
2620 * If PP tries to move the cursor back farther than the first page in
2621 * memory, then the cursor stops at the first page.
2622 *
2623 * @args[0] defines the number of pages to go backwards. 0 is treated
2624 * as 1.
2625 *
2626 * Defaults:
2627 * args[0]: 1
2628 *
2629 * Probably not worth implementing. We only support a single page.
2630 */
2631
2632 return 0;
2633}
2634
2635static int screen_PPA(term_screen *screen, const term_seq *seq) {
2636 /*
2637 * PPA - page-position-absolute
2638 * This control function can move the cursor to the corresponding row
2639 * and column on any page in page memory. You select the page by its
2640 * number. If there is only one page, then the terminal ignores PPA.
2641 *
2642 * @args[0] is the number of the page to move the cursor to. If it is
2643 * greater than the number of the last page in memory, then the cursor
2644 * stops at the last page. If it is less than the number of the first
2645 * page, then the cursor stops at the first page.
2646 *
2647 * Defaults:
2648 * args[0]: 1
2649 *
2650 * Probably not worth implementing. We only support a single page.
2651 */
2652
2653 return 0;
2654}
2655
2656static int screen_PPB(term_screen *screen, const term_seq *seq) {
2657 /*
2658 * PPB - page-position-backward
2659 * This control function moves the cursor backward to the corresponding
2660 * row and column on one of the preceding pages in page memory. If there
2661 * is only one page, then the terminal ignores PPB.
2662 *
2663 * @args[0] indicates the number of pages to move the cursor backward.
2664 * If it tries to move the cursor back farther than the first page in
2665 * memory, then the cursor stops at the first page. 0 is treated as 1.
2666 *
2667 * Defaults:
2668 * args[0]: 1
2669 *
2670 * Probably not worth implementing. We only support a single page.
2671 */
2672
2673 return 0;
2674}
2675
2676static int screen_PPR(term_screen *screen, const term_seq *seq) {
2677 /*
2678 * PPR - page-position-relative
2679 * This control function moves the cursor forward to the corresponding
2680 * row and column on one of the following pages in page memory. If there
2681 * is only one page, then the terminal ignores PPR.
2682 *
2683 * @args[0] indicates how many pages to move the cursor forward. If it
2684 * tries to move the cursor beyond the last page in memory, then the
2685 * cursor stops at the last page. 0 is treated as 1.
2686 *
2687 * Defaults:
2688 * args[0]: 1
2689 *
2690 * Probably not worth implementing. We only support a single page.
2691 */
2692
2693 return 0;
2694}
2695
2696static int screen_RC(term_screen *screen, const term_seq *seq) {
2697 /*
2698 * RC - restore-cursor
2699 */
2700
2701 return screen_DECRC(screen, seq);
2702}
2703
2704static int screen_REP(term_screen *screen, const term_seq *seq) {
2705 /*
2706 * REP - repeat
2707 * Repeat the preceding graphics-character the given number of times.
2708 * @args[0] specifies how often it shall be repeated. 0 is treated as 1.
2709 *
2710 * Defaults:
2711 * args[0]: 1
2712 *
2713 * Probably not worth implementing.
2714 */
2715
2716 return 0;
2717}
2718
2719static int screen_RI(term_screen *screen, const term_seq *seq) {
2720 /*
2721 * RI - reverse-index
2722 * Moves the cursor up one line in the same column. If the cursor is at
2723 * the top margin, the page scrolls down.
2724 */
2725
2726 screen_cursor_up(screen, 1, true);
2727
2728 return 0;
2729}
2730
2731static int screen_RIS(term_screen *screen, const term_seq *seq) {
2732 /*
2733 * RIS - reset-to-initial-state
2734 * This control function causes a nonvolatile memory (NVR) recall to
2735 * occur. RIS replaces all set-up features with their saved settings.
2736 *
2737 * The terminal stores these saved settings in NVR memory. The saved
2738 * setting for a feature is the same as the factory-default setting,
2739 * unless you saved a new setting.
2740 */
2741
2742 term_screen_hard_reset(screen);
2743
2744 return 0;
2745}
2746
2747static int screen_RM_ANSI(term_screen *screen, const term_seq *seq) {
2748 /*
2749 * RM_ANSI - reset-mode-ansi
2750 *
2751 * TODO: implement (see VT510rm manual)
2752 */
2753
2754 unsigned int i;
2755
2756 for (i = 0; i < seq->n_args; ++i)
7ee738ec 2757 screen_mode_change_ansi(screen, seq->args[i], false);
e432f9e8
DH
2758
2759 return 0;
2760}
2761
2762static int screen_RM_DEC(term_screen *screen, const term_seq *seq) {
2763 /*
2764 * RM_DEC - reset-mode-dec
2765 * This is the same as RM_ANSI but for DEC modes.
2766 */
2767
2768 unsigned int i;
2769
2770 for (i = 0; i < seq->n_args; ++i)
7ee738ec 2771 screen_mode_change_dec(screen, seq->args[i], false);
e432f9e8
DH
2772
2773 return 0;
2774}
2775
2776static int screen_S7C1T(term_screen *screen, const term_seq *seq) {
2777 /*
2778 * S7C1T - set-7bit-c1-terminal
2779 * This causes the terminal to start sending C1 controls as 7bit
2780 * sequences instead of 8bit C1 controls.
2781 * This is ignored if the terminal is below level-2 emulation mode
2782 * (VT100 and below), the terminal already sends 7bit controls then.
2783 */
2784
2785 if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
2786 screen->flags |= TERM_FLAG_7BIT_MODE;
2787
2788 return 0;
2789}
2790
2791static int screen_S8C1T(term_screen *screen, const term_seq *seq) {
2792 /*
2793 * S8C1T - set-8bit-c1-terminal
2794 * This causes the terminal to start sending C1 controls as 8bit C1
2795 * control instead of 7bit sequences.
2796 * This is ignored if the terminal is below level-2 emulation mode
2797 * (VT100 and below). The terminal always sends 7bit controls in those
2798 * modes.
2799 */
2800
2801 if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
2802 screen->flags &= ~TERM_FLAG_7BIT_MODE;
2803
2804 return 0;
2805}
2806
2807static int screen_SCS(term_screen *screen, const term_seq *seq) {
2808 /*
2809 * SCS - select-character-set
2810 * Designate character sets to G-sets. The mapping from intermediates
2811 * and terminal characters in the escape sequence to G-sets and
2812 * character-sets is non-trivial and implemented separately. See there
2813 * for more information.
2814 * This call simply sets the selected G-set to the desired
2815 * character-set.
2816 */
2817
2818 term_charset *cs = NULL;
2819
2820 /* TODO: support more of them? */
2821 switch (seq->charset) {
2822 case TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL:
2823 case TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL:
2824 case TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL:
2825 case TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL:
2826 case TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL:
2827 case TERM_CHARSET_ISO_LATIN_CYRILLIC:
2828 break;
2829
2830 case TERM_CHARSET_DEC_SPECIAL_GRAPHIC:
2831 cs = &term_dec_special_graphics;
2832 break;
2833 case TERM_CHARSET_DEC_SUPPLEMENTAL:
2834 cs = &term_dec_supplemental_graphics;
2835 break;
2836 case TERM_CHARSET_DEC_TECHNICAL:
2837 case TERM_CHARSET_CYRILLIC_DEC:
2838 case TERM_CHARSET_DUTCH_NRCS:
2839 case TERM_CHARSET_FINNISH_NRCS:
2840 case TERM_CHARSET_FRENCH_NRCS:
2841 case TERM_CHARSET_FRENCH_CANADIAN_NRCS:
2842 case TERM_CHARSET_GERMAN_NRCS:
2843 case TERM_CHARSET_GREEK_DEC:
2844 case TERM_CHARSET_GREEK_NRCS:
2845 case TERM_CHARSET_HEBREW_DEC:
2846 case TERM_CHARSET_HEBREW_NRCS:
2847 case TERM_CHARSET_ITALIAN_NRCS:
2848 case TERM_CHARSET_NORWEGIAN_DANISH_NRCS:
2849 case TERM_CHARSET_PORTUGUESE_NRCS:
2850 case TERM_CHARSET_RUSSIAN_NRCS:
2851 case TERM_CHARSET_SCS_NRCS:
2852 case TERM_CHARSET_SPANISH_NRCS:
2853 case TERM_CHARSET_SWEDISH_NRCS:
2854 case TERM_CHARSET_SWISS_NRCS:
2855 case TERM_CHARSET_TURKISH_DEC:
2856 case TERM_CHARSET_TURKISH_NRCS:
2857 break;
2858
2859 case TERM_CHARSET_USERPREF_SUPPLEMENTAL:
2860 break;
2861 }
2862
2863 if (seq->intermediates & TERM_SEQ_FLAG_POPEN)
2864 screen->g0 = cs ? : &term_unicode_lower;
2865 else if (seq->intermediates & TERM_SEQ_FLAG_PCLOSE)
2866 screen->g1 = cs ? : &term_unicode_upper;
2867 else if (seq->intermediates & TERM_SEQ_FLAG_MULT)
2868 screen->g2 = cs ? : &term_unicode_lower;
2869 else if (seq->intermediates & TERM_SEQ_FLAG_PLUS)
2870 screen->g3 = cs ? : &term_unicode_upper;
2871 else if (seq->intermediates & TERM_SEQ_FLAG_MINUS)
2872 screen->g1 = cs ? : &term_unicode_upper;
2873 else if (seq->intermediates & TERM_SEQ_FLAG_DOT)
2874 screen->g2 = cs ? : &term_unicode_lower;
2875 else if (seq->intermediates & TERM_SEQ_FLAG_SLASH)
2876 screen->g3 = cs ? : &term_unicode_upper;
2877
2878 return 0;
2879}
2880
2881static int screen_SD(term_screen *screen, const term_seq *seq) {
2882 /*
2883 * SD - scroll-down
2884 * This control function moves the user window down a specified number
2885 * of lines in page memory.
2886 * @args[0] is the number of lines to move the
2887 * user window up in page memory. New lines appear at the top of the
2888 * display. Old lines disappear at the bottom of the display. You
2889 * cannot pan past the top margin of the current page. 0 is treated
2890 * as 1.
2891 *
2892 * Defaults:
2893 * args[0]: 1
2894 */
2895
2896 unsigned int num = 1;
2897
2898 if (seq->args[0] > 0)
2899 num = seq->args[0];
2900
3ae49a8f 2901 term_page_scroll_down(screen->page, num, &screen->state.attr, screen->age, NULL);
e432f9e8
DH
2902
2903 return 0;
2904}
2905
2906static int screen_SGR(term_screen *screen, const term_seq *seq) {
2907 /*
2908 * SGR - select-graphics-rendition
2909 */
2910
2911 term_color *dst;
2912 unsigned int i, code;
2913 int v;
2914
2915 if (seq->n_args < 1) {
3ae49a8f 2916 zero(screen->state.attr);
e432f9e8
DH
2917 return 0;
2918 }
2919
2920 for (i = 0; i < seq->n_args; ++i) {
2921 v = seq->args[i];
2922 switch (v) {
2923 case 1:
3ae49a8f 2924 screen->state.attr.bold = 1;
e432f9e8
DH
2925 break;
2926 case 3:
3ae49a8f 2927 screen->state.attr.italic = 1;
e432f9e8
DH
2928 break;
2929 case 4:
3ae49a8f 2930 screen->state.attr.underline = 1;
e432f9e8
DH
2931 break;
2932 case 5:
3ae49a8f 2933 screen->state.attr.blink = 1;
e432f9e8
DH
2934 break;
2935 case 7:
3ae49a8f 2936 screen->state.attr.inverse = 1;
e432f9e8
DH
2937 break;
2938 case 8:
3ae49a8f 2939 screen->state.attr.hidden = 1;
e432f9e8
DH
2940 break;
2941 case 22:
3ae49a8f 2942 screen->state.attr.bold = 0;
e432f9e8
DH
2943 break;
2944 case 23:
3ae49a8f 2945 screen->state.attr.italic = 0;
e432f9e8
DH
2946 break;
2947 case 24:
3ae49a8f 2948 screen->state.attr.underline = 0;
e432f9e8
DH
2949 break;
2950 case 25:
3ae49a8f 2951 screen->state.attr.blink = 0;
e432f9e8
DH
2952 break;
2953 case 27:
3ae49a8f 2954 screen->state.attr.inverse = 0;
e432f9e8
DH
2955 break;
2956 case 28:
3ae49a8f 2957 screen->state.attr.hidden = 0;
e432f9e8
DH
2958 break;
2959 case 30 ... 37:
3ae49a8f 2960 screen->state.attr.fg.ccode = v - 30 + TERM_CCODE_BLACK;
e432f9e8
DH
2961 break;
2962 case 39:
3ae49a8f 2963 screen->state.attr.fg.ccode = 0;
e432f9e8
DH
2964 break;
2965 case 40 ... 47:
3ae49a8f 2966 screen->state.attr.bg.ccode = v - 40 + TERM_CCODE_BLACK;
e432f9e8
DH
2967 break;
2968 case 49:
3ae49a8f 2969 screen->state.attr.bg.ccode = 0;
e432f9e8
DH
2970 break;
2971 case 90 ... 97:
3ae49a8f 2972 screen->state.attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK;
e432f9e8
DH
2973 break;
2974 case 100 ... 107:
3ae49a8f 2975 screen->state.attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK;
e432f9e8
DH
2976 break;
2977 case 38:
2978 /* fallthrough */
2979 case 48:
2980
2981 if (v == 38)
3ae49a8f 2982 dst = &screen->state.attr.fg;
e432f9e8 2983 else
3ae49a8f 2984 dst = &screen->state.attr.bg;
e432f9e8
DH
2985
2986 ++i;
2987 if (i >= seq->n_args)
2988 break;
2989
2990 switch (seq->args[i]) {
2991 case 2:
2992 /* 24bit-color support */
2993
2994 i += 3;
2995 if (i >= seq->n_args)
2996 break;
2997
2998 dst->ccode = TERM_CCODE_RGB;
2999 dst->red = (seq->args[i - 2] >= 0) ? seq->args[i - 2] : 0;
3000 dst->green = (seq->args[i - 1] >= 0) ? seq->args[i - 1] : 0;
3001 dst->blue = (seq->args[i] >= 0) ? seq->args[i] : 0;
3002
3003 break;
3004 case 5:
3005 /* 256-color support */
3006
3007 ++i;
3008 if (i >= seq->n_args || seq->args[i] < 0)
3009 break;
3010
56dec05d 3011 dst->ccode = TERM_CCODE_256;
e432f9e8 3012 code = seq->args[i];
56dec05d 3013 dst->c256 = code < 256 ? code : 0;
e432f9e8
DH
3014
3015 break;
3016 }
3017
3018 break;
3019 case -1:
3020 /* fallthrough */
3021 case 0:
3ae49a8f 3022 zero(screen->state.attr);
e432f9e8
DH
3023 break;
3024 }
3025 }
3026
3027 return 0;
3028}
3029
3030static int screen_SI(term_screen *screen, const term_seq *seq) {
3031 /*
3032 * SI - shift-in
3033 * Map G0 into GL.
3034 */
3035
3ae49a8f 3036 screen->state.gl = &screen->g0;
e432f9e8
DH
3037
3038 return 0;
3039}
3040
3041static int screen_SM_ANSI(term_screen *screen, const term_seq *seq) {
3042 /*
3043 * SM_ANSI - set-mode-ansi
3044 *
3045 * TODO: implement
3046 */
3047
3048 unsigned int i;
3049
3050 for (i = 0; i < seq->n_args; ++i)
7ee738ec 3051 screen_mode_change_ansi(screen, seq->args[i], true);
e432f9e8
DH
3052
3053 return 0;
3054}
3055
3056static int screen_SM_DEC(term_screen *screen, const term_seq *seq) {
3057 /*
3058 * SM_DEC - set-mode-dec
3059 * This is the same as SM_ANSI but for DEC modes.
3060 */
3061
3062 unsigned int i;
3063
3064 for (i = 0; i < seq->n_args; ++i)
7ee738ec 3065 screen_mode_change_dec(screen, seq->args[i], true);
e432f9e8
DH
3066
3067 return 0;
3068}
3069
3070static int screen_SO(term_screen *screen, const term_seq *seq) {
3071 /*
3072 * SO - shift-out
3073 * Map G1 into GL.
3074 */
3075
3ae49a8f 3076 screen->state.gl = &screen->g1;
e432f9e8
DH
3077
3078 return 0;
3079}
3080
3081static int screen_SPA(term_screen *screen, const term_seq *seq) {
3082 /*
3083 * SPA - start-of-protected-area
3084 *
3085 * TODO: What is this?
3086 */
3087
3088 return 0;
3089}
3090
3091static int screen_SS2(term_screen *screen, const term_seq *seq) {
3092 /*
3093 * SS2 - single-shift-2
3094 * Temporarily map G2 into GL for the next graphics character.
3095 */
3096
3ae49a8f 3097 screen->state.glt = &screen->g2;
e432f9e8
DH
3098
3099 return 0;
3100}
3101
3102static int screen_SS3(term_screen *screen, const term_seq *seq) {
3103 /*
3104 * SS3 - single-shift-3
3105 * Temporarily map G3 into GL for the next graphics character
3106 */
3107
3ae49a8f 3108 screen->state.glt = &screen->g3;
e432f9e8
DH
3109
3110 return 0;
3111}
3112
3113static int screen_ST(term_screen *screen, const term_seq *seq) {
3114 /*
3115 * ST - string-terminator
3116 * The string-terminator is usually part of control-sequences and
3117 * handled by the parser. In all other situations it is silently
3118 * ignored.
3119 */
3120
3121 return 0;
3122}
3123
3124static int screen_SU(term_screen *screen, const term_seq *seq) {
3125 /*
3126 * SU - scroll-up
3127 * This control function moves the user window up a specified number of
3128 * lines in page memory.
3129 * @args[0] is the number of lines to move the
3130 * user window down in page memory. New lines appear at the bottom of
3131 * the display. Old lines disappear at the top of the display. You
3132 * cannot pan past the bottom margin of the current page. 0 is treated
3133 * as 1.
3134 *
3135 * Defaults:
3136 * args[0]: 1
3137 */
3138
3139 unsigned int num = 1;
3140
3141 if (seq->args[0] > 0)
3142 num = seq->args[0];
3143
3ae49a8f 3144 term_page_scroll_up(screen->page, num, &screen->state.attr, screen->age, screen->history);
e432f9e8
DH
3145
3146 return 0;
3147}
3148
3149static int screen_SUB(term_screen *screen, const term_seq *seq) {
3150 /*
3151 * SUB - substitute
3152 * Cancel the current control-sequence and print a replacement
3153 * character. Our parser already handles this so all we have to do is
3154 * print the replacement character.
3155 */
3156
3157 static const term_seq rep = {
3158 .type = TERM_SEQ_GRAPHIC,
3159 .command = TERM_CMD_GRAPHIC,
3160 .terminator = 0xfffd,
3161 };
3162
3163 return screen_GRAPHIC(screen, &rep);
3164}
3165
3166static int screen_TBC(term_screen *screen, const term_seq *seq) {
3167 /*
3168 * TBC - tab-clear
3169 * This clears tab-stops. If @args[0] is 0, the tab-stop at the current
3170 * cursor position is cleared. If it is 3, all tab stops are cleared.
3171 *
3172 * Defaults:
3173 * args[0]: 0
3174 */
3175
3176 unsigned int mode = 0, pos;
3177
3178 if (seq->args[0] > 0)
3179 mode = seq->args[0];
3180
3181 switch (mode) {
3182 case 0:
3ae49a8f 3183 pos = screen->state.cursor_x;
e432f9e8
DH
3184 if (screen->page->width > 0)
3185 screen->tabs[pos / 8] &= ~(1U << (pos % 8));
3186 break;
3187 case 3:
3188 if (screen->page->width > 0)
3189 memset(screen->tabs, 0, (screen->page->width + 7) / 8);
3190 break;
3191 }
3192
3193 return 0;
3194}
3195
3196static int screen_VPA(term_screen *screen, const term_seq *seq) {
3197 /*
3198 * VPA - vertical-line-position-absolute
3199 * VPA causes the active position to be moved to the corresponding
3200 * horizontal position. @args[0] specifies the line to jump to. If an
3201 * attempt is made to move the active position below the last line, then
3202 * the active position stops on the last line. 0 is treated as 1.
3203 *
3204 * Defaults:
3205 * args[0]: 1
3206 */
3207
3208 unsigned int pos = 1;
3209
3210 if (seq->args[0] > 0)
3211 pos = seq->args[0];
3212
3213 screen_cursor_clear_wrap(screen);
3ae49a8f 3214 screen_cursor_set_rel(screen, screen->state.cursor_x, pos - 1);
e432f9e8
DH
3215
3216 return 0;
3217}
3218
3219static int screen_VPR(term_screen *screen, const term_seq *seq) {
3220 /*
3221 * VPR - vertical-line-position-relative
3222 * VPR causes the active position to be moved to the corresponding
3223 * horizontal position. @args[0] specifies the number of lines to jump
3224 * down relative to the current cursor position. If an attempt is made
3225 * to move the active position below the last line, the active position
3226 * stops at the last line. 0 is treated as 1.
3227 *
3228 * Defaults:
3229 * args[0]: 1
3230 */
3231
3232 unsigned int num = 1;
3233
3234 if (seq->args[0] > 0)
3235 num = seq->args[0];
3236
3237 screen_cursor_clear_wrap(screen);
3238 screen_cursor_down(screen, num, false);
3239
3240 return 0;
3241}
3242
3243static int screen_VT(term_screen *screen, const term_seq *seq) {
3244 /*
3245 * VT - vertical-tab
3246 * This causes a vertical jump by one line. Terminals treat it exactly
3247 * the same as LF.
3248 */
3249
3250 return screen_LF(screen, seq);
3251}
3252
3253static int screen_XTERM_CLLHP(term_screen *screen, const term_seq *seq) {
3254 /*
3255 * XTERM_CLLHP - xterm-cursor-lower-left-hp-bugfix
3256 * Move the cursor to the lower-left corner of the page. This is an HP
3257 * bugfix by xterm.
3258 *
3259 * Probably not worth implementing.
3260 */
3261
3262 return 0;
3263}
3264
3265static int screen_XTERM_IHMT(term_screen *screen, const term_seq *seq) {
3266 /*
3267 * XTERM_IHMT - xterm-initiate-highlight-mouse-tracking
3268 *
3269 * Probably not worth implementing.
3270 */
3271
3272 return 0;
3273}
3274
3275static int screen_XTERM_MLHP(term_screen *screen, const term_seq *seq) {
3276 /*
3277 * XTERM_MLHP - xterm-memory-lock-hp-bugfix
3278 *
3279 * Probably not worth implementing.
3280 */
3281
3282 return 0;
3283}
3284
3285static int screen_XTERM_MUHP(term_screen *screen, const term_seq *seq) {
3286 /*
3287 * XTERM_MUHP - xterm-memory-unlock-hp-bugfix
3288 *
3289 * Probably not worth implementing.
3290 */
3291
3292 return 0;
3293}
3294
3295static int screen_XTERM_RPM(term_screen *screen, const term_seq *seq) {
3296 /*
3297 * XTERM_RPM - xterm-restore-private-mode
3298 *
3299 * Probably not worth implementing.
3300 */
3301
3302 return 0;
3303}
3304
3305static int screen_XTERM_RRV(term_screen *screen, const term_seq *seq) {
3306 /*
3307 * XTERM_RRV - xterm-reset-resource-value
3308 *
3309 * Probably not worth implementing.
3310 */
3311
3312 return 0;
3313}
3314
3315static int screen_XTERM_RTM(term_screen *screen, const term_seq *seq) {
3316 /*
3317 * XTERM_RTM - xterm-reset-title-mode
3318 *
3319 * Probably not worth implementing.
3320 */
3321
3322 return 0;
3323}
3324
3325static int screen_XTERM_SACL1(term_screen *screen, const term_seq *seq) {
3326 /*
3327 * XTERM_SACL1 - xterm-set-ansi-conformance-level-1
3328 *
3329 * Probably not worth implementing.
3330 */
3331
3332 return 0;
3333}
3334
3335static int screen_XTERM_SACL2(term_screen *screen, const term_seq *seq) {
3336 /*
3337 * XTERM_SACL2 - xterm-set-ansi-conformance-level-2
3338 *
3339 * Probably not worth implementing.
3340 */
3341
3342 return 0;
3343}
3344
3345static int screen_XTERM_SACL3(term_screen *screen, const term_seq *seq) {
3346 /*
3347 * XTERM_SACL3 - xterm-set-ansi-conformance-level-3
3348 *
3349 * Probably not worth implementing.
3350 */
3351
3352 return 0;
3353}
3354
3355static int screen_XTERM_SDCS(term_screen *screen, const term_seq *seq) {
3356 /*
3357 * XTERM_SDCS - xterm-set-default-character-set
3358 * Select the default character set. We treat this the same as UTF-8 as
3359 * this is our default character set. As we always use UTF-8, this
3360 * becomes as no-op.
3361 */
3362
3363 return 0;
3364}
3365
3366static int screen_XTERM_SGFX(term_screen *screen, const term_seq *seq) {
3367 /*
3368 * XTERM_SGFX - xterm-sixel-graphics
3369 *
3370 * Probably not worth implementing.
3371 */
3372
3373 return 0;
3374}
3375
3376static int screen_XTERM_SPM(term_screen *screen, const term_seq *seq) {
3377 /*
3378 * XTERM_SPM - xterm-set-private-mode
3379 *
3380 * Probably not worth implementing.
3381 */
3382
3383 return 0;
3384}
3385
3386static int screen_XTERM_SRV(term_screen *screen, const term_seq *seq) {
3387 /*
3388 * XTERM_SRV - xterm-set-resource-value
3389 *
3390 * Probably not worth implementing.
3391 */
3392
3393 return 0;
3394}
3395
3396static int screen_XTERM_STM(term_screen *screen, const term_seq *seq) {
3397 /*
3398 * XTERM_STM - xterm-set-title-mode
3399 *
3400 * Probably not worth implementing.
3401 */
3402
3403 return 0;
3404}
3405
3406static int screen_XTERM_SUCS(term_screen *screen, const term_seq *seq) {
3407 /*
3408 * XTERM_SUCS - xterm-select-utf8-character-set
3409 * Select UTF-8 as character set. This is our default on only character
3410 * set. Hence, this is a no-op.
3411 */
3412
3413 return 0;
3414}
3415
3416static int screen_XTERM_WM(term_screen *screen, const term_seq *seq) {
3417 /*
3418 * XTERM_WM - xterm-window-management
3419 *
3420 * Probably not worth implementing.
3421 */
3422
3423 return 0;
3424}
3425
3426/*
3427 * Feeding data
3428 * The screen_feed_*() handlers take data from the user and feed it into the
3429 * screen. Once the parser has detected a sequence, we parse the command-type
3430 * and forward it to the command-dispatchers.
3431 */
3432
3433static int screen_feed_cmd(term_screen *screen, const term_seq *seq) {
3434 switch (seq->command) {
3435 case TERM_CMD_GRAPHIC:
3436 return screen_GRAPHIC(screen, seq);
3437 case TERM_CMD_BEL:
3438 return screen_BEL(screen, seq);
3439 case TERM_CMD_BS:
3440 return screen_BS(screen, seq);
3441 case TERM_CMD_CBT:
3442 return screen_CBT(screen, seq);
3443 case TERM_CMD_CHA:
3444 return screen_CHA(screen, seq);
3445 case TERM_CMD_CHT:
3446 return screen_CHT(screen, seq);
3447 case TERM_CMD_CNL:
3448 return screen_CNL(screen, seq);
3449 case TERM_CMD_CPL:
3450 return screen_CPL(screen, seq);
3451 case TERM_CMD_CR:
3452 return screen_CR(screen, seq);
3453 case TERM_CMD_CUB:
3454 return screen_CUB(screen, seq);
3455 case TERM_CMD_CUD:
3456 return screen_CUD(screen, seq);
3457 case TERM_CMD_CUF:
3458 return screen_CUF(screen, seq);
3459 case TERM_CMD_CUP:
3460 return screen_CUP(screen, seq);
3461 case TERM_CMD_CUU:
3462 return screen_CUU(screen, seq);
3463 case TERM_CMD_DA1:
3464 return screen_DA1(screen, seq);
3465 case TERM_CMD_DA2:
3466 return screen_DA2(screen, seq);
3467 case TERM_CMD_DA3:
3468 return screen_DA3(screen, seq);
3469 case TERM_CMD_DC1:
3470 return screen_DC1(screen, seq);
3471 case TERM_CMD_DC3:
3472 return screen_DC3(screen, seq);
3473 case TERM_CMD_DCH:
3474 return screen_DCH(screen, seq);
3475 case TERM_CMD_DECALN:
3476 return screen_DECALN(screen, seq);
3477 case TERM_CMD_DECANM:
3478 return screen_DECANM(screen, seq);
3479 case TERM_CMD_DECBI:
3480 return screen_DECBI(screen, seq);
3481 case TERM_CMD_DECCARA:
3482 return screen_DECCARA(screen, seq);
3483 case TERM_CMD_DECCRA:
3484 return screen_DECCRA(screen, seq);
3485 case TERM_CMD_DECDC:
3486 return screen_DECDC(screen, seq);
3487 case TERM_CMD_DECDHL_BH:
3488 return screen_DECDHL_BH(screen, seq);
3489 case TERM_CMD_DECDHL_TH:
3490 return screen_DECDHL_TH(screen, seq);
3491 case TERM_CMD_DECDWL:
3492 return screen_DECDWL(screen, seq);
3493 case TERM_CMD_DECEFR:
3494 return screen_DECEFR(screen, seq);
3495 case TERM_CMD_DECELF:
3496 return screen_DECELF(screen, seq);
3497 case TERM_CMD_DECELR:
3498 return screen_DECELR(screen, seq);
3499 case TERM_CMD_DECERA:
3500 return screen_DECERA(screen, seq);
3501 case TERM_CMD_DECFI:
3502 return screen_DECFI(screen, seq);
3503 case TERM_CMD_DECFRA:
3504 return screen_DECFRA(screen, seq);
3505 case TERM_CMD_DECIC:
3506 return screen_DECIC(screen, seq);
3507 case TERM_CMD_DECID:
3508 return screen_DECID(screen, seq);
3509 case TERM_CMD_DECINVM:
3510 return screen_DECINVM(screen, seq);
3511 case TERM_CMD_DECKBD:
3512 return screen_DECKBD(screen, seq);
3513 case TERM_CMD_DECKPAM:
3514 return screen_DECKPAM(screen, seq);
3515 case TERM_CMD_DECKPNM:
3516 return screen_DECKPNM(screen, seq);
3517 case TERM_CMD_DECLFKC:
3518 return screen_DECLFKC(screen, seq);
3519 case TERM_CMD_DECLL:
3520 return screen_DECLL(screen, seq);
3521 case TERM_CMD_DECLTOD:
3522 return screen_DECLTOD(screen, seq);
3523 case TERM_CMD_DECPCTERM:
3524 return screen_DECPCTERM(screen, seq);
3525 case TERM_CMD_DECPKA:
3526 return screen_DECPKA(screen, seq);
3527 case TERM_CMD_DECPKFMR:
3528 return screen_DECPKFMR(screen, seq);
3529 case TERM_CMD_DECRARA:
3530 return screen_DECRARA(screen, seq);
3531 case TERM_CMD_DECRC:
3532 return screen_DECRC(screen, seq);
3533 case TERM_CMD_DECREQTPARM:
3534 return screen_DECREQTPARM(screen, seq);
3535 case TERM_CMD_DECRPKT:
3536 return screen_DECRPKT(screen, seq);
3537 case TERM_CMD_DECRQCRA:
3538 return screen_DECRQCRA(screen, seq);
3539 case TERM_CMD_DECRQDE:
3540 return screen_DECRQDE(screen, seq);
3541 case TERM_CMD_DECRQKT:
3542 return screen_DECRQKT(screen, seq);
3543 case TERM_CMD_DECRQLP:
3544 return screen_DECRQLP(screen, seq);
3545 case TERM_CMD_DECRQM_ANSI:
3546 return screen_DECRQM_ANSI(screen, seq);
3547 case TERM_CMD_DECRQM_DEC:
3548 return screen_DECRQM_DEC(screen, seq);
3549 case TERM_CMD_DECRQPKFM:
3550 return screen_DECRQPKFM(screen, seq);
3551 case TERM_CMD_DECRQPSR:
3552 return screen_DECRQPSR(screen, seq);
3553 case TERM_CMD_DECRQTSR:
3554 return screen_DECRQTSR(screen, seq);
3555 case TERM_CMD_DECRQUPSS:
3556 return screen_DECRQUPSS(screen, seq);
3557 case TERM_CMD_DECSACE:
3558 return screen_DECSACE(screen, seq);
3559 case TERM_CMD_DECSASD:
3560 return screen_DECSASD(screen, seq);
3561 case TERM_CMD_DECSC:
3562 return screen_DECSC(screen, seq);
3563 case TERM_CMD_DECSCA:
3564 return screen_DECSCA(screen, seq);
3565 case TERM_CMD_DECSCL:
3566 return screen_DECSCL(screen, seq);
3567 case TERM_CMD_DECSCP:
3568 return screen_DECSCP(screen, seq);
3569 case TERM_CMD_DECSCPP:
3570 return screen_DECSCPP(screen, seq);
3571 case TERM_CMD_DECSCS:
3572 return screen_DECSCS(screen, seq);
3573 case TERM_CMD_DECSCUSR:
3574 return screen_DECSCUSR(screen, seq);
3575 case TERM_CMD_DECSDDT:
3576 return screen_DECSDDT(screen, seq);
3577 case TERM_CMD_DECSDPT:
3578 return screen_DECSDPT(screen, seq);
3579 case TERM_CMD_DECSED:
3580 return screen_DECSED(screen, seq);
3581 case TERM_CMD_DECSEL:
3582 return screen_DECSEL(screen, seq);
3583 case TERM_CMD_DECSERA:
3584 return screen_DECSERA(screen, seq);
3585 case TERM_CMD_DECSFC:
3586 return screen_DECSFC(screen, seq);
3587 case TERM_CMD_DECSKCV:
3588 return screen_DECSKCV(screen, seq);
3589 case TERM_CMD_DECSLCK:
3590 return screen_DECSLCK(screen, seq);
3591 case TERM_CMD_DECSLE:
3592 return screen_DECSLE(screen, seq);
3593 case TERM_CMD_DECSLPP:
3594 return screen_DECSLPP(screen, seq);
3595 case TERM_CMD_DECSLRM_OR_SC:
3596 return screen_DECSLRM_OR_SC(screen, seq);
3597 case TERM_CMD_DECSMBV:
3598 return screen_DECSMBV(screen, seq);
3599 case TERM_CMD_DECSMKR:
3600 return screen_DECSMKR(screen, seq);
3601 case TERM_CMD_DECSNLS:
3602 return screen_DECSNLS(screen, seq);
3603 case TERM_CMD_DECSPP:
3604 return screen_DECSPP(screen, seq);
3605 case TERM_CMD_DECSPPCS:
3606 return screen_DECSPPCS(screen, seq);
3607 case TERM_CMD_DECSPRTT:
3608 return screen_DECSPRTT(screen, seq);
3609 case TERM_CMD_DECSR:
3610 return screen_DECSR(screen, seq);
3611 case TERM_CMD_DECSRFR:
3612 return screen_DECSRFR(screen, seq);
3613 case TERM_CMD_DECSSCLS:
3614 return screen_DECSSCLS(screen, seq);
3615 case TERM_CMD_DECSSDT:
3616 return screen_DECSSDT(screen, seq);
3617 case TERM_CMD_DECSSL:
3618 return screen_DECSSL(screen, seq);
3619 case TERM_CMD_DECST8C:
3620 return screen_DECST8C(screen, seq);
3621 case TERM_CMD_DECSTBM:
3622 return screen_DECSTBM(screen, seq);
3623 case TERM_CMD_DECSTR:
3624 return screen_DECSTR(screen, seq);
3625 case TERM_CMD_DECSTRL:
3626 return screen_DECSTRL(screen, seq);
3627 case TERM_CMD_DECSWBV:
3628 return screen_DECSWBV(screen, seq);
3629 case TERM_CMD_DECSWL:
3630 return screen_DECSWL(screen, seq);
3631 case TERM_CMD_DECTID:
3632 return screen_DECTID(screen, seq);
3633 case TERM_CMD_DECTME:
3634 return screen_DECTME(screen, seq);
3635 case TERM_CMD_DECTST:
3636 return screen_DECTST(screen, seq);
3637 case TERM_CMD_DL:
3638 return screen_DL(screen, seq);
3639 case TERM_CMD_DSR_ANSI:
3640 return screen_DSR_ANSI(screen, seq);
3641 case TERM_CMD_DSR_DEC:
3642 return screen_DSR_DEC(screen, seq);
3643 case TERM_CMD_ECH:
3644 return screen_ECH(screen, seq);
3645 case TERM_CMD_ED:
3646 return screen_ED(screen, seq);
3647 case TERM_CMD_EL:
3648 return screen_EL(screen, seq);
3649 case TERM_CMD_ENQ:
3650 return screen_ENQ(screen, seq);
3651 case TERM_CMD_EPA:
3652 return screen_EPA(screen, seq);
3653 case TERM_CMD_FF:
3654 return screen_FF(screen, seq);
3655 case TERM_CMD_HPA:
3656 return screen_HPA(screen, seq);
3657 case TERM_CMD_HPR:
3658 return screen_HPR(screen, seq);
3659 case TERM_CMD_HT:
3660 return screen_HT(screen, seq);
3661 case TERM_CMD_HTS:
3662 return screen_HTS(screen, seq);
3663 case TERM_CMD_HVP:
3664 return screen_HVP(screen, seq);
3665 case TERM_CMD_ICH:
3666 return screen_ICH(screen, seq);
3667 case TERM_CMD_IL:
3668 return screen_IL(screen, seq);
3669 case TERM_CMD_IND:
3670 return screen_IND(screen, seq);
3671 case TERM_CMD_LF:
3672 return screen_LF(screen, seq);
3673 case TERM_CMD_LS1R:
3674 return screen_LS1R(screen, seq);
3675 case TERM_CMD_LS2:
3676 return screen_LS2(screen, seq);
3677 case TERM_CMD_LS2R:
3678 return screen_LS2R(screen, seq);
3679 case TERM_CMD_LS3:
3680 return screen_LS3(screen, seq);
3681 case TERM_CMD_LS3R:
3682 return screen_LS3R(screen, seq);
3683 case TERM_CMD_MC_ANSI:
3684 return screen_MC_ANSI(screen, seq);
3685 case TERM_CMD_MC_DEC:
3686 return screen_MC_DEC(screen, seq);
3687 case TERM_CMD_NEL:
3688 return screen_NEL(screen, seq);
3689 case TERM_CMD_NP:
3690 return screen_NP(screen, seq);
3691 case TERM_CMD_NULL:
3692 return screen_NULL(screen, seq);
3693 case TERM_CMD_PP:
3694 return screen_PP(screen, seq);
3695 case TERM_CMD_PPA:
3696 return screen_PPA(screen, seq);
3697 case TERM_CMD_PPB:
3698 return screen_PPB(screen, seq);
3699 case TERM_CMD_PPR:
3700 return screen_PPR(screen, seq);
3701 case TERM_CMD_RC:
3702 return screen_RC(screen, seq);
3703 case TERM_CMD_REP:
3704 return screen_REP(screen, seq);
3705 case TERM_CMD_RI:
3706 return screen_RI(screen, seq);
3707 case TERM_CMD_RIS:
3708 return screen_RIS(screen, seq);
3709 case TERM_CMD_RM_ANSI:
3710 return screen_RM_ANSI(screen, seq);
3711 case TERM_CMD_RM_DEC:
3712 return screen_RM_DEC(screen, seq);
3713 case TERM_CMD_S7C1T:
3714 return screen_S7C1T(screen, seq);
3715 case TERM_CMD_S8C1T:
3716 return screen_S8C1T(screen, seq);
3717 case TERM_CMD_SCS:
3718 return screen_SCS(screen, seq);
3719 case TERM_CMD_SD:
3720 return screen_SD(screen, seq);
3721 case TERM_CMD_SGR:
3722 return screen_SGR(screen, seq);
3723 case TERM_CMD_SI:
3724 return screen_SI(screen, seq);
3725 case TERM_CMD_SM_ANSI:
3726 return screen_SM_ANSI(screen, seq);
3727 case TERM_CMD_SM_DEC:
3728 return screen_SM_DEC(screen, seq);
3729 case TERM_CMD_SO:
3730 return screen_SO(screen, seq);
3731 case TERM_CMD_SPA:
3732 return screen_SPA(screen, seq);
3733 case TERM_CMD_SS2:
3734 return screen_SS2(screen, seq);
3735 case TERM_CMD_SS3:
3736 return screen_SS3(screen, seq);
3737 case TERM_CMD_ST:
3738 return screen_ST(screen, seq);
3739 case TERM_CMD_SU:
3740 return screen_SU(screen, seq);
3741 case TERM_CMD_SUB:
3742 return screen_SUB(screen, seq);
3743 case TERM_CMD_TBC:
3744 return screen_TBC(screen, seq);
3745 case TERM_CMD_VPA:
3746 return screen_VPA(screen, seq);
3747 case TERM_CMD_VPR:
3748 return screen_VPR(screen, seq);
3749 case TERM_CMD_VT:
3750 return screen_VT(screen, seq);
3751 case TERM_CMD_XTERM_CLLHP:
3752 return screen_XTERM_CLLHP(screen, seq);
3753 case TERM_CMD_XTERM_IHMT:
3754 return screen_XTERM_IHMT(screen, seq);
3755 case TERM_CMD_XTERM_MLHP:
3756 return screen_XTERM_MLHP(screen, seq);
3757 case TERM_CMD_XTERM_MUHP:
3758 return screen_XTERM_MUHP(screen, seq);
3759 case TERM_CMD_XTERM_RPM:
3760 return screen_XTERM_RPM(screen, seq);
3761 case TERM_CMD_XTERM_RRV:
3762 return screen_XTERM_RRV(screen, seq);
3763 case TERM_CMD_XTERM_RTM:
3764 return screen_XTERM_RTM(screen, seq);
3765 case TERM_CMD_XTERM_SACL1:
3766 return screen_XTERM_SACL1(screen, seq);
3767 case TERM_CMD_XTERM_SACL2:
3768 return screen_XTERM_SACL2(screen, seq);
3769 case TERM_CMD_XTERM_SACL3:
3770 return screen_XTERM_SACL3(screen, seq);
3771 case TERM_CMD_XTERM_SDCS:
3772 return screen_XTERM_SDCS(screen, seq);
3773 case TERM_CMD_XTERM_SGFX:
3774 return screen_XTERM_SGFX(screen, seq);
3775 case TERM_CMD_XTERM_SPM:
3776 return screen_XTERM_SPM(screen, seq);
3777 case TERM_CMD_XTERM_SRV:
3778 return screen_XTERM_SRV(screen, seq);
3779 case TERM_CMD_XTERM_STM:
3780 return screen_XTERM_STM(screen, seq);
3781 case TERM_CMD_XTERM_SUCS:
3782 return screen_XTERM_SUCS(screen, seq);
3783 case TERM_CMD_XTERM_WM:
3784 return screen_XTERM_WM(screen, seq);
3785 }
3786
3787 return 0;
3788}
3789
dda57d91
DH
3790unsigned int term_screen_get_width(term_screen *screen) {
3791 assert_return(screen, -EINVAL);
3792
3793 return screen->page->width;
3794}
3795
3796unsigned int term_screen_get_height(term_screen *screen) {
3797 assert_return(screen, -EINVAL);
3798
3799 return screen->page->height;
3800}
3801
ce04e233
DH
3802uint64_t term_screen_get_age(term_screen *screen) {
3803 assert_return(screen, 0);
3804
3805 return screen->age;
3806}
3807
e432f9e8 3808int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) {
f1f5b2a3 3809 uint32_t *ucs4_str;
e432f9e8
DH
3810 size_t i, j, ucs4_len;
3811 const term_seq *seq;
3812 int r;
3813
3814 assert_return(screen, -EINVAL);
3815
ce04e233
DH
3816 ++screen->age;
3817
e432f9e8
DH
3818 /* Feed bytes into utf8 decoder and handle parsed ucs4 chars. We always
3819 * treat data as UTF-8, but the parser makes sure to fall back to raw
3820 * 8bit mode if the stream is not valid UTF-8. This should be more than
3821 * enough to support old 7bit/8bit modes. */
3822 for (i = 0; i < size; ++i) {
f1f5b2a3 3823 ucs4_len = term_utf8_decode(&screen->utf8, &ucs4_str, in[i]);
e432f9e8
DH
3824 for (j = 0; j < ucs4_len; ++j) {
3825 r = term_parser_feed(screen->parser, &seq, ucs4_str[j]);
3826 if (r < 0) {
3827 return r;
3828 } else if (r != TERM_SEQ_NONE) {
3829 r = screen_feed_cmd(screen, seq);
3830 if (r < 0)
3831 return r;
3832 }
3833 }
3834 }
3835
3836 return 0;
3837}
3838
f8958c34
DH
3839static char *screen_map_key(term_screen *screen,
3840 char *p,
3841 const uint32_t *keysyms,
3842 size_t n_syms,
3843 uint32_t ascii,
3844 const uint32_t *ucs4,
3845 unsigned int mods) {
3846 char ch, ch2, ch_mods;
3847 uint32_t v;
3848 size_t i;
3849
3850 /* TODO: All these key-mappings need to be verified. Public information
3851 * on those mappings is pretty scarce and every emulator seems to do it
3852 * slightly differently.
3853 * A lot of mappings are also missing. */
3854
3855 if (n_syms < 1)
3856 return p;
3857
3858 if (n_syms == 1)
3859 v = keysyms[0];
3860 else
3861 v = XKB_KEY_NoSymbol;
3862
3863 /* In some mappings, the modifiers are encoded as CSI parameters. The
3864 * encoding is rather arbitrary, but seems to work. */
3865 ch_mods = 0;
3866 switch (mods & (TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT | TERM_KBDMOD_CTRL)) {
3867 case TERM_KBDMOD_SHIFT:
3868 ch_mods = '2';
3869 break;
3870 case TERM_KBDMOD_ALT:
3871 ch_mods = '3';
3872 break;
3873 case TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
3874 ch_mods = '4';
3875 break;
3876 case TERM_KBDMOD_CTRL:
3877 ch_mods = '5';
3878 break;
3879 case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT:
3880 ch_mods = '6';
3881 break;
3882 case TERM_KBDMOD_CTRL | TERM_KBDMOD_ALT:
3883 ch_mods = '7';
3884 break;
3885 case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
3886 ch_mods = '8';
3887 break;
3888 }
3889
3890 /* A user might actually use multiple layouts for keyboard
3891 * input. @keysyms[0] contains the actual keysym that the user
3892 * used. But if this keysym is not in the ascii range, the
3893 * input handler does check all other layouts that the user
3894 * specified whether one of them maps the key to some ASCII
3895 * keysym and provides this via @ascii. We always use the real
3896 * keysym except when handling CTRL+<XY> shortcuts we use the
3897 * ascii keysym. This is for compatibility to xterm et. al. so
3898 * ctrl+c always works regardless of the currently active
3899 * keyboard layout. But if no ascii-sym is found, we still use
3900 * the real keysym. */
3901 if (ascii == XKB_KEY_NoSymbol)
3902 ascii = v;
3903
3904 /* map CTRL+<ascii> */
3905 if (mods & TERM_KBDMOD_CTRL) {
3906 switch (ascii) {
3907 case 0x60 ... 0x7e:
3908 /* Right hand side is mapped to the left and then
3909 * treated equally. Fall through to left-hand side.. */
3910 ascii -= 0x20;
3911 case 0x20 ... 0x5f:
3912 /* Printable ASCII is mapped 1-1 in XKB and in
3913 * combination with CTRL bit 7 is flipped. This
3914 * is equivalent to the caret-notation. */
3915 *p++ = ascii ^ 0x40;
3916 return p;
3917 }
3918 }
3919
3920 /* map cursor keys */
3921 ch = 0;
3922 switch (v) {
3923 case XKB_KEY_Up:
3924 ch = 'A';
3925 break;
3926 case XKB_KEY_Down:
3927 ch = 'B';
3928 break;
3929 case XKB_KEY_Right:
3930 ch = 'C';
3931 break;
3932 case XKB_KEY_Left:
3933 ch = 'D';
3934 break;
3935 case XKB_KEY_Home:
3936 ch = 'H';
3937 break;
3938 case XKB_KEY_End:
3939 ch = 'F';
3940 break;
3941 }
3942 if (ch) {
3943 *p++ = 0x1b;
3944 if (screen->flags & TERM_FLAG_CURSOR_KEYS)
3945 *p++ = 'O';
3946 else
3947 *p++ = '[';
3948 if (ch_mods) {
3949 *p++ = '1';
3950 *p++ = ';';
3951 *p++ = ch_mods;
3952 }
3953 *p++ = ch;
3954 return p;
3955 }
3956
3957 /* map action keys */
3958 ch = 0;
3959 switch (v) {
3960 case XKB_KEY_Find:
3961 ch = '1';
3962 break;
3963 case XKB_KEY_Insert:
3964 ch = '2';
3965 break;
3966 case XKB_KEY_Delete:
3967 ch = '3';
3968 break;
3969 case XKB_KEY_Select:
3970 ch = '4';
3971 break;
3972 case XKB_KEY_Page_Up:
3973 ch = '5';
3974 break;
3975 case XKB_KEY_Page_Down:
3976 ch = '6';
3977 break;
3978 }
3979 if (ch) {
3980 *p++ = 0x1b;
3981 *p++ = '[';
3982 *p++ = ch;
3983 if (ch_mods) {
3984 *p++ = ';';
3985 *p++ = ch_mods;
3986 }
3987 *p++ = '~';
3988 return p;
3989 }
3990
3991 /* map lower function keys */
3992 ch = 0;
3993 switch (v) {
3994 case XKB_KEY_F1:
3995 ch = 'P';
3996 break;
3997 case XKB_KEY_F2:
3998 ch = 'Q';
3999 break;
4000 case XKB_KEY_F3:
4001 ch = 'R';
4002 break;
4003 case XKB_KEY_F4:
4004 ch = 'S';
4005 break;
4006 }
4007 if (ch) {
4008 if (ch_mods) {
4009 *p++ = 0x1b;
4010 *p++ = '[';
4011 *p++ = '1';
4012 *p++ = ';';
4013 *p++ = ch_mods;
4014 *p++ = ch;
4015 } else {
4016 *p++ = 0x1b;
4017 *p++ = 'O';
4018 *p++ = ch;
4019 }
4020
4021 return p;
4022 }
4023
4024 /* map upper function keys */
4025 ch = 0;
4026 ch2 = 0;
4027 switch (v) {
4028 case XKB_KEY_F5:
4029 ch = '1';
4030 ch2 = '5';
4031 break;
4032 case XKB_KEY_F6:
4033 ch = '1';
4034 ch2 = '7';
4035 break;
4036 case XKB_KEY_F7:
4037 ch = '1';
4038 ch2 = '8';
4039 break;
4040 case XKB_KEY_F8:
4041 ch = '1';
4042 ch2 = '9';
4043 break;
4044 case XKB_KEY_F9:
4045 ch = '2';
4046 ch2 = '0';
4047 break;
4048 case XKB_KEY_F10:
4049 ch = '2';
4050 ch2 = '1';
4051 break;
4052 case XKB_KEY_F11:
4053 ch = '2';
4054 ch2 = '2';
4055 break;
4056 case XKB_KEY_F12:
4057 ch = '2';
4058 ch2 = '3';
4059 break;
4060 }
4061 if (ch) {
4062 *p++ = 0x1b;
4063 *p++ = '[';
4064 *p++ = ch;
4065 if (ch2)
4066 *p++ = ch2;
4067 if (ch_mods) {
4068 *p++ = ';';
4069 *p++ = ch_mods;
4070 }
4071 *p++ = '~';
4072 return p;
4073 }
4074
4075 /* map special keys */
4076 switch (v) {
4077 case 0xff08: /* XKB_KEY_BackSpace */
4078 case 0xff09: /* XKB_KEY_Tab */
4079 case 0xff0a: /* XKB_KEY_Linefeed */
4080 case 0xff0b: /* XKB_KEY_Clear */
4081 case 0xff15: /* XKB_KEY_Sys_Req */
4082 case 0xff1b: /* XKB_KEY_Escape */
4083 case 0xffff: /* XKB_KEY_Delete */
4084 *p++ = v - 0xff00;
4085 return p;
4086 case 0xff13: /* XKB_KEY_Pause */
4087 /* TODO: What should we do with this key?
4088 * Sending XOFF is awful as there is no simple
4089 * way on modern keyboards to send XON again.
4090 * If someone wants this, we can re-eanble
4091 * optionally. */
4092 return p;
4093 case 0xff14: /* XKB_KEY_Scroll_Lock */
4094 /* TODO: What should we do on scroll-lock?
4095 * Sending 0x14 is what the specs say but it is
4096 * not used today the way most users would
4097 * expect so we disable it. If someone wants
4098 * this, we can re-enable it (optionally). */
4099 return p;
4100 case XKB_KEY_Return:
4101 *p++ = 0x0d;
4102 if (screen->flags & TERM_FLAG_NEWLINE_MODE)
4103 *p++ = 0x0a;
4104 return p;
4105 case XKB_KEY_ISO_Left_Tab:
4106 *p++ = 0x09;
4107 return p;
4108 }
4109
4110 /* map unicode keys */
4111 for (i = 0; i < n_syms; ++i)
4112 p += term_utf8_encode(p, ucs4[i]);
4113
4114 return p;
4115}
4116
4117int term_screen_feed_keyboard(term_screen *screen,
4118 const uint32_t *keysyms,
4119 size_t n_syms,
4120 uint32_t ascii,
4121 const uint32_t *ucs4,
4122 unsigned int mods) {
4123 _cleanup_free_ char *dyn = NULL;
4124 static const size_t padding = 1;
00777762 4125 char buf[128], *start, *p;
f8958c34 4126
e432f9e8
DH
4127 assert_return(screen, -EINVAL);
4128
f8958c34
DH
4129 /* allocate buffer if too small */
4130 start = buf;
4131 if (4 * n_syms + padding > sizeof(buf)) {
4132 dyn = malloc(4 * n_syms + padding);
4133 if (!dyn)
4134 return -ENOMEM;
e432f9e8 4135
f8958c34
DH
4136 start = dyn;
4137 }
4138
4139 /* reserve prefix space */
4140 start += padding;
4141 p = start;
4142
4143 p = screen_map_key(screen, p, keysyms, n_syms, ascii, ucs4, mods);
4144 if (!p || p - start < 1)
4145 return 0;
4146
4147 /* The ALT modifier causes ESC to be prepended to any key-stroke. We
4148 * already accounted for that buffer space above, so simply prepend it
4149 * here.
4150 * TODO: is altSendsEscape a suitable default? What are the semantics
4151 * exactly? Is it used in C0/C1 conversion? Is it prepended if there
4152 * already is an escape character? */
4153 if (mods & TERM_KBDMOD_ALT && *start != 0x1b)
4154 *--start = 0x1b;
4155
4156 /* turn C0 into C1 */
4157 if (!(screen->flags & TERM_FLAG_7BIT_MODE) && p - start >= 2)
4158 if (start[0] == 0x1b && start[1] >= 0x40 && start[1] <= 0x5f)
4159 *++start ^= 0x40;
4160
4161 return screen_write(screen, start, p - start);
e432f9e8
DH
4162}
4163
4164int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) {
4165 unsigned int i;
4166 uint8_t *t;
4167 int r;
4168
4169 assert_return(screen, -EINVAL);
4170
3ae49a8f 4171 r = term_page_reserve(screen->page_main, x, y, &screen->state.attr, screen->age);
e432f9e8
DH
4172 if (r < 0)
4173 return r;
4174
3ae49a8f 4175 r = term_page_reserve(screen->page_alt, x, y, &screen->state.attr, screen->age);
e432f9e8
DH
4176 if (r < 0)
4177 return r;
4178
4179 if (x > screen->n_tabs) {
4180 t = realloc(screen->tabs, (x + 7) / 8);
4181 if (!t)
4182 return -ENOMEM;
4183
4184 screen->tabs = t;
4185 screen->n_tabs = x;
4186 }
4187
4188 for (i = (screen->page->width + 7) / 8 * 8; i < x; i += 8)
4189 screen->tabs[i / 8] = 0x1;
4190
3ae49a8f
DH
4191 term_page_resize(screen->page_main, x, y, &screen->state.attr, screen->age, screen->history);
4192 term_page_resize(screen->page_alt, x, y, &screen->state.attr, screen->age, NULL);
e432f9e8 4193
3ae49a8f
DH
4194 screen->state.cursor_x = screen_clamp_x(screen, screen->state.cursor_x);
4195 screen->state.cursor_y = screen_clamp_x(screen, screen->state.cursor_y);
e432f9e8
DH
4196 screen_cursor_clear_wrap(screen);
4197
4198 return 0;
4199}
4200
4201void term_screen_soft_reset(term_screen *screen) {
4202 unsigned int i;
4203
4204 assert(screen);
4205
e432f9e8
DH
4206 screen->g0 = &term_unicode_lower;
4207 screen->g1 = &term_unicode_upper;
4208 screen->g2 = &term_unicode_lower;
4209 screen->g3 = &term_unicode_upper;
3ae49a8f
DH
4210 screen->state.attr = screen->default_attr;
4211 screen->state.gl = &screen->g0;
4212 screen->state.gr = &screen->g1;
4213 screen->state.glt = NULL;
4214 screen->state.grt = NULL;
4215 screen->state.auto_wrap = 0;
4216 screen->state.origin_mode = 0;
4217
4218 screen->saved = screen->state;
4219 screen->saved.cursor_x = 0;
4220 screen->saved.cursor_y = 0;
c7afe4f3 4221 screen->saved_alt = screen->saved;
e432f9e8
DH
4222
4223 screen->page = screen->page_main;
4224 screen->history = screen->history_main;
4225 screen->flags = TERM_FLAG_7BIT_MODE;
4226 screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
e432f9e8
DH
4227
4228 for (i = 0; i < screen->page->width; i += 8)
4229 screen->tabs[i / 8] = 0x1;
4230
4231 term_page_set_scroll_region(screen->page_main, 0, screen->page->height);
4232 term_page_set_scroll_region(screen->page_alt, 0, screen->page->height);
4233}
4234
4235void term_screen_hard_reset(term_screen *screen) {
4236 assert(screen);
4237
4238 term_screen_soft_reset(screen);
4239 zero(screen->utf8);
3ae49a8f
DH
4240 screen->state.cursor_x = 0;
4241 screen->state.cursor_y = 0;
4242 term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
4243 term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
e432f9e8
DH
4244}
4245
4246int term_screen_set_answerback(term_screen *screen, const char *answerback) {
4247 char *t = NULL;
4248
4249 assert_return(screen, -EINVAL);
4250
4251 if (answerback) {
4252 t = strdup(answerback);
4253 if (!t)
4254 return -ENOMEM;
4255 }
4256
4257 free(screen->answerback);
4258 screen->answerback = t;
4259
4260 return 0;
4261}
be502213
DH
4262
4263int term_screen_draw(term_screen *screen,
4264 int (*draw_fn) (term_screen *screen,
4265 void *userdata,
4266 unsigned int x,
4267 unsigned int y,
4268 const term_attr *attr,
4269 const uint32_t *ch,
4270 size_t n_ch,
4271 unsigned int ch_width),
4272 void *userdata,
4273 uint64_t *fb_age) {
4274 uint64_t cell_age, line_age, age = 0;
4275 term_charbuf_t ch_buf;
4276 const uint32_t *ch_str;
4277 unsigned int i, j, cw;
4278 term_page *page;
4279 term_line *line;
4280 term_cell *cell;
4281 size_t ch_n;
4282 int r;
4283
4284 assert(screen);
4285 assert(draw_fn);
4286
4287 if (fb_age)
4288 age = *fb_age;
4289
4290 page = screen->page;
4291
4292 for (j = 0; j < page->height; ++j) {
4293 line = page->lines[j];
4294 line_age = MAX(line->age, page->age);
4295
4296 for (i = 0; i < page->width; ++i) {
cad8fe9a
DH
4297 term_attr attr;
4298
be502213
DH
4299 cell = &line->cells[i];
4300 cell_age = MAX(cell->age, line_age);
4301
4302 if (age != 0 && cell_age <= age)
4303 continue;
4304
4305 ch_str = term_char_resolve(cell->ch, &ch_n, &ch_buf);
4306
4307 /* Character-width of 0 is used for cleared cells.
4308 * Always treat this as single-cell character, so
4309 * renderers can assume ch_width is set properpy. */
4310 cw = MAX(cell->cwidth, 1U);
4311
cad8fe9a 4312 attr = cell->attr;
3ae49a8f 4313 if (i == screen->state.cursor_x && j == screen->state.cursor_y &&
cad8fe9a
DH
4314 !(screen->flags & TERM_FLAG_HIDE_CURSOR))
4315 attr.inverse ^= 1;
4316
be502213
DH
4317 r = draw_fn(screen,
4318 userdata,
4319 i,
4320 j,
cad8fe9a 4321 &attr,
be502213
DH
4322 ch_str,
4323 ch_n,
4324 cw);
4325 if (r != 0)
4326 return r;
4327 }
4328 }
4329
4330 if (fb_age)
ce04e233 4331 *fb_age = screen->age;
be502213
DH
4332
4333 return 0;
4334}