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 /*----- Operations table --------------------------------------------------*/
69 /* Incorporate the published control-block structure into our more elaborate
72 #define TTY_BASEPFX struct tty tty
73 #define TTY_BASEUSFX struct tty tty
76 void (*release)(struct tty */*tty*/);
77 /* Free any resources held by the backend. */
79 /* The following operations handle the correspondingly named interface
82 int (*setattr)(struct tty */*tty*/,
83 const struct gprintf_ops */*gops*/, void */*go*/,
84 const struct tty_attr */*a*/);
85 int (*setmodes)(struct tty */*tty*/,
86 const struct gprintf_ops */*gops*/, void */*go*/,
87 uint32 /*modes_bic*/, uint32 /*modes_xor*/);
88 int (*move)(struct tty */*tty*/,
89 const struct gprintf_ops */*gops*/, void */*go*/,
90 unsigned /*orig*/, int /*y*/, int /*x*/);
91 int (*repeat)(struct tty */*tty*/,
92 const struct gprintf_ops */*gops*/, void */*go*/,
93 int /*ch*/, unsigned /*n*/);
94 int (*erase)(struct tty */*tty*/,
95 const struct gprintf_ops */*gops*/, void */*go*/,
97 int (*erch)(struct tty */*tty*/,
98 const struct gprintf_ops */*gops*/, void */*go*/,
100 int (*ins)(struct tty */*tty*/,
101 const struct gprintf_ops */*gops*/, void */*go*/,
102 unsigned /*f*/, unsigned /*n*/);
103 int (*inch)(struct tty */*tty*/,
104 const struct gprintf_ops */*gops*/, void */*go*/,
106 int (*del)(struct tty */*tty*/,
107 const struct gprintf_ops */*gops*/, void */*go*/,
108 unsigned /*f*/, unsigned /*n*/);
110 #define TTY_BASEOPSPFX struct tty_ops tty
111 #define TTY_BASEOPSUXFX struct tty_ops tty
113 /*----- Common support machinery ------------------------------------------*/
117 * Arguments@ @expr@ = expression to evaluate
119 * Use: Evaluate @expr@. If the result is (strictly) negative, then
120 * set @rc = -1@ and transfer control to the label @end@.
123 #define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
127 * Arguments: @const char *fmt@ = format control string
128 * @...@ = format arguemnts
132 * Use: Maybe report a debugging message to standard error.
135 static PRINTF_LIKE(1, 2) void debug(const char *fmt, ...)
140 p = getenv("MLIB_TTY_DEBUG");
141 if (p && *p != 'n' && *p != '0') {
143 fputs("mLib TTY: ", stderr);
144 vfprintf(stderr, fmt, ap);
150 /* --- @common_init@ --- *
152 * Arguments: @struct tty *tty@ = pointer to terminal control block
153 * @FILE *fp@ = output file stream
157 * Use: Perform general initialization on the terminal control
160 * Specifically, this fills in the @fpout@, @baud@, @ht@, and
161 * @wd@ slots. The width and height come from the kernel, or,
162 * failing that, the environment.
165 static void common_init(struct tty *tty, FILE *fp)
167 static const struct baudtab { speed_t code; unsigned baud; } baudtab[] = {
169 ;;; The baud-rate table is very boring to type. To make life less
170 ;;; awful, put the rates in this list and evaluate the code to get Emacs
171 ;;; to regenerate it.
173 (let ((bauds '(50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600
174 19200 38400 57600 115200 230400 460800 500000 576000
175 921600 1000000 1152000 1500000 2000000 2500000 3000000
178 (goto-char (point-min))
179 (search-forward (concat "***" "BEGIN baudlist" "***"))
180 (beginning-of-line 2)
181 (delete-region (point)
183 (search-forward "***END***")
186 (dolist (baud (sort (copy-list bauds) #'<))
187 (insert (format "#ifdef B%d\n { B%d, %d },\n#endif\n"
190 /***BEGIN baudlist***/
258 { B1000000, 1000000 },
261 { B1152000, 1152000 },
264 { B1500000, 1500000 },
267 { B2000000, 2000000 },
270 { B2500000, 2500000 },
273 { B3000000, 3000000 },
276 { B3500000, 3500000 },
279 { B4000000, 4000000 },
285 const struct baudtab *b;
286 const char *p; int n;
290 /* Save the output stream. */
293 /* Determine the output baud rate. Unhelpfully, the kernel provides a
294 * weird code, so we have to convert it into an actual rate in bits per
297 if (!fp || tcgetattr(fileno(fp), &c))
300 code = cfgetospeed(&c);
301 for (b = baudtab; b->baud; b++)
302 if (b->code == code) { tty->baud = b->baud; goto found_baud; }
308 /* If the kernel didn't tell us the terminal dimensions, try to read them
309 * from the environment.
312 { p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
314 { p = getenv("LINES"); if (p) { n = atoi(p); if (n) tty->ht = n; } }
317 /* --- @env_colour_caps@ --- *
319 * Arguments: @unsigned *caps_inout@ = attribute capabilities to update
320 * @unsigned f@ = flags
324 * Use: Check the %|FORCE_COLOR|% environment variable and update the
325 * capabilities as required.
327 * The %|FORCE_COLOR|% variable originates with the Node
328 * community, with two objectives: (a) to convey policy
329 * regarding whether to produce coloured output, and (b) to
330 * describe the colour capabilities of the terminal, because the
331 * traditional mechanisms are deemed inadequate.
333 * The following values have defined meanings.
335 * * Unset or empty: no effect.
337 * * %|0|%: monochrome; don't produce colour.
339 * * %|1|%: 16 colours; 1-bit-per-channel colours are
340 * available, with an additional common brightness bit.
342 * * %|2|%: 256 colours; the `xterm' 256-bit palette is
343 * available, consisting of the 1-bit-per-channel colours
344 * with common brightness bit, a 6 × 6 × 6 colour cube, and
345 * a 24-level greyscale ramp.
347 * * %|3|%: full 24-bit colour.
349 * * Anything else: a request to use colour if available.
350 * This is ignored here, in the expectation that it will be
351 * given effect elsewhere, e.g., by @ttycolour_enablep@.
353 * If @ECCF_SET@ is set, then set or clear capabilities as
354 * required. Otherwise, clear capability bits which are denied
355 * by the variable setting, but no bits will be set. (This
356 * latter is necessary for backends which use terminal
357 * databases, since they can't be expected to make up the
358 * necessary control sequences for themselves.)
362 static void env_colour_caps(unsigned *caps_inout, unsigned f)
365 unsigned caps = *caps_inout, mask;
367 p = getenv("FORCE_COLOR"); if (!p) return;
373 mask = TTACF_FG | TTACF_BG | TTACF_1BPC | TTACF_1BPCBR;
376 mask = TTACF_FG | TTACF_BG |
377 TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
380 mask = TTACF_FG | TTACF_BG |
381 TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
386 if (!(f&ECCF_SET)) caps &= mask;
387 else caps = (caps&~(TTACF_CSPCMASK | TTACF_FG | TTACF_BG)) | mask;
392 /* --- @clamp_colours@ --- *
394 * Arguments: @uint32 *space_out, *colour_out@ = selected space and colour
395 * @uint32 space, colour@ = requested space and colour
396 * @uint32 acaps@ = terminal's attribute capability mask
400 * Use: Select the best approximation to the requested colour which
401 * can be accommodated by the terminal.
404 /* #define DEBUG_CLAMP */
406 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
407 uint32 space, uint32 colour, uint32 acaps)
409 unsigned r, g, b, rr, gg, bb, y, t, u;
410 uint32 best_colour = 0, best_space; int best_error;
418 /* Check the colour space. If it's one that the terminal can handle, then
419 * return the colour unchanged. Otherwise, extract the channel components
425 /* No colour wanted at all. There's nothing to do here. */
427 *space_out = TTCSPC_NONE; *colour_out = 0;
431 /* One-bit-per-channel colour.
433 * There's no standardized mapping for these; indeed, they're commonly
434 * configurable by users. Since there are also `bright' versions of
435 * each, it's probably not right to just map zero to zero and one to
439 if (colour >= 8) goto inval;
440 if (acaps&(TTACF_1BPC | TTACF_1BPCBR))
441 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
443 #define C1BPC_FULL 0xcc
444 #define C1BPC_CVT(col, bit) \
445 (C1BPC_FULL&((((col)&(1 << (bit))) << (8 - (bit))) - 1))
446 r = C1BPC_CVT(colour, 0);
447 g = C1BPC_CVT(colour, 1);
448 b = C1BPC_CVT(colour, 2);
452 /* One-bit-per-channel colour, with global brightness. Again, there's
453 * no standardized mapping. Apply a boost across all three channels.
456 if (colour >= 16) goto inval;
457 if (!(colour&TT1BPC_BRI) && (acaps&(TTACF_1BPC | TTACF_1BPCBR)))
458 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
459 else if (acaps&TTACF_1BPCBR)
460 { *space_out = TTCSPC_1BPCBR; *colour_out = colour; return; }
462 #define C1BPC_BRIGHT 0x33
463 r = C1BPC_CVT(colour, 0) + C1BPC_BRIGHT;
464 g = C1BPC_CVT(colour, 1) + C1BPC_BRIGHT;
465 b = C1BPC_CVT(colour, 2) + C1BPC_BRIGHT;
470 /* Four-levels-per-channel colour. These are part of an `indexed'
471 * colour space which is theoretically controlled by applications but
472 * (a) that's rare, and (b) worrying about that won't do us any good.
474 * Each channel has four levels, but they're not assigned linearly.
477 if (colour >= 64) goto inval;
478 if (acaps&TTACF_4LPC)
479 { *space_out = TTCSPC_4LPC; *colour_out = colour; return; }
485 #define C4LPC_CVT(ch) (t = (ch), t == 0 ? C4LPC_L0 : \
486 t == 1 ? C4LPC_L1 : \
487 t == 2 ? C4LPC_L2 : \
489 r = C4LPC_CVT(TTCOL_2BR(colour));
490 g = C4LPC_CVT(TTCOL_2BG(colour));
491 b = C4LPC_CVT(TTCOL_2BB(colour));
496 /* Eight-levels greyscale. Again, these are part of an `indexed'
497 * colour space which is under application control, and, again, the
498 * levels aren't linear, but they're not far off linear.
500 if (colour >= 8) goto inval;
501 if (acaps&TTACF_8LGS)
502 { *space_out = TTCSPC_8LGS; *colour_out = colour; return; }
504 r = g = b = 255*(colour ? colour + 3 : 2)/11;
508 /* Six-levels-per-channel colour. Again, `indexed' colour space under
509 * application control. This time the mapping is essentially liner.
512 if (colour >= 216) goto inval;
513 if (acaps&TTACF_6LPC)
514 { *space_out = TTCSPC_6LPC; *colour_out = colour; return; }
516 #define C6LPC_CVT(ch) (t = (ch), t ? (40*t + 55) : 0)
517 r = C6LPC_CVT(TTCOL_6LR(colour));
518 g = C6LPC_CVT(TTCOL_6LG(colour));
519 b = C6LPC_CVT(TTCOL_6LB(colour));
524 /* Twenty-four-levels greyscale. Same story. */
526 if (colour >= 24) goto inval;
527 if (acaps&TTACF_24LGS)
528 { *space_out = TTCSPC_24LGS; *colour_out = colour; return; }
530 r = g = b = 10*colour + 8;
534 /* Eight-bits-per-channel colour. No conversion to apply here. */
536 if (colour >= 0x01000000) goto inval;
537 if (acaps&TTACF_8BPC)
538 { *space_out = TTCSPC_8BPC; *colour_out = colour; return; }
540 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
549 /* We didn't get an exact match, so we'll have to make do with what we've
552 best_error = -1; best_space = TTCSPC_NONE; best_colour = 0;
553 D( fprintf(stderr, "\n;; APPROX space %u, colour 0x%lx = %u/%u/%u\n",
554 space, (unsigned long)colour, r, g, b); )
556 /* Approximate colour weightings for human colour vision. */
560 #define TOTWT (RWT + GWT + BWT)
562 /* Determine the optimal grey approximation for this colour. */
563 y = (RWT*r + GWT*g + BWT*b + TOTWT/2)/TOTWT;
565 #define TRY_APPROX(rr, gg, bb, spc, clr) do { \
566 /* If the approximation (RR, GG, BB) is closer to the current best \
567 * then accept SPC and CLR as the new best space/colour option. \
570 int _r_err = (rr) - r, _g_err = (gg) - g, _b_err = (bb) - b; \
573 if (_r_err < 0) _r_err = -_r_err; \
574 if (_g_err < 0) _g_err = -_g_err; \
575 if (_b_err < 0) _b_err = -_b_err; \
577 _err = RWT*_r_err + GWT*_g_err + BWT*_b_err; \
579 ";; candidate space %u, colour 0x%lx = %u/%u/%u; " \
581 (spc), (unsigned long)(clr), (rr), (gg), (bb), _err); ) \
582 if (best_error < 0 || _err < best_error) { \
583 best_error = _err; best_space = (spc); best_colour = (clr); \
584 D( fprintf(stderr, ";;\tNEW BEST APPROXIMATION\n"); ) \
588 if (!(acaps&(TTACF_4LPC | TTACF_6LPC | TTACF_8BPC))) {
589 /* The one-bit-per-channel colours are very variable, but there's little
590 * choice, so we'll have to try. We assume the same mapping as on the
594 if (acaps&(TTACF_1BPC | TTACF_1BPCBR)) {
595 /* One-bit-per-channel colour. */
597 #define C1BPC_APPROX(cc, c, bit) do { \
598 if ((c) <= C1BPC_FULL/2) (cc) = 0; \
599 else { (cc) = C1BPC_FULL; t |= (bit); } \
602 C1BPC_APPROX(rr, r, TT1BPC_RED);
603 C1BPC_APPROX(gg, g, TT1BPC_GRN);
604 C1BPC_APPROX(bb, b, TT1BPC_BLU);
606 TRY_APPROX(rr, gg, bb, TTCSPC_1BPC, t);
609 if (acaps&TTACF_1BPCBR) {
610 /* One-bit-per-channel colour, with global brightness. */
612 #define C1BPCBR_APPROX(cc, c, bit) do { \
613 if ((c) <= C1BPC_FULL/2 + C1BPC_BRIGHT) (cc) = C1BPC_BRIGHT; \
614 else { (cc) = 255; t |= (bit); } \
617 C1BPCBR_APPROX(rr, r, TT1BPC_RED);
618 C1BPCBR_APPROX(gg, g, TT1BPC_GRN);
619 C1BPCBR_APPROX(bb, b, TT1BPC_BLU);
620 #undef C1BPCBR_APPROX
621 TRY_APPROX(rr, gg, bb, TTCSPC_1BPCBR, t);
625 if (acaps&TTACF_4LPC) {
626 /* Four-levels-per-channel colour. */
628 #define C4LPC_APPROX(cc, c, sh) do { \
631 if (_c > (C4LPC_L2 + C4LPC_L3)/2) \
632 { (cc) = C4LPC_L3; t |= 3 << (sh); } \
633 else if (_c > (C4LPC_L1 + C4LPC_L2)/2) \
634 { (cc) = C4LPC_L2; t |= 2 << (sh); } \
635 else if (_c > (C4LPC_L0 + C4LPC_L1)/2) \
636 { (cc) = C4LPC_L1; t |= 1 << (sh); } \
641 C4LPC_APPROX(rr, r, 4);
642 C4LPC_APPROX(gg, g, 2);
643 C4LPC_APPROX(bb, b, 0);
645 TRY_APPROX(rr, gg, bb, TTCSPC_4LPC, t);
648 if (acaps&TTACF_8LGS) {
649 /* Eight-levels greyscale. */
652 if (u <= 2) { u = 2; t = 0; }
653 else if (u == 3) { u = 4; t = 1; }
654 else if (u == 11) { u = 10; t = 7; }
656 u = (255*u)/11; TRY_APPROX(u, u, u, TTCSPC_8LGS, t);
659 if (acaps&TTACF_6LPC) {
660 /* Six-levels-per-channel colour. */
662 #define C6LPC_APPROX(cc, c, f) do { \
665 if (_c < 36) (cc) = 0; \
666 else { u = (_c - 36)/40; t += (f)*u; (cc) = 40*u + 55; } \
669 C6LPC_APPROX(rr, r, 36);
670 C6LPC_APPROX(gg, g, 6);
671 C6LPC_APPROX(bb, b, 1);
673 TRY_APPROX(rr, gg, bb, TTCSPC_6LPC, t);
676 if (acaps&TTACF_24LGS) {
677 /* Twenty-four-levels greyscale. */
679 if (y < 3) { t = 0; u = 8; }
680 else if (y >= 243) { t = 23; u = 238; }
681 else { t = (y - 3)/10; u = 10*t + 8; }
682 TRY_APPROX(u, u, u, TTCSPC_24LGS, t);
685 if (acaps&TTACF_8BPC) {
686 /* Eight-bits-per-channel colour. */
689 D( fprintf(stderr, ";; accept exact 8bpc colour\n"); )
690 best_error = 0; best_space = TTCSPC_8BPC;
691 best_colour = TTCOL_MK8B(r, g, b);
696 *space_out = best_space; *colour_out = best_colour;
700 /* Invalid colour selection. Ignore this. */
701 *space_out = TTCSPC_NONE; *colour_out = 0;
726 /* --- @clamp_attr@ --- *
728 * Arguments: @struct tty_attr *a_out@ = selected attributes
729 * @const struct tty_attr *a@ = requested attributes
730 * @uint32 acaps@ = terminal's attribute capability mask
734 * Use: Select the closest approximation to the requested attributes
735 * which can be accommodated by the terminal.
738 static void clamp_attr(struct tty_attr *a_out,
739 const struct tty_attr *a, uint32 acaps)
741 uint32 ff = 0, f = a ? a->f : 0, t;
743 /* Line attributes. */
744 t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
749 if (!acaps&TTACF_ULINE) t = TTLN_NONE;
752 if (acaps&TTACF_UULINE) ;
753 else if (acaps&TTACF_ULINE) t = TTLN_ULINE;
759 ff |= t << TTAF_LNSHIFT;
762 t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
764 case TTWT_MED: break;
765 case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
766 case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
767 default: t = TTWT_MED; break;
769 ff |= t << TTAF_WTSHIFT;
771 /* Other text attributes. */
772 if (acaps&TTACF_STRIKE) ff |= f&TTAF_STRIKE;
773 if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
774 if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
776 /* Foreground and background colours. */
777 if (acaps&TTACF_FG) {
778 clamp_colours(&t, &a_out->fg,
779 (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a ? a->fg : 0,
781 ff |= t << TTAF_FGSPCSHIFT;
783 if (acaps&TTACF_BG) {
784 clamp_colours(&t, &a_out->bg,
785 (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a ? a->bg : 0,
787 ff |= t << TTAF_BGSPCSHIFT;
791 a_out->f = ff; a_out->_res0 = 0;
794 /* --- @stupid_repeat@ --- *
796 * Arguments: @struct tty *tty@ = control block pointer
797 * @const struct gprintf_ops *gops, void *go@ = output
799 * @int ch@ = character to write
800 * @unsigned n@ = number of copies
802 * Returns: Zero on success, %$-1$% on error.
804 * Use: Write @n@ copies of the character @ch@ to the terminal, the
805 * hard way. This function tries to be reasonably efficient, by
806 * transmitting buffers rather than exercising the output
807 * machinery for each individual character.
810 static int stupid_repeat(struct tty *tty,
811 const struct gprintf_ops *gops, void *go,
819 { memset(buf, ch, n); CHECK(gops->putm(go, buf, n)); }
821 memset(buf, ch, sizeof(buf));
824 CHECK(gops->putm(go, buf, nn));
825 n -= nn; if (!n) break;
834 /*----- Common machinery for %|termcap|% and %|terminfo|% -----------------*/
836 #if defined(HAVE_TERMINFO) || \
837 defined(HAVE_TERMCAP) || \
838 defined(HAVE_UNIBILIUM)
840 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
844 * The `termcap' and `terminfo' functions call a user-provided function to
845 * actually send control codes to the terminal. The bad news is that
846 * `termcap' doesn't provide any way to pass information to the output
847 * function beyond the character to be sent, and `terminfo' doesn't fix this
848 * mistake. So we must save the necessary context as global variables. For
849 * good measure, at least some implementations ignore errors from the output
850 * function, so we must keep track of them ourselves. More global variables.
852 * It's worse. Both libraries maintain significant global state of their
853 * own. And, at least with the `ncurses' implementation, the two share the
854 * same global state. The only thing to do is maintain a big interlock to
855 * make sure that only one is active at a time.
857 static const struct gprintf_ops *global_gops; /* output operations ... */
858 static void *global_gout; /* and context, for @caps_putch */
859 static char global_buf[4096]; /* a big output buffer */
860 static size_t global_len; /* length of buffer used */
861 static int global_err; /* error latch, zero if all ok */
862 static struct tty *global_lock = 0; /* interlock for global state */
864 /* --- @caps_claim@ --- *
868 * Returns: Zero on success, %$-1$% if already claimed.
870 * Use: Return %$-1$% if the interlock is already held. This is a
871 * function to call near the beginning of initializing a new
872 * control block, before the common global state gets
873 * clobbered. If initialization is successful, the caller is
874 * expected to actually store the control block pointer in
875 * @global_lock@ themselves.
878 static int caps_claim(void)
881 { debug("termcap/terminfo terminal already open"); return (-1); }
886 /* --- @caps_claim@, @caps_release@ --- *
888 * Arguments: @struct tty *tty@ = control block pointer for current lock
893 * Use: Release the lock.
896 static void caps_release(struct tty *tty)
897 { assert(global_lock == tty); global_lock = 0; }
899 /* --- @caps_putch@ --- *
901 * Arguments: @int ch@ = character to write
903 * Returns: Nonnegative on success, negative on failure. (But @tputs@
906 * Use: Output the character @ch@.
909 static int caps_putch(int ch)
911 if (global_len >= sizeof(global_buf)) {
912 if (global_gops->putm(global_gout, global_buf, global_len))
916 global_buf[global_len++] = ch;
920 /* --- @caps_prepout@ --- *
922 * Arguments: @struct tty *tty@ = control block pointer (ignored)
923 * @const struct gprintf_ops *gops, void *go@ = output
928 * Use: Prepare output to the given destination.
931 static void caps_prepout(struct tty *tty,
932 const struct gprintf_ops *gops, void *go)
933 { assert(!global_len); global_gops = gops; global_gout = go; }
935 /* --- @caps_flush@ --- *
937 * Arguments: @struct tty *tty@ = control block pointer (ignored)
939 * Returns: Zero for success, %$-1$% if error pending.
941 * Use: Flush the output buffer to the backend. If an error is
942 * pending, clear it and return failure.
945 static int caps_flush(struct tty *tty)
950 if (global_gops->putm(global_gout, global_buf, global_len)) rc = -1;
953 global_err = 0; return (rc);
958 /* The list of interesting capabilities.
960 * We never actually need all of these: some are only needed if others are
961 * unavailable. But the list isn't too huge, so we'll live with it.
963 * The main thing is that each capability has three different names: the
964 * `full' name (corresponding to a `terminfo' variable name), the `terminfo'
965 * capability name, as used in terminal descriptions, and the two-character
966 * `termcap' name. Unibilium uses the long names, but to reduce typing, the
967 * `unibi_' prefix is omitted here. (Annoyingly, in `ncurses', at least, the
968 * `variable' names are `secretly' macros referencing a current state, and
969 * premature expansion causes misery, so I've left the leading underscores in
970 * place as a countermeasure.) Internally, we use the short `terminfo'
971 * names, since they generally express the most useful information in the
975 #define BASICCAPS(_bool, _int, _str) \
976 _str(_repeat_char, rep, rp) \
977 _str(_pad_char, pad, pc) _int(_padding_baud_rate, pb, pb) \
978 _bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo) \
979 _bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
981 #define ATTRCAPS(_bool, _int, _str) \
982 _str(_exit_attribute_mode, sgr0, me) \
983 _str(_enter_underline_mode, smul, us) \
984 _str(_exit_underline_mode, rmul, ue) \
985 _str(_enter_italics_mode, sitm, ZH) _str(_exit_italics_mode, ritm, ZR) \
986 _str(_enter_bold_mode, bold, md) _str(_enter_dim_mode, dim, mh) \
987 _str(_enter_reverse_mode, rev, mr) \
988 COLOURCAPS(_bool, _int, _str)
990 #define COLOURCAPS(_bool, _int, _str) \
991 _str(_set_a_foreground, setaf, AF) _str(_set_a_background, setab, AB) \
992 _str(_orig_pair, op, op) _int(_max_colors, colors, Co)
994 #define MODECAPS(_bool, _int, _str) \
995 _str(_carriage_return, cr, cr) _str(_newline, nel, nw) \
996 _str(_enter_am_mode, smam, SA) _str(_exit_am_mode, rmam, RA) \
997 _str(_enter_ca_mode, smcup, ti) _str(_exit_ca_mode, rmcup, te) \
998 _str(_cursor_normal, cnorm, vs) _str(_cursor_invisible, civis, vi) \
999 _str(_enter_insert_mode, smir, im) _str(_exit_insert_mode, rmir, ei) \
1000 _str(_enter_delete_mode, smdc, dm) _str(_exit_delete_mode, rmdc, ed)
1002 #define MOVECAPS(_bool, _int, _str) \
1003 _str(_cursor_home, home, ho) \
1004 _str(_cursor_address, cup, cm) \
1005 _str(_row_address, vpa, cv) _str(_column_address, hpa, ch) \
1006 _str(_cursor_left, cub1, le) _str(_parm_left_cursor, cub, LE) \
1007 _str(_cursor_right, cuf1, nd) _str(_parm_right_cursor, cuf, RI) \
1008 _str(_cursor_up, cuu1, up) _str(_parm_up_cursor, cuu, UP) \
1009 _str(_cursor_down, cud1, do) _str(_parm_down_cursor, cud, DO)
1011 #define SCROLLCAPS(_bool, _int, _str) \
1012 _str(_change_scroll_region, csr, cs) \
1013 _str(_scroll_forward, ind, sf) _str(_parm_index, indn, SF) \
1014 _str(_scroll_reverse, ri, sr) _str(_parm_rindex, rin, SR)
1016 #define ERASECAPS(_bool, _int, _str) \
1017 _str(_erase_chars, ech, ec) \
1018 _str(_clr_bol, el1, cb) _str(_clr_eol, el, ce) \
1019 _str(_clr_eos, ed, cd) _str(_clear_screen, clear, cl)
1021 #define INSDELCAPS(_bool, _int, _str) \
1022 _str(_insert_character, ich1, ic) _str(_parm_ich, ich, IC) \
1023 _str(_insert_padding, ip, ip) \
1024 _str(_insert_line, il1, al) _str(_parm_insert_line, il, AL) \
1025 _str(_delete_character, dch1, dc) _str(_parm_dch, dch, DC) \
1026 _str(_delete_line, dl1, dl) _str(_parm_delete_line, dl, DL)
1028 #define STORECAPS(_bool, _int, _str) \
1029 BASICCAPS(_bool, _int, _str) \
1030 ATTRCAPS(_bool, _int, _str) \
1031 MODECAPS(_bool, _int, _str) \
1032 MOVECAPS(_bool, _int, _str) \
1033 SCROLLCAPS(_bool, _int, _str) \
1034 ERASECAPS(_bool, _int, _str) \
1035 INSDELCAPS(_bool, _int, _str)
1037 #ifdef HAVE_UNIBILIUM
1038 # define UNIBI_(x) unibi##x
1040 # define UNIBI_(x) 0
1043 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
1044 /* Expand a capability triple into a group of three usable C arguments. If
1045 * Unibilium isn't available, then we can use nonsense for its cap index.
1048 /* Some other capabilities which we want to refer to during initialization,
1049 * but don't need to keep around.
1051 #define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
1052 #define CAP_BCE CAPREF(_back_color_erase, bce, ut)
1053 #define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
1054 #define CAP_XVPA CAPREF(_col_addr_glitch, xhpa, YA)
1055 #define CAP_AM CAPREF(_auto_right_margin, am, am)
1056 #define CAP_XENL CAPREF(_eat_newline_glitch, xenl, xn)
1057 #define CAP_HT CAPREF(_lines, lines, li)
1058 #define CAP_WD CAPREF(_columns, cols, co)
1060 /* Additional operations required of terminal backends which make use of the
1061 * common capability machinery.
1063 struct tty_capopslots {
1065 /* Retrieving capabilities. */
1066 int (*boolcap)(struct tty */*tty*/,
1067 int /*uix*/, const char */*info*/, const char */*cap*/);
1068 int (*intcap)(struct tty */*tty*/,
1069 int /*uix*/, const char */*info*/, const char */*cap*/);
1070 const char *(*strcap)(struct tty */*tty*/,
1071 int /*uix*/, const char */*info*/,
1072 const char */*cap*/);
1074 /* Preparing and completing output. */
1075 void (*prepout)(struct tty */*tty*/,
1076 const struct gprintf_ops */*gops*/, void */*go*/);
1077 int (*flush)(struct tty */*tty*/);
1079 /* Writing capabilities with various kinds of arguments. */
1080 int (*put0)(struct tty */*tty*/, unsigned /*npad*/, const char */*cap*/);
1081 int (*put1i)(struct tty */*tty*/,
1082 unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1083 int (*put2i)(struct tty */*tty*/,
1085 const char */*cap*/, int /*i0*/, int /*i1*/);
1087 #define TTY_CAPOPSPFX TTY_BASEOPSPFX; struct tty_capopslots cap
1088 struct tty_capops { TTY_CAPOPSPFX; };
1089 #define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
1090 union tty_capopsu { TTY_CAPOPSUSFX; };
1092 /* An extension of the control block to track the above capabilities. */
1093 struct tty_capslots {
1094 #define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
1095 #define DEF_INTCAP(uix, info, cap) int info;
1096 #define DEF_STRCAP(uix, info, cap) const char *info;
1097 STORECAPS(DEF_BOOLCAP, DEF_INTCAP, DEF_STRCAP)
1102 #define TTY_CAPSPFX \
1104 struct tty_capslots cap
1105 struct tty_caps { TTY_CAPSPFX; };
1106 #define TTY_CAPSUSFX \
1107 struct tty_caps cap; \
1109 union tty_capsu { TTY_CAPSUSFX; };
1111 /* ---- @init_caps@ --- *
1113 * Arguments: @struct tty *tty@ = control block pointer
1117 * Use: Populate the capabilities in the terminal control block, and
1118 * advertise the results to the public part. Set @ht@ and @wd@
1119 * from the terminal description if they've not been set
1123 static void init_caps(struct tty_caps *t)
1125 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1128 t->tty.acaps = t->tty.ocaps = 0;
1129 t->tty.st.modes = 0; t->tty.st.attr.f = 0;
1130 t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1132 /* Inhale all of the interesting terminal capabilities. */
1133 #define GETBOOL(var, info, cap_) \
1134 t->cap.info = ops->cap.boolcap(&t->tty, CAPREF(var, info, cap_));
1135 #define GETINT(var, info, cap_) \
1136 t->cap.info = ops->cap.intcap(&t->tty, CAPREF(var, info, cap_));
1137 #define GETSTR(var, info, cap_) \
1138 t->cap.info = ops->cap.strcap(&t->tty, CAPREF(var, info, cap_));
1139 STORECAPS(GETBOOL, GETINT, GETSTR)
1144 #define CLEAR_BOOL(uix, info, cap_) t->cap.info = 0;
1145 #define CLEAR_INT(uix, info, cap_) t->cap.info = 0;
1146 #define CLEAR_STR(uix, info, cap_) t->cap.info = 0;
1147 #define CLEAR_CAPS(caplist) \
1148 do { caplist(CLEAR_BOOL, CLEAR_INT, CLEAR_STR) } while (0)
1150 /* Basic capabilities. */
1151 if (!t->cap.cr) t->cap.cr = "\r";
1152 if (!t->cap.nel) t->cap.nel = "\r\n";
1154 /* Attribute capabilities. */
1155 if (ops->cap.intcap(&t->tty, CAP_XMC) > 0 || !t->cap.sgr0)
1156 CLEAR_CAPS(ATTRCAPS);
1158 if (t->cap.smul) t->tty.acaps |= TTACF_ULINE;
1159 if (t->cap.bold) t->tty.acaps |= TTACF_BOLD;
1160 if (t->cap.dim) t->tty.acaps |= TTACF_DIM;
1161 if (t->cap.sitm) t->tty.acaps |= TTACF_ITAL;
1162 if (t->cap.rev) t->tty.acaps |= TTACF_INVV;
1164 if (!t->cap.op || (!t->cap.setaf && !t->cap.setab))
1165 CLEAR_CAPS(COLOURCAPS);
1167 if (t->cap.setaf) t->tty.acaps |= TTACF_FG;
1168 if (t->cap.setab) t->tty.acaps |= TTACF_BG;
1169 t->tty.acaps |= TTACF_1BPC;
1170 if (t->cap.colors >= 16777216)
1171 t->tty.acaps |= TTACF_1BPC | TTACF_8BPC;
1172 else if (t->cap.colors >= 256)
1173 t->tty.acaps |= TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
1174 else if (t->cap.colors == 88)
1175 t->tty.acaps |= TTACF_1BPCBR | TTACF_4LPC | TTACF_8LGS;
1176 else if (t->cap.colors >= 16)
1177 t->tty.acaps |= TTACF_1BPCBR;
1178 if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
1179 env_colour_caps(&t->tty.acaps, 0);
1183 /* Motion capabilities. */
1184 if (ops->cap.boolcap(&t->tty, CAP_XHPA)) t->cap.hpa = 0;
1185 if (ops->cap.boolcap(&t->tty, CAP_XVPA)) t->cap.vpa = 0;
1186 if (!t->cap.cub1) t->cap.cub1 = "\b";
1187 if ((t->cap.cuf || t->cap.cuf1) &&
1188 (t->cap.cuu || t->cap.cuu1) &&
1189 (t->cap.cud || t->cap.cud1)) {
1190 t->tty.ocaps |= TTCF_RELMV;
1191 if (t->cap.vpa) t->tty.ocaps |= TTCF_ABSMV | TTCF_MIXMV;
1192 else if (t->cap.home) t->tty.ocaps |= TTCF_ABSMV;
1193 } else if (t->cap.cup ||
1194 (t->cap.hpa && t->cap.vpa) ||
1195 (t->cap.home && (t->cap.cuf || t->cap.cuf1)))
1196 t->tty.ocaps |= TTCF_ABSMV;
1198 /* Mode capabilities. */
1199 if (t->cap.smam && t->cap.rmam) t->tty.ocaps |= TTMF_AUTOM;
1200 if (t->cap.smcup && t->cap.rmcup) t->tty.ocaps |= TTMF_FSCRN;
1201 if (t->cap.smir && t->cap.rmir) t->tty.ocaps |= TTMF_INS;
1202 if (t->cap.smdc && t->cap.rmdc) t->tty.ocaps |= TTMF_DEL;
1203 if (t->cap.cnorm && t->cap.civis)
1204 { t->tty.ocaps |= TTMF_CVIS; t->tty.st.modes |= TTMF_CVIS; }
1205 if (ops->cap.boolcap(&t->tty, CAP_AM)) {
1206 t->tty.st.modes |= TTMF_AUTOM;
1207 if (ops->cap.boolcap(&t->tty, CAP_XENL)) t->tty.ocaps |= TTCF_MMARG;
1211 if (t->cap.ech) t->tty.ocaps |= TTCF_ERCH;
1212 if (t->cap.el1) t->tty.ocaps |= TTCF_ERBOL;
1213 if (t->cap.el) t->tty.ocaps |= TTCF_EREOL;
1214 if (t->cap.ed) t->tty.ocaps |= TTCF_EREOD;
1215 if (t->cap.clear || (t->cap.ed && t->cap.home)) t->tty.ocaps |= TTCF_ERDSP;
1217 /* Insertion and deletion. */
1218 if (t->cap.ich || t->cap.ich1) t->tty.ocaps |= TTCF_INSCH;
1219 if (t->cap.il || t->cap.il1) t->tty.ocaps |= TTCF_INSLN;
1220 if (t->cap.dch || t->cap.dch1) t->tty.ocaps |= TTCF_DELCH;
1221 if (t->cap.dl || t->cap.dl1) t->tty.ocaps |= TTCF_DELLN;
1225 { wd = ops->cap.intcap(&t->tty, CAP_WD); if (wd > 0) t->tty.wd = wd; }
1227 { ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
1230 /* Macros for formatting capabilities. */
1231 #define PUT0V(npad, cap_) \
1232 CHECK(ops->cap.put0(&t->tty, (npad), (cap_)))
1233 #define PUT1IV(npad, cap_, i0) \
1234 CHECK(ops->cap.put1i(&t->tty, (npad), (cap_), (i0)))
1235 #define PUT2IV(npad, cap_, i0, i1) \
1236 CHECK(ops->cap.put2i(&t->tty, (npad), (cap_), (i0), (i1)))
1238 #define PUT0(npad, name) PUT0V(npad, t->cap.name)
1239 #define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
1240 #define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
1242 /* --- @caps_setcolour@ --- *
1244 * Arguments: @struct tty_caps *t@ = extended control block pointer
1247 static int caps_setcolour(struct tty_caps *t,
1248 const char *cap, uint32 spc, uint32 clr)
1250 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1254 case TTCSPC_1BPC: case TTCSPC_1BPCBR: PUT1IV(0, cap, clr); break;
1255 case TTCSPC_4LPC: case TTCSPC_6LPC: PUT1IV(0, cap, clr + 16); break;
1256 case TTCSPC_8LGS: PUT1IV(0, cap, clr + 80); break;
1257 case TTCSPC_24LGS: PUT1IV(0, cap, clr + 232); break;
1260 /* There's an unfortunate ambiguity in the %|setaf|% conventions. The
1261 * first eight colours should be dark shades of blue, but in fact
1262 * they're interpreted as the one-bit-per-channel basic colours by
1263 * common `terminfo' settings. Notice and kludge by adding a little
1264 * red. This will tinge the colour magenta, but all such colours are
1265 * so dark as to be effectively black anyway, so I doubt that this will
1268 if (spc == TTCSPC_8BPC && clr < 8) clr += 65536;
1269 PUT1IV(0, cap, clr); break;
1271 /* case TTCSPC_NONE: */
1272 default: rc = -1; goto end;
1279 static int caps_setattr_internal(struct tty_caps *t,
1280 const struct tty_attr *a)
1282 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1286 /* Work out what needs doing. */
1287 diff = a->f ^ t->tty.st.attr.f;
1289 /* Some terminals might not be able to clear individual attributes that
1290 * they can set, and some capabilities for turning attributes on don't even
1291 * have a corresponding attribute for turning them off again individually,
1292 * so we have to use %|sgr0|% to start from scratch. Of course, if we need
1293 * to do that, we need to restore the other active attributes, so we must
1296 if (((diff&TTAF_LNMASK) && !(a->f&TTAF_LNMASK) && !t->cap.rmul) ||
1297 ((diff&TTAF_WTMASK) && !(a->f&TTAF_WTMASK)) ||
1298 ((diff&~a->f&TTAF_ITAL) && !t->cap.ritm) ||
1299 (diff&~a->f&TTAF_INVV))
1300 { PUT0(0, sgr0); diff = a->f; }
1303 if (diff&TTAF_LNMASK)
1304 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1305 case TTLN_NONE: PUT0(0, rmul); break;
1306 case TTLN_ULINE: PUT0(0, smul); break;
1307 /* case TTLN_UULINE: */
1308 /* case TTLN_STRIKE: */
1309 default: rc = -1; goto end;
1313 if (diff&TTAF_WTMASK)
1314 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1315 /* case TTWT_MED: */
1316 case TTWT_BOLD: PUT0(0, bold); break;
1317 case TTWT_DIM: PUT0(0, dim); break;
1318 default: rc = -1; goto end;
1321 /* Other text effects. */
1322 if (diff&TTAF_ITAL) {
1323 if (a->f&TTAF_ITAL) PUT0(0, sitm);
1326 if (diff&a->f&TTAF_INVV) PUT0(0, rev);
1329 if (((diff&TTAF_FGSPCMASK) && !(a->f&TTAF_FGSPCMASK)) ||
1330 ((diff&TTAF_BGSPCMASK) && !(a->f&TTAF_BGSPCMASK))) {
1331 /* There's no capability string for resetting just the foreground
1332 * or background colours to the defaults, so deal with that here.
1336 diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
1337 (a->f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
1339 if ((diff&TTAF_FGSPCMASK) || a->fg != t->tty.st.attr.fg)
1340 CHECK(caps_setcolour(t, t->cap.setaf,
1341 (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
1342 if ((diff&TTAF_BGSPCMASK) || a->bg != t->tty.st.attr.bg)
1343 CHECK(caps_setcolour(t, t->cap.setab,
1344 (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
1349 t->tty.st.attr = *a; return (rc);
1352 static int caps_setattr(struct tty *tty,
1353 const struct gprintf_ops *gops, void *go,
1354 const struct tty_attr *a)
1356 struct tty_caps *t = (struct tty_caps *)tty;
1357 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1360 ops->cap.prepout(&t->tty, gops, go);
1361 rc = caps_setattr_internal(t, a);
1362 if (ops->cap.flush(&t->tty)) rc = -1;
1366 static int caps_setmodes(struct tty *tty,
1367 const struct gprintf_ops *gops, void *go,
1368 uint32 modes_bic, uint32 modes_xor)
1370 struct tty_caps *t = (struct tty_caps *)tty;
1371 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1376 /* Figure out which modes to set. */
1377 modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
1378 diff = modes ^ t->tty.st.modes;
1380 /* Prepare output. */
1381 ops->cap.prepout(&t->tty, gops, go);
1383 /* Automatic margins. */
1384 if (diff&TTMF_AUTOM) {
1385 if (modes&TTMF_AUTOM) PUT0(0, smam);
1390 if (diff&TTMF_FSCRN) {
1391 if (modes&TTMF_FSCRN) PUT0(0, smcup);
1392 else PUT0(0, rmcup);
1395 /* Cursor visibility. */
1396 if (diff&TTMF_CVIS) {
1397 if (modes&TTMF_CVIS) PUT0(0, civis);
1398 else PUT0(0, cnorm);
1402 if (diff&TTMF_INS) {
1403 cap = modes&TTMF_INS ? t->cap.smir : t->cap.rmir;
1404 if (cap) PUT0V(0, cap);
1405 else if (!t->cap.ich) { rc = -1; goto end; }
1408 /* Delete characters. */
1409 if (diff&TTMF_DEL) {
1410 cap = modes&TTMF_DEL ? t->cap.smdc : t->cap.rmdc;
1411 if (cap) PUT0V(0, cap);
1412 else if (!t->cap.dch && !t->cap.dch1) { rc = -1; goto end; }
1418 if (ops->cap.flush(&t->tty)) rc = -1;
1419 t->tty.st.modes = modes; return (rc);
1422 #define CIF_PADMUL 1u
1423 static int caps_iterate(struct tty_caps *t,
1424 const char *cap1, const char *capn,
1425 unsigned f, unsigned npad, unsigned n)
1427 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1431 if (cap1 && (n == 1 || !capn))
1432 while (n--) PUT0V(npad, cap1);
1434 max = npad && (f&CIF_PADMUL) ? INT_MAX/npad : INT_MAX;
1436 nn = n; if (nn > max) nn = max;
1437 PUT1IV(npad, capn, nn);
1446 static int caps_move_relative(struct tty_caps *t,
1448 const char *fw1, const char *fwn,
1449 const char *rv1, const char *rvn)
1451 const char *mv1, *mvn;
1453 if (!delta) return (0);
1454 else if (delta > 0) { mv1 = fw1; mvn = fwn; }
1455 else { mv1 = rv1; mvn = rvn; delta = - delta; }
1456 return (caps_iterate(t, mv1, mvn, 0, 0, delta));
1459 static int caps_move(struct tty *tty,
1460 const struct gprintf_ops *gops, void *go,
1461 unsigned orig, int y, int x)
1463 struct tty_caps *t = (struct tty_caps *)tty;
1464 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1468 ops->cap.prepout(&t->tty, gops, go);
1470 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
1473 if (!t->cap.msgr && a.f) { PUT0(0, sgr0); t->tty.st.attr.f = 0; }
1477 if (t->cap.home && !x && !y)
1479 else if (t->cap.cup)
1480 PUT2I(1, cup, y, x);
1481 else if (t->cap.vpa) {
1487 CHECK(caps_move_relative(t, x,
1488 t->cap.cuf1, t->cap.cuf,
1489 t->cap.cub1, t->cap.cub));
1491 } else if (t->cap.home) {
1493 CHECK(caps_iterate(t, t->cap.cud1, t->cap.cud, 0, 1, y));
1494 CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1496 { rc = -1; goto end; }
1500 CHECK(caps_move_relative(t, y,
1501 t->cap.cud1, t->cap.cud,
1502 t->cap.cuu1, t->cap.cuu));
1503 CHECK(caps_move_relative(t, x,
1504 t->cap.cuf1, t->cap.cuf,
1505 t->cap.cub1, t->cap.cub));
1508 case TTOF_XHOME | TTOF_YCUR:
1509 if (x == 0 && y == 1)
1512 CHECK(caps_move_relative(t, y,
1513 t->cap.cud1, t->cap.cud,
1514 t->cap.cuu1, t->cap.cuu));
1515 if (t->cap.hpa && x)
1519 CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1524 case TTOF_XCUR | TTOF_YHOME:
1526 CHECK(caps_move_relative(t, x,
1527 t->cap.cuf1, t->cap.cuf,
1528 t->cap.cub1, t->cap.cub));
1536 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
1538 if (!t->cap.msgr && a.f)
1539 CHECK(caps_setattr_internal(t, &a));
1543 if (ops->cap.flush(&t->tty)) rc = -1;
1547 static int caps_repeat(struct tty *tty,
1548 const struct gprintf_ops *gops, void *go,
1551 struct tty_caps *t = (struct tty_caps *)tty;
1552 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1556 ops->cap.prepout(&t->tty, gops, go);
1558 CHECK(stupid_repeat(tty, gops, go, ch, n));
1562 nn = n; if (nn > INT_MAX) nn = INT_MAX;
1563 PUT2I((nn + wd - 1)/wd, rep, ch, nn);
1569 if (ops->cap.flush(&t->tty)) rc = -1;
1573 static int caps_erase(struct tty *tty,
1574 const struct gprintf_ops *gops, void *go,
1577 struct tty_caps *t = (struct tty_caps *)tty;
1578 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1581 ops->cap.prepout(&t->tty, gops, go);
1583 switch (f&(TTEF_BEGIN | TTEF_END)) {
1586 case TTEF_BEGIN | TTEF_END:
1587 if (t->cap.clear) PUT0(t->tty.ht, clear);
1588 else { PUT0(1, home); PUT0(t->tty.ht, ed); }
1591 PUT0(t->tty.ht, ed);
1598 if (f&TTEF_BEGIN) PUT0(1, el1);
1599 if (f&TTEF_END) PUT0(1, el);
1603 if (ops->cap.flush(&t->tty)) rc = -1;
1607 static int caps_erch(struct tty *tty,
1608 const struct gprintf_ops *gops, void *go,
1611 struct tty_caps *t = (struct tty_caps *)tty;
1612 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1615 ops->cap.prepout(&t->tty, gops, go);
1616 if (n) PUT1I(1, ech, n);
1619 if (ops->cap.flush(&t->tty)) rc = -1;
1623 static int caps_ins(struct tty *tty,
1624 const struct gprintf_ops *gops, void *go,
1625 unsigned f, unsigned n)
1627 struct tty_caps *t = (struct tty_caps *)tty;
1628 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1631 ops->cap.prepout(&t->tty, gops, go);
1633 CHECK(caps_iterate(t, t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
1635 CHECK(caps_iterate(t, t->cap.ich1, t->cap.ich, 0, 1, n));
1638 if (ops->cap.flush(&t->tty)) rc = -1;
1642 static int caps_inch(struct tty *tty,
1643 const struct gprintf_ops *gops, void *go,
1646 struct tty_caps *t = (struct tty_caps *)tty;
1647 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1650 ops->cap.prepout(&t->tty, gops, go);
1651 if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
1652 { rc = -1; goto end; }
1653 if (t->cap.ich) PUT0(1, ich);
1654 CHECK(gops->putch(go, ch));
1655 if (t->cap.ip) PUT0(1, ip);
1658 if (ops->cap.flush(&t->tty)) rc = -1;
1662 static int caps_del(struct tty *tty,
1663 const struct gprintf_ops *gops, void *go,
1664 unsigned f, unsigned n)
1666 struct tty_caps *t = (struct tty_caps *)tty;
1667 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1670 ops->cap.prepout(&t->tty, gops, go);
1673 CHECK(caps_iterate(t, t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
1675 CHECK(caps_iterate(t, t->cap.dch1, t->cap.dch, 0, 1, n));
1679 if (ops->cap.flush(&t->tty)) rc = -1;
1690 #define TTY_CAPOPS \
1691 caps_setattr, caps_setmodes, \
1692 caps_move, caps_repeat, \
1693 caps_erase, caps_erch, caps_ins, caps_inch, caps_del
1697 /*----- Termcap -----------------------------------------------------------*/
1701 struct tty_termcapslots {
1702 char termbuf[4096], capbuf[4096], *capcur;
1704 struct tty_termcap { TTY_CAPSPFX; struct tty_termcapslots tc; };
1705 union tty_termcapu { struct tty_termcap tc; TTY_CAPSUSFX; };
1707 static int termcap_boolcap(struct tty *tty,
1708 int uix, const char *info, const char *cap)
1712 p = tgetflag(cap); assert(p >= 0);
1716 static int termcap_intcap(struct tty *tty,
1717 int uix, const char *info, const char *cap)
1721 n = tgetnum(cap); assert(n >= -1);
1725 static const char *termcap_strcap(struct tty *tty,
1726 int uix, const char *info,
1729 struct tty_termcap *t = (struct tty_termcap *)tty;
1732 p = tgetstr(cap, &t->tc.capcur); assert(p != (const char *)-1);
1736 static int termcap_put0(struct tty *tty,
1737 unsigned npad, const char *cap)
1739 if (!cap) return (-1);
1740 return (tputs(cap, npad, caps_putch));
1743 static int termcap_put1i(struct tty *tty,
1744 unsigned npad, const char *cap, int i0)
1746 if (!cap) return (-1);
1747 return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
1750 static int termcap_put2i(struct tty *tty,
1751 unsigned npad, const char *cap, int i0, int i1)
1753 if (!cap) return (-1);
1754 return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
1757 static const union tty_capopsu termcap_ops = { {
1758 { caps_release, TTY_CAPOPS },
1759 { termcap_boolcap, termcap_intcap, termcap_strcap,
1760 caps_prepout, caps_flush,
1761 termcap_put0, termcap_put1i, termcap_put2i }
1764 static struct tty *termcap_init(FILE *fp)
1766 union tty_termcapu *u = 0; struct tty *ret = 0;
1769 if (caps_claim()) goto end;
1770 term = getenv("TERM"); if (!term) goto end;
1772 if (tgetent(u->tc.tc.termbuf, term) < 1) goto end;
1773 u->tc.tc.capcur = u->tc.tc.capbuf;
1774 u->tty.ops = &termcap_ops.tty;
1775 common_init(&u->tty, fp);
1777 ret = &u->tty; u = 0;
1779 xfree(u); global_lock = ret; return (ret);
1784 /*----- Terminfo ----------------------------------------------------------*/
1786 #ifdef HAVE_TERMINFO
1788 static int terminfo_boolcap(struct tty *tty,
1789 int uix, const char *info, const char *cap)
1793 p = tigetflag(info); assert(p >= 0);
1797 static int terminfo_intcap(struct tty *tty,
1798 int uix, const char *info, const char *cap)
1802 n = tigetnum(info); assert(n >= -1);
1806 static const char *terminfo_strcap(struct tty *tty,
1807 int uix, const char *info,
1812 p = tigetstr(info); assert(p != (const char *)-1);
1816 static int terminfo_put0(struct tty *tty,
1817 unsigned npad, const char *cap)
1819 if (!cap) return (-1);
1820 return (tputs(cap, npad, caps_putch));
1823 static int terminfo_put1i(struct tty *tty,
1824 unsigned npad, const char *cap, int i0)
1826 if (!cap) return (-1);
1827 return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
1830 static int terminfo_put2i(struct tty *tty,
1831 unsigned npad, const char *cap, int i0, int i1)
1833 if (!cap) return (-1);
1834 return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
1837 static const union tty_capopsu terminfo_ops = { {
1838 { caps_release, TTY_CAPOPS },
1839 { terminfo_boolcap, terminfo_intcap, terminfo_strcap,
1840 caps_prepout, caps_flush,
1841 terminfo_put0, terminfo_put1i, terminfo_put2i }
1844 static struct tty *terminfo_init(FILE *fp)
1846 union tty_capsu *u = 0; struct tty *ret = 0;
1849 if (caps_claim()) goto end;
1850 if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
1852 u->tty.ops = &terminfo_ops.tty;
1853 common_init(&u->tty, fp);
1855 ret = &u->tty; u = 0;
1857 xfree(u); global_lock = ret; return (ret);
1862 /*----- Unibilium ---------------------------------------------------------*/
1864 #ifdef HAVE_UNIBILIUM
1866 struct tty_unibislots {
1868 unibi_var_t dy[26], st[26];
1869 const struct gprintf_ops *gops; void *go;
1870 char buf[4096]; size_t n;
1873 struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
1874 union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
1876 static int termunibi_boolcap(struct tty *tty,
1877 int uix, const char *info, const char *cap)
1879 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1881 return (unibi_get_bool(t->u.ut, uix));
1884 static int termunibi_intcap(struct tty *tty,
1885 int uix, const char *info, const char *cap)
1887 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1889 return (unibi_get_num(t->u.ut, uix));
1892 static const char *termunibi_strcap(struct tty *tty,
1893 int uix, const char *info,
1896 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1898 return (unibi_get_str(t->u.ut, uix));
1901 struct termunibi_outctx {
1902 struct tty_unibilium *t;
1905 static void termunibi_putm(void *ctx, const char *p, size_t sz)
1907 struct tty_unibilium *t = ctx;
1910 n = sizeof(t->u.buf) - t->u.n;
1912 { memcpy(t->u.buf + t->u.n, p, sz); t->u.n += sz; }
1914 if (n) { memcpy(t->u.buf + t->u.n, p, n); p += n; sz -= n; }
1916 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1918 if (sz <= sizeof(t->u.buf)) break;
1919 memcpy(t->u.buf, p, sizeof(t->u.buf));
1920 p += sizeof(t->u.buf); sz -= sizeof(t->u.buf);
1922 memcpy(t->u.buf, p, sz); t->u.n = sz;
1926 static void termunibi_pad(void *ctx, size_t ms, int mulp, int forcep)
1928 struct tty_unibilium *t = ctx;
1933 /* Based on 7 data bits, 1 stop bit, 1 parity bit. */
1934 #define BITS_PER_KB 9000
1936 if (forcep || (t->tty.baud >= t->cap.pb && !t->cap.xon)) {
1938 tv.tv_sec = ms/1000; tv.tv_usec = 1000*(ms%1000);
1940 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1944 if (t->tty.fpout) fflush(t->tty.fpout);
1945 select(0, 0, 0, 0, &tv);
1947 pc = t->cap.pad ? *t->cap.pad : 0;
1948 sz = (ms*t->tty.baud + BITS_PER_KB - 1)/BITS_PER_KB;
1949 n = sizeof(t->u.buf) - t->u.n;
1951 { memset(t->u.buf + t->u.n, pc, sz); t->u.n += sz; }
1953 if (n) { memset(t->u.buf + t->u.n, pc, sz); sz -= n; }
1954 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1956 if (sz < sizeof(t->u.buf))
1957 memset(t->u.buf, pc, sz);
1959 memset(t->u.buf, pc, sizeof(t->u.buf));
1961 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1963 sz -= sizeof(t->u.buf);
1964 } while (sz > sizeof(t->u.buf));
1974 static void termunibi_prepout(struct tty *tty,
1975 const struct gprintf_ops *gops, void *go)
1977 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1979 assert(!t->u.n); t->u.gops = gops; t->u.go = go;
1982 static int termunibi_flush(struct tty *tty)
1984 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1988 if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) rc = -1;
1991 t->u.err = 0; return (rc);
1994 static int termunibi_put0(struct tty *tty,
1995 unsigned npad, const char *cap)
1997 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2000 if (!cap) return (-1);
2001 unibi_format(t->u.dy, t->u.st, cap, arg,
2007 static int termunibi_put1i(struct tty *tty,
2008 unsigned npad, const char *cap, int i0)
2010 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2013 if (!cap) return (-1);
2014 arg[0] = unibi_var_from_num(i0);
2015 unibi_format(t->u.dy, t->u.st, cap, arg,
2021 static int termunibi_put2i(struct tty *tty,
2023 const char *cap, int i0, int i1)
2025 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2028 if (!cap) return (-1);
2029 arg[0] = unibi_var_from_num(i0);
2030 arg[1] = unibi_var_from_num(i1);
2031 unibi_format(t->u.dy, t->u.st, cap, arg,
2037 static void termunibi_release(struct tty *tty)
2039 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2041 unibi_destroy(t->u.ut);
2044 static const union tty_capopsu termunibi_ops = { {
2045 { termunibi_release, TTY_CAPOPS },
2046 { termunibi_boolcap, termunibi_intcap, termunibi_strcap,
2047 termunibi_prepout, termunibi_flush,
2048 termunibi_put0, termunibi_put1i, termunibi_put2i }
2051 static struct tty *termunibi_init(FILE *fp)
2053 union tty_unibiliumu *u = 0; struct tty *ret = 0;
2057 term = getenv("TERM"); if (!term) goto end;
2058 ut = unibi_from_term(term); if (!ut) goto end;
2060 u->tty.ops = &termunibi_ops.tty;
2061 u->u.u.ut = ut; ut = 0;
2062 u->u.u.n = 0; u->u.u.err = 0;
2063 common_init(&u->tty, fp);
2065 ret = &u->tty; u = 0;
2067 xfree(u); if (ut) unibi_destroy(ut);
2073 /*----- ANSI terminals ----------------------------------------------------*/
2075 struct tty_ansislots {
2077 #define TAF_CNCATTR 1u /* attributes can be cancelled */
2078 #define TAF_EDITN 2u /* insert/delete multiple */
2079 #define TAF_SEMI 4u /* semicolons in CSI 38 m colour */
2081 struct tty_ansi { TTY_BASEPFX; struct tty_ansislots ansi; };
2082 union tty_ansiu { struct tty_ansi ansi; TTY_BASEUSFX; };
2084 /* Control sequences.
2086 * * CUP: \33 [ Y ; X H `cursor position' [vt100]
2088 * * CUU/CUD/CUR/CUL: \33 [ N A/B/C/D `cursor up/down/right/left'
2090 * * DCH: \33 [ N P `delete character' [vt220]
2091 * (single char only in vt102?)
2093 * * DL: \33 [ N M `delete line' [vt220]
2094 * (single line only in vt102?)
2096 * * ECH: \33 [ N X `erase characters' [vt220]
2098 * * ED: \33 [ P J `erase in display'
2099 * P = 0 erase to end-of-screen [vt100]
2100 * P = 1 erase from start-of-screen [vt100]
2101 * P = 2 erase entire screen [vt100]
2103 * * EL: \33 [ P K `erase in line'
2104 * P = 0 erase to end-of-line [vt100]
2105 * P = 1 erase from start-of-line [vt100]
2106 * P = 2 erase entire line [vt100]
2108 * * HPA/VPA: \33 [ I G/d `horizontal/vertical position
2109 * absolute' [ecma48-4]
2111 * * ICH: \33 [ N @ `insert character' [vt220]
2112 * (single char only in vt102?)
2114 * * IL: \33 [ N L `insert line' [vt220]
2115 * (single line only in vt102?)
2117 * * SGR: \33 [ P ; ... m `select graphics rendition'
2118 * P = 0 cancel all attributes [vt100]
2119 * P = 1 bold [vt100]
2120 * P = 2 dim [ecma48-4]
2121 * P = 3 italics [ecma48-4]
2122 * P = 4 underline [vt100]
2123 * P = 7 inverse video [vt100]
2124 * P = 9 strikeout [ecma48-4]
2125 * P = 21 double underline [ecma48-4]
2126 * P = 22 cancal bold/dim [vt220]
2127 * P = 24 cancel underline [vt220]
2128 * P = 27 cancel inverse video [vt220]
2129 * P = 30 + 4 R + 2 G + B set 1BPC foreground [ecma48-4]
2130 * P = 38 : 2 : ? : R : G : B set foreground [iso8613-6]
2131 * P = 38 : 5 : N set foreground [iso8613-6, xterm]
2132 * P = 39 cancel foreground [ecma48-4]
2133 * P = 40--49 as above, for background
2134 * P = 90 + 4 R + 2 G + B set bright 1BPC foreground [xterm]
2135 * P = 100 + 4 R + 2 G + B set bright 1BPC background [xterm]
2137 * * SM/RM: \33 [ P ; ... h/l `set/reset modes'
2138 * M = 4 insert [vt220]
2140 * * SM, RM: \33 [ ? P ; ... h/l `set/reset private modes'
2141 * M = 7 auto right margin [vt100]
2142 * M = 25 visible cursor [vt220]
2143 * M = 1049 alternate screen [xterm]
2145 * * \33 [ P ; X ; Y t `window manipulation'
2146 * P = 22, X = 0 save title and icon [xterm]
2147 * P = 23, X = 0 restore title and icon [xterm]
2150 static void ansi_release(struct tty *tty) { ; }
2152 #define PUTCH(ch) CHECK(gops->putch(go, (ch)))
2153 #define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
2155 if (!(f&TAF_SEMI)) f |= TAF_SEMI; \
2159 static int ansi_setcolour(struct tty_ansi *t, unsigned *f_inout,
2160 const struct gprintf_ops *gops, void *go,
2162 uint32 spc, uint32 clr)
2164 unsigned f = *f_inout;
2169 SEMI; CHECK(gprintf(gops, go, "%d", norm + 9));
2172 SEMI; CHECK(gprintf(gops, go, "%d", norm + clr));
2175 SEMI; CHECK(gprintf(gops, go, "%d", br + (clr&~TT1BPC_BRI)));
2177 case TTCSPC_4LPC: case TTCSPC_6LPC:
2179 if (t->ansi.f&TAF_SEMI)
2180 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 16));
2182 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 16));
2186 if (t->ansi.f&TAF_SEMI)
2187 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 80));
2189 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 80));
2193 if (t->ansi.f&TAF_SEMI)
2194 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 232));
2196 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 232));
2200 if (t->ansi.f&TAF_SEMI)
2201 CHECK(gprintf(gops, go, "%d;2;%d;%d;%d", norm + 8,
2202 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
2204 CHECK(gprintf(gops, go, "%d:2::%d:%d:%d", norm + 8,
2205 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
2213 *f_inout = f; return (rc);
2216 static int ansi_setattr(struct tty *tty,
2217 const struct gprintf_ops *gops, void *go,
2218 const struct tty_attr *a)
2220 struct tty_ansi *t = (struct tty_ansi *)tty;
2223 unsigned z, c, f = 0;
2225 diff = a->f ^ t->tty.st.attr.f;
2226 if (!diff && a->fg == t->tty.st.attr.fg && a->bg == t->tty.st.attr.bg)
2230 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
2231 if (CLEARP(TTAF_LNMASK)) c += 3;
2232 if (CLEARP(TTAF_WTMASK)) c += 3;
2233 if (diff&~a->f&TTAF_INVV) c += 3;
2234 if (diff&~a->f&TTAF_STRIKE) c += 3;
2235 if (diff&~a->f&TTAF_ITAL) c += 3;
2236 if (CLEARP(TTAF_FGSPCMASK)) c += 3;
2237 if (CLEARP(TTAF_BGSPCMASK)) c += 3;
2241 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
2242 case TTLN_ULINE: z += 2; break;
2243 case TTLN_UULINE: z += 3; break;
2245 if (a->f&TTAF_WTMASK) z += 2;
2246 if (a->f&TTAF_INVV) z += 2;
2247 if (a->f&TTAF_STRIKE) z += 2;
2248 if (a->f&TTAF_ITAL) z += 2;
2249 #define COLOURCOST(col) do { \
2250 switch ((a->f&TTAF_##col##SPCMASK) >> TTAF_##col##SPCSHIFT) { \
2251 case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break; \
2252 case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break; \
2253 case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break; \
2254 case TTCSPC_8BPC: z += 16; break; \
2257 COLOURCOST(FG); COLOURCOST(BG);
2263 { SEMI; diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0; }
2265 if (diff&TTAF_LNMASK)
2266 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
2267 case TTLN_NONE: SEMI; PUTLIT("24"); break;
2268 case TTLN_ULINE: SEMI; PUTCH('4'); break;
2269 case TTLN_UULINE: SEMI; PUTLIT("21"); break;
2270 default: rc = -1; goto end;
2273 if (diff&TTAF_WTMASK)
2274 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
2275 case TTWT_MED: SEMI; PUTLIT("22"); break;
2276 case TTWT_BOLD: SEMI; PUTCH('1'); break;
2277 case TTWT_DIM: SEMI; PUTCH('2'); break;
2278 default: rc = -1; goto end;
2282 { SEMI; if (a->f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
2283 if (diff&TTAF_STRIKE)
2284 { SEMI; if (a->f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
2286 { SEMI; if (a->f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
2288 if (diff&TTAF_FGSPCMASK || a->fg != tty->st.attr.fg)
2289 CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
2290 (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
2291 if (diff&TTAF_BGSPCMASK || a->bg != tty->st.attr.bg)
2292 CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
2293 (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
2297 t->tty.st.attr = *a; return (rc);
2301 static int ansi_setmodes(struct tty *tty,
2302 const struct gprintf_ops *gops, void *go,
2303 uint32 modes_bic, uint32 modes_xor)
2308 /* Figure out which modes to set. */
2309 modes = (tty->st.modes&~modes_bic) ^ modes_xor;
2310 diff = modes ^ tty->st.modes;
2312 if (diff&TTMF_AUTOM) {
2313 if (modes&TTMF_AUTOM) PUTLIT("\33[?7h");
2314 else PUTLIT("\33[?7l");
2317 if (diff&TTMF_FSCRN) {
2318 if (modes&TTMF_FSCRN) PUTLIT("\33[?1049h\33[22;0;0t");
2319 else PUTLIT("\33[?1049l\33[23;0;0t");
2322 if (diff&TTMF_CVIS) {
2323 if (modes&TTMF_CVIS) PUTLIT("\33[?25h");
2324 else PUTLIT("\33[?25l");
2327 if (diff&TTMF_INS) {
2328 if (modes&TTMF_INS) PUTLIT("\33[4h");
2329 else PUTLIT("\33[4l");
2334 tty->st.modes = modes;
2338 static int ansi_move(struct tty *tty,
2339 const struct gprintf_ops *gops, void *go,
2340 unsigned orig, int y, int x)
2344 if (orig == TTORG_HOME) {
2346 if (!y) PUTLIT("\33[H");
2347 else CHECK(gprintf(gops, go, "\33[%dH", y + 1));
2349 if (!y) CHECK(gprintf(gops, go, "\33[;%dH", x + 1));
2350 else CHECK(gprintf(gops, go, "\33[%d,%dH", y + 1, x + 1));
2352 } else if (orig == (TTOF_XHOME | TTOF_YCUR) && x == 0 && y == 1)
2355 if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
2356 else if (y == -1) PUTLIT("\33[A");
2357 else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
2358 else if (y == +1) PUTLIT("\33[B"); /* not %|^J|%! */
2359 else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
2360 if (!(orig&TTOF_XCUR)) {
2363 else if (tty->ocaps&TTCF_MIXMV)
2364 CHECK(gprintf(gops, go, "\33[%dG", x + 1));
2366 CHECK(gprintf(gops, go, "\r\33[%dC", x));
2368 if (x == -1) PUTCH('\b');
2369 else if (x < 0) CHECK(gprintf(gops, go, "\33[%dD", -x));
2370 else if (x == +1) PUTLIT("\33[C");
2371 else if (x > 0) CHECK(gprintf(gops, go, "\33[%dC", x));
2379 static int ansi_erase(struct tty *tty,
2380 const struct gprintf_ops *gops, void *go,
2386 switch (f&(TTEF_BEGIN | TTEF_END)) {
2388 case TTEF_BEGIN: PUTLIT("\33[1J"); break;
2389 case TTEF_END: PUTLIT("\33[J"); break;
2390 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2J"); break;
2393 switch (f&(TTEF_BEGIN | TTEF_END)) {
2395 case TTEF_BEGIN: PUTLIT("\33[1K"); break;
2396 case TTEF_END: PUTLIT("\33[K"); break;
2397 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2K"); break;
2404 static int ansi_erch(struct tty *tty,
2405 const struct gprintf_ops *gops, void *go,
2410 if (n == 1) PUTLIT("\33[X");
2411 else if (n) CHECK(gprintf(gops, go, "\33[%uX", n));
2417 static int ansi_ins(struct tty *tty,
2418 const struct gprintf_ops *gops, void *go,
2419 unsigned f, unsigned n)
2424 if (n == 1) PUTLIT("\33[L");
2425 else if (n) CHECK(gprintf(gops, go, "\33[%uL", n));
2427 if (n == 1) PUTLIT("\33[@");
2428 else if (n) CHECK(gprintf(gops, go, "\33[%u@", n));
2435 static int ansi_inch(struct tty *tty,
2436 const struct gprintf_ops *gops, void *go,
2439 if (!(tty->st.modes&TTMF_INS)) return (-1);
2440 else return (gops->putch(go, ch));
2443 static int ansi_del(struct tty *tty,
2444 const struct gprintf_ops *gops, void *go,
2445 unsigned f, unsigned n)
2450 if (n == 1) PUTLIT("\33[M");
2451 else if (n) CHECK(gprintf(gops, go, "\33[%uM", n));
2453 if (n == 1) PUTLIT("\33[P");
2454 else if (n) CHECK(gprintf(gops, go, "\33[%uP", n));
2467 static const struct tty_ops ansi_ops = {
2469 ansi_setattr, ansi_setmodes,
2470 ansi_move, stupid_repeat,
2471 ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del
2474 static struct tty *ansi_init(FILE *fp)
2477 #define COLS_8 (TTACF_FG | TTACF_BG | TTACF_1BPC)
2478 #define COLS_16 (COLS_8 | TTACF_1BPCBR)
2479 #define COLS_88 (COLS_16 | TTACF_4LPC | TTACF_8LGS)
2480 #define COLS_256 (COLS_16 | TTACF_6LPC | TTACF_24LGS)
2481 #define COLS_16M (COLS_256 | TTACF_8BPC)
2483 #define EDIT_OPS (TTCF_ERCH | \
2484 TTCF_DELCH | TTCF_DELLN | \
2485 TTCF_INSCH | TTCF_INSLN)
2487 static const struct flagmap {
2489 uint32 acaps, ocaps;
2492 { "dim", TTACF_DIM, 0, 0 },
2493 { "uuline", TTACF_UULINE, 0, 0 },
2494 { "strike", TTACF_STRIKE, 0, 0 },
2495 { "ital", TTACF_ITAL, 0, 0 },
2496 { "cvis", 0, TTMF_CVIS, 0 },
2497 { "fscrn", 0, TTMF_FSCRN, 0 },
2498 { "insmode", 0, TTMF_INS, 0 },
2499 { "hvpa" , 0, TTCF_MIXMV, 0 },
2500 { "edit", 0, EDIT_OPS, 0 },
2501 { "cncattr", 0, 0, TAF_CNCATTR },
2502 { "editn", 0, 0, TAF_EDITN },
2503 { "semi", 0, 0, TAF_SEMI },
2509 static const struct kw { const char *name; uint32 val; }
2515 { "256", COLS_256 },
2516 { "16m", COLS_16M },
2520 static const struct enummap {
2523 const struct kw *kw;
2525 { "colours", TTACF_CSPCMASK, kw_colours },
2530 static const struct termmap {
2532 unsigned acaps, ocaps, tf;
2535 #define VT100_ACAPS (TTACF_ULINE | TTACF_BOLD | TTACF_INVV)
2536 #define VT100_OCAPS (TTMF_AUTOM | \
2537 TTCF_RELMV | TTCF_ABSMV | \
2539 TTCF_ERBOL | TTCF_EREOL | \
2540 TTCF_ERBOD | TTCF_EREOD | TTCF_ERDSP)
2541 #define VT100_TF (0)
2543 #define VT102_ACAPS (VT100_ACAPS)
2544 #define VT102_OCAPS (VT100_OCAPS | \
2546 TTCF_INSCH | TTCF_INSLN | TTCF_DELCH | TTCF_DELLN)
2547 #define VT102_TF (VT100_TF)
2549 #define VT220_ACAPS (VT102_ACAPS)
2550 #define VT220_OCAPS (VT102_OCAPS | TTMF_CVIS | TTCF_ERCH)
2551 #define VT220_TF (VT102_TF | TAF_CNCATTR | TAF_EDITN)
2553 #define ECMA48_ACAPS (VT220_ACAPS | TTACF_DIM)
2554 #define ECMA48_OCAPS (VT220_OCAPS | TTCF_MIXMV)
2555 #define ECMA48_TF (VT220_TF)
2557 #define XTERM_ACAPS (ECMA48_ACAPS)
2558 #define XTERM_OCAPS (ECMA48_OCAPS | TTMF_FSCRN)
2559 #define XTERM_TF (ECMA48_TF)
2561 #define STRIKE TTACF_STRIKE
2562 #define ITAL TTACF_ITAL
2563 #define SEMI TAF_SEMI
2565 #define T(pat, base, cols, acaps, ocaps, tf) \
2567 base##_ACAPS | COLS_##cols | (acaps), \
2568 base##_OCAPS | (ocaps), base##_TF | (tf) }
2570 T("color_xterm", XTERM, 8, STRIKE | ITAL, 0, 0),
2572 T("gnome", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
2573 /*T("gonme-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
2575 T("linux", XTERM, 16, 0, 0, 0),
2577 T("putty", XTERM, 16M, 0, 0, SEMI),
2579 T("vt100*", VT100, NO, 0, 0, 0),
2580 T("vt102*", VT102, NO, 0, 0, 0),
2581 T("vt[2-5][0-9][0-9]*", VT220, NO, 0, 0, 0),
2583 T("vte", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
2584 /*T("vte-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
2586 T("win", XTERM, 16M, 0, 0, SEMI),
2588 T("xterm", XTERM, 16M, STRIKE | ITAL, 0, 0),
2589 T("xterm-color", XTERM, 8, STRIKE | ITAL, 0, 0),
2590 T("xterm-16color", XTERM, 16, STRIKE | ITAL, 0, 0),
2591 T("xterm-88color", XTERM, 88, STRIKE | ITAL, 0, SEMI),
2592 T("xterm-256color", XTERM, 256, STRIKE | ITAL, 0, SEMI),
2593 T("xterm-direct", XTERM, 16M, STRIKE | ITAL, 0, 0),
2594 T("xterm-*", XTERM, 16M, STRIKE | ITAL, 0, 0),
2596 /*T("*-color", XTERM, 16, 0, 0, 0),*/
2597 /*T("*-16color", XTERM, 16, 0, 0, 0),*/
2598 T("*-88color", XTERM, 88, 0, 0, SEMI),
2599 T("*-256color", XTERM, 256, 0, 0, SEMI),
2600 T("*-direct", XTERM, 16M, 0, 0, SEMI),
2602 T("*", XTERM, 16, 0, 0, 0),
2637 union tty_ansiu *u = 0; struct tty *ret = 0;
2638 const char *term, *config, *p, *l;
2639 const struct kw *kw;
2640 const struct enummap *em;
2641 const struct flagmap *fm;
2642 const struct termmap *tm;
2645 acaps = 0, ocaps = 0, tf = 0,
2646 acapset = 0, ocapset = 0, tfset = 0,
2650 config = getenv("MLIB_TTY_ANSICONFIG");
2651 term = getenv("TERM");
2653 if (term && STRCMP(term, ==, "dumb")) goto end;
2656 l = config + strlen(config);
2660 if (config >= l) goto done_config;
2661 else if (!ISSPACE(*config)) break;
2664 for (p = config + 1; p < l && !ISSPACE(*p); p++);
2665 if (*config == '+' || *config == '-') {
2666 if (*config == '+') f |= f_sense;
2668 config++; n = p - config;
2670 for (fm = flagmap; fm->name; fm++)
2671 if (STRNCMP(config, ==, fm->name, n) && !fm->name[n])
2673 debug("unknown flag `%.*s'", (int)n, config); goto next_config;
2675 if ((acapset&fm->acaps) || (ocapset&fm->ocaps) || (tfset&fm->tf)) {
2676 debug("duplicate setting for `%s'", fm->name);
2680 { acaps |= fm->acaps; ocaps |= fm->ocaps; tf |= fm->tf; }
2681 acapset |= fm->acaps; ocapset |= fm->ocaps; tfset |= fm->tf;
2684 p = memchr(config, '=', n);
2686 debug("missing `=' in setting `%.*s'", (int)n, config);
2690 for (em = enummap; em->name; em++)
2691 if (STRNCMP(config, ==, em->name, nn) && !em->name[nn])
2693 debug("unknown setting `%.*s'", (int)nn, config); goto next_config;
2695 p++; nn = n - nn - 1;
2696 for (kw = em->kw; kw->name; kw++)
2697 if (STRNCMP(p, ==, kw->name, nn) && !kw->name[nn])
2699 debug("unknown `%s' value `%.*s", em->name, (int)nn, p);
2702 if (acapset&em->mask) {
2703 debug("duplicate setting for `%s'", em->name);
2706 acaps |= kw->val; acapset |= em->mask;
2716 for (tm = termmap; tm->pat; tm++)
2717 if (str_match(tm->pat, term))
2721 acaps |= tm->acaps&~acapset;
2722 ocaps |= tm->ocaps&~ocapset;
2723 tf |= tm->tf&~tfset;
2726 if (!(acapset&TTACF_CSPCMASK)) env_colour_caps(&acaps, ECCF_SET);
2727 if (acaps&TTACF_CSPCMASK) ocaps |= TTCF_BGER;
2730 u->tty.ops = &ansi_ops;
2731 u->tty.acaps = acaps;
2732 u->tty.ocaps = ocaps;
2733 u->ansi.ansi.f = tf;
2734 u->tty.wd = 80; u->tty.ht = 25;
2735 u->tty.st.modes = TTMF_AUTOM | (u->tty.ocaps&TTMF_CVIS);
2736 u->tty.st.attr.f = 0; u->tty.st.attr.fg = u->tty.st.attr.bg = 0;
2737 common_init(&u->ansi.tty, fp);
2738 ret = &u->tty; u = 0;
2740 xfree(u); return (ret);
2745 /*----- Backend selection -------------------------------------------------*/
2747 struct tty *tty_open(FILE *fp, unsigned f, const unsigned *backends)
2749 static const struct betab {
2750 const char *name; unsigned code;
2751 struct tty *(*init)(FILE */*fp*/);
2753 { "unibilium", TTBK_UNIBI, termunibi_init },
2754 { "terminfo", TTBK_TERMINFO, terminfo_init },
2755 { "termcap", TTBK_TERMCAP, termcap_init },
2756 { "ansi", TTBK_ANSI, ansi_init },
2760 const struct betab *bt;
2761 const char *config, *p, *l;
2762 struct tty *tty = 0;
2766 if (fp || !(f&TTF_OPEN))
2767 fpin = fp != stdout ? fp : isatty(STDIN_FILENO) ? stdin : 0;
2769 if (isatty(STDIN_FILENO)) fpin = stdin;
2771 if (isatty(STDOUT_FILENO)) { fp = stdout; f |= TTF_BORROW; }
2772 else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
2774 fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
2775 fpin = fp; f &= ~TTF_BORROW;
2779 config = getenv("MLIB_TTY_BACKENDS");
2781 l = config + strlen(config);
2784 if (config >= l) goto done_config;
2785 else if (!ISSPACE(*config)) break;
2788 for (p = config + 1; p < l && !ISSPACE(*p); p++);
2791 for (bt = betab; bt->name; bt++)
2792 if (STRNCMP(config, ==, bt->name, n) && !bt->name[n])
2794 debug("unknown backend `%.*s'", (int)n, config); goto next_config;
2796 tty = bt->init(fp); if (tty) goto found;
2797 debug("failed to initialize `%s'", bt->name);
2802 } else if (backends)
2804 for (bt = betab; bt->name; bt++)
2805 if (*backends == bt->code) goto found_bycode;
2806 debug("unknown backend code %u", *backends); goto next_code;
2808 tty = bt->init(fp); if (tty) goto found;
2809 debug("failed to initialize `%s'", bt->name);
2814 for (bt = betab; bt->name; bt++) {
2815 tty = bt->init(fp); if (tty) goto found;
2816 debug("failed to initialize `%s'", bt->name);
2819 debug("all backends failed"); goto end;
2821 debug("selected backend `%s'", bt->name);
2822 tty->fpin = fpin; tty->f = f; fp = 0;
2824 if (fp && !(f&TTF_BORROW)) fclose(fp);
2828 void tty_close(struct tty *tty)
2831 if (tty->fpout && !(tty->f&TTF_BORROW)) fclose(tty->fpout);
2832 tty->ops->release(tty); xfree(tty);
2837 int tty_resized(struct tty *tty)
2841 if (!tty || !tty->fpout) { errno = ENOTTY; return (-1); }
2842 else if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
2843 else if (tty->wd == ws.ws_col && tty->ht == ws.ws_row) return (0);
2844 else { tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (1); }
2847 /*----- Terminal operations -----------------------------------------------*/
2849 int tty_setattr(struct tty *tty, const struct tty_attr *a)
2853 if (!tty || !tty->fpout)
2856 clamp_attr(&aa, a, tty->acaps);
2857 return (tty->ops->setattr(tty, &file_printops, tty->fpout, &aa));
2861 int tty_setattrg(struct tty *tty,
2862 const struct gprintf_ops *gops, void *go,
2863 const struct tty_attr *a)
2870 clamp_attr(&aa, a, tty->acaps);
2871 return (tty->ops->setattr(tty, gops, go, &aa));
2875 int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
2877 if (!tty || !tty->fpout) return (-1);
2878 else return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa));
2881 int tty_setattrlistg(struct tty *tty,
2882 const struct gprintf_ops *gops, void *go,
2883 const struct tty_attrlist *aa)
2885 if (!tty) return (-1);
2887 if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
2888 return (tty->ops->setattr(tty, gops, go, &aa->attr));
2889 else if (!aa->cap_mask)
2893 int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
2895 if (!tty || !tty->fpout) return (-1);
2896 else return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
2897 modes_bic, modes_xor));
2900 int tty_setmodesg(struct tty *tty,
2901 const struct gprintf_ops *gops, void *go,
2902 uint32 modes_bic, uint32 modes_xor)
2904 if (!tty) return (-1);
2905 else return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor));
2908 int tty_move(struct tty *tty, unsigned orig, int y, int x)
2910 if (!tty || !tty->fpout) return (-1);
2911 else return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x));
2914 int tty_moveg(struct tty *tty,
2915 const struct gprintf_ops *gops, void *go,
2916 unsigned orig, int y, int x)
2918 if (!tty) return (-1);
2919 else return (tty->ops->move(tty, gops, go, orig, y, x));
2922 int tty_repeat(struct tty *tty, int ch, unsigned n)
2924 if (!tty || !tty->fpout) return (-1);
2925 else return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n));
2928 int tty_repeatg(struct tty *tty,
2929 const struct gprintf_ops *gops, void *go,
2932 if (!tty) return (-1);
2933 else return (tty->ops->repeat(tty, gops, go, ch, n));
2936 int tty_erase(struct tty *tty, unsigned f)
2938 if (!tty || !tty->fpout) return (-1);
2939 else return (tty->ops->erase(tty, &file_printops, tty->fpout, f));
2942 int tty_eraseg(struct tty *tty,
2943 const struct gprintf_ops *gops, void *go,
2946 if (!tty) return (-1);
2947 else return (tty->ops->erase(tty, gops, go, f));
2950 int tty_erch(struct tty *tty, unsigned n)
2952 if (!tty || !tty->fpout) return (-1);
2953 else return (tty->ops->erch(tty, &file_printops, tty->fpout, n));
2956 int tty_erchg(struct tty *tty,
2957 const struct gprintf_ops *gops, void *go,
2960 if (!tty) return (-1);
2961 else return (tty->ops->erch(tty, gops, go, n));
2964 int tty_ins(struct tty *tty, unsigned f, unsigned n)
2966 if (!tty || !tty->fpout) return (-1);
2967 else return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n));
2970 int tty_insg(struct tty *tty,
2971 const struct gprintf_ops *gops, void *go,
2972 unsigned f, unsigned n)
2974 if (!tty) return (-1);
2975 else return (tty->ops->ins(tty, gops, go, f, n));
2978 int tty_inch(struct tty *tty, int ch)
2980 if (!tty || !tty->fpout) return (-1);
2981 else return (tty->ops->inch(tty, &file_printops, tty->fpout, ch));
2984 int tty_inchg(struct tty *tty,
2985 const struct gprintf_ops *gops, void *go,
2988 if (!tty) return (-1);
2989 else return (tty->ops->inch(tty, gops, go, ch));
2992 int tty_del(struct tty *tty, unsigned f, unsigned n)
2994 if (!tty || !tty->fpout) return (-1);
2995 else return (tty->ops->del(tty, &file_printops, tty->fpout, f, n));
2998 int tty_delg(struct tty *tty,
2999 const struct gprintf_ops *gops, void *go,
3000 unsigned f, unsigned n)
3002 if (!tty) return (-1);
3003 else return (tty->ops->del(tty, gops, go, f, n));
3006 int tty_restore(struct tty *tty, const struct tty_state *st)
3008 if (!tty || !tty->fpout) return (-1);
3009 else return (tty_restoreg(tty, &file_printops, tty->fpout, st));
3012 int tty_restoreg(struct tty *tty,
3013 const struct gprintf_ops *gops, void *go,
3014 const struct tty_state *st)
3019 tty->ops->setmodes(tty, gops, go, MASK32, st->modes) ||
3020 tty->ops->setattr(tty, gops, go, &st->attr))
3021 { rc = -1; goto end; }
3027 /*----- That's all, folks -------------------------------------------------*/