]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-terminal/term-internal.h
terminal: add screen-handling
[thirdparty/systemd.git] / src / libsystemd-terminal / term-internal.h
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 #pragma once
23
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include "util.h"
28
29 typedef struct term_char term_char_t;
30 typedef struct term_charbuf term_charbuf_t;
31
32 typedef struct term_color term_color;
33 typedef struct term_attr term_attr;
34 typedef struct term_cell term_cell;
35 typedef struct term_line term_line;
36
37 typedef struct term_page term_page;
38 typedef struct term_history term_history;
39
40 typedef struct term_utf8 term_utf8;
41 typedef struct term_seq term_seq;
42 typedef struct term_parser term_parser;
43 typedef uint32_t term_charset[96];
44
45 typedef struct term_screen term_screen;
46
47 /*
48 * Miscellaneous
49 * Sundry things and external helpers.
50 */
51
52 int mk_wcwidth(wchar_t ucs4);
53 int mk_wcwidth_cjk(wchar_t ucs4);
54 int mk_wcswidth(const wchar_t *str, size_t len);
55 int mk_wcswidth_cjk(const wchar_t *str, size_t len);
56
57 /*
58 * Ageing
59 * Redrawing terminals is quite expensive. Therefore, we avoid redrawing on
60 * each single modification and mark modified cells instead. This way, we know
61 * which cells to redraw on the next frame. However, a single DIRTY flag is not
62 * enough for double/triple buffered screens, hence, we use an AGE field for
63 * each cell. If the cell is modified, we simply increase the age by one. Each
64 * framebuffer can then remember its last rendered age and request an update of
65 * all newer cells.
66 * TERM_AGE_NULL is special. If used as cell age, the cell must always be
67 * redrawn (forced update). If used as framebuffer age, all cells are drawn.
68 * This way, we can allow integer wrap-arounds.
69 */
70
71 typedef uint64_t term_age_t;
72
73 #define TERM_AGE_NULL 0
74
75 /*
76 * Characters
77 * Each cell in a terminal page contains only a single character. This is
78 * usually a single UCS-4 value. However, Unicode allows combining-characters,
79 * therefore, the number of UCS-4 characters per cell must be unlimited. The
80 * term_char_t object wraps the internal combining char API so it can be
81 * treated as a single object.
82 */
83
84 struct term_char {
85 /* never access this value directly */
86 uint64_t _value;
87 };
88
89 struct term_charbuf {
90 /* 3 bytes + zero-terminator */
91 uint32_t buf[4];
92 };
93
94 #define TERM_CHAR_INIT(_val) ((term_char_t){ ._value = (_val) })
95 #define TERM_CHAR_NULL TERM_CHAR_INIT(0)
96
97 term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4);
98 term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4);
99 term_char_t term_char_dup(term_char_t ch);
100 term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4);
101
102 const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b);
103 unsigned int term_char_lookup_width(term_char_t ch);
104
105 /* true if @ch is TERM_CHAR_NULL, otherwise false */
106 static inline bool term_char_is_null(term_char_t ch) {
107 return ch._value == 0;
108 }
109
110 /* true if @ch is dynamically allocated and needs to be freed */
111 static inline bool term_char_is_allocated(term_char_t ch) {
112 return !term_char_is_null(ch) && !(ch._value & 0x1);
113 }
114
115 /* true if (a == b), otherwise false; this is (a == b), NOT (*a == *b) */
116 static inline bool term_char_same(term_char_t a, term_char_t b) {
117 return a._value == b._value;
118 }
119
120 /* true if (*a == *b), otherwise false; this is implied by (a == b) */
121 static inline bool term_char_equal(term_char_t a, term_char_t b) {
122 const uint32_t *sa, *sb;
123 term_charbuf_t ca, cb;
124 size_t na, nb;
125
126 sa = term_char_resolve(a, &na, &ca);
127 sb = term_char_resolve(b, &nb, &cb);
128 return na == nb && !memcmp(sa, sb, sizeof(*sa) * na);
129 }
130
131 /* free @ch in case it is dynamically allocated */
132 static inline term_char_t term_char_free(term_char_t ch) {
133 if (term_char_is_allocated(ch))
134 term_char_set(ch, 0);
135
136 return TERM_CHAR_NULL;
137 }
138
139 /* gcc _cleanup_ helpers */
140 #define _term_char_free_ _cleanup_(term_char_freep)
141 static inline void term_char_freep(term_char_t *p) {
142 term_char_free(*p);
143 }
144
145 /*
146 * Attributes
147 * Each cell in a terminal page can have its own set of attributes. These alter
148 * the behavior of the renderer for this single cell. We use term_attr to
149 * specify attributes.
150 * The only non-obvious field is "ccode" for foreground and background colors.
151 * This field contains the terminal color-code in case no full RGB information
152 * was given by the host. It is also required for dynamic color palettes. If it
153 * is set to TERM_CCODE_RGB, the "red", "green" and "blue" fields contain the
154 * full RGB color.
155 */
156
157 enum {
158 /* special color-codes */
159 TERM_CCODE_DEFAULT, /* default foreground/background color */
160 TERM_CCODE_256, /* 256color code */
161 TERM_CCODE_RGB, /* color is specified as RGB */
162
163 /* dark color-codes */
164 TERM_CCODE_BLACK,
165 TERM_CCODE_RED,
166 TERM_CCODE_GREEN,
167 TERM_CCODE_YELLOW,
168 TERM_CCODE_BLUE,
169 TERM_CCODE_MAGENTA,
170 TERM_CCODE_CYAN,
171 TERM_CCODE_WHITE, /* technically: light grey */
172
173 /* light color-codes */
174 TERM_CCODE_LIGHT_BLACK = TERM_CCODE_BLACK + 8, /* technically: dark grey */
175 TERM_CCODE_LIGHT_RED = TERM_CCODE_RED + 8,
176 TERM_CCODE_LIGHT_GREEN = TERM_CCODE_GREEN + 8,
177 TERM_CCODE_LIGHT_YELLOW = TERM_CCODE_YELLOW + 8,
178 TERM_CCODE_LIGHT_BLUE = TERM_CCODE_BLUE + 8,
179 TERM_CCODE_LIGHT_MAGENTA = TERM_CCODE_MAGENTA + 8,
180 TERM_CCODE_LIGHT_CYAN = TERM_CCODE_CYAN + 8,
181 TERM_CCODE_LIGHT_WHITE = TERM_CCODE_WHITE + 8,
182
183 TERM_CCODE_CNT,
184 };
185
186 struct term_color {
187 uint8_t ccode;
188 uint8_t c256;
189 uint8_t red;
190 uint8_t green;
191 uint8_t blue;
192 };
193
194 struct term_attr {
195 term_color fg; /* foreground color */
196 term_color bg; /* background color */
197
198 unsigned int bold : 1; /* bold font */
199 unsigned int italic : 1; /* italic font */
200 unsigned int underline : 1; /* underline text */
201 unsigned int inverse : 1; /* inverse fg/bg */
202 unsigned int protect : 1; /* protect from erase */
203 unsigned int blink : 1; /* blink text */
204 unsigned int hidden : 1; /* hidden */
205 };
206
207 /*
208 * Cells
209 * The term_cell structure respresents a single cell in a terminal page. It
210 * contains the stored character, the age of the cell and all its attributes.
211 */
212
213 struct term_cell {
214 term_char_t ch; /* stored char or TERM_CHAR_NULL */
215 term_age_t age; /* cell age or TERM_AGE_NULL */
216 term_attr attr; /* cell attributes */
217 unsigned int cwidth; /* cached term_char_lookup_width(cell->ch) */
218 };
219
220 /*
221 * Lines
222 * Instead of storing cells in a 2D array, we store them in an array of
223 * dynamically allocated lines. This way, scrolling can be implemented very
224 * fast without moving any cells at all. Similarly, the scrollback-buffer is
225 * much simpler to implement.
226 * We use term_line to store a single line. It contains an array of cells, a
227 * fill-state which remembers the amount of blanks on the right side, a
228 * separate age just for the line which can overwrite the age for all cells,
229 * and some management data.
230 */
231
232 struct term_line {
233 term_line *lines_next; /* linked-list for histories */
234 term_line *lines_prev; /* linked-list for histories */
235
236 unsigned int width; /* visible width of line */
237 unsigned int n_cells; /* # of allocated cells */
238 term_cell *cells; /* cell-array */
239
240 term_age_t age; /* line age */
241 unsigned int fill; /* # of valid cells; starting left */
242 };
243
244 int term_line_new(term_line **out);
245 term_line *term_line_free(term_line *line);
246
247 #define _term_line_free_ _cleanup_(term_line_freep)
248 DEFINE_TRIVIAL_CLEANUP_FUNC(term_line*, term_line_free);
249
250 int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width);
251 void term_line_set_width(term_line *line, unsigned int width);
252 void term_line_write(term_line *line, unsigned int pos_x, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode);
253 void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
254 void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
255 void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age);
256 void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected);
257 void term_line_reset(term_line *line, const term_attr *attr, term_age_t age);
258
259 void term_line_link(term_line *line, term_line **first, term_line **last);
260 void term_line_link_tail(term_line *line, term_line **first, term_line **last);
261 void term_line_unlink(term_line *line, term_line **first, term_line **last);
262
263 #define TERM_LINE_LINK(_line, _head) term_line_link((_line), &(_head)->lines_first, &(_head)->lines_last)
264 #define TERM_LINE_LINK_TAIL(_line, _head) term_line_link_tail((_line), &(_head)->lines_first, &(_head)->lines_last)
265 #define TERM_LINE_UNLINK(_line, _head) term_line_unlink((_line), &(_head)->lines_first, &(_head)->lines_last)
266
267 /*
268 * Pages
269 * A page represents the 2D table containing all cells of a terminal. It stores
270 * lines as an array of pointers so scrolling becomes a simple line-shuffle
271 * operation.
272 * Scrolling is always targeted only at the scroll-region defined via scroll_idx
273 * and scroll_num. The fill-state keeps track of the number of touched lines in
274 * the scroll-region. @width and @height describe the visible region of the page
275 * and are guaranteed to be allocated at all times.
276 */
277
278 struct term_page {
279 term_age_t age; /* page age */
280
281 term_line **lines; /* array of line-pointers */
282 term_line **line_cache; /* cache for temporary operations */
283 unsigned int n_lines; /* # of allocated lines */
284
285 unsigned int width; /* width of visible area */
286 unsigned int height; /* height of visible area */
287 unsigned int scroll_idx; /* scrolling-region start index */
288 unsigned int scroll_num; /* scrolling-region length in lines */
289 unsigned int scroll_fill; /* # of valid scroll-lines */
290 };
291
292 int term_page_new(term_page **out);
293 term_page *term_page_free(term_page *page);
294
295 #define _term_page_free_ _cleanup_(term_page_freep)
296 DEFINE_TRIVIAL_CLEANUP_FUNC(term_page*, term_page_free);
297
298 term_cell *term_page_get_cell(term_page *page, unsigned int x, unsigned int y);
299
300 int term_page_reserve(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age);
301 void term_page_resize(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age, term_history *history);
302 void term_page_write(term_page *page, unsigned int pos_x, unsigned int pos_y, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode);
303 void term_page_insert_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age);
304 void term_page_delete_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age);
305 void term_page_append_combchar(term_page *page, unsigned int pos_x, unsigned int pos_y, uint32_t ucs4, term_age_t age);
306 void term_page_erase(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int to_x, unsigned int to_y, const term_attr *attr, term_age_t age, bool keep_protected);
307 void term_page_reset(term_page *page, const term_attr *attr, term_age_t age);
308
309 void term_page_set_scroll_region(term_page *page, unsigned int idx, unsigned int num);
310 void term_page_scroll_up(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
311 void term_page_scroll_down(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
312 void term_page_insert_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
313 void term_page_delete_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
314
315 /*
316 * Histories
317 * Scroll-back buffers use term_history objects to store scroll-back lines. A
318 * page is independent of the history used. All page operations that modify a
319 * history take it as separate argument. You're free to pass NULL at all times
320 * if no history should be used.
321 * Lines are stored in a linked list as no complex operations are ever done on
322 * history lines, besides pushing/poping. Note that history lines do not have a
323 * guaranteed minimum length. Any kind of line might be stored there. Missing
324 * cells should be cleared to the background color.
325 */
326
327 struct term_history {
328 term_line *lines_first;
329 term_line *lines_last;
330 unsigned int n_lines;
331 unsigned int max_lines;
332 };
333
334 int term_history_new(term_history **out);
335 term_history *term_history_free(term_history *history);
336
337 #define _term_history_free_ _cleanup_(term_history_freep)
338 DEFINE_TRIVIAL_CLEANUP_FUNC(term_history*, term_history_free);
339
340 void term_history_clear(term_history *history);
341 void term_history_trim(term_history *history, unsigned int max);
342 void term_history_push(term_history *history, term_line *line);
343 term_line *term_history_pop(term_history *history, unsigned int reserve_width, const term_attr *attr, term_age_t age);
344 unsigned int term_history_peek(term_history *history, unsigned int max, unsigned int reserve_width, const term_attr *attr, term_age_t age);
345
346 /*
347 * UTF-8
348 * The UTF-decoder and encoder are adjusted for terminals and provide proper
349 * fallbacks for invalid UTF-8. In terminals it's quite usual to use fallbacks
350 * instead of rejecting invalid input. This way, old legacy applications still
351 * work (this is especially important for 7bit/ASCII DEC modes).
352 */
353
354 struct term_utf8 {
355 uint32_t chars[5];
356 uint32_t ucs4;
357
358 unsigned int i_bytes : 3;
359 unsigned int n_bytes : 3;
360 unsigned int valid : 1;
361 };
362
363 size_t term_utf8_encode(char *out_utf8, uint32_t g);
364 const uint32_t *term_utf8_decode(term_utf8 *p, size_t *out_len, char c);
365
366 /*
367 * Parsers
368 * The term_parser object parses control-sequences for both host and terminal
369 * side. Based on this parser, there is a set of command-parsers that take a
370 * term_seq sequence and returns the command it represents. This is different
371 * for host and terminal side so a different set of parsers is provided.
372 */
373
374 enum {
375 TERM_SEQ_NONE, /* placeholder, no sequence parsed */
376
377 TERM_SEQ_IGNORE, /* no-op character */
378 TERM_SEQ_GRAPHIC, /* graphic character */
379 TERM_SEQ_CONTROL, /* control character */
380 TERM_SEQ_ESCAPE, /* escape sequence */
381 TERM_SEQ_CSI, /* control sequence function */
382 TERM_SEQ_DCS, /* device control string */
383 TERM_SEQ_OSC, /* operating system control */
384
385 TERM_SEQ_CNT
386 };
387
388 enum {
389 /* these must be kept compatible to (1U << (ch - 0x20)) */
390
391 TERM_SEQ_FLAG_SPACE = (1U << 0), /* char: */
392 TERM_SEQ_FLAG_BANG = (1U << 1), /* char: ! */
393 TERM_SEQ_FLAG_DQUOTE = (1U << 2), /* char: " */
394 TERM_SEQ_FLAG_HASH = (1U << 3), /* char: # */
395 TERM_SEQ_FLAG_CASH = (1U << 4), /* char: $ */
396 TERM_SEQ_FLAG_PERCENT = (1U << 5), /* char: % */
397 TERM_SEQ_FLAG_AND = (1U << 6), /* char: & */
398 TERM_SEQ_FLAG_SQUOTE = (1U << 7), /* char: ' */
399 TERM_SEQ_FLAG_POPEN = (1U << 8), /* char: ( */
400 TERM_SEQ_FLAG_PCLOSE = (1U << 9), /* char: ) */
401 TERM_SEQ_FLAG_MULT = (1U << 10), /* char: * */
402 TERM_SEQ_FLAG_PLUS = (1U << 11), /* char: + */
403 TERM_SEQ_FLAG_COMMA = (1U << 12), /* char: , */
404 TERM_SEQ_FLAG_MINUS = (1U << 13), /* char: - */
405 TERM_SEQ_FLAG_DOT = (1U << 14), /* char: . */
406 TERM_SEQ_FLAG_SLASH = (1U << 15), /* char: / */
407
408 /* 16-35 is reserved for numbers; unused */
409
410 /* COLON is reserved = (1U << 26), char: : */
411 /* SEMICOLON is reserved = (1U << 27), char: ; */
412 TERM_SEQ_FLAG_LT = (1U << 28), /* char: < */
413 TERM_SEQ_FLAG_EQUAL = (1U << 29), /* char: = */
414 TERM_SEQ_FLAG_GT = (1U << 30), /* char: > */
415 TERM_SEQ_FLAG_WHAT = (1U << 31), /* char: ? */
416 };
417
418 enum {
419 TERM_CMD_NONE, /* placeholder */
420 TERM_CMD_GRAPHIC, /* graphics character */
421
422 TERM_CMD_BEL, /* bell */
423 TERM_CMD_BS, /* backspace */
424 TERM_CMD_CBT, /* cursor-backward-tabulation */
425 TERM_CMD_CHA, /* cursor-horizontal-absolute */
426 TERM_CMD_CHT, /* cursor-horizontal-forward-tabulation */
427 TERM_CMD_CNL, /* cursor-next-line */
428 TERM_CMD_CPL, /* cursor-previous-line */
429 TERM_CMD_CR, /* carriage-return */
430 TERM_CMD_CUB, /* cursor-backward */
431 TERM_CMD_CUD, /* cursor-down */
432 TERM_CMD_CUF, /* cursor-forward */
433 TERM_CMD_CUP, /* cursor-position */
434 TERM_CMD_CUU, /* cursor-up */
435 TERM_CMD_DA1, /* primary-device-attributes */
436 TERM_CMD_DA2, /* secondary-device-attributes */
437 TERM_CMD_DA3, /* tertiary-device-attributes */
438 TERM_CMD_DC1, /* device-control-1 or XON */
439 TERM_CMD_DC3, /* device-control-3 or XOFF */
440 TERM_CMD_DCH, /* delete-character */
441 TERM_CMD_DECALN, /* screen-alignment-pattern */
442 TERM_CMD_DECANM, /* ansi-mode */
443 TERM_CMD_DECBI, /* back-index */
444 TERM_CMD_DECCARA, /* change-attributes-in-rectangular-area */
445 TERM_CMD_DECCRA, /* copy-rectangular-area */
446 TERM_CMD_DECDC, /* delete-column */
447 TERM_CMD_DECDHL_BH, /* double-width-double-height-line: bottom half */
448 TERM_CMD_DECDHL_TH, /* double-width-double-height-line: top half */
449 TERM_CMD_DECDWL, /* double-width-single-height-line */
450 TERM_CMD_DECEFR, /* enable-filter-rectangle */
451 TERM_CMD_DECELF, /* enable-local-functions */
452 TERM_CMD_DECELR, /* enable-locator-reporting */
453 TERM_CMD_DECERA, /* erase-rectangular-area */
454 TERM_CMD_DECFI, /* forward-index */
455 TERM_CMD_DECFRA, /* fill-rectangular-area */
456 TERM_CMD_DECIC, /* insert-column */
457 TERM_CMD_DECID, /* return-terminal-id */
458 TERM_CMD_DECINVM, /* invoke-macro */
459 TERM_CMD_DECKBD, /* keyboard-language-selection */
460 TERM_CMD_DECKPAM, /* keypad-application-mode */
461 TERM_CMD_DECKPNM, /* keypad-numeric-mode */
462 TERM_CMD_DECLFKC, /* local-function-key-control */
463 TERM_CMD_DECLL, /* load-leds */
464 TERM_CMD_DECLTOD, /* load-time-of-day */
465 TERM_CMD_DECPCTERM, /* pcterm-mode */
466 TERM_CMD_DECPKA, /* program-key-action */
467 TERM_CMD_DECPKFMR, /* program-key-free-memory-report */
468 TERM_CMD_DECRARA, /* reverse-attributes-in-rectangular-area */
469 TERM_CMD_DECRC, /* restore-cursor */
470 TERM_CMD_DECREQTPARM, /* request-terminal-parameters */
471 TERM_CMD_DECRPKT, /* report-key-type */
472 TERM_CMD_DECRQCRA, /* request-checksum-of-rectangular-area */
473 TERM_CMD_DECRQDE, /* request-display-extent */
474 TERM_CMD_DECRQKT, /* request-key-type */
475 TERM_CMD_DECRQLP, /* request-locator-position */
476 TERM_CMD_DECRQM_ANSI, /* request-mode-ansi */
477 TERM_CMD_DECRQM_DEC, /* request-mode-dec */
478 TERM_CMD_DECRQPKFM, /* request-program-key-free-memory */
479 TERM_CMD_DECRQPSR, /* request-presentation-state-report */
480 TERM_CMD_DECRQTSR, /* request-terminal-state-report */
481 TERM_CMD_DECRQUPSS, /* request-user-preferred-supplemental-set */
482 TERM_CMD_DECSACE, /* select-attribute-change-extent */
483 TERM_CMD_DECSASD, /* select-active-status-display */
484 TERM_CMD_DECSC, /* save-cursor */
485 TERM_CMD_DECSCA, /* select-character-protection-attribute */
486 TERM_CMD_DECSCL, /* select-conformance-level */
487 TERM_CMD_DECSCP, /* select-communication-port */
488 TERM_CMD_DECSCPP, /* select-columns-per-page */
489 TERM_CMD_DECSCS, /* select-communication-speed */
490 TERM_CMD_DECSCUSR, /* set-cursor-style */
491 TERM_CMD_DECSDDT, /* select-disconnect-delay-time */
492 TERM_CMD_DECSDPT, /* select-digital-printed-data-type */
493 TERM_CMD_DECSED, /* selective-erase-in-display */
494 TERM_CMD_DECSEL, /* selective-erase-in-line */
495 TERM_CMD_DECSERA, /* selective-erase-rectangular-area */
496 TERM_CMD_DECSFC, /* select-flow-control */
497 TERM_CMD_DECSKCV, /* set-key-click-volume */
498 TERM_CMD_DECSLCK, /* set-lock-key-style */
499 TERM_CMD_DECSLE, /* select-locator-events */
500 TERM_CMD_DECSLPP, /* set-lines-per-page */
501 TERM_CMD_DECSLRM_OR_SC, /* set-left-and-right-margins or save-cursor */
502 TERM_CMD_DECSMBV, /* set-margin-bell-volume */
503 TERM_CMD_DECSMKR, /* select-modifier-key-reporting */
504 TERM_CMD_DECSNLS, /* set-lines-per-screen */
505 TERM_CMD_DECSPP, /* set-port-parameter */
506 TERM_CMD_DECSPPCS, /* select-pro-printer-character-set */
507 TERM_CMD_DECSPRTT, /* select-printer-type */
508 TERM_CMD_DECSR, /* secure-reset */
509 TERM_CMD_DECSRFR, /* select-refresh-rate */
510 TERM_CMD_DECSSCLS, /* set-scroll-speed */
511 TERM_CMD_DECSSDT, /* select-status-display-line-type */
512 TERM_CMD_DECSSL, /* select-setup-language */
513 TERM_CMD_DECST8C, /* set-tab-at-every-8-columns */
514 TERM_CMD_DECSTBM, /* set-top-and-bottom-margins */
515 TERM_CMD_DECSTR, /* soft-terminal-reset */
516 TERM_CMD_DECSTRL, /* set-transmit-rate-limit */
517 TERM_CMD_DECSWBV, /* set-warning-bell-volume */
518 TERM_CMD_DECSWL, /* single-width-single-height-line */
519 TERM_CMD_DECTID, /* select-terminal-id */
520 TERM_CMD_DECTME, /* terminal-mode-emulation */
521 TERM_CMD_DECTST, /* invoke-confidence-test */
522 TERM_CMD_DL, /* delete-line */
523 TERM_CMD_DSR_ANSI, /* device-status-report-ansi */
524 TERM_CMD_DSR_DEC, /* device-status-report-dec */
525 TERM_CMD_ECH, /* erase-character */
526 TERM_CMD_ED, /* erase-in-display */
527 TERM_CMD_EL, /* erase-in-line */
528 TERM_CMD_ENQ, /* enquiry */
529 TERM_CMD_EPA, /* end-of-guarded-area */
530 TERM_CMD_FF, /* form-feed */
531 TERM_CMD_HPA, /* horizontal-position-absolute */
532 TERM_CMD_HPR, /* horizontal-position-relative */
533 TERM_CMD_HT, /* horizontal-tab */
534 TERM_CMD_HTS, /* horizontal-tab-set */
535 TERM_CMD_HVP, /* horizontal-and-vertical-position */
536 TERM_CMD_ICH, /* insert-character */
537 TERM_CMD_IL, /* insert-line */
538 TERM_CMD_IND, /* index */
539 TERM_CMD_LF, /* line-feed */
540 TERM_CMD_LS1R, /* locking-shift-1-right */
541 TERM_CMD_LS2, /* locking-shift-2 */
542 TERM_CMD_LS2R, /* locking-shift-2-right */
543 TERM_CMD_LS3, /* locking-shift-3 */
544 TERM_CMD_LS3R, /* locking-shift-3-right */
545 TERM_CMD_MC_ANSI, /* media-copy-ansi */
546 TERM_CMD_MC_DEC, /* media-copy-dec */
547 TERM_CMD_NEL, /* next-line */
548 TERM_CMD_NP, /* next-page */
549 TERM_CMD_NULL, /* null */
550 TERM_CMD_PP, /* preceding-page */
551 TERM_CMD_PPA, /* page-position-absolute */
552 TERM_CMD_PPB, /* page-position-backward */
553 TERM_CMD_PPR, /* page-position-relative */
554 TERM_CMD_RC, /* restore-cursor */
555 TERM_CMD_REP, /* repeat */
556 TERM_CMD_RI, /* reverse-index */
557 TERM_CMD_RIS, /* reset-to-initial-state */
558 TERM_CMD_RM_ANSI, /* reset-mode-ansi */
559 TERM_CMD_RM_DEC, /* reset-mode-dec */
560 TERM_CMD_S7C1T, /* set-7bit-c1-terminal */
561 TERM_CMD_S8C1T, /* set-8bit-c1-terminal */
562 TERM_CMD_SCS, /* select-character-set */
563 TERM_CMD_SD, /* scroll-down */
564 TERM_CMD_SGR, /* select-graphics-rendition */
565 TERM_CMD_SI, /* shift-in */
566 TERM_CMD_SM_ANSI, /* set-mode-ansi */
567 TERM_CMD_SM_DEC, /* set-mode-dec */
568 TERM_CMD_SO, /* shift-out */
569 TERM_CMD_SPA, /* start-of-protected-area */
570 TERM_CMD_SS2, /* single-shift-2 */
571 TERM_CMD_SS3, /* single-shift-3 */
572 TERM_CMD_ST, /* string-terminator */
573 TERM_CMD_SU, /* scroll-up */
574 TERM_CMD_SUB, /* substitute */
575 TERM_CMD_TBC, /* tab-clear */
576 TERM_CMD_VPA, /* vertical-line-position-absolute */
577 TERM_CMD_VPR, /* vertical-line-position-relative */
578 TERM_CMD_VT, /* vertical-tab */
579 TERM_CMD_XTERM_CLLHP, /* xterm-cursor-lower-left-hp-bugfix */
580 TERM_CMD_XTERM_IHMT, /* xterm-initiate-highlight-mouse-tracking*/
581 TERM_CMD_XTERM_MLHP, /* xterm-memory-lock-hp-bugfix */
582 TERM_CMD_XTERM_MUHP, /* xterm-memory-unlock-hp-bugfix */
583 TERM_CMD_XTERM_RPM, /* xterm-restore-private-mode */
584 TERM_CMD_XTERM_RRV, /* xterm-reset-resource-value */
585 TERM_CMD_XTERM_RTM, /* xterm-reset-title-mode */
586 TERM_CMD_XTERM_SACL1, /* xterm-set-ansi-conformance-level-1 */
587 TERM_CMD_XTERM_SACL2, /* xterm-set-ansi-conformance-level-2 */
588 TERM_CMD_XTERM_SACL3, /* xterm-set-ansi-conformance-level-3 */
589 TERM_CMD_XTERM_SDCS, /* xterm-set-default-character-set */
590 TERM_CMD_XTERM_SGFX, /* xterm-sixel-graphics */
591 TERM_CMD_XTERM_SPM, /* xterm-set-private-mode */
592 TERM_CMD_XTERM_SRV, /* xterm-set-resource-value */
593 TERM_CMD_XTERM_STM, /* xterm-set-title-mode */
594 TERM_CMD_XTERM_SUCS, /* xterm-set-utf8-character-set */
595 TERM_CMD_XTERM_WM, /* xterm-window-management */
596
597 TERM_CMD_CNT
598 };
599
600 enum {
601 /*
602 * Charsets: DEC marks charsets according to "Digital Equ. Corp.".
603 * NRCS marks charsets according to the "National Replacement
604 * Character Sets". ISO marks charsets according to ISO-8859.
605 * The USERDEF charset is special and can be modified by the host.
606 */
607
608 TERM_CHARSET_NONE,
609
610 /* 96-compat charsets */
611 TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
612 TERM_CHARSET_BRITISH_NRCS = TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
613 TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
614 TERM_CHARSET_AMERICAN_NRCS = TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
615 TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL,
616 TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL,
617 TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL,
618 TERM_CHARSET_ISO_LATIN_CYRILLIC,
619
620 TERM_CHARSET_96_CNT,
621
622 /* 94-compat charsets */
623 TERM_CHARSET_DEC_SPECIAL_GRAPHIC = TERM_CHARSET_96_CNT,
624 TERM_CHARSET_DEC_SUPPLEMENTAL,
625 TERM_CHARSET_DEC_TECHNICAL,
626 TERM_CHARSET_CYRILLIC_DEC,
627 TERM_CHARSET_DUTCH_NRCS,
628 TERM_CHARSET_FINNISH_NRCS,
629 TERM_CHARSET_FRENCH_NRCS,
630 TERM_CHARSET_FRENCH_CANADIAN_NRCS,
631 TERM_CHARSET_GERMAN_NRCS,
632 TERM_CHARSET_GREEK_DEC,
633 TERM_CHARSET_GREEK_NRCS,
634 TERM_CHARSET_HEBREW_DEC,
635 TERM_CHARSET_HEBREW_NRCS,
636 TERM_CHARSET_ITALIAN_NRCS,
637 TERM_CHARSET_NORWEGIAN_DANISH_NRCS,
638 TERM_CHARSET_PORTUGUESE_NRCS,
639 TERM_CHARSET_RUSSIAN_NRCS,
640 TERM_CHARSET_SCS_NRCS,
641 TERM_CHARSET_SPANISH_NRCS,
642 TERM_CHARSET_SWEDISH_NRCS,
643 TERM_CHARSET_SWISS_NRCS,
644 TERM_CHARSET_TURKISH_DEC,
645 TERM_CHARSET_TURKISH_NRCS,
646
647 TERM_CHARSET_94_CNT,
648
649 /* special charsets */
650 TERM_CHARSET_USERPREF_SUPPLEMENTAL = TERM_CHARSET_94_CNT,
651
652 TERM_CHARSET_CNT,
653 };
654
655 extern term_charset term_unicode_lower;
656 extern term_charset term_unicode_upper;
657 extern term_charset term_dec_supplemental_graphics;
658 extern term_charset term_dec_special_graphics;
659
660 #define TERM_PARSER_ARG_MAX (16)
661 #define TERM_PARSER_ST_MAX (4096)
662
663 struct term_seq {
664 unsigned int type;
665 unsigned int command;
666 uint32_t terminator;
667 unsigned int intermediates;
668 unsigned int charset;
669 unsigned int n_args;
670 int args[TERM_PARSER_ARG_MAX];
671 unsigned int n_st;
672 char *st;
673 };
674
675 struct term_parser {
676 term_seq seq;
677 size_t st_alloc;
678 unsigned int state;
679
680 bool is_host : 1;
681 };
682
683 int term_parser_new(term_parser **out, bool host);
684 term_parser *term_parser_free(term_parser *parser);
685 int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw);
686
687 #define _term_parser_free_ _cleanup_(term_parser_freep)
688 DEFINE_TRIVIAL_CLEANUP_FUNC(term_parser*, term_parser_free);
689
690 /*
691 * Screens
692 * A term_screen object represents the terminal-side of the communication. It
693 * connects the term-parser and term-pages and handles all required commands.
694 * All state is managed by it.
695 */
696
697 enum {
698 TERM_FLAG_7BIT_MODE = (1U << 0), /* 7bit mode (default: off) */
699 TERM_FLAG_HIDE_CURSOR = (1U << 1), /* hide cursor caret (default: off) */
700 TERM_FLAG_INHIBIT_TPARM = (1U << 2), /* do not send TPARM unrequested (default: off) */
701 TERM_FLAG_NEWLINE_MODE = (1U << 3), /* perform carriage-return on line-feeds (default: off) */
702 TERM_FLAG_ORIGIN_MODE = (1U << 4), /* in origin mode, the cursor is bound by the margins (default: off) */
703 TERM_FLAG_PENDING_WRAP = (1U << 5), /* wrap-around is pending */
704 TERM_FLAG_AUTO_WRAP = (1U << 6), /* auto-wrap mode causes line-wraps at line-ends (default: off) */
705 TERM_FLAG_KEYPAD_MODE = (1U << 7), /* application-keypad mode (default: off) */
706 TERM_FLAG_CURSOR_KEYS = (1U << 8), /* enable application cursor-keys (default: off) */
707 };
708
709 enum {
710 TERM_CONFORMANCE_LEVEL_VT52,
711 TERM_CONFORMANCE_LEVEL_VT100,
712 TERM_CONFORMANCE_LEVEL_VT400,
713 TERM_CONFORMANCE_LEVEL_CNT,
714 };
715
716 typedef int (*term_screen_write_fn) (term_screen *screen, void *userdata, const void *buf, size_t size);
717 typedef int (*term_screen_cmd_fn) (term_screen *screen, void *userdata, unsigned int cmd, const term_seq *seq);
718
719 struct term_screen {
720 unsigned long ref;
721 term_age_t age;
722
723 term_page *page;
724 term_page *page_main;
725 term_page *page_alt;
726 term_history *history;
727 term_history *history_main;
728
729 unsigned int n_tabs;
730 uint8_t *tabs;
731
732 term_utf8 utf8;
733 term_parser *parser;
734
735 term_screen_write_fn write_fn;
736 void *write_fn_data;
737 term_screen_cmd_fn cmd_fn;
738 void *cmd_fn_data;
739
740 unsigned int flags;
741 unsigned int conformance_level;
742 unsigned int cursor_x;
743 unsigned int cursor_y;
744 term_attr attr;
745 term_attr default_attr;
746
747 term_charset **gl;
748 term_charset **gr;
749 term_charset **glt;
750 term_charset **grt;
751 term_charset *g0;
752 term_charset *g1;
753 term_charset *g2;
754 term_charset *g3;
755
756 char *answerback;
757
758 struct {
759 unsigned int cursor_x;
760 unsigned int cursor_y;
761 term_attr attr;
762 term_charset **gl;
763 term_charset **gr;
764 term_charset **glt;
765 term_charset **grt;
766 unsigned int flags;
767 } saved;
768 };
769
770 int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data);
771 term_screen *term_screen_ref(term_screen *screen);
772 term_screen *term_screen_unref(term_screen *screen);
773
774 DEFINE_TRIVIAL_CLEANUP_FUNC(term_screen*, term_screen_unref);
775
776 int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size);
777 int term_screen_feed_keyboard(term_screen *screen, uint32_t keysym, uint32_t ascii, uint32_t ucs4, unsigned int mods);
778 int term_screen_resize(term_screen *screen, unsigned int width, unsigned int height);
779 void term_screen_soft_reset(term_screen *screen);
780 void term_screen_hard_reset(term_screen *screen);
781
782 int term_screen_set_answerback(term_screen *screen, const char *answerback);