Bug 1082: mem_free_set in abort_preloading
[elinks.git] / src / terminal / screen.c
bloba14c25f6c0e66eee2f5b452efb0fede1c72fd491
1 /** Terminal screen drawing routines.
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdlib.h>
9 #include <string.h>
11 #include "elinks.h"
13 #include "config/options.h"
14 #include "intl/charsets.h"
15 #include "main/module.h"
16 #include "osdep/ascii.h"
17 #include "osdep/osdep.h"
18 #include "terminal/color.h"
19 #include "terminal/draw.h"
20 #include "terminal/hardio.h"
21 #include "terminal/kbd.h"
22 #include "terminal/screen.h"
23 #include "terminal/terminal.h"
24 #include "util/conv.h"
25 #include "util/error.h"
26 #include "util/memory.h"
27 #include "util/string.h"
30 /* TODO: We must use termcap/terminfo if available! --pasky
31 * Please mention ELinks bug 96 in commit logs. --KON */
33 /** Mapping from (enum ::border_char - 0xB0) to ASCII characters. */
34 const unsigned char frame_dumb[48] = " ||||++||++++++--|-+||++--|-+----++++++++ ";
36 /** Mapping from (enum ::border_char - 0xB0) to VT100 line-drawing
37 * characters. */
38 static const unsigned char frame_vt100[48] = "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla ";
40 /** Mapping from (enum ::border_char - 0xB0) to VT100 line-drawing
41 * characters encoded in CP437.
42 * When UTF-8 I/O is enabled, ELinks uses this array instead of
43 * ::frame_vt100[], and converts the characters from CP437 to UTF-8. */
44 static const unsigned char frame_vt100_u[48] = {
45 177, 177, 177, 179, 180, 180, 180, 191,
46 191, 180, 179, 191, 217, 217, 217, 191,
47 192, 193, 194, 195, 196, 197, 195, 195,
48 192, 218, 193, 194, 195, 196, 197, 193,
49 193, 194, 194, 192, 192, 218, 218, 197,
50 197, 217, 218, 177, 32, 32, 32, 32
53 /** Mapping from (enum ::border_char - 0xB0) to obsolete FreeBSD ACS
54 * graphics.
56 * This is for FreeBSD fonts that place graphics characters in the
57 * 0x80...0x9F range, which ISO 8859 does not use. The characters are
58 * supposed to be sorted according to the codes used in VT100 or in
59 * the terminfo "acsc" capability:
61 * @verbatim
62 * 0x80 U+2588 '0' ACS_BLOCK
63 * 0x81 U+25C6 '`' ACS_DIAMOND
64 * 0x82 U+2592 'a' ACS_CKBOARD
65 * 0x83 U+2409 'b' -
66 * 0x84 U+240C 'c' -
67 * 0x85 U+240D 'd' -
68 * 0x86 U+240A 'e' -
69 * 0x87 U+00B0 'f' ACS_DEGREE
70 * 0x88 U+00B1 'g' ACS_PLMINUS
71 * 0x89 U+2424 'h' -
72 * 0x8A U+240B 'i' -
73 * 0x8B U+2518 'j' ACS_LRCORNER
74 * 0x8C U+2510 'k' ACS_URCORNER
75 * 0x8D U+250C 'l' ACS_ULCORNER
76 * 0x8E U+2514 'm' ACS_LLCORNER
77 * 0x8F U+253C 'n' ACS_PLUS
78 * 0x90 U+23BA 'o' ACS_S1
79 * 0x91 U+23BB 'p' ACS_S3
80 * 0x92 U+2500 'q' ACS_HLINE
81 * 0x93 U+23BC 'r' ACS_S7
82 * 0x94 U+23BD 's' ACS_S9
83 * 0x95 U+251C 't' ACS_LTEE
84 * 0x96 U+2524 'u' ACS_RTEE
85 * 0x97 U+2534 'v' ACS_BTEE
86 * 0x98 U+252C 'w' ACS_TTEE
87 * 0x99 U+2502 'x' ACS_VLINE
88 * 0x9A U+2264 'y' ACS_LEQUAL
89 * 0x9B U+2265 'z' ACS_GEQUAL
90 * 0x9C U+03C0 '{' ACS_PI
91 * 0x9D U+2260 '|' ACS_NEQUAL
92 * 0x9E U+00A3 '}' ACS_STERLING
93 * 0x9F U+00B7 '~' ACS_BULLET
94 * @endverbatim
96 * (Ncurses 5.5 defines ACS_BOARD using 'h' and ACS_LANTERN using 'i',
97 * but those are not the characters meant above.)
99 * In FreeBSD CVS, src/share/syscons/fonts/iso-8x16.fnt revision 1.1
100 * includes these characters, except it has a space at 0x80. In
101 * revision 1.2 however, all the characters not defined by ISO 8859-1
102 * have been blanked out. This change was made on 2001-11-22 and
103 * included in FreeBSD 4.6.0, which was then released in June 2002.
104 * Yet, support for these characters was added to Links on 2003-11-18
105 * and to ELinks on 2003-12-07, so perhaps we should keep it for now. */
106 static const unsigned char frame_freebsd[48] = {
107 130, 138, 128, 153, 150, 150, 150, 140,
108 140, 150, 153, 140, 139, 139, 139, 140,
109 142, 151, 152, 149, 146, 143, 149, 149,
110 142, 141, 151, 152, 149, 146, 143, 151,
111 151, 152, 152, 142, 142, 141, 141, 143,
112 143, 139, 141, 128, 128, 128, 128, 128,
115 /** Mapping from (enum ::border_char - 0xB0) to obsolete FreeBSD ACS
116 * graphics encoded in CP437.
117 * When UTF-8 I/O is enabled, ELinks uses this array instead of
118 * ::frame_freebsd[], and converts the characters from CP437 to UTF-8.
120 * Derived from ::frame_freebsd[] by converting the characters to
121 * Unicode and back to CP437. frame_freebsd[1] = 138 = 0x8a = U+240B
122 * SYMBOL FOR VERTICAL TABULATION does not exist in CP437, so we
123 * substitute U+2592 MEDIUM SHADE. */
124 static const unsigned char frame_freebsd_u[48] = {
125 177, 177, 219, 179, 180, 180, 180, 191,
126 191, 180, 179, 191, 217, 217, 217, 191,
127 192, 193, 194, 195, 196, 197, 195, 195,
128 192, 218, 193, 194, 195, 196, 197, 193,
129 193, 194, 194, 192, 192, 218, 218, 197,
130 197, 217, 218, 219, 219, 219, 219, 219,
133 /** Mapping from (enum ::border_char - 0xB0) to KOI8-R. */
134 static const unsigned char frame_koi[48] = {
135 144, 145, 146, 129, 135, 178, 180, 167,
136 166, 181, 161, 168, 174, 173, 172, 131,
137 132, 137, 136, 134, 128, 138, 175, 176,
138 171, 165, 187, 184, 177, 160, 190, 185,
139 186, 182, 183, 170, 169, 162, 164, 189,
140 188, 133, 130, 141, 140, 142, 143, 139,
143 /** Mapping from (enum ::border_char - 0xB0) to CP850 or CP852. Most of
144 * this table is just 0xB0 + @<index in table>, because these codepages
145 * are quite similar to CP437. However, they lack some line-drawing
146 * characters, so we must use others instead. */
147 static const unsigned char frame_restrict[48] = {
148 176, 177, 178, 179, 180, 179, 186, 186,
149 205, 185, 186, 187, 188, 186, 205, 191,
150 192, 193, 194, 195, 196, 197, 179, 186,
151 200, 201, 202, 203, 204, 205, 206, 205,
152 196, 205, 196, 186, 205, 205, 186, 186,
153 179, 217, 218, 219, 220, 221, 222, 223,
156 #define TERM_STRING(str) INIT_STRING(str, sizeof(str) - 1)
158 /** Like add_string_to_string() but has fewer checks to slow it down. */
159 #define add_term_string(str, tstr) \
160 add_bytes_to_string(str, (tstr).source, (tstr).length)
162 /** Frame begin/end sequences that switch fonts with ECMA-48 SGR.
163 * ECMA-48: CSI Ps... 06/13 = SGR - SELECT GRAPHIC RENDITION
164 * - Ps = 10 = primary (default) font
165 * - Ps = 11 = first alternative font */
166 static const struct string m11_hack_frame_seqs[] = {
167 /* end border: */ TERM_STRING("\033[10m"),
168 /* begin border: */ TERM_STRING("\033[11m"),
171 /** Frame begin/end sequences for VT100. */
172 static const struct string vt100_frame_seqs[] = {
173 /* end border: */ TERM_STRING("\x0f"),
174 /* begin border: */ TERM_STRING("\x0e"),
177 /** Italic begin/end sequences using ECMA-48 SGR.
178 * ECMA-48: CSI Ps... 06/13 = SGR - SELECT GRAPHIC RENDITION
179 * - Ps = 3 = italicized
180 * - Ps = 20 = Fraktur (Gothic)
181 * - Ps = 23 = not italicized, not fraktur */
182 static const struct string italic_seqs[] = {
183 /* end italics: */ TERM_STRING("\033[23m"),
184 /* begin italics: */ TERM_STRING("\033[3m"),
187 /** Underline begin/end sequences using ECMA-48 SGR.
188 * ECMA-48: CSI Ps... 06/13 = SGR - SELECT GRAPHIC RENDITION
189 * - Ps = 4 = singly underlined
190 * - Ps = 21 = doubly underlined
191 * - Ps = 24 = not underlined (neither singly nor doubly) */
192 static const struct string underline_seqs[] = {
193 /* end underline: */ TERM_STRING("\033[24m"),
194 /* begin underline: */ TERM_STRING("\033[4m"),
197 /* elinks --dump has a separate implementation of color-changing
198 * sequences and does not use these. */
199 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
200 static const struct string color256_seqs[] = {
201 /* foreground: */ TERM_STRING("\033[0;38;5;%dm"),
202 /* background: */ TERM_STRING("\033[48;5;%dm"),
205 static const struct string fbterm_color256_seqs[] = {
206 /* foreground: */ TERM_STRING("\033[m\033[1;%d}"),
207 /* background: */ TERM_STRING("\033[2;%d}"),
209 #endif
211 /** Used in @c add_char*() and @c redraw_screen() to reduce the logic.
212 * It is updated from terminal._template_.* using option.change_hook.
214 * @todo TODO: termcap/terminfo can maybe gradually be introduced via
215 * this structure. We'll see. --jonas */
216 struct screen_driver {
217 LIST_HEAD(struct screen_driver);
219 /** The terminal._template_.type. Together with the #name member they
220 * uniquely identify the screen_driver. */
221 enum term_mode_type type;
223 /** set_screen_driver_opt() sets these. */
224 struct screen_driver_opt {
225 /** Charsets when doing UTF-8 I/O.
226 * [0] is the common charset and [1] is the frame charset.
227 * Test whether to use UTF-8 I/O using the use_utf8_io() macro. */
228 int charsets[2];
230 /** The frame translation table. May be NULL. */
231 const unsigned char *frame;
233 /** The frame mode setup and teardown sequences. May be NULL. */
234 const struct string *frame_seqs;
236 /** The italic mode setup and teardown sequences. May be NULL. */
237 const struct string *italic;
239 /** The underline mode setup and teardown sequences. May be NULL. */
240 const struct string *underline;
242 /** The color mode */
243 enum color_mode color_mode;
245 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
246 const struct string *color256_seqs;
247 #endif
248 /** These are directly derived from the terminal options. */
249 unsigned int transparent:1;
251 #ifdef CONFIG_UTF8
252 /* Whether the charset of the terminal is UTF-8. This
253 * is the same as is_cp_utf8(charsets[0]), except the
254 * latter might crash if UTF-8 I/O is disabled. */
255 unsigned int utf8_cp:1;
256 #endif /* CONFIG_UTF8 */
258 #ifdef CONFIG_COMBINE
259 /* Whether the terminal supports combining characters. */
260 unsigned int combine:1;
261 #endif /* CONFIG_COMBINE */
262 } opt;
264 /* The terminal._template_ name. */
265 unsigned char name[1]; /* XXX: Keep last! */
268 /** Default options for ::TERM_DUMB. */
269 static const struct screen_driver_opt dumb_screen_driver_opt = {
270 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
271 /* frame: */ frame_dumb,
272 /* frame_seqs: */ NULL,
273 /* italic: */ italic_seqs,
274 /* underline: */ underline_seqs,
275 /* color_mode: */ COLOR_MODE_16,
276 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
277 /* color256_seqs: */ color256_seqs,
278 #endif
279 /* transparent: */ 1,
280 #ifdef CONFIG_UTF8
281 /* utf8_cp: */ 0,
282 #endif /* CONFIG_UTF8 */
283 #ifdef CONFIG_COMBINE
284 /* combine */ 0,
285 #endif /* CONFIG_COMBINE */
288 /** Default options for ::TERM_VT100. */
289 static const struct screen_driver_opt vt100_screen_driver_opt = {
290 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
291 /* frame: */ frame_vt100,
292 /* frame_seqs: */ vt100_frame_seqs,
293 /* italic: */ italic_seqs,
294 /* underline: */ underline_seqs,
295 /* color_mode: */ COLOR_MODE_16,
296 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
297 /* color256_seqs: */ color256_seqs,
298 #endif
299 /* transparent: */ 1,
300 #ifdef CONFIG_UTF8
301 /* utf8_cp: */ 0,
302 #endif /* CONFIG_UTF8 */
303 #ifdef CONFIG_COMBINE
304 /* combine */ 0,
305 #endif /* CONFIG_COMBINE */
308 /** Default options for ::TERM_LINUX. */
309 static const struct screen_driver_opt linux_screen_driver_opt = {
310 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
311 /* frame: */ NULL, /* No restrict_852 */
312 /* frame_seqs: */ NULL, /* No m11_hack */
313 /* italic: */ italic_seqs,
314 /* underline: */ underline_seqs,
315 /* color_mode: */ COLOR_MODE_16,
316 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
317 /* color256_seqs: */ color256_seqs,
318 #endif
319 /* transparent: */ 1,
320 #ifdef CONFIG_UTF8
321 /* utf8_cp: */ 0,
322 #endif /* CONFIG_UTF8 */
323 #ifdef CONFIG_COMBINE
324 /* combine */ 0,
325 #endif /* CONFIG_COMBINE */
328 /** Default options for ::TERM_KOI8. */
329 static const struct screen_driver_opt koi8_screen_driver_opt = {
330 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
331 /* frame: */ frame_koi,
332 /* frame_seqs: */ NULL,
333 /* italic: */ italic_seqs,
334 /* underline: */ underline_seqs,
335 /* color_mode: */ COLOR_MODE_16,
336 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
337 /* color256_seqs: */ color256_seqs,
338 #endif
339 /* transparent: */ 1,
340 #ifdef CONFIG_UTF8
341 /* utf8_cp: */ 0,
342 #endif /* CONFIG_UTF8 */
343 #ifdef CONFIG_COMBINE
344 /* combine */ 0,
345 #endif /* CONFIG_COMBINE */
348 /** Default options for ::TERM_FREEBSD. */
349 static const struct screen_driver_opt freebsd_screen_driver_opt = {
350 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
351 /* frame: */ frame_freebsd,
352 /* frame_seqs: */ NULL, /* No m11_hack */
353 /* italic: */ italic_seqs,
354 /* underline: */ underline_seqs,
355 /* color_mode: */ COLOR_MODE_16,
356 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
357 /* color256_seqs: */ color256_seqs,
358 #endif
359 /* transparent: */ 1,
360 #ifdef CONFIG_UTF8
361 /* utf8_cp: */ 0,
362 #endif /* CONFIG_UTF8 */
363 #ifdef CONFIG_COMBINE
364 /* combine */ 0,
365 #endif /* CONFIG_COMBINE */
368 /** Default options for ::TERM_FBTERM. */
369 static const struct screen_driver_opt fbterm_screen_driver_opt = {
370 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
371 /* frame: */ NULL,
372 /* frame_seqs: */ NULL, /* No m11_hack */
373 /* italic: */ italic_seqs,
374 /* underline: */ underline_seqs,
375 /* color_mode: */ COLOR_MODE_16,
376 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
377 /* color256_seqs: */ fbterm_color256_seqs,
378 #endif
379 /* transparent: */ 1,
380 #ifdef CONFIG_UTF8
381 /* utf8_cp: */ 0,
382 #endif /* CONFIG_UTF8 */
383 #ifdef CONFIG_COMBINE
384 /* combine */ 0,
385 #endif /* CONFIG_COMBINE */
388 /** Default options for all the different types of terminals.
389 * XXX: Keep in sync with enum term_mode_type. */
390 static const struct screen_driver_opt *const screen_driver_opts[] = {
391 /* TERM_DUMB: */ &dumb_screen_driver_opt,
392 /* TERM_VT100: */ &vt100_screen_driver_opt,
393 /* TERM_LINUX: */ &linux_screen_driver_opt,
394 /* TERM_KOI8: */ &koi8_screen_driver_opt,
395 /* TERM_FREEBSD: */ &freebsd_screen_driver_opt,
396 /* TERM_FBTERM: */ &fbterm_screen_driver_opt,
399 #define use_utf8_io(driver) ((driver)->opt.charsets[0] != -1)
401 static INIT_LIST_OF(struct screen_driver, active_screen_drivers);
403 /** Set screen_driver.opt according to screen_driver.type and @a term_spec.
404 * Other members of @a *driver need not have been initialized.
406 * If you modify anything here, check whether option descriptions
407 * should be updated. */
408 static void
409 set_screen_driver_opt(struct screen_driver *driver, struct option *term_spec)
411 const int cp = get_opt_codepage_tree(term_spec, "charset", NULL);
412 int utf8_io = get_opt_bool_tree(term_spec, "utf_8_io", NULL);
414 /* Copy all the original options from constants, so that this
415 * function need not carefully restore options one by one. */
416 copy_struct(&driver->opt, screen_driver_opts[driver->type]);
418 #ifdef CONFIG_COMBINE
419 driver->opt.combine = get_opt_bool_tree(term_spec, "combine", NULL);
420 #endif /* CONFIG_COMBINE */
421 #ifdef CONFIG_UTF8
422 /* Force UTF-8 I/O if the UTF-8 charset is selected. Various
423 * places assume that the terminal's charset is unibyte if
424 * UTF-8 I/O is disabled. (bug 827) */
425 if (is_cp_utf8(cp)) {
426 driver->opt.utf8_cp = 1;
427 utf8_io = 1;
428 } else {
429 driver->opt.utf8_cp = 0;
431 #endif /* CONFIG_UTF8 */
433 driver->opt.color_mode = get_opt_int_tree(term_spec, "colors", NULL);
434 driver->opt.transparent = get_opt_bool_tree(term_spec, "transparency",
435 NULL);
437 if (get_opt_bool_tree(term_spec, "italic", NULL)) {
438 driver->opt.italic = italic_seqs;
439 } else {
440 driver->opt.italic = NULL;
443 if (get_opt_bool_tree(term_spec, "underline", NULL)) {
444 driver->opt.underline = underline_seqs;
445 } else {
446 driver->opt.underline = NULL;
449 if (utf8_io) {
450 driver->opt.charsets[0] = cp;
452 /* When we're using UTF-8 I/O, we never need to switch
453 * charsets or fonts for drawing frames, because the
454 * characters encoded in UTF-8 are already unambiguous. */
455 driver->opt.frame_seqs = NULL;
457 if (driver->type == TERM_LINUX || driver->type == TERM_FBTERM) {
458 if (get_opt_bool_tree(term_spec, "restrict_852", NULL))
459 driver->opt.frame = frame_restrict;
460 driver->opt.charsets[1] = get_cp_index("cp437");
462 } else if (driver->type == TERM_FREEBSD) {
463 driver->opt.frame = frame_freebsd_u;
464 driver->opt.charsets[1] = get_cp_index("cp437");
466 } else if (driver->type == TERM_VT100) {
467 driver->opt.frame = frame_vt100_u;
468 driver->opt.charsets[1] = get_cp_index("cp437");
470 } else if (driver->type == TERM_KOI8) {
471 driver->opt.charsets[1] = get_cp_index("koi8-r");
473 } else {
474 #ifdef CONFIG_UTF8
475 /* Don't let driver->opt.charsets[1] become
476 * UTF-8, because it is passed to cp2u(),
477 * which supports only unibyte characters. */
478 if (driver->opt.utf8_cp)
479 driver->opt.charsets[1] = get_cp_index("US-ASCII");
480 else
481 #endif /* CONFIG_UTF8 */
482 driver->opt.charsets[1] = driver->opt.charsets[0];
485 } else { /* !utf8_io */
486 driver->opt.charsets[0] = -1;
488 if (driver->type == TERM_LINUX) {
489 if (get_opt_bool_tree(term_spec, "restrict_852", NULL))
490 driver->opt.frame = frame_restrict;
492 if (get_opt_bool_tree(term_spec, "m11_hack", NULL))
493 driver->opt.frame_seqs = m11_hack_frame_seqs;
495 } else if (driver->type == TERM_FREEBSD) {
496 if (get_opt_bool_tree(term_spec, "m11_hack", NULL))
497 driver->opt.frame_seqs = m11_hack_frame_seqs;
499 } else if (driver->type == TERM_VT100) {
500 driver->opt.frame = frame_vt100;
502 } /* !utf8_io */
505 static int
506 screen_driver_change_hook(struct session *ses, struct option *term_spec,
507 struct option *changed)
509 enum term_mode_type type = get_opt_int_tree(term_spec, "type", NULL);
510 struct screen_driver *driver;
511 unsigned char *name = term_spec->name;
513 foreach (driver, active_screen_drivers)
514 if (driver->type == type && !strcmp(driver->name, name)) {
515 set_screen_driver_opt(driver, term_spec);
516 break;
519 return 0;
522 static inline struct screen_driver *
523 add_screen_driver(enum term_mode_type type, struct terminal *term, int env_len)
525 struct screen_driver *driver;
527 /* One byte is reserved for name in struct screen_driver. */
528 driver = mem_alloc(sizeof(*driver) + env_len);
529 if (!driver) return NULL;
531 /* These four operations fully initialize *driver. */
532 add_to_list(active_screen_drivers, driver);
533 driver->type = type;
534 set_screen_driver_opt(driver, term->spec);
535 memcpy(driver->name, term->spec->name, env_len + 1);
537 term->spec->change_hook = screen_driver_change_hook;
539 #ifdef CONFIG_UTF8
540 term->utf8_cp = driver->opt.utf8_cp;
541 term->utf8_io = use_utf8_io(driver);
542 #endif /* CONFIG_UTF8 */
544 return driver;
547 static inline struct screen_driver *
548 get_screen_driver(struct terminal *term)
550 enum term_mode_type type = get_opt_int_tree(term->spec, "type", NULL);
551 unsigned char *name = term->spec->name;
552 int len = strlen(name);
553 struct screen_driver *driver;
555 foreach (driver, active_screen_drivers) {
556 if (driver->type != type) continue;
557 if (strcmp(driver->name, name)) continue;
559 /* Some simple probably useless MRU ;) */
560 move_to_top_of_list(active_screen_drivers, driver);
562 #ifdef CONFIG_UTF8
563 term->utf8_cp = driver->opt.utf8_cp;
564 term->utf8_io = use_utf8_io(driver);
565 #endif /* CONFIG_UTF8 */
566 return driver;
569 return add_screen_driver(type, term, len);
572 /** Release private screen drawing utilities. */
573 void
574 done_screen_drivers(struct module *xxx)
576 free_list(active_screen_drivers);
580 /** Adds the term code for positioning the cursor at @a x and @a y to
581 * @a string. The template term code is: "\033[<y>;<x>H" */
582 static inline struct string *
583 add_cursor_move_to_string(struct string *screen, int y, int x)
585 #define CURSOR_NUM_LEN 10 /* 10 chars for @y and @x numbers should be more than enough. */
586 unsigned char code[4 + 2 * CURSOR_NUM_LEN + 1];
587 unsigned int length = 2;
589 code[0] = '\033';
590 code[1] = '[';
592 if (ulongcat(code, &length, y, CURSOR_NUM_LEN, 0) < 0)
593 return screen;
595 code[length++] = ';';
597 if (ulongcat(code, &length, x, CURSOR_NUM_LEN, 0) < 0)
598 return screen;
600 code[length++] = 'H';
602 return add_bytes_to_string(screen, code, length);
603 #undef CURSOR_NUM_LEN
606 struct screen_state {
607 unsigned char border;
608 unsigned char italic;
609 unsigned char underline;
610 unsigned char bold;
611 unsigned char attr;
612 /** Following should match the screen_char.color field. */
613 unsigned char color[SCREEN_COLOR_SIZE];
616 #if defined(CONFIG_TRUE_COLOR)
617 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }
618 #elif defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
619 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF } }
620 #else
621 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0xFF, 0, { 0xFF } }
622 #endif
624 #ifdef CONFIG_TRUE_COLOR
625 static inline int
626 compare_color_true(unsigned char *a, unsigned char *b)
628 return !memcmp(a, b, 6);
631 static inline int
632 compare_bg_color_true(unsigned char *a, unsigned char *b)
634 return (a[3] == b[3] && a[4] == b[4] && a[5] == b[5]);
637 static inline int
638 compare_fg_color_true(unsigned char *a, unsigned char *b)
640 return (a[0] == b[0] && a[1] == b[1] && a[2] == b[2]);
643 static inline void
644 copy_color_true(unsigned char *a, unsigned char *b)
646 memcpy(a, b, 6);
649 static inline int
650 background_is_black(unsigned char *a)
652 static unsigned char b[6] = {0, 0, 0, 0, 0, 0};
654 return compare_bg_color_true(a, b);
656 #endif
658 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
659 static inline int
660 compare_color_256(unsigned char *a, unsigned char *b)
662 return (a[0] == b[0] && a[1] == b[1]);
665 static inline int
666 compare_bg_color_256(unsigned char *a, unsigned char *b)
668 return (a[1] == b[1]);
671 static inline int
672 compare_fg_color_256(unsigned char *a, unsigned char *b)
674 return (a[0] == b[0]);
677 static inline void
678 copy_color_256(unsigned char *a, unsigned char *b)
680 a[0] = b[0];
681 a[1] = b[1];
683 #endif
685 static inline int
686 compare_color_16(unsigned char *a, unsigned char *b)
688 return (a[0] == b[0]);
691 static inline int
692 compare_bg_color_16(unsigned char *a, unsigned char *b)
694 return (TERM_COLOR_BACKGROUND_16(a) == TERM_COLOR_BACKGROUND_16(b));
697 static inline int
698 compare_fg_color_16(unsigned char *a, unsigned char *b)
700 return (TERM_COLOR_FOREGROUND_16(a) == TERM_COLOR_FOREGROUND_16(b));
703 static inline void
704 copy_color_16(unsigned char *a, unsigned char *b)
706 a[0] = b[0];
709 #ifdef CONFIG_UTF8
710 static inline void
711 add_char_data(struct string *screen, struct screen_driver *driver,
712 unicode_val_T data, unsigned char border)
713 #else /* !CONFIG_UTF8 */
714 static inline void
715 add_char_data(struct string *screen, struct screen_driver *driver,
716 unsigned char data, unsigned char border)
717 #endif /* !CONFIG_UTF8 */
719 /* charset use_utf8_io border data add_to_string
720 * ------- ----------- ------ ---------------- ----------------
721 * unibyte 0 0 terminal unibyte terminal unibyte
722 * unibyte 0 1 enum border_char border unibyte
723 * unibyte 1 0 terminal unibyte UTF-8
724 * unibyte 1 1 enum border_char UTF-8
725 * UTF-8 1 0 UTF-32 (*) UTF-8
726 * UTF-8 1 1 enum border_char UTF-8
728 * (*) For "UTF-32" above, data can also be UCS_NO_CHAR,
729 * in which case this function must not alter *screen.
732 if (border && driver->opt.frame && data >= 176 && data < 224)
733 data = driver->opt.frame[data - 176];
735 #ifdef CONFIG_UTF8
736 if (driver->opt.utf8_cp) {
737 if (border) {
738 data = cp2u(driver->opt.charsets[1],
739 (unsigned char) data);
741 if (data == UCS_NO_CHAR)
742 return;
743 #ifdef CONFIG_COMBINE
744 if (data >= UCS_BEGIN_COMBINED && data <= last_combined) {
745 unicode_val_T *text = combined[data - UCS_BEGIN_COMBINED];
747 if (driver->opt.combine) {
748 /* XTerm */
749 while (*text != UCS_END_COMBINED) {
750 add_to_string(screen, encode_utf8(*text));
751 text++;
753 return;
754 } else {
755 /* Others */
756 data = *text;
759 #endif /* CONFIG_COMBINE */
760 if (!isscreensafe_ucs(data))
761 data = UCS_SPACE;
762 add_to_string(screen, encode_utf8(data));
763 } else
764 #endif /* CONFIG_UTF8 */
765 if (use_utf8_io(driver)) {
766 int charset = driver->opt.charsets[!!border];
768 if (border || isscreensafe(data))
769 add_to_string(screen, cp2utf8(charset, data));
770 else /* UCS_SPACE <= 0x7F and so fits in one UTF-8 byte */
771 add_char_to_string(screen, UCS_SPACE);
772 } else {
773 if (border || isscreensafe(data))
774 add_char_to_string(screen, (unsigned char)data);
775 else
776 add_char_to_string(screen, ' ');
780 /** Time critical section. */
781 static inline void
782 add_char16(struct string *screen, struct screen_driver *driver,
783 struct screen_char *ch, struct screen_state *state)
785 unsigned char border = (ch->attr & SCREEN_ATTR_FRAME);
786 unsigned char italic = (ch->attr & SCREEN_ATTR_ITALIC);
787 unsigned char underline = (ch->attr & SCREEN_ATTR_UNDERLINE);
788 unsigned char bold = (ch->attr & SCREEN_ATTR_BOLD);
790 if (
791 #ifdef CONFIG_UTF8
792 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
793 #endif /* CONFIG_UTF8 */
794 border != state->border && driver->opt.frame_seqs
796 state->border = border;
797 add_term_string(screen, driver->opt.frame_seqs[!!border]);
800 if (
801 #ifdef CONFIG_UTF8
802 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
803 #endif /* CONFIG_UTF8 */
804 italic != state->italic && driver->opt.italic
806 state->italic = italic;
807 add_term_string(screen, driver->opt.italic[!!italic]);
810 if (
811 #ifdef CONFIG_UTF8
812 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
813 #endif /* CONFIG_UTF8 */
814 underline != state->underline && driver->opt.underline
816 state->underline = underline;
817 add_term_string(screen, driver->opt.underline[!!underline]);
820 if (
821 #ifdef CONFIG_UTF8
822 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
823 #endif /* CONFIG_UTF8 */
824 bold != state->bold
826 state->bold = bold;
827 if (bold) {
828 add_bytes_to_string(screen, "\033[1m", 4);
829 } else {
830 /* Force repainting of the other attributes. */
831 state->color[0] = ch->color[0] + 1;
835 if (
836 #ifdef CONFIG_UTF8
837 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
838 #endif /* CONFIG_UTF8 */
839 !compare_color_16(ch->color, state->color)
841 copy_color_16(state->color, ch->color);
843 add_bytes_to_string(screen, "\033[0", 3);
845 /* @set_screen_driver_opt has set @driver->opt.color_mode
846 * according to terminal-type-specific options.
847 * The caller of @add_char16 has already partially
848 * checked it, but there are still these possibilities:
849 * - COLOR_MODE_MONO. Then don't show colors, but
850 * perhaps use the standout attribute.
851 * - COLOR_MODE_16. Use 16 colors.
852 * - An unsupported color mode. Use 16 colors. */
853 if (driver->opt.color_mode != COLOR_MODE_MONO) {
854 unsigned char code[6] = ";30;40";
855 unsigned char bgcolor = TERM_COLOR_BACKGROUND_16(ch->color);
857 code[2] += TERM_COLOR_FOREGROUND_16(ch->color);
859 if (!driver->opt.transparent || bgcolor != 0) {
860 code[5] += bgcolor;
861 add_bytes_to_string(screen, code, 6);
862 } else {
863 add_bytes_to_string(screen, code, 3);
866 } else if (ch->attr & SCREEN_ATTR_STANDOUT) {
867 /* Flip the fore- and background colors for highlighing
868 * purposes. */
869 add_bytes_to_string(screen, ";7", 2);
872 if (italic && driver->opt.italic) {
873 add_bytes_to_string(screen, ";3", 2);
876 if (underline && driver->opt.underline) {
877 add_bytes_to_string(screen, ";4", 2);
880 /* Check if the char should be rendered bold. */
881 if (bold) {
882 add_bytes_to_string(screen, ";1", 2);
885 add_bytes_to_string(screen, "m", 1);
888 add_char_data(screen, driver, ch->data, border);
891 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
892 static inline void
893 add_char_color(struct string *screen, const struct string *seq, unsigned char color)
895 unsigned char color_buf[3];
896 unsigned char *color_pos = color_buf;
897 int seq_pos = 0;
898 int color_len = 1;
900 check_string_magic(seq);
901 for (; seq->source[seq_pos] != '%'; seq_pos++) ;
903 add_bytes_to_string(screen, seq->source, seq_pos);
905 if (color < 10) {
906 color_pos += 2;
907 } else {
908 int color2;
910 ++color_len;
911 if (color < 100) {
912 ++color_pos;
913 } else {
914 ++color_len;
916 if (color < 200) {
917 color_buf[0] = '1';
918 color -= 100;
919 } else {
920 color_buf[0] = '2';
921 color -= 200;
925 color2 = (color % 10);
926 color /= 10;
927 color_buf[1] = '0' + color;
928 color = color2;
931 color_buf[2] = '0' + color;
933 add_bytes_to_string(screen, color_pos, color_len);
935 seq_pos += 2; /* Skip "%d" */
936 add_bytes_to_string(screen, &seq->source[seq_pos], seq->length - seq_pos);
939 #define add_background_color(str, seq, chr) add_char_color(str, &(seq)[1], (chr)->color[1])
940 #define add_foreground_color(str, seq, chr) add_char_color(str, &(seq)[0], (chr)->color[0])
942 /** Time critical section. */
943 static inline void
944 add_char256(struct string *screen, struct screen_driver *driver,
945 struct screen_char *ch, struct screen_state *state)
947 unsigned char attr_delta = (ch->attr ^ state->attr);
949 if (
950 #ifdef CONFIG_UTF8
951 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
952 #endif /* CONFIG_UTF8 */
953 attr_delta
955 if ((attr_delta & SCREEN_ATTR_FRAME) && driver->opt.frame_seqs) {
956 state->border = !!(ch->attr & SCREEN_ATTR_FRAME);
957 add_term_string(screen, driver->opt.frame_seqs[state->border]);
960 if ((attr_delta & SCREEN_ATTR_ITALIC) && driver->opt.italic) {
961 state->italic = !!(ch->attr & SCREEN_ATTR_ITALIC);
962 add_term_string(screen, driver->opt.italic[state->italic]);
965 if ((attr_delta & SCREEN_ATTR_UNDERLINE) && driver->opt.underline) {
966 state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
967 add_term_string(screen, driver->opt.underline[state->underline]);
970 if (attr_delta & SCREEN_ATTR_BOLD) {
971 if (ch->attr & SCREEN_ATTR_BOLD) {
972 add_bytes_to_string(screen, "\033[1m", 4);
973 } else {
974 /* Force repainting of the other attributes. */
975 state->color[0] = ch->color[0] + 1;
979 state->attr = ch->attr;
982 if (
983 #ifdef CONFIG_UTF8
984 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
985 #endif /* CONFIG_UTF8 */
986 !compare_color_256(ch->color, state->color)
988 copy_color_256(state->color, ch->color);
990 add_foreground_color(screen, driver->opt.color256_seqs, ch);
991 if (!driver->opt.transparent || ch->color[1] != 0) {
992 add_background_color(screen, driver->opt.color256_seqs, ch);
995 if (ch->attr & SCREEN_ATTR_BOLD)
996 add_bytes_to_string(screen, "\033[1m", 4);
998 if (ch->attr & SCREEN_ATTR_ITALIC && driver->opt.italic) {
999 state->italic = !!(ch->attr & SCREEN_ATTR_ITALIC);
1000 add_term_string(screen, driver->opt.italic[state->italic]);
1003 if (ch->attr & SCREEN_ATTR_UNDERLINE && driver->opt.underline) {
1004 state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
1005 add_term_string(screen, driver->opt.underline[state->underline]);
1009 add_char_data(screen, driver, ch->data, ch->attr & SCREEN_ATTR_FRAME);
1011 #endif
1013 #ifdef CONFIG_TRUE_COLOR
1014 static const struct string color_true_seqs[] = {
1015 /* foreground: */ TERM_STRING("\033[0;38;2"),
1016 /* background: */ TERM_STRING("\033[48;2"),
1018 #define add_true_background_color(str, seq, chr) add_char_true_color(str, &(seq)[1], &(chr)->color[3])
1019 #define add_true_foreground_color(str, seq, chr) add_char_true_color(str, &(seq)[0], &(chr)->color[0])
1020 static inline void
1021 add_char_true_color(struct string *screen, const struct string *seq, unsigned char *colors)
1023 unsigned char color_buf[3];
1024 int i;
1026 check_string_magic(seq);
1027 add_string_to_string(screen, seq);
1028 for (i = 0; i < 3; i++) {
1029 unsigned char *color_pos = color_buf;
1030 int color_len = 1;
1031 unsigned char color = colors[i];
1033 add_char_to_string(screen, ';');
1035 if (color < 10) {
1036 color_pos += 2;
1037 } else {
1038 int color2;
1040 ++color_len;
1041 if (color < 100) {
1042 ++color_pos;
1043 } else {
1044 ++color_len;
1046 if (color < 200) {
1047 color_buf[0] = '1';
1048 color -= 100;
1049 } else {
1050 color_buf[0] = '2';
1051 color -= 200;
1055 color2 = (color % 10);
1056 color /= 10;
1057 color_buf[1] = '0' + color;
1058 color = color2;
1060 color_buf[2] = '0' + color;
1062 add_bytes_to_string(screen, color_pos, color_len);
1064 add_char_to_string(screen, 'm');
1067 /** Time critical section. */
1068 static inline void
1069 add_char_true(struct string *screen, struct screen_driver *driver,
1070 struct screen_char *ch, struct screen_state *state)
1072 unsigned char attr_delta = (ch->attr ^ state->attr);
1074 if (
1075 #ifdef CONFIG_UTF8
1076 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
1077 #endif /* CONFIG_UTF8 */
1078 attr_delta
1080 if ((attr_delta & SCREEN_ATTR_FRAME) && driver->opt.frame_seqs) {
1081 state->border = !!(ch->attr & SCREEN_ATTR_FRAME);
1082 add_term_string(screen, driver->opt.frame_seqs[state->border]);
1085 if ((attr_delta & SCREEN_ATTR_ITALIC) && driver->opt.italic) {
1086 state->italic = !!(ch->attr & SCREEN_ATTR_ITALIC);
1087 add_term_string(screen, driver->opt.italic[state->italic]);
1090 if ((attr_delta & SCREEN_ATTR_UNDERLINE) && driver->opt.underline) {
1091 state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
1092 add_term_string(screen, driver->opt.underline[state->underline]);
1095 if (attr_delta & SCREEN_ATTR_BOLD) {
1096 if (ch->attr & SCREEN_ATTR_BOLD) {
1097 add_bytes_to_string(screen, "\033[1m", 4);
1098 } else {
1099 /* Force repainting of the other attributes. */
1100 state->color[0] = ch->color[0] + 1;
1104 state->attr = ch->attr;
1107 if (
1108 #ifdef CONFIG_UTF8
1109 !(driver->opt.utf8_cp && ch->data == UCS_NO_CHAR) &&
1110 #endif /* CONFIG_UTF8 */
1111 !compare_color_true(ch->color, state->color)
1113 copy_color_true(state->color, ch->color);
1115 add_true_foreground_color(screen, color_true_seqs, ch);
1116 if (!driver->opt.transparent || !background_is_black(ch->color)) {
1117 add_true_background_color(screen, color_true_seqs, ch);
1120 if (ch->attr & SCREEN_ATTR_BOLD)
1121 add_bytes_to_string(screen, "\033[1m", 4);
1123 if (ch->attr & SCREEN_ATTR_ITALIC && driver->opt.italic) {
1124 state->italic = !!(ch->attr & SCREEN_ATTR_ITALIC);
1125 add_term_string(screen, driver->opt.italic[state->italic]);
1128 if (ch->attr & SCREEN_ATTR_UNDERLINE && driver->opt.underline) {
1129 state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
1130 add_term_string(screen, driver->opt.underline[state->underline]);
1134 add_char_data(screen, driver, ch->data, ch->attr & SCREEN_ATTR_FRAME);
1136 #endif
1138 #define add_chars(image_, term_, driver_, state_, ADD_CHAR, compare_bg_color, compare_fg_color) \
1140 struct terminal_screen *screen = (term_)->screen; \
1141 int y = screen->dirty_from; \
1142 int ypos = y * (term_)->width; \
1143 int prev_y = -1; \
1144 int xmax = (term_)->width - 1; \
1145 int ymax = (term_)->height - 1; \
1146 struct screen_char *current = &screen->last_image[ypos]; \
1147 struct screen_char *pos = &screen->image[ypos]; \
1148 struct screen_char *prev_pos = NULL; /* Warning prevention. */ \
1150 int_upper_bound(&screen->dirty_to, ymax); \
1152 for (; y <= screen->dirty_to; y++) { \
1153 int is_last_line = (y == ymax); \
1154 int x = 0; \
1156 for (; x <= xmax; x++, current++, pos++) { \
1157 /* Workaround for terminals without
1158 * "eat_newline_glitch (xn)", e.g., the cons25 family
1159 * of terminals and cygwin terminal.
1160 * It prevents display distortion, but char at bottom
1161 * right of terminal will not be drawn.
1162 * A better fix would be to correctly detects
1163 * terminal type, and/or add a terminal option for
1164 * this purpose. */ \
1166 if (is_last_line && x == xmax) \
1167 break; \
1169 if (compare_bg_color(pos->color, current->color)) { \
1170 /* No update for exact match. */ \
1171 if (compare_fg_color(pos->color, current->color)\
1172 && pos->data == current->data \
1173 && pos->attr == current->attr) \
1174 continue; \
1176 /* Else if the color match and the data is
1177 * ``space''. */ \
1178 if (pos->data <= ' ' && current->data <= ' ' \
1179 && pos->attr == current->attr) \
1180 continue; \
1183 /* Move the cursor when @prev_pos is more than 10 chars
1184 * away. */ \
1185 if (prev_y != y || prev_pos + 10 <= pos) { \
1186 add_cursor_move_to_string(image_, y + 1, x + 1);\
1187 prev_pos = pos; \
1188 prev_y = y; \
1191 for (; prev_pos <= pos ; prev_pos++) \
1192 ADD_CHAR(image_, driver_, prev_pos, state_); \
1197 /*! Updating of the terminal screen is done by checking what needs to
1198 * be updated using the last screen. */
1199 void
1200 redraw_screen(struct terminal *term)
1202 struct screen_driver *driver;
1203 struct string image;
1204 struct screen_state state = INIT_SCREEN_STATE;
1205 struct terminal_screen *screen = term->screen;
1207 if (!screen || screen->dirty_from > screen->dirty_to) return;
1208 if (term->master && is_blocked()) return;
1210 driver = get_screen_driver(term);
1211 if (!driver) return;
1213 if (!init_string(&image)) return;
1215 switch (driver->opt.color_mode) {
1216 default:
1217 /* If the desired color mode was not compiled in,
1218 * use 16 colors. */
1219 case COLOR_MODE_MONO:
1220 case COLOR_MODE_16:
1221 add_chars(&image, term, driver, &state, add_char16, compare_bg_color_16, compare_fg_color_16);
1222 break;
1223 #ifdef CONFIG_88_COLORS
1224 case COLOR_MODE_88:
1225 add_chars(&image, term, driver, &state, add_char256, compare_bg_color_256, compare_fg_color_256);
1226 break;
1227 #endif
1228 #ifdef CONFIG_256_COLORS
1229 case COLOR_MODE_256:
1230 add_chars(&image, term, driver, &state, add_char256, compare_bg_color_256, compare_fg_color_256);
1231 break;
1232 #endif
1233 #ifdef CONFIG_TRUE_COLOR
1234 case COLOR_MODE_TRUE_COLOR:
1235 add_chars(&image, term, driver, &state, add_char_true, compare_bg_color_true, compare_fg_color_true);
1236 break;
1237 #endif
1238 case COLOR_MODES:
1239 case COLOR_MODE_DUMP:
1240 INTERNAL("Invalid color mode (%d).", driver->opt.color_mode);
1241 return;
1244 if (image.length) {
1245 if (driver->opt.color_mode != COLOR_MODE_MONO)
1246 add_bytes_to_string(&image, "\033[37;40m", 8);
1248 add_bytes_to_string(&image, "\033[0m", 4);
1250 /* If we ended in border state end the frame mode. */
1251 if (state.border && driver->opt.frame_seqs)
1252 add_term_string(&image, driver->opt.frame_seqs[0]);
1256 /* Even if nothing was redrawn, we possibly still need to move
1257 * cursor. */
1258 if (image.length
1259 || screen->cx != screen->lcx
1260 || screen->cy != screen->lcy) {
1261 screen->lcx = screen->cx;
1262 screen->lcy = screen->cy;
1264 add_cursor_move_to_string(&image, screen->cy + 1,
1265 screen->cx + 1);
1268 if (image.length) {
1269 if (term->master) want_draw();
1270 hard_write(term->fdout, image.source, image.length);
1271 if (term->master) done_draw();
1274 done_string(&image);
1276 copy_screen_chars(screen->last_image, screen->image, term->width * term->height);
1277 screen->dirty_from = term->height;
1278 screen->dirty_to = 0;
1281 void
1282 erase_screen(struct terminal *term)
1284 if (term->master) {
1285 if (is_blocked()) return;
1286 want_draw();
1289 hard_write(term->fdout, "\033[2J\033[1;1H", 10);
1290 if (term->master) done_draw();
1293 void
1294 beep_terminal(struct terminal *term)
1296 #ifdef CONFIG_OS_WIN32
1297 MessageBeep(MB_ICONEXCLAMATION);
1298 #else
1299 hard_write(term->fdout, "\a", 1);
1300 #endif
1303 struct terminal_screen *
1304 init_screen(void)
1306 struct terminal_screen *screen;
1308 screen = mem_calloc(1, sizeof(*screen));
1309 if (!screen) return NULL;
1311 screen->lcx = -1;
1312 screen->lcy = -1;
1314 return screen;
1317 /*! The two images are allocated in one chunk.
1318 * @todo TODO: It seems allocation failure here is fatal.
1319 * We should do something! */
1320 void
1321 resize_screen(struct terminal *term, int width, int height)
1323 struct terminal_screen *screen;
1324 struct screen_char *image;
1325 size_t size, bsize;
1327 assert(term && term->screen);
1329 screen = term->screen;
1331 assert(width >= 0);
1332 assert(height >= 0);
1334 size = width * height;
1335 if (size <= 0) return;
1337 bsize = size * sizeof(*image);
1339 image = mem_realloc(screen->image, bsize * 2);
1340 if (!image) return;
1342 screen->image = image;
1343 screen->last_image = image + size;
1345 memset(screen->image, 0, bsize);
1346 memset(screen->last_image, 0xFF, bsize);
1348 term->width = width;
1349 term->height = height;
1350 set_screen_dirty(screen, 0, height);
1353 void
1354 done_screen(struct terminal_screen *screen)
1356 mem_free_if(screen->image);
1357 mem_free(screen);
1360 struct module terminal_screen_module = struct_module(
1361 /* Because this module is a submodule of terminal_module,
1362 * which is listed in main_modules rather than in builtin_modules,
1363 * its name does not appear in the user interface and
1364 * so need not be translatable. */
1365 /* name: */ "Terminal Screen",
1366 /* options: */ NULL,
1367 /* hooks: */ NULL,
1368 /* submodules: */ NULL,
1369 /* data: */ NULL,
1370 /* init: */ NULL,
1371 /* done: */ done_screen_drivers