| /*-*- 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 "util.h" |
| |
| typedef struct term_char term_char_t; |
| typedef struct term_charbuf term_charbuf_t; |
| |
| typedef struct term_color term_color; |
| typedef struct term_attr term_attr; |
| 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 struct term_utf8 term_utf8; |
| typedef struct term_seq term_seq; |
| typedef struct term_parser term_parser; |
| typedef uint32_t term_charset[96]; |
| |
| typedef struct term_screen term_screen; |
| |
| /* |
| * 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); |
| |
| /* |
| * Ageing |
| * Redrawing terminals is quite expensive. Therefore, we avoid redrawing on |
| * each single modification and mark modified cells instead. This way, we know |
| * which cells to redraw on the next frame. However, a single DIRTY flag is not |
| * enough for double/triple buffered screens, hence, we use an AGE field for |
| * each cell. If the cell is modified, we simply increase the age by one. Each |
| * framebuffer can then remember its last rendered age and request an update of |
| * all newer cells. |
| * TERM_AGE_NULL is special. If used as cell age, the cell must always be |
| * redrawn (forced update). If used as framebuffer age, all cells are drawn. |
| * This way, we can allow integer wrap-arounds. |
| */ |
| |
| typedef uint64_t term_age_t; |
| |
| #define TERM_AGE_NULL 0 |
| |
| /* |
| * 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); |
| } |
| |
| /* |
| * Attributes |
| * Each cell in a terminal page can have its own set of attributes. These alter |
| * the behavior of the renderer for this single cell. We use term_attr to |
| * specify attributes. |
| * The only non-obvious field is "ccode" for foreground and background colors. |
| * This field contains the terminal color-code in case no full RGB information |
| * was given by the host. It is also required for dynamic color palettes. If it |
| * is set to TERM_CCODE_RGB, the "red", "green" and "blue" fields contain the |
| * full RGB color. |
| */ |
| |
| enum { |
| /* special color-codes */ |
| TERM_CCODE_DEFAULT, /* default foreground/background color */ |
| TERM_CCODE_256, /* 256color code */ |
| TERM_CCODE_RGB, /* color is specified as RGB */ |
| |
| /* dark color-codes */ |
| TERM_CCODE_BLACK, |
| TERM_CCODE_RED, |
| TERM_CCODE_GREEN, |
| TERM_CCODE_YELLOW, |
| TERM_CCODE_BLUE, |
| TERM_CCODE_MAGENTA, |
| TERM_CCODE_CYAN, |
| TERM_CCODE_WHITE, /* technically: light grey */ |
| |
| /* light color-codes */ |
| TERM_CCODE_LIGHT_BLACK = TERM_CCODE_BLACK + 8, /* technically: dark grey */ |
| TERM_CCODE_LIGHT_RED = TERM_CCODE_RED + 8, |
| TERM_CCODE_LIGHT_GREEN = TERM_CCODE_GREEN + 8, |
| TERM_CCODE_LIGHT_YELLOW = TERM_CCODE_YELLOW + 8, |
| TERM_CCODE_LIGHT_BLUE = TERM_CCODE_BLUE + 8, |
| TERM_CCODE_LIGHT_MAGENTA = TERM_CCODE_MAGENTA + 8, |
| TERM_CCODE_LIGHT_CYAN = TERM_CCODE_CYAN + 8, |
| TERM_CCODE_LIGHT_WHITE = TERM_CCODE_WHITE + 8, |
| |
| TERM_CCODE_CNT, |
| }; |
| |
| struct term_color { |
| uint8_t ccode; |
| uint8_t c256; |
| uint8_t red; |
| uint8_t green; |
| uint8_t blue; |
| }; |
| |
| struct term_attr { |
| term_color fg; /* foreground color */ |
| term_color bg; /* background color */ |
| |
| unsigned int bold : 1; /* bold font */ |
| unsigned int italic : 1; /* italic font */ |
| unsigned int underline : 1; /* underline text */ |
| unsigned int inverse : 1; /* inverse fg/bg */ |
| unsigned int protect : 1; /* protect from erase */ |
| unsigned int blink : 1; /* blink text */ |
| unsigned int hidden : 1; /* hidden */ |
| }; |
| |
| /* |
| * 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); |
| |
| /* |
| * UTF-8 |
| * The UTF-decoder and encoder are adjusted for terminals and provide proper |
| * fallbacks for invalid UTF-8. In terminals it's quite usual to use fallbacks |
| * instead of rejecting invalid input. This way, old legacy applications still |
| * work (this is especially important for 7bit/ASCII DEC modes). |
| */ |
| |
| struct term_utf8 { |
| uint32_t chars[5]; |
| uint32_t ucs4; |
| |
| unsigned int i_bytes : 3; |
| unsigned int n_bytes : 3; |
| unsigned int valid : 1; |
| }; |
| |
| size_t term_utf8_encode(char *out_utf8, uint32_t g); |
| const uint32_t *term_utf8_decode(term_utf8 *p, size_t *out_len, char c); |
| |
| /* |
| * 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; |
| }; |
| |
| int term_parser_new(term_parser **out, bool host); |
| term_parser *term_parser_free(term_parser *parser); |
| int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw); |
| |
| #define _term_parser_free_ _cleanup_(term_parser_freep) |
| DEFINE_TRIVIAL_CLEANUP_FUNC(term_parser*, term_parser_free); |
| |
| /* |
| * 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: off) */ |
| 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_ORIGIN_MODE = (1U << 4), /* in origin mode, the cursor is bound by the margins (default: off) */ |
| TERM_FLAG_PENDING_WRAP = (1U << 5), /* wrap-around is pending */ |
| TERM_FLAG_AUTO_WRAP = (1U << 6), /* auto-wrap mode causes line-wraps at line-ends (default: off) */ |
| TERM_FLAG_KEYPAD_MODE = (1U << 7), /* application-keypad mode (default: off) */ |
| TERM_FLAG_CURSOR_KEYS = (1U << 8), /* enable application cursor-keys (default: off) */ |
| }; |
| |
| enum { |
| TERM_CONFORMANCE_LEVEL_VT52, |
| TERM_CONFORMANCE_LEVEL_VT100, |
| TERM_CONFORMANCE_LEVEL_VT400, |
| TERM_CONFORMANCE_LEVEL_CNT, |
| }; |
| |
| typedef int (*term_screen_write_fn) (term_screen *screen, void *userdata, const void *buf, size_t size); |
| typedef int (*term_screen_cmd_fn) (term_screen *screen, void *userdata, unsigned int cmd, const term_seq *seq); |
| |
| 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; |
| unsigned int cursor_x; |
| unsigned int cursor_y; |
| term_attr attr; |
| term_attr default_attr; |
| |
| term_charset **gl; |
| term_charset **gr; |
| term_charset **glt; |
| term_charset **grt; |
| term_charset *g0; |
| term_charset *g1; |
| term_charset *g2; |
| term_charset *g3; |
| |
| char *answerback; |
| |
| struct { |
| unsigned int cursor_x; |
| unsigned int cursor_y; |
| term_attr attr; |
| term_charset **gl; |
| term_charset **gr; |
| term_charset **glt; |
| term_charset **grt; |
| unsigned int flags; |
| } saved; |
| }; |
| |
| 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); |
| term_screen *term_screen_ref(term_screen *screen); |
| term_screen *term_screen_unref(term_screen *screen); |
| |
| DEFINE_TRIVIAL_CLEANUP_FUNC(term_screen*, term_screen_unref); |
| |
| int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size); |
| int term_screen_feed_keyboard(term_screen *screen, uint32_t keysym, uint32_t ascii, uint32_t ucs4, unsigned int mods); |
| int term_screen_resize(term_screen *screen, unsigned int width, unsigned int height); |
| void term_screen_soft_reset(term_screen *screen); |
| void term_screen_hard_reset(term_screen *screen); |
| |
| int term_screen_set_answerback(term_screen *screen, const char *answerback); |