elinks-0.11.4rc1
[elinks/elinks-j605.git] / src / terminal / screen.c
blob4ec286964e35ea5a2eee062a0929927d494bb6cb
1 /* Terminal screen drawing routines. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "config/options.h"
13 #include "intl/charsets.h"
14 #include "osdep/ascii.h"
15 #include "osdep/osdep.h"
16 #include "terminal/color.h"
17 #include "terminal/draw.h"
18 #include "terminal/hardio.h"
19 #include "terminal/kbd.h"
20 #include "terminal/screen.h"
21 #include "terminal/terminal.h"
22 #include "util/conv.h"
23 #include "util/error.h"
24 #include "util/memory.h"
25 #include "util/string.h"
28 /* TODO: We must use termcap/terminfo if available! --pasky */
30 unsigned char frame_dumb[48] = " ||||++||++++++--|-+||++--|-+----++++++++ ";
31 static unsigned char frame_vt100[48] = "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla ";
33 /* For UTF8 I/O */
34 static unsigned char frame_vt100_u[48] = {
35 177, 177, 177, 179, 180, 180, 180, 191,
36 191, 180, 179, 191, 217, 217, 217, 191,
37 192, 193, 194, 195, 196, 197, 195, 195,
38 192, 218, 193, 194, 195, 196, 197, 193,
39 193, 194, 194, 192, 192, 218, 218, 197,
40 197, 217, 218, 177, 32, 32, 32, 32
43 static unsigned char frame_freebsd[48] = {
44 130, 138, 128, 153, 150, 150, 150, 140,
45 140, 150, 153, 140, 139, 139, 139, 140,
46 142, 151, 152, 149, 146, 143, 149, 149,
47 142, 141, 151, 152, 149, 146, 143, 151,
48 151, 152, 152, 142, 142, 141, 141, 143,
49 143, 139, 141, 128, 128, 128, 128, 128,
52 static unsigned char frame_koi[48] = {
53 144, 145, 146, 129, 135, 178, 180, 167,
54 166, 181, 161, 168, 174, 173, 172, 131,
55 132, 137, 136, 134, 128, 138, 175, 176,
56 171, 165, 187, 184, 177, 160, 190, 185,
57 186, 182, 183, 170, 169, 162, 164, 189,
58 188, 133, 130, 141, 140, 142, 143, 139,
61 /* Most of this table is just 176 + <index in table>. */
62 static unsigned char frame_restrict[48] = {
63 176, 177, 178, 179, 180, 179, 186, 186,
64 205, 185, 186, 187, 188, 186, 205, 191,
65 192, 193, 194, 195, 196, 197, 179, 186,
66 200, 201, 202, 203, 204, 205, 206, 205,
67 196, 205, 196, 186, 205, 205, 186, 186,
68 179, 217, 218, 219, 220, 221, 222, 223,
71 #define TERM_STRING(str) INIT_STRING(str, sizeof(str) - 1)
73 #define add_term_string(str, tstr) \
74 add_bytes_to_string(str, (tstr).source, (tstr).length)
76 static struct string m11_hack_frame_seqs[] = {
77 /* end border: */ TERM_STRING("\033[10m"),
78 /* begin border: */ TERM_STRING("\033[11m"),
81 static struct string vt100_frame_seqs[] = {
82 /* end border: */ TERM_STRING("\x0f"),
83 /* begin border: */ TERM_STRING("\x0e"),
86 static struct string underline_seqs[] = {
87 /* begin underline: */ TERM_STRING("\033[24m"),
88 /* end underline: */ TERM_STRING("\033[4m"),
91 /* Used in {add_char*()} and {redraw_screen()} to reduce the logic. It is
92 * updated from terminal._template_.* using option change_hooks. */
93 /* TODO: termcap/terminfo can maybe gradually be introduced via this
94 * structure. We'll see. --jonas */
95 struct screen_driver {
96 LIST_HEAD(struct screen_driver);
98 /* The terminal._template_.type. Together with the @name member the
99 * uniquely identify the screen_driver. */
100 enum term_mode_type type;
102 /* Charsets when doing UTF8 I/O. */
103 /* [0] is the common charset and [1] is the frame charset.
104 * Test wether to use UTF8 I/O using the use_utf8_io() macro. */
105 int charsets[2];
107 /* The frame translation table. May be NULL. */
108 unsigned char *frame;
110 /* The frame mode setup and teardown sequences. May be NULL. */
111 struct string *frame_seqs;
113 /* The underline mode setup and teardown sequences. May be NULL. */
114 struct string *underline;
116 /* The color mode */
117 enum color_mode color_mode;
119 /* These are directly derived from the terminal options. */
120 unsigned int transparent:1;
122 /* The terminal._template_ name. */
123 unsigned char name[1]; /* XXX: Keep last! */
126 static struct screen_driver dumb_screen_driver = {
127 NULL_LIST_HEAD,
128 /* type: */ TERM_DUMB,
129 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
130 /* frame: */ frame_dumb,
131 /* frame_seqs: */ NULL,
132 /* underline: */ underline_seqs,
133 /* color_mode: */ COLOR_MODE_16,
134 /* transparent: */ 1,
137 static struct screen_driver vt100_screen_driver = {
138 NULL_LIST_HEAD,
139 /* type: */ TERM_VT100,
140 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
141 /* frame: */ frame_vt100,
142 /* frame_seqs: */ vt100_frame_seqs,
143 /* underline: */ underline_seqs,
144 /* color_mode: */ COLOR_MODE_16,
145 /* transparent: */ 1,
148 static struct screen_driver linux_screen_driver = {
149 NULL_LIST_HEAD,
150 /* type: */ TERM_LINUX,
151 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
152 /* frame: */ NULL, /* No restrict_852 */
153 /* frame_seqs: */ NULL, /* No m11_hack */
154 /* underline: */ underline_seqs,
155 /* color_mode: */ COLOR_MODE_16,
156 /* transparent: */ 1,
159 static struct screen_driver koi8_screen_driver = {
160 NULL_LIST_HEAD,
161 /* type: */ TERM_KOI8,
162 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
163 /* frame: */ frame_koi,
164 /* frame_seqs: */ NULL,
165 /* underline: */ underline_seqs,
166 /* color_mode: */ COLOR_MODE_16,
167 /* transparent: */ 1,
170 static struct screen_driver freebsd_screen_driver = {
171 NULL_LIST_HEAD,
172 /* type: */ TERM_FREEBSD,
173 /* charsets: */ { -1, -1 }, /* No UTF8 I/O */
174 /* frame: */ frame_freebsd,
175 /* frame_seqs: */ NULL, /* No m11_hack */
176 /* underline: */ underline_seqs,
177 /* color_mode: */ COLOR_MODE_16,
178 /* transparent: */ 1,
181 /* XXX: Keep in sync with enum term_mode_type. */
182 static struct screen_driver *screen_drivers[] = {
183 /* TERM_DUMB: */ &dumb_screen_driver,
184 /* TERM_VT100: */ &vt100_screen_driver,
185 /* TERM_LINUX: */ &linux_screen_driver,
186 /* TERM_KOI8: */ &koi8_screen_driver,
187 /* TERM_FREEBSD: */ &freebsd_screen_driver,
191 static INIT_LIST_HEAD(active_screen_drivers);
193 static void
194 update_screen_driver(struct screen_driver *driver, struct option *term_spec)
196 int utf8_io = get_opt_bool_tree(term_spec, "utf_8_io");
198 driver->color_mode = get_opt_int_tree(term_spec, "colors");
199 driver->transparent = get_opt_bool_tree(term_spec, "transparency");
201 if (get_opt_bool_tree(term_spec, "underline")) {
202 driver->underline = underline_seqs;
203 } else {
204 driver->underline = NULL;
207 if (utf8_io) {
208 driver->charsets[0] = get_opt_codepage_tree(term_spec, "charset");
209 if (driver->type == TERM_LINUX) {
210 if (get_opt_bool_tree(term_spec, "restrict_852"))
211 driver->frame = frame_restrict;
213 driver->charsets[1] = get_cp_index("cp437");
215 } else if (driver->type == TERM_FREEBSD) {
216 driver->charsets[1] = get_cp_index("cp437");
218 } else if (driver->type == TERM_VT100) {
219 driver->frame = frame_vt100_u;
220 driver->charsets[1] = get_cp_index("cp437");
222 } else if (driver->type == TERM_KOI8) {
223 driver->charsets[1] = get_cp_index("koi8-r");
225 } else {
226 driver->charsets[1] = driver->charsets[0];
229 } else {
230 driver->charsets[0] = -1;
231 if (driver->type == TERM_LINUX) {
232 if (get_opt_bool_tree(term_spec, "restrict_852"))
233 driver->frame = frame_restrict;
235 if (get_opt_bool_tree(term_spec, "m11_hack"))
236 driver->frame_seqs = m11_hack_frame_seqs;
238 } else if (driver->type == TERM_FREEBSD) {
239 if (get_opt_bool_tree(term_spec, "m11_hack"))
240 driver->frame_seqs = m11_hack_frame_seqs;
242 } else if (driver->type == TERM_VT100) {
243 driver->frame = frame_vt100;
248 static int
249 screen_driver_change_hook(struct session *ses, struct option *term_spec,
250 struct option *changed)
252 enum term_mode_type type = get_opt_int_tree(term_spec, "type");
253 struct screen_driver *driver;
254 unsigned char *name = term_spec->name;
256 foreach (driver, active_screen_drivers)
257 if (driver->type == type && !strcmp(driver->name, name)) {
258 update_screen_driver(driver, term_spec);
259 break;
262 return 0;
265 static inline struct screen_driver *
266 add_screen_driver(enum term_mode_type type, struct terminal *term, int env_len)
268 struct screen_driver *driver;
270 /* One byte is reserved for name in struct screen_driver. */
271 driver = mem_alloc(sizeof(*driver) + env_len);
272 if (!driver) return NULL;
274 memcpy(driver, screen_drivers[type], sizeof(*driver) - 1);
275 memcpy(driver->name, term->spec->name, env_len + 1);
277 add_to_list(active_screen_drivers, driver);
279 update_screen_driver(driver, term->spec);
281 term->spec->change_hook = screen_driver_change_hook;
283 return driver;
286 static inline struct screen_driver *
287 get_screen_driver(struct terminal *term)
289 enum term_mode_type type = get_opt_int_tree(term->spec, "type");
290 unsigned char *name = term->spec->name;
291 int len = strlen(name);
292 struct screen_driver *driver;
294 foreach (driver, active_screen_drivers) {
295 if (driver->type != type) continue;
296 if (strcmp(driver->name, name)) continue;
298 /* Some simple probably useless MRU ;) */
299 move_to_top_of_list(active_screen_drivers, driver);
301 return driver;
304 return add_screen_driver(type, term, len);
307 void
308 done_screen_drivers(void)
310 free_list(active_screen_drivers);
314 /* Adds the term code for positioning the cursor at @x and @y to @string.
315 * The template term code is: "\033[<@y>;<@x>H" */
316 static inline struct string *
317 add_cursor_move_to_string(struct string *screen, int y, int x)
319 #define CURSOR_NUM_LEN 10 /* 10 chars for @y and @x numbers should be more than enough. */
320 unsigned char code[4 + 2 * CURSOR_NUM_LEN + 1];
321 unsigned int length = 2;
323 code[0] = '\033';
324 code[1] = '[';
326 if (ulongcat(code, &length, y, CURSOR_NUM_LEN, 0) < 0)
327 return screen;
329 code[length++] = ';';
331 if (ulongcat(code, &length, x, CURSOR_NUM_LEN, 0) < 0)
332 return screen;
334 code[length++] = 'H';
336 return add_bytes_to_string(screen, code, length);
337 #undef CURSOR_NUM_LEN
340 struct screen_state {
341 unsigned char border;
342 unsigned char underline;
343 unsigned char bold;
344 unsigned char attr;
345 /* Following should match struct screen_char color field. */
346 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
347 unsigned char color[2];
348 #else
349 unsigned char color[1];
350 #endif
353 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
354 #define compare_color(a, b) ((a)[0] == (b)[0] && (a)[1] == (b)[1])
355 #define copy_color(a, b) do { (a)[0] = (b)[0]; (a)[1] = (b)[1]; } while (0)
356 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF } }
357 #else
358 #define compare_color(a, b) ((a)[0] == (b)[0])
359 #define copy_color(a, b) do { (a)[0] = (b)[0]; } while (0)
360 #define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF } }
361 #endif
363 #define compare_bg_color(a, b) (TERM_COLOR_BACKGROUND(a) == TERM_COLOR_BACKGROUND(b))
364 #define compare_fg_color(a, b) (TERM_COLOR_FOREGROUND(a) == TERM_COLOR_FOREGROUND(b))
366 #define use_utf8_io(driver) ((driver)->charsets[0] != -1)
368 static inline void
369 add_char_data(struct string *screen, struct screen_driver *driver,
370 unsigned char data, unsigned char border)
372 if (!isscreensafe(data)) {
373 add_char_to_string(screen, ' ');
374 return;
377 if (border && driver->frame && data >= 176 && data < 224)
378 data = driver->frame[data - 176];
380 if (use_utf8_io(driver)) {
381 int charset = driver->charsets[!!border];
383 add_to_string(screen, cp2utf_8(charset, data));
384 return;
387 add_char_to_string(screen, data);
390 /* Time critical section. */
391 static inline void
392 add_char16(struct string *screen, struct screen_driver *driver,
393 struct screen_char *ch, struct screen_state *state)
395 unsigned char border = (ch->attr & SCREEN_ATTR_FRAME);
396 unsigned char underline = (ch->attr & SCREEN_ATTR_UNDERLINE);
397 unsigned char bold = (ch->attr & SCREEN_ATTR_BOLD);
399 if (border != state->border && driver->frame_seqs) {
400 state->border = border;
401 add_term_string(screen, driver->frame_seqs[!!border]);
404 if (underline != state->underline && driver->underline) {
405 state->underline = underline;
406 add_term_string(screen, driver->underline[!!underline]);
409 if (bold != state->bold) {
410 state->bold = bold;
411 if (bold) {
412 add_bytes_to_string(screen, "\033[1m", 4);
413 } else {
414 /* Force repainting of the other attributes. */
415 state->color[0] = ch->color[0] + 1;
419 if (!compare_color(ch->color, state->color)) {
420 copy_color(state->color, ch->color);
422 add_bytes_to_string(screen, "\033[0", 3);
424 if (driver->color_mode == COLOR_MODE_16) {
425 unsigned char code[6] = ";30;40";
426 unsigned char bgcolor = TERM_COLOR_BACKGROUND(ch->color);
428 code[2] += TERM_COLOR_FOREGROUND(ch->color);
430 if (!driver->transparent || bgcolor != 0) {
431 code[5] += bgcolor;
432 add_bytes_to_string(screen, code, 6);
433 } else {
434 add_bytes_to_string(screen, code, 3);
437 } else if (ch->attr & SCREEN_ATTR_STANDOUT) {
438 /* Flip the fore- and background colors for highlighing
439 * purposes. */
440 add_bytes_to_string(screen, ";7", 2);
443 if (underline && driver->underline) {
444 add_bytes_to_string(screen, ";4", 2);
447 /* Check if the char should be rendered bold. */
448 if (bold) {
449 add_bytes_to_string(screen, ";1", 2);
452 add_bytes_to_string(screen, "m", 1);
455 add_char_data(screen, driver, ch->data, border);
458 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
459 static struct string color256_seqs[] = {
460 /* foreground: */ TERM_STRING("\033[0;38;5;%dm"),
461 /* background: */ TERM_STRING("\033[48;5;%dm"),
464 static inline void
465 add_char_color(struct string *screen, struct string *seq, unsigned char color)
467 unsigned char color_buf[3];
468 unsigned char *color_pos = color_buf;
469 int seq_pos = 0;
470 int color_len = 1;
472 check_string_magic(seq);
473 for (; seq->source[seq_pos] != '%'; seq_pos++) ;
475 add_bytes_to_string(screen, seq->source, seq_pos);
477 if (color < 10) {
478 color_pos += 2;
479 } else {
480 int color2;
482 ++color_len;
483 if (color < 100) {
484 ++color_pos;
485 } else {
486 ++color_len;
488 if (color < 200) {
489 color_buf[0] = '1';
490 color -= 100;
491 } else {
492 color_buf[0] = '2';
493 color -= 200;
497 color2 = (color % 10);
498 color /= 10;
499 color_buf[1] = '0' + color;
500 color = color2;
503 color_buf[2] = '0' + color;
505 add_bytes_to_string(screen, color_pos, color_len);
507 seq_pos += 2; /* Skip "%d" */
508 add_bytes_to_string(screen, &seq->source[seq_pos], seq->length - seq_pos);
511 #define add_background_color(str, seq, chr) add_char_color(str, &(seq)[1], (chr)->color[1])
512 #define add_foreground_color(str, seq, chr) add_char_color(str, &(seq)[0], (chr)->color[0])
514 /* Time critical section. */
515 static inline void
516 add_char256(struct string *screen, struct screen_driver *driver,
517 struct screen_char *ch, struct screen_state *state)
519 unsigned char attr_delta = (ch->attr ^ state->attr);
521 if (attr_delta) {
522 if ((attr_delta & SCREEN_ATTR_FRAME) && driver->frame_seqs) {
523 state->border = !!(ch->attr & SCREEN_ATTR_FRAME);
524 add_term_string(screen, driver->frame_seqs[state->border]);
527 if ((attr_delta & SCREEN_ATTR_UNDERLINE) && driver->underline) {
528 state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
529 add_term_string(screen, driver->underline[state->underline]);
532 if (attr_delta & SCREEN_ATTR_BOLD) {
533 if (ch->attr & SCREEN_ATTR_BOLD) {
534 add_bytes_to_string(screen, "\033[1m", 4);
535 } else {
536 /* Force repainting of the other attributes. */
537 state->color[0] = ch->color[0] + 1;
541 state->attr = ch->attr;
544 if (!compare_color(ch->color, state->color)) {
545 copy_color(state->color, ch->color);
547 add_foreground_color(screen, color256_seqs, ch);
548 if (!driver->transparent || ch->color[1] != 0) {
549 add_background_color(screen, color256_seqs, ch);
552 if (ch->attr & SCREEN_ATTR_BOLD)
553 add_bytes_to_string(screen, "\033[1m", 4);
555 if (ch->attr & SCREEN_ATTR_UNDERLINE && driver->underline) {
556 state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
557 add_term_string(screen, driver->underline[state->underline]);
561 add_char_data(screen, driver, ch->data, ch->attr & SCREEN_ATTR_FRAME);
563 #endif
565 #define add_chars(image_, term_, driver_, state_, ADD_CHAR) \
567 struct terminal_screen *screen = (term_)->screen; \
568 int y = screen->dirty_from; \
569 int ypos = y * (term_)->width; \
570 int prev_y = -1; \
571 int xmax = (term_)->width - 1; \
572 int ymax = (term_)->height - 1; \
573 struct screen_char *current = &screen->last_image[ypos]; \
574 struct screen_char *pos = &screen->image[ypos]; \
575 struct screen_char *prev_pos = NULL; /* Warning prevention. */ \
577 int_upper_bound(&screen->dirty_to, ymax); \
579 for (; y <= screen->dirty_to; y++) { \
580 int is_last_line = (y == ymax); \
581 int x = 0; \
583 for (; x <= xmax; x++, current++, pos++) { \
584 /* Workaround for terminals without
585 * "eat_newline_glitch (xn)", e.g., the cons25 family
586 * of terminals and cygwin terminal.
587 * It prevents display distortion, but char at bottom
588 * right of terminal will not be drawn.
589 * A better fix would be to correctly detects
590 * terminal type, and/or add a terminal option for
591 * this purpose. */ \
593 if (is_last_line && x == xmax) \
594 break; \
596 if (compare_bg_color(pos->color, current->color)) { \
597 /* No update for exact match. */ \
598 if (compare_fg_color(pos->color, current->color)\
599 && pos->data == current->data \
600 && pos->attr == current->attr) \
601 continue; \
603 /* Else if the color match and the data is
604 * ``space''. */ \
605 if (pos->data <= ' ' && current->data <= ' ' \
606 && pos->attr == current->attr) \
607 continue; \
610 /* Move the cursor when @prev_pos is more than 10 chars
611 * away. */ \
612 if (prev_y != y || prev_pos + 10 <= pos) { \
613 add_cursor_move_to_string(image_, y + 1, x + 1);\
614 prev_pos = pos; \
615 prev_y = y; \
618 for (; prev_pos <= pos ; prev_pos++) \
619 ADD_CHAR(image_, driver_, prev_pos, state_); \
624 /* Updating of the terminal screen is done by checking what needs to be updated
625 * using the last screen. */
626 void
627 redraw_screen(struct terminal *term)
629 struct screen_driver *driver;
630 struct string image;
631 struct screen_state state = INIT_SCREEN_STATE;
632 struct terminal_screen *screen = term->screen;
634 if (!screen || screen->dirty_from > screen->dirty_to) return;
635 if (term->master && is_blocked()) return;
637 driver = get_screen_driver(term);
638 if (!driver) return;
640 if (!init_string(&image)) return;
642 switch (driver->color_mode) {
643 case COLOR_MODE_MONO:
644 case COLOR_MODE_16:
645 add_chars(&image, term, driver, &state, add_char16);
646 break;
647 #ifdef CONFIG_88_COLORS
648 case COLOR_MODE_88:
649 add_chars(&image, term, driver, &state, add_char256);
650 break;
651 #endif
652 #ifdef CONFIG_256_COLORS
653 case COLOR_MODE_256:
654 add_chars(&image, term, driver, &state, add_char256);
655 break;
656 #endif
657 case COLOR_MODES:
658 case COLOR_MODE_DUMP:
659 default:
660 INTERNAL("Invalid color mode (%d).", driver->color_mode);
661 return;
664 if (image.length) {
665 if (driver->color_mode)
666 add_bytes_to_string(&image, "\033[37;40m", 8);
668 add_bytes_to_string(&image, "\033[0m", 4);
670 /* If we ended in border state end the frame mode. */
671 if (state.border && driver->frame_seqs)
672 add_term_string(&image, driver->frame_seqs[0]);
676 /* Even if nothing was redrawn, we possibly still need to move
677 * cursor. */
678 if (image.length
679 || screen->cx != screen->lcx
680 || screen->cy != screen->lcy) {
681 screen->lcx = screen->cx;
682 screen->lcy = screen->cy;
684 add_cursor_move_to_string(&image, screen->cy + 1,
685 screen->cx + 1);
688 if (image.length) {
689 if (term->master) want_draw();
690 hard_write(term->fdout, image.source, image.length);
691 if (term->master) done_draw();
694 done_string(&image);
696 copy_screen_chars(screen->last_image, screen->image, term->width * term->height);
697 screen->dirty_from = term->height;
698 screen->dirty_to = 0;
701 void
702 erase_screen(struct terminal *term)
704 if (term->master) {
705 if (is_blocked()) return;
706 want_draw();
709 hard_write(term->fdout, "\033[2J\033[1;1H", 10);
710 if (term->master) done_draw();
713 void
714 beep_terminal(struct terminal *term)
716 #ifdef CONFIG_WIN32
717 MessageBeep(MB_ICONEXCLAMATION);
718 #else
719 hard_write(term->fdout, "\a", 1);
720 #endif
723 struct terminal_screen *
724 init_screen(void)
726 struct terminal_screen *screen;
728 screen = mem_calloc(1, sizeof(*screen));
729 if (!screen) return NULL;
731 screen->lcx = -1;
732 screen->lcy = -1;
734 return screen;
737 /* The two images are allocated in one chunk. */
738 /* TODO: It seems allocation failure here is fatal. We should do something! */
739 void
740 resize_screen(struct terminal *term, int width, int height)
742 struct terminal_screen *screen;
743 struct screen_char *image;
744 size_t size, bsize;
746 assert(term && term->screen);
748 screen = term->screen;
750 assert(width >= 0);
751 assert(height >= 0);
753 size = width * height;
754 if (size <= 0) return;
756 bsize = size * sizeof(*image);
758 image = mem_realloc(screen->image, bsize * 2);
759 if (!image) return;
761 screen->image = image;
762 screen->last_image = image + size;
764 memset(screen->image, 0, bsize);
765 memset(screen->last_image, 0xFF, bsize);
767 term->width = width;
768 term->height = height;
769 set_screen_dirty(screen, 0, height);
772 void
773 done_screen(struct terminal_screen *screen)
775 mem_free_if(screen->image);
776 mem_free(screen);