1 /*
2  term-terminfo.c : irssi
3 
4     Copyright (C) 2001 Timo Sirainen
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "module.h"
22 #include "signals.h"
23 #include "term.h"
24 #include "terminfo-core.h"
25 #include "fe-windows.h"
26 #include "gui-printtext.h"
27 #include "utf8.h"
28 
29 #include <signal.h>
30 #include <termios.h>
31 #include <stdio.h>
32 
33 /* returns number of characters in the beginning of the buffer being a
34    a single character, or -1 if more input is needed. The character will be
35    saved in result */
36 typedef int (*TERM_INPUT_FUNC)(const unsigned char *buffer, int size,
37 			       unichar *result);
38 
39 struct _TERM_WINDOW {
40         /* Terminal to use for window */
41 	TERM_REC *term;
42 
43         /* Area for window in terminal */
44 	int x, y;
45 	int width, height;
46 };
47 
48 TERM_WINDOW *root_window;
49 
50 static char *term_lines_empty; /* 1 if line is entirely empty */
51 static int vcmove, vcx, vcy, curs_visible;
52 static int crealx, crealy, cforcemove;
53 static int curs_x, curs_y;
54 
55 static unsigned int last_fg, last_bg;
56 static int last_attrs;
57 
58 static GSource *sigcont_source;
59 static volatile sig_atomic_t got_sigcont;
60 static int freeze_counter;
61 
62 static TERM_INPUT_FUNC input_func;
63 static unsigned char term_inbuf[256];
64 static int term_inbuf_pos;
65 
66 /* SIGCONT handler */
sig_cont(int p)67 static void sig_cont(int p)
68 {
69         got_sigcont = TRUE;
70 }
71 
72 /* SIGCONT GSource */
sigcont_prepare(GSource * source,gint * timeout)73 static gboolean sigcont_prepare(GSource *source, gint *timeout)
74 {
75 	*timeout = -1;
76 	return got_sigcont;
77 }
78 
sigcont_check(GSource * source)79 static gboolean sigcont_check(GSource *source)
80 {
81 	return got_sigcont;
82 }
83 
sigcont_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)84 static gboolean sigcont_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
85 {
86 	got_sigcont = FALSE;
87 	if (callback == NULL)
88 		return TRUE;
89 	return callback(user_data);
90 }
91 
do_redraw(gpointer unused)92 static gboolean do_redraw(gpointer unused)
93 {
94 	terminfo_cont(current_term);
95 	irssi_redraw();
96 
97         return 1;
98 }
99 
100 static GSourceFuncs sigcont_funcs = {
101 	.prepare = sigcont_prepare,
102 	.check = sigcont_check,
103 	.dispatch = sigcont_dispatch
104 };
105 
term_atexit(void)106 static void term_atexit(void)
107 {
108 	if (!quitting && current_term && current_term->TI_rmcup) {
109 		/* Unexpected exit, avoid switching out of alternate screen
110 		   to keep any on-screen errors (like noperl_die()'s) */
111 		current_term->TI_rmcup = NULL;
112 	}
113 
114 	term_deinit();
115 }
116 
term_init(void)117 int term_init(void)
118 {
119 	struct sigaction act;
120         int width, height;
121 
122 	last_fg = last_bg = -1;
123 	last_attrs = 0;
124 	vcx = vcy = 0; crealx = crealy = -1;
125 	vcmove = FALSE; cforcemove = TRUE;
126         curs_visible = TRUE;
127 
128 	current_term = terminfo_core_init(stdin, stdout);
129 	if (current_term == NULL)
130 		return FALSE;
131 
132 	if (term_get_size(&width, &height)) {
133                 current_term->width = width;
134                 current_term->height = height;
135 	}
136 
137         /* grab CONT signal */
138 	sigemptyset(&act.sa_mask);
139 	act.sa_flags = 0;
140 	act.sa_handler = sig_cont;
141 	sigaction(SIGCONT, &act, NULL);
142 	sigcont_source = g_source_new(&sigcont_funcs, sizeof(GSource));
143 	g_source_set_callback(sigcont_source, do_redraw, NULL, NULL);
144 	g_source_attach(sigcont_source, NULL);
145 
146 	curs_x = curs_y = 0;
147 	term_width = current_term->width;
148 	term_height = current_term->height;
149 	root_window = term_window_create(0, 0, term_width, term_height);
150 
151         term_lines_empty = g_new0(char, term_height);
152 
153         term_set_input_type(TERM_TYPE_8BIT);
154 	term_common_init();
155 	atexit(term_atexit);
156         return TRUE;
157 }
158 
term_deinit(void)159 void term_deinit(void)
160 {
161 	if (current_term != NULL) {
162 		signal(SIGCONT, SIG_DFL);
163 		g_source_destroy(sigcont_source);
164 		g_source_unref(sigcont_source);
165 
166 		term_common_deinit();
167 		terminfo_core_deinit(current_term);
168 		current_term = NULL;
169 	}
170 }
171 
term_move_real(void)172 static void term_move_real(void)
173 {
174 	if (vcx != crealx || vcy != crealy || cforcemove) {
175 		if (curs_visible) {
176 			terminfo_set_cursor_visible(FALSE);
177 			curs_visible = FALSE;
178 		}
179 
180 		if (cforcemove) {
181 			crealx = crealy = -1;
182 			cforcemove = FALSE;
183 		}
184 		terminfo_move_relative(crealx, crealy, vcx, vcy);
185                 crealx = vcx; crealy = vcy;
186 	}
187 
188         vcmove = FALSE;
189 }
190 
191 /* Cursor position is unknown - move it immediately to known position */
term_move_reset(int x,int y)192 static void term_move_reset(int x, int y)
193 {
194 	if (x >= term_width) x = term_width-1;
195 	if (y >= term_height) y = term_height-1;
196 
197 	vcx = x; vcy = y;
198         cforcemove = TRUE;
199         term_move_real();
200 }
201 
202 /* Resize terminal - if width or height is negative,
203    the new size is unknown and should be figured out somehow */
term_resize(int width,int height)204 void term_resize(int width, int height)
205 {
206 	if (width < 0 || height < 0) {
207 		width = current_term->width;
208                 height = current_term->height;
209 	}
210 
211 	if (term_width != width || term_height != height) {
212 		term_width = current_term->width = width;
213 		term_height = current_term->height = height;
214 		term_window_move(root_window, 0, 0, term_width, term_height);
215 
216                 g_free(term_lines_empty);
217 		term_lines_empty = g_new0(char, term_height);
218 	}
219 
220         term_move_reset(0, 0);
221 }
222 
term_resize_final(int width,int height)223 void term_resize_final(int width, int height)
224 {
225 }
226 
227 /* Returns TRUE if terminal has colors */
term_has_colors(void)228 int term_has_colors(void)
229 {
230         return current_term->TI_colors > 0;
231 }
232 
233 /* Force the colors on any way you can */
term_force_colors(int set)234 void term_force_colors(int set)
235 {
236 	terminfo_setup_colors(current_term, set);
237 }
238 
239 /* Clear screen */
term_clear(void)240 void term_clear(void)
241 {
242         term_set_color(root_window, ATTR_RESET);
243 	terminfo_clear();
244         term_move_reset(0, 0);
245 
246 	memset(term_lines_empty, 1, term_height);
247 }
248 
249 /* Beep */
term_beep(void)250 void term_beep(void)
251 {
252         terminfo_beep(current_term);
253 }
254 
255 /* Create a new window in terminal */
term_window_create(int x,int y,int width,int height)256 TERM_WINDOW *term_window_create(int x, int y, int width, int height)
257 {
258 	TERM_WINDOW *window;
259 
260 	window = g_new0(TERM_WINDOW, 1);
261         window->term = current_term;
262 	window->x = x; window->y = y;
263 	window->width = width; window->height = height;
264         return window;
265 }
266 
267 /* Destroy a terminal window */
term_window_destroy(TERM_WINDOW * window)268 void term_window_destroy(TERM_WINDOW *window)
269 {
270         g_free(window);
271 }
272 
273 /* Move/resize a window */
term_window_move(TERM_WINDOW * window,int x,int y,int width,int height)274 void term_window_move(TERM_WINDOW *window, int x, int y,
275 		      int width, int height)
276 {
277 	window->x = x;
278 	window->y = y;
279 	window->width = width;
280         window->height = height;
281 }
282 
283 /* Clear window */
term_window_clear(TERM_WINDOW * window)284 void term_window_clear(TERM_WINDOW *window)
285 {
286 	int y;
287 
288 	terminfo_set_normal();
289 	if (window->y == 0 && window->height == term_height && window->width == term_width) {
290 		term_clear();
291 	} else {
292 		for (y = 0; y < window->height; y++) {
293 			term_move(window, 0, y);
294 			term_clrtoeol(window);
295 		}
296 	}
297 }
298 
299 /* Scroll window up/down */
term_window_scroll(TERM_WINDOW * window,int count)300 void term_window_scroll(TERM_WINDOW *window, int count)
301 {
302 	int y;
303 
304 	terminfo_scroll(window->y, window->y+window->height-1, count);
305         term_move_reset(vcx, vcy);
306 
307         /* set the newly scrolled area dirty */
308 	for (y = 0; (window->y+y) < term_height && y < window->height; y++)
309 		term_lines_empty[window->y+y] = FALSE;
310 }
311 
term_putchar(int c)312 inline static int term_putchar(int c)
313 {
314         return fputc(c, current_term->out);
315 }
316 
317 /* copied from terminfo-core.c */
318 int tputs();
319 
termctl_set_color_24bit(int bg,unsigned int lc)320 static int termctl_set_color_24bit(int bg, unsigned int lc)
321 {
322 	static char buf[20];
323 	const unsigned char color[] = { lc >> 16, lc >> 8, lc };
324 
325 	if (!term_use_colors24) {
326 		if (bg)
327 			terminfo_set_bg(color_24bit_256(color));
328 		else
329 			terminfo_set_fg(color_24bit_256(color));
330 		return -1;
331 	}
332 
333 	/* \e[x8;2;...;...;...m */
334 	sprintf(buf, "\033[%d8;2;%d;%d;%dm", bg ? 4 : 3, color[0], color[1], color[2]);
335 	return tputs(buf, 0, term_putchar);
336 }
337 
338 #define COLOR_RESET UINT_MAX
339 #define COLOR_BLACK24 COLOR_RESET - 1
340 
341 /* Change active color */
342 #ifdef TERM_TRUECOLOR
term_set_color2(TERM_WINDOW * window,int col,unsigned int fgcol24,unsigned int bgcol24)343 void term_set_color2(TERM_WINDOW *window, int col, unsigned int fgcol24, unsigned int bgcol24)
344 #else
345 void term_set_color(TERM_WINDOW *window, int col)
346 #endif
347 {
348 	int set_normal;
349 
350 	unsigned int fg, bg;
351 #ifdef TERM_TRUECOLOR
352 	if (col & ATTR_FGCOLOR24) {
353 		if (fgcol24)
354 			fg = fgcol24 << 8;
355 		else
356 			fg = COLOR_BLACK24;
357 	} else
358 #endif
359 		fg = (col & FG_MASK);
360 
361 #ifdef TERM_TRUECOLOR
362 	if (col & ATTR_BGCOLOR24) {
363 		if (bgcol24)
364 			bg = bgcol24 << 8;
365 		else
366 			bg = COLOR_BLACK24;
367 	} else
368 #endif
369 		bg = ((col & BG_MASK) >> BG_SHIFT);
370 
371 	if (!term_use_colors && bg > 0)
372 		col |= ATTR_REVERSE;
373 
374         set_normal = ((col & ATTR_RESETFG) && last_fg != COLOR_RESET) ||
375 		((col & ATTR_RESETBG) && last_bg != COLOR_RESET);
376 	if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
377 	    ((last_attrs & ATTR_REVERSE) && (col & ATTR_REVERSE) == 0) ||
378 	    ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
379 		/* we'll need to get rid of bold/blink/reverse - this
380 		   can only be done with setting the default color */
381 		set_normal = TRUE;
382 	}
383 
384 	if (set_normal) {
385 		last_fg = last_bg = COLOR_RESET;
386                 last_attrs = 0;
387 		terminfo_set_normal();
388 	}
389 
390 	/* set foreground color */
391 	if (fg != last_fg &&
392 	    (fg != 0 || (col & ATTR_RESETFG) == 0)) {
393                 if (term_use_colors) {
394 			last_fg = fg;
395 			if (fg >> 8)
396 				termctl_set_color_24bit(0,
397 							last_fg == COLOR_BLACK24 ? 0
398 							: last_fg >> 8);
399 			else
400 				terminfo_set_fg(last_fg);
401 		}
402 	}
403 
404 	/* set background color */
405 	if (window && window->term->TI_colors &&
406 	    (term_color256map[bg&0xff]&8) == window->term->TI_colors)
407 		col |= ATTR_BLINK;
408 	if (col & ATTR_BLINK)
409 		current_term->set_blink(current_term);
410 
411 	if (bg != last_bg &&
412 	    (bg != 0 || (col & ATTR_RESETBG) == 0)) {
413                 if (term_use_colors) {
414 			last_bg = bg;
415 			if (bg >> 8)
416 				termctl_set_color_24bit(1,
417 							last_bg == COLOR_BLACK24 ? 0
418 							: last_bg >> 8);
419 			else
420 				terminfo_set_bg(last_bg);
421 		}
422 	}
423 
424 	/* reversed text */
425 	if (col & ATTR_REVERSE)
426 		terminfo_set_reverse();
427 
428 	/* bold */
429 	if (window && window->term->TI_colors &&
430 	    (term_color256map[fg&0xff]&8) == window->term->TI_colors)
431 		col |= ATTR_BOLD;
432 	if (col & ATTR_BOLD)
433 		terminfo_set_bold();
434 
435 	/* underline */
436 	if (col & ATTR_UNDERLINE) {
437 		if ((last_attrs & ATTR_UNDERLINE) == 0)
438 			terminfo_set_uline(TRUE);
439 	} else if (last_attrs & ATTR_UNDERLINE)
440 		terminfo_set_uline(FALSE);
441 
442 	/* italic */
443 	if (col & ATTR_ITALIC) {
444 		if ((last_attrs & ATTR_ITALIC) == 0)
445 			terminfo_set_italic(TRUE);
446 	} else if (last_attrs & ATTR_ITALIC)
447 		terminfo_set_italic(FALSE);
448 
449 	/* update the new attribute settings whilst ignoring color values.  */
450 	last_attrs = col & ~( BG_MASK | FG_MASK );
451 }
452 
term_move(TERM_WINDOW * window,int x,int y)453 void term_move(TERM_WINDOW *window, int x, int y)
454 {
455 	if (x >= 0 && y >= 0) {
456 		vcmove = TRUE;
457 		vcx = x+window->x;
458 		vcy = y+window->y;
459 
460 		if (vcx >= term_width)
461 			vcx = term_width-1;
462 		if (vcy >= term_height)
463 			vcy = term_height-1;
464 	}
465 }
466 
term_printed_text(int count)467 static void term_printed_text(int count)
468 {
469 	term_lines_empty[vcy] = FALSE;
470 
471 	/* if we continued writing past the line, wrap to next line.
472 	   However, next term_move() really shouldn't try to cache
473 	   the move, otherwise terminals would try to combine the
474 	   last word in upper line with first word in lower line. */
475 	vcx += count;
476 	while (vcx >= term_width) {
477 		vcx -= term_width;
478 		if (vcy < term_height-1) vcy++;
479 		if (vcx > 0) term_lines_empty[vcy] = FALSE;
480 	}
481 
482 	crealx += count;
483 	if (crealx >= term_width)
484 		cforcemove = TRUE;
485 }
486 
term_addch(TERM_WINDOW * window,char chr)487 void term_addch(TERM_WINDOW *window, char chr)
488 {
489 	if (vcmove) term_move_real();
490 
491 	/* With UTF-8, move cursor only if this char is either
492 	   single-byte (8. bit off) or beginning of multibyte
493 	   (7. bit off) */
494 	if (term_type != TERM_TYPE_UTF8 ||
495 	    (chr & 0x80) == 0 || (chr & 0x40) == 0) {
496 		term_printed_text(1);
497 	}
498 
499 	putc(chr, window->term->out);
500 }
501 
term_addch_utf8(TERM_WINDOW * window,unichar chr)502 static void term_addch_utf8(TERM_WINDOW *window, unichar chr)
503 {
504 	char buf[10];
505 	int i, len;
506 
507 	len = g_unichar_to_utf8(chr, buf);
508 	for (i = 0;  i < len; i++)
509                 putc(buf[i], window->term->out);
510 }
511 
term_add_unichar(TERM_WINDOW * window,unichar chr)512 void term_add_unichar(TERM_WINDOW *window, unichar chr)
513 {
514 	if (vcmove) term_move_real();
515 
516 	switch (term_type) {
517 	case TERM_TYPE_UTF8:
518 		term_printed_text(unichar_isprint(chr) ? i_wcwidth(chr) : 1);
519                 term_addch_utf8(window, chr);
520 		break;
521 	case TERM_TYPE_BIG5:
522 		if (chr > 0xff) {
523 			term_printed_text(2);
524 			putc((chr >> 8) & 0xff, window->term->out);
525 		} else {
526 			term_printed_text(1);
527 		}
528 		putc((chr & 0xff), window->term->out);
529                 break;
530 	default:
531 		term_printed_text(1);
532 		putc(chr, window->term->out);
533                 break;
534 	}
535 }
536 
term_addstr(TERM_WINDOW * window,const char * str)537 int term_addstr(TERM_WINDOW *window, const char *str)
538 {
539 	int len, raw_len;
540 	unichar tmp;
541 	const char *ptr;
542 
543 	if (vcmove) term_move_real();
544 
545 	len = 0;
546 	raw_len = strlen(str);
547 
548 	/* The string length depends on the terminal encoding */
549 
550 	ptr = str;
551 
552 	if (term_type == TERM_TYPE_UTF8) {
553 		while (*ptr != '\0') {
554 			tmp = g_utf8_get_char_validated(ptr, -1);
555 			/* On utf8 error, treat as single byte and try to
556 			   continue interpreting rest of string as utf8 */
557 			if (tmp == (gunichar)-1 || tmp == (gunichar)-2) {
558 				len++;
559 				ptr++;
560 			} else {
561 				len += unichar_isprint(tmp) ? i_wcwidth(tmp) : 1;
562 				ptr = g_utf8_next_char(ptr);
563 			}
564 		}
565 	} else
566 		len = raw_len;
567 
568         term_printed_text(len);
569 
570 	/* Use strlen() here since we need the number of raw bytes */
571 	fwrite(str, 1, raw_len, window->term->out);
572 
573 	return len;
574 }
575 
term_clrtoeol(TERM_WINDOW * window)576 void term_clrtoeol(TERM_WINDOW *window)
577 {
578 	if (vcx < window->x) {
579 		/* we just wrapped outside of the split, warp the cursor back into the window */
580 		vcx += window->x;
581 		vcmove = TRUE;
582 	}
583 	if (window->x + window->width < term_width) {
584 		/* we need to fill a vertical split */
585 		if (vcmove) term_move_real();
586 		terminfo_repeat(' ', window->x + window->width - vcx + 1);
587 		terminfo_move(vcx, vcy);
588 		term_lines_empty[vcy] = FALSE;
589 	} else {
590 		/* clrtoeol() doesn't necessarily understand colors */
591 		if (last_fg == -1 && last_bg == -1 &&
592 		    (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE|ATTR_ITALIC)) == 0) {
593 			if (!term_lines_empty[vcy]) {
594 				if (vcmove) term_move_real();
595 				terminfo_clrtoeol();
596 				if (vcx == 0) term_lines_empty[vcy] = TRUE;
597 			}
598 		} else if (vcx < term_width) {
599 			/* we'll need to fill the line ourself. */
600 			if (vcmove) term_move_real();
601 			terminfo_repeat(' ', term_width-vcx);
602 			terminfo_move(vcx, vcy);
603 			term_lines_empty[vcy] = FALSE;
604 		}
605 	}
606 }
607 
term_window_clrtoeol(TERM_WINDOW * window,int ypos)608 void term_window_clrtoeol(TERM_WINDOW* window, int ypos)
609 {
610 	if (ypos >= 0 && window->y + ypos != vcy) {
611 		/* the line is already full */
612 		return;
613 	}
614 	term_clrtoeol(window);
615 	if (window->x + window->width < term_width) {
616 		gui_printtext_window_border(window->x + window->width, window->y + ypos);
617 		term_set_color(window, ATTR_RESET);
618 	}
619 }
620 
term_window_clrtoeol_abs(TERM_WINDOW * window,int ypos)621 void term_window_clrtoeol_abs(TERM_WINDOW* window, int ypos)
622 {
623 	term_window_clrtoeol(window, ypos - window->y);
624 }
625 
term_move_cursor(int x,int y)626 void term_move_cursor(int x, int y)
627 {
628 	curs_x = x;
629         curs_y = y;
630 }
631 
term_refresh(TERM_WINDOW * window)632 void term_refresh(TERM_WINDOW *window)
633 {
634 	if (freeze_counter > 0)
635 		return;
636 
637 	term_move(root_window, curs_x, curs_y);
638 	term_move_real();
639 
640 	if (!curs_visible) {
641 		terminfo_set_cursor_visible(TRUE);
642                 curs_visible = TRUE;
643 	}
644 
645 	term_set_color(window, ATTR_RESET);
646 	fflush(window != NULL ? window->term->out : current_term->out);
647 }
648 
term_refresh_freeze(void)649 void term_refresh_freeze(void)
650 {
651         freeze_counter++;
652 }
653 
term_refresh_thaw(void)654 void term_refresh_thaw(void)
655 {
656 	if (--freeze_counter == 0)
657                 term_refresh(NULL);
658 }
659 
term_stop(void)660 void term_stop(void)
661 {
662 	terminfo_stop(current_term);
663 	kill(getpid(), SIGTSTP);
664 	/* this call needs to stay here in case the TSTP was ignored,
665 	   because then we never see a CONT to call the restoration
666 	   code. On the other hand we also cannot remove the CONT
667 	   handler because then nothing would restore the screen when
668 	   Irssi is killed with TSTP/STOP from external. */
669 	terminfo_cont(current_term);
670 	irssi_redraw();
671 }
672 
input_utf8(const unsigned char * buffer,int size,unichar * result)673 static int input_utf8(const unsigned char *buffer, int size, unichar *result)
674 {
675 	unichar c = g_utf8_get_char_validated((char *) buffer, size);
676 
677 	/* GLib >= 2.63 do not accept Unicode NUL anymore */
678 	if (c == (unichar) -2 && *buffer == 0 && size > 0)
679 		c = 0;
680 
681 	switch (c) {
682 	case (unichar)-1:
683 		/* not UTF8 - fallback to 8bit ascii */
684 		*result = *buffer;
685 		return 1;
686 	case (unichar)-2:
687                 /* need more data */
688 		return -1;
689 	default:
690 		*result = c;
691 		return g_utf8_skip[*buffer];
692 	}
693 }
694 
input_big5(const unsigned char * buffer,int size,unichar * result)695 static int input_big5(const unsigned char *buffer, int size, unichar *result)
696 {
697 	if (is_big5_hi(*buffer)) {
698 		/* could be */
699 		if (size == 1)
700 			return -1;
701 
702 		if (is_big5_los(buffer[1]) || is_big5_lox(buffer[1])) {
703                         *result = buffer[1] + ((int) *buffer << 8);
704 			return 2;
705 		}
706 	}
707 
708         *result = *buffer;
709 	return 1;
710 }
711 
input_8bit(const unsigned char * buffer,int size,unichar * result)712 static int input_8bit(const unsigned char *buffer, int size, unichar *result)
713 {
714         *result = *buffer;
715         return 1;
716 }
717 
term_set_input_type(int type)718 void term_set_input_type(int type)
719 {
720 	switch (type) {
721 	case TERM_TYPE_UTF8:
722                 input_func = input_utf8;
723                 break;
724 	case TERM_TYPE_BIG5:
725                 input_func = input_big5;
726 		break;
727 	default:
728                 input_func = input_8bit;
729 	}
730 }
731 
term_gets(GArray * buffer,int * line_count)732 void term_gets(GArray *buffer, int *line_count)
733 {
734 	int ret, i, char_len;
735 
736         /* fread() doesn't work */
737 
738 	ret = read(fileno(current_term->in),
739 		   term_inbuf + term_inbuf_pos, sizeof(term_inbuf)-term_inbuf_pos);
740 	if (ret == 0) {
741 		/* EOF - terminal got lost */
742 		ret = -1;
743 	} else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
744 		ret = 0;
745 	if (ret == -1)
746 		signal_emit("command quit", 1, "Lost terminal");
747 
748 	if (ret > 0) {
749                 /* convert input to unichars. */
750 		term_inbuf_pos += ret;
751 		for (i = 0; i < term_inbuf_pos; ) {
752 			unichar key;
753 			char_len = input_func(term_inbuf+i, term_inbuf_pos-i,
754 					      &key);
755 			if (char_len < 0)
756 				break;
757 			g_array_append_val(buffer, key);
758 			if (key == '\r' || key == '\n')
759 				(*line_count)++;
760 
761 			i += char_len;
762 		}
763 
764 		if (i >= term_inbuf_pos)
765 			term_inbuf_pos = 0;
766 		else if (i > 0) {
767 			memmove(term_inbuf, term_inbuf+i, term_inbuf_pos-i);
768                         term_inbuf_pos -= i;
769 		}
770 	}
771 }
772 
773 static const char* term_env_warning =
774 	"You seem to be running Irssi inside %2$s, but the TERM environment variable "
775 	"is set to '%1$s', which can cause display glitches.\n"
776 	"Consider changing TERM to '%2$s' or '%2$s-256color' instead.";
777 
term_environment_check(void)778 void term_environment_check(void)
779 {
780 	const char *term, *sty, *tmux, *multiplexer;
781 
782 	term = g_getenv("TERM");
783 	sty = g_getenv("STY");
784 	tmux = g_getenv("TMUX");
785 
786 	multiplexer = (sty && *sty) ? "screen" :
787 	              (tmux && *tmux) ? "tmux" : NULL;
788 
789 	if (!multiplexer) {
790 		return;
791 	}
792 
793 	if (term && (g_str_has_prefix(term, "screen") ||
794 	             g_str_has_prefix(term, "tmux"))) {
795 		return;
796 	}
797 
798 	g_warning(term_env_warning, term, multiplexer);
799 }
800