summaryrefslogtreecommitdiff
path: root/src/libsystemd-terminal/term-internal.h
blob: 8c6a00188c34ccb17ab14e187d5f1f38ce51d019 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/

/***
  This file is part of systemd.

  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>

  systemd is free software; you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

#pragma once

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "term.h"
#include "util.h"

typedef struct term_char term_char_t;
typedef struct term_charbuf term_charbuf_t;

typedef struct term_cell term_cell;
typedef struct term_line term_line;

typedef struct term_page term_page;
typedef struct term_history term_history;

typedef uint32_t term_charset[96];
typedef struct term_state term_state;

/*
 * Miscellaneous
 * Sundry things and external helpers.
 */

int mk_wcwidth(wchar_t ucs4);
int mk_wcwidth_cjk(wchar_t ucs4);
int mk_wcswidth(const wchar_t *str, size_t len);
int mk_wcswidth_cjk(const wchar_t *str, size_t len);

/*
 * Characters
 * Each cell in a terminal page contains only a single character. This is
 * usually a single UCS-4 value. However, Unicode allows combining-characters,
 * therefore, the number of UCS-4 characters per cell must be unlimited. The
 * term_char_t object wraps the internal combining char API so it can be
 * treated as a single object.
 */

struct term_char {
        /* never access this value directly */
        uint64_t _value;
};

struct term_charbuf {
        /* 3 bytes + zero-terminator */
        uint32_t buf[4];
};

#define TERM_CHAR_INIT(_val) ((term_char_t){ ._value = (_val) })
#define TERM_CHAR_NULL TERM_CHAR_INIT(0)

term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4);
term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4);
term_char_t term_char_dup(term_char_t ch);
term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4);

const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b);
unsigned int term_char_lookup_width(term_char_t ch);

/* true if @ch is TERM_CHAR_NULL, otherwise false */
static inline bool term_char_is_null(term_char_t ch) {
        return ch._value == 0;
}

/* true if @ch is dynamically allocated and needs to be freed */
static inline bool term_char_is_allocated(term_char_t ch) {
        return !term_char_is_null(ch) && !(ch._value & 0x1);
}

/* true if (a == b), otherwise false; this is (a == b), NOT (*a == *b) */
static inline bool term_char_same(term_char_t a, term_char_t b) {
        return a._value == b._value;
}

/* true if (*a == *b), otherwise false; this is implied by (a == b) */
static inline bool term_char_equal(term_char_t a, term_char_t b) {
        const uint32_t *sa, *sb;
        term_charbuf_t ca, cb;
        size_t na, nb;

        sa = term_char_resolve(a, &na, &ca);
        sb = term_char_resolve(b, &nb, &cb);
        return na == nb && !memcmp(sa, sb, sizeof(*sa) * na);
}

/* free @ch in case it is dynamically allocated */
static inline term_char_t term_char_free(term_char_t ch) {
        if (term_char_is_allocated(ch))
                term_char_set(ch, 0);

        return TERM_CHAR_NULL;
}

/* gcc _cleanup_ helpers */
#define _term_char_free_ _cleanup_(term_char_freep)
static inline void term_char_freep(term_char_t *p) {
        term_char_free(*p);
}

/*
 * Cells
 * The term_cell structure respresents a single cell in a terminal page. It
 * contains the stored character, the age of the cell and all its attributes.
 */

struct term_cell {
        term_char_t ch;         /* stored char or TERM_CHAR_NULL */
        term_age_t age;         /* cell age or TERM_AGE_NULL */
        term_attr attr;         /* cell attributes */
        unsigned int cwidth;    /* cached term_char_lookup_width(cell->ch) */
};

/*
 * Lines
 * Instead of storing cells in a 2D array, we store them in an array of
 * dynamically allocated lines. This way, scrolling can be implemented very
 * fast without moving any cells at all. Similarly, the scrollback-buffer is
 * much simpler to implement.
 * We use term_line to store a single line. It contains an array of cells, a
 * fill-state which remembers the amount of blanks on the right side, a
 * separate age just for the line which can overwrite the age for all cells,
 * and some management data.
 */

struct term_line {
        term_line *lines_next;          /* linked-list for histories */
        term_line *lines_prev;          /* linked-list for histories */

        unsigned int width;             /* visible width of line */
        unsigned int n_cells;           /* # of allocated cells */
        term_cell *cells;               /* cell-array */

        term_age_t age;                 /* line age */
        unsigned int fill;              /* # of valid cells; starting left */
};

int term_line_new(term_line **out);
term_line *term_line_free(term_line *line);

#define _term_line_free_ _cleanup_(term_line_freep)
DEFINE_TRIVIAL_CLEANUP_FUNC(term_line*, term_line_free);

int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width);
void term_line_set_width(term_line *line, unsigned int width);
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);
void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age);
void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected);
void term_line_reset(term_line *line, const term_attr *attr, term_age_t age);

void term_line_link(term_line *line, term_line **first, term_line **last);
void term_line_link_tail(term_line *line, term_line **first, term_line **last);
void term_line_unlink(term_line *line, term_line **first, term_line **last);

#define TERM_LINE_LINK(_line, _head) term_line_link((_line), &(_head)->lines_first, &(_head)->lines_last)
#define TERM_LINE_LINK_TAIL(_line, _head) term_line_link_tail((_line), &(_head)->lines_first, &(_head)->lines_last)
#define TERM_LINE_UNLINK(_line, _head) term_line_unlink((_line), &(_head)->lines_first, &(_head)->lines_last)

/*
 * Pages
 * A page represents the 2D table containing all cells of a terminal. It stores
 * lines as an array of pointers so scrolling becomes a simple line-shuffle
 * operation.
 * Scrolling is always targeted only at the scroll-region defined via scroll_idx
 * and scroll_num. The fill-state keeps track of the number of touched lines in
 * the scroll-region. @width and @height describe the visible region of the page
 * and are guaranteed to be allocated at all times.
 */

struct term_page {
        term_age_t age;                 /* page age */

        term_line **lines;              /* array of line-pointers */
        term_line **line_cache;         /* cache for temporary operations */
        unsigned int n_lines;           /* # of allocated lines */

        unsigned int width;             /* width of visible area */
        unsigned int height;            /* height of visible area */
        unsigned int scroll_idx;        /* scrolling-region start index */
        unsigned int scroll_num;        /* scrolling-region length in lines */
        unsigned int scroll_fill;       /* # of valid scroll-lines */
};

int term_page_new(term_page **out);
term_page *term_page_free(term_page *page);

#define _term_page_free_ _cleanup_(term_page_freep)
DEFINE_TRIVIAL_CLEANUP_FUNC(term_page*, term_page_free);

term_cell *term_page_get_cell(term_page *page, unsigned int x, unsigned int y);

int term_page_reserve(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age);
void term_page_resize(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age, term_history *history);
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);
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);
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);
void term_page_append_combchar(term_page *page, unsigned int pos_x, unsigned int pos_y, uint32_t ucs4, term_age_t age);
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);
void term_page_reset(term_page *page, const term_attr *attr, term_age_t age);

void term_page_set_scroll_region(term_page *page, unsigned int idx, unsigned int num);
void term_page_scroll_up(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
void term_page_scroll_down(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
void term_page_insert_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
void term_page_delete_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);

/*
 * Histories
 * Scroll-back buffers use term_history objects to store scroll-back lines. A
 * page is independent of the history used. All page operations that modify a
 * history take it as separate argument. You're free to pass NULL at all times
 * if no history should be used.
 * Lines are stored in a linked list as no complex operations are ever done on
 * history lines, besides pushing/poping. Note that history lines do not have a
 * guaranteed minimum length. Any kind of line might be stored there. Missing
 * cells should be cleared to the background color.
 */

struct term_history {
        term_line *lines_first;
        term_line *lines_last;
        unsigned int n_lines;
        unsigned int max_lines;
};

int term_history_new(term_history **out);
term_history *term_history_free(term_history *history);

#define _term_history_free_ _cleanup_(term_history_freep)
DEFINE_TRIVIAL_CLEANUP_FUNC(term_history*, term_history_free);

void term_history_clear(term_history *history);
void term_history_trim(term_history *history, unsigned int max);
void term_history_push(term_history *history, term_line *line);
term_line *term_history_pop(term_history *history, unsigned int reserve_width, const term_attr *attr, term_age_t age);
unsigned int term_history_peek(term_history *history, unsigned int max, unsigned int reserve_width, const term_attr *attr, term_age_t age);

/*
 * Parsers
 * The term_parser object parses control-sequences for both host and terminal
 * side. Based on this parser, there is a set of command-parsers that take a
 * term_seq sequence and returns the command it represents. This is different
 * for host and terminal side so a different set of parsers is provided.
 */

enum {
        TERM_SEQ_NONE,                  /* placeholder, no sequence parsed */

        TERM_SEQ_IGNORE,                /* no-op character */
        TERM_SEQ_GRAPHIC,               /* graphic character */
        TERM_SEQ_CONTROL,               /* control character */
        TERM_SEQ_ESCAPE,                /* escape sequence */
        TERM_SEQ_CSI,                   /* control sequence function */
        TERM_SEQ_DCS,                   /* device control string */
        TERM_SEQ_OSC,                   /* operating system control */

        TERM_SEQ_CNT
};

enum {
        /* these must be kept compatible to (1U << (ch - 0x20)) */

        TERM_SEQ_FLAG_SPACE             = (1U <<  0),   /* char:   */
        TERM_SEQ_FLAG_BANG              = (1U <<  1),   /* char: ! */
        TERM_SEQ_FLAG_DQUOTE            = (1U <<  2),   /* char: " */
        TERM_SEQ_FLAG_HASH              = (1U <<  3),   /* char: # */
        TERM_SEQ_FLAG_CASH              = (1U <<  4),   /* char: $ */
        TERM_SEQ_FLAG_PERCENT           = (1U <<  5),   /* char: % */
        TERM_SEQ_FLAG_AND               = (1U <<  6),   /* char: & */
        TERM_SEQ_FLAG_SQUOTE            = (1U <<  7),   /* char: ' */
        TERM_SEQ_FLAG_POPEN             = (1U <<  8),   /* char: ( */
        TERM_SEQ_FLAG_PCLOSE            = (1U <<  9),   /* char: ) */
        TERM_SEQ_FLAG_MULT              = (1U << 10),   /* char: * */
        TERM_SEQ_FLAG_PLUS              = (1U << 11),   /* char: + */
        TERM_SEQ_FLAG_COMMA             = (1U << 12),   /* char: , */
        TERM_SEQ_FLAG_MINUS             = (1U << 13),   /* char: - */
        TERM_SEQ_FLAG_DOT               = (1U << 14),   /* char: . */
        TERM_SEQ_FLAG_SLASH             = (1U << 15),   /* char: / */

        /* 16-35 is reserved for numbers; unused */

        /* COLON is reserved            = (1U << 26),      char: : */
        /* SEMICOLON is reserved        = (1U << 27),      char: ; */
        TERM_SEQ_FLAG_LT                = (1U << 28),   /* char: < */
        TERM_SEQ_FLAG_EQUAL             = (1U << 29),   /* char: = */
        TERM_SEQ_FLAG_GT                = (1U << 30),   /* char: > */
        TERM_SEQ_FLAG_WHAT              = (1U << 31),   /* char: ? */
};

enum {
        TERM_CMD_NONE,                          /* placeholder */
        TERM_CMD_GRAPHIC,                       /* graphics character */

        TERM_CMD_BEL,                           /* bell */
        TERM_CMD_BS,                            /* backspace */
        TERM_CMD_CBT,                           /* cursor-backward-tabulation */
        TERM_CMD_CHA,                           /* cursor-horizontal-absolute */
        TERM_CMD_CHT,                           /* cursor-horizontal-forward-tabulation */
        TERM_CMD_CNL,                           /* cursor-next-line */
        TERM_CMD_CPL,                           /* cursor-previous-line */
        TERM_CMD_CR,                            /* carriage-return */
        TERM_CMD_CUB,                           /* cursor-backward */
        TERM_CMD_CUD,                           /* cursor-down */
        TERM_CMD_CUF,                           /* cursor-forward */
        TERM_CMD_CUP,                           /* cursor-position */
        TERM_CMD_CUU,                           /* cursor-up */
        TERM_CMD_DA1,                           /* primary-device-attributes */
        TERM_CMD_DA2,                           /* secondary-device-attributes */
        TERM_CMD_DA3,                           /* tertiary-device-attributes */
        TERM_CMD_DC1,                           /* device-control-1 or XON */
        TERM_CMD_DC3,                           /* device-control-3 or XOFF */
        TERM_CMD_DCH,                           /* delete-character */
        TERM_CMD_DECALN,                        /* screen-alignment-pattern */
        TERM_CMD_DECANM,                        /* ansi-mode */
        TERM_CMD_DECBI,                         /* back-index */
        TERM_CMD_DECCARA,                       /* change-attributes-in-rectangular-area */
        TERM_CMD_DECCRA,                        /* copy-rectangular-area */
        TERM_CMD_DECDC,                         /* delete-column */
        TERM_CMD_DECDHL_BH,                     /* double-width-double-height-line: bottom half */
        TERM_CMD_DECDHL_TH,                     /* double-width-double-height-line: top half */
        TERM_CMD_DECDWL,                        /* double-width-single-height-line */
        TERM_CMD_DECEFR,                        /* enable-filter-rectangle */
        TERM_CMD_DECELF,                        /* enable-local-functions */
        TERM_CMD_DECELR,                        /* enable-locator-reporting */
        TERM_CMD_DECERA,                        /* erase-rectangular-area */
        TERM_CMD_DECFI,                         /* forward-index */
        TERM_CMD_DECFRA,                        /* fill-rectangular-area */
        TERM_CMD_DECIC,                         /* insert-column */
        TERM_CMD_DECID,                         /* return-terminal-id */
        TERM_CMD_DECINVM,                       /* invoke-macro */
        TERM_CMD_DECKBD,                        /* keyboard-language-selection */
        TERM_CMD_DECKPAM,                       /* keypad-application-mode */
        TERM_CMD_DECKPNM,                       /* keypad-numeric-mode */
        TERM_CMD_DECLFKC,                       /* local-function-key-control */
        TERM_CMD_DECLL,                         /* load-leds */
        TERM_CMD_DECLTOD,                       /* load-time-of-day */
        TERM_CMD_DECPCTERM,                     /* pcterm-mode */
        TERM_CMD_DECPKA,                        /* program-key-action */
        TERM_CMD_DECPKFMR,                      /* program-key-free-memory-report */
        TERM_CMD_DECRARA,                       /* reverse-attributes-in-rectangular-area */
        TERM_CMD_DECRC,                         /* restore-cursor */
        TERM_CMD_DECREQTPARM,                   /* request-terminal-parameters */
        TERM_CMD_DECRPKT,                       /* report-key-type */
        TERM_CMD_DECRQCRA,                      /* request-checksum-of-rectangular-area */
        TERM_CMD_DECRQDE,                       /* request-display-extent */
        TERM_CMD_DECRQKT,                       /* request-key-type */
        TERM_CMD_DECRQLP,                       /* request-locator-position */
        TERM_CMD_DECRQM_ANSI,                   /* request-mode-ansi */
        TERM_CMD_DECRQM_DEC,                    /* request-mode-dec */
        TERM_CMD_DECRQPKFM,                     /* request-program-key-free-memory */
        TERM_CMD_DECRQPSR,                      /* request-presentation-state-report */
        TERM_CMD_DECRQTSR,                      /* request-terminal-state-report */
        TERM_CMD_DECRQUPSS,                     /* request-user-preferred-supplemental-set */
        TERM_CMD_DECSACE,                       /* select-attribute-change-extent */
        TERM_CMD_DECSASD,                       /* select-active-status-display */
        TERM_CMD_DECSC,                         /* save-cursor */
        TERM_CMD_DECSCA,                        /* select-character-protection-attribute */
        TERM_CMD_DECSCL,                        /* select-conformance-level */
        TERM_CMD_DECSCP,                        /* select-communication-port */
        TERM_CMD_DECSCPP,                       /* select-columns-per-page */
        TERM_CMD_DECSCS,                        /* select-communication-speed */
        TERM_CMD_DECSCUSR,                      /* set-cursor-style */
        TERM_CMD_DECSDDT,                       /* select-disconnect-delay-time */
        TERM_CMD_DECSDPT,                       /* select-digital-printed-data-type */
        TERM_CMD_DECSED,                        /* selective-erase-in-display */
        TERM_CMD_DECSEL,                        /* selective-erase-in-line */
        TERM_CMD_DECSERA,                       /* selective-erase-rectangular-area */
        TERM_CMD_DECSFC,                        /* select-flow-control */
        TERM_CMD_DECSKCV,                       /* set-key-click-volume */
        TERM_CMD_DECSLCK,                       /* set-lock-key-style */
        TERM_CMD_DECSLE,                        /* select-locator-events */
        TERM_CMD_DECSLPP,                       /* set-lines-per-page */
        TERM_CMD_DECSLRM_OR_SC,                 /* set-left-and-right-margins or save-cursor */
        TERM_CMD_DECSMBV,                       /* set-margin-bell-volume */
        TERM_CMD_DECSMKR,                       /* select-modifier-key-reporting */
        TERM_CMD_DECSNLS,                       /* set-lines-per-screen */
        TERM_CMD_DECSPP,                        /* set-port-parameter */
        TERM_CMD_DECSPPCS,                      /* select-pro-printer-character-set */
        TERM_CMD_DECSPRTT,                      /* select-printer-type */
        TERM_CMD_DECSR,                         /* secure-reset */
        TERM_CMD_DECSRFR,                       /* select-refresh-rate */
        TERM_CMD_DECSSCLS,                      /* set-scroll-speed */
        TERM_CMD_DECSSDT,                       /* select-status-display-line-type */
        TERM_CMD_DECSSL,                        /* select-setup-language */
        TERM_CMD_DECST8C,                       /* set-tab-at-every-8-columns */
        TERM_CMD_DECSTBM,                       /* set-top-and-bottom-margins */
        TERM_CMD_DECSTR,                        /* soft-terminal-reset */
        TERM_CMD_DECSTRL,                       /* set-transmit-rate-limit */
        TERM_CMD_DECSWBV,                       /* set-warning-bell-volume */
        TERM_CMD_DECSWL,                        /* single-width-single-height-line */
        TERM_CMD_DECTID,                        /* select-terminal-id */
        TERM_CMD_DECTME,                        /* terminal-mode-emulation */
        TERM_CMD_DECTST,                        /* invoke-confidence-test */
        TERM_CMD_DL,                            /* delete-line */
        TERM_CMD_DSR_ANSI,                      /* device-status-report-ansi */
        TERM_CMD_DSR_DEC,                       /* device-status-report-dec */
        TERM_CMD_ECH,                           /* erase-character */
        TERM_CMD_ED,                            /* erase-in-display */
        TERM_CMD_EL,                            /* erase-in-line */
        TERM_CMD_ENQ,                           /* enquiry */
        TERM_CMD_EPA,                           /* end-of-guarded-area */
        TERM_CMD_FF,                            /* form-feed */
        TERM_CMD_HPA,                           /* horizontal-position-absolute */
        TERM_CMD_HPR,                           /* horizontal-position-relative */
        TERM_CMD_HT,                            /* horizontal-tab */
        TERM_CMD_HTS,                           /* horizontal-tab-set */
        TERM_CMD_HVP,                           /* horizontal-and-vertical-position */
        TERM_CMD_ICH,                           /* insert-character */
        TERM_CMD_IL,                            /* insert-line */
        TERM_CMD_IND,                           /* index */
        TERM_CMD_LF,                            /* line-feed */
        TERM_CMD_LS1R,                          /* locking-shift-1-right */
        TERM_CMD_LS2,                           /* locking-shift-2 */
        TERM_CMD_LS2R,                          /* locking-shift-2-right */
        TERM_CMD_LS3,                           /* locking-shift-3 */
        TERM_CMD_LS3R,                          /* locking-shift-3-right */
        TERM_CMD_MC_ANSI,                       /* media-copy-ansi */
        TERM_CMD_MC_DEC,                        /* media-copy-dec */
        TERM_CMD_NEL,                           /* next-line */
        TERM_CMD_NP,                            /* next-page */
        TERM_CMD_NULL,                          /* null */
        TERM_CMD_PP,                            /* preceding-page */
        TERM_CMD_PPA,                           /* page-position-absolute */
        TERM_CMD_PPB,                           /* page-position-backward */
        TERM_CMD_PPR,                           /* page-position-relative */
        TERM_CMD_RC,                            /* restore-cursor */
        TERM_CMD_REP,                           /* repeat */
        TERM_CMD_RI,                            /* reverse-index */
        TERM_CMD_RIS,                           /* reset-to-initial-state */
        TERM_CMD_RM_ANSI,                       /* reset-mode-ansi */
        TERM_CMD_RM_DEC,                        /* reset-mode-dec */
        TERM_CMD_S7C1T,                         /* set-7bit-c1-terminal */
        TERM_CMD_S8C1T,                         /* set-8bit-c1-terminal */
        TERM_CMD_SCS,                           /* select-character-set */
        TERM_CMD_SD,                            /* scroll-down */
        TERM_CMD_SGR,                           /* select-graphics-rendition */
        TERM_CMD_SI,                            /* shift-in */
        TERM_CMD_SM_ANSI,                       /* set-mode-ansi */
        TERM_CMD_SM_DEC,                        /* set-mode-dec */
        TERM_CMD_SO,                            /* shift-out */
        TERM_CMD_SPA,                           /* start-of-protected-area */
        TERM_CMD_SS2,                           /* single-shift-2 */
        TERM_CMD_SS3,                           /* single-shift-3 */
        TERM_CMD_ST,                            /* string-terminator */
        TERM_CMD_SU,                            /* scroll-up */
        TERM_CMD_SUB,                           /* substitute */
        TERM_CMD_TBC,                           /* tab-clear */
        TERM_CMD_VPA,                           /* vertical-line-position-absolute */
        TERM_CMD_VPR,                           /* vertical-line-position-relative */
        TERM_CMD_VT,                            /* vertical-tab */
        TERM_CMD_XTERM_CLLHP,                   /* xterm-cursor-lower-left-hp-bugfix */
        TERM_CMD_XTERM_IHMT,                    /* xterm-initiate-highlight-mouse-tracking */
        TERM_CMD_XTERM_MLHP,                    /* xterm-memory-lock-hp-bugfix */
        TERM_CMD_XTERM_MUHP,                    /* xterm-memory-unlock-hp-bugfix */
        TERM_CMD_XTERM_RPM,                     /* xterm-restore-private-mode */
        TERM_CMD_XTERM_RRV,                     /* xterm-reset-resource-value */
        TERM_CMD_XTERM_RTM,                     /* xterm-reset-title-mode */
        TERM_CMD_XTERM_SACL1,                   /* xterm-set-ansi-conformance-level-1 */
        TERM_CMD_XTERM_SACL2,                   /* xterm-set-ansi-conformance-level-2 */
        TERM_CMD_XTERM_SACL3,                   /* xterm-set-ansi-conformance-level-3 */
        TERM_CMD_XTERM_SDCS,                    /* xterm-set-default-character-set */
        TERM_CMD_XTERM_SGFX,                    /* xterm-sixel-graphics */
        TERM_CMD_XTERM_SPM,                     /* xterm-set-private-mode */
        TERM_CMD_XTERM_SRV,                     /* xterm-set-resource-value */
        TERM_CMD_XTERM_STM,                     /* xterm-set-title-mode */
        TERM_CMD_XTERM_SUCS,                    /* xterm-set-utf8-character-set */
        TERM_CMD_XTERM_WM,                      /* xterm-window-management */

        TERM_CMD_CNT
};

enum {
        /*
         * Charsets: DEC marks charsets according to "Digital Equ. Corp.".
         *           NRCS marks charsets according to the "National Replacement
         *           Character Sets". ISO marks charsets according to ISO-8859.
         * The USERDEF charset is special and can be modified by the host.
         */

        TERM_CHARSET_NONE,

        /* 96-compat charsets */
        TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
        TERM_CHARSET_BRITISH_NRCS = TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
        TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
        TERM_CHARSET_AMERICAN_NRCS = TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
        TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL,
        TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL,
        TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL,
        TERM_CHARSET_ISO_LATIN_CYRILLIC,

        TERM_CHARSET_96_CNT,

        /* 94-compat charsets */
        TERM_CHARSET_DEC_SPECIAL_GRAPHIC = TERM_CHARSET_96_CNT,
        TERM_CHARSET_DEC_SUPPLEMENTAL,
        TERM_CHARSET_DEC_TECHNICAL,
        TERM_CHARSET_CYRILLIC_DEC,
        TERM_CHARSET_DUTCH_NRCS,
        TERM_CHARSET_FINNISH_NRCS,
        TERM_CHARSET_FRENCH_NRCS,
        TERM_CHARSET_FRENCH_CANADIAN_NRCS,
        TERM_CHARSET_GERMAN_NRCS,
        TERM_CHARSET_GREEK_DEC,
        TERM_CHARSET_GREEK_NRCS,
        TERM_CHARSET_HEBREW_DEC,
        TERM_CHARSET_HEBREW_NRCS,
        TERM_CHARSET_ITALIAN_NRCS,
        TERM_CHARSET_NORWEGIAN_DANISH_NRCS,
        TERM_CHARSET_PORTUGUESE_NRCS,
        TERM_CHARSET_RUSSIAN_NRCS,
        TERM_CHARSET_SCS_NRCS,
        TERM_CHARSET_SPANISH_NRCS,
        TERM_CHARSET_SWEDISH_NRCS,
        TERM_CHARSET_SWISS_NRCS,
        TERM_CHARSET_TURKISH_DEC,
        TERM_CHARSET_TURKISH_NRCS,

        TERM_CHARSET_94_CNT,

        /* special charsets */
        TERM_CHARSET_USERPREF_SUPPLEMENTAL = TERM_CHARSET_94_CNT,

        TERM_CHARSET_CNT,
};

extern term_charset term_unicode_lower;
extern term_charset term_unicode_upper;
extern term_charset term_dec_supplemental_graphics;
extern term_charset term_dec_special_graphics;

#define TERM_PARSER_ARG_MAX (16)
#define TERM_PARSER_ST_MAX (4096)

struct term_seq {
        unsigned int type;
        unsigned int command;
        uint32_t terminator;
        unsigned int intermediates;
        unsigned int charset;
        unsigned int n_args;
        int args[TERM_PARSER_ARG_MAX];
        unsigned int n_st;
        char *st;
};

struct term_parser {
        term_seq seq;
        size_t st_alloc;
        unsigned int state;

        bool is_host : 1;
};

/*
 * Screens
 * A term_screen object represents the terminal-side of the communication. It
 * connects the term-parser and term-pages and handles all required commands.
 * All state is managed by it.
 */

enum {
        TERM_FLAG_7BIT_MODE                     = (1U << 0),    /* 7bit mode (default: on) */
        TERM_FLAG_HIDE_CURSOR                   = (1U << 1),    /* hide cursor caret (default: off) */
        TERM_FLAG_INHIBIT_TPARM                 = (1U << 2),    /* do not send TPARM unrequested (default: off) */
        TERM_FLAG_NEWLINE_MODE                  = (1U << 3),    /* perform carriage-return on line-feeds (default: off) */
        TERM_FLAG_PENDING_WRAP                  = (1U << 4),    /* wrap-around is pending */
        TERM_FLAG_KEYPAD_MODE                   = (1U << 5),    /* application-keypad mode (default: off) */
        TERM_FLAG_CURSOR_KEYS                   = (1U << 6),    /* enable application cursor-keys (default: off) */
};

enum {
        TERM_CONFORMANCE_LEVEL_VT52,
        TERM_CONFORMANCE_LEVEL_VT100,
        TERM_CONFORMANCE_LEVEL_VT400,
        TERM_CONFORMANCE_LEVEL_CNT,
};

struct term_state {
        unsigned int cursor_x;
        unsigned int cursor_y;
        term_attr attr;
        term_charset **gl;
        term_charset **gr;
        term_charset **glt;
        term_charset **grt;

        bool auto_wrap : 1;
        bool origin_mode : 1;
};

struct term_screen {
        unsigned long ref;
        term_age_t age;

        term_page *page;
        term_page *page_main;
        term_page *page_alt;
        term_history *history;
        term_history *history_main;

        unsigned int n_tabs;
        uint8_t *tabs;

        term_utf8 utf8;
        term_parser *parser;

        term_screen_write_fn write_fn;
        void *write_fn_data;
        term_screen_cmd_fn cmd_fn;
        void *cmd_fn_data;

        unsigned int flags;
        unsigned int conformance_level;
        term_attr default_attr;

        term_charset *g0;
        term_charset *g1;
        term_charset *g2;
        term_charset *g3;

        char *answerback;

        term_state state;
        term_state saved;
        term_state saved_alt;
};