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 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.
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;
51 static int last_fg, last_bg, last_attrs;
53 static GSource *sigcont_source;
54 static volatile sig_atomic_t got_sigcont;
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)
68 static gboolean sigcont_prepare(GSource *source, gint *timeout)
74 static gboolean sigcont_check(GSource *source)
79 static gboolean sigcont_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
84 return callback(user_data);
87 static gboolean do_redraw(gpointer unused)
89 terminfo_cont(current_term);
95 static GSourceFuncs sigcont_funcs = {
96 .prepare = sigcont_prepare,
97 .check = sigcont_check,
98 .dispatch = sigcont_dispatch
103 struct sigaction act;
106 last_fg = last_bg = -1;
108 vcx = vcy = 0; crealx = crealy = -1;
109 vcmove = FALSE; cforcemove = TRUE;
112 current_term = terminfo_core_init(stdin, stdout);
113 if (current_term == NULL)
116 if (term_get_size(&width, &height)) {
117 current_term->width = width;
118 current_term->height = height;
121 /* grab CONT signal */
122 sigemptyset(&act.sa_mask);
124 act.sa_handler = sig_cont;
125 sigaction(SIGCONT, &act, NULL);
126 sigcont_source = g_source_new(&sigcont_funcs, sizeof(GSource));
127 g_source_set_callback(sigcont_source, do_redraw, NULL, NULL);
128 g_source_attach(sigcont_source, NULL);
131 term_width = current_term->width;
132 term_height = current_term->height;
133 root_window = term_window_create(0, 0, term_width, term_height);
135 term_lines_empty = g_new0(char, term_height);
137 term_set_input_type(TERM_TYPE_8BIT);
139 g_atexit(term_deinit);
143 void term_deinit(void)
145 if (current_term != NULL) {
146 signal(SIGCONT, SIG_DFL);
147 g_source_destroy(sigcont_source);
148 g_source_unref(sigcont_source);
150 term_common_deinit();
151 terminfo_core_deinit(current_term);
156 static void term_move_real(void)
158 if (vcx != crealx || vcy != crealy || cforcemove) {
160 terminfo_set_cursor_visible(FALSE);
161 curs_visible = FALSE;
165 crealx = crealy = -1;
168 terminfo_move_relative(crealx, crealy, vcx, vcy);
169 crealx = vcx; crealy = vcy;
175 /* Cursor position is unknown - move it immediately to known position */
176 static void term_move_reset(int x, int y)
178 if (x >= term_width) x = term_width-1;
179 if (y >= term_height) y = term_height-1;
186 /* Resize terminal - if width or height is negative,
187 the new size is unknown and should be figured out somehow */
188 void term_resize(int width, int height)
190 if (width < 0 || height < 0) {
191 width = current_term->width;
192 height = current_term->height;
195 if (term_width != width || term_height != height) {
196 term_width = current_term->width = width;
197 term_height = current_term->height = height;
198 term_window_move(root_window, 0, 0, term_width, term_height);
200 g_free(term_lines_empty);
201 term_lines_empty = g_new0(char, term_height);
204 term_move_reset(0, 0);
207 void term_resize_final(int width, int height)
211 /* Returns TRUE if terminal has colors */
212 int term_has_colors(void)
214 return current_term->TI_colors > 0;
217 /* Force the colors on any way you can */
218 void term_force_colors(int set)
220 terminfo_setup_colors(current_term, set);
224 void term_clear(void)
226 term_set_color(root_window, ATTR_RESET);
228 term_move_reset(0, 0);
230 memset(term_lines_empty, 1, term_height);
236 terminfo_beep(current_term);
239 /* Create a new window in terminal */
240 TERM_WINDOW *term_window_create(int x, int y, int width, int height)
244 window = g_new0(TERM_WINDOW, 1);
245 window->term = current_term;
246 window->x = x; window->y = y;
247 window->width = width; window->height = height;
251 /* Destroy a terminal window */
252 void term_window_destroy(TERM_WINDOW *window)
257 /* Move/resize a window */
258 void term_window_move(TERM_WINDOW *window, int x, int y,
259 int width, int height)
263 window->width = width;
264 window->height = height;
268 void term_window_clear(TERM_WINDOW *window)
272 terminfo_set_normal();
273 if (window->y == 0 && window->height == term_height) {
276 for (y = 0; y < window->height; y++) {
277 term_move(window, 0, y);
278 term_clrtoeol(window);
283 /* Scroll window up/down */
284 void term_window_scroll(TERM_WINDOW *window, int count)
288 terminfo_scroll(window->y, window->y+window->height-1, count);
289 term_move_reset(vcx, vcy);
291 /* set the newly scrolled area dirty */
292 for (y = 0; y < window->height; y++)
293 term_lines_empty[window->y+y] = FALSE;
296 /* Change active color */
297 void term_set_color(TERM_WINDOW *window, int col)
301 int bg = (col & 0xf0) >> 4;
303 set_normal = ((col & ATTR_RESETFG) && last_fg != -1) ||
304 ((col & ATTR_RESETBG) && last_bg != -1);
305 if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
306 ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
307 /* we'll need to get rid of bold/blink - this can only be
308 done with setting the default color */
313 last_fg = last_bg = -1;
315 terminfo_set_normal();
318 if (!term_use_colors && (col & 0xf0) != 0)
321 /* reversed text (use standout) */
322 if (col & ATTR_REVERSE) {
323 if ((last_attrs & ATTR_REVERSE) == 0)
324 terminfo_set_standout(TRUE);
325 } else if (last_attrs & ATTR_REVERSE)
326 terminfo_set_standout(FALSE);
328 /* set foreground color */
330 (fg != 0 || (col & ATTR_RESETFG) == 0)) {
331 if (term_use_colors) {
333 terminfo_set_fg(last_fg);
337 /* set background color */
338 if (col & 0x80 && window->term->TI_colors == 8)
340 if (col & ATTR_BLINK)
341 current_term->set_blink(current_term);
344 (bg != 0 || (col & ATTR_RESETBG) == 0)) {
345 if (term_use_colors) {
347 terminfo_set_bg(last_bg);
352 if (col & 0x08 && window->term->TI_colors == 8)
358 if (col & ATTR_UNDERLINE) {
359 if ((last_attrs & ATTR_UNDERLINE) == 0)
360 terminfo_set_uline(TRUE);
361 } else if (last_attrs & ATTR_UNDERLINE)
362 terminfo_set_uline(FALSE);
364 last_attrs = col & ~0xff;
367 void term_move(TERM_WINDOW *window, int x, int y)
369 if (x >= 0 && y >= 0) {
374 if (vcx >= term_width)
376 if (vcy >= term_height)
381 static void term_printed_text(int count)
383 term_lines_empty[vcy] = FALSE;
385 /* if we continued writing past the line, wrap to next line.
386 However, next term_move() really shouldn't try to cache
387 the move, otherwise terminals would try to combine the
388 last word in upper line with first word in lower line. */
390 while (vcx >= term_width) {
392 if (vcy < term_height-1) vcy++;
393 if (vcx > 0) term_lines_empty[vcy] = FALSE;
397 if (crealx >= term_width)
401 void term_addch(TERM_WINDOW *window, char chr)
403 if (vcmove) term_move_real();
405 /* With UTF-8, move cursor only if this char is either
406 single-byte (8. bit off) or beginning of multibyte
408 if (term_type != TERM_TYPE_UTF8 ||
409 (chr & 0x80) == 0 || (chr & 0x40) == 0) {
410 term_printed_text(1);
413 putc(chr, window->term->out);
416 static void term_addch_utf8(TERM_WINDOW *window, unichar chr)
421 len = g_unichar_to_utf8(chr, buf);
422 for (i = 0; i < len; i++)
423 putc(buf[i], window->term->out);
426 void term_add_unichar(TERM_WINDOW *window, unichar chr)
428 if (vcmove) term_move_real();
432 term_printed_text(unichar_isprint(chr) ? mk_wcwidth(chr) : 1);
433 term_addch_utf8(window, chr);
437 term_printed_text(2);
438 putc((chr >> 8) & 0xff, window->term->out);
440 term_printed_text(1);
442 putc((chr & 0xff), window->term->out);
445 term_printed_text(1);
446 putc(chr, window->term->out);
451 void term_addstr(TERM_WINDOW *window, const char *str)
455 if (vcmove) term_move_real();
456 len = strlen(str); /* FIXME utf8 or big5 */
457 term_printed_text(len);
459 fwrite(str, 1, len, window->term->out);
462 void term_clrtoeol(TERM_WINDOW *window)
464 /* clrtoeol() doesn't necessarily understand colors */
465 if (last_fg == -1 && last_bg == -1 &&
466 (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) {
467 if (!term_lines_empty[vcy]) {
468 if (vcmove) term_move_real();
470 if (vcx == 0) term_lines_empty[vcy] = TRUE;
472 } else if (vcx < term_width) {
473 /* we'll need to fill the line ourself. */
474 if (vcmove) term_move_real();
475 terminfo_repeat(' ', term_width-vcx);
476 terminfo_move(vcx, vcy);
477 term_lines_empty[vcy] = FALSE;
481 void term_move_cursor(int x, int y)
487 void term_refresh(TERM_WINDOW *window)
489 if (freeze_counter > 0)
492 term_move(root_window, curs_x, curs_y);
496 terminfo_set_cursor_visible(TRUE);
500 term_set_color(window, ATTR_RESET);
501 fflush(window != NULL ? window->term->out : current_term->out);
504 void term_refresh_freeze(void)
509 void term_refresh_thaw(void)
511 if (--freeze_counter == 0)
517 terminfo_stop(current_term);
518 kill(getpid(), SIGTSTP);
519 terminfo_cont(current_term);
523 static int input_utf8(const unsigned char *buffer, int size, unichar *result)
525 unichar c = g_utf8_get_char_validated(buffer, size);
529 /* not UTF8 - fallback to 8bit ascii */
537 return g_utf8_skip[*buffer];
541 static int input_big5(const unsigned char *buffer, int size, unichar *result)
543 if (is_big5_hi(*buffer)) {
548 if (is_big5_los(buffer[1]) || is_big5_lox(buffer[1])) {
549 *result = buffer[1] + ((int) *buffer << 8);
558 static int input_8bit(const unsigned char *buffer, int size, unichar *result)
564 void term_set_input_type(int type)
568 input_func = input_utf8;
571 input_func = input_big5;
574 input_func = input_8bit;
578 void term_gets(GArray *buffer, int *line_count)
580 int ret, i, char_len;
582 /* fread() doesn't work */
584 ret = read(fileno(current_term->in),
585 term_inbuf + term_inbuf_pos, sizeof(term_inbuf)-term_inbuf_pos);
587 /* EOF - terminal got lost */
589 } else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
592 signal_emit("command quit", 1, "Lost terminal");
595 /* convert input to unichars. */
596 term_inbuf_pos += ret;
597 for (i = 0; i < term_inbuf_pos; ) {
599 char_len = input_func(term_inbuf+i, term_inbuf_pos-i,
603 g_array_append_val(buffer, key);
604 if (key == '\r' || key == '\n')
610 if (i >= term_inbuf_pos)
613 memmove(term_inbuf, term_inbuf+i, term_inbuf_pos-i);