2 term-terminfo.c : irssi
4 Copyright (C) 2001 Timo Sirainen
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.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "terminfo-core.h"
29 /* returns number of characters in the beginning of the buffer being a
30 a single character, or -1 if more input is needed. The character will be
32 typedef int (*TERM_INPUT_FUNC)(const unsigned char *buffer, int size,
36 /* Terminal to use for window */
39 /* Area for window in terminal */
44 TERM_WINDOW *root_window;
46 static char *term_lines_empty; /* 1 if line is entirely empty */
47 static int vcmove, vcx, vcy, curs_visible;
48 static int crealx, crealy, cforcemove;
49 static int curs_x, curs_y;
50 static int auto_detach;
52 static int last_fg, last_bg, last_attrs;
54 static int redraw_needed, redraw_tag;
55 static int freeze_counter;
57 static TERM_INPUT_FUNC input_func;
58 static unsigned char term_inbuf[256];
59 static int term_inbuf_pos;
62 static void sig_cont(int p)
65 terminfo_cont(current_term);
68 static int redraw_timeout(void)
72 redraw_needed = FALSE;
83 last_fg = last_bg = -1;
85 vcx = vcy = 0; crealx = crealy = -1;
86 vcmove = FALSE; cforcemove = TRUE;
89 current_term = terminfo_core_init(stdin, stdout);
90 if (current_term == NULL)
93 if (term_get_size(&width, &height)) {
94 current_term->width = width;
95 current_term->height = height;
98 /* grab CONT signal */
99 sigemptyset(&act.sa_mask);
101 act.sa_handler = sig_cont;
102 sigaction(SIGCONT, &act, NULL);
103 redraw_tag = g_timeout_add(500, (GSourceFunc) redraw_timeout, NULL);
106 term_width = current_term->width;
107 term_height = current_term->height;
108 root_window = term_window_create(0, 0, term_width, term_height);
109 term_detached = FALSE;
111 term_lines_empty = g_new0(char, term_height);
113 term_set_input_type(TERM_TYPE_8BIT);
115 g_atexit(term_deinit);
119 void term_deinit(void)
121 if (current_term != NULL) {
122 signal(SIGCONT, SIG_DFL);
123 g_source_remove(redraw_tag);
125 term_common_deinit();
126 terminfo_core_deinit(current_term);
131 static void term_move_real(void)
133 if (term_detached) return;
135 if (vcx != crealx || vcy != crealy || cforcemove) {
137 terminfo_set_cursor_visible(FALSE);
138 curs_visible = FALSE;
142 crealx = crealy = -1;
145 terminfo_move_relative(crealx, crealy, vcx, vcy);
146 crealx = vcx; crealy = vcy;
152 /* Cursor position is unknown - move it immediately to known position */
153 static void term_move_reset(int x, int y)
155 if (x >= term_width) x = term_width-1;
156 if (y >= term_height) y = term_height-1;
163 /* Resize terminal - if width or height is negative,
164 the new size is unknown and should be figured out somehow */
165 void term_resize(int width, int height)
167 if (width < 0 || height < 0) {
168 terminfo_resize(current_term);
169 width = current_term->width;
170 height = current_term->height;
173 if (term_width != width || term_height != height) {
174 term_width = current_term->width = width;
175 term_height = current_term->height = height;
176 term_window_move(root_window, 0, 0, term_width, term_height);
178 g_free(term_lines_empty);
179 term_lines_empty = g_new0(char, term_height);
182 term_move_reset(0, 0);
185 void term_resize_final(int width, int height)
189 /* Returns TRUE if terminal has colors */
190 int term_has_colors(void)
192 return current_term->has_colors;
195 /* Force the colors on any way you can */
196 void term_force_colors(int set)
198 if (term_detached) return;
200 terminfo_setup_colors(current_term, set);
204 void term_clear(void)
206 if (term_detached) return;
208 term_set_color(root_window, ATTR_RESET);
210 term_move_reset(0, 0);
212 memset(term_lines_empty, 1, term_height);
218 if (term_detached) return;
220 terminfo_beep(current_term);
223 /* Create a new window in terminal */
224 TERM_WINDOW *term_window_create(int x, int y, int width, int height)
228 window = g_new0(TERM_WINDOW, 1);
229 window->term = current_term;
230 window->x = x; window->y = y;
231 window->width = width; window->height = height;
235 /* Destroy a terminal window */
236 void term_window_destroy(TERM_WINDOW *window)
241 /* Move/resize a window */
242 void term_window_move(TERM_WINDOW *window, int x, int y,
243 int width, int height)
247 window->width = width;
248 window->height = height;
252 void term_window_clear(TERM_WINDOW *window)
256 if (term_detached) return;
258 terminfo_set_normal();
259 if (window->y == 0 && window->height == term_height) {
262 for (y = 0; y < window->height; y++) {
263 term_move(window, 0, y);
264 term_clrtoeol(window);
269 /* Scroll window up/down */
270 void term_window_scroll(TERM_WINDOW *window, int count)
274 if (term_detached) return;
276 terminfo_scroll(window->y, window->y+window->height-1, count);
277 term_move_reset(vcx, vcy);
279 /* set the newly scrolled area dirty */
280 for (y = 0; y < window->height; y++)
281 term_lines_empty[window->y+y] = FALSE;
284 /* Change active color */
285 void term_set_color(TERM_WINDOW *window, int col)
289 if (term_detached) return;
291 set_normal = ((col & ATTR_RESETFG) && last_fg != -1) ||
292 ((col & ATTR_RESETBG) && last_bg != -1);
293 if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
294 ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
295 /* we'll need to get rid of bold/blink - this can only be
296 done with setting the default color */
301 last_fg = last_bg = -1;
303 terminfo_set_normal();
306 if (!term_use_colors && (col & 0xf0) != 0)
309 /* reversed text (use standout) */
310 if (col & ATTR_REVERSE) {
311 if ((last_attrs & ATTR_REVERSE) == 0)
312 terminfo_set_standout(TRUE);
313 } else if (last_attrs & ATTR_REVERSE)
314 terminfo_set_standout(FALSE);
316 /* set foreground color */
317 if ((col & 0x0f) != last_fg &&
318 ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) {
319 if (term_use_colors) {
320 last_fg = col & 0x0f;
321 terminfo_set_fg(last_fg);
325 /* set background color */
326 if (col & ATTR_BLINK)
331 if ((col & 0xf0) >> 4 != last_bg &&
332 ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) {
333 if (term_use_colors) {
334 last_bg = (col & 0xf0) >> 4;
335 terminfo_set_bg(last_bg);
342 else if (col & ATTR_BOLD)
346 if (col & ATTR_UNDERLINE) {
347 if ((last_attrs & ATTR_UNDERLINE) == 0)
348 terminfo_set_uline(TRUE);
349 } else if (last_attrs & ATTR_UNDERLINE)
350 terminfo_set_uline(FALSE);
352 last_attrs = col & ~0xff;
355 void term_move(TERM_WINDOW *window, int x, int y)
361 if (vcx >= term_width)
363 if (vcy >= term_height)
367 static void term_printed_text(int count)
369 term_lines_empty[vcy] = FALSE;
371 /* if we continued writing past the line, wrap to next line.
372 However, next term_move() really shouldn't try to cache
373 the move, otherwise terminals would try to combine the
374 last word in upper line with first word in lower line. */
377 while (vcx >= term_width) {
379 if (vcy < term_height) vcy++;
380 if (vcx > 0) term_lines_empty[vcy] = FALSE;
384 void term_addch(TERM_WINDOW *window, int chr)
386 if (term_detached) return;
388 if (vcmove) term_move_real();
390 /* With UTF-8, move cursor only if this char is either single-byte
391 (8. bit on) or beginning of multibyte (7+8 bits on) */
392 if (term_type != TERM_TYPE_UTF8 ||
393 (chr & 0x80) == 0 || (chr & 0x40) == 0) {
394 term_printed_text(1);
397 if (vcy != term_height || vcx != 0)
398 putc(chr, window->term->out);
401 static void term_addch_utf8(TERM_WINDOW *window, unichar chr)
406 len = utf16_char_to_utf8(chr, buf);
407 for (i = 0; i < len; i++)
408 putc(buf[i], window->term->out);
411 void term_add_unichar(TERM_WINDOW *window, unichar chr)
413 if (term_detached) return;
415 if (vcmove) term_move_real();
416 term_printed_text(1);
417 if (vcy == term_height && vcx == 0)
418 return; /* last char in screen */
422 term_addch_utf8(window, chr);
425 putc((chr >> 8) & 0xff, window->term->out);
426 putc((chr & 0xff), window->term->out);
429 putc(chr, window->term->out);
434 void term_addstr(TERM_WINDOW *window, const char *str)
438 if (term_detached) return;
440 if (vcmove) term_move_real();
442 term_printed_text(len);
444 if (vcy != term_height || vcx != 0)
445 fputs(str, window->term->out);
447 fwrite(str, 1, len-1, window->term->out);
450 void term_clrtoeol(TERM_WINDOW *window)
452 if (term_detached) return;
454 /* clrtoeol() doesn't necessarily understand colors */
455 if (last_fg == -1 && last_bg == -1 &&
456 (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) {
457 if (!term_lines_empty[vcy]) {
458 if (vcmove) term_move_real();
460 if (vcx == 0) term_lines_empty[vcy] = TRUE;
462 } else if (vcx < term_width) {
463 /* we'll need to fill the line ourself. */
464 if (vcmove) term_move_real();
465 terminfo_repeat(' ', term_width-vcx);
466 terminfo_move(vcx, vcy);
467 term_lines_empty[vcy] = FALSE;
471 void term_move_cursor(int x, int y)
477 void term_refresh(TERM_WINDOW *window)
479 if (term_detached || freeze_counter > 0)
482 term_move(root_window, curs_x, curs_y);
486 terminfo_set_cursor_visible(TRUE);
489 term_set_color(window, ATTR_RESET);
490 fflush(window != NULL ? window->term->out : current_term->out);
493 void term_refresh_freeze(void)
497 if (!term_detached && curs_visible) {
498 terminfo_set_cursor_visible(FALSE);
499 curs_visible = FALSE;
503 void term_refresh_thaw(void)
505 if (--freeze_counter == 0)
509 void term_auto_detach(int set)
514 void term_detach(void)
516 terminfo_stop(current_term);
518 fclose(current_term->in);
519 fclose(current_term->out);
521 current_term->in = NULL;
522 current_term->out = NULL;
523 term_detached = TRUE;
526 void term_attach(FILE *in, FILE *out)
528 current_term->in = in;
529 current_term->out = out;
530 term_detached = FALSE;
532 terminfo_cont(current_term);
539 kill(getpid(), SIGSTOP);
541 terminfo_stop(current_term);
542 kill(getpid(), SIGSTOP);
543 terminfo_cont(current_term);
548 static int input_utf8(const unsigned char *buffer, int size, unichar *result)
550 const unsigned char *end = buffer;
552 *result = get_utf8_char(&end, size);
555 /* not UTF8 - fallback to 8bit ascii */
562 return (int) (end-buffer)+1;
566 /* XXX I didn't check the encoding range of big5+. This is standard big5. */
567 #define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
568 #define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
569 #define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE)
570 #define is_big5(hi,lo) (is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo)))
572 static int input_big5(const unsigned char *buffer, int size, unichar *result)
574 if (is_big5_hi(*buffer)) {
579 if (is_big5_los(buffer[1]) || is_big5_lox(buffer[1])) {
580 *result = buffer[1] + ((int) *buffer << 8);
589 static int input_8bit(const unsigned char *buffer, int size, unichar *result)
595 void term_set_input_type(int type)
599 input_func = input_utf8;
602 input_func = input_big5;
605 input_func = input_8bit;
609 int term_gets(unichar *buffer, int size)
611 int ret, i, char_len;
616 /* fread() doesn't work */
617 if (size > sizeof(term_inbuf)-term_inbuf_pos)
618 size = sizeof(term_inbuf)-term_inbuf_pos;
620 ret = read(fileno(current_term->in),
621 term_inbuf + term_inbuf_pos, size);
623 /* EOF - terminal got lost */
627 } else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
631 /* convert input to unichars. */
632 term_inbuf_pos += ret;
634 for (i = 0; i < term_inbuf_pos; ) {
635 char_len = input_func(term_inbuf+i, term_inbuf_pos-i,
645 if (i >= term_inbuf_pos)
648 memmove(term_inbuf+i, term_inbuf, term_inbuf_pos-i);