5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
38 #include <sys/types.h>
42 #include <sys/ioctl.h>
43 #include <sys/select.h>
58 # include <unibilium.h>
67 /*----- Common support machinery ------------------------------------------*/
71 * Arguments: @const char *fmt@ = format control string
72 * @...@ = format arguemnts
76 * Use: Maybe report a debugging message to standard error.
79 static PRINTF_LIKE(1, 2) void debug(const char *fmt, ...)
84 p = getenv("MLIB_TTY_DEBUG");
85 if (p && *p != 'n' && *p != '0') {
87 fputs("mLib TTY: ", stderr);
88 vfprintf(stderr, fmt, ap);
94 static void common_init(struct tty *tty, FILE *fp)
96 static const struct baudtab { speed_t code; unsigned baud; } baudtab[] = {
98 ;;; The baud-rate table is very boring to type. To make life less
99 ;;; awful, put the rates in this list and evaluate the code to get Emacs
100 ;;; to regenerate it.
102 (let ((bauds '(50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600
103 19200 38400 57600 115200 230400 460800 500000 576000
104 921600 1000000 1152000 1500000 2000000 2500000 3000000
107 (goto-char (point-min))
108 (search-forward (concat "***" "BEGIN baudlist" "***"))
109 (beginning-of-line 2)
110 (delete-region (point)
112 (search-forward "***END***")
115 (dolist (baud (sort (copy-list bauds) #'<))
116 (insert (format "#ifdef B%d\n { B%d, %d },\n#endif\n"
119 /***BEGIN baudlist***/
187 { B1000000, 1000000 },
190 { B1152000, 1152000 },
193 { B1500000, 1500000 },
196 { B2000000, 2000000 },
199 { B2500000, 2500000 },
202 { B3000000, 3000000 },
205 { B3500000, 3500000 },
208 { B4000000, 4000000 },
214 const struct baudtab *b;
215 const char *p; int n;
220 if (!fp || tcgetattr(fileno(fp), &c))
223 code = cfgetospeed(&c);
224 for (b = baudtab; b->baud; b++)
225 if (b->code == code) { tty->baud = b->baud; goto found_baud; }
232 { p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
234 { p = getenv("LINES"); if (p) { n = atoi(p); if (n) tty->ht = n; } }
237 static void env_colour_caps(unsigned *caps_inout)
240 unsigned caps = *caps_inout;
242 p = getenv("FORCE_COLOR");
245 caps &= TTACF_CSPCMASK | TTACF_FG | TTACF_BG;
248 caps = (caps&~TTACF_CSPCMASK) | TTACF_FG | TTACF_BG |
249 TTACF_1BPC | TTACF_1BPCBR;
252 caps = (caps&~TTACF_CSPCMASK) | TTACF_FG | TTACF_BG |
253 TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_8LGS;
256 caps = (caps&~TTACF_CSPCMASK) | TTACF_FG | TTACF_BG |
257 TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
263 /* --- @clamp_colours@ --- *
265 * Arguments: @uint32 *space_out, *colour_out@ = selected space and colour
266 * @uint32 space, colour@ = requested space and colour
267 * @uint32 acaps@ = terminal's attribute capability mask
271 * Use: Select the best approximation to the requested colour which
272 * can be accommodated by the terminal.
275 /* #define DEBUG_CLAMP */
277 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
278 uint32 space, uint32 colour, uint32 acaps)
280 unsigned r, g, b, rr, gg, bb, y, t, u;
281 uint32 best_colour = 0, best_space; int best_error;
289 /* Check the colour space. If it's one that the terminal can handle, then
290 * return the colour unchanged. Otherwise, extract the channel components
296 /* No colour wanted at all. There's nothing to do here. */
298 *space_out = TTCSPC_NONE; *colour_out = 0;
302 /* One-bit-per-channel colour.
304 * There's no standardized mapping for these; indeed, they're commonly
305 * configurable by users. Since there are also `bright' versions of
306 * each, it's probably not right to just map zero to zero and one to
310 if (colour >= 8) goto inval;
311 if (acaps&(TTACF_1BPC | TTACF_1BPCBR))
312 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
314 #define C1BPC_FULL 0xcc
315 #define C1BPC_CVT(col, bit) \
316 (C1BPC_FULL&((((col)&(1 << (bit))) << (8 - (bit))) - 1))
317 r = C1BPC_CVT(colour, 0);
318 g = C1BPC_CVT(colour, 1);
319 b = C1BPC_CVT(colour, 2);
323 /* One-bit-per-channel colour, with global brightness. Again, there's
324 * no standardized mapping. Apply a boost across all three channels.
327 if (colour >= 16) goto inval;
328 if (!(colour&TT1BPC_BRI) && (acaps&(TTACF_1BPC | TTACF_1BPCBR)))
329 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
330 else if (acaps&TTACF_1BPCBR)
331 { *space_out = TTCSPC_1BPCBR; *colour_out = colour; return; }
333 #define C1BPC_BRIGHT 0x33
334 r = C1BPC_CVT(colour, 0) + C1BPC_BRIGHT;
335 g = C1BPC_CVT(colour, 1) + C1BPC_BRIGHT;
336 b = C1BPC_CVT(colour, 2) + C1BPC_BRIGHT;
341 /* Four-levels-per-channel colour. These are part of an `indexed'
342 * colour space which is theoretically controlled by applications but
343 * (a) that's rare, and (b) worrying about that won't do us any good.
345 * Each channel has four levels, but they're not assigned linearly.
348 if (colour >= 64) goto inval;
349 if (acaps&TTACF_4LPC)
350 { *space_out = TTCSPC_4LPC; *colour_out = colour; return; }
356 #define C4LPC_CVT(ch) (t = (ch), t == 0 ? C4LPC_L0 : \
357 t == 1 ? C4LPC_L1 : \
358 t == 2 ? C4LPC_L2 : \
360 r = C4LPC_CVT(TTCOL_2BR(colour));
361 g = C4LPC_CVT(TTCOL_2BG(colour));
362 b = C4LPC_CVT(TTCOL_2BB(colour));
367 /* Eight-levels greyscale. Again, these are part of an `indexed'
368 * colour space which is under application control, and, again, the
369 * levels aren't linear, but they're not far off linear.
371 if (colour >= 8) goto inval;
372 if (acaps&TTACF_8LGS)
373 { *space_out = TTCSPC_8LGS; *colour_out = colour; return; }
375 r = g = b = 255*(colour ? colour + 3 : 2)/11;
379 /* Six-levels-per-channel colour. Again, `indexed' colour space under
380 * application control. This time the mapping is essentially liner.
383 if (colour >= 216) goto inval;
384 if (acaps&TTACF_6LPC)
385 { *space_out = TTCSPC_6LPC; *colour_out = colour; return; }
387 #define C6LPC_CVT(ch) (t = (ch), t ? (40*t + 55) : 0)
388 r = C6LPC_CVT(TTCOL_6LR(colour));
389 g = C6LPC_CVT(TTCOL_6LG(colour));
390 b = C6LPC_CVT(TTCOL_6LB(colour));
395 /* Twenty-four-levels greyscale. Same story. */
397 if (colour >= 24) goto inval;
398 if (acaps&TTACF_24LGS)
399 { *space_out = TTCSPC_24LGS; *colour_out = colour; return; }
401 r = g = b = 10*colour + 8;
405 /* Eight-bits-per-channel colour. No conversion to apply here. */
407 if (colour >= 0x01000000) goto inval;
408 if (acaps&TTACF_8BPC)
409 { *space_out = TTCSPC_8BPC; *colour_out = colour; return; }
411 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
420 /* We didn't get an exact match, so we'll have to make do with what we've
423 best_error = -1; best_space = TTCSPC_NONE; best_colour = 0;
424 D( fprintf(stderr, "\n;; APPROX space %u, colour 0x%lx = %u/%u/%u\n",
425 space, (unsigned long)colour, r, g, b); )
427 /* Approximate colour weightings for human colour vision. */
431 #define TOTWT (RWT + GWT + BWT)
433 /* Determine the optimal grey approximation for this colour. */
434 y = (RWT*r + GWT*g + BWT*b + TOTWT/2)/TOTWT;
436 #define TRY_APPROX(rr, gg, bb, spc, clr) do { \
437 /* If the approximation (RR, GG, BB) is closer to the current best \
438 * then accept SPC and CLR as the new best space/colour option. \
441 int _r_err = (rr) - r, _g_err = (gg) - g, _b_err = (bb) - b; \
444 if (_r_err < 0) _r_err = -_r_err; \
445 if (_g_err < 0) _g_err = -_g_err; \
446 if (_b_err < 0) _b_err = -_b_err; \
448 _err = RWT*_r_err + GWT*_g_err + BWT*_b_err; \
450 ";; candidate space %u, colour 0x%lx = %u/%u/%u; " \
452 (spc), (unsigned long)(clr), (rr), (gg), (bb), _err); ) \
453 if (best_error < 0 || _err < best_error) { \
454 best_error = _err; best_space = (spc); best_colour = (clr); \
455 D( fprintf(stderr, ";;\tNEW BEST APPROXIMATION\n"); ) \
459 if (!(acaps&(TTACF_4LPC | TTACF_6LPC | TTACF_8BPC))) {
460 /* The one-bit-per-channel colours are very variable, but there's little
461 * choice, so we'll have to try. We assume the same mapping as on the
465 if (acaps&(TTACF_1BPC | TTACF_1BPCBR)) {
466 /* One-bit-per-channel colour. */
468 #define C1BPC_APPROX(cc, c, bit) do { \
469 if ((c) <= C1BPC_FULL/2) (cc) = 0; \
470 else { (cc) = C1BPC_FULL; t |= (bit); } \
473 C1BPC_APPROX(rr, r, TT1BPC_RED);
474 C1BPC_APPROX(gg, g, TT1BPC_GRN);
475 C1BPC_APPROX(bb, b, TT1BPC_BLU);
477 TRY_APPROX(rr, gg, bb, TTCSPC_1BPC, t);
480 if (acaps&TTACF_1BPCBR) {
481 /* One-bit-per-channel colour, with global brightness. */
483 #define C1BPCBR_APPROX(cc, c, bit) do { \
484 if ((c) <= C1BPC_FULL/2 + C1BPC_BRIGHT) (cc) = C1BPC_BRIGHT; \
485 else { (cc) = 255; t |= (bit); } \
488 C1BPCBR_APPROX(rr, r, TT1BPC_RED);
489 C1BPCBR_APPROX(gg, g, TT1BPC_GRN);
490 C1BPCBR_APPROX(bb, b, TT1BPC_BLU);
491 #undef C1BPCBR_APPROX
492 TRY_APPROX(rr, gg, bb, TTCSPC_1BPCBR, t);
496 if (acaps&TTACF_4LPC) {
497 /* Four-levels-per-channel colour. */
499 #define C4LPC_APPROX(cc, c, sh) do { \
502 if (_c > (C4LPC_L2 + C4LPC_L3)/2) \
503 { (cc) = C4LPC_L3; t |= 3 << (sh); } \
504 else if (_c > (C4LPC_L1 + C4LPC_L2)/2) \
505 { (cc) = C4LPC_L2; t |= 2 << (sh); } \
506 else if (_c > (C4LPC_L0 + C4LPC_L1)/2) \
507 { (cc) = C4LPC_L1; t |= 1 << (sh); } \
512 C4LPC_APPROX(rr, r, 4);
513 C4LPC_APPROX(gg, g, 2);
514 C4LPC_APPROX(bb, b, 0);
516 TRY_APPROX(rr, gg, bb, TTCSPC_4LPC, t);
519 if (acaps&TTACF_8LGS) {
520 /* Eight-levels greyscale. */
523 if (u <= 2) { u = 2; t = 0; }
524 else if (u == 3) { u = 4; t = 1; }
525 else if (u == 11) { u = 10; t = 7; }
527 u = (255*u)/11; TRY_APPROX(u, u, u, TTCSPC_8LGS, t);
530 if (acaps&TTACF_6LPC) {
531 /* Six-levels-per-channel colour. */
533 #define C6LPC_APPROX(cc, c, f) do { \
536 if (_c < 36) (cc) = 0; \
537 else { u = (_c - 36)/40; t += (f)*u; (cc) = 40*u + 55; } \
540 C6LPC_APPROX(rr, r, 36);
541 C6LPC_APPROX(gg, g, 6);
542 C6LPC_APPROX(bb, b, 1);
544 TRY_APPROX(rr, gg, bb, TTCSPC_6LPC, t);
547 if (acaps&TTACF_24LGS) {
548 /* Twenty-four-levels greyscale. */
550 if (y < 3) { t = 0; u = 8; }
551 else if (y >= 243) { t = 23; u = 238; }
552 else { t = (y - 3)/10; u = 10*t + 8; }
553 TRY_APPROX(u, u, u, TTCSPC_24LGS, t);
556 if (acaps&TTACF_8BPC) {
557 /* Eight-bits-per-channel colour. */
560 D( fprintf(stderr, ";; accept exact 8bpc colour\n"); )
561 best_error = 0; best_space = TTCSPC_8BPC;
562 best_colour = TTCOL_MK8B(r, g, b);
567 *space_out = best_space; *colour_out = best_colour;
571 /* Invalid colour selection. Ignore this. */
572 *space_out = TTCSPC_NONE; *colour_out = 0;
597 /* --- @tty_clampattr@ --- *
599 * Arguments: @struct tty_attr *a_out@ = selected attributes
600 * @const struct tty_attr *a@ = requested attributes
601 * @uint32 acaps@ = terminal's attribute capability mask
605 * Use: Select the closest approximation to the requested attributes
606 * which can be accommodated by the terminal.
609 void tty_clampattr(struct tty_attr *a_out,
610 const struct tty_attr *a, uint32 acaps)
612 uint32 ff = 0, f = a ? a->f : 0, t;
614 /* Line attributes. */
615 t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
620 if (!acaps&TTACF_ULINE) t = TTLN_NONE;
623 if (acaps&TTACF_UULINE) ;
624 else if (acaps&TTACF_ULINE) t = TTLN_ULINE;
630 ff |= t << TTAF_LNSHIFT;
633 t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
635 case TTWT_MED: break;
636 case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
637 case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
638 default: t = TTWT_MED; break;
640 ff |= t << TTAF_WTSHIFT;
642 /* Other text attributes. */
643 if (acaps&TTACF_STRIKE) ff |= f&TTAF_STRIKE;
644 if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
645 if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
647 /* Foreground and background colours. */
648 if (acaps&TTACF_FG) {
649 clamp_colours(&t, &a_out->fg,
650 (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a ? a->fg : 0,
652 ff |= t << TTAF_FGSPCSHIFT;
654 if (acaps&TTACF_BG) {
655 clamp_colours(&t, &a_out->bg,
656 (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a ? a->bg : 0,
658 ff |= t << TTAF_BGSPCSHIFT;
662 a_out->f = ff; a_out->_res0 = 0;
665 int tty_resized(struct tty *tty)
669 if (!tty->fpout) { errno = ENOTTY; return (-1); }
670 if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
671 tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (0);
674 /*----- Common machinery for `termcap' and `terminfo' ---------------------*/
676 #if defined(HAVE_TERMINFO) || \
677 defined(HAVE_TERMCAP) || \
678 defined(HAVE_UNIBILIUM)
680 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
682 static const struct gprintf_ops *global_gops;
683 static void *global_gout;
684 static struct tty *global_lock = 0;
686 static void caps_release(struct tty *tty)
687 { assert(global_lock == tty); global_lock = 0; }
689 static int caps_putch(int ch)
690 { return (global_gops->putch(global_gout, ch)); }
694 #ifdef HAVE_UNIBILIUM
695 # define UNIBI_(x) unibi##x
700 #define BASICCAPS(_bool, _int, _str) \
701 _str(_repeat_char, rep, rp) \
702 _int(_padding_baud_rate, pb, pb) _str(_pad_char, pad, pc) \
703 _bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo) \
704 _bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
706 #define ATTRCAPS(_bool, _int, _str) \
707 _str(_exit_attribute_mode, sgr0, me) \
708 _str(_enter_underline_mode, smul, us) \
709 _str(_exit_underline_mode, rmul, ue) \
710 _str(_enter_italics_mode, sitm, ZH) _str(_exit_italics_mode, ritm, ZR) \
711 _str(_enter_bold_mode, bold, md) _str(_enter_dim_mode, dim, mh) \
712 _str(_enter_reverse_mode, rev, mr) \
713 COLOURCAPS(_bool, _int, _str)
715 #define COLOURCAPS(_bool, _int, _str) \
716 _str(_set_a_foreground, setaf, AF) _str(_set_a_background, setab, AB) \
717 _str(_orig_pair, op, op) _int(_max_colors, colors, Co)
719 #define MODECAPS(_bool, _int, _str) \
720 _str(_carriage_return, cr, cr) _str(_newline, nel, nw) \
721 _str(_enter_am_mode, smam, SA) _str(_exit_am_mode, rmam, RA) \
722 _str(_enter_ca_mode, smcup, ti) _str(_exit_ca_mode, rmcup, te) \
723 _str(_cursor_normal, cnorm, vs) _str(_cursor_invisible, civis, vi) \
724 _str(_enter_insert_mode, smir, im) _str(_exit_insert_mode, rmir, ei) \
725 _str(_enter_delete_mode, smdc, dm) _str(_exit_delete_mode, rmdc, ed)
727 #define MOVECAPS(_bool, _int, _str) \
728 _str(_cursor_home, home, ho) \
729 _str(_cursor_address, cup, cm) \
730 _str(_row_address, vpa, cv) _str(_column_address, hpa, ch) \
731 _str(_cursor_left, cub1, le) _str(_parm_left_cursor, cub, LE) \
732 _str(_cursor_right, cuf1, nd) _str(_parm_right_cursor, cuf, RI) \
733 _str(_cursor_up, cuu1, up) _str(_parm_up_cursor, cuu, UP) \
734 _str(_cursor_down, cud1, do) _str(_parm_down_cursor, cud, DO)
736 #define SCROLLCAPS(_bool, _int, _str) \
737 _str(_change_scroll_region, csr, cs) \
738 _str(_scroll_forward, ind, sf) _str(_parm_index, indn, SF) \
739 _str(_scroll_reverse, ri, sr) _str(_parm_rindex, rin, SR)
741 #define ERASECAPS(_bool, _int, _str) \
742 _str(_erase_chars, ech, ec) \
743 _str(_clr_bol, el1, cb) _str(_clr_eol, el, ce) \
744 _str(_clr_eos, ed, cd) _str(_clear_screen, clear, cl)
746 #define INSDELCAPS(_bool, _int, _str) \
747 _str(_insert_character, ich1, ic) _str(_parm_ich, ich, IC) \
748 _str(_insert_padding, ip, ip) \
749 _str(_insert_line, il1, al) _str(_parm_insert_line, il, AL) \
750 _str(_delete_character, dch1, dc) _str(_parm_dch, dch, DC) \
751 _str(_delete_line, dl1, dl) _str(_parm_delete_line, dl, DL)
753 #define STORECAPS(_bool, _int, _str) \
754 BASICCAPS(_bool, _int, _str) \
755 ATTRCAPS(_bool, _int, _str) \
756 MODECAPS(_bool, _int, _str) \
757 MOVECAPS(_bool, _int, _str) \
758 SCROLLCAPS(_bool, _int, _str) \
759 ERASECAPS(_bool, _int, _str) \
760 INSDELCAPS(_bool, _int, _str)
762 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
764 #define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
765 #define CAP_BCE CAPREF(_back_color_erase, bce, ut)
766 #define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
767 #define CAP_XVPA CAPREF(_col_addr_glitch, xhpa, YA)
768 #define CAP_AM CAPREF(_auto_right_margin, am, am)
769 #define CAP_XENL CAPREF(_eat_newline_glitch, xenl, xn)
770 #define CAP_HT CAPREF(_lines, lines, li)
771 #define CAP_WD CAPREF(_columns, cols, co)
773 #define TTY_BASEOPSPFX struct tty_ops tty
774 #define TTY_BASEOPSUXFX struct tty_ops tty
775 #define TTY_BASEPFX struct tty tty
776 #define TTY_BASEUSFX struct tty tty
778 struct tty_capopslots {
779 int (*boolcap)(struct tty */*tty*/,
780 int /*uix*/, const char */*info*/, const char */*cap*/);
781 int (*intcap)(struct tty */*tty*/,
782 int /*uix*/, const char */*info*/, const char */*cap*/);
783 const char *(*strcap)(struct tty */*tty*/,
784 int /*uix*/, const char */*info*/,
785 const char */*cap*/);
786 int (*put0)(struct tty */*tty*/,
787 const struct gprintf_ops */*gops*/, void */*go*/,
788 unsigned /*npad*/, const char */*cap*/);
789 int (*put1i)(struct tty */*tty*/,
790 const struct gprintf_ops */*gops*/, void */*go*/,
791 unsigned /*npad*/, const char */*cap*/, int /*i0*/);
792 int (*put2i)(struct tty */*tty*/,
793 const struct gprintf_ops */*gops*/, void */*go*/,
795 const char */*cap*/, int /*i0*/, int /*i1*/);
797 #define TTY_CAPOPSPFX TTY_BASEOPSPFX; struct tty_capopslots cap
798 struct tty_capops { TTY_CAPOPSPFX; };
799 #define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
800 union tty_capopsu { TTY_CAPOPSUSFX; };
802 struct tty_capslots {
803 #define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
804 #define DEF_INTCAP(uix, info, cap) int info;
805 #define DEF_STRCAP(uix, info, cap) const char *info;
806 STORECAPS(DEF_BOOLCAP, DEF_INTCAP, DEF_STRCAP)
811 #define TTY_CAPSPFX \
813 struct tty_capslots cap
814 struct tty_caps { TTY_CAPSPFX; };
815 #define TTY_CAPSUSFX \
816 struct tty_caps cap; \
818 union tty_capsu { TTY_CAPSUSFX; };
820 static void init_caps(struct tty_caps *t)
822 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
825 t->tty.acaps = t->tty.ocaps = 0;
826 t->tty.st.modes = 0; t->tty.st.attr.f = 0;
827 t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
829 /* Inhale all of the interesting terminal capabilities. */
830 #define GETBOOL(var, info, cap_) \
831 t->cap.info = ops->cap.boolcap(&t->tty, CAPREF(var, info, cap_));
832 #define GETINT(var, info, cap_) \
833 t->cap.info = ops->cap.intcap(&t->tty, CAPREF(var, info, cap_));
834 #define GETSTR(var, info, cap_) \
835 t->cap.info = ops->cap.strcap(&t->tty, CAPREF(var, info, cap_));
836 STORECAPS(GETBOOL, GETINT, GETSTR)
841 #define CLEAR_BOOL(uix, info, cap_) t->cap.info = 0;
842 #define CLEAR_INT(uix, info, cap_) t->cap.info = 0;
843 #define CLEAR_STR(uix, info, cap_) t->cap.info = 0;
844 #define CLEAR_CAPS(caplist) \
845 do { caplist(CLEAR_BOOL, CLEAR_INT, CLEAR_STR) } while (0)
847 /* Basic capabilities. */
848 if (!t->cap.cr) t->cap.cr = "\r";
849 if (!t->cap.nel) t->cap.nel = "\r\n";
851 /* Attribute capabilities. */
852 if (ops->cap.intcap(&t->tty, CAP_XMC) > 0 || !t->cap.sgr0)
853 CLEAR_CAPS(ATTRCAPS);
855 if (t->cap.smul) t->tty.acaps |= TTACF_ULINE;
856 if (t->cap.bold) t->tty.acaps |= TTACF_BOLD;
857 if (t->cap.dim) t->tty.acaps |= TTACF_DIM;
858 if (t->cap.sitm) t->tty.acaps |= TTACF_ITAL;
859 if (t->cap.rev) t->tty.acaps |= TTACF_INVV;
861 if (!t->cap.op || (!t->cap.setaf && !t->cap.setab))
862 CLEAR_CAPS(COLOURCAPS);
864 if (t->cap.setaf) t->tty.acaps |= TTACF_FG;
865 if (t->cap.setab) t->tty.acaps |= TTACF_BG;
866 t->tty.acaps |= TTACF_1BPC;
867 if (t->cap.colors >= 16777216)
868 t->tty.acaps |= TTACF_1BPC | TTACF_8BPC;
869 else if (t->cap.colors >= 256)
870 t->tty.acaps |= TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
871 else if (t->cap.colors == 88)
872 t->tty.acaps |= TTACF_1BPCBR | TTACF_4LPC | TTACF_8LGS;
873 else if (t->cap.colors >= 16)
874 t->tty.acaps |= TTACF_1BPCBR;
875 if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
876 env_colour_caps(&t->tty.acaps);
880 /* Motion capabilities. */
881 if (ops->cap.boolcap(&t->tty, CAP_XHPA)) t->cap.hpa = 0;
882 if (ops->cap.boolcap(&t->tty, CAP_XVPA)) t->cap.vpa = 0;
883 if (!t->cap.cub1) t->cap.cub1 = "\b";
884 if ((t->cap.cuf || t->cap.cuf1) &&
885 (t->cap.cuu || t->cap.cuu1) &&
886 (t->cap.cud || t->cap.cud1)) {
887 t->tty.ocaps |= TTCF_RELMV;
888 if (t->cap.vpa) t->tty.ocaps |= TTCF_ABSMV | TTCF_MIXMV;
889 else if (t->cap.home) t->tty.ocaps |= TTCF_ABSMV;
890 } else if (t->cap.cup ||
891 (t->cap.hpa && t->cap.vpa) ||
892 (t->cap.home && (t->cap.cuf || t->cap.cuf1)))
893 t->tty.ocaps |= TTCF_ABSMV;
895 /* Mode capabilities. */
896 if (t->cap.smam && t->cap.rmam) t->tty.ocaps |= TTMF_AUTOM;
897 if (t->cap.smcup && t->cap.rmcup) t->tty.ocaps |= TTMF_FSCRN;
898 if (t->cap.smir && t->cap.rmir) t->tty.ocaps |= TTMF_INS;
899 if (t->cap.smdc && t->cap.rmdc) t->tty.ocaps |= TTMF_DEL;
900 if (t->cap.cnorm && t->cap.civis)
901 { t->tty.ocaps |= TTMF_CVIS; t->tty.st.modes |= TTMF_CVIS; }
902 if (ops->cap.boolcap(&t->tty, CAP_AM)) {
903 t->tty.st.modes |= TTMF_AUTOM;
904 if (ops->cap.boolcap(&t->tty, CAP_XENL)) t->tty.ocaps |= TTCF_MMARG;
908 if (t->cap.ech) t->tty.ocaps |= TTCF_ERCH;
909 if (t->cap.el1) t->tty.ocaps |= TTCF_ERBOL;
910 if (t->cap.el) t->tty.ocaps |= TTCF_EREOL;
911 if (t->cap.ed) t->tty.ocaps |= TTCF_EREOD;
912 if (t->cap.clear || (t->cap.ed && t->cap.home)) t->tty.ocaps |= TTCF_ERDSP;
914 /* Insertion and deletion. */
915 if (t->cap.ich || t->cap.ich1) t->tty.ocaps |= TTCF_INSCH;
916 if (t->cap.il || t->cap.il1) t->tty.ocaps |= TTCF_INSLN;
917 if (t->cap.dch || t->cap.dch1) t->tty.ocaps |= TTCF_DELCH;
918 if (t->cap.dl || t->cap.dl1) t->tty.ocaps |= TTCF_DELLN;
922 { wd = ops->cap.intcap(&t->tty, CAP_WD); if (wd > 0) t->tty.wd = wd; }
924 { ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
927 #define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
929 #define PUT0V(npad, cap_) \
930 CHECK(ops->cap.put0(&t->tty, gops, go, (npad), (cap_)))
931 #define PUT1IV(npad, cap_, i0) \
932 CHECK(ops->cap.put1i(&t->tty, gops, go, (npad), (cap_), (i0)))
933 #define PUT2IV(npad, cap_, i0, i1) \
934 CHECK(ops->cap.put2i(&t->tty, gops, go, (npad), (cap_), (i0), (i1)))
936 #define PUT0(npad, name) PUT0V(npad, t->cap.name)
937 #define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
938 #define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
940 static int caps_setcolour(struct tty_caps *t,
941 const struct gprintf_ops *gops, void *go,
942 const char *cap, uint32 spc, uint32 clr)
944 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
948 case TTCSPC_1BPC: case TTCSPC_1BPCBR: PUT1IV(0, cap, clr); break;
949 case TTCSPC_4LPC: case TTCSPC_6LPC: PUT1IV(0, cap, clr + 16); break;
950 case TTCSPC_8LGS: PUT1IV(0, cap, clr + 80); break;
951 case TTCSPC_24LGS: PUT1IV(0, cap, clr + 232); break;
954 /* There's an unfortunate ambiguity in the `setaf' conventions. The
955 * first eight colours should be dark shades of blue, but in fact
956 * they're interpreted as the one-bit-per-channel basic colours by
957 * common `terminfo' settings. Notice and kludge by adding a little
958 * red. This will tinge the colour magenta, but all such colours are
959 * so dark as to be effectively black anyway, so I doubt that this will
962 if (spc == TTCSPC_8BPC && clr < 8) clr += 65536;
963 PUT1IV(0, cap, clr); break;
965 /* case TTCSPC_NONE: */
966 default: rc = -1; goto end;
973 static int caps_setattr(struct tty *tty,
974 const struct gprintf_ops *gops, void *go,
975 const struct tty_attr *a)
977 struct tty_caps *t = (struct tty_caps *)tty;
978 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
983 /* Work out what needs doing. */
984 tty_clampattr(&aa, a, t->tty.acaps);
985 diff = aa.f ^ t->tty.st.attr.f;
987 /* Some terminals might not be able to clear individual attributes that
988 * they can set, and some capabilities for turning attributes on don't even
989 * have a corresponding attribute for turning them off again individually,
990 * so we have to use `sgr0' to start from scratch. Of course, if we need
991 * to do that, we need to restore the other active attributes, so we must
994 if (((diff&TTAF_LNMASK) && !(aa.f&TTAF_LNMASK) && !t->cap.rmul) ||
995 ((diff&TTAF_WTMASK) && !(aa.f&TTAF_WTMASK)) ||
996 ((diff&~aa.f&TTAF_ITAL) && !t->cap.ritm) ||
997 (diff&~aa.f&TTAF_INVV))
998 { PUT0(0, sgr0); diff = aa.f; }
1001 if (diff&TTAF_LNMASK)
1002 switch ((aa.f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1003 case TTLN_NONE: PUT0(0, rmul); break;
1004 case TTLN_ULINE: PUT0(0, smul); break;
1005 /* case TTLN_UULINE: */
1006 /* case TTLN_STRIKE: */
1007 default: rc = -1; goto end;
1011 if (diff&TTAF_WTMASK)
1012 switch ((aa.f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1013 /* case TTWT_MED: */
1014 case TTWT_BOLD: PUT0(0, bold); break;
1015 case TTWT_DIM: PUT0(0, dim); break;
1016 default: rc = -1; goto end;
1019 /* Other text effects. */
1020 if (diff&TTAF_ITAL) {
1021 if (aa.f&TTAF_ITAL) PUT0(0, sitm);
1024 if (diff&aa.f&TTAF_INVV) PUT0(0, rev);
1027 if (((diff&TTAF_FGSPCMASK) && !(aa.f&TTAF_FGSPCMASK)) ||
1028 ((diff&TTAF_BGSPCMASK) && !(aa.f&TTAF_BGSPCMASK))) {
1029 /* There's no capability string for resetting just the foreground
1030 * or background colours to the defaults, so deal with that here.
1034 diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
1035 (aa.f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
1037 if ((diff&TTAF_FGSPCMASK) || aa.fg != t->tty.st.attr.fg)
1038 CHECK(caps_setcolour(t, gops, go, t->cap.setaf,
1039 (aa.f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, aa.fg));
1040 if ((diff&TTAF_BGSPCMASK) || aa.bg != t->tty.st.attr.bg)
1041 CHECK(caps_setcolour(t, gops, go, t->cap.setab,
1042 (aa.f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, aa.bg));
1047 t->tty.st.attr = aa; return (rc);
1050 static int caps_setmodes(struct tty *tty,
1051 const struct gprintf_ops *gops, void *go,
1052 uint32 modes_bic, uint32 modes_xor)
1054 struct tty_caps *t = (struct tty_caps *)tty;
1055 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1060 /* Figure out which modes to set. */
1061 modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
1062 diff = modes ^ t->tty.st.modes;
1064 /* Automatic margins. */
1065 if (diff&TTMF_AUTOM) {
1066 if (modes&TTMF_AUTOM) PUT0(0, smam);
1071 if (diff&TTMF_FSCRN) {
1072 if (modes&TTMF_FSCRN) PUT0(0, smcup);
1073 else PUT0(0, rmcup);
1076 /* Cursor visibility. */
1077 if (diff&TTMF_CVIS) {
1078 if (modes&TTMF_CVIS) PUT0(0, civis);
1079 else PUT0(0, cnorm);
1083 if (diff&TTMF_INS) {
1084 cap = modes&TTMF_INS ? t->cap.smir : t->cap.rmir;
1085 if (cap) PUT0V(0, cap);
1086 else if (!t->cap.ich) { rc = -1; goto end; }
1089 /* Delete characters. */
1090 if (diff&TTMF_DEL) {
1091 cap = modes&TTMF_DEL ? t->cap.smdc : t->cap.rmdc;
1092 if (cap) PUT0V(0, cap);
1093 else if (!t->cap.dch && !t->cap.dch1) { rc = -1; goto end; }
1099 t->tty.st.modes = modes; return (rc);
1102 #define CIF_PADMUL 1u
1104 static int caps_iterate(struct tty_caps *t,
1105 const struct gprintf_ops *gops, void *go,
1106 const char *cap1, const char *capn,
1107 unsigned f, unsigned npad, unsigned n)
1109 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1113 if (cap1 && (n == 1 || !capn))
1114 while (n--) PUT0V(npad, cap1);
1116 max = npad && (f&CIF_PADMUL) ? INT_MAX/npad : INT_MAX;
1118 nn = n; if (nn > max) nn = max;
1119 PUT1IV(npad, capn, nn);
1128 static int caps_move_relative(struct tty_caps *t,
1129 const struct gprintf_ops *gops, void *go,
1131 const char *fw1, const char *fwn,
1132 const char *rv1, const char *rvn)
1134 const char *mv1, *mvn;
1136 if (!delta) return (0);
1137 else if (delta > 0) { mv1 = fw1; mvn = fwn; }
1138 else { mv1 = rv1; mvn = rvn; delta = - delta; }
1139 return (caps_iterate(t, gops, go, mv1, mvn, 0, 0, delta));
1142 static int caps_move(struct tty *tty,
1143 const struct gprintf_ops *gops, void *go,
1144 unsigned orig, int y, int x)
1146 struct tty_caps *t = (struct tty_caps *)tty;
1147 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1151 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
1154 if (!t->cap.msgr && a.f) { PUT0(0, sgr0); t->tty.st.attr.f = 0; }
1158 if (t->cap.home && !x && !y)
1160 else if (t->cap.cup)
1161 PUT2I(1, cup, y, x);
1162 else if (t->cap.vpa) {
1168 CHECK(caps_move_relative(t, gops, go, x,
1169 t->cap.cuf1, t->cap.cuf,
1170 t->cap.cub1, t->cap.cub));
1172 } else if (t->cap.home) {
1174 CHECK(caps_iterate(t, gops, go, t->cap.cud1, t->cap.cud, 0, 1, y));
1175 CHECK(caps_iterate(t, gops, go, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1177 { rc = -1; goto end; }
1181 CHECK(caps_move_relative(t, gops, go, y,
1182 t->cap.cud1, t->cap.cud,
1183 t->cap.cuu1, t->cap.cuu));
1184 CHECK(caps_move_relative(t, gops, go, x,
1185 t->cap.cuf1, t->cap.cuf,
1186 t->cap.cub1, t->cap.cub));
1189 case TTOF_XHOME | TTOF_YCUR:
1190 if (x == 0 && y == 1)
1193 CHECK(caps_move_relative(t, gops, go, y,
1194 t->cap.cud1, t->cap.cud,
1195 t->cap.cuu1, t->cap.cuu));
1196 if (t->cap.hpa && x)
1200 CHECK(caps_iterate(t, gops, go, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1205 case TTOF_XCUR | TTOF_YHOME:
1207 CHECK(caps_move_relative(t, gops, go, x,
1208 t->cap.cuf1, t->cap.cuf,
1209 t->cap.cub1, t->cap.cub));
1217 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
1219 if (!t->cap.msgr && t->tty.st.attr.f)
1220 CHECK(caps_setattr(&t->tty, gops, go, &a));
1227 static int caps_repeat(struct tty *tty,
1228 const struct gprintf_ops *gops, void *go,
1231 struct tty_caps *t = (struct tty_caps *)tty;
1232 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1237 while (n--) CHECK(gops->putch(go, ch));
1241 nn = n; if (nn > INT_MAX) nn = INT_MAX;
1242 PUT2I((nn + wd - 1)/wd, rep, ch, nn);
1251 static int caps_erase(struct tty *tty,
1252 const struct gprintf_ops *gops, void *go,
1255 struct tty_caps *t = (struct tty_caps *)tty;
1256 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1260 switch (f&(TTEF_BEGIN | TTEF_END)) {
1263 case TTEF_BEGIN | TTEF_END:
1264 if (t->cap.clear) PUT0(t->tty.ht, clear);
1265 else { PUT0(1, home); PUT0(t->tty.ht, ed); }
1268 PUT0(t->tty.ht, ed);
1275 if (f&TTEF_BEGIN) PUT0(1, el1);
1276 if (f&TTEF_END) PUT0(1, el);
1283 static int caps_erch(struct tty *tty,
1284 const struct gprintf_ops *gops, void *go,
1287 struct tty_caps *t = (struct tty_caps *)tty;
1288 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1291 if (n) PUT1I(1, ech, n);
1297 static int caps_ins(struct tty *tty,
1298 const struct gprintf_ops *gops, void *go,
1299 unsigned f, unsigned n)
1301 struct tty_caps *t = (struct tty_caps *)tty;
1305 CHECK(caps_iterate(t, gops, go,
1306 t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
1308 CHECK(caps_iterate(t, gops, go,
1309 t->cap.ich1, t->cap.ich, 0, 1, n));
1315 static int caps_inch(struct tty *tty,
1316 const struct gprintf_ops *gops, void *go,
1319 struct tty_caps *t = (struct tty_caps *)tty;
1320 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1323 if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
1324 { rc = -1; goto end; }
1325 if (t->cap.ich) PUT0(1, ich);
1326 CHECK(gops->putch(go, ch));
1327 if (t->cap.ip) PUT0(1, ip);
1333 static int caps_del(struct tty *tty,
1334 const struct gprintf_ops *gops, void *go,
1335 unsigned f, unsigned n)
1337 struct tty_caps *t = (struct tty_caps *)tty;
1342 CHECK(caps_iterate(t, gops, go,
1343 t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
1345 CHECK(caps_iterate(t, gops, go,
1346 t->cap.dch1, t->cap.dch, 0, 1, n));
1361 #define TTY_CAPOPS \
1362 caps_setattr, caps_setmodes, \
1363 caps_move, caps_repeat, \
1364 caps_erase, caps_erch, caps_ins, caps_inch, caps_del, \
1369 /*----- Termcap -----------------------------------------------------------*/
1373 struct tty_termcapslots {
1374 char termbuf[4096], capbuf[4096], *capcur;
1376 struct tty_termcap { TTY_CAPSPFX; struct tty_termcapslots tc; };
1377 union tty_termcapu { struct tty_termcap tc; TTY_CAPSUSFX; };
1379 static int termcap_boolcap(struct tty *tty,
1380 int uix, const char *info, const char *cap)
1384 p = tgetflag(cap); assert(p >= 0);
1388 static int termcap_intcap(struct tty *tty,
1389 int uix, const char *info, const char *cap)
1393 n = tgetnum(cap); assert(n >= -1);
1397 static const char *termcap_strcap(struct tty *tty,
1398 int uix, const char *info,
1401 struct tty_termcap *t = (struct tty_termcap *)tty;
1404 p = tgetstr(cap, &t->tc.capcur); assert(p != (const char *)-1);
1408 static int termcap_put0(struct tty *tty,
1409 const struct gprintf_ops *gops, void *go,
1410 unsigned npad, const char *cap)
1412 if (!cap) return (-1);
1413 global_gops = gops; global_gout = go;
1414 return (tputs(cap, npad, caps_putch));
1417 static int termcap_put1i(struct tty *tty,
1418 const struct gprintf_ops *gops, void *go,
1419 unsigned npad, const char *cap, int i0)
1421 if (!cap) return (-1);
1422 global_gops = gops; global_gout = go;
1423 return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
1426 static int termcap_put2i(struct tty *tty,
1427 const struct gprintf_ops *gops, void *go,
1429 const char *cap, int i0, int i1)
1431 if (!cap) return (-1);
1432 global_gops = gops; global_gout = go;
1433 return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
1436 static const union tty_capopsu termcap_ops = { {
1437 { caps_release, TTY_CAPOPS },
1438 { termcap_boolcap, termcap_intcap, termcap_strcap,
1439 termcap_put0, termcap_put1i, termcap_put2i }
1442 static struct tty *termcap_init(FILE *fp)
1444 union tty_termcapu *u = 0; struct tty *ret = 0;
1448 { debug("termcap/terminfo terminal already open"); goto end; }
1449 term = getenv("TERM"); if (!term) goto end;
1451 if (tgetent(u->tc.tc.termbuf, term) < 1) goto end;
1452 u->tc.tc.capcur = u->tc.tc.capbuf;
1453 u->tty.ops = &termcap_ops.tty;
1454 common_init(&u->tty, fp);
1456 ret = &u->tty; u = 0;
1458 xfree(u); global_lock = ret; return (ret);
1463 /*----- Terminfo ----------------------------------------------------------*/
1465 #ifdef HAVE_TERMINFO
1467 static int terminfo_boolcap(struct tty *tty,
1468 int uix, const char *info, const char *cap)
1472 p = tigetflag(info); assert(p >= 0);
1476 static int terminfo_intcap(struct tty *tty,
1477 int uix, const char *info, const char *cap)
1481 n = tigetnum(info); assert(n >= -1);
1485 static const char *terminfo_strcap(struct tty *tty,
1486 int uix, const char *info,
1491 p = tigetstr(info); assert(p != (const char *)-1);
1495 static int terminfo_put0(struct tty *tty,
1496 const struct gprintf_ops *gops, void *go,
1497 unsigned npad, const char *cap)
1499 if (!cap) return (-1);
1500 global_gops = gops; global_gout = go;
1501 return (tputs(cap, npad, caps_putch));
1504 static int terminfo_put1i(struct tty *tty,
1505 const struct gprintf_ops *gops, void *go,
1506 unsigned npad, const char *cap, int i0)
1508 if (!cap) return (-1);
1509 global_gops = gops; global_gout = go;
1510 return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
1513 static int terminfo_put2i(struct tty *tty,
1514 const struct gprintf_ops *gops, void *go,
1516 const char *cap, int i0, int i1)
1518 if (!cap) return (-1);
1519 global_gops = gops; global_gout = go;
1520 return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
1523 static const union tty_capopsu terminfo_ops = { {
1524 { caps_release, TTY_CAPOPS },
1525 { terminfo_boolcap, terminfo_intcap, terminfo_strcap,
1526 terminfo_put0, terminfo_put1i, terminfo_put2i }
1529 static struct tty *terminfo_init(FILE *fp)
1531 union tty_capsu *u = 0; struct tty *ret = 0;
1535 { debug("termcap/terminfo terminal already open"); goto end; }
1536 if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
1538 u->tty.ops = &terminfo_ops.tty;
1539 common_init(&u->tty, fp);
1541 ret = &u->tty; u = 0;
1543 xfree(u); global_lock = ret; return (ret);
1548 /*----- Unibilium ---------------------------------------------------------*/
1550 #ifdef HAVE_UNIBILIUM
1552 struct tty_unibislots {
1554 unibi_var_t dy[26], st[26];
1556 struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
1557 union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
1559 static int termunibi_boolcap(struct tty *tty,
1560 int uix, const char *info, const char *cap)
1562 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1564 return (unibi_get_bool(t->u.ut, uix));
1567 static int termunibi_intcap(struct tty *tty,
1568 int uix, const char *info, const char *cap)
1570 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1572 return (unibi_get_num(t->u.ut, uix));
1575 static const char *termunibi_strcap(struct tty *tty,
1576 int uix, const char *info,
1579 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1581 return (unibi_get_str(t->u.ut, uix));
1584 struct termunibi_outctx {
1585 struct tty_unibilium *t;
1586 const struct gprintf_ops *gops; void *go;
1591 static void termunibi_putch(void *ctx, const char *p, size_t sz)
1593 struct termunibi_outctx *out = ctx;
1595 if (out->gops->putm(out->go, p, sz)) out->rc = -1;
1598 static void termunibi_pad(void *ctx, size_t ms, int mulp, int forcep)
1601 struct termunibi_outctx *out = ctx;
1602 struct tty_unibilium *t = out->t;
1606 /* Based on 7 data bits, 1 stop bit, 1 parity bit. */
1607 #define BITS_PER_KB 9000
1609 if (forcep || t->tty.baud >= t->cap.pb) {
1611 tv.tv_sec = ms/1000; tv.tv_usec = 1000*(ms%1000);
1612 if (t->tty.fpout) fflush(t->tty.fpout);
1613 select(0, 0, 0, 0, &tv);
1615 n = (ms*t->tty.baud + BITS_PER_KB - 1)/BITS_PER_KB;
1617 if (n < sizeof(out->pad)) nn = n;
1618 else nn = sizeof(out->pad);
1619 if (out->gops->putm(out->go, pad, nn)) out->rc = -1;
1628 static void setup_termunibi_outctx(struct tty_unibilium *t,
1629 struct termunibi_outctx *out,
1630 const struct gprintf_ops *gops, void *go)
1632 out->t = t; out->rc = 0;
1633 out->gops = gops; out->go = go;
1635 memset(out->pad, t->cap.pad ? *t->cap.pad : 0, sizeof(out->pad));
1638 static int termunibi_put0(struct tty *tty,
1639 const struct gprintf_ops *gops, void *go,
1640 unsigned npad, const char *cap)
1642 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1643 struct termunibi_outctx out;
1646 if (!cap) return (-1);
1647 setup_termunibi_outctx(t, &out, gops, go);
1648 unibi_format(t->u.dy, t->u.st, cap, arg,
1649 termunibi_putch, &out,
1650 termunibi_pad, &out);
1654 static int termunibi_put1i(struct tty *tty,
1655 const struct gprintf_ops *gops, void *go,
1656 unsigned npad, const char *cap, int i0)
1658 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1659 struct termunibi_outctx out;
1662 if (!cap) return (-1);
1663 setup_termunibi_outctx(t, &out, gops, go);
1664 arg[0] = unibi_var_from_num(i0);
1665 unibi_format(t->u.dy, t->u.st, cap, arg,
1666 termunibi_putch, &out,
1667 termunibi_pad, &out);
1671 static int termunibi_put2i(struct tty *tty,
1672 const struct gprintf_ops *gops, void *go,
1674 const char *cap, int i0, int i1)
1676 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1677 struct termunibi_outctx out;
1680 if (!cap) return (-1);
1681 setup_termunibi_outctx(t, &out, gops, go);
1682 arg[0] = unibi_var_from_num(i0);
1683 arg[1] = unibi_var_from_num(i1);
1684 unibi_format(t->u.dy, t->u.st, cap, arg,
1685 termunibi_putch, &out,
1686 termunibi_pad, &out);
1690 static void termunibi_release(struct tty *tty)
1692 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1694 unibi_destroy(t->u.ut);
1697 static const union tty_capopsu termunibi_ops = { {
1698 { termunibi_release, TTY_CAPOPS },
1699 { termunibi_boolcap, termunibi_intcap, termunibi_strcap,
1700 termunibi_put0, termunibi_put1i, termunibi_put2i }
1703 static struct tty *termunibi_init(FILE *fp)
1705 union tty_unibiliumu *u = 0; struct tty *ret = 0;
1709 term = getenv("TERM"); if (!term) goto end;
1710 ut = unibi_from_term(term); if (!ut) goto end;
1712 u->tty.ops = &termunibi_ops.tty;
1713 u->u.u.ut = ut; ut = 0;
1714 common_init(&u->tty, fp);
1716 ret = &u->tty; u = 0;
1718 xfree(u); if (ut) unibi_destroy(ut);
1724 /*----- ANSI terminals ----------------------------------------------------*/
1726 struct tty_ansislots {
1728 #define TAF_CNCATTR 1u /* attributes can be cancelled */
1729 #define TAF_EDITN 2u /* insert/delete multiple */
1730 #define TAF_SEMI 4u /* semicolons in CSI 38 m colour */
1732 struct tty_ansi { TTY_BASEPFX; struct tty_ansislots ansi; };
1733 union tty_ansiu { struct tty_ansi ansi; TTY_BASEUSFX; };
1735 /* Control sequences.
1737 * * CUP: \33 [ Y ; X H `cursor position' [vt100]
1739 * * CUU/CUD/CUR/CUL: \33 [ N A/B/C/D `cursor up/down/right/left'
1741 * * DCH: \33 [ N P `delete character' [vt220]
1742 * (single char only in vt102?)
1744 * * DL: \33 [ N M `delete line' [vt220]
1745 * (single line only in vt102?)
1747 * * ECH: \33 [ N X `erase characters' [vt220]
1749 * * ED: \33 [ P J `erase in display'
1750 * P = 0 erase to end-of-screen [vt100]
1751 * P = 1 erase from start-of-screen [vt100]
1752 * P = 2 erase entire screen [vt100]
1754 * * EL: \33 [ P K `erase in line'
1755 * P = 0 erase to end-of-line [vt100]
1756 * P = 1 erase from start-of-line [vt100]
1757 * P = 2 erase entire line [vt100]
1759 * * HPA/VPA: \33 [ I G/d `horizontal/vertical position
1760 * absolute' [ecma48-4]
1762 * * ICH: \33 [ N @ `insert character' [vt220]
1763 * (single char only in vt102?)
1765 * * IL: \33 [ N L `insert line' [vt220]
1766 * (single line only in vt102?)
1768 * * SGR: \33 [ P ; ... m `select graphics rendition'
1769 * P = 0 cancel all attributes [vt100]
1770 * P = 1 bold [vt100]
1771 * P = 2 dim [ecma48-4]
1772 * P = 3 italics [ecma48-4]
1773 * P = 4 underline [vt100]
1774 * P = 7 inverse video [vt100]
1775 * P = 9 strikeout [ecma48-4]
1776 * P = 21 double underline [ecma48-4]
1777 * P = 22 cancal bold/dim [vt220]
1778 * P = 24 cancel underline [vt220]
1779 * P = 27 cancel inverse video [vt220]
1780 * P = 30 + 4 R + 2 G + B set 1BPC foreground [ecma48-4]
1781 * P = 38 : 2 : ? : R : G : B set foreground [iso8613-6]
1782 * P = 38 : 5 : N set foreground [iso8613-6, xterm]
1783 * P = 39 cancel foreground [ecma48-4]
1784 * P = 40--49 as above, for background
1785 * P = 90 + 4 R + 2 G + B set bright 1BPC foreground [xterm]
1786 * P = 100 + 4 R + 2 G + B set bright 1BPC background [xterm]
1788 * * SM/RM: \33 [ P ; ... h/l `set/reset modes'
1789 * M = 4 insert [vt220]
1791 * * SM, RM: \33 [ ? P ; ... h/l `set/reset private modes'
1792 * M = 7 auto right margin [vt100]
1793 * M = 25 visible cursor [vt220]
1794 * M = 1049 alternate screen [xterm]
1796 * * \33 [ P ; X ; Y t `window manipulation'
1797 * P = 22, X = 0 save title and icon [xterm]
1798 * P = 23, X = 0 restore title and icon [xterm]
1801 static void ansi_release(struct tty *tty) { ; }
1803 #define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
1805 #define PUTCH(ch) CHECK(gops->putch(go, (ch)))
1806 #define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
1808 if (!(f&TAF_SEMI)) f |= TAF_SEMI; \
1812 static int ansi_setcolour(struct tty_ansi *t, unsigned *f_inout,
1813 const struct gprintf_ops *gops, void *go,
1815 uint32 spc, uint32 clr)
1817 unsigned f = *f_inout;
1822 SEMI; CHECK(gprintf(gops, go, "%d", norm + 9));
1825 SEMI; CHECK(gprintf(gops, go, "%d", norm + clr));
1828 SEMI; CHECK(gprintf(gops, go, "%d", br + (clr&~TT1BPC_BRI)));
1830 case TTCSPC_4LPC: case TTCSPC_6LPC:
1832 if (t->ansi.f&TAF_SEMI)
1833 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 16));
1835 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 16));
1839 if (t->ansi.f&TAF_SEMI)
1840 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 80));
1842 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 80));
1846 if (t->ansi.f&TAF_SEMI)
1847 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 232));
1849 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 232));
1853 if (t->ansi.f&TAF_SEMI)
1854 CHECK(gprintf(gops, go, "%d;2;%d;%d;%d", norm + 8,
1855 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
1857 CHECK(gprintf(gops, go, "%d:2::%d:%d:%d", norm + 8,
1858 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
1866 *f_inout = f; return (rc);
1869 static int ansi_setattr(struct tty *tty,
1870 const struct gprintf_ops *gops, void *go,
1871 const struct tty_attr *a)
1873 struct tty_ansi *t = (struct tty_ansi *)tty;
1877 unsigned z, c, f = 0;
1879 tty_clampattr(&aa, a, t->tty.acaps);
1880 diff = aa.f ^ t->tty.st.attr.f;
1881 if (!diff && aa.fg == t->tty.st.attr.fg && aa.bg == t->tty.st.attr.bg)
1885 #define CLEARP(mask) ((diff&(mask)) && !(aa.f&(mask)))
1886 if (CLEARP(TTAF_LNMASK)) c += 3;
1887 if (CLEARP(TTAF_WTMASK)) c += 3;
1888 if (diff&~aa.f&TTAF_INVV) c += 3;
1889 if (diff&~aa.f&TTAF_STRIKE) c += 3;
1890 if (diff&~aa.f&TTAF_ITAL) c += 3;
1891 if (CLEARP(TTAF_FGSPCMASK)) c += 3;
1892 if (CLEARP(TTAF_BGSPCMASK)) c += 3;
1896 switch ((aa.f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1897 case TTLN_ULINE: z += 2; break;
1898 case TTLN_UULINE: z += 3; break;
1900 if (aa.f&TTAF_WTMASK) z += 2;
1901 if (aa.f&TTAF_INVV) z += 2;
1902 if (aa.f&TTAF_STRIKE) z += 2;
1903 if (aa.f&TTAF_ITAL) z += 2;
1904 #define COLOURCOST(col) do { \
1905 switch ((aa.f&TTAF_##col##SPCMASK) >> TTAF_##col##SPCSHIFT) { \
1906 case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break; \
1907 case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break; \
1908 case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break; \
1909 case TTCSPC_8BPC: z += 16; break; \
1912 COLOURCOST(FG); COLOURCOST(BG);
1917 if (z <= c) { SEMI; diff = aa.f; }
1919 if (diff&TTAF_LNMASK)
1920 switch ((aa.f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1921 case TTLN_NONE: SEMI; PUTLIT("24"); break;
1922 case TTLN_ULINE: SEMI; PUTCH('4'); break;
1923 case TTLN_UULINE: SEMI; PUTLIT("21"); break;
1924 default: rc = -1; goto end;
1927 if (diff&TTAF_WTMASK)
1928 switch ((aa.f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1929 case TTWT_MED: SEMI; PUTLIT("22"); break;
1930 case TTWT_BOLD: SEMI; PUTCH('1'); break;
1931 case TTWT_DIM: SEMI; PUTCH('2'); break;
1932 default: rc = -1; goto end;
1936 { SEMI; if (aa.f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
1937 if (diff&TTAF_STRIKE)
1938 { SEMI; if (aa.f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
1940 { SEMI; if (aa.f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
1942 if (diff&TTAF_FGSPCMASK || aa.fg != tty->st.attr.fg)
1943 CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
1944 (aa.f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, aa.fg));
1945 if (diff&TTAF_BGSPCMASK || aa.bg != tty->st.attr.bg)
1946 CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
1947 (aa.f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, aa.bg));
1951 t->tty.st.attr = aa; return (rc);
1955 static int ansi_setmodes(struct tty *tty,
1956 const struct gprintf_ops *gops, void *go,
1957 uint32 modes_bic, uint32 modes_xor)
1962 /* Figure out which modes to set. */
1963 modes = (tty->st.modes&~modes_bic) ^ modes_xor;
1964 diff = modes ^ tty->st.modes;
1966 if (diff&TTMF_AUTOM) {
1967 if (modes&TTMF_AUTOM) PUTLIT("\33[?7h");
1968 else PUTLIT("\33[?7l");
1971 if (diff&TTMF_FSCRN) {
1972 if (modes&TTMF_FSCRN) PUTLIT("\33[?1049h\33[22;0;0t");
1973 else PUTLIT("\33[?1049l\33[23;0;0t");
1976 if (diff&TTMF_CVIS) {
1977 if (modes&TTMF_CVIS) PUTLIT("\33[?25h");
1978 else PUTLIT("\33[?25l");
1981 if (diff&TTMF_INS) {
1982 if (modes&TTMF_INS) PUTLIT("\33[4h");
1983 else PUTLIT("\33[4l");
1988 tty->st.modes = modes;
1992 static int ansi_move(struct tty *tty,
1993 const struct gprintf_ops *gops, void *go,
1994 unsigned orig, int y, int x)
1998 if (orig == TTORG_HOME) {
2000 if (!y) PUTLIT("\33[H");
2001 else CHECK(gprintf(gops, go, "\33[%dH", y + 1));
2003 if (!y) CHECK(gprintf(gops, go, "\33[;%dH", x + 1));
2004 else CHECK(gprintf(gops, go, "\33[%d,%dH", y + 1, x + 1));
2006 } else if (orig == (TTOF_XHOME | TTOF_YCUR) && x == 0 && y == 1)
2009 if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
2010 else if (y == -1) PUTLIT("\33[A");
2011 else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
2012 else if (y == +1) PUTLIT("\33[B"); /* not `^J'! */
2013 else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
2014 if (!(orig&TTOF_XCUR)) {
2017 else if (tty->ocaps&TTCF_MIXMV)
2018 CHECK(gprintf(gops, go, "\33[%dG", x + 1));
2020 CHECK(gprintf(gops, go, "\r\33[%dC", x));
2022 if (x == -1) PUTCH('\b');
2023 else if (x < 0) CHECK(gprintf(gops, go, "\33[%dD", -x));
2024 else if (x == +1) PUTLIT("\33[C");
2025 else if (x > 0) CHECK(gprintf(gops, go, "\33[%dC", x));
2033 static int ansi_repeat(struct tty *tty,
2034 const struct gprintf_ops *gops, void *go,
2039 while (n--) PUTCH(ch);
2045 static int ansi_erase(struct tty *tty,
2046 const struct gprintf_ops *gops, void *go,
2052 switch (f&(TTEF_BEGIN | TTEF_END)) {
2054 case TTEF_BEGIN: PUTLIT("\33[1J"); break;
2055 case TTEF_END: PUTLIT("\33[J"); break;
2056 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2J"); break;
2059 switch (f&(TTEF_BEGIN | TTEF_END)) {
2061 case TTEF_BEGIN: PUTLIT("\33[1K"); break;
2062 case TTEF_END: PUTLIT("\33[K"); break;
2063 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2K"); break;
2070 static int ansi_erch(struct tty *tty,
2071 const struct gprintf_ops *gops, void *go,
2076 if (n == 1) PUTLIT("\33[X");
2077 else if (n) CHECK(gprintf(gops, go, "\33[%uX", n));
2083 static int ansi_ins(struct tty *tty,
2084 const struct gprintf_ops *gops, void *go,
2085 unsigned f, unsigned n)
2090 if (n == 1) PUTLIT("\33[L");
2091 else if (n) CHECK(gprintf(gops, go, "\33[%uL", n));
2093 if (n == 1) PUTLIT("\33[@");
2094 else if (n) CHECK(gprintf(gops, go, "\33[%u@", n));
2101 static int ansi_inch(struct tty *tty,
2102 const struct gprintf_ops *gops, void *go,
2105 if (!(tty->st.modes&TTMF_INS)) return (-1);
2106 else return (gops->putch(go, ch));
2109 static int ansi_del(struct tty *tty,
2110 const struct gprintf_ops *gops, void *go,
2111 unsigned f, unsigned n)
2116 if (n == 1) PUTLIT("\33[M");
2117 else if (n) CHECK(gprintf(gops, go, "\33[%uM", n));
2119 if (n == 1) PUTLIT("\33[P");
2120 else if (n) CHECK(gprintf(gops, go, "\33[%uP", n));
2133 static const struct tty_ops ansi_ops = {
2135 ansi_setattr, ansi_setmodes,
2136 ansi_move, ansi_repeat,
2137 ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del,
2141 static struct tty *ansi_init(FILE *fp)
2144 #define COLS_8 (TTACF_FG | TTACF_BG | TTACF_1BPC)
2145 #define COLS_16 (COLS_8 | TTACF_1BPCBR)
2146 #define COLS_88 (COLS_16 | TTACF_4LPC | TTACF_8LGS)
2147 #define COLS_256 (COLS_16 | TTACF_6LPC | TTACF_24LGS)
2148 #define COLS_16M (COLS_256 | TTACF_8BPC)
2150 #define EDIT_OPS (TTCF_ERCH | \
2151 TTCF_DELCH | TTCF_DELLN | \
2152 TTCF_INSCH | TTCF_INSLN)
2154 static const struct flagmap {
2156 uint32 acaps, ocaps;
2159 { "dim", TTACF_DIM, 0, 0 },
2160 { "uuline", TTACF_UULINE, 0, 0 },
2161 { "strike", TTACF_STRIKE, 0, 0 },
2162 { "ital", TTACF_ITAL, 0, 0 },
2163 { "cvis", 0, TTMF_CVIS, 0 },
2164 { "fscrn", 0, TTMF_FSCRN, 0 },
2165 { "insmode", 0, TTMF_INS, 0 },
2166 { "hvpa" , 0, TTCF_MIXMV, 0 },
2167 { "edit", 0, EDIT_OPS, 0 },
2168 { "cncattr", 0, 0, TAF_CNCATTR },
2169 { "editn", 0, 0, TAF_EDITN },
2170 { "semi", 0, 0, TAF_SEMI },
2176 static const struct kw { const char *name; uint32 val; }
2182 { "256", COLS_256 },
2183 { "16m", COLS_16M },
2187 static const struct enummap {
2190 const struct kw *kw;
2192 { "colours", TTACF_CSPCMASK, kw_colours },
2197 static const struct termmap {
2199 unsigned acaps, ocaps, tf;
2202 #define VT100_ACAPS (TTACF_ULINE | TTACF_BOLD | TTACF_INVV)
2203 #define VT100_OCAPS (TTMF_AUTOM | \
2204 TTCF_RELMV | TTCF_ABSMV | \
2206 TTCF_ERBOL | TTCF_EREOL | \
2207 TTCF_ERBOD | TTCF_EREOD | TTCF_ERDSP)
2208 #define VT100_TF (0)
2210 #define VT102_ACAPS (VT100_ACAPS)
2211 #define VT102_OCAPS (VT100_OCAPS | \
2213 TTCF_INSCH | TTCF_INSLN | TTCF_DELCH | TTCF_DELLN)
2214 #define VT102_TF (VT100_TF)
2216 #define VT220_ACAPS (VT102_ACAPS)
2217 #define VT220_OCAPS (VT102_OCAPS | TTMF_CVIS | TTCF_ERCH)
2218 #define VT220_TF (VT102_TF | TAF_CNCATTR | TAF_EDITN)
2220 #define ECMA48_ACAPS (VT220_ACAPS | TTACF_DIM)
2221 #define ECMA48_OCAPS (VT220_OCAPS | TTCF_MIXMV)
2222 #define ECMA48_TF (VT220_TF)
2224 #define XTERM_ACAPS (ECMA48_ACAPS)
2225 #define XTERM_OCAPS (ECMA48_OCAPS | TTMF_FSCRN)
2226 #define XTERM_TF (ECMA48_TF)
2228 #define STRIKE TTACF_STRIKE
2229 #define ITAL TTACF_ITAL
2230 #define SEMI TAF_SEMI
2232 #define T(pat, base, cols, acaps, ocaps, tf) \
2234 base##_ACAPS | COLS_##cols | (acaps), \
2235 base##_OCAPS | (ocaps), base##_TF | (tf) }
2237 T("color_xterm", XTERM, 8, STRIKE | ITAL, 0, 0),
2239 T("gnome", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
2240 /*T("gonme-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
2242 T("linux", XTERM, 16, 0, 0, 0),
2244 T("putty", XTERM, 16M, 0, 0, SEMI),
2246 T("vt100*", VT100, NO, 0, 0, 0),
2247 T("vt102*", VT102, NO, 0, 0, 0),
2248 T("vt[2-5][0-9][0-9]*", VT220, NO, 0, 0, 0),
2250 T("vte", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
2251 /*T("vte-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
2253 T("win", XTERM, 16M, 0, 0, SEMI),
2255 T("xterm", XTERM, 16M, STRIKE | ITAL, 0, 0),
2256 T("xterm-color", XTERM, 8, STRIKE | ITAL, 0, 0),
2257 T("xterm-16color", XTERM, 16, STRIKE | ITAL, 0, 0),
2258 T("xterm-88color", XTERM, 88, STRIKE | ITAL, 0, SEMI),
2259 T("xterm-256color", XTERM, 256, STRIKE | ITAL, 0, SEMI),
2260 T("xterm-direct", XTERM, 16M, STRIKE | ITAL, 0, 0),
2261 T("xterm-*", XTERM, 16M, STRIKE | ITAL, 0, 0),
2263 /*T("*-color", XTERM, 16, 0, 0, 0),*/
2264 /*T("*-16color", XTERM, 16, 0, 0, 0),*/
2265 T("*-88color", XTERM, 88, 0, 0, SEMI),
2266 T("*-256color", XTERM, 256, 0, 0, SEMI),
2267 T("*-direct", XTERM, 16M, 0, 0, SEMI),
2269 T("*", XTERM, 16, 0, 0, 0),
2304 union tty_ansiu *u = 0; struct tty *ret = 0;
2305 const char *term, *config, *p, *l;
2306 const struct kw *kw;
2307 const struct enummap *em;
2308 const struct flagmap *fm;
2309 const struct termmap *tm;
2312 acaps = 0, ocaps = 0, tf = 0,
2313 acapset = 0, ocapset = 0, tfset = 0,
2317 config = getenv("MLIB_TTY_ANSICONFIG");
2318 term = getenv("TERM");
2320 if (term && STRCMP(term, ==, "dumb")) goto end;
2323 l = config + strlen(config);
2327 if (config >= l) goto done_config;
2328 else if (!ISSPACE(*config)) break;
2331 for (p = config + 1; p < l && !ISSPACE(*p); p++);
2332 if (*config == '+' || *config == '-') {
2333 if (*config == '+') f |= f_sense;
2335 config++; n = p - config;
2337 for (fm = flagmap; fm->name; fm++)
2338 if (STRNCMP(config, ==, fm->name, n) && !fm->name[n])
2340 debug("unknown flag `%.*s'", (int)n, config); goto next_config;
2342 if ((acapset&fm->acaps) || (ocapset&fm->ocaps) || (tfset&fm->tf)) {
2343 debug("duplicate setting for `%s'", fm->name);
2347 { acaps |= fm->acaps; ocaps |= fm->ocaps; tf |= fm->tf; }
2348 acapset |= fm->acaps; ocapset |= fm->ocaps; tfset |= fm->tf;
2351 p = memchr(config, '=', n);
2353 debug("missing `=' in setting `%.*s'", (int)n, config);
2357 for (em = enummap; em->name; em++)
2358 if (STRNCMP(config, ==, em->name, nn) && !em->name[nn])
2360 debug("unknown setting `%.*s'", (int)nn, config); goto next_config;
2362 p++; nn = n - nn - 1;
2363 for (kw = em->kw; kw->name; kw++)
2364 if (STRNCMP(p, ==, kw->name, nn) && !kw->name[nn])
2366 debug("unknown `%s' value `%.*s", em->name, (int)nn, p);
2369 if (acapset&em->mask) {
2370 debug("duplicate setting for `%s'", em->name);
2373 acaps |= kw->val; acapset |= em->mask;
2383 for (tm = termmap; tm->pat; tm++)
2384 if (str_match(tm->pat, term))
2388 acaps |= tm->acaps&~acapset;
2389 ocaps |= tm->ocaps&~ocapset;
2390 tf |= tm->tf&~tfset;
2393 env_colour_caps(&acaps);
2394 if (acaps&TTACF_CSPCMASK) ocaps |= TTCF_BGER;
2397 u->tty.ops = &ansi_ops;
2398 u->tty.acaps = acaps;
2399 u->tty.ocaps = ocaps;
2400 u->ansi.ansi.f = tf;
2401 u->tty.wd = 80; u->tty.ht = 25;
2402 u->tty.st.modes = TTMF_AUTOM | (u->tty.ocaps&TTMF_CVIS);
2403 u->tty.st.attr.f = 0; u->tty.st.attr.fg = u->tty.st.attr.bg = 0;
2404 common_init(&u->ansi.tty, fp);
2405 ret = &u->tty; u = 0;
2407 xfree(u); return (ret);
2412 /*----- Backend selection -------------------------------------------------*/
2414 struct tty *tty_open(FILE *fp, unsigned f, const unsigned *backends)
2416 static const struct betab {
2417 const char *name; unsigned code;
2418 struct tty *(*init)(FILE */*fp*/);
2420 { "unibilium", TTBK_UNIBI, termunibi_init },
2421 { "terminfo", TTBK_TERMINFO, terminfo_init },
2422 { "termcap", TTBK_TERMCAP, termcap_init },
2423 { "ansi", TTBK_ANSI, ansi_init },
2427 const struct betab *bt;
2428 const char *config, *p, *l;
2429 struct tty *tty = 0;
2433 if (fp || !(f&TTF_OPEN))
2434 fpin = fp != stdout ? fp : isatty(STDIN_FILENO) ? stdin : 0;
2436 if (isatty(STDIN_FILENO)) fpin = stdin;
2438 if (isatty(STDOUT_FILENO)) { fp = stdout; f |= TTF_BORROW; }
2439 else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
2441 fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
2446 config = getenv("MLIB_TTY_BACKENDS");
2448 l = config + strlen(config);
2451 if (config >= l) goto done_config;
2452 else if (!ISSPACE(*config)) break;
2455 for (p = config + 1; p < l && !ISSPACE(*p); p++);
2458 for (bt = betab; bt->name; bt++)
2459 if (STRNCMP(config, ==, bt->name, n) && !bt->name[n])
2461 debug("unknown backend `%.*s'", (int)n, config); goto next_config;
2463 tty = bt->init(fp); if (tty) goto found;
2464 debug("failed to initialize `%s'", bt->name);
2469 } else if (backends)
2471 for (bt = betab; bt->name; bt++)
2472 if (*backends == bt->code) goto found_bycode;
2473 debug("unknown backend code %u", *backends); goto next_code;
2475 tty = bt->init(fp); if (tty) goto found;
2476 debug("failed to initialize `%s'", bt->name);
2481 for (bt = betab; bt->name; bt++) {
2482 tty = bt->init(fp); if (tty) goto found;
2483 debug("failed to initialize `%s'", bt->name);
2486 debug("all backends failed"); goto end;
2488 debug("selected backend `%s'", bt->name);
2489 tty->fpin = fpin; tty->f = f; fp = 0;
2491 if (fp && !(f&TTF_BORROW)) fclose(fp);
2495 void tty_close(struct tty *tty)
2498 if (tty->fpout && !(tty->f&TTF_BORROW)) fclose(tty->fpout);
2499 tty->ops->release(tty); xfree(tty);
2503 /*----- Terminal operations -----------------------------------------------*/
2505 int tty_setattrg(struct tty *tty,
2506 const struct gprintf_ops *gops, void *go,
2507 const struct tty_attr *a)
2508 { return (tty->ops->setattr(tty, gops, go, a)); }
2510 int tty_setattr(struct tty *tty, const struct tty_attr *a)
2511 { return (tty->ops->setattr(tty, &file_printops, tty->fpout, a)); }
2513 int tty_setattrlistg(struct tty *tty,
2514 const struct gprintf_ops *gops, void *go,
2515 const struct tty_attrlist *aa)
2518 if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
2519 return (tty->ops->setattr(tty, gops, go, &aa->attr));
2520 else if (!aa->cap_mask)
2524 int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
2525 { return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa)); }
2527 int tty_setmodesg(struct tty *tty,
2528 const struct gprintf_ops *gops, void *go,
2529 uint32 modes_bic, uint32 modes_xor)
2530 { return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor)); }
2532 int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
2534 return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
2535 modes_bic, modes_xor));
2538 int tty_moveg(struct tty *tty,
2539 const struct gprintf_ops *gops, void *go,
2540 unsigned orig, int y, int x)
2541 { return (tty->ops->move(tty, gops, go, orig, y, x)); }
2543 int tty_move(struct tty *tty, unsigned orig, int y, int x)
2544 { return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x)); }
2546 int tty_repeatg(struct tty *tty,
2547 const struct gprintf_ops *gops, void *go,
2549 { return (tty->ops->repeat(tty, gops, go, ch, n)); }
2551 int tty_repeat(struct tty *tty, int ch, unsigned n)
2552 { return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n)); }
2554 int tty_eraseg(struct tty *tty,
2555 const struct gprintf_ops *gops, void *go,
2557 { return (tty->ops->erase(tty, gops, go, f)); }
2559 int tty_erase(struct tty *tty, unsigned f)
2560 { return (tty->ops->erase(tty, &file_printops, tty->fpout, f)); }
2562 int tty_erchg(struct tty *tty,
2563 const struct gprintf_ops *gops, void *go,
2565 { return (tty->ops->erch(tty, gops, go, n)); }
2567 int tty_erch(struct tty *tty, unsigned n)
2568 { return (tty->ops->erch(tty, &file_printops, tty->fpout, n)); }
2570 int tty_insg(struct tty *tty,
2571 const struct gprintf_ops *gops, void *go,
2572 unsigned f, unsigned n)
2573 { return (tty->ops->ins(tty, gops, go, f, n)); }
2575 int tty_ins(struct tty *tty, unsigned f, unsigned n)
2576 { return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n)); }
2578 int tty_inchg(struct tty *tty,
2579 const struct gprintf_ops *gops, void *go,
2581 { return (tty->ops->inch(tty, gops, go, ch)); }
2583 int tty_inch(struct tty *tty, int ch)
2584 { return (tty->ops->inch(tty, &file_printops, tty->fpout, ch)); }
2586 int tty_delg(struct tty *tty,
2587 const struct gprintf_ops *gops, void *go,
2588 unsigned f, unsigned n)
2589 { return (tty->ops->del(tty, gops, go, f, n)); }
2591 int tty_del(struct tty *tty, unsigned f, unsigned n)
2592 { return (tty->ops->del(tty, &file_printops, tty->fpout, f, n)); }
2594 /*----- That's all, folks -------------------------------------------------*/