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