links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

terminal.c (27780B)


      1 /* terminal.c
      2  * (c) 2002 Mikulas Patocka
      3  * This file is a part of the Links program, released under GPL.
      4  */
      5 
      6 #include <limits.h>
      7 
      8 #include "links.h"
      9 
     10 static void in_term(void *);
     11 static void check_if_no_terminal(void);
     12 
     13 int
     14 hard_write(int fd, const unsigned char *p, int l)
     15 {
     16 	int t = 0;
     17 	while (l > 0) {
     18 		int w;
     19 		EINTRLOOP(w, (int)write(fd, p, l));
     20 		if (w < 0)
     21 			return -1;
     22 		if (!w) {
     23 			errno = ENOSPC;
     24 			break;
     25 		}
     26 		t += w;
     27 		p += w;
     28 		l -= w;
     29 	}
     30 	return t;
     31 }
     32 
     33 int
     34 hard_read(int fd, unsigned char *p, int l)
     35 {
     36 	int r = 1;
     37 	int t = 0;
     38 	while (l > 0 && r) {
     39 		EINTRLOOP(r, (int)read(fd, p, l));
     40 		if (r < 0)
     41 			return -1;
     42 		t += r;
     43 		p += r;
     44 		l -= r;
     45 	}
     46 	return t;
     47 }
     48 
     49 unsigned char *
     50 get_cwd(void)
     51 {
     52 	int bufsize = 128;
     53 	unsigned char *buf;
     54 	unsigned char *gcr;
     55 	while (1) {
     56 		buf = xmalloc(bufsize);
     57 		ENULLLOOP(gcr, cast_uchar getcwd(cast_char buf, bufsize));
     58 		if (gcr)
     59 			return buf;
     60 		free(buf);
     61 		if (errno != ERANGE)
     62 			break;
     63 		if ((unsigned)bufsize > INT_MAX - 128)
     64 			overalloc();
     65 		bufsize += 128;
     66 	}
     67 	return NULL;
     68 }
     69 
     70 void
     71 set_cwd(unsigned char *path)
     72 {
     73 	int rs;
     74 	if (path)
     75 		EINTRLOOP(rs, chdir(cast_const_char path));
     76 }
     77 
     78 unsigned char
     79 get_attribute(int fg, int bg)
     80 {
     81 	return ((bg & 7) << 3) | (fg & 7) | ((fg & 8) << 3);
     82 }
     83 
     84 struct list_head terminals = { &terminals, &terminals };
     85 
     86 static void
     87 set_margin(struct terminal *term)
     88 {
     89 	term->left_margin = 0;
     90 	term->x = term->real_x;
     91 	term->top_margin = 0;
     92 	term->y = term->real_y;
     93 }
     94 
     95 static void
     96 alloc_term_screen(struct terminal *term)
     97 {
     98 	chr *s, *t;
     99 	if (term->x < 0)
    100 		term->x = 1;
    101 	if (term->y < 0)
    102 		term->y = 1;
    103 	if ((term->x && term->x * term->y / term->x != term->y)
    104 	    || term->x * term->y > INT_MAX / sizeof(*term->screen))
    105 		overalloc();
    106 	s = xrealloc(term->screen, term->x * term->y * sizeof(*term->screen));
    107 	t = xrealloc(term->last_screen,
    108 	             term->x * term->y * sizeof(*term->screen));
    109 	memset(t, -1, term->x * term->y * sizeof(*term->screen));
    110 	term->last_screen = t;
    111 	memset(s, 0, term->x * term->y * sizeof(*term->screen));
    112 	term->screen = s;
    113 	term->dirty = 1;
    114 	term->lcx = -1;
    115 	term->lcy = -1;
    116 }
    117 
    118 static void
    119 clear_terminal(struct terminal *term)
    120 {
    121 	fill_area(term, 0, 0, term->x, term->y, ' ', 0);
    122 	set_cursor(term, 0, 0, 0, 0);
    123 }
    124 
    125 void
    126 redraw_below_window(struct window *win)
    127 {
    128 	int tr;
    129 	struct terminal *term = win->term;
    130 	struct window *w = NULL;
    131 	struct list_head *lw;
    132 	struct links_event ev = { EV_REDRAW, 0, 0, 0 };
    133 	ev.x = term->x;
    134 	ev.y = term->y;
    135 	if (term->redrawing >= 2)
    136 		return;
    137 	tr = term->redrawing;
    138 	win->term->redrawing = 2;
    139 	foreachback (struct window, w, lw, term->windows) {
    140 		if (w == win)
    141 			break;
    142 		w->handler(w, &ev, 0);
    143 	}
    144 	term->redrawing = tr;
    145 }
    146 
    147 static void
    148 redraw_terminal_ev(struct terminal *term, int e)
    149 {
    150 	struct window *win = NULL;
    151 	struct list_head *lwin;
    152 	struct links_event ev = { 0, 0, 0, 0 };
    153 	ev.ev = e;
    154 	ev.x = term->x;
    155 	ev.y = term->y;
    156 	clear_terminal(term);
    157 	term->redrawing = 2;
    158 	foreachback (struct window, win, lwin, term->windows)
    159 		win->handler(win, &ev, 0);
    160 	term->redrawing = 0;
    161 }
    162 
    163 static void
    164 redraw_terminal(struct terminal *term)
    165 {
    166 	redraw_terminal_ev(term, EV_REDRAW);
    167 }
    168 
    169 static void
    170 redraw_terminal_all(struct terminal *term)
    171 {
    172 	redraw_terminal_ev(term, EV_RESIZE);
    173 }
    174 
    175 static void
    176 erase_screen(struct terminal *term)
    177 {
    178 	if (!term->master || !is_blocked())
    179 		hard_write(term->fdout, cast_uchar "\033[2J\033[1;1H", 10);
    180 }
    181 
    182 static void
    183 redraw_terminal_cls(struct terminal *term)
    184 {
    185 	erase_screen(term);
    186 	set_margin(term);
    187 	alloc_term_screen(term);
    188 	redraw_terminal_all(term);
    189 }
    190 
    191 void
    192 cls_redraw_all_terminals(void)
    193 {
    194 	struct terminal *term = NULL;
    195 	struct list_head *lterm;
    196 	foreach (struct terminal, term, lterm, terminals) {
    197 		redraw_terminal_cls(term);
    198 	}
    199 }
    200 
    201 void
    202 draw_to_window(struct window *win, void (*fn)(struct terminal *term, void *),
    203                void *data)
    204 {
    205 	struct terminal *term = win->term;
    206 	struct window *w = NULL;
    207 	struct list_head *lw;
    208 	struct links_event ev = { EV_REDRAW, 0, 0, 0 };
    209 
    210 	pr(fn(term, data)){};
    211 	term = win->term;
    212 	if (win->list_entry.prev == &term->windows || term->redrawing)
    213 		return;
    214 	term->redrawing = 1;
    215 	ev.x = term->x;
    216 	ev.y = term->y;
    217 	foreachbackfrom (struct window, w, lw, term->windows,
    218 	                 win->list_entry.prev)
    219 		w->handler(w, &ev, 0);
    220 	term->redrawing = 0;
    221 }
    222 
    223 void
    224 add_window(struct terminal *term,
    225            void (*handler)(struct window *, struct links_event *, int),
    226            void *data)
    227 {
    228 	struct links_event ev = { EV_INIT, 0, 0, 0 };
    229 	struct window *win;
    230 	ev.x = term->x;
    231 	ev.y = term->y;
    232 	win = mem_calloc(sizeof(struct window));
    233 	win->handler = handler;
    234 	win->data = data;
    235 	win->term = term;
    236 	win->xp = win->yp = 0;
    237 	add_to_list(term->windows, win);
    238 	win->handler(win, &ev, 0);
    239 }
    240 
    241 void
    242 delete_window(struct window *win)
    243 {
    244 	struct terminal *term = win->term;
    245 	struct links_event ev = { EV_ABORT, 0, 0, 0 };
    246 	win->handler(win, &ev, 1);
    247 	del_from_list(win);
    248 	free(win->data);
    249 	redraw_terminal(term);
    250 	free(win);
    251 }
    252 
    253 void
    254 delete_window_ev(struct window *win, struct links_event *ev)
    255 {
    256 	struct terminal *term = win->term;
    257 	struct list_head *lw = win->list_entry.next;
    258 	delete_window(win);
    259 	if (ev && lw != &term->windows) {
    260 		struct window *w = list_struct(lw, struct window);
    261 		w->handler(w, ev, 1);
    262 	}
    263 }
    264 
    265 void
    266 set_window_ptr(struct window *win, int x, int y)
    267 {
    268 	if (win->xp == x && win->yp == y)
    269 		return;
    270 	win->xp = x;
    271 	win->yp = y;
    272 }
    273 
    274 void
    275 get_parent_ptr(struct window *win, int *x, int *y)
    276 {
    277 	if (win->list_entry.next != &win->term->windows) {
    278 		struct window *next =
    279 		    list_struct(win->list_entry.next, struct window);
    280 		*x = next->xp;
    281 		*y = next->yp;
    282 	} else {
    283 		*x = *y = 0;
    284 	}
    285 }
    286 
    287 struct ewd {
    288 	void (*fn)(void *);
    289 	void *data;
    290 	int b;
    291 };
    292 
    293 static void
    294 empty_window_handler(struct window *win, struct links_event *ev, int fwd)
    295 {
    296 	struct terminal *term = win->term;
    297 	struct list_head *ln;
    298 	struct ewd *ewd = win->data;
    299 	int x, y;
    300 	void (*fn)(void *) = ewd->fn;
    301 	void *data = ewd->data;
    302 	if (ewd->b)
    303 		return;
    304 	switch ((int)ev->ev) {
    305 	case EV_INIT:
    306 	case EV_RESIZE:
    307 	case EV_REDRAW:
    308 		get_parent_ptr(win, &x, &y);
    309 		set_window_ptr(win, x, y);
    310 		return;
    311 	case EV_ABORT:
    312 		fn(data);
    313 		return;
    314 	}
    315 	ewd->b = 1;
    316 	ln = win->list_entry.next;
    317 	delete_window(win);
    318 	fn(data);
    319 	if (ln != &term->windows) {
    320 		struct window *n = list_struct(ln, struct window);
    321 		n->handler(n, ev, fwd);
    322 	}
    323 }
    324 
    325 void
    326 add_empty_window(struct terminal *term, void (*fn)(void *), void *data)
    327 {
    328 	struct ewd *ewd;
    329 	ewd = xmalloc(sizeof(struct ewd));
    330 	ewd->fn = fn;
    331 	ewd->data = data;
    332 	ewd->b = 0;
    333 	add_window(term, empty_window_handler, ewd);
    334 }
    335 
    336 struct list_head term_specs = { &term_specs, &term_specs };
    337 
    338 void
    339 free_term_specs(void)
    340 {
    341 	free_list(struct term_spec, term_specs);
    342 }
    343 
    344 static struct term_spec dumb_term = {
    345 	init_list_1st(NULL) "", 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
    346 };
    347 
    348 static struct term_spec *
    349 default_term_spec(unsigned char *term)
    350 {
    351 	return &dumb_term;
    352 }
    353 
    354 static struct term_spec *
    355 get_term_spec(unsigned char *term)
    356 {
    357 	struct term_spec *t = NULL;
    358 	struct list_head *lt;
    359 	foreach (struct term_spec, t, lt, term_specs)
    360 		if (!casestrcmp(t->term, term))
    361 			return t;
    362 	return default_term_spec(term);
    363 }
    364 
    365 static void
    366 sync_term_specs(void)
    367 {
    368 	struct terminal *term = NULL;
    369 	struct list_head *lterm;
    370 	foreach (struct terminal, term, lterm, terminals)
    371 		term->spec = get_term_spec(term->term);
    372 }
    373 
    374 struct term_spec *
    375 new_term_spec(unsigned char *term)
    376 {
    377 	struct term_spec *t = NULL;
    378 	struct list_head *lt;
    379 	foreach (struct term_spec, t, lt, term_specs)
    380 		if (!casestrcmp(t->term, term))
    381 			return t;
    382 	t = xmalloc(sizeof(struct term_spec));
    383 	memcpy(t, default_term_spec(term), sizeof(struct term_spec));
    384 	if (strlen(cast_const_char term) < MAX_TERM_LEN)
    385 		strcpy(cast_char t->term, cast_const_char term);
    386 	else {
    387 		memcpy(t->term, term, MAX_TERM_LEN - 1);
    388 		t->term[MAX_TERM_LEN - 1] = 0;
    389 	}
    390 	add_to_list(term_specs, t);
    391 	sync_term_specs();
    392 	return t;
    393 }
    394 
    395 struct terminal *
    396 init_term(int fdin, int fdout,
    397           void (*root_window)(struct window *, struct links_event *, int))
    398 {
    399 	static tcount terminal_count = 0;
    400 	struct terminal *term;
    401 	struct window *win;
    402 	term = mem_calloc(sizeof(struct terminal));
    403 	term->count = terminal_count++;
    404 	term->fdin = fdin;
    405 	term->fdout = fdout;
    406 	term->master = term->fdout == 1;
    407 	term->lcx = -1;
    408 	term->lcy = -1;
    409 	term->dirty = 1;
    410 	term->blocked = -1;
    411 	term->screen = NULL;
    412 	term->last_screen = NULL;
    413 	term->spec = default_term_spec(cast_uchar "");
    414 	term->input_queue = NULL;
    415 	init_list(term->windows);
    416 	term->handle_to_close = -1;
    417 	win = mem_calloc(sizeof(struct window));
    418 	win->handler = root_window;
    419 	win->term = term;
    420 	add_to_list(term->windows, win);
    421 	add_to_list(terminals, term);
    422 	set_handlers(fdin, in_term, NULL, term);
    423 	return term;
    424 }
    425 
    426 static int
    427 process_utf_8(struct terminal *term, struct links_event *ev)
    428 {
    429 	size_t l;
    430 	unsigned char *p;
    431 	unsigned c;
    432 	if (ev->ev == EV_KBD) {
    433 		if (ev->x <= 0 || ev->x >= 0x100)
    434 			goto direct;
    435 		if ((term->utf8_paste_mode ^ ev->y) & KBD_PASTING) {
    436 			term->utf8_paste_mode = ev->y & KBD_PASTING;
    437 			term->utf8_buffer[0] = 0;
    438 		}
    439 		if ((l = strlen(cast_const_char term->utf8_buffer))
    440 		        >= sizeof(term->utf8_buffer) - 1
    441 		    || ev->x < 0x80 || ev->x >= 0xc0) {
    442 			term->utf8_buffer[0] = 0;
    443 			l = 0;
    444 		}
    445 		term->utf8_buffer[l] = (unsigned char)ev->x;
    446 		term->utf8_buffer[l + 1] = 0;
    447 		p = term->utf8_buffer;
    448 		GET_UTF_8(p, c);
    449 		if (!c)
    450 			return 0;
    451 		ev->x = c;
    452 direct:
    453 		term->utf8_buffer[0] = 0;
    454 	}
    455 	return 1;
    456 }
    457 
    458 static void
    459 in_term(void *term_)
    460 {
    461 	struct terminal *term = (struct terminal *)term_;
    462 	struct links_event *ev;
    463 	int r;
    464 	unsigned char *iq;
    465 	if ((unsigned)term->qlen + ALLOC_GR > INT_MAX)
    466 		overalloc();
    467 	iq = xrealloc(term->input_queue, term->qlen + ALLOC_GR);
    468 	term->input_queue = iq;
    469 	EINTRLOOP(r, (int)read(term->fdin, iq + term->qlen, ALLOC_GR));
    470 	if (r <= 0) {
    471 		if (r == -1 && errno != ECONNRESET)
    472 			error(
    473 			    "ERROR: error %d on terminal: could not read event",
    474 			    errno);
    475 		destroy_terminal(term);
    476 		return;
    477 	}
    478 	term->qlen += r;
    479 test_queue:
    480 	if ((size_t)term->qlen < sizeof(struct links_event))
    481 		return;
    482 	ev = (struct links_event *)iq;
    483 	r = sizeof(struct links_event);
    484 	if (ev->ev != EV_INIT && ev->ev != EV_RESIZE && ev->ev != EV_REDRAW
    485 	    && ev->ev != EV_KBD && ev->ev != EV_MOUSE && ev->ev != EV_ABORT) {
    486 		error("ERROR: error on terminal: bad event %d", ev->ev);
    487 		goto mm;
    488 	}
    489 	if (ev->ev == EV_INIT) {
    490 		int init_len;
    491 		if ((size_t)term->qlen < sizeof(struct links_event)
    492 		                             + MAX_TERM_LEN + MAX_CWD_LEN
    493 		                             + 3 * sizeof(int))
    494 			return;
    495 		init_len =
    496 		    *(int *)(iq + sizeof(struct links_event) + MAX_TERM_LEN
    497 		             + MAX_CWD_LEN + 2 * sizeof(int));
    498 		if ((size_t)term->qlen < sizeof(struct links_event)
    499 		                             + MAX_TERM_LEN + MAX_CWD_LEN
    500 		                             + 3 * sizeof(int) + init_len)
    501 			return;
    502 		memcpy(term->term, iq + sizeof(struct links_event),
    503 		       MAX_TERM_LEN);
    504 		term->term[MAX_TERM_LEN - 1] = 0;
    505 		memcpy(term->cwd,
    506 		       iq + sizeof(struct links_event) + MAX_TERM_LEN,
    507 		       MAX_CWD_LEN);
    508 		term->cwd[MAX_CWD_LEN - 1] = 0;
    509 		term->environment = *(int *)(iq + sizeof(struct links_event)
    510 		                             + MAX_TERM_LEN + MAX_CWD_LEN);
    511 		ev->b = (long)(iq + sizeof(struct links_event) + MAX_TERM_LEN
    512 		               + MAX_CWD_LEN + 2 * sizeof(int));
    513 		r = (int)sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN
    514 		    + 3 * (int)sizeof(int) + init_len;
    515 		sync_term_specs();
    516 	}
    517 	if (ev->ev == EV_REDRAW || ev->ev == EV_RESIZE || ev->ev == EV_INIT) {
    518 		struct window *win = NULL;
    519 		struct list_head *lwin;
    520 
    521 		term->real_x = ev->x;
    522 		term->real_y = ev->y;
    523 
    524 		set_margin(term);
    525 
    526 send_redraw:
    527 		if (ev->x < 0 || ev->y < 0) {
    528 			error("ERROR: bad terminal size: %d, %d", (int)ev->x,
    529 			      (int)ev->y);
    530 			goto mm;
    531 		}
    532 		alloc_term_screen(term);
    533 		clear_terminal(term);
    534 		erase_screen(term);
    535 		term->redrawing = 1;
    536 		foreachback (struct window, win, lwin, term->windows)
    537 			win->handler(win, ev, 0);
    538 		term->redrawing = 0;
    539 	}
    540 	if (ev->ev == EV_MOUSE) {
    541 		ev->x -= term->left_margin;
    542 		ev->y -= term->top_margin;
    543 	}
    544 	if (ev->ev == EV_KBD || ev->ev == EV_MOUSE) {
    545 		if (ev->ev == EV_KBD && upcase(ev->x) == 'L'
    546 		    && !(ev->y & KBD_PASTING) && ev->y & KBD_CTRL) {
    547 			ev->ev = EV_REDRAW;
    548 			ev->x = term->x;
    549 			ev->y = term->y;
    550 			goto send_redraw;
    551 		}
    552 		if (ev->ev == EV_KBD && ev->x == KBD_STOP) {
    553 			abort_background_connections();
    554 		}
    555 		if (!list_empty(term->windows)) {
    556 			if (ev->ev == EV_KBD && ev->x == KBD_CTRL_C
    557 			    && !(ev->y & KBD_PASTING)) {
    558 				struct window *prev = list_struct(
    559 				    term->windows.prev, struct window);
    560 				prev->handler(prev, ev, 0);
    561 			} else {
    562 				if (process_utf_8(term, ev)) {
    563 					struct window *next = list_struct(
    564 					    term->windows.next, struct window);
    565 					next->handler(next, ev, 0);
    566 				}
    567 			}
    568 		}
    569 	}
    570 	if (ev->ev == EV_ABORT) {
    571 		destroy_terminal(term);
    572 		return;
    573 	}
    574 mm:
    575 	if (term->qlen == r)
    576 		term->qlen = 0;
    577 	else
    578 		memmove(iq, iq + r, term->qlen -= r);
    579 	goto test_queue;
    580 }
    581 
    582 static inline int
    583 getcompcode(int c)
    584 {
    585 	return (c << 1 | (c & 4) >> 2) & 7;
    586 }
    587 
    588 unsigned char frame_dumb[49] =
    589     "   ||||++||++++++--|-+||++--|-+----++++++++     ";
    590 static unsigned char frame_vt100[49] =
    591     "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla    ";
    592 
    593 #define SETPOS(x, y)                                                           \
    594 	{                                                                      \
    595 		l = add_to_str(&a, l, cast_uchar "\033[");                     \
    596 		l = add_num_to_str(&a, l, (y) + 1 + term->top_margin);         \
    597 		l = add_chr_to_str(&a, l, ';');                                \
    598 		l = add_num_to_str(&a, l, (x) + 1 + term->left_margin);        \
    599 		l = add_chr_to_str(&a, l, 'H');                                \
    600 	}
    601 
    602 #define PRINT_CHAR(p)                                                          \
    603 	{                                                                      \
    604 		char_t c = term->screen[p].ch;                                 \
    605 		unsigned char A = term->screen[p].at & 0x7f;                   \
    606 		unsigned char frm = !!(term->screen[p].at & ATTR_FRAME);       \
    607 		if (s->mode == TERM_VT100) {                                   \
    608 			if (frm != mode) {                                     \
    609 				if (!(mode = frm))                             \
    610 					l = add_to_str(&a, l,                  \
    611 					               cast_uchar "\017");     \
    612 				else                                           \
    613 					l = add_to_str(&a, l,                  \
    614 					               cast_uchar "\016");     \
    615 			}                                                      \
    616 			if (frm && c >= 176 && c < 224)                        \
    617 				c = frame_vt100[c - 176];                      \
    618 		} else if (s->mode == TERM_DUMB && frm && c >= 176 && c < 224) \
    619 			c = frame_dumb[c - 176];                               \
    620 		if (!(A & 0100) && (A >> 3) == (A & 7))                        \
    621 			A = (A & 070) | 7 * !(A & 020);                        \
    622 		if (A != attrib) {                                             \
    623 			attrib = A;                                            \
    624 			l = add_to_str(&a, l, cast_uchar "\033[0");            \
    625 			if (s->col) {                                          \
    626 				unsigned char m[4];                            \
    627 				m[0] = ';';                                    \
    628 				m[1] = '3';                                    \
    629 				m[3] = 0;                                      \
    630 				m[2] = (attrib & 7) + '0';                     \
    631 				l = add_to_str(&a, l, m);                      \
    632 				m[1] = '4';                                    \
    633 				m[2] = ((attrib >> 3) & 7) + '0';              \
    634 				l = add_to_str(&a, l, m);                      \
    635 			} else if (getcompcode(attrib & 7)                     \
    636 			           < getcompcode(attrib >> 3 & 7))             \
    637 				l = add_to_str(&a, l, cast_uchar ";7");        \
    638 			if (attrib & 0100)                                     \
    639 				l = add_to_str(&a, l, cast_uchar ";1");        \
    640 			l = add_chr_to_str(&a, l, 'm');                        \
    641 		}                                                              \
    642 		if (c >= ' ' && c != 127 && c != 155) {                        \
    643 			if (c < 128 || frm) {                                  \
    644 				l = add_chr_to_str(&a, l, (unsigned char)c);   \
    645 			} else {                                               \
    646 				/*                                             \
    647 				 * Linux UTF-8 console is broken and doesn't   \
    648 				 * advance cursor on some characters. So we    \
    649 				 * first print an one-byte replacement, then   \
    650 				 * set the cursor back, then print the UTF-8   \
    651 				 * character and finally set the cursor again. \
    652 				 */                                            \
    653 				unsigned char *r;                              \
    654 				r = u2cp(c);                                   \
    655 				if (!(r && r[0] >= 32 && r[0] < 127 && !r[1])) \
    656 					r = cast_uchar "*";                    \
    657 				l = add_chr_to_str(&a, l, r[0]);               \
    658 				if (cx + 1 < term->x)                          \
    659 					l = add_chr_to_str(&a, l, 8);          \
    660 				else                                           \
    661 					SETPOS(cx, y);                         \
    662 				l = add_to_str(&a, l, encode_utf_8(c));        \
    663 				SETPOS(cx + 1, y);                             \
    664 				print_next = 1;                                \
    665 			}                                                      \
    666 		} else if (!c || c == 1)                                       \
    667 			l = add_chr_to_str(&a, l, ' ');                        \
    668 		else                                                           \
    669 			l = add_chr_to_str(&a, l, '.');                        \
    670 		cx++;                                                          \
    671 	}
    672 
    673 static void
    674 redraw_screen(struct terminal *term)
    675 {
    676 	int x, y, p = 0;
    677 	int cx = term->lcx, cy = term->lcy;
    678 	unsigned char *a;
    679 	int attrib = -1;
    680 	int mode = -1;
    681 	int l = 0;
    682 	int print_next = 0;
    683 	struct term_spec *s;
    684 	if (!term->dirty || (term->master && is_blocked()))
    685 		return;
    686 	a = NULL;
    687 	s = term->spec;
    688 	for (y = 0; y < term->y; y++) {
    689 		if (!memcmp(&term->screen[p], &term->last_screen[p],
    690 		            sizeof(chr) * term->x)) {
    691 			p += term->x;
    692 			continue;
    693 		}
    694 		for (x = 0; x < term->x; x++, p++) {
    695 			int i;
    696 			if (y == term->y - 1 && x == term->x - 1
    697 			    && term->left_margin + term->x == term->real_x
    698 			    && term->top_margin + term->y == term->real_y)
    699 				break;
    700 			if (term->screen[p].ch == term->last_screen[p].ch
    701 			    && term->screen[p].at == term->last_screen[p].at) {
    702 				/* make sure that padding is identical */
    703 				if (chr_has_padding)
    704 					memcpy(&term->last_screen[p],
    705 					       &term->screen[p], sizeof(chr));
    706 				if (print_next) {
    707 					print_next = 0;
    708 					goto must_print_next;
    709 				}
    710 				continue;
    711 			}
    712 			memcpy(&term->last_screen[p], &term->screen[p],
    713 			       sizeof(chr));
    714 must_print_next:
    715 			if (cx == x && cy == y)
    716 				goto pc;
    717 			else if (cy == y && x - cx < 10 && x - cx > 0) {
    718 				for (i = x - cx; i >= 0; i--) {
    719 ppc:
    720 					PRINT_CHAR(p - i);
    721 				}
    722 			} else {
    723 				SETPOS(x, y);
    724 				cx = x;
    725 				cy = y;
    726 pc:
    727 				i = 0;
    728 				goto ppc;
    729 			}
    730 		}
    731 		if (print_next && term->left_margin + term->x < term->real_x) {
    732 			l = add_to_str(&a, l, cast_uchar "\033[0m ");
    733 			attrib = -1;
    734 			print_next = 0;
    735 		}
    736 	}
    737 	if (l) {
    738 		if (s->col)
    739 			l = add_to_str(&a, l, cast_uchar "\033[37;40m");
    740 		l = add_to_str(&a, l, cast_uchar "\033[0m");
    741 		if (s->mode == TERM_VT100)
    742 			l = add_to_str(&a, l, cast_uchar "\017");
    743 	}
    744 	term->lcx = cx;
    745 	term->lcy = cy;
    746 	if (term->cx != term->lcx || term->cy != term->lcy) {
    747 		term->lcx = term->cx;
    748 		term->lcy = term->cy;
    749 		l = add_to_str(&a, l, cast_uchar "\033[");
    750 		l = add_num_to_str(&a, l, term->cy + 1 + term->top_margin);
    751 		l = add_chr_to_str(&a, l, ';');
    752 		l = add_num_to_str(&a, l, term->cx + 1 + term->left_margin);
    753 		l = add_chr_to_str(&a, l, 'H');
    754 	}
    755 	hard_write(term->fdout, a, l);
    756 	free(a);
    757 	term->dirty = 0;
    758 }
    759 
    760 void
    761 redraw_all_terminals(void)
    762 {
    763 	struct terminal *term = NULL;
    764 	struct list_head *lterm;
    765 	foreach (struct terminal, term, lterm, terminals)
    766 		redraw_screen(term);
    767 }
    768 
    769 void
    770 flush_terminal(struct terminal *term)
    771 {
    772 	redraw_screen(term);
    773 }
    774 
    775 void
    776 destroy_terminal(void *term_)
    777 {
    778 	struct terminal *term = (struct terminal *)term_;
    779 	int rs;
    780 	unregister_bottom_half(destroy_terminal, term);
    781 	while (!list_empty(term->windows)) {
    782 		delete_window(list_struct(term->windows.next, struct window));
    783 	}
    784 	del_from_list(term);
    785 	close_socket(&term->blocked);
    786 	free(term->title);
    787 	free(term->screen);
    788 	free(term->last_screen);
    789 	free(term->input_queue);
    790 	set_handlers(term->fdin, NULL, NULL, NULL);
    791 	EINTRLOOP(rs, close(term->fdin));
    792 	if (!term->master) {
    793 		if (term->fdout != term->fdin)
    794 			EINTRLOOP(rs, close(term->fdout));
    795 	} else {
    796 		unhandle_terminal_signals(term);
    797 		free_all_itrms();
    798 		if (!list_empty(terminals)) {
    799 			os_detach_console();
    800 		}
    801 	}
    802 	if (term->handle_to_close != -1) {
    803 		hard_write(term->handle_to_close, cast_uchar "x", 1);
    804 		close_socket(&term->handle_to_close);
    805 	}
    806 	free(term);
    807 	check_if_no_terminal();
    808 }
    809 
    810 void
    811 destroy_all_terminals(void)
    812 {
    813 	while (!list_empty(terminals)) {
    814 		destroy_terminal(list_struct(terminals.next, struct terminal));
    815 	}
    816 }
    817 
    818 static void
    819 check_if_no_terminal(void)
    820 {
    821 	if (!list_empty(terminals))
    822 		return;
    823 	terminate_loop = 1;
    824 }
    825 
    826 void
    827 set_char(struct terminal *t, int x, int y, unsigned ch, unsigned char at)
    828 {
    829 	t->dirty = 1;
    830 	if (x >= 0 && x < t->x && y >= 0 && y < t->y) {
    831 		chr *cc = &t->screen[x + t->x * y];
    832 		cc->ch = ch;
    833 		cc->at = at;
    834 	}
    835 }
    836 
    837 const chr *
    838 get_char(struct terminal *t, int x, int y)
    839 {
    840 	int lx, ly;
    841 	lx = t->x - 1;
    842 	ly = t->y - 1;
    843 	if ((lx | ly) < 0) {
    844 		static const chr empty = { ' ', 070 };
    845 		return &empty;
    846 	}
    847 	if (x > lx)
    848 		x = lx;
    849 	else if (x < 0)
    850 		x = 0;
    851 	if (y > ly)
    852 		y = ly;
    853 	else if (y < 0)
    854 		y = 0;
    855 	return &t->screen[x + t->x * y];
    856 }
    857 
    858 void
    859 set_color(struct terminal *t, int x, int y, unsigned char c)
    860 {
    861 	t->dirty = 1;
    862 	if (x >= 0 && x < t->x && y >= 0 && y < t->y)
    863 		t->screen[x + t->x * y].at =
    864 		    (t->screen[x + t->x * y].at & ATTR_FRAME)
    865 		    | (c & ~ATTR_FRAME);
    866 }
    867 
    868 void
    869 set_only_char(struct terminal *t, int x, int y, unsigned ch, unsigned char at)
    870 {
    871 	const chr *cc;
    872 	t->dirty = 1;
    873 	cc = get_char(t, x, y);
    874 	at = (at & ATTR_FRAME) | (cc->at & ~ATTR_FRAME);
    875 	set_char(t, x, y, ch, at);
    876 }
    877 
    878 void
    879 set_line(struct terminal *t, int x, int y, int l, chr *line)
    880 {
    881 	int i;
    882 	chr *cc;
    883 	t->dirty = 1;
    884 	if (y < 0 || y >= t->y)
    885 		return;
    886 	i = x >= 0 ? 0 : -x;
    887 	cc = &t->screen[x + i + t->x * y];
    888 	line = &line[i];
    889 	i = (x + l <= t->x ? l : t->x - x) - i;
    890 	if (i <= 0)
    891 		return;
    892 	memcpy(cc, line, i * sizeof(chr));
    893 }
    894 
    895 void
    896 set_line_color(struct terminal *t, int x, int y, int l, unsigned char c)
    897 {
    898 	int i;
    899 	t->dirty = 1;
    900 	if (y < 0 || y >= t->y)
    901 		return;
    902 	for (i = x >= 0 ? 0 : -x; i < (x + l <= t->x ? l : t->x - x); i++)
    903 		t->screen[x + i + t->x * y].at =
    904 		    (t->screen[x + i + t->x * y].at & ATTR_FRAME)
    905 		    | (c & ~ATTR_FRAME);
    906 }
    907 
    908 void
    909 fill_area(struct terminal *t, int x, int y, int xw, int yw, unsigned ch,
    910           unsigned char at)
    911 {
    912 	int i;
    913 	chr *p, *ps;
    914 	if (x < 0) {
    915 		xw += x;
    916 		x = 0;
    917 	}
    918 	if (x + xw > t->x)
    919 		xw = t->x - x;
    920 	if (xw <= 0)
    921 		return;
    922 	if (y < 0) {
    923 		yw += y;
    924 		y = 0;
    925 	}
    926 	if (y + yw > t->y)
    927 		yw = t->y - y;
    928 	if (yw <= 0)
    929 		return;
    930 	t->dirty = 1;
    931 	p = ps = &t->screen[x + t->x * y];
    932 	for (i = 0; i < xw; i++) {
    933 		p->ch = ch;
    934 		p->at = at;
    935 		p++;
    936 	}
    937 	p = ps;
    938 	for (i = 1; i < yw; i++) {
    939 		p += t->x;
    940 		memcpy(p, ps, xw * sizeof(chr));
    941 	}
    942 }
    943 
    944 static int p1[] = { 218, 191, 192, 217, 179, 196 };
    945 static int p2[] = { 201, 187, 200, 188, 186, 205 };
    946 
    947 void
    948 draw_frame(struct terminal *t, int x, int y, int xw, int yw, unsigned char c,
    949            int w)
    950 {
    951 	int *p = w > 1 ? p2 : p1;
    952 	c |= ATTR_FRAME;
    953 	set_char(t, x, y, p[0], c);
    954 	set_char(t, x + xw - 1, y, p[1], c);
    955 	set_char(t, x, y + yw - 1, p[2], c);
    956 	set_char(t, x + xw - 1, y + yw - 1, p[3], c);
    957 	fill_area(t, x, y + 1, 1, yw - 2, p[4], c);
    958 	fill_area(t, x + xw - 1, y + 1, 1, yw - 2, p[4], c);
    959 	fill_area(t, x + 1, y, xw - 2, 1, p[5], c);
    960 	fill_area(t, x + 1, y + yw - 1, xw - 2, 1, p[5], c);
    961 }
    962 
    963 void
    964 print_text(struct terminal *t, int x, int y, int l, unsigned char *text,
    965            unsigned char c)
    966 {
    967 	for (; l--; x++) {
    968 		unsigned u = GET_TERM_CHAR(t, &text);
    969 		if (!u)
    970 			break;
    971 		set_char(t, x, y, u, c);
    972 	}
    973 }
    974 
    975 void
    976 set_cursor(struct terminal *term, int x, int y, int altx, int alty)
    977 {
    978 	term->dirty = 1;
    979 	if (term->spec->block_cursor) {
    980 		x = altx;
    981 		y = alty;
    982 	}
    983 	if (x >= term->x)
    984 		x = term->x - 1;
    985 	if (y >= term->y)
    986 		y = term->y - 1;
    987 	if (x < 0)
    988 		x = 0;
    989 	if (y < 0)
    990 		y = 0;
    991 	term->cx = x;
    992 	term->cy = y;
    993 }
    994 
    995 static void
    996 exec_thread(void *path_, int p)
    997 {
    998 	char *path = path_;
    999 	int rs;
   1000 	if (path[0] == 2)
   1001 		EINTRLOOP(rs, setpgid(0, 0));
   1002 	exe(path + 1, path[0]);
   1003 	if (path[1 + strlen(path + 1) + 1])
   1004 		EINTRLOOP(rs, unlink(path + 1 + strlen(path + 1) + 1));
   1005 }
   1006 
   1007 static void
   1008 close_handle(void *p)
   1009 {
   1010 	int *h = p;
   1011 	close_socket(h);
   1012 }
   1013 
   1014 static void
   1015 unblock_terminal(void *term_)
   1016 {
   1017 	struct terminal *term = (struct terminal *)term_;
   1018 	close_handle(&term->blocked);
   1019 	term->blocked = -1;
   1020 	set_handlers(term->fdin, in_term, NULL, term);
   1021 	unblock_itrm(term->fdin);
   1022 	/* clear the dirty flag because unblock_itrm queued a resize
   1023 	   event - so avoid double redraw */
   1024 	term->dirty = 0;
   1025 }
   1026 
   1027 void
   1028 exec_on_terminal(struct terminal *term, unsigned char *path,
   1029                  unsigned char *delet, unsigned char fg)
   1030 {
   1031 	int rs;
   1032 	if (path && !*path)
   1033 		return;
   1034 	if (!path)
   1035 		path = cast_uchar "";
   1036 	if (term->master) {
   1037 		if (!*path) {
   1038 			dispatch_special(delet);
   1039 		} else {
   1040 			int blockh;
   1041 			unsigned char *param;
   1042 			size_t paraml;
   1043 			if (is_blocked() && fg) {
   1044 				if (*delet)
   1045 					EINTRLOOP(
   1046 					    rs, unlink(cast_const_char delet));
   1047 				return;
   1048 			}
   1049 			param = NULL;
   1050 			paraml = add_chr_to_str(&param, 0, fg);
   1051 			paraml = add_to_str(&param, paraml, path);
   1052 			paraml = add_chr_to_str(&param, paraml, 0);
   1053 			paraml = add_to_str(&param, paraml, delet);
   1054 			if (fg == 1)
   1055 				block_itrm(term->fdin);
   1056 			if ((blockh = start_thread(exec_thread, param,
   1057 			                           paraml + 1, *delet != 0))
   1058 			    == -1) {
   1059 				if (fg == 1)
   1060 					unblock_itrm(term->fdin);
   1061 				free(param);
   1062 				return;
   1063 			}
   1064 			free(param);
   1065 			if (fg == 1) {
   1066 				term->blocked = blockh;
   1067 				set_handlers(blockh, unblock_terminal, NULL,
   1068 				             term);
   1069 				set_handlers(term->fdin, NULL, NULL, term);
   1070 			} else {
   1071 				set_handlers(blockh, close_handle, NULL,
   1072 				             &blockh);
   1073 			}
   1074 		}
   1075 	} else {
   1076 		unsigned char *data;
   1077 		size_t datal;
   1078 		data = NULL;
   1079 		datal = add_chr_to_str(&data, 0, 0);
   1080 		datal = add_chr_to_str(&data, datal, fg);
   1081 		datal = add_to_str(&data, datal, path);
   1082 		datal = add_chr_to_str(&data, datal, 0);
   1083 		datal = add_to_str(&data, datal, delet);
   1084 		hard_write(term->fdout, data, datal + 1);
   1085 		free(data);
   1086 	}
   1087 }
   1088 
   1089 void
   1090 do_terminal_function(struct terminal *term, unsigned char code,
   1091                      unsigned char *data)
   1092 {
   1093 	unsigned char *x_data;
   1094 	size_t x_datal;
   1095 	x_data = NULL;
   1096 	x_datal = add_chr_to_str(&x_data, 0, code);
   1097 	x_datal = add_to_str(&x_data, x_datal, data);
   1098 	exec_on_terminal(term, NULL, x_data, 0);
   1099 	free(x_data);
   1100 }
   1101 
   1102 void
   1103 set_terminal_title(struct terminal *term, unsigned char *title)
   1104 {
   1105 	if (strlen(cast_const_char title) > 10000)
   1106 		title[10000] = 0;
   1107 	if (strchr(cast_const_char title, 1)) {
   1108 		unsigned char *a, *b;
   1109 		for (a = title, b = title; *a; a++)
   1110 			if (*a != 1)
   1111 				*b++ = *a;
   1112 		*b = 0;
   1113 	}
   1114 	if (term->title
   1115 	    && !strcmp(cast_const_char title, cast_const_char term->title))
   1116 		goto ret;
   1117 	free(term->title);
   1118 	term->title = stracpy(title);
   1119 	do_terminal_function(term, TERM_FN_TITLE, title);
   1120 ret:
   1121 	free(title);
   1122 }
   1123 
   1124 struct terminal *
   1125 find_terminal(tcount count)
   1126 {
   1127 	struct terminal *term = NULL;
   1128 	struct list_head *lterm;
   1129 	foreach (struct terminal, term, lterm, terminals)
   1130 		if (term->count == count)
   1131 			return term;
   1132 	return NULL;
   1133 }