]>
Commit | Line | Data |
---|---|---|
84da4a30 DH |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com> | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #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 | ||
28622e8f DH |
37 | typedef struct term_page term_page; |
38 | typedef struct term_history term_history; | |
39 | ||
1c9633d6 DH |
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 | ||
e432f9e8 DH |
45 | typedef struct term_screen term_screen; |
46 | ||
84da4a30 DH |
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 { | |
952f4b59 DH |
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 | ||
84da4a30 DH |
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 | ||
84da4a30 DH |
183 | TERM_CCODE_CNT, |
184 | }; | |
185 | ||
186 | struct term_color { | |
187 | uint8_t ccode; | |
952f4b59 | 188 | uint8_t c256; |
84da4a30 DH |
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 */ | |
952f4b59 | 204 | unsigned int hidden : 1; /* hidden */ |
84da4a30 DH |
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) | |
28622e8f DH |
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); | |
1c9633d6 DH |
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 */ | |
e432f9e8 DH |
438 | TERM_CMD_DC1, /* device-control-1 or XON */ |
439 | TERM_CMD_DC3, /* device-control-3 or XOFF */ | |
1c9633d6 DH |
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 */ | |
e432f9e8 DH |
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 */ | |
1c9633d6 DH |
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); | |
e432f9e8 DH |
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); |