3 #include "terminfo-core.h"
5 #ifndef _POSIX_VDISABLE
6 # define _POSIX_VDISABLE 0
9 #define tput(s) tputs(s, 0, term_putchar)
10 inline static int term_putchar(int c)
12 return fputc(c, current_term->out);
15 /* Don't bother including curses.h because of these -
16 they might not even be defined there */
25 #define term_getstr(x, buffer) tigetstr(x.ti_name)
26 #define term_getnum(x) tigetnum(x.ti_name);
27 #define term_getflag(x) tigetflag(x.ti_name);
33 #define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer)
34 #define term_getnum(x) tgetnum(x.tc_name)
35 #define term_getflag(x) tgetflag(x.tc_name)
38 #define CAP_TYPE_FLAG 0
39 #define CAP_TYPE_INT 1
40 #define CAP_TYPE_STR 2
43 const char *ti_name; /* terminfo name */
44 const char *tc_name; /* termcap name */
49 TERM_REC *current_term;
51 /* Define only what we might need */
52 static TERMINFO_REC tcaps[] = {
54 { "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
55 { "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
58 { "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
59 { "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
60 { "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
61 { "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
62 { "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
63 { "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
64 { "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
65 { "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
66 { "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
69 { "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
70 { "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
71 { "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
72 { "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
73 { "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
74 { "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
75 { "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
76 { "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
77 { "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
78 { "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
81 { "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
82 { "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
84 /* Clearing to end of line */
85 { "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
87 /* Repeating character */
88 { "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
91 { "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
92 { "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
93 { "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
94 { "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
95 { "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
96 { "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
97 { "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
98 { "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
99 { "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
100 { "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
101 { "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
102 { "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
105 { "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
108 /* Move cursor (cursor_address / cup) */
109 static void _move_cup(TERM_REC *term, int x, int y)
111 tput(tparm(term->TI_cup, y, x));
114 /* Move cursor (column_address+row_address / hpa+vpa) */
115 static void _move_pa(TERM_REC *term, int x, int y)
117 tput(tparm(term->TI_hpa, x));
118 tput(tparm(term->TI_vpa, y));
121 /* Move cursor from a known position */
122 static void _move_relative(TERM_REC *term, int oldx, int oldy, int x, int y)
124 if (oldx == 0 && x == 0 && y == oldy+1) {
125 /* move to beginning of next line -
126 hope this works everywhere */
131 if (oldx > 0 && y == oldy) {
132 /* move cursor left/right */
133 if (x == oldx-1 && term->TI_cub1) {
134 tput(tparm(term->TI_cub1));
137 if (x == oldx+1 && y == oldy && term->TI_cuf1) {
138 tput(tparm(term->TI_cuf1));
143 /* fallback to absolute positioning */
145 tput(tparm(term->TI_cup, y, x));
150 tput(tparm(term->TI_vpa, y));
152 tput(tparm(term->TI_hpa, x));
155 /* Set cursor visible/invisible */
156 static void _set_cursor_visible(TERM_REC *term, int set)
158 tput(tparm(set ? term->TI_cnorm : term->TI_civis));
161 #define scroll_region_setup(term, y1, y2) \
162 if ((term)->TI_csr != NULL) \
163 tput(tparm((term)->TI_csr, y1, y2)); \
164 else if ((term)->TI_wind != NULL) \
165 tput(tparm((term)->TI_wind, y1, y2, 0, (term)->width-1));
167 /* Scroll (change_scroll_region+parm_rindex+parm_index / csr+rin+indn) */
168 static void _scroll_region(TERM_REC *term, int y1, int y2, int count)
170 /* setup the scrolling region to wanted area */
171 scroll_region_setup(term, y1, y2);
173 term->move(term, 0, y1);
175 term->move(term, 0, y2);
176 tput(tparm(term->TI_indn, count, count));
177 } else if (count < 0) {
178 term->move(term, 0, y1);
179 tput(tparm(term->TI_rin, -count, -count));
182 /* reset the scrolling region to full screen */
183 scroll_region_setup(term, 0, term->height-1);
186 /* Scroll (change_scroll_region+scroll_reverse+scroll_forward / csr+ri+ind) */
187 static void _scroll_region_1(TERM_REC *term, int y1, int y2, int count)
191 /* setup the scrolling region to wanted area */
192 scroll_region_setup(term, y1, y2);
195 term->move(term, 0, y2);
196 for (i = 0; i < count; i++)
197 tput(tparm(term->TI_ind));
198 } else if (count < 0) {
199 term->move(term, 0, y1);
200 for (i = count; i < 0; i++)
201 tput(tparm(term->TI_ri));
204 /* reset the scrolling region to full screen */
205 scroll_region_setup(term, 0, term->height-1);
208 /* Scroll (parm_insert_line+parm_delete_line / il+dl) */
209 static void _scroll_line(TERM_REC *term, int y1, int y2, int count)
211 /* setup the scrolling region to wanted area -
212 this might not necessarily work with il/dl, but at least it
213 looks better if it does */
214 scroll_region_setup(term, y1, y2);
217 term->move(term, 0, y1);
218 tput(tparm(term->TI_dl, count, count));
219 term->move(term, 0, y2-count+1);
220 tput(tparm(term->TI_il, count, count));
221 } else if (count < 0) {
222 term->move(term, 0, y2+count+1);
223 tput(tparm(term->TI_dl, -count, -count));
224 term->move(term, 0, y1);
225 tput(tparm(term->TI_il, -count, -count));
228 /* reset the scrolling region to full screen */
229 scroll_region_setup(term, 0, term->height-1);
232 /* Scroll (insert_line+delete_line / il1+dl1) */
233 static void _scroll_line_1(TERM_REC *term, int y1, int y2, int count)
238 term->move(term, 0, y1);
239 for (i = 0; i < count; i++)
240 tput(tparm(term->TI_dl1));
241 term->move(term, 0, y2-count+1);
242 for (i = 0; i < count; i++)
243 tput(tparm(term->TI_il1));
244 } else if (count < 0) {
245 term->move(term, 0, y2+count+1);
246 for (i = count; i < 0; i++)
247 tput(tparm(term->TI_dl1));
248 term->move(term, 0, y1);
249 for (i = count; i < 0; i++)
250 tput(tparm(term->TI_il1));
254 /* Clear screen (clear_screen / clear) */
255 static void _clear_screen(TERM_REC *term)
257 tput(tparm(term->TI_clear));
260 /* Clear screen (clr_eos / ed) */
261 static void _clear_eos(TERM_REC *term)
263 term->move(term, 0, 0);
264 tput(tparm(term->TI_ed));
267 /* Clear screen (parm_delete_line / dl) */
268 static void _clear_del(TERM_REC *term)
270 term->move(term, 0, 0);
271 tput(tparm(term->TI_dl, term->height, term->height));
274 /* Clear screen (delete_line / dl1) */
275 static void _clear_del_1(TERM_REC *term)
279 term->move(term, 0, 0);
280 for (i = 0; i < term->height; i++)
281 tput(tparm(term->TI_dl1));
284 /* Clear to end of line (clr_eol / el) */
285 static void _clrtoeol(TERM_REC *term)
287 tput(tparm(term->TI_el));
290 /* Repeat character (rep / rp) */
291 static void _repeat(TERM_REC *term, char chr, int count)
293 tput(tparm(term->TI_rep, chr, count));
296 /* Repeat character (manual) */
297 static void _repeat_manual(TERM_REC *term, char chr, int count)
300 putc(chr, term->out);
305 /* Reset all terminal attributes */
306 static void _set_normal(TERM_REC *term)
308 tput(tparm(term->TI_normal));
311 static void _set_blink(TERM_REC *term)
313 tput(tparm(term->TI_blink));
317 static void _set_bold(TERM_REC *term)
319 tput(tparm(term->TI_bold));
322 /* Underline on/off */
323 static void _set_uline(TERM_REC *term, int set)
325 tput(tparm(set ? term->TI_smul : term->TI_rmul));
328 /* Standout on/off */
329 static void _set_standout(TERM_REC *term, int set)
331 tput(tparm(set ? term->TI_smso : term->TI_rmso));
334 /* Change foreground color */
335 static void _set_fg(TERM_REC *term, int color)
337 tput(tparm(term->TI_fg[color % term->TI_colors]));
340 /* Change background color */
341 static void _set_bg(TERM_REC *term, int color)
343 tput(tparm(term->TI_bg[color % term->TI_colors]));
347 static void _beep(TERM_REC *term)
349 tput(tparm(term->TI_bel));
352 static void _ignore(TERM_REC *term)
356 static void _ignore_parm(TERM_REC *term, int param)
360 static void term_fill_capabilities(TERM_REC *term)
366 #ifndef HAVE_TERMINFO
367 char *tptr = term->buffer2;
369 for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) {
370 ptr = G_STRUCT_MEMBER_P(term, tcaps[i].offset);
372 switch (tcaps[i].type) {
374 ival = term_getflag(tcaps[i]);
378 ival = term_getnum(tcaps[i]);
382 sval = term_getstr(tcaps[i], tptr);
383 if (sval == (char *) -1)
384 *(char **)ptr = NULL;
386 *(char **)ptr = sval;
392 static void terminfo_colors_deinit(TERM_REC *term)
396 if (terminfo_is_colors_set(term)) {
397 for (i = 0; i < term->TI_colors; i++) {
398 g_free(term->TI_fg[i]);
399 g_free(term->TI_bg[i]);
402 g_free_and_null(term->TI_fg);
403 g_free_and_null(term->TI_bg);
407 /* Setup colors - if force is set, use ANSI-style colors if
408 terminal capabilities don't contain color codes */
409 void terminfo_setup_colors(TERM_REC *term, int force)
411 static const char ansitab[16] = {
412 0, 4, 2, 6, 1, 5, 3, 7,
413 8, 12, 10, 14, 9, 13, 11, 15
415 unsigned int i, color;
417 terminfo_colors_deinit(term);
419 if (force && term->TI_setf == NULL && term->TI_setaf == NULL)
422 if ((term->TI_setf || term->TI_setaf || force) &&
423 term->TI_colors > 0) {
424 term->TI_fg = g_new0(char *, term->TI_colors);
425 term->TI_bg = g_new0(char *, term->TI_colors);
426 term->set_fg = _set_fg;
427 term->set_bg = _set_bg;
431 term->set_fg = term->set_bg = _ignore_parm;
434 if (term->TI_setaf) {
435 for (i = 0; i < term->TI_colors; i++) {
436 color = i < 16 ? ansitab[i] : i;
437 term->TI_fg[i] = g_strdup(tparm(term->TI_setaf, color, 0));
439 } else if (term->TI_setf) {
440 for (i = 0; i < term->TI_colors; i++)
441 term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0));
443 for (i = 0; i < 8; i++)
444 term->TI_fg[i] = g_strdup_printf("\033[%dm", 30+ansitab[i]);
447 if (term->TI_setab) {
448 for (i = 0; i < term->TI_colors; i++) {
449 color = i < 16 ? ansitab[i] : i;
450 term->TI_bg[i] = g_strdup(tparm(term->TI_setab, color, 0));
452 } else if (term->TI_setb) {
453 for (i = 0; i < term->TI_colors; i++)
454 term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0));
456 for (i = 0; i < 8; i++)
457 term->TI_bg[i] = g_strdup_printf("\033[%dm", 40+ansitab[i]);
461 static void terminfo_input_init(TERM_REC *term)
463 tcgetattr(fileno(term->in), &term->old_tio);
464 memcpy(&term->tio, &term->old_tio, sizeof(term->tio));
466 term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */
467 term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */
468 term->tio.c_cc[VTIME] = 0; /* No timer */
470 /* Disable INTR, QUIT, VDSUSP and SUSP keys */
471 term->tio.c_cc[VINTR] = _POSIX_VDISABLE;
472 term->tio.c_cc[VQUIT] = _POSIX_VDISABLE;
474 term->tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
477 term->tio.c_cc[VSUSP] = _POSIX_VDISABLE;
480 tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
484 static void terminfo_input_deinit(TERM_REC *term)
486 tcsetattr(fileno(term->in), TCSADRAIN, &term->old_tio);
489 void terminfo_cont(TERM_REC *term)
492 tput(tparm(term->TI_smcup));
493 terminfo_input_init(term);
496 void terminfo_stop(TERM_REC *term)
499 terminfo_set_normal();
500 /* move cursor to bottom of the screen */
501 terminfo_move(0, term->height-1);
505 tput(tparm(term->TI_rmcup));
507 /* reset input settings */
508 terminfo_input_deinit(term);
512 static int term_setup(TERM_REC *term)
520 term_env = getenv("TERM");
521 if (term_env == NULL) {
522 fprintf(stderr, "TERM environment not set\n");
527 if (setupterm(term_env, 1, &err) != 0) {
528 fprintf(stderr, "setupterm() failed for TERM=%s: %d\n", term_env, err);
532 if (tgetent(term->buffer1, term_env) < 1)
534 fprintf(stderr, "Termcap not found for TERM=%s\n", term_env);
539 term_fill_capabilities(term);
541 /* Cursor movement */
543 term->move = _move_cup;
544 else if (term->TI_hpa && term->TI_vpa)
545 term->move = _move_pa;
547 fprintf(stderr, "Terminal doesn't support cursor movement\n");
550 term->move_relative = _move_relative;
551 term->set_cursor_visible = term->TI_civis && term->TI_cnorm ?
552 _set_cursor_visible : _ignore_parm;
555 if ((term->TI_csr || term->TI_wind) && term->TI_rin && term->TI_indn)
556 term->scroll = _scroll_region;
557 else if (term->TI_il && term->TI_dl)
558 term->scroll = _scroll_line;
559 else if ((term->TI_csr || term->TI_wind) && term->TI_ri && term->TI_ind)
560 term->scroll = _scroll_region_1;
561 else if (term->scroll == NULL && (term->TI_il1 && term->TI_dl1))
562 term->scroll = _scroll_line_1;
563 else if (term->scroll == NULL) {
564 fprintf(stderr, "Terminal doesn't support scrolling\n");
568 /* Clearing screen */
570 term->clear = _clear_screen;
571 else if (term->TI_ed)
572 term->clear = _clear_eos;
573 else if (term->TI_dl)
574 term->clear = _clear_del;
575 else if (term->TI_dl1)
576 term->clear = _clear_del_1;
578 /* we could do this by line inserts as well, but don't
579 bother - if some terminal has insert line it most probably
580 has delete line as well, if not a regular clear screen */
581 fprintf(stderr, "Terminal doesn't support clearing screen\n");
585 /* Clearing to end of line */
587 term->clrtoeol = _clrtoeol;
589 fprintf(stderr, "Terminal doesn't support clearing to end of line\n");
593 /* Repeating character */
595 term->repeat = _repeat;
597 term->repeat = _repeat_manual;
599 /* Bold, underline, standout */
600 term->set_blink = term->TI_blink ? _set_blink : _ignore;
601 term->set_bold = term->TI_bold ? _set_bold : _ignore;
602 term->set_uline = term->TI_smul && term->TI_rmul ?
603 _set_uline : _ignore_parm;
604 term->set_standout = term->TI_smso && term->TI_rmso ?
605 _set_standout : _ignore_parm;
607 /* Create a string to set all attributes off */
608 str = g_string_new(NULL);
610 g_string_append(str, term->TI_sgr0);
611 if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0))
612 g_string_append(str, term->TI_rmul);
613 if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0))
614 g_string_append(str, term->TI_rmso);
615 term->TI_normal = str->str;
616 g_string_free(str, FALSE);
617 term->set_normal = _set_normal;
619 term->beep = term->TI_bel ? _beep : _ignore;
621 terminfo_setup_colors(term, FALSE);
626 TERM_REC *terminfo_core_init(FILE *in, FILE *out)
628 TERM_REC *old_term, *term;
630 old_term = current_term;
631 current_term = term = g_new0(TERM_REC, 1);
636 if (!term_setup(term)) {
641 current_term = old_term;
645 void terminfo_core_deinit(TERM_REC *term)
649 old_term = current_term;
651 term->set_normal(term);
652 current_term = old_term;
656 g_free(term->TI_normal);
657 terminfo_colors_deinit(term);