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