1 /* Terminal screen drawing routines. */
12 #include "config/options.h"
13 #include "intl/charsets.h"
14 #include "main/module.h"
15 #include "osdep/ascii.h"
16 #include "osdep/osdep.h"
17 #include "terminal/color.h"
18 #include "terminal/draw.h"
19 #include "terminal/hardio.h"
20 #include "terminal/kbd.h"
21 #include "terminal/screen.h"
22 #include "terminal/terminal.h"
23 #include "util/conv.h"
24 #include "util/error.h"
25 #include "util/memory.h"
26 #include "util/string.h"
29 /* TODO: We must use termcap/terminfo if available! --pasky */
31 unsigned char frame_dumb
[48] = " ||||++||++++++--|-+||++--|-+----++++++++ ";
32 static unsigned char frame_vt100
[48] = "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla ";
36 static unsigned char frame_vt100_u
[48] = {
37 177, 177, 177, 179, 180, 180, 180, 191,
38 191, 180, 179, 191, 217, 217, 217, 191,
39 192, 193, 194, 195, 196, 197, 195, 195,
40 192, 218, 193, 194, 195, 196, 197, 193,
41 193, 194, 194, 192, 192, 218, 218, 197,
42 197, 217, 218, 177, 32, 32, 32, 32
44 #endif /* CONFIG_UTF8 */
46 static unsigned char frame_freebsd
[48] = {
47 130, 138, 128, 153, 150, 150, 150, 140,
48 140, 150, 153, 140, 139, 139, 139, 140,
49 142, 151, 152, 149, 146, 143, 149, 149,
50 142, 141, 151, 152, 149, 146, 143, 151,
51 151, 152, 152, 142, 142, 141, 141, 143,
52 143, 139, 141, 128, 128, 128, 128, 128,
55 static unsigned char frame_koi
[48] = {
56 144, 145, 146, 129, 135, 178, 180, 167,
57 166, 181, 161, 168, 174, 173, 172, 131,
58 132, 137, 136, 134, 128, 138, 175, 176,
59 171, 165, 187, 184, 177, 160, 190, 185,
60 186, 182, 183, 170, 169, 162, 164, 189,
61 188, 133, 130, 141, 140, 142, 143, 139,
64 /* Most of this table is just 176 + <index in table>. */
65 static unsigned char frame_restrict
[48] = {
66 176, 177, 178, 179, 180, 179, 186, 186,
67 205, 185, 186, 187, 188, 186, 205, 191,
68 192, 193, 194, 195, 196, 197, 179, 186,
69 200, 201, 202, 203, 204, 205, 206, 205,
70 196, 205, 196, 186, 205, 205, 186, 186,
71 179, 217, 218, 219, 220, 221, 222, 223,
74 #define TERM_STRING(str) INIT_STRING(str, sizeof(str) - 1)
76 #define add_term_string(str, tstr) \
77 add_bytes_to_string(str, (tstr).source, (tstr).length)
79 static struct string m11_hack_frame_seqs
[] = {
80 /* end border: */ TERM_STRING("\033[10m"),
81 /* begin border: */ TERM_STRING("\033[11m"),
85 static struct string utf8_linux_frame_seqs
[] = {
86 /* end border: */ TERM_STRING("\033[10m\033%G"),
87 /* begin border: */ TERM_STRING("\033%@\033[11m"),
89 #endif /* CONFIG_UTF8 */
91 static struct string vt100_frame_seqs
[] = {
92 /* end border: */ TERM_STRING("\x0f"),
93 /* begin border: */ TERM_STRING("\x0e"),
96 static struct string underline_seqs
[] = {
97 /* begin underline: */ TERM_STRING("\033[24m"),
98 /* end underline: */ TERM_STRING("\033[4m"),
101 /* Used in {add_char*()} and {redraw_screen()} to reduce the logic. It is
102 * updated from terminal._template_.* using option change_hooks. */
103 /* TODO: termcap/terminfo can maybe gradually be introduced via this
104 * structure. We'll see. --jonas */
105 struct screen_driver
{
106 LIST_HEAD(struct screen_driver
);
108 /* The terminal._template_.type. Together with the @name member the
109 * uniquely identify the screen_driver. */
110 enum term_mode_type type
;
113 /* Charsets when doing UTF8 I/O. */
114 /* [0] is the common charset and [1] is the frame charset.
115 * Test wether to use UTF8 I/O using the use_utf8_io() macro. */
117 #endif /* CONFIG_UTF8 */
119 /* The frame translation table. May be NULL. */
120 unsigned char *frame
;
122 /* The frame mode setup and teardown sequences. May be NULL. */
123 struct string
*frame_seqs
;
125 /* The underline mode setup and teardown sequences. May be NULL. */
126 struct string
*underline
;
129 enum color_mode color_mode
;
131 /* These are directly derived from the terminal options. */
132 unsigned int transparent
:1;
135 /* UTF-8 I/O. Forced on if the UTF-8 charset is selected. (bug 827) */
137 #endif /* CONFIG_UTF8 */
139 /* The terminal._template_ name. */
140 unsigned char name
[1]; /* XXX: Keep last! */
143 static struct screen_driver dumb_screen_driver
= {
145 /* type: */ TERM_DUMB
,
147 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
148 #endif /* CONFIG_UTF8 */
149 /* frame: */ frame_dumb
,
150 /* frame_seqs: */ NULL
,
151 /* underline: */ underline_seqs
,
152 /* color_mode: */ COLOR_MODE_16
,
153 /* transparent: */ 1,
156 #endif /* CONFIG_UTF8 */
159 static struct screen_driver vt100_screen_driver
= {
161 /* type: */ TERM_VT100
,
163 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
164 #endif /* CONFIG_UTF8 */
165 /* frame: */ frame_vt100
,
166 /* frame_seqs: */ vt100_frame_seqs
,
167 /* underline: */ underline_seqs
,
168 /* color_mode: */ COLOR_MODE_16
,
169 /* transparent: */ 1,
172 #endif /* CONFIG_UTF8 */
175 static struct screen_driver linux_screen_driver
= {
177 /* type: */ TERM_LINUX
,
179 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
180 #endif /* CONFIG_UTF8 */
181 /* frame: */ NULL
, /* No restrict_852 */
182 /* frame_seqs: */ NULL
, /* No m11_hack */
183 /* underline: */ underline_seqs
,
184 /* color_mode: */ COLOR_MODE_16
,
185 /* transparent: */ 1,
188 #endif /* CONFIG_UTF8 */
191 static struct screen_driver koi8_screen_driver
= {
193 /* type: */ TERM_KOI8
,
195 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
196 #endif /* CONFIG_UTF8 */
197 /* frame: */ frame_koi
,
198 /* frame_seqs: */ NULL
,
199 /* underline: */ underline_seqs
,
200 /* color_mode: */ COLOR_MODE_16
,
201 /* transparent: */ 1,
204 #endif /* CONFIG_UTF8 */
207 static struct screen_driver freebsd_screen_driver
= {
209 /* type: */ TERM_FREEBSD
,
211 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
212 #endif /* CONFIG_UTF8 */
213 /* frame: */ frame_freebsd
,
214 /* frame_seqs: */ NULL
, /* No m11_hack */
215 /* underline: */ underline_seqs
,
216 /* color_mode: */ COLOR_MODE_16
,
217 /* transparent: */ 1,
220 #endif /* CONFIG_UTF8 */
223 /* XXX: Keep in sync with enum term_mode_type. */
224 static struct screen_driver
*screen_drivers
[] = {
225 /* TERM_DUMB: */ &dumb_screen_driver
,
226 /* TERM_VT100: */ &vt100_screen_driver
,
227 /* TERM_LINUX: */ &linux_screen_driver
,
228 /* TERM_KOI8: */ &koi8_screen_driver
,
229 /* TERM_FREEBSD: */ &freebsd_screen_driver
,
233 #define use_utf8_io(driver) ((driver)->utf8)
235 #define use_utf8_io(driver) ((driver)->charsets[0] != -1)
236 #endif /* CONFIG_UTF8 */
238 static INIT_LIST_HEAD(active_screen_drivers
);
241 update_screen_driver(struct screen_driver
*driver
, struct option
*term_spec
)
244 /* Force UTF-8 I/O if the UTF-8 charset is selected. Various
245 * places assume that the terminal's charset is unibyte if
246 * UTF-8 I/O is disabled. (bug 827) */
247 driver
->utf8
= get_opt_bool_tree(term_spec
, "utf_8_io")
248 || is_cp_utf8(get_opt_codepage_tree(term_spec
, "charset"));
250 int utf8_io
= get_opt_bool_tree(term_spec
, "utf_8_io");
251 #endif /* CONFIG_UTF8 */
253 driver
->color_mode
= get_opt_int_tree(term_spec
, "colors");
254 driver
->transparent
= get_opt_bool_tree(term_spec
, "transparency");
256 if (get_opt_bool_tree(term_spec
, "underline")) {
257 driver
->underline
= underline_seqs
;
259 driver
->underline
= NULL
;
263 if (driver
->type
== TERM_LINUX
) {
264 if (get_opt_bool_tree(term_spec
, "restrict_852"))
265 driver
->frame
= frame_restrict
;
267 if (get_opt_bool_tree(term_spec
, "m11_hack"))
268 driver
->frame_seqs
= m11_hack_frame_seqs
;
271 driver
->frame_seqs
= utf8_linux_frame_seqs
;
273 } else if (driver
->type
== TERM_FREEBSD
) {
274 if (get_opt_bool_tree(term_spec
, "m11_hack"))
275 driver
->frame_seqs
= m11_hack_frame_seqs
;
277 } else if (driver
->type
== TERM_VT100
) {
278 driver
->frame
= frame_vt100
;
282 driver
->charsets
[0] = get_opt_codepage_tree(term_spec
, "charset");
283 if (driver
->type
== TERM_LINUX
) {
284 if (get_opt_bool_tree(term_spec
, "restrict_852"))
285 driver
->frame
= frame_restrict
;
287 driver
->charsets
[1] = get_cp_index("cp437");
289 } else if (driver
->type
== TERM_FREEBSD
) {
290 driver
->charsets
[1] = get_cp_index("cp437");
292 } else if (driver
->type
== TERM_VT100
) {
293 driver
->frame
= frame_vt100_u
;
294 driver
->charsets
[1] = get_cp_index("cp437");
296 } else if (driver
->type
== TERM_KOI8
) {
297 driver
->charsets
[1] = get_cp_index("koi8-r");
300 driver
->charsets
[1] = driver
->charsets
[0];
304 driver
->charsets
[0] = -1;
305 if (driver
->type
== TERM_LINUX
) {
306 if (get_opt_bool_tree(term_spec
, "restrict_852"))
307 driver
->frame
= frame_restrict
;
309 if (get_opt_bool_tree(term_spec
, "m11_hack"))
310 driver
->frame_seqs
= m11_hack_frame_seqs
;
312 } else if (driver
->type
== TERM_FREEBSD
) {
313 if (get_opt_bool_tree(term_spec
, "m11_hack"))
314 driver
->frame_seqs
= m11_hack_frame_seqs
;
315 } else if (driver
->type
== TERM_VT100
) {
316 driver
->frame
= frame_vt100
;
319 #endif /* CONFIG_UTF8 */
323 screen_driver_change_hook(struct session
*ses
, struct option
*term_spec
,
324 struct option
*changed
)
326 enum term_mode_type type
= get_opt_int_tree(term_spec
, "type");
327 struct screen_driver
*driver
;
328 unsigned char *name
= term_spec
->name
;
329 int len
= strlen(name
);
331 foreach (driver
, active_screen_drivers
)
332 if (driver
->type
== type
&& !memcmp(driver
->name
, name
, len
)) {
333 update_screen_driver(driver
, term_spec
);
340 static inline struct screen_driver
*
341 add_screen_driver(enum term_mode_type type
, struct terminal
*term
, int env_len
)
343 struct screen_driver
*driver
;
345 /* One byte is reserved for name in struct screen_driver. */
346 driver
= mem_alloc(sizeof(*driver
) + env_len
);
347 if (!driver
) return NULL
;
349 memcpy(driver
, screen_drivers
[type
], sizeof(*driver
) - 1);
350 memcpy(driver
->name
, term
->spec
->name
, env_len
+ 1);
352 add_to_list(active_screen_drivers
, driver
);
354 update_screen_driver(driver
, term
->spec
);
356 term
->spec
->change_hook
= screen_driver_change_hook
;
359 term
->utf8
= use_utf8_io(driver
);
360 #endif /* CONFIG_UTF8 */
365 static inline struct screen_driver
*
366 get_screen_driver(struct terminal
*term
)
368 enum term_mode_type type
= get_opt_int_tree(term
->spec
, "type");
369 unsigned char *name
= term
->spec
->name
;
370 int len
= strlen(name
);
371 struct screen_driver
*driver
;
373 foreach (driver
, active_screen_drivers
) {
374 if (driver
->type
!= type
) continue;
375 if (memcmp(driver
->name
, name
, len
+ 1)) continue;
377 /* Some simple probably useless MRU ;) */
378 move_to_top_of_list(active_screen_drivers
, driver
);
381 term
->utf8
= use_utf8_io(driver
);
382 #endif /* CONFIG_UTF8 */
386 return add_screen_driver(type
, term
, len
);
389 /* Release private screen drawing utilities. */
391 done_screen_drivers(struct module
*xxx
)
393 free_list(active_screen_drivers
);
397 /* Adds the term code for positioning the cursor at @x and @y to @string.
398 * The template term code is: "\033[<@y>;<@x>H" */
399 static inline struct string
*
400 add_cursor_move_to_string(struct string
*screen
, int y
, int x
)
402 #define CURSOR_NUM_LEN 10 /* 10 chars for @y and @x numbers should be more than enough. */
403 unsigned char code
[4 + 2 * CURSOR_NUM_LEN
+ 1];
404 unsigned int length
= 2;
409 if (ulongcat(code
, &length
, y
, CURSOR_NUM_LEN
, 0) < 0)
412 code
[length
++] = ';';
414 if (ulongcat(code
, &length
, x
, CURSOR_NUM_LEN
, 0) < 0)
417 code
[length
++] = 'H';
419 return add_bytes_to_string(screen
, code
, length
);
420 #undef CURSOR_NUM_LEN
423 struct screen_state
{
424 unsigned char border
;
425 unsigned char underline
;
428 /* Following should match struct screen_char color field. */
429 unsigned char color
[SCREEN_COLOR_SIZE
];
432 #if defined(CONFIG_TRUE_COLOR)
433 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }
434 #elif defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
435 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF } }
437 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF } }
440 #ifdef CONFIG_TRUE_COLOR
442 compare_color_true(unsigned char *a
, unsigned char *b
)
444 return !memcmp(a
, b
, 6);
448 compare_bg_color_true(unsigned char *a
, unsigned char *b
)
450 return (a
[3] == b
[3] && a
[4] == b
[4] && a
[5] == b
[5]);
454 compare_fg_color_true(unsigned char *a
, unsigned char *b
)
456 return (a
[0] == b
[0] && a
[1] == b
[1] && a
[2] == b
[2]);
460 copy_color_true(unsigned char *a
, unsigned char *b
)
466 background_is_black(unsigned char *a
)
468 static unsigned char b
[6] = {0, 0, 0, 0, 0, 0};
470 return compare_bg_color_true(a
, b
);
474 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
476 compare_color_256(unsigned char *a
, unsigned char *b
)
478 return (a
[0] == b
[0] && a
[1] == b
[1]);
482 compare_bg_color_256(unsigned char *a
, unsigned char *b
)
484 return (a
[1] == b
[1]);
488 compare_fg_color_256(unsigned char *a
, unsigned char *b
)
490 return (a
[0] == b
[0]);
494 copy_color_256(unsigned char *a
, unsigned char *b
)
502 compare_color_16(unsigned char *a
, unsigned char *b
)
504 return (a
[0] == b
[0]);
508 compare_bg_color_16(unsigned char *a
, unsigned char *b
)
510 return (TERM_COLOR_BACKGROUND_16(a
) == TERM_COLOR_BACKGROUND_16(b
));
514 compare_fg_color_16(unsigned char *a
, unsigned char *b
)
516 return (TERM_COLOR_FOREGROUND_16(a
) == TERM_COLOR_FOREGROUND_16(b
));
520 copy_color_16(unsigned char *a
, unsigned char *b
)
527 add_char_data(struct string
*screen
, struct screen_driver
*driver
,
528 unicode_val_T data
, unsigned char border
)
531 add_char_data(struct string
*screen
, struct screen_driver
*driver
,
532 unsigned char data
, unsigned char border
)
533 #endif /* CONFIG_UTF8 */
535 if (!isscreensafe(data
)) {
536 add_char_to_string(screen
, ' ');
540 if (border
&& driver
->frame
&& data
>= 176 && data
< 224)
541 data
= driver
->frame
[data
- 176];
543 if (use_utf8_io(driver
)) {
546 add_char_to_string(screen
, (unsigned char)data
);
548 if (data
!= UCS_NO_CHAR
)
549 add_to_string(screen
, encode_utf8(data
));
551 int charset
= driver
->charsets
[!!border
];
553 add_to_string(screen
, cp2utf8(charset
, data
));
554 #endif /* CONFIG_UTF8 */
558 add_char_to_string(screen
, (unsigned char)data
);
561 /* Time critical section. */
563 add_char16(struct string
*screen
, struct screen_driver
*driver
,
564 struct screen_char
*ch
, struct screen_state
*state
)
566 unsigned char border
= (ch
->attr
& SCREEN_ATTR_FRAME
);
567 unsigned char underline
= (ch
->attr
& SCREEN_ATTR_UNDERLINE
);
568 unsigned char bold
= (ch
->attr
& SCREEN_ATTR_BOLD
);
572 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
573 #endif /* CONFIG_UTF8 */
574 border
!= state
->border
&& driver
->frame_seqs
576 state
->border
= border
;
577 add_term_string(screen
, driver
->frame_seqs
[!!border
]);
582 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
583 #endif /* CONFIG_UTF8 */
584 underline
!= state
->underline
&& driver
->underline
586 state
->underline
= underline
;
587 add_term_string(screen
, driver
->underline
[!!underline
]);
592 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
593 #endif /* CONFIG_UTF8 */
598 add_bytes_to_string(screen
, "\033[1m", 4);
600 /* Force repainting of the other attributes. */
601 state
->color
[0] = ch
->color
[0] + 1;
607 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
608 #endif /* CONFIG_UTF8 */
609 !compare_color_16(ch
->color
, state
->color
)
611 copy_color_16(state
->color
, ch
->color
);
613 add_bytes_to_string(screen
, "\033[0", 3);
615 /* @update_screen_driver has set @driver->color_mode
616 * according to terminal-type-specific options.
617 * The caller of @add_char16 has already partially
618 * checked it, but there are still these possibilities:
619 * - COLOR_MODE_MONO. Then don't show colors, but
620 * perhaps use the standout attribute.
621 * - COLOR_MODE_16. Use 16 colors.
622 * - An unsupported color mode. Use 16 colors. */
623 if (driver
->color_mode
!= COLOR_MODE_MONO
) {
624 unsigned char code
[6] = ";30;40";
625 unsigned char bgcolor
= TERM_COLOR_BACKGROUND_16(ch
->color
);
627 code
[2] += TERM_COLOR_FOREGROUND_16(ch
->color
);
629 if (!driver
->transparent
|| bgcolor
!= 0) {
631 add_bytes_to_string(screen
, code
, 6);
633 add_bytes_to_string(screen
, code
, 3);
636 } else if (ch
->attr
& SCREEN_ATTR_STANDOUT
) {
637 /* Flip the fore- and background colors for highlighing
639 add_bytes_to_string(screen
, ";7", 2);
642 if (underline
&& driver
->underline
) {
643 add_bytes_to_string(screen
, ";4", 2);
646 /* Check if the char should be rendered bold. */
648 add_bytes_to_string(screen
, ";1", 2);
651 add_bytes_to_string(screen
, "m", 1);
654 add_char_data(screen
, driver
, ch
->data
, border
);
657 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
658 static struct string color256_seqs
[] = {
659 /* foreground: */ TERM_STRING("\033[0;38;5;%dm"),
660 /* background: */ TERM_STRING("\033[48;5;%dm"),
664 add_char_color(struct string
*screen
, struct string
*seq
, unsigned char color
)
666 unsigned char color_buf
[3];
667 unsigned char *color_pos
= color_buf
;
671 check_string_magic(seq
);
672 for (; seq
->source
[seq_pos
] != '%'; seq_pos
++) ;
674 add_bytes_to_string(screen
, seq
->source
, seq_pos
);
696 color2
= (color
% 10);
698 color_buf
[1] = '0' + color
;
702 color_buf
[2] = '0' + color
;
704 add_bytes_to_string(screen
, color_pos
, color_len
);
706 seq_pos
+= 2; /* Skip "%d" */
707 add_bytes_to_string(screen
, &seq
->source
[seq_pos
], seq
->length
- seq_pos
);
710 #define add_background_color(str, seq, chr) add_char_color(str, &(seq)[1], (chr)->color[1])
711 #define add_foreground_color(str, seq, chr) add_char_color(str, &(seq)[0], (chr)->color[0])
713 /* Time critical section. */
715 add_char256(struct string
*screen
, struct screen_driver
*driver
,
716 struct screen_char
*ch
, struct screen_state
*state
)
718 unsigned char attr_delta
= (ch
->attr
^ state
->attr
);
722 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
723 #endif /* CONFIG_UTF8 */
726 if ((attr_delta
& SCREEN_ATTR_FRAME
) && driver
->frame_seqs
) {
727 state
->border
= !!(ch
->attr
& SCREEN_ATTR_FRAME
);
728 add_term_string(screen
, driver
->frame_seqs
[state
->border
]);
731 if ((attr_delta
& SCREEN_ATTR_UNDERLINE
) && driver
->underline
) {
732 state
->underline
= !!(ch
->attr
& SCREEN_ATTR_UNDERLINE
);
733 add_term_string(screen
, driver
->underline
[state
->underline
]);
736 if (attr_delta
& SCREEN_ATTR_BOLD
) {
737 if (ch
->attr
& SCREEN_ATTR_BOLD
) {
738 add_bytes_to_string(screen
, "\033[1m", 4);
740 /* Force repainting of the other attributes. */
741 state
->color
[0] = ch
->color
[0] + 1;
745 state
->attr
= ch
->attr
;
750 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
751 #endif /* CONFIG_UTF8 */
752 !compare_color_256(ch
->color
, state
->color
)
754 copy_color_256(state
->color
, ch
->color
);
756 add_foreground_color(screen
, color256_seqs
, ch
);
757 if (!driver
->transparent
|| ch
->color
[1] != 0) {
758 add_background_color(screen
, color256_seqs
, ch
);
761 if (ch
->attr
& SCREEN_ATTR_BOLD
)
762 add_bytes_to_string(screen
, "\033[1m", 4);
764 if (ch
->attr
& SCREEN_ATTR_UNDERLINE
&& driver
->underline
) {
765 state
->underline
= !!(ch
->attr
& SCREEN_ATTR_UNDERLINE
);
766 add_term_string(screen
, driver
->underline
[state
->underline
]);
770 add_char_data(screen
, driver
, ch
->data
, ch
->attr
& SCREEN_ATTR_FRAME
);
774 #ifdef CONFIG_TRUE_COLOR
775 static struct string color_true_seqs
[] = {
776 /* foreground: */ TERM_STRING("\033[0;38;2"),
777 /* background: */ TERM_STRING("\033[48;2"),
779 #define add_true_background_color(str, seq, chr) add_char_true_color(str, &(seq)[1], &(chr)->color[3])
780 #define add_true_foreground_color(str, seq, chr) add_char_true_color(str, &(seq)[0], &(chr)->color[0])
782 add_char_true_color(struct string
*screen
, struct string
*seq
, unsigned char *colors
)
784 unsigned char color_buf
[3];
787 check_string_magic(seq
);
788 add_string_to_string(screen
, seq
);
789 for (i
= 0; i
< 3; i
++) {
790 unsigned char *color_pos
= color_buf
;
792 unsigned char color
= colors
[i
];
794 add_char_to_string(screen
, ';');
816 color2
= (color
% 10);
818 color_buf
[1] = '0' + color
;
821 color_buf
[2] = '0' + color
;
823 add_bytes_to_string(screen
, color_pos
, color_len
);
825 add_char_to_string(screen
, 'm');
828 /* Time critical section. */
830 add_char_true(struct string
*screen
, struct screen_driver
*driver
,
831 struct screen_char
*ch
, struct screen_state
*state
)
833 unsigned char attr_delta
= (ch
->attr
^ state
->attr
);
837 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
838 #endif /* CONFIG_UTF8 */
841 if ((attr_delta
& SCREEN_ATTR_FRAME
) && driver
->frame_seqs
) {
842 state
->border
= !!(ch
->attr
& SCREEN_ATTR_FRAME
);
843 add_term_string(screen
, driver
->frame_seqs
[state
->border
]);
846 if ((attr_delta
& SCREEN_ATTR_UNDERLINE
) && driver
->underline
) {
847 state
->underline
= !!(ch
->attr
& SCREEN_ATTR_UNDERLINE
);
848 add_term_string(screen
, driver
->underline
[state
->underline
]);
851 if (attr_delta
& SCREEN_ATTR_BOLD
) {
852 if (ch
->attr
& SCREEN_ATTR_BOLD
) {
853 add_bytes_to_string(screen
, "\033[1m", 4);
855 /* Force repainting of the other attributes. */
856 state
->color
[0] = ch
->color
[0] + 1;
860 state
->attr
= ch
->attr
;
865 (!use_utf8_io(driver
) || ch
->data
!= UCS_NO_CHAR
) &&
866 #endif /* CONFIG_UTF8 */
867 !compare_color_true(ch
->color
, state
->color
)
869 copy_color_true(state
->color
, ch
->color
);
871 add_true_foreground_color(screen
, color_true_seqs
, ch
);
872 if (!driver
->transparent
|| !background_is_black(ch
->color
)) {
873 add_true_background_color(screen
, color_true_seqs
, ch
);
876 if (ch
->attr
& SCREEN_ATTR_BOLD
)
877 add_bytes_to_string(screen
, "\033[1m", 4);
879 if (ch
->attr
& SCREEN_ATTR_UNDERLINE
&& driver
->underline
) {
880 state
->underline
= !!(ch
->attr
& SCREEN_ATTR_UNDERLINE
);
881 add_term_string(screen
, driver
->underline
[state
->underline
]);
885 add_char_data(screen
, driver
, ch
->data
, ch
->attr
& SCREEN_ATTR_FRAME
);
889 #define add_chars(image_, term_, driver_, state_, ADD_CHAR, compare_bg_color, compare_fg_color) \
891 struct terminal_screen *screen = (term_)->screen; \
892 int y = screen->dirty_from; \
893 int ypos = y * (term_)->width; \
895 int xmax = (term_)->width - 1; \
896 int ymax = (term_)->height - 1; \
897 struct screen_char *current = &screen->last_image[ypos]; \
898 struct screen_char *pos = &screen->image[ypos]; \
899 struct screen_char *prev_pos = NULL; /* Warning prevention. */ \
901 int_upper_bound(&screen->dirty_to, ymax); \
903 for (; y <= screen->dirty_to; y++) { \
904 int is_last_line = (y == ymax); \
907 for (; x <= xmax; x++, current++, pos++) { \
908 /* Workaround for terminals without
909 * "eat_newline_glitch (xn)", e.g., the cons25 family
910 * of terminals and cygwin terminal.
911 * It prevents display distortion, but char at bottom
912 * right of terminal will not be drawn.
913 * A better fix would be to correctly detects
914 * terminal type, and/or add a terminal option for
917 if (is_last_line && x == xmax) \
920 if (compare_bg_color(pos->color, current->color)) { \
921 /* No update for exact match. */ \
922 if (compare_fg_color(pos->color, current->color)\
923 && pos->data == current->data \
924 && pos->attr == current->attr) \
927 /* Else if the color match and the data is
929 if (pos->data <= ' ' && current->data <= ' ' \
930 && pos->attr == current->attr) \
934 /* Move the cursor when @prev_pos is more than 10 chars
936 if (prev_y != y || prev_pos + 10 <= pos) { \
937 add_cursor_move_to_string(image_, y + 1, x + 1);\
942 for (; prev_pos <= pos ; prev_pos++) \
943 ADD_CHAR(image_, driver_, prev_pos, state_); \
948 /* Updating of the terminal screen is done by checking what needs to be updated
949 * using the last screen. */
951 redraw_screen(struct terminal
*term
)
953 struct screen_driver
*driver
;
955 struct screen_state state
= INIT_SCREEN_STATE
;
956 struct terminal_screen
*screen
= term
->screen
;
958 if (!screen
|| screen
->dirty_from
> screen
->dirty_to
) return;
959 if (term
->master
&& is_blocked()) return;
961 driver
= get_screen_driver(term
);
964 if (!init_string(&image
)) return;
966 switch (driver
->color_mode
) {
968 /* If the desired color mode was not compiled in,
970 case COLOR_MODE_MONO
:
972 add_chars(&image
, term
, driver
, &state
, add_char16
, compare_bg_color_16
, compare_fg_color_16
);
974 #ifdef CONFIG_88_COLORS
976 add_chars(&image
, term
, driver
, &state
, add_char256
, compare_bg_color_256
, compare_fg_color_256
);
979 #ifdef CONFIG_256_COLORS
981 add_chars(&image
, term
, driver
, &state
, add_char256
, compare_bg_color_256
, compare_fg_color_256
);
984 #ifdef CONFIG_TRUE_COLOR
985 case COLOR_MODE_TRUE_COLOR
:
986 add_chars(&image
, term
, driver
, &state
, add_char_true
, compare_bg_color_true
, compare_fg_color_true
);
990 case COLOR_MODE_DUMP
:
991 INTERNAL("Invalid color mode (%d).", driver
->color_mode
);
996 if (driver
->color_mode
!= COLOR_MODE_MONO
)
997 add_bytes_to_string(&image
, "\033[37;40m", 8);
999 add_bytes_to_string(&image
, "\033[0m", 4);
1001 /* If we ended in border state end the frame mode. */
1002 if (state
.border
&& driver
->frame_seqs
)
1003 add_term_string(&image
, driver
->frame_seqs
[0]);
1007 /* Even if nothing was redrawn, we possibly still need to move
1010 || screen
->cx
!= screen
->lcx
1011 || screen
->cy
!= screen
->lcy
) {
1012 screen
->lcx
= screen
->cx
;
1013 screen
->lcy
= screen
->cy
;
1015 add_cursor_move_to_string(&image
, screen
->cy
+ 1,
1020 if (term
->master
) want_draw();
1021 hard_write(term
->fdout
, image
.source
, image
.length
);
1022 if (term
->master
) done_draw();
1025 done_string(&image
);
1027 copy_screen_chars(screen
->last_image
, screen
->image
, term
->width
* term
->height
);
1028 screen
->dirty_from
= term
->height
;
1029 screen
->dirty_to
= 0;
1033 erase_screen(struct terminal
*term
)
1036 if (is_blocked()) return;
1040 hard_write(term
->fdout
, "\033[2J\033[1;1H", 10);
1041 if (term
->master
) done_draw();
1045 beep_terminal(struct terminal
*term
)
1047 #ifdef CONFIG_OS_WIN32
1048 MessageBeep(MB_ICONEXCLAMATION
);
1050 hard_write(term
->fdout
, "\a", 1);
1054 struct terminal_screen
*
1057 struct terminal_screen
*screen
;
1059 screen
= mem_calloc(1, sizeof(*screen
));
1060 if (!screen
) return NULL
;
1068 /* The two images are allocated in one chunk. */
1069 /* TODO: It seems allocation failure here is fatal. We should do something! */
1071 resize_screen(struct terminal
*term
, int width
, int height
)
1073 struct terminal_screen
*screen
;
1074 struct screen_char
*image
;
1077 assert(term
&& term
->screen
);
1079 screen
= term
->screen
;
1082 assert(height
>= 0);
1084 size
= width
* height
;
1085 if (size
<= 0) return;
1087 bsize
= size
* sizeof(*image
);
1089 image
= mem_realloc(screen
->image
, bsize
* 2);
1092 screen
->image
= image
;
1093 screen
->last_image
= image
+ size
;
1095 memset(screen
->image
, 0, bsize
);
1096 memset(screen
->last_image
, 0xFF, bsize
);
1098 term
->width
= width
;
1099 term
->height
= height
;
1100 set_screen_dirty(screen
, 0, height
);
1104 done_screen(struct terminal_screen
*screen
)
1106 mem_free_if(screen
->image
);
1110 struct module terminal_screen_module
= struct_module(
1111 /* name: */ "Terminal Screen",
1112 /* options: */ NULL
,
1114 /* submodules: */ NULL
,
1117 /* done: */ done_screen_drivers