chiark / gitweb /
terminal: provide display dimensions to API users
[elogind.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);