chiark / gitweb /
terminal: extend RGB attributes
[elogind.git] / src / libsystemd-terminal / term-internal.h
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #pragma once
23
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include "util.h"
28
29 typedef struct term_char term_char_t;
30 typedef struct term_charbuf term_charbuf_t;
31
32 typedef struct term_color term_color;
33 typedef struct term_attr term_attr;
34 typedef struct term_cell term_cell;
35 typedef struct term_line term_line;
36
37 /*
38  * Miscellaneous
39  * Sundry things and external helpers.
40  */
41
42 int mk_wcwidth(wchar_t ucs4);
43 int mk_wcwidth_cjk(wchar_t ucs4);
44 int mk_wcswidth(const wchar_t *str, size_t len);
45 int mk_wcswidth_cjk(const wchar_t *str, size_t len);
46
47 /*
48  * Ageing
49  * Redrawing terminals is quite expensive. Therefore, we avoid redrawing on
50  * each single modification and mark modified cells instead. This way, we know
51  * which cells to redraw on the next frame. However, a single DIRTY flag is not
52  * enough for double/triple buffered screens, hence, we use an AGE field for
53  * each cell. If the cell is modified, we simply increase the age by one. Each
54  * framebuffer can then remember its last rendered age and request an update of
55  * all newer cells.
56  * TERM_AGE_NULL is special. If used as cell age, the cell must always be
57  * redrawn (forced update). If used as framebuffer age, all cells are drawn.
58  * This way, we can allow integer wrap-arounds.
59  */
60
61 typedef uint64_t term_age_t;
62
63 #define TERM_AGE_NULL 0
64
65 /*
66  * Characters
67  * Each cell in a terminal page contains only a single character. This is
68  * usually a single UCS-4 value. However, Unicode allows combining-characters,
69  * therefore, the number of UCS-4 characters per cell must be unlimited. The
70  * term_char_t object wraps the internal combining char API so it can be
71  * treated as a single object.
72  */
73
74 struct term_char {
75         /* never access this value directly */
76         uint64_t _value;
77 };
78
79 struct term_charbuf {
80         /* 3 bytes + zero-terminator */
81         uint32_t buf[4];
82 };
83
84 #define TERM_CHAR_INIT(_val) ((term_char_t){ ._value = (_val) })
85 #define TERM_CHAR_NULL TERM_CHAR_INIT(0)
86
87 term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4);
88 term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4);
89 term_char_t term_char_dup(term_char_t ch);
90 term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4);
91
92 const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b);
93 unsigned int term_char_lookup_width(term_char_t ch);
94
95 /* true if @ch is TERM_CHAR_NULL, otherwise false */
96 static inline bool term_char_is_null(term_char_t ch) {
97         return ch._value == 0;
98 }
99
100 /* true if @ch is dynamically allocated and needs to be freed */
101 static inline bool term_char_is_allocated(term_char_t ch) {
102         return !term_char_is_null(ch) && !(ch._value & 0x1);
103 }
104
105 /* true if (a == b), otherwise false; this is (a == b), NOT (*a == *b) */
106 static inline bool term_char_same(term_char_t a, term_char_t b) {
107         return a._value == b._value;
108 }
109
110 /* true if (*a == *b), otherwise false; this is implied by (a == b) */
111 static inline bool term_char_equal(term_char_t a, term_char_t b) {
112         const uint32_t *sa, *sb;
113         term_charbuf_t ca, cb;
114         size_t na, nb;
115
116         sa = term_char_resolve(a, &na, &ca);
117         sb = term_char_resolve(b, &nb, &cb);
118         return na == nb && !memcmp(sa, sb, sizeof(*sa) * na);
119 }
120
121 /* free @ch in case it is dynamically allocated */
122 static inline term_char_t term_char_free(term_char_t ch) {
123         if (term_char_is_allocated(ch))
124                 term_char_set(ch, 0);
125
126         return TERM_CHAR_NULL;
127 }
128
129 /* gcc _cleanup_ helpers */
130 #define _term_char_free_ _cleanup_(term_char_freep)
131 static inline void term_char_freep(term_char_t *p) {
132         term_char_free(*p);
133 }
134
135 /*
136  * Attributes
137  * Each cell in a terminal page can have its own set of attributes. These alter
138  * the behavior of the renderer for this single cell. We use term_attr to
139  * specify attributes.
140  * The only non-obvious field is "ccode" for foreground and background colors.
141  * This field contains the terminal color-code in case no full RGB information
142  * was given by the host. It is also required for dynamic color palettes. If it
143  * is set to TERM_CCODE_RGB, the "red", "green" and "blue" fields contain the
144  * full RGB color.
145  */
146
147 enum {
148         /* special color-codes */
149         TERM_CCODE_DEFAULT,                                             /* default foreground/background color */
150         TERM_CCODE_256,                                                 /* 256color code */
151         TERM_CCODE_RGB,                                                 /* color is specified as RGB */
152
153         /* dark color-codes */
154         TERM_CCODE_BLACK,
155         TERM_CCODE_RED,
156         TERM_CCODE_GREEN,
157         TERM_CCODE_YELLOW,
158         TERM_CCODE_BLUE,
159         TERM_CCODE_MAGENTA,
160         TERM_CCODE_CYAN,
161         TERM_CCODE_WHITE,                                               /* technically: light grey */
162
163         /* light color-codes */
164         TERM_CCODE_LIGHT_BLACK          = TERM_CCODE_BLACK + 8,         /* technically: dark grey */
165         TERM_CCODE_LIGHT_RED            = TERM_CCODE_RED + 8,
166         TERM_CCODE_LIGHT_GREEN          = TERM_CCODE_GREEN + 8,
167         TERM_CCODE_LIGHT_YELLOW         = TERM_CCODE_YELLOW + 8,
168         TERM_CCODE_LIGHT_BLUE           = TERM_CCODE_BLUE + 8,
169         TERM_CCODE_LIGHT_MAGENTA        = TERM_CCODE_MAGENTA + 8,
170         TERM_CCODE_LIGHT_CYAN           = TERM_CCODE_CYAN + 8,
171         TERM_CCODE_LIGHT_WHITE          = TERM_CCODE_WHITE + 8,
172
173         TERM_CCODE_CNT,
174 };
175
176 struct term_color {
177         uint8_t ccode;
178         uint8_t c256;
179         uint8_t red;
180         uint8_t green;
181         uint8_t blue;
182 };
183
184 struct term_attr {
185         term_color fg;                          /* foreground color */
186         term_color bg;                          /* background color */
187
188         unsigned int bold : 1;                  /* bold font */
189         unsigned int italic : 1;                /* italic font */
190         unsigned int underline : 1;             /* underline text */
191         unsigned int inverse : 1;               /* inverse fg/bg */
192         unsigned int protect : 1;               /* protect from erase */
193         unsigned int blink : 1;                 /* blink text */
194         unsigned int hidden : 1;                /* hidden */
195 };
196
197 /*
198  * Cells
199  * The term_cell structure respresents a single cell in a terminal page. It
200  * contains the stored character, the age of the cell and all its attributes.
201  */
202
203 struct term_cell {
204         term_char_t ch;         /* stored char or TERM_CHAR_NULL */
205         term_age_t age;         /* cell age or TERM_AGE_NULL */
206         term_attr attr;         /* cell attributes */
207         unsigned int cwidth;    /* cached term_char_lookup_width(cell->ch) */
208 };
209
210 /*
211  * Lines
212  * Instead of storing cells in a 2D array, we store them in an array of
213  * dynamically allocated lines. This way, scrolling can be implemented very
214  * fast without moving any cells at all. Similarly, the scrollback-buffer is
215  * much simpler to implement.
216  * We use term_line to store a single line. It contains an array of cells, a
217  * fill-state which remembers the amount of blanks on the right side, a
218  * separate age just for the line which can overwrite the age for all cells,
219  * and some management data.
220  */
221
222 struct term_line {
223         term_line *lines_next;          /* linked-list for histories */
224         term_line *lines_prev;          /* linked-list for histories */
225
226         unsigned int width;             /* visible width of line */
227         unsigned int n_cells;           /* # of allocated cells */
228         term_cell *cells;               /* cell-array */
229
230         term_age_t age;                 /* line age */
231         unsigned int fill;              /* # of valid cells; starting left */
232 };
233
234 int term_line_new(term_line **out);
235 term_line *term_line_free(term_line *line);
236
237 #define _term_line_free_ _cleanup_(term_line_freep)
238 DEFINE_TRIVIAL_CLEANUP_FUNC(term_line*, term_line_free);
239
240 int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width);
241 void term_line_set_width(term_line *line, unsigned int width);
242 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);
243 void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
244 void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
245 void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age);
246 void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected);
247 void term_line_reset(term_line *line, const term_attr *attr, term_age_t age);
248
249 void term_line_link(term_line *line, term_line **first, term_line **last);
250 void term_line_link_tail(term_line *line, term_line **first, term_line **last);
251 void term_line_unlink(term_line *line, term_line **first, term_line **last);
252
253 #define TERM_LINE_LINK(_line, _head) term_line_link((_line), &(_head)->lines_first, &(_head)->lines_last)
254 #define TERM_LINE_LINK_TAIL(_line, _head) term_line_link_tail((_line), &(_head)->lines_first, &(_head)->lines_last)
255 #define TERM_LINE_UNLINK(_line, _head) term_line_unlink((_line), &(_head)->lines_first, &(_head)->lines_last)