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 tty->baud = 0; tty->wd = tty->ht = 0;
298 if (fp && !tcgetattr(fileno(fp), &c)) {
299 code = cfgetospeed(&c);
300 for (b = baudtab; b->baud; b++)
301 if (b->code == code) { tty->baud = b->baud; goto found_baud; }
306 /* If the kernel didn't tell us the terminal dimensions, try to read them
307 * from the environment.
310 { p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
312 { p = getenv("LINES"); if (p) { n = atoi(p); if (n) tty->ht = n; } }
315 /* --- @env_colour_caps@ --- *
317 * Arguments: @unsigned *caps_inout@ = attribute capabilities to update
318 * @unsigned f@ = flags
322 * Use: Check the %|FORCE_COLOR|% environment variable and update the
323 * capabilities as required.
325 * The %|FORCE_COLOR|% variable originates with the Node
326 * community, with two objectives: (a) to convey policy
327 * regarding whether to produce coloured output, and (b) to
328 * describe the colour capabilities of the terminal, because the
329 * traditional mechanisms are deemed inadequate.
331 * The following values have defined meanings.
333 * * Unset or empty: no effect.
335 * * %|0|%: monochrome; don't produce colour.
337 * * %|1|%: 16 colours; 1-bit-per-channel colours are
338 * available, with an additional common brightness bit.
340 * * %|2|%: 256 colours; the `xterm' 256-bit palette is
341 * available, consisting of the 1-bit-per-channel colours
342 * with common brightness bit, a 6 × 6 × 6 colour cube, and
343 * a 24-level greyscale ramp.
345 * * %|3|%: full 24-bit colour.
347 * * Anything else: a request to use colour if available.
348 * This is ignored here, in the expectation that it will be
349 * given effect elsewhere, e.g., by @ttycolour_enablep@.
351 * If @ECCF_SET@ is set, then set or clear capabilities as
352 * required. Otherwise, clear capability bits which are denied
353 * by the variable setting, but no bits will be set. (This
354 * latter is necessary for backends which use terminal
355 * databases, since they can't be expected to make up the
356 * necessary control sequences for themselves.)
360 static void env_colour_caps(unsigned *caps_inout, unsigned f)
363 unsigned caps = *caps_inout, mask;
365 p = getenv("FORCE_COLOR"); if (!p) return;
371 mask = TTACF_FG | TTACF_BG | TTACF_1BPC | TTACF_1BPCBR;
374 mask = TTACF_FG | TTACF_BG |
375 TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
378 mask = TTACF_FG | TTACF_BG |
379 TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
384 if (!(f&ECCF_SET)) caps &= mask;
385 else caps = (caps&~(TTACF_CSPCMASK | TTACF_FG | TTACF_BG)) | mask;
390 /* --- @clamp_colours@ --- *
392 * Arguments: @uint32 *space_out, *colour_out@ = selected space and colour
393 * @uint32 space, colour@ = requested space and colour
394 * @uint32 acaps@ = terminal's attribute capability mask
398 * Use: Select the best approximation to the requested colour which
399 * can be accommodated by the terminal.
402 /* #define DEBUG_CLAMP */
404 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
405 uint32 space, uint32 colour, uint32 acaps)
407 unsigned r, g, b, rr, gg, bb, y, t, u;
408 uint32 best_colour = 0, best_space; int best_error;
416 /* Check the colour space. If it's one that the terminal can handle, then
417 * return the colour unchanged. Otherwise, extract the channel components
423 /* No colour wanted at all. There's nothing to do here. */
425 *space_out = TTCSPC_NONE; *colour_out = 0;
429 /* One-bit-per-channel colour.
431 * There's no standardized mapping for these; indeed, they're commonly
432 * configurable by users. Since there are also `bright' versions of
433 * each, it's probably not right to just map zero to zero and one to
437 if (colour >= 8) goto inval;
438 if (acaps&(TTACF_1BPC | TTACF_1BPCBR))
439 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
441 #define C1BPC_FULL 0xcc
442 #define C1BPC_CVT(col, bit) \
443 (C1BPC_FULL&((((col)&(1 << (bit))) << (8 - (bit))) - 1))
444 r = C1BPC_CVT(colour, 0);
445 g = C1BPC_CVT(colour, 1);
446 b = C1BPC_CVT(colour, 2);
450 /* One-bit-per-channel colour, with global brightness. Again, there's
451 * no standardized mapping. Apply a boost across all three channels.
454 if (colour >= 16) goto inval;
455 if (!(colour&TT1BPC_BRI) && (acaps&(TTACF_1BPC | TTACF_1BPCBR)))
456 { *space_out = TTCSPC_1BPC; *colour_out = colour; return; }
457 else if (acaps&TTACF_1BPCBR)
458 { *space_out = TTCSPC_1BPCBR; *colour_out = colour; return; }
460 #define C1BPC_BRIGHT 0x33
461 r = C1BPC_CVT(colour, 0) + C1BPC_BRIGHT;
462 g = C1BPC_CVT(colour, 1) + C1BPC_BRIGHT;
463 b = C1BPC_CVT(colour, 2) + C1BPC_BRIGHT;
468 /* Four-levels-per-channel colour. These are part of an `indexed'
469 * colour space which is theoretically controlled by applications but
470 * (a) that's rare, and (b) worrying about that won't do us any good.
472 * Each channel has four levels, but they're not assigned linearly.
475 if (colour >= 64) goto inval;
476 if (acaps&TTACF_4LPC)
477 { *space_out = TTCSPC_4LPC; *colour_out = colour; return; }
483 #define C4LPC_CVT(ch) (t = (ch), t == 0 ? C4LPC_L0 : \
484 t == 1 ? C4LPC_L1 : \
485 t == 2 ? C4LPC_L2 : \
487 r = C4LPC_CVT(TTCOL_2BR(colour));
488 g = C4LPC_CVT(TTCOL_2BG(colour));
489 b = C4LPC_CVT(TTCOL_2BB(colour));
494 /* Eight-levels greyscale. Again, these are part of an `indexed'
495 * colour space which is under application control, and, again, the
496 * levels aren't linear, but they're not far off linear.
498 if (colour >= 8) goto inval;
499 if (acaps&TTACF_8LGS)
500 { *space_out = TTCSPC_8LGS; *colour_out = colour; return; }
502 r = g = b = 255*(colour ? colour + 3 : 2)/11;
506 /* Six-levels-per-channel colour. Again, `indexed' colour space under
507 * application control. This time the mapping is essentially liner.
510 if (colour >= 216) goto inval;
511 if (acaps&TTACF_6LPC)
512 { *space_out = TTCSPC_6LPC; *colour_out = colour; return; }
514 #define C6LPC_CVT(ch) (t = (ch), t ? (40*t + 55) : 0)
515 r = C6LPC_CVT(TTCOL_6LR(colour));
516 g = C6LPC_CVT(TTCOL_6LG(colour));
517 b = C6LPC_CVT(TTCOL_6LB(colour));
522 /* Twenty-four-levels greyscale. Same story. */
524 if (colour >= 24) goto inval;
525 if (acaps&TTACF_24LGS)
526 { *space_out = TTCSPC_24LGS; *colour_out = colour; return; }
528 r = g = b = 10*colour + 8;
532 /* Eight-bits-per-channel colour. No conversion to apply here. */
534 if (colour >= 0x01000000) goto inval;
535 if (acaps&TTACF_8BPC)
536 { *space_out = TTCSPC_8BPC; *colour_out = colour; return; }
538 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
547 /* We didn't get an exact match, so we'll have to make do with what we've
550 best_error = -1; best_space = TTCSPC_NONE; best_colour = 0;
551 D( fprintf(stderr, "\n;; APPROX space %u, colour 0x%lx = %u/%u/%u\n",
552 space, (unsigned long)colour, r, g, b); )
554 /* Approximate colour weightings for human colour vision. */
558 #define TOTWT (RWT + GWT + BWT)
560 /* Determine the optimal grey approximation for this colour. */
561 y = (RWT*r + GWT*g + BWT*b + TOTWT/2)/TOTWT;
563 #define TRY_APPROX(rr, gg, bb, spc, clr) do { \
564 /* If the approximation (RR, GG, BB) is closer to the current best \
565 * then accept SPC and CLR as the new best space/colour option. \
568 int _r_err = (rr) - r, _g_err = (gg) - g, _b_err = (bb) - b; \
571 if (_r_err < 0) _r_err = -_r_err; \
572 if (_g_err < 0) _g_err = -_g_err; \
573 if (_b_err < 0) _b_err = -_b_err; \
575 _err = RWT*_r_err + GWT*_g_err + BWT*_b_err; \
577 ";; candidate space %u, colour 0x%lx = %u/%u/%u; " \
579 (spc), (unsigned long)(clr), (rr), (gg), (bb), _err); ) \
580 if (best_error < 0 || _err < best_error) { \
581 best_error = _err; best_space = (spc); best_colour = (clr); \
582 D( fprintf(stderr, ";;\tNEW BEST APPROXIMATION\n"); ) \
586 if (!(acaps&(TTACF_4LPC | TTACF_6LPC | TTACF_8BPC))) {
587 /* The one-bit-per-channel colours are very variable, but there's little
588 * choice, so we'll have to try. We assume the same mapping as on the
592 if (acaps&(TTACF_1BPC | TTACF_1BPCBR)) {
593 /* One-bit-per-channel colour. */
595 #define C1BPC_APPROX(cc, c, bit) do { \
596 if ((c) <= C1BPC_FULL/2) (cc) = 0; \
597 else { (cc) = C1BPC_FULL; t |= (bit); } \
600 C1BPC_APPROX(rr, r, TT1BPC_RED);
601 C1BPC_APPROX(gg, g, TT1BPC_GRN);
602 C1BPC_APPROX(bb, b, TT1BPC_BLU);
604 TRY_APPROX(rr, gg, bb, TTCSPC_1BPC, t);
607 if (acaps&TTACF_1BPCBR) {
608 /* One-bit-per-channel colour, with global brightness. */
610 #define C1BPCBR_APPROX(cc, c, bit) do { \
611 if ((c) <= C1BPC_FULL/2 + C1BPC_BRIGHT) (cc) = C1BPC_BRIGHT; \
612 else { (cc) = 255; t |= (bit); } \
615 C1BPCBR_APPROX(rr, r, TT1BPC_RED);
616 C1BPCBR_APPROX(gg, g, TT1BPC_GRN);
617 C1BPCBR_APPROX(bb, b, TT1BPC_BLU);
618 #undef C1BPCBR_APPROX
619 TRY_APPROX(rr, gg, bb, TTCSPC_1BPCBR, t);
623 if (acaps&TTACF_4LPC) {
624 /* Four-levels-per-channel colour. */
626 #define C4LPC_APPROX(cc, c, sh) do { \
629 if (_c > (C4LPC_L2 + C4LPC_L3)/2) \
630 { (cc) = C4LPC_L3; t |= 3 << (sh); } \
631 else if (_c > (C4LPC_L1 + C4LPC_L2)/2) \
632 { (cc) = C4LPC_L2; t |= 2 << (sh); } \
633 else if (_c > (C4LPC_L0 + C4LPC_L1)/2) \
634 { (cc) = C4LPC_L1; t |= 1 << (sh); } \
639 C4LPC_APPROX(rr, r, 4);
640 C4LPC_APPROX(gg, g, 2);
641 C4LPC_APPROX(bb, b, 0);
643 TRY_APPROX(rr, gg, bb, TTCSPC_4LPC, t);
646 if (acaps&TTACF_8LGS) {
647 /* Eight-levels greyscale. */
650 if (u <= 2) { u = 2; t = 0; }
651 else if (u == 3) { u = 4; t = 1; }
652 else if (u == 11) { u = 10; t = 7; }
654 u = (255*u)/11; TRY_APPROX(u, u, u, TTCSPC_8LGS, t);
657 if (acaps&TTACF_6LPC) {
658 /* Six-levels-per-channel colour. */
660 #define C6LPC_APPROX(cc, c, f) do { \
663 if (_c < 36) (cc) = 0; \
664 else { u = (_c - 36)/40; t += (f)*u; (cc) = 40*u + 55; } \
667 C6LPC_APPROX(rr, r, 36);
668 C6LPC_APPROX(gg, g, 6);
669 C6LPC_APPROX(bb, b, 1);
671 TRY_APPROX(rr, gg, bb, TTCSPC_6LPC, t);
674 if (acaps&TTACF_24LGS) {
675 /* Twenty-four-levels greyscale. */
677 if (y < 3) { t = 0; u = 8; }
678 else if (y >= 243) { t = 23; u = 238; }
679 else { t = (y - 3)/10; u = 10*t + 8; }
680 TRY_APPROX(u, u, u, TTCSPC_24LGS, t);
683 if (acaps&TTACF_8BPC) {
684 /* Eight-bits-per-channel colour. */
687 D( fprintf(stderr, ";; accept exact 8bpc colour\n"); )
688 best_error = 0; best_space = TTCSPC_8BPC;
689 best_colour = TTCOL_MK8B(r, g, b);
694 *space_out = best_space; *colour_out = best_colour;
698 /* Invalid colour selection. Ignore this. */
699 *space_out = TTCSPC_NONE; *colour_out = 0;
724 /* --- @clamp_attr@ --- *
726 * Arguments: @struct tty_attr *a_out@ = selected attributes
727 * @const struct tty_attr *a@ = requested attributes
728 * @uint32 acaps@ = terminal's attribute capability mask
732 * Use: Select the closest approximation to the requested attributes
733 * which can be accommodated by the terminal.
736 static void clamp_attr(struct tty_attr *a_out,
737 const struct tty_attr *a, uint32 acaps)
739 uint32 ff = 0, f = a ? a->f : 0, t;
741 /* Line attributes. */
742 t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
747 if (!acaps&TTACF_ULINE) t = TTLN_NONE;
750 if (acaps&TTACF_UULINE) ;
751 else if (acaps&TTACF_ULINE) t = TTLN_ULINE;
757 ff |= t << TTAF_LNSHIFT;
760 t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
762 case TTWT_MED: break;
763 case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
764 case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
765 default: t = TTWT_MED; break;
767 ff |= t << TTAF_WTSHIFT;
769 /* Other text attributes. */
770 if (acaps&TTACF_STRIKE) ff |= f&TTAF_STRIKE;
771 if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
772 if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
774 /* Foreground and background colours. */
775 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))
786 clamp_colours(&t, &a_out->bg,
787 (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a ? a->bg : 0,
789 ff |= t << TTAF_BGSPCSHIFT;
793 a_out->f = ff; a_out->_res0 = 0;
796 /* --- @stupid_repeat@ --- *
798 * Arguments: @struct tty *tty@ = control block pointer
799 * @const struct gprintf_ops *gops, void *go@ = output
801 * @int ch@ = character to write
802 * @unsigned n@ = number of copies
804 * Returns: Zero on success, %$-1$% on error.
806 * Use: Write @n@ copies of the character @ch@ to the terminal, the
807 * hard way. This function tries to be reasonably efficient, by
808 * transmitting buffers rather than exercising the output
809 * machinery for each individual character.
812 static int stupid_repeat(struct tty *tty,
813 const struct gprintf_ops *gops, void *go,
821 { memset(buf, ch, n); CHECK(gops->putm(go, buf, n)); }
823 memset(buf, ch, sizeof(buf));
826 CHECK(gops->putm(go, buf, nn));
827 n -= nn; if (!n) break;
836 /*----- Common machinery for %|termcap|% and %|terminfo|% -----------------*/
838 #if defined(HAVE_TERMINFO) || \
839 defined(HAVE_TERMCAP) || \
840 defined(HAVE_UNIBILIUM)
842 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
846 * The `termcap' and `terminfo' functions call a user-provided function to
847 * actually send control codes to the terminal. The bad news is that
848 * `termcap' doesn't provide any way to pass information to the output
849 * function beyond the character to be sent, and `terminfo' doesn't fix this
850 * mistake. So we must save the necessary context as global variables. For
851 * good measure, at least some implementations ignore errors from the output
852 * function, so we must keep track of them ourselves. More global variables.
854 * It's worse. Both libraries maintain significant global state of their
855 * own. And, at least with the `ncurses' implementation, the two share the
856 * same global state. The only thing to do is maintain a big interlock to
857 * make sure that only one is active at a time.
859 static const struct gprintf_ops *global_gops; /* output operations ... */
860 static void *global_gout; /* and context, for @caps_putch */
861 static char global_buf[4096]; /* a big output buffer */
862 static size_t global_len; /* length of buffer used */
863 static int global_err; /* error latch, zero if all ok */
864 static struct tty *global_lock = 0; /* interlock for global state */
866 /* --- @caps_claim@ --- *
870 * Returns: Zero on success, %$-1$% if already claimed.
872 * Use: Return %$-1$% if the interlock is already held. This is a
873 * function to call near the beginning of initializing a new
874 * control block, before the common global state gets
875 * clobbered. If initialization is successful, the caller is
876 * expected to actually store the control block pointer in
877 * @global_lock@ themselves.
880 static int caps_claim(void)
883 { debug("termcap/terminfo terminal already open"); return (-1); }
888 /* --- @caps_claim@, @caps_release@ --- *
890 * Arguments: @struct tty *tty@ = control block pointer for current lock
895 * Use: Release the lock.
898 static void caps_release(struct tty *tty)
899 { assert(global_lock == tty); global_lock = 0; }
901 /* --- @caps_putch@ --- *
903 * Arguments: @int ch@ = character to write
905 * Returns: Nonnegative on success, negative on failure. (But @tputs@
908 * Use: Output the character @ch@.
911 static int caps_putch(int ch)
913 if (global_len >= sizeof(global_buf)) {
914 if (global_gops->putm(global_gout, global_buf, global_len))
918 global_buf[global_len++] = ch;
922 /* --- @caps_prepout@ --- *
924 * Arguments: @struct tty *tty@ = control block pointer (ignored)
925 * @const struct gprintf_ops *gops, void *go@ = output
930 * Use: Prepare output to the given destination.
933 static void caps_prepout(struct tty *tty,
934 const struct gprintf_ops *gops, void *go)
935 { assert(!global_len); global_gops = gops; global_gout = go; }
937 /* --- @caps_flush@ --- *
939 * Arguments: @struct tty *tty@ = control block pointer (ignored)
941 * Returns: Zero for success, %$-1$% if error pending.
943 * Use: Flush the output buffer to the backend. If an error is
944 * pending, clear it and return failure.
947 static int caps_flush(struct tty *tty)
952 if (global_gops->putm(global_gout, global_buf, global_len)) rc = -1;
955 global_err = 0; return (rc);
960 /* The list of interesting capabilities.
962 * We never actually need all of these: some are only needed if others are
963 * unavailable. But the list isn't too huge, so we'll live with it.
965 * The main thing is that each capability has three different names: the
966 * `full' name (corresponding to a `terminfo' variable name), the `terminfo'
967 * capability name, as used in terminal descriptions, and the two-character
968 * `termcap' name. Unibilium uses the long names, but to reduce typing, the
969 * `unibi_' prefix is omitted here. (Annoyingly, in `ncurses', at least, the
970 * `variable' names are `secretly' macros referencing a current state, and
971 * premature expansion causes misery, so I've left the leading underscores in
972 * place as a countermeasure.) Internally, we use the short `terminfo'
973 * names, since they generally express the most useful information in the
977 #define BASICCAPS(_bool, _int, _str) \
978 _str(_repeat_char, rep, rp) \
979 _str(_pad_char, pad, pc) _int(_padding_baud_rate, pb, pb) \
980 _bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo) \
981 _bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
983 #define ATTRCAPS(_bool, _int, _str) \
984 _str(_exit_attribute_mode, sgr0, me) \
985 _str(_enter_underline_mode, smul, us) \
986 _str(_exit_underline_mode, rmul, ue) \
987 _str(_enter_italics_mode, sitm, ZH) _str(_exit_italics_mode, ritm, ZR) \
988 _str(_enter_bold_mode, bold, md) _str(_enter_dim_mode, dim, mh) \
989 _str(_enter_reverse_mode, rev, mr) \
990 COLOURCAPS(_bool, _int, _str)
992 #define COLOURCAPS(_bool, _int, _str) \
993 _str(_set_a_foreground, setaf, AF) _str(_set_a_background, setab, AB) \
994 _str(_orig_pair, op, op) _int(_max_colors, colors, Co)
996 #define MODECAPS(_bool, _int, _str) \
997 _str(_carriage_return, cr, cr) _str(_newline, nel, nw) \
998 _str(_enter_am_mode, smam, SA) _str(_exit_am_mode, rmam, RA) \
999 _str(_enter_ca_mode, smcup, ti) _str(_exit_ca_mode, rmcup, te) \
1000 _str(_cursor_normal, cnorm, vs) _str(_cursor_invisible, civis, vi) \
1001 _str(_enter_insert_mode, smir, im) _str(_exit_insert_mode, rmir, ei) \
1002 _str(_enter_delete_mode, smdc, dm) _str(_exit_delete_mode, rmdc, ed)
1004 #define MOVECAPS(_bool, _int, _str) \
1005 _str(_cursor_home, home, ho) \
1006 _str(_cursor_address, cup, cm) \
1007 _str(_row_address, vpa, cv) _str(_column_address, hpa, ch) \
1008 _str(_cursor_left, cub1, le) _str(_parm_left_cursor, cub, LE) \
1009 _str(_cursor_right, cuf1, nd) _str(_parm_right_cursor, cuf, RI) \
1010 _str(_cursor_up, cuu1, up) _str(_parm_up_cursor, cuu, UP) \
1011 _str(_cursor_down, cud1, do) _str(_parm_down_cursor, cud, DO)
1013 #define SCROLLCAPS(_bool, _int, _str) \
1014 _str(_change_scroll_region, csr, cs) \
1015 _str(_scroll_forward, ind, sf) _str(_parm_index, indn, SF) \
1016 _str(_scroll_reverse, ri, sr) _str(_parm_rindex, rin, SR)
1018 #define ERASECAPS(_bool, _int, _str) \
1019 _str(_erase_chars, ech, ec) \
1020 _str(_clr_bol, el1, cb) _str(_clr_eol, el, ce) \
1021 _str(_clr_eos, ed, cd) _str(_clear_screen, clear, cl)
1023 #define INSDELCAPS(_bool, _int, _str) \
1024 _str(_insert_character, ich1, ic) _str(_parm_ich, ich, IC) \
1025 _str(_insert_padding, ip, ip) \
1026 _str(_insert_line, il1, al) _str(_parm_insert_line, il, AL) \
1027 _str(_delete_character, dch1, dc) _str(_parm_dch, dch, DC) \
1028 _str(_delete_line, dl1, dl) _str(_parm_delete_line, dl, DL)
1030 #define STORECAPS(_bool, _int, _str) \
1031 BASICCAPS(_bool, _int, _str) \
1032 ATTRCAPS(_bool, _int, _str) \
1033 MODECAPS(_bool, _int, _str) \
1034 MOVECAPS(_bool, _int, _str) \
1035 SCROLLCAPS(_bool, _int, _str) \
1036 ERASECAPS(_bool, _int, _str) \
1037 INSDELCAPS(_bool, _int, _str)
1039 #ifdef HAVE_UNIBILIUM
1040 # define UNIBI_(x) unibi##x
1042 # define UNIBI_(x) 0
1045 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
1046 /* Expand a capability triple into a group of three usable C arguments. If
1047 * Unibilium isn't available, then we can use nonsense for its cap index.
1050 /* Some other capabilities which we want to refer to during initialization,
1051 * but don't need to keep around.
1053 #define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
1054 #define CAP_BCE CAPREF(_back_color_erase, bce, ut)
1055 #define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
1056 #define CAP_XVPA CAPREF(_col_addr_glitch, xhpa, YA)
1057 #define CAP_AM CAPREF(_auto_right_margin, am, am)
1058 #define CAP_XENL CAPREF(_eat_newline_glitch, xenl, xn)
1059 #define CAP_HT CAPREF(_lines, lines, li)
1060 #define CAP_WD CAPREF(_columns, cols, co)
1062 /* Additional operations required of terminal backends which make use of the
1063 * common capability machinery.
1065 struct tty_capopslots {
1067 /* Retrieving capabilities. */
1068 int (*boolcap)(struct tty */*tty*/,
1069 int /*uix*/, const char */*info*/, const char */*cap*/);
1070 int (*intcap)(struct tty */*tty*/,
1071 int /*uix*/, const char */*info*/, const char */*cap*/);
1072 const char *(*strcap)(struct tty */*tty*/,
1073 int /*uix*/, const char */*info*/,
1074 const char */*cap*/);
1076 /* Preparing and completing output. */
1077 void (*prepout)(struct tty */*tty*/,
1078 const struct gprintf_ops */*gops*/, void */*go*/);
1079 int (*flush)(struct tty */*tty*/);
1081 /* Writing capabilities with various kinds of arguments. */
1082 int (*put0)(struct tty */*tty*/, unsigned /*npad*/, const char */*cap*/);
1083 int (*put1i)(struct tty */*tty*/,
1084 unsigned /*npad*/, const char */*cap*/, int /*i0*/);
1085 int (*put2i)(struct tty */*tty*/,
1087 const char */*cap*/, int /*i0*/, int /*i1*/);
1089 #define TTY_CAPOPSPFX TTY_BASEOPSPFX; struct tty_capopslots cap
1090 struct tty_capops { TTY_CAPOPSPFX; };
1091 #define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
1092 union tty_capopsu { TTY_CAPOPSUSFX; };
1094 /* An extension of the control block to track the above capabilities. */
1095 struct tty_capslots {
1096 #define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
1097 #define DEF_INTCAP(uix, info, cap) int info;
1098 #define DEF_STRCAP(uix, info, cap) const char *info;
1099 STORECAPS(DEF_BOOLCAP, DEF_INTCAP, DEF_STRCAP)
1103 #define LEN_BOOLCAP(uix, info, cap)
1104 #define LEN_INTCAP(uix, info, cap)
1105 #define LEN_STRCAP(uix, info, cap) unsigned info##_cost;
1106 ATTRCAPS(LEN_BOOLCAP, LEN_INTCAP, LEN_STRCAP)
1111 #define TTY_CAPSPFX \
1113 struct tty_capslots cap
1114 struct tty_caps { TTY_CAPSPFX; };
1115 #define TTY_CAPSUSFX \
1116 struct tty_caps cap; \
1118 union tty_capsu { TTY_CAPSUSFX; };
1120 /* ---- @init_caps@ --- *
1122 * Arguments: @struct tty *tty@ = control block pointer
1126 * Use: Populate the capabilities in the terminal control block, and
1127 * advertise the results to the public part. Set @ht@ and @wd@
1128 * from the terminal description if they've not been set
1132 static void init_caps(struct tty_caps *t)
1134 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1137 t->tty.acaps = t->tty.ocaps = 0;
1138 t->tty.st.modes = 0; t->tty.st.attr.f = 0;
1139 t->tty.st.attr.fg = t->tty.st.attr.bg = 0;
1141 /* Inhale all of the interesting terminal capabilities. */
1142 #define GETBOOL(var, info, cap_) \
1143 t->cap.info = ops->cap.boolcap(&t->tty, CAPREF(var, info, cap_));
1144 #define GETINT(var, info, cap_) \
1145 t->cap.info = ops->cap.intcap(&t->tty, CAPREF(var, info, cap_));
1146 #define GETSTR(var, info, cap_) \
1147 t->cap.info = ops->cap.strcap(&t->tty, CAPREF(var, info, cap_));
1148 STORECAPS(GETBOOL, GETINT, GETSTR)
1153 #define COST_BOOLCAP(uix, info, cap_)
1154 #define COST_INTCAP(uix, info, cap_)
1155 #define COST_STRCAP(uix, info, cap_) \
1156 if (!t->cap.info) t->cap.info##_cost = -1; \
1157 else t->cap.info##_cost = \
1158 strlen(tgoto(t->cap.info, 0, t->cap.colors - 1));
1159 ATTRCAPS(COST_BOOLCAP, COST_INTCAP, COST_STRCAP)
1164 #define CLEAR_BOOL(uix, info, cap_) t->cap.info = 0;
1165 #define CLEAR_INT(uix, info, cap_) t->cap.info = 0;
1166 #define CLEAR_STR(uix, info, cap_) t->cap.info = 0;
1167 #define CLEARCAPS(caplist) \
1168 do { caplist(CLEAR_BOOL, CLEAR_INT, CLEAR_STR) } while (0)
1170 /* Basic capabilities. */
1171 if (!t->cap.cr) t->cap.cr = "\r";
1172 if (!t->cap.nel) t->cap.nel = "\r\n";
1174 /* Attribute capabilities. */
1175 if (ops->cap.intcap(&t->tty, CAP_XMC) > 0)
1176 CLEARCAPS(ATTRCAPS);
1178 if (t->cap.smul && (t->cap.rmul || t->cap.sgr0))
1179 t->tty.acaps |= TTACF_ULINE;
1180 if (t->cap.bold && t->cap.sgr0)
1181 t->tty.acaps |= TTACF_BOLD;
1182 if (t->cap.dim && t->cap.sgr0)
1183 t->tty.acaps |= TTACF_DIM;
1184 if (t->cap.sitm && (t->cap.ritm || t->cap.sgr0))
1185 t->tty.acaps |= TTACF_ITAL;
1186 if (t->cap.rev && t->cap.sgr0)
1187 t->tty.acaps |= TTACF_INVV;
1189 if ((t->cap.setaf || t->cap.setab) && (t->cap.op || t->cap.sgr0)) {
1190 if (t->cap.setaf) t->tty.acaps |= TTACF_FG;
1191 if (t->cap.setab) t->tty.acaps |= TTACF_BG;
1192 t->tty.acaps |= TTACF_1BPC;
1193 if (t->cap.colors >= 16777216)
1194 t->tty.acaps |= TTACF_1BPC | TTACF_8BPC;
1195 else if (t->cap.colors >= 256)
1196 t->tty.acaps |= TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
1197 else if (t->cap.colors == 88)
1198 t->tty.acaps |= TTACF_1BPCBR | TTACF_4LPC | TTACF_8LGS;
1199 else if (t->cap.colors >= 16)
1200 t->tty.acaps |= TTACF_1BPCBR;
1201 if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
1202 env_colour_caps(&t->tty.acaps, 0);
1206 /* Motion capabilities. */
1207 if (ops->cap.boolcap(&t->tty, CAP_XHPA)) t->cap.hpa = 0;
1208 if (ops->cap.boolcap(&t->tty, CAP_XVPA)) t->cap.vpa = 0;
1209 if (!t->cap.cub1) t->cap.cub1 = "\b";
1210 if ((t->cap.cuf || t->cap.cuf1) &&
1211 (t->cap.cuu || t->cap.cuu1) &&
1212 (t->cap.cud || t->cap.cud1)) {
1213 t->tty.ocaps |= TTCF_RELMV;
1214 if (t->cap.vpa) t->tty.ocaps |= TTCF_ABSMV | TTCF_MIXMV;
1215 else if (t->cap.home) t->tty.ocaps |= TTCF_ABSMV;
1216 } else if (t->cap.cup ||
1217 (t->cap.hpa && t->cap.vpa) ||
1218 (t->cap.home && (t->cap.cuf || t->cap.cuf1)))
1219 t->tty.ocaps |= TTCF_ABSMV;
1221 /* Mode capabilities. */
1222 if (t->cap.smam && t->cap.rmam) t->tty.ocaps |= TTMF_AUTOM;
1223 if (t->cap.smcup && t->cap.rmcup) t->tty.ocaps |= TTMF_FSCRN;
1224 if (t->cap.smir && t->cap.rmir) t->tty.ocaps |= TTMF_INS;
1225 if (t->cap.smdc && t->cap.rmdc) t->tty.ocaps |= TTMF_DEL;
1226 if (t->cap.cnorm && t->cap.civis)
1227 { t->tty.ocaps |= TTMF_CVIS; t->tty.st.modes |= TTMF_CVIS; }
1228 if (ops->cap.boolcap(&t->tty, CAP_AM)) {
1229 t->tty.st.modes |= TTMF_AUTOM;
1230 if (ops->cap.boolcap(&t->tty, CAP_XENL)) t->tty.ocaps |= TTCF_MMARG;
1234 if (t->cap.ech) t->tty.ocaps |= TTCF_ERCH;
1235 if (t->cap.el1) t->tty.ocaps |= TTCF_ERBOL;
1236 if (t->cap.el) t->tty.ocaps |= TTCF_EREOL;
1237 if (t->cap.ed) t->tty.ocaps |= TTCF_EREOD;
1238 if (t->cap.clear || (t->cap.ed && t->cap.home)) t->tty.ocaps |= TTCF_ERDSP;
1240 /* Insertion and deletion. */
1241 if (t->cap.ich || t->cap.ich1) t->tty.ocaps |= TTCF_INSCH;
1242 if (t->cap.il || t->cap.il1) t->tty.ocaps |= TTCF_INSLN;
1243 if (t->cap.dch || t->cap.dch1) t->tty.ocaps |= TTCF_DELCH;
1244 if (t->cap.dl || t->cap.dl1) t->tty.ocaps |= TTCF_DELLN;
1248 { wd = ops->cap.intcap(&t->tty, CAP_WD); if (wd > 0) t->tty.wd = wd; }
1250 { ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
1258 /* Macros for formatting capabilities. */
1259 #define PUT0V(npad, cap_) \
1260 CHECK(ops->cap.put0(&t->tty, (npad), (cap_)))
1261 #define PUT1IV(npad, cap_, i0) \
1262 CHECK(ops->cap.put1i(&t->tty, (npad), (cap_), (i0)))
1263 #define PUT2IV(npad, cap_, i0, i1) \
1264 CHECK(ops->cap.put2i(&t->tty, (npad), (cap_), (i0), (i1)))
1266 #define PUT0(npad, name) PUT0V(npad, t->cap.name)
1267 #define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
1268 #define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
1270 /* --- @caps_setcolour@ --- *
1272 * Arguments: @struct tty_caps *t@ = extended control block pointer
1275 static int caps_setcolour(struct tty_caps *t,
1276 const char *cap, uint32 spc, uint32 clr)
1278 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1282 case TTCSPC_1BPC: case TTCSPC_1BPCBR: PUT1IV(0, cap, clr); break;
1283 case TTCSPC_4LPC: case TTCSPC_6LPC: PUT1IV(0, cap, clr + 16); break;
1284 case TTCSPC_8LGS: PUT1IV(0, cap, clr + 80); break;
1285 case TTCSPC_24LGS: PUT1IV(0, cap, clr + 232); break;
1288 /* There's an unfortunate ambiguity in the %|setaf|% conventions. The
1289 * first eight colours should be dark shades of blue, but in fact
1290 * they're interpreted as the one-bit-per-channel basic colours by
1291 * common `terminfo' settings. Notice and kludge by adding a little
1292 * red. This will tinge the colour magenta, but all such colours are
1293 * so dark as to be effectively black anyway, so I doubt that this will
1296 if (spc == TTCSPC_8BPC && clr < 8) clr += 65536;
1297 PUT1IV(0, cap, clr); break;
1299 /* case TTCSPC_NONE: */
1300 default: rc = -1; goto end;
1307 static int caps_setattr_internal(struct tty_caps *t,
1308 const struct tty_attr *a)
1310 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1313 unsigned c, z, f = 0;
1316 /* Work out what needs doing. */
1317 diff = a->f ^ t->tty.st.attr.f;
1319 /* Some terminals might not be able to clear individual attributes that
1320 * they can set, and some capabilities for turning attributes on don't even
1321 * have a corresponding attribute for turning them off again individually,
1322 * so we have to use %|sgr0|% to start from scratch. Of course, if we need
1323 * to do that, we need to restore the other active attributes, so we must
1328 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
1329 #define ADDCOST(cap_) do { \
1330 if (t->cap.cap_) c += t->cap.cap_##_cost; \
1331 else f |= f_clrall; \
1333 if (CLEARP(TTAF_LNMASK)) ADDCOST(rmul);
1334 if (CLEARP(TTAF_WTMASK)) f |= f_clrall;
1335 if (diff&~a->f&TTAF_INVV) f |= f_clrall;
1336 if (diff&~a->f&TTAF_ITAL) ADDCOST(ritm);
1337 if (CLEARP(TTAF_FGSPCMASK) || CLEARP(TTAF_BGSPCMASK)) ADDCOST(op);
1342 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1343 case TTLN_ULINE: z += t->cap.smul_cost; break;
1345 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1346 case TTWT_BOLD: z += t->cap.bold_cost; break;
1347 case TTWT_DIM: z += t->cap.dim_cost; break;
1349 if (a->f&TTAF_INVV) z += t->cap.rev_cost;
1350 if (a->f&TTAF_ITAL) z += t->cap.sitm_cost;
1351 if (a->f&TTAF_FGSPCMASK) z += t->cap.setaf_cost;
1352 if (a->f&TTAF_BGSPCMASK) z += t->cap.setab_cost;
1354 if ((t->cap.sgr0 && z + t->cap.sgr0_cost < c) || (f&f_clrall))
1355 { PUT0(0, sgr0); diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg; }
1358 if (diff&TTAF_LNMASK)
1359 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
1360 case TTLN_NONE: PUT0(0, rmul); break;
1361 case TTLN_ULINE: PUT0(0, smul); break;
1362 /* case TTLN_UULINE: */
1363 /* case TTLN_STRIKE: */
1364 default: rc = -1; goto end;
1368 if (diff&TTAF_WTMASK)
1369 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
1370 /* case TTWT_MED: */
1371 case TTWT_BOLD: PUT0(0, bold); break;
1372 case TTWT_DIM: PUT0(0, dim); break;
1373 default: rc = -1; goto end;
1376 /* Other text effects. */
1377 if (diff&a->f&TTAF_INVV) PUT0(0, rev);
1378 if (diff&TTAF_ITAL) {
1379 if (a->f&TTAF_ITAL) PUT0(0, sitm);
1384 if (((diff&TTAF_FGSPCMASK) && !(a->f&TTAF_FGSPCMASK)) ||
1385 ((diff&TTAF_BGSPCMASK) && !(a->f&TTAF_BGSPCMASK))) {
1386 /* There's no capability string for resetting just the foreground
1387 * or background colours to the defaults, so deal with that here.
1391 diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
1392 (a->f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
1394 if ((diff&TTAF_FGSPCMASK) || a->fg != t->tty.st.attr.fg)
1395 CHECK(caps_setcolour(t, t->cap.setaf,
1396 (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
1397 if ((diff&TTAF_BGSPCMASK) || a->bg != t->tty.st.attr.bg)
1398 CHECK(caps_setcolour(t, t->cap.setab,
1399 (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
1404 t->tty.st.attr = *a; return (rc);
1409 static int caps_setattr(struct tty *tty,
1410 const struct gprintf_ops *gops, void *go,
1411 const struct tty_attr *a)
1413 struct tty_caps *t = (struct tty_caps *)tty;
1414 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1417 ops->cap.prepout(&t->tty, gops, go);
1418 rc = caps_setattr_internal(t, a);
1419 if (ops->cap.flush(&t->tty)) rc = -1;
1423 static int caps_setmodes(struct tty *tty,
1424 const struct gprintf_ops *gops, void *go,
1425 uint32 modes_bic, uint32 modes_xor)
1427 struct tty_caps *t = (struct tty_caps *)tty;
1428 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1433 /* Figure out which modes to set. */
1434 modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
1435 diff = modes ^ t->tty.st.modes;
1437 /* Prepare output. */
1438 ops->cap.prepout(&t->tty, gops, go);
1440 /* Automatic margins. */
1441 if (diff&TTMF_AUTOM) {
1442 if (modes&TTMF_AUTOM) PUT0(0, smam);
1447 if (diff&TTMF_FSCRN) {
1448 if (modes&TTMF_FSCRN) PUT0(0, smcup);
1449 else PUT0(0, rmcup);
1452 /* Cursor visibility. */
1453 if (diff&TTMF_CVIS) {
1454 if (modes&TTMF_CVIS) PUT0(0, civis);
1455 else PUT0(0, cnorm);
1459 if (diff&TTMF_INS) {
1460 cap = modes&TTMF_INS ? t->cap.smir : t->cap.rmir;
1461 if (cap) PUT0V(0, cap);
1462 else if (!t->cap.ich) { rc = -1; goto end; }
1465 /* Delete characters. */
1466 if (diff&TTMF_DEL) {
1467 cap = modes&TTMF_DEL ? t->cap.smdc : t->cap.rmdc;
1468 if (cap) PUT0V(0, cap);
1469 else if (!t->cap.dch && !t->cap.dch1) { rc = -1; goto end; }
1475 if (ops->cap.flush(&t->tty)) rc = -1;
1476 t->tty.st.modes = modes; return (rc);
1479 #define CIF_PADMUL 1u
1480 static int caps_iterate(struct tty_caps *t,
1481 const char *cap1, const char *capn,
1482 unsigned f, unsigned npad, unsigned n)
1484 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1488 if (cap1 && (n == 1 || !capn))
1489 while (n--) PUT0V(npad, cap1);
1491 max = npad && (f&CIF_PADMUL) ? INT_MAX/npad : INT_MAX;
1493 nn = n; if (nn > max) nn = max;
1494 PUT1IV(npad, capn, nn);
1503 static int caps_move_relative(struct tty_caps *t,
1505 const char *fw1, const char *fwn,
1506 const char *rv1, const char *rvn)
1508 const char *mv1, *mvn;
1510 if (!delta) return (0);
1511 else if (delta > 0) { mv1 = fw1; mvn = fwn; }
1512 else { mv1 = rv1; mvn = rvn; delta = - delta; }
1513 return (caps_iterate(t, mv1, mvn, 0, 0, delta));
1516 static int caps_move(struct tty *tty,
1517 const struct gprintf_ops *gops, void *go,
1518 unsigned orig, int y, int x)
1520 struct tty_caps *t = (struct tty_caps *)tty;
1521 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1525 ops->cap.prepout(&t->tty, gops, go);
1527 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
1530 if (!t->cap.msgr && a.f) { PUT0(0, sgr0); t->tty.st.attr.f = 0; }
1534 if (t->cap.home && !x && !y)
1536 else if (t->cap.cup)
1537 PUT2I(1, cup, y, x);
1538 else if (t->cap.vpa) {
1544 CHECK(caps_move_relative(t, x,
1545 t->cap.cuf1, t->cap.cuf,
1546 t->cap.cub1, t->cap.cub));
1548 } else if (t->cap.home) {
1550 CHECK(caps_iterate(t, t->cap.cud1, t->cap.cud, 0, 1, y));
1551 CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1553 { rc = -1; goto end; }
1557 CHECK(caps_move_relative(t, y,
1558 t->cap.cud1, t->cap.cud,
1559 t->cap.cuu1, t->cap.cuu));
1560 CHECK(caps_move_relative(t, x,
1561 t->cap.cuf1, t->cap.cuf,
1562 t->cap.cub1, t->cap.cub));
1565 case TTOF_XHOME | TTOF_YCUR:
1566 if (x == 0 && y == 1)
1569 CHECK(caps_move_relative(t, y,
1570 t->cap.cud1, t->cap.cud,
1571 t->cap.cuu1, t->cap.cuu));
1572 if (t->cap.hpa && x)
1576 CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
1581 case TTOF_XCUR | TTOF_YHOME:
1583 CHECK(caps_move_relative(t, x,
1584 t->cap.cuf1, t->cap.cuf,
1585 t->cap.cub1, t->cap.cub));
1593 if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
1595 if (!t->cap.msgr && a.f)
1596 CHECK(caps_setattr_internal(t, &a));
1600 if (ops->cap.flush(&t->tty)) rc = -1;
1604 static int caps_repeat(struct tty *tty,
1605 const struct gprintf_ops *gops, void *go,
1608 struct tty_caps *t = (struct tty_caps *)tty;
1609 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1613 ops->cap.prepout(&t->tty, gops, go);
1615 CHECK(stupid_repeat(tty, gops, go, ch, n));
1619 nn = n; if (nn > INT_MAX) nn = INT_MAX;
1620 PUT2I((nn + wd - 1)/wd, rep, ch, nn);
1626 if (ops->cap.flush(&t->tty)) rc = -1;
1630 static int caps_erase(struct tty *tty,
1631 const struct gprintf_ops *gops, void *go,
1634 struct tty_caps *t = (struct tty_caps *)tty;
1635 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1638 ops->cap.prepout(&t->tty, gops, go);
1640 switch (f&(TTEF_BEGIN | TTEF_END)) {
1643 case TTEF_BEGIN | TTEF_END:
1644 if (t->cap.clear) PUT0(t->tty.ht, clear);
1645 else { PUT0(1, home); PUT0(t->tty.ht, ed); }
1648 PUT0(t->tty.ht, ed);
1655 if (f&TTEF_BEGIN) PUT0(1, el1);
1656 if (f&TTEF_END) PUT0(1, el);
1660 if (ops->cap.flush(&t->tty)) rc = -1;
1664 static int caps_erch(struct tty *tty,
1665 const struct gprintf_ops *gops, void *go,
1668 struct tty_caps *t = (struct tty_caps *)tty;
1669 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1672 ops->cap.prepout(&t->tty, gops, go);
1673 if (n) PUT1I(1, ech, n);
1676 if (ops->cap.flush(&t->tty)) rc = -1;
1680 static int caps_ins(struct tty *tty,
1681 const struct gprintf_ops *gops, void *go,
1682 unsigned f, unsigned n)
1684 struct tty_caps *t = (struct tty_caps *)tty;
1685 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1688 ops->cap.prepout(&t->tty, gops, go);
1690 CHECK(caps_iterate(t, t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
1692 CHECK(caps_iterate(t, t->cap.ich1, t->cap.ich, 0, 1, n));
1695 if (ops->cap.flush(&t->tty)) rc = -1;
1699 static int caps_inch(struct tty *tty,
1700 const struct gprintf_ops *gops, void *go,
1703 struct tty_caps *t = (struct tty_caps *)tty;
1704 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1707 ops->cap.prepout(&t->tty, gops, go);
1708 if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
1709 { rc = -1; goto end; }
1710 if (t->cap.ich) PUT0(1, ich);
1711 CHECK(gops->putch(go, ch));
1712 if (t->cap.ip) PUT0(1, ip);
1715 if (ops->cap.flush(&t->tty)) rc = -1;
1719 static int caps_del(struct tty *tty,
1720 const struct gprintf_ops *gops, void *go,
1721 unsigned f, unsigned n)
1723 struct tty_caps *t = (struct tty_caps *)tty;
1724 const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
1727 ops->cap.prepout(&t->tty, gops, go);
1730 CHECK(caps_iterate(t, t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
1732 CHECK(caps_iterate(t, t->cap.dch1, t->cap.dch, 0, 1, n));
1736 if (ops->cap.flush(&t->tty)) rc = -1;
1747 #define TTY_CAPOPS \
1748 caps_setattr, caps_setmodes, \
1749 caps_move, caps_repeat, \
1750 caps_erase, caps_erch, caps_ins, caps_inch, caps_del
1754 /*----- Termcap -----------------------------------------------------------*/
1758 struct tty_termcapslots {
1759 char termbuf[4096], capbuf[4096], *capcur;
1761 struct tty_termcap { TTY_CAPSPFX; struct tty_termcapslots tc; };
1762 union tty_termcapu { struct tty_termcap tc; TTY_CAPSUSFX; };
1764 static int termcap_boolcap(struct tty *tty,
1765 int uix, const char *info, const char *cap)
1769 p = tgetflag(cap); assert(p >= 0);
1773 static int termcap_intcap(struct tty *tty,
1774 int uix, const char *info, const char *cap)
1778 n = tgetnum(cap); assert(n >= -1);
1782 static const char *termcap_strcap(struct tty *tty,
1783 int uix, const char *info,
1786 struct tty_termcap *t = (struct tty_termcap *)tty;
1789 p = tgetstr(cap, &t->tc.capcur); assert(p != (const char *)-1);
1793 static int termcap_put0(struct tty *tty,
1794 unsigned npad, const char *cap)
1796 if (!cap) return (-1);
1797 return (tputs(cap, npad, caps_putch));
1800 static int termcap_put1i(struct tty *tty,
1801 unsigned npad, const char *cap, int i0)
1803 if (!cap) return (-1);
1804 return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
1807 static int termcap_put2i(struct tty *tty,
1808 unsigned npad, const char *cap, int i0, int i1)
1810 if (!cap) return (-1);
1811 return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
1814 static const union tty_capopsu termcap_ops = { {
1815 { caps_release, TTY_CAPOPS },
1816 { termcap_boolcap, termcap_intcap, termcap_strcap,
1817 caps_prepout, caps_flush,
1818 termcap_put0, termcap_put1i, termcap_put2i }
1821 static struct tty *termcap_init(FILE *fp)
1823 union tty_termcapu *u = 0; struct tty *ret = 0;
1826 if (caps_claim()) goto end;
1827 term = getenv("TERM"); if (!term) goto end;
1829 if (tgetent(u->tc.tc.termbuf, term) < 1) goto end;
1830 u->tc.tc.capcur = u->tc.tc.capbuf;
1831 u->tty.ops = &termcap_ops.tty;
1832 common_init(&u->tty, fp);
1834 ret = &u->tty; u = 0;
1836 xfree(u); global_lock = ret; return (ret);
1841 /*----- Terminfo ----------------------------------------------------------*/
1843 #ifdef HAVE_TERMINFO
1845 static int terminfo_boolcap(struct tty *tty,
1846 int uix, const char *info, const char *cap)
1850 p = tigetflag(info); assert(p >= 0);
1854 static int terminfo_intcap(struct tty *tty,
1855 int uix, const char *info, const char *cap)
1859 n = tigetnum(info); assert(n >= -1);
1863 static const char *terminfo_strcap(struct tty *tty,
1864 int uix, const char *info,
1869 p = tigetstr(info); assert(p != (const char *)-1);
1873 static int terminfo_put0(struct tty *tty,
1874 unsigned npad, const char *cap)
1876 if (!cap) return (-1);
1877 return (tputs(cap, npad, caps_putch));
1880 static int terminfo_put1i(struct tty *tty,
1881 unsigned npad, const char *cap, int i0)
1883 if (!cap) return (-1);
1884 return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
1887 static int terminfo_put2i(struct tty *tty,
1888 unsigned npad, const char *cap, int i0, int i1)
1890 if (!cap) return (-1);
1891 return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
1894 static const union tty_capopsu terminfo_ops = { {
1895 { caps_release, TTY_CAPOPS },
1896 { terminfo_boolcap, terminfo_intcap, terminfo_strcap,
1897 caps_prepout, caps_flush,
1898 terminfo_put0, terminfo_put1i, terminfo_put2i }
1901 static struct tty *terminfo_init(FILE *fp)
1903 union tty_capsu *u = 0; struct tty *ret = 0;
1906 if (caps_claim()) goto end;
1907 if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
1909 u->tty.ops = &terminfo_ops.tty;
1910 common_init(&u->tty, fp);
1912 ret = &u->tty; u = 0;
1914 xfree(u); global_lock = ret; return (ret);
1919 /*----- Unibilium ---------------------------------------------------------*/
1921 #ifdef HAVE_UNIBILIUM
1923 struct tty_unibislots {
1925 unibi_var_t dy[26], st[26];
1926 const struct gprintf_ops *gops; void *go;
1927 char buf[4096]; size_t n;
1930 struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
1931 union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
1933 static int termunibi_boolcap(struct tty *tty,
1934 int uix, const char *info, const char *cap)
1936 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1938 return (unibi_get_bool(t->u.ut, uix));
1941 static int termunibi_intcap(struct tty *tty,
1942 int uix, const char *info, const char *cap)
1944 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1946 return (unibi_get_num(t->u.ut, uix));
1949 static const char *termunibi_strcap(struct tty *tty,
1950 int uix, const char *info,
1953 struct tty_unibilium *t = (struct tty_unibilium *)tty;
1955 return (unibi_get_str(t->u.ut, uix));
1958 struct termunibi_outctx {
1959 struct tty_unibilium *t;
1962 static void termunibi_putm(void *ctx, const char *p, size_t sz)
1964 struct tty_unibilium *t = ctx;
1967 n = sizeof(t->u.buf) - t->u.n;
1969 { memcpy(t->u.buf + t->u.n, p, sz); t->u.n += sz; }
1971 if (n) { memcpy(t->u.buf + t->u.n, p, n); p += n; sz -= n; }
1973 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
1975 if (sz <= sizeof(t->u.buf)) break;
1976 memcpy(t->u.buf, p, sizeof(t->u.buf));
1977 p += sizeof(t->u.buf); sz -= sizeof(t->u.buf);
1979 memcpy(t->u.buf, p, sz); t->u.n = sz;
1983 static void termunibi_pad(void *ctx, size_t ms, int mulp, int forcep)
1985 struct tty_unibilium *t = ctx;
1990 /* Based on 7 data bits, 1 stop bit, 1 parity bit. */
1991 #define BITS_PER_KB 9000
1993 if (forcep || (t->tty.baud >= t->cap.pb && !t->cap.xon)) {
1995 tv.tv_sec = ms/1000; tv.tv_usec = 1000*(ms%1000);
1997 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
2001 if (t->tty.fpout) fflush(t->tty.fpout);
2002 select(0, 0, 0, 0, &tv);
2004 pc = t->cap.pad ? *t->cap.pad : 0;
2005 sz = (ms*t->tty.baud + BITS_PER_KB - 1)/BITS_PER_KB;
2006 n = sizeof(t->u.buf) - t->u.n;
2008 { memset(t->u.buf + t->u.n, pc, sz); t->u.n += sz; }
2010 if (n) { memset(t->u.buf + t->u.n, pc, sz); sz -= n; }
2011 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
2013 if (sz < sizeof(t->u.buf))
2014 memset(t->u.buf, pc, sz);
2016 memset(t->u.buf, pc, sizeof(t->u.buf));
2018 if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
2020 sz -= sizeof(t->u.buf);
2021 } while (sz > sizeof(t->u.buf));
2031 static void termunibi_prepout(struct tty *tty,
2032 const struct gprintf_ops *gops, void *go)
2034 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2036 assert(!t->u.n); t->u.gops = gops; t->u.go = go;
2039 static int termunibi_flush(struct tty *tty)
2041 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2045 if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) rc = -1;
2048 t->u.err = 0; return (rc);
2051 static int termunibi_put0(struct tty *tty,
2052 unsigned npad, const char *cap)
2054 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2057 if (!cap) return (-1);
2058 unibi_format(t->u.dy, t->u.st, cap, arg,
2064 static int termunibi_put1i(struct tty *tty,
2065 unsigned npad, const char *cap, int i0)
2067 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2070 if (!cap) return (-1);
2071 arg[0] = unibi_var_from_num(i0);
2072 unibi_format(t->u.dy, t->u.st, cap, arg,
2078 static int termunibi_put2i(struct tty *tty,
2080 const char *cap, int i0, int i1)
2082 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2085 if (!cap) return (-1);
2086 arg[0] = unibi_var_from_num(i0);
2087 arg[1] = unibi_var_from_num(i1);
2088 unibi_format(t->u.dy, t->u.st, cap, arg,
2094 static void termunibi_release(struct tty *tty)
2096 struct tty_unibilium *t = (struct tty_unibilium *)tty;
2098 unibi_destroy(t->u.ut);
2101 static const union tty_capopsu termunibi_ops = { {
2102 { termunibi_release, TTY_CAPOPS },
2103 { termunibi_boolcap, termunibi_intcap, termunibi_strcap,
2104 termunibi_prepout, termunibi_flush,
2105 termunibi_put0, termunibi_put1i, termunibi_put2i }
2108 static struct tty *termunibi_init(FILE *fp)
2110 union tty_unibiliumu *u = 0; struct tty *ret = 0;
2114 term = getenv("TERM"); if (!term) goto end;
2115 ut = unibi_from_term(term); if (!ut) goto end;
2117 u->tty.ops = &termunibi_ops.tty;
2118 u->u.u.ut = ut; ut = 0;
2119 u->u.u.n = 0; u->u.u.err = 0;
2120 common_init(&u->tty, fp);
2122 ret = &u->tty; u = 0;
2124 xfree(u); if (ut) unibi_destroy(ut);
2130 /*----- ANSI terminals ----------------------------------------------------*/
2132 struct tty_ansislots {
2134 #define TAF_CNCATTR 1u /* attributes can be cancelled */
2135 #define TAF_EDITN 2u /* insert/delete multiple */
2136 #define TAF_SEMI 4u /* semicolons in CSI 38 m colour */
2138 struct tty_ansi { TTY_BASEPFX; struct tty_ansislots ansi; };
2139 union tty_ansiu { struct tty_ansi ansi; TTY_BASEUSFX; };
2141 /* Control sequences.
2143 * * CUP: \33 [ Y ; X H `cursor position' [vt100]
2145 * * CUU/CUD/CUR/CUL: \33 [ N A/B/C/D `cursor up/down/right/left'
2147 * * DCH: \33 [ N P `delete character' [vt220]
2148 * (single char only in vt102?)
2150 * * DL: \33 [ N M `delete line' [vt220]
2151 * (single line only in vt102?)
2153 * * ECH: \33 [ N X `erase characters' [vt220]
2155 * * ED: \33 [ P J `erase in display'
2156 * P = 0 erase to end-of-screen [vt100]
2157 * P = 1 erase from start-of-screen [vt100]
2158 * P = 2 erase entire screen [vt100]
2160 * * EL: \33 [ P K `erase in line'
2161 * P = 0 erase to end-of-line [vt100]
2162 * P = 1 erase from start-of-line [vt100]
2163 * P = 2 erase entire line [vt100]
2165 * * HPA/VPA: \33 [ I G/d `horizontal/vertical position
2166 * absolute' [ecma48-4]
2168 * * ICH: \33 [ N @ `insert character' [vt220]
2169 * (single char only in vt102?)
2171 * * IL: \33 [ N L `insert line' [vt220]
2172 * (single line only in vt102?)
2174 * * SGR: \33 [ P ; ... m `select graphics rendition'
2175 * P = 0 cancel all attributes [vt100]
2176 * P = 1 bold [vt100]
2177 * P = 2 dim [ecma48-4]
2178 * P = 3 italics [ecma48-4]
2179 * P = 4 underline [vt100]
2180 * P = 7 inverse video [vt100]
2181 * P = 9 strikeout [ecma48-4]
2182 * P = 21 double underline [ecma48-4]
2183 * P = 22 cancal bold/dim [vt220]
2184 * P = 24 cancel underline [vt220]
2185 * P = 27 cancel inverse video [vt220]
2186 * P = 30 + 4 R + 2 G + B set 1BPC foreground [ecma48-4]
2187 * P = 38 : 2 : ? : R : G : B set foreground [iso8613-6]
2188 * P = 38 : 5 : N set foreground [iso8613-6, xterm]
2189 * P = 39 cancel foreground [ecma48-4]
2190 * P = 40--49 as above, for background
2191 * P = 90 + 4 R + 2 G + B set bright 1BPC foreground [xterm]
2192 * P = 100 + 4 R + 2 G + B set bright 1BPC background [xterm]
2194 * * SM/RM: \33 [ P ; ... h/l `set/reset modes'
2195 * M = 4 insert [vt220]
2197 * * SM, RM: \33 [ ? P ; ... h/l `set/reset private modes'
2198 * M = 7 auto right margin [vt100]
2199 * M = 25 visible cursor [vt220]
2200 * M = 1049 alternate screen [xterm]
2202 * * \33 [ P ; X ; Y t `window manipulation'
2203 * P = 22, X = 0 save title and icon [xterm]
2204 * P = 23, X = 0 restore title and icon [xterm]
2207 static void ansi_release(struct tty *tty) { ; }
2209 #define PUTCH(ch) CHECK(gops->putch(go, (ch)))
2210 #define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
2212 if (!(f&TAF_SEMI)) f |= TAF_SEMI; \
2216 static int ansi_setcolour(struct tty_ansi *t, unsigned *f_inout,
2217 const struct gprintf_ops *gops, void *go,
2219 uint32 spc, uint32 clr)
2221 unsigned f = *f_inout;
2226 SEMI; CHECK(gprintf(gops, go, "%d", norm + 9));
2229 SEMI; CHECK(gprintf(gops, go, "%d", norm + clr));
2232 SEMI; CHECK(gprintf(gops, go, "%d", br + (clr&~TT1BPC_BRI)));
2234 case TTCSPC_4LPC: case TTCSPC_6LPC:
2236 if (t->ansi.f&TAF_SEMI)
2237 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 16));
2239 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 16));
2243 if (t->ansi.f&TAF_SEMI)
2244 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 80));
2246 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 80));
2250 if (t->ansi.f&TAF_SEMI)
2251 CHECK(gprintf(gops, go, "%d;5;%d", norm + 8, clr + 232));
2253 CHECK(gprintf(gops, go, "%d:5:%d", norm + 8, clr + 232));
2257 if (t->ansi.f&TAF_SEMI)
2258 CHECK(gprintf(gops, go, "%d;2;%d;%d;%d", norm + 8,
2259 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
2261 CHECK(gprintf(gops, go, "%d:2::%d:%d:%d", norm + 8,
2262 TTCOL_8BR(clr), TTCOL_8BG(clr), TTCOL_8BB(clr)));
2270 *f_inout = f; return (rc);
2273 static int ansi_setattr(struct tty *tty,
2274 const struct gprintf_ops *gops, void *go,
2275 const struct tty_attr *a)
2277 struct tty_ansi *t = (struct tty_ansi *)tty;
2280 unsigned z, c, f = 0;
2282 diff = a->f ^ t->tty.st.attr.f;
2283 if (!diff && a->fg == t->tty.st.attr.fg && a->bg == t->tty.st.attr.bg)
2287 #define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
2288 if (CLEARP(TTAF_LNMASK)) c += 3;
2289 if (CLEARP(TTAF_WTMASK)) c += 3;
2290 if (diff&~a->f&TTAF_INVV) c += 3;
2291 if (diff&~a->f&TTAF_STRIKE) c += 3;
2292 if (diff&~a->f&TTAF_ITAL) c += 3;
2293 if (CLEARP(TTAF_FGSPCMASK)) c += 3;
2294 if (CLEARP(TTAF_BGSPCMASK)) c += 3;
2298 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
2299 case TTLN_ULINE: z += 2; break;
2300 case TTLN_UULINE: z += 3; break;
2302 if (a->f&TTAF_WTMASK) z += 2;
2303 if (a->f&TTAF_INVV) z += 2;
2304 if (a->f&TTAF_STRIKE) z += 2;
2305 if (a->f&TTAF_ITAL) z += 2;
2306 #define COLOURCOST(col) do { \
2307 switch ((a->f&TTAF_##col##SPCMASK) >> TTAF_##col##SPCSHIFT) { \
2308 case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break; \
2309 case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break; \
2310 case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break; \
2311 case TTCSPC_8BPC: z += 16; break; \
2314 COLOURCOST(FG); COLOURCOST(BG);
2319 if (z < c || (c && !(t->ansi.f&TAF_CNCATTR)))
2320 { SEMI; diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0; }
2322 if (diff&TTAF_LNMASK)
2323 switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
2324 case TTLN_NONE: SEMI; PUTLIT("24"); break;
2325 case TTLN_ULINE: SEMI; PUTCH('4'); break;
2326 case TTLN_UULINE: SEMI; PUTLIT("21"); break;
2327 default: rc = -1; goto end;
2330 if (diff&TTAF_WTMASK)
2331 switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
2332 case TTWT_MED: SEMI; PUTLIT("22"); break;
2333 case TTWT_BOLD: SEMI; PUTCH('1'); break;
2334 case TTWT_DIM: SEMI; PUTCH('2'); break;
2335 default: rc = -1; goto end;
2339 { SEMI; if (a->f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
2340 if (diff&TTAF_STRIKE)
2341 { SEMI; if (a->f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
2343 { SEMI; if (a->f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
2345 if (diff&TTAF_FGSPCMASK || a->fg != tty->st.attr.fg)
2346 CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
2347 (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
2348 if (diff&TTAF_BGSPCMASK || a->bg != tty->st.attr.bg)
2349 CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
2350 (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
2354 t->tty.st.attr = *a; return (rc);
2358 static int ansi_setmodes(struct tty *tty,
2359 const struct gprintf_ops *gops, void *go,
2360 uint32 modes_bic, uint32 modes_xor)
2365 /* Figure out which modes to set. */
2366 modes = (tty->st.modes&~modes_bic) ^ modes_xor;
2367 diff = modes ^ tty->st.modes;
2369 if (diff&TTMF_AUTOM) {
2370 if (modes&TTMF_AUTOM) PUTLIT("\33[?7h");
2371 else PUTLIT("\33[?7l");
2374 if (diff&TTMF_FSCRN) {
2375 if (modes&TTMF_FSCRN) PUTLIT("\33[?1049h\33[22;0;0t");
2376 else PUTLIT("\33[?1049l\33[23;0;0t");
2379 if (diff&TTMF_CVIS) {
2380 if (modes&TTMF_CVIS) PUTLIT("\33[?25h");
2381 else PUTLIT("\33[?25l");
2384 if (diff&TTMF_INS) {
2385 if (modes&TTMF_INS) PUTLIT("\33[4h");
2386 else PUTLIT("\33[4l");
2391 tty->st.modes = modes;
2395 static int ansi_move(struct tty *tty,
2396 const struct gprintf_ops *gops, void *go,
2397 unsigned orig, int y, int x)
2401 if (orig == TTORG_HOME) {
2403 if (!y) PUTLIT("\33[H");
2404 else CHECK(gprintf(gops, go, "\33[%dH", y + 1));
2406 if (!y) CHECK(gprintf(gops, go, "\33[;%dH", x + 1));
2407 else CHECK(gprintf(gops, go, "\33[%d,%dH", y + 1, x + 1));
2409 } else if (orig == (TTOF_XHOME | TTOF_YCUR) && x == 0 && y == 1)
2412 if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
2413 else if (y == -1) PUTLIT("\33[A");
2414 else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
2415 else if (y == +1) PUTLIT("\33[B"); /* not %|^J|%! */
2416 else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
2417 if (!(orig&TTOF_XCUR)) {
2420 else if (tty->ocaps&TTCF_MIXMV)
2421 CHECK(gprintf(gops, go, "\33[%dG", x + 1));
2423 CHECK(gprintf(gops, go, "\r\33[%dC", x));
2425 if (x == -1) PUTCH('\b');
2426 else if (x < 0) CHECK(gprintf(gops, go, "\33[%dD", -x));
2427 else if (x == +1) PUTLIT("\33[C");
2428 else if (x > 0) CHECK(gprintf(gops, go, "\33[%dC", x));
2436 static int ansi_erase(struct tty *tty,
2437 const struct gprintf_ops *gops, void *go,
2443 switch (f&(TTEF_BEGIN | TTEF_END)) {
2445 case TTEF_BEGIN: PUTLIT("\33[1J"); break;
2446 case TTEF_END: PUTLIT("\33[J"); break;
2447 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2J"); break;
2450 switch (f&(TTEF_BEGIN | TTEF_END)) {
2452 case TTEF_BEGIN: PUTLIT("\33[1K"); break;
2453 case TTEF_END: PUTLIT("\33[K"); break;
2454 case TTEF_BEGIN | TTEF_END: PUTLIT("\33[2K"); break;
2461 static int ansi_erch(struct tty *tty,
2462 const struct gprintf_ops *gops, void *go,
2467 if (n == 1) PUTLIT("\33[X");
2468 else if (n) CHECK(gprintf(gops, go, "\33[%uX", n));
2474 static int ansi_ins(struct tty *tty,
2475 const struct gprintf_ops *gops, void *go,
2476 unsigned f, unsigned n)
2481 if (n == 1) PUTLIT("\33[L");
2482 else if (n) CHECK(gprintf(gops, go, "\33[%uL", n));
2484 if (n == 1) PUTLIT("\33[@");
2485 else if (n) CHECK(gprintf(gops, go, "\33[%u@", n));
2492 static int ansi_inch(struct tty *tty,
2493 const struct gprintf_ops *gops, void *go,
2496 if (!(tty->st.modes&TTMF_INS)) return (-1);
2497 else return (gops->putch(go, ch));
2500 static int ansi_del(struct tty *tty,
2501 const struct gprintf_ops *gops, void *go,
2502 unsigned f, unsigned n)
2507 if (n == 1) PUTLIT("\33[M");
2508 else if (n) CHECK(gprintf(gops, go, "\33[%uM", n));
2510 if (n == 1) PUTLIT("\33[P");
2511 else if (n) CHECK(gprintf(gops, go, "\33[%uP", n));
2524 static const struct tty_ops ansi_ops = {
2526 ansi_setattr, ansi_setmodes,
2527 ansi_move, stupid_repeat,
2528 ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del
2531 static struct tty *ansi_init(FILE *fp)
2534 #define COLS_8 (TTACF_FG | TTACF_BG | TTACF_1BPC)
2535 #define COLS_16 (COLS_8 | TTACF_1BPCBR)
2536 #define COLS_88 (COLS_16 | TTACF_4LPC | TTACF_8LGS)
2537 #define COLS_256 (COLS_16 | TTACF_6LPC | TTACF_24LGS)
2538 #define COLS_16M (COLS_256 | TTACF_8BPC)
2540 #define EDIT_OPS (TTCF_ERCH | \
2541 TTCF_DELCH | TTCF_DELLN | \
2542 TTCF_INSCH | TTCF_INSLN)
2544 static const struct flagmap {
2546 uint32 acaps, ocaps;
2549 { "dim", TTACF_DIM, 0, 0 },
2550 { "uuline", TTACF_UULINE, 0, 0 },
2551 { "strike", TTACF_STRIKE, 0, 0 },
2552 { "ital", TTACF_ITAL, 0, 0 },
2553 { "cvis", 0, TTMF_CVIS, 0 },
2554 { "fscrn", 0, TTMF_FSCRN, 0 },
2555 { "insmode", 0, TTMF_INS, 0 },
2556 { "hvpa" , 0, TTCF_MIXMV, 0 },
2557 { "edit", 0, EDIT_OPS, 0 },
2558 { "cncattr", 0, 0, TAF_CNCATTR },
2559 { "editn", 0, 0, TAF_EDITN },
2560 { "semi", 0, 0, TAF_SEMI },
2566 static const struct kw { const char *name; uint32 val; }
2572 { "256", COLS_256 },
2573 { "16m", COLS_16M },
2577 static const struct enummap {
2580 const struct kw *kw;
2582 { "colours", TTACF_CSPCMASK | TTACF_FG | TTACF_BG,
2588 static const struct termmap {
2590 unsigned acaps, ocaps, tf;
2593 #define VT100_ACAPS (TTACF_ULINE | TTACF_BOLD | TTACF_INVV)
2594 #define VT100_OCAPS (TTMF_AUTOM | \
2595 TTCF_RELMV | TTCF_ABSMV | \
2597 TTCF_ERBOL | TTCF_EREOL | \
2598 TTCF_ERBOD | TTCF_EREOD | TTCF_ERDSP)
2599 #define VT100_TF (0)
2601 #define VT102_ACAPS (VT100_ACAPS)
2602 #define VT102_OCAPS (VT100_OCAPS | \
2604 TTCF_INSCH | TTCF_INSLN | TTCF_DELCH | TTCF_DELLN)
2605 #define VT102_TF (VT100_TF)
2607 #define VT220_ACAPS (VT102_ACAPS)
2608 #define VT220_OCAPS (VT102_OCAPS | TTMF_CVIS | TTCF_ERCH)
2609 #define VT220_TF (VT102_TF | TAF_CNCATTR | TAF_EDITN)
2611 #define ECMA48_ACAPS (VT220_ACAPS | TTACF_DIM)
2612 #define ECMA48_OCAPS (VT220_OCAPS | TTCF_MIXMV)
2613 #define ECMA48_TF (VT220_TF)
2615 #define XTERM_ACAPS (ECMA48_ACAPS)
2616 #define XTERM_OCAPS (ECMA48_OCAPS | TTMF_FSCRN)
2617 #define XTERM_TF (ECMA48_TF)
2619 #define STRIKE TTACF_STRIKE
2620 #define ITAL TTACF_ITAL
2621 #define SEMI TAF_SEMI
2623 #define T(pat, base, cols, acaps, ocaps, tf) \
2625 base##_ACAPS | COLS_##cols | (acaps), \
2626 base##_OCAPS | (ocaps), base##_TF | (tf) }
2628 T("color_xterm", XTERM, 8, STRIKE | ITAL, 0, 0),
2630 T("gnome", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
2631 /*T("gonme-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
2633 T("linux", XTERM, 16, 0, 0, 0),
2635 T("putty", XTERM, 16M, 0, 0, SEMI),
2637 T("vt100*", VT100, NO, 0, 0, 0),
2638 T("vt102*", VT102, NO, 0, 0, 0),
2639 T("vt[2-5][0-9][0-9]*", VT220, NO, 0, 0, 0),
2641 T("vte", XTERM, 16M, STRIKE | ITAL, 0, SEMI),
2642 /*T("vte-*" XTERM, 16M, STRIKE | ITAL, 0, SEMI),*/
2644 T("win", XTERM, 16M, 0, 0, SEMI),
2646 T("xterm", XTERM, 16M, STRIKE | ITAL, 0, 0),
2647 T("xterm-color", XTERM, 8, STRIKE | ITAL, 0, 0),
2648 T("xterm-16color", XTERM, 16, STRIKE | ITAL, 0, 0),
2649 T("xterm-88color", XTERM, 88, STRIKE | ITAL, 0, SEMI),
2650 T("xterm-256color", XTERM, 256, STRIKE | ITAL, 0, SEMI),
2651 T("xterm-direct", XTERM, 16M, STRIKE | ITAL, 0, 0),
2652 T("xterm-*", XTERM, 16M, STRIKE | ITAL, 0, 0),
2654 /*T("*-color", XTERM, 16, 0, 0, 0),*/
2655 /*T("*-16color", XTERM, 16, 0, 0, 0),*/
2656 T("*-88color", XTERM, 88, 0, 0, SEMI),
2657 T("*-256color", XTERM, 256, 0, 0, SEMI),
2658 T("*-direct", XTERM, 16M, 0, 0, SEMI),
2660 T("*", XTERM, 16, 0, 0, 0),
2695 union tty_ansiu *u = 0; struct tty *ret = 0;
2696 const char *term, *config, *p, *l;
2697 const struct kw *kw;
2698 const struct enummap *em;
2699 const struct flagmap *fm;
2700 const struct termmap *tm;
2703 acaps = 0, ocaps = 0, tf = 0,
2704 acapset = 0, ocapset = 0, tfset = 0,
2708 config = getenv("MLIB_TTY_ANSICONFIG");
2709 term = getenv("TERM");
2711 if (term && STRCMP(term, ==, "dumb")) goto end;
2714 l = config + strlen(config);
2718 if (config >= l) goto done_config;
2719 else if (!ISSPACE(*config)) break;
2722 for (p = config + 1; p < l && !ISSPACE(*p); p++);
2723 if (*config == '+' || *config == '-') {
2724 if (*config == '+') f |= f_sense;
2726 config++; n = p - config;
2728 for (fm = flagmap; fm->name; fm++)
2729 if (STRNCMP(config, ==, fm->name, n) && !fm->name[n])
2731 debug("unknown flag `%.*s'", (int)n, config); goto next_config;
2733 if ((acapset&fm->acaps) || (ocapset&fm->ocaps) || (tfset&fm->tf)) {
2734 debug("duplicate setting for `%s'", fm->name);
2738 { acaps |= fm->acaps; ocaps |= fm->ocaps; tf |= fm->tf; }
2739 acapset |= fm->acaps; ocapset |= fm->ocaps; tfset |= fm->tf;
2742 p = memchr(config, '=', n);
2744 debug("missing `=' in setting `%.*s'", (int)n, config);
2748 for (em = enummap; em->name; em++)
2749 if (STRNCMP(config, ==, em->name, nn) && !em->name[nn])
2751 debug("unknown setting `%.*s'", (int)nn, config); goto next_config;
2753 p++; nn = n - nn - 1;
2754 for (kw = em->kw; kw->name; kw++)
2755 if (STRNCMP(p, ==, kw->name, nn) && !kw->name[nn])
2757 debug("unknown `%s' value `%.*s", em->name, (int)nn, p);
2760 if (acapset&em->mask) {
2761 debug("duplicate setting for `%s'", em->name);
2764 acaps |= kw->val; acapset |= em->mask;
2774 for (tm = termmap; tm->pat; tm++)
2775 if (str_match(tm->pat, term))
2779 acaps |= tm->acaps&~acapset;
2780 ocaps |= tm->ocaps&~ocapset;
2781 tf |= tm->tf&~tfset;
2784 if (!(acapset&TTACF_CSPCMASK)) env_colour_caps(&acaps, ECCF_SET);
2785 if (acaps&TTACF_CSPCMASK) ocaps |= TTCF_BGER;
2788 u->tty.ops = &ansi_ops;
2789 u->tty.acaps = acaps;
2790 u->tty.ocaps = ocaps;
2791 u->ansi.ansi.f = tf;
2792 u->tty.wd = 80; u->tty.ht = 25;
2793 u->tty.st.modes = TTMF_AUTOM | (u->tty.ocaps&TTMF_CVIS);
2794 u->tty.st.attr.f = 0; u->tty.st.attr.fg = u->tty.st.attr.bg = 0;
2795 common_init(&u->ansi.tty, fp);
2796 ret = &u->tty; u = 0;
2798 xfree(u); return (ret);
2803 /*----- Backend selection -------------------------------------------------*/
2805 struct tty *tty_open(FILE *fp, unsigned f, const unsigned *backends)
2807 static const struct betab {
2808 const char *name; unsigned code;
2809 struct tty *(*init)(FILE */*fp*/);
2811 #ifdef HAVE_UNIBILIUM
2812 { "unibilium", TTBK_UNIBI, termunibi_init },
2814 #ifdef HAVE_TERMINFO
2815 { "terminfo", TTBK_TERMINFO, terminfo_init },
2818 { "termcap", TTBK_TERMCAP, termcap_init },
2820 { "ansi", TTBK_ANSI, ansi_init },
2824 const struct betab *bt;
2825 const char *config, *p, *l;
2826 struct tty *tty = 0;
2830 if (fp || !(f&TTF_OPEN))
2831 fpin = fp != stdout ? fp : isatty(STDIN_FILENO) ? stdin : 0;
2833 if (isatty(STDIN_FILENO)) fpin = stdin;
2835 if (isatty(STDOUT_FILENO)) { fp = stdout; f |= TTF_BORROW; }
2836 else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
2838 fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
2839 fpin = fp; f &= ~TTF_BORROW;
2843 config = getenv("MLIB_TTY_BACKENDS");
2845 l = config + strlen(config);
2848 if (config >= l) goto done_config;
2849 else if (!ISSPACE(*config)) break;
2852 for (p = config + 1; p < l && !ISSPACE(*p); p++);
2855 for (bt = betab; bt->name; bt++)
2856 if (STRNCMP(config, ==, bt->name, n) && !bt->name[n])
2858 debug("unknown backend `%.*s'", (int)n, config); goto next_config;
2860 tty = bt->init(fp); if (tty) goto found;
2861 debug("failed to initialize `%s'", bt->name);
2866 } else if (backends)
2868 for (bt = betab; bt->name; bt++)
2869 if (*backends == bt->code) goto found_bycode;
2870 debug("unknown backend code %u", *backends); goto next_code;
2872 tty = bt->init(fp); if (tty) goto found;
2873 debug("failed to initialize `%s'", bt->name);
2878 for (bt = betab; bt->name; bt++) {
2879 tty = bt->init(fp); if (tty) goto found;
2880 debug("failed to initialize `%s'", bt->name);
2883 debug("all backends failed"); goto end;
2885 debug("selected backend `%s'", bt->name);
2886 tty->fpin = fpin; tty->f = f; fp = 0;
2888 if (fp && !(f&TTF_BORROW)) fclose(fp);
2892 void tty_close(struct tty *tty)
2895 if (tty->fpout && !(tty->f&TTF_BORROW)) fclose(tty->fpout);
2896 tty->ops->release(tty); xfree(tty);
2900 int tty_resized(struct tty *tty)
2904 if (!tty || !tty->fpout) { errno = ENOTTY; return (-1); }
2905 else if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
2906 else if (tty->wd == ws.ws_col && tty->ht == ws.ws_row) return (0);
2907 else { tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (1); }
2910 /*----- Terminal operations -----------------------------------------------*/
2912 int tty_setattr(struct tty *tty, const struct tty_attr *a)
2916 if (!tty || !tty->fpout)
2919 clamp_attr(&aa, a, tty->acaps);
2920 return (tty->ops->setattr(tty, &file_printops, tty->fpout, &aa));
2924 int tty_setattrg(struct tty *tty,
2925 const struct gprintf_ops *gops, void *go,
2926 const struct tty_attr *a)
2933 clamp_attr(&aa, a, tty->acaps);
2934 return (tty->ops->setattr(tty, gops, go, &aa));
2938 int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
2940 if (!tty || !tty->fpout) return (-1);
2941 else return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa));
2944 int tty_setattrlistg(struct tty *tty,
2945 const struct gprintf_ops *gops, void *go,
2946 const struct tty_attrlist *aa)
2948 if (!tty) return (-1);
2950 if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
2951 return (tty->ops->setattr(tty, gops, go, &aa->attr));
2952 else if (!aa->cap_mask)
2956 int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
2958 if (!tty || !tty->fpout) return (-1);
2959 else return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
2960 modes_bic, modes_xor));
2963 int tty_setmodesg(struct tty *tty,
2964 const struct gprintf_ops *gops, void *go,
2965 uint32 modes_bic, uint32 modes_xor)
2967 if (!tty) return (-1);
2968 else return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor));
2971 int tty_move(struct tty *tty, unsigned orig, int y, int x)
2973 if (!tty || !tty->fpout) return (-1);
2974 else return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x));
2977 int tty_moveg(struct tty *tty,
2978 const struct gprintf_ops *gops, void *go,
2979 unsigned orig, int y, int x)
2981 if (!tty) return (-1);
2982 else return (tty->ops->move(tty, gops, go, orig, y, x));
2985 int tty_repeat(struct tty *tty, int ch, unsigned n)
2987 if (!tty || !tty->fpout) return (-1);
2988 else return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n));
2991 int tty_repeatg(struct tty *tty,
2992 const struct gprintf_ops *gops, void *go,
2995 if (!tty) return (-1);
2996 else return (tty->ops->repeat(tty, gops, go, ch, n));
2999 int tty_erase(struct tty *tty, unsigned f)
3001 if (!tty || !tty->fpout) return (-1);
3002 else return (tty->ops->erase(tty, &file_printops, tty->fpout, f));
3005 int tty_eraseg(struct tty *tty,
3006 const struct gprintf_ops *gops, void *go,
3009 if (!tty) return (-1);
3010 else return (tty->ops->erase(tty, gops, go, f));
3013 int tty_erch(struct tty *tty, unsigned n)
3015 if (!tty || !tty->fpout) return (-1);
3016 else return (tty->ops->erch(tty, &file_printops, tty->fpout, n));
3019 int tty_erchg(struct tty *tty,
3020 const struct gprintf_ops *gops, void *go,
3023 if (!tty) return (-1);
3024 else return (tty->ops->erch(tty, gops, go, n));
3027 int tty_ins(struct tty *tty, unsigned f, unsigned n)
3029 if (!tty || !tty->fpout) return (-1);
3030 else return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n));
3033 int tty_insg(struct tty *tty,
3034 const struct gprintf_ops *gops, void *go,
3035 unsigned f, unsigned n)
3037 if (!tty) return (-1);
3038 else return (tty->ops->ins(tty, gops, go, f, n));
3041 int tty_inch(struct tty *tty, int ch)
3043 if (!tty || !tty->fpout) return (-1);
3044 else return (tty->ops->inch(tty, &file_printops, tty->fpout, ch));
3047 int tty_inchg(struct tty *tty,
3048 const struct gprintf_ops *gops, void *go,
3051 if (!tty) return (-1);
3052 else return (tty->ops->inch(tty, gops, go, ch));
3055 int tty_del(struct tty *tty, unsigned f, unsigned n)
3057 if (!tty || !tty->fpout) return (-1);
3058 else return (tty->ops->del(tty, &file_printops, tty->fpout, f, n));
3061 int tty_delg(struct tty *tty,
3062 const struct gprintf_ops *gops, void *go,
3063 unsigned f, unsigned n)
3065 if (!tty) return (-1);
3066 else return (tty->ops->del(tty, gops, go, f, n));
3069 int tty_restore(struct tty *tty, const struct tty_state *st)
3071 if (!tty || !tty->fpout) return (-1);
3072 else return (tty_restoreg(tty, &file_printops, tty->fpout, st));
3075 int tty_restoreg(struct tty *tty,
3076 const struct gprintf_ops *gops, void *go,
3077 const struct tty_state *st)
3082 tty->ops->setmodes(tty, gops, go, MASK32, st->modes) ||
3083 tty->ops->setattr(tty, gops, go, &st->attr))
3084 { rc = -1; goto end; }
3090 /*----- That's all, folks -------------------------------------------------*/