#include "str.h"
#include "tty.h"
+/*----- Operations table --------------------------------------------------*/
+
+/* Incorporate the published control-block structure into our more elaborate
+ * object model.
+ */
+#define TTY_BASEPFX struct tty tty
+#define TTY_BASEUSFX struct tty tty
+
+struct tty_ops {
+ void (*release)(struct tty */*tty*/);
+ /* Free any resources held by the backend. */
+
+ /* The following operations handle the correspondingly named interface
+ * functions.
+ */
+ int (*setattr)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ const struct tty_attr */*a*/);
+ int (*setmodes)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ uint32 /*modes_bic*/, uint32 /*modes_xor*/);
+ int (*move)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*orig*/, int /*y*/, int /*x*/);
+ int (*repeat)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ int /*ch*/, unsigned /*n*/);
+ int (*erase)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*f*/);
+ int (*erch)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*n*/);
+ int (*ins)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*f*/, unsigned /*n*/);
+ int (*inch)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ int /*ch*/);
+ int (*del)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*f*/, unsigned /*n*/);
+};
+#define TTY_BASEOPSPFX struct tty_ops tty
+#define TTY_BASEOPSUXFX struct tty_ops tty
+
/*----- Common support machinery ------------------------------------------*/
+/* --- @CHECK@ --- *
+ *
+ * Arguments@ @expr@ = expression to evaluate
+ *
+ * Use: Evaluate @expr@. If the result is (strictly) negative, then
+ * set @rc = -1@ and transfer control to the label @end@.
+ */
+
+#define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
+
/* --- @debug@ --- *
*
* Arguments: @const char *fmt@ = format control string
}
}
+/* --- @common_init@ --- *
+ *
+ * Arguments: @struct tty *tty@ = pointer to terminal control block
+ * @FILE *fp@ = output file stream
+ *
+ * Returns: ---
+ *
+ * Use: Perform general initialization on the terminal control
+ * block.
+ *
+ * Specifically, this fills in the @fpout@, @baud@, @ht@, and
+ * @wd@ slots. The width and height come from the kernel, or,
+ * failing that, the environment.
+ */
+
static void common_init(struct tty *tty, FILE *fp)
{
static const struct baudtab { speed_t code; unsigned baud; } baudtab[] = {
struct termios c;
speed_t code;
+ /* Save the output stream. */
tty->fpout = fp;
+
+ /* Determine the output baud rate. Unhelpfully, the kernel provides a
+ * weird code, so we have to convert it into an actual rate in bits per
+ * second.
+ */
if (!fp || tcgetattr(fileno(fp), &c))
tty->baud = 0;
else {
tty_resized(tty);
}
+ /* If the kernel didn't tell us the terminal dimensions, try to read them
+ * from the environment.
+ */
if (!tty->wd)
{ p = getenv("COLUMNS"); if (p) { n = atoi(p); if (n) tty->wd = n; } }
if (!tty->ht)
{ p = getenv("LINES"); if (p) { n = atoi(p); if (n) tty->ht = n; } }
}
-static void env_colour_caps(unsigned *caps_inout)
+/* --- @env_colour_caps@ --- *
+ *
+ * Arguments: @unsigned *caps_inout@ = attribute capabilities to update
+ * @unsigned f@ = flags
+ *
+ * Returns: ---
+ *
+ * Use: Check the %|FORCE_COLOR|% environment variable and update the
+ * capabilities as required.
+ *
+ * The %|FORCE_COLOR|% variable originates with the Node
+ * community, with two objectives: (a) to convey policy
+ * regarding whether to produce coloured output, and (b) to
+ * describe the colour capabilities of the terminal, because the
+ * traditional mechanisms are deemed inadequate.
+ *
+ * The following values have defined meanings.
+ *
+ * * Unset or empty: no effect.
+ *
+ * * %|0|%: monochrome; don't produce colour.
+ *
+ * * %|1|%: 16 colours; 1-bit-per-channel colours are
+ * available, with an additional common brightness bit.
+ *
+ * * %|2|%: 256 colours; the `xterm' 256-bit palette is
+ * available, consisting of the 1-bit-per-channel colours
+ * with common brightness bit, a 6 × 6 × 6 colour cube, and
+ * a 24-level greyscale ramp.
+ *
+ * * %|3|%: full 24-bit colour.
+ *
+ * * Anything else: a request to use colour if available.
+ * This is ignored here, in the expectation that it will be
+ * given effect elsewhere, e.g., by @ttycolour_enablep@.
+ *
+ * If @ECCF_SET@ is set, then set or clear capabilities as
+ * required. Otherwise, clear capability bits which are denied
+ * by the variable setting, but no bits will be set. (This
+ * latter is necessary for backends which use terminal
+ * databases, since they can't be expected to make up the
+ * necessary control sequences for themselves.)
+ */
+
+#define ECCF_SET 1u
+static void env_colour_caps(unsigned *caps_inout, unsigned f)
{
const char *p;
- unsigned caps = *caps_inout;
+ unsigned caps = *caps_inout, mask;
- p = getenv("FORCE_COLOR");
- if (p) switch (*p) {
+ p = getenv("FORCE_COLOR"); if (!p) return;
+ switch (*p) {
case '0':
- caps &= TTACF_CSPCMASK | TTACF_FG | TTACF_BG;
+ mask = 0;
break;
case '1':
- caps = (caps&~TTACF_CSPCMASK) | TTACF_FG | TTACF_BG |
- TTACF_1BPC | TTACF_1BPCBR;
+ mask = TTACF_FG | TTACF_BG | TTACF_1BPC | TTACF_1BPCBR;
break;
case '2':
- caps = (caps&~TTACF_CSPCMASK) | TTACF_FG | TTACF_BG |
- TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_8LGS;
+ mask = TTACF_FG | TTACF_BG |
+ TTACF_1BPC | TTACF_1BPCBR | TTACF_6LPC | TTACF_24LGS;
break;
case '3':
- caps = (caps&~TTACF_CSPCMASK) | TTACF_FG | TTACF_BG |
+ mask = TTACF_FG | TTACF_BG |
TTACF_1BPC | TTACF_1BPCBR | TTACF_8BPC;
break;
+ default:
+ return;
}
+ if (!(f&ECCF_SET)) caps &= mask;
+ else caps = (caps&~(TTACF_CSPCMASK | TTACF_FG | TTACF_BG)) | mask;
+
*caps_inout = caps;
}
#undef D
}
-/* --- @tty_clampattr@ --- *
+/* --- @clamp_attr@ --- *
*
* Arguments: @struct tty_attr *a_out@ = selected attributes
* @const struct tty_attr *a@ = requested attributes
* which can be accommodated by the terminal.
*/
-void tty_clampattr(struct tty_attr *a_out,
- const struct tty_attr *a, uint32 acaps)
+static void clamp_attr(struct tty_attr *a_out,
+ const struct tty_attr *a, uint32 acaps)
{
uint32 ff = 0, f = a ? a->f : 0, t;
a_out->f = ff; a_out->_res0 = 0;
}
-int tty_resized(struct tty *tty)
+/* --- @stupid_repeat@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @int ch@ = character to write
+ * @unsigned n@ = number of copies
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Write @n@ copies of the character @ch@ to the terminal, the
+ * hard way. This function tries to be reasonably efficient, by
+ * transmitting buffers rather than exercising the output
+ * machinery for each individual character.
+ */
+
+static int stupid_repeat(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ int ch, unsigned n)
{
- struct winsize ws;
+ char buf[4096];
+ unsigned nn;
+ int rc;
- if (!tty->fpout) { errno = ENOTTY; return (-1); }
- if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
- tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (0);
+ if (n < sizeof(buf))
+ { memset(buf, ch, n); CHECK(gops->putm(go, buf, n)); }
+ else {
+ memset(buf, ch, sizeof(buf));
+ nn = sizeof(buf);
+ for (;;) {
+ CHECK(gops->putm(go, buf, nn));
+ n -= nn; if (!n) break;
+ if (n < nn) nn = n;
+ }
+ }
+ rc = 0;
+end:
+ return (rc);
}
-/*----- Common machinery for `termcap' and `terminfo' ---------------------*/
+/*----- Common machinery for %|termcap|% and %|terminfo|% -----------------*/
#if defined(HAVE_TERMINFO) || \
defined(HAVE_TERMCAP) || \
#if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
-static const struct gprintf_ops *global_gops;
-static void *global_gout;
-static struct tty *global_lock = 0;
+/* Global state.
+ *
+ * The `termcap' and `terminfo' functions call a user-provided function to
+ * actually send control codes to the terminal. The bad news is that
+ * `termcap' doesn't provide any way to pass information to the output
+ * function beyond the character to be sent, and `terminfo' doesn't fix this
+ * mistake. So we must save the necessary context as global variables. For
+ * good measure, at least some implementations ignore errors from the output
+ * function, so we must keep track of them ourselves. More global variables.
+ *
+ * It's worse. Both libraries maintain significant global state of their
+ * own. And, at least with the `ncurses' implementation, the two share the
+ * same global state. The only thing to do is maintain a big interlock to
+ * make sure that only one is active at a time.
+ */
+static const struct gprintf_ops *global_gops; /* output operations ... */
+static void *global_gout; /* and context, for @caps_putch */
+static char global_buf[4096]; /* a big output buffer */
+static size_t global_len; /* length of buffer used */
+static int global_err; /* error latch, zero if all ok */
+static struct tty *global_lock = 0; /* interlock for global state */
+
+/* --- @caps_claim@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Zero on success, %$-1$% if already claimed.
+ *
+ * Use: Return %$-1$% if the interlock is already held. This is a
+ * function to call near the beginning of initializing a new
+ * control block, before the common global state gets
+ * clobbered. If initialization is successful, the caller is
+ * expected to actually store the control block pointer in
+ * @global_lock@ themselves.
+ */
+
+static int caps_claim(void)
+{
+ if (global_lock)
+ { debug("termcap/terminfo terminal already open"); return (-1); }
+ else
+ return (0);
+}
+
+/* --- @caps_claim@, @caps_release@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer for current lock
+ * holder
+ *
+ * Returns: ---
+ *
+ * Use: Release the lock.
+ */
static void caps_release(struct tty *tty)
{ assert(global_lock == tty); global_lock = 0; }
+/* --- @caps_putch@ --- *
+ *
+ * Arguments: @int ch@ = character to write
+ *
+ * Returns: Nonnegative on success, negative on failure. (But @tputs@
+ * ignores this.)
+ *
+ * Use: Output the character @ch@.
+ */
+
static int caps_putch(int ch)
- { return (global_gops->putch(global_gout, ch)); }
+{
+ if (global_len >= sizeof(global_buf)) {
+ if (global_gops->putm(global_gout, global_buf, global_len))
+ global_err = -1;
+ global_len = 0;
+ }
+ global_buf[global_len++] = ch;
+ return (0);
+}
-#endif
+/* --- @caps_prepout@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer (ignored)
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ *
+ * Returns: ---
+ *
+ * Use: Prepare output to the given destination.
+ */
+
+static void caps_prepout(struct tty *tty,
+ const struct gprintf_ops *gops, void *go)
+ { assert(!global_len); global_gops = gops; global_gout = go; }
+
+/* --- @caps_flush@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer (ignored)
+ *
+ * Returns: Zero for success, %$-1$% if error pending.
+ *
+ * Use: Flush the output buffer to the backend. If an error is
+ * pending, clear it and return failure.
+ */
+
+static int caps_flush(struct tty *tty)
+{
+ int rc = global_err;
+
+ if (global_len) {
+ if (global_gops->putm(global_gout, global_buf, global_len)) rc = -1;
+ global_len = 0;
+ }
+ global_err = 0; return (rc);
+}
-#ifdef HAVE_UNIBILIUM
-# define UNIBI_(x) unibi##x
-#else
-# define UNIBI_(x) 0
#endif
+/* The list of interesting capabilities.
+ *
+ * We never actually need all of these: some are only needed if others are
+ * unavailable. But the list isn't too huge, so we'll live with it.
+ *
+ * The main thing is that each capability has three different names: the
+ * `full' name (corresponding to a `terminfo' variable name), the `terminfo'
+ * capability name, as used in terminal descriptions, and the two-character
+ * `termcap' name. Unibilium uses the long names, but to reduce typing, the
+ * `unibi_' prefix is omitted here. (Annoyingly, in `ncurses', at least, the
+ * `variable' names are `secretly' macros referencing a current state, and
+ * premature expansion causes misery, so I've left the leading underscores in
+ * place as a countermeasure.) Internally, we use the short `terminfo'
+ * names, since they generally express the most useful information in the
+ * smallest space.
+ */
+
#define BASICCAPS(_bool, _int, _str) \
_str(_repeat_char, rep, rp) \
- _int(_padding_baud_rate, pb, pb) _str(_pad_char, pad, pc) \
+ _str(_pad_char, pad, pc) _int(_padding_baud_rate, pb, pb) \
_bool(_no_pad_char, npc, NP) _bool(_xon_xoff, xon, xo) \
_bool(_move_insert_mode, mir, mi) _bool(_move_standout_mode, msgr, ms)
ERASECAPS(_bool, _int, _str) \
INSDELCAPS(_bool, _int, _str)
+#ifdef HAVE_UNIBILIUM
+# define UNIBI_(x) unibi##x
+#else
+# define UNIBI_(x) 0
+#endif
+
#define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
+ /* Expand a capability triple into a group of three usable C arguments. If
+ * Unibilium isn't available, then we can use nonsense for its cap index.
+ */
+/* Some other capabilities which we want to refer to during initialization,
+ * but don't need to keep around.
+ */
#define CAP_XMC CAPREF(_magic_cookie_glitch, xmc, sg)
#define CAP_BCE CAPREF(_back_color_erase, bce, ut)
#define CAP_XHPA CAPREF(_row_addr_glitch, xvpa, YD)
#define CAP_HT CAPREF(_lines, lines, li)
#define CAP_WD CAPREF(_columns, cols, co)
-#define TTY_BASEOPSPFX struct tty_ops tty
-#define TTY_BASEOPSUXFX struct tty_ops tty
-#define TTY_BASEPFX struct tty tty
-#define TTY_BASEUSFX struct tty tty
-
+/* Additional operations required of terminal backends which make use of the
+ * common capability machinery.
+ */
struct tty_capopslots {
+
+ /* Retrieving capabilities. */
int (*boolcap)(struct tty */*tty*/,
int /*uix*/, const char */*info*/, const char */*cap*/);
int (*intcap)(struct tty */*tty*/,
const char *(*strcap)(struct tty */*tty*/,
int /*uix*/, const char */*info*/,
const char */*cap*/);
- int (*put0)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- unsigned /*npad*/, const char */*cap*/);
+
+ /* Preparing and completing output. */
+ void (*prepout)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/);
+ int (*flush)(struct tty */*tty*/);
+
+ /* Writing capabilities with various kinds of arguments. */
+ int (*put0)(struct tty */*tty*/, unsigned /*npad*/, const char */*cap*/);
int (*put1i)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*npad*/, const char */*cap*/, int /*i0*/);
int (*put2i)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*npad*/,
const char */*cap*/, int /*i0*/, int /*i1*/);
};
#define TTY_CAPOPSUSFX struct tty_capops cap; TTY_BASEOPSUXFX
union tty_capopsu { TTY_CAPOPSUSFX; };
+/* An extension of the control block to track the above capabilities. */
struct tty_capslots {
#define DEF_BOOLCAP(uix, info, cap) unsigned info : 1;
#define DEF_INTCAP(uix, info, cap) int info;
struct tty tty
union tty_capsu { TTY_CAPSUSFX; };
+/* ---- @init_caps@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ *
+ * Returns: ---
+ *
+ * Use: Populate the capabilities in the terminal control block, and
+ * advertise the results to the public part. Set @ht@ and @wd@
+ * from the terminal description if they've not been set
+ * already.
+ */
+
static void init_caps(struct tty_caps *t)
{
const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
else if (t->cap.colors >= 16)
t->tty.acaps |= TTACF_1BPCBR;
if (ops->cap.boolcap(&t->tty, CAP_BCE)) t->tty.ocaps |= TTCF_BGER;
- env_colour_caps(&t->tty.acaps);
+ env_colour_caps(&t->tty.acaps, 0);
}
}
{ ht = ops->cap.intcap(&t->tty, CAP_HT); if (ht > 0) t->tty.ht = ht; }
}
-#define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
-
+/* Macros for formatting capabilities. */
#define PUT0V(npad, cap_) \
- CHECK(ops->cap.put0(&t->tty, gops, go, (npad), (cap_)))
+ CHECK(ops->cap.put0(&t->tty, (npad), (cap_)))
#define PUT1IV(npad, cap_, i0) \
- CHECK(ops->cap.put1i(&t->tty, gops, go, (npad), (cap_), (i0)))
+ CHECK(ops->cap.put1i(&t->tty, (npad), (cap_), (i0)))
#define PUT2IV(npad, cap_, i0, i1) \
- CHECK(ops->cap.put2i(&t->tty, gops, go, (npad), (cap_), (i0), (i1)))
+ CHECK(ops->cap.put2i(&t->tty, (npad), (cap_), (i0), (i1)))
#define PUT0(npad, name) PUT0V(npad, t->cap.name)
#define PUT1I(npad, name, i0) PUT1IV(npad, t->cap.name, i0)
#define PUT2I(npad, name, i0, i1) PUT2IV(npad, t->cap.name, i0, i1)
+/* --- @caps_setcolour@ --- *
+ *
+ * Arguments: @struct tty_caps *t@ = extended control block pointer
+ *
+ */
static int caps_setcolour(struct tty_caps *t,
- const struct gprintf_ops *gops, void *go,
const char *cap, uint32 spc, uint32 clr)
{
const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
case TTCSPC_24LGS: PUT1IV(0, cap, clr + 232); break;
case TTCSPC_8BPC:
- /* There's an unfortunate ambiguity in the `setaf' conventions. The
+ /* There's an unfortunate ambiguity in the %|setaf|% conventions. The
* first eight colours should be dark shades of blue, but in fact
* they're interpreted as the one-bit-per-channel basic colours by
* common `terminfo' settings. Notice and kludge by adding a little
return (rc);
}
-static int caps_setattr(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
- const struct tty_attr *a)
+static int caps_setattr_internal(struct tty_caps *t,
+ const struct tty_attr *a)
{
- struct tty_caps *t = (struct tty_caps *)tty;
const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
- struct tty_attr aa;
uint32 diff;
int rc;
/* Work out what needs doing. */
- tty_clampattr(&aa, a, t->tty.acaps);
- diff = aa.f ^ t->tty.st.attr.f;
+ diff = a->f ^ t->tty.st.attr.f;
/* Some terminals might not be able to clear individual attributes that
* they can set, and some capabilities for turning attributes on don't even
* have a corresponding attribute for turning them off again individually,
- * so we have to use `sgr0' to start from scratch. Of course, if we need
+ * so we have to use %|sgr0|% to start from scratch. Of course, if we need
* to do that, we need to restore the other active attributes, so we must
* check up front.
*/
- if (((diff&TTAF_LNMASK) && !(aa.f&TTAF_LNMASK) && !t->cap.rmul) ||
- ((diff&TTAF_WTMASK) && !(aa.f&TTAF_WTMASK)) ||
- ((diff&~aa.f&TTAF_ITAL) && !t->cap.ritm) ||
- (diff&~aa.f&TTAF_INVV))
- { PUT0(0, sgr0); diff = aa.f; }
+ if (((diff&TTAF_LNMASK) && !(a->f&TTAF_LNMASK) && !t->cap.rmul) ||
+ ((diff&TTAF_WTMASK) && !(a->f&TTAF_WTMASK)) ||
+ ((diff&~a->f&TTAF_ITAL) && !t->cap.ritm) ||
+ (diff&~a->f&TTAF_INVV))
+ { PUT0(0, sgr0); diff = a->f; }
/* Line style. */
if (diff&TTAF_LNMASK)
- switch ((aa.f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
+ switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
case TTLN_NONE: PUT0(0, rmul); break;
case TTLN_ULINE: PUT0(0, smul); break;
/* case TTLN_UULINE: */
/* Text weight. */
if (diff&TTAF_WTMASK)
- switch ((aa.f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
+ switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
/* case TTWT_MED: */
case TTWT_BOLD: PUT0(0, bold); break;
case TTWT_DIM: PUT0(0, dim); break;
/* Other text effects. */
if (diff&TTAF_ITAL) {
- if (aa.f&TTAF_ITAL) PUT0(0, sitm);
+ if (a->f&TTAF_ITAL) PUT0(0, sitm);
else PUT0(0, ritm);
}
- if (diff&aa.f&TTAF_INVV) PUT0(0, rev);
+ if (diff&a->f&TTAF_INVV) PUT0(0, rev);
/* Colours. */
- if (((diff&TTAF_FGSPCMASK) && !(aa.f&TTAF_FGSPCMASK)) ||
- ((diff&TTAF_BGSPCMASK) && !(aa.f&TTAF_BGSPCMASK))) {
+ if (((diff&TTAF_FGSPCMASK) && !(a->f&TTAF_FGSPCMASK)) ||
+ ((diff&TTAF_BGSPCMASK) && !(a->f&TTAF_BGSPCMASK))) {
/* There's no capability string for resetting just the foreground
* or background colours to the defaults, so deal with that here.
*/
PUT0(0, op);
diff = (diff&~(TTAF_FGSPCMASK | TTAF_BGSPCMASK)) |
- (aa.f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
+ (a->f&(TTAF_FGSPCMASK | TTAF_BGSPCMASK));
}
- if ((diff&TTAF_FGSPCMASK) || aa.fg != t->tty.st.attr.fg)
- CHECK(caps_setcolour(t, gops, go, t->cap.setaf,
- (aa.f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, aa.fg));
- if ((diff&TTAF_BGSPCMASK) || aa.bg != t->tty.st.attr.bg)
- CHECK(caps_setcolour(t, gops, go, t->cap.setab,
- (aa.f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, aa.bg));
+ if ((diff&TTAF_FGSPCMASK) || a->fg != t->tty.st.attr.fg)
+ CHECK(caps_setcolour(t, t->cap.setaf,
+ (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
+ if ((diff&TTAF_BGSPCMASK) || a->bg != t->tty.st.attr.bg)
+ CHECK(caps_setcolour(t, t->cap.setab,
+ (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
/* All done. */
rc = 0;
end:
- t->tty.st.attr = aa; return (rc);
+ t->tty.st.attr = *a; return (rc);
+}
+
+static int caps_setattr(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ const struct tty_attr *a)
+{
+ struct tty_caps *t = (struct tty_caps *)tty;
+ const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
+ int rc;
+
+ ops->cap.prepout(&t->tty, gops, go);
+ rc = caps_setattr_internal(t, a);
+ if (ops->cap.flush(&t->tty)) rc = -1;
+ return (rc);
}
static int caps_setmodes(struct tty *tty,
modes = (t->tty.st.modes&~modes_bic) ^ modes_xor;
diff = modes ^ t->tty.st.modes;
+ /* Prepare output. */
+ ops->cap.prepout(&t->tty, gops, go);
+
/* Automatic margins. */
if (diff&TTMF_AUTOM) {
if (modes&TTMF_AUTOM) PUT0(0, smam);
/* Done. */
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
t->tty.st.modes = modes; return (rc);
}
#define CIF_PADMUL 1u
-
static int caps_iterate(struct tty_caps *t,
- const struct gprintf_ops *gops, void *go,
const char *cap1, const char *capn,
unsigned f, unsigned npad, unsigned n)
{
}
static int caps_move_relative(struct tty_caps *t,
- const struct gprintf_ops *gops, void *go,
int delta,
const char *fw1, const char *fwn,
const char *rv1, const char *rvn)
if (!delta) return (0);
else if (delta > 0) { mv1 = fw1; mvn = fwn; }
else { mv1 = rv1; mvn = rvn; delta = - delta; }
- return (caps_iterate(t, gops, go, mv1, mvn, 0, 0, delta));
+ return (caps_iterate(t, mv1, mvn, 0, 0, delta));
}
static int caps_move(struct tty *tty,
struct tty_attr a;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
+
if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, rmir);
a = t->tty.st.attr;
PUT1I(1, hpa, x);
else {
PUT0(1, cr);
- CHECK(caps_move_relative(t, gops, go, x,
+ CHECK(caps_move_relative(t, x,
t->cap.cuf1, t->cap.cuf,
t->cap.cub1, t->cap.cub));
}
} else if (t->cap.home) {
PUT0(1, home);
- CHECK(caps_iterate(t, gops, go, t->cap.cud1, t->cap.cud, 0, 1, y));
- CHECK(caps_iterate(t, gops, go, t->cap.cuf1, t->cap.cuf, 0, 1, x));
+ CHECK(caps_iterate(t, t->cap.cud1, t->cap.cud, 0, 1, y));
+ CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
} else
{ rc = -1; goto end; }
break;
case TTORG_CUR:
- CHECK(caps_move_relative(t, gops, go, y,
+ CHECK(caps_move_relative(t, y,
t->cap.cud1, t->cap.cud,
t->cap.cuu1, t->cap.cuu));
- CHECK(caps_move_relative(t, gops, go, x,
+ CHECK(caps_move_relative(t, x,
t->cap.cuf1, t->cap.cuf,
t->cap.cub1, t->cap.cub));
break;
if (x == 0 && y == 1)
PUT0(1, nel);
else {
- CHECK(caps_move_relative(t, gops, go, y,
+ CHECK(caps_move_relative(t, y,
t->cap.cud1, t->cap.cud,
t->cap.cuu1, t->cap.cuu));
if (t->cap.hpa && x)
PUT1I(1, hpa, x);
else {
PUT0(1, cr);
- CHECK(caps_iterate(t, gops, go, t->cap.cuf1, t->cap.cuf, 0, 1, x));
+ CHECK(caps_iterate(t, t->cap.cuf1, t->cap.cuf, 0, 1, x));
}
}
break;
case TTOF_XCUR | TTOF_YHOME:
PUT1I(1, vpa, y);
- CHECK(caps_move_relative(t, gops, go, x,
+ CHECK(caps_move_relative(t, x,
t->cap.cuf1, t->cap.cuf,
t->cap.cub1, t->cap.cub));
break;
if (!t->cap.mir && (t->tty.st.modes&TTMF_INS)) PUT0(0, smir);
- if (!t->cap.msgr && t->tty.st.attr.f)
- CHECK(caps_setattr(&t->tty, gops, go, &a));
+ if (!t->cap.msgr && a.f)
+ CHECK(caps_setattr_internal(t, &a));
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
unsigned wd, nn;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
if (!t->cap.rep)
- while (n--) CHECK(gops->putch(go, ch));
+ CHECK(stupid_repeat(tty, gops, go, ch, n));
else {
wd = t->tty.wd;
while (n) {
}
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
if (f&TTEF_DSP)
switch (f&(TTEF_BEGIN | TTEF_END)) {
case 0:
}
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
if (n) PUT1I(1, ech, n);
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
unsigned f, unsigned n)
{
struct tty_caps *t = (struct tty_caps *)tty;
+ const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
if (f&TTIDF_LN)
- CHECK(caps_iterate(t, gops, go,
- t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
+ CHECK(caps_iterate(t, t->cap.il1, t->cap.il, CIF_PADMUL, 1, n));
else
- CHECK(caps_iterate(t, gops, go,
- t->cap.ich1, t->cap.ich, 0, 1, n));
+ CHECK(caps_iterate(t, t->cap.ich1, t->cap.ich, 0, 1, n));
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
if (t->cap.smir ? !(t->tty.st.modes&TTMF_INS) : !t->cap.ich)
{ rc = -1; goto end; }
if (t->cap.ich) PUT0(1, ich);
if (t->cap.ip) PUT0(1, ip);
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
unsigned f, unsigned n)
{
struct tty_caps *t = (struct tty_caps *)tty;
+ const struct tty_capops *ops = (const struct tty_capops *)t->tty.ops;
int rc;
+ ops->cap.prepout(&t->tty, gops, go);
if (n) {
if (f&TTIDF_LN)
- CHECK(caps_iterate(t, gops, go,
- t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
+ CHECK(caps_iterate(t, t->cap.dl1, t->cap.dl, CIF_PADMUL, 1, n));
else
- CHECK(caps_iterate(t, gops, go,
- t->cap.dch1, t->cap.dch, 0, 1, n));
+ CHECK(caps_iterate(t, t->cap.dch1, t->cap.dch, 0, 1, n));
}
rc = 0;
end:
+ if (ops->cap.flush(&t->tty)) rc = -1;
return (rc);
}
-#undef CHECK
#undef PUT0V
#undef PUT1IV
#undef PUT2IV
#define TTY_CAPOPS \
caps_setattr, caps_setmodes, \
caps_move, caps_repeat, \
- caps_erase, caps_erch, caps_ins, caps_inch, caps_del, \
- 0, 0, 0, 0
+ caps_erase, caps_erch, caps_ins, caps_inch, caps_del
#endif
}
static int termcap_put0(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
unsigned npad, const char *cap)
{
if (!cap) return (-1);
- global_gops = gops; global_gout = go;
return (tputs(cap, npad, caps_putch));
}
static int termcap_put1i(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
unsigned npad, const char *cap, int i0)
{
if (!cap) return (-1);
- global_gops = gops; global_gout = go;
return (tputs(tgoto(cap, -1, i0), npad, caps_putch) == OK ? 0 : -1);
}
static int termcap_put2i(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
- unsigned npad,
- const char *cap, int i0, int i1)
+ unsigned npad, const char *cap, int i0, int i1)
{
if (!cap) return (-1);
- global_gops = gops; global_gout = go;
return (tputs(tgoto(cap, i1, i0), npad, caps_putch) == OK ? 0 : -1);
}
static const union tty_capopsu termcap_ops = { {
{ caps_release, TTY_CAPOPS },
{ termcap_boolcap, termcap_intcap, termcap_strcap,
+ caps_prepout, caps_flush,
termcap_put0, termcap_put1i, termcap_put2i }
} };
union tty_termcapu *u = 0; struct tty *ret = 0;
const char *term;
- if (global_lock)
- { debug("termcap/terminfo terminal already open"); goto end; }
+ if (caps_claim()) goto end;
term = getenv("TERM"); if (!term) goto end;
XNEW(u);
if (tgetent(u->tc.tc.termbuf, term) < 1) goto end;
}
static int terminfo_put0(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
unsigned npad, const char *cap)
{
if (!cap) return (-1);
- global_gops = gops; global_gout = go;
return (tputs(cap, npad, caps_putch));
}
static int terminfo_put1i(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
unsigned npad, const char *cap, int i0)
{
if (!cap) return (-1);
- global_gops = gops; global_gout = go;
return (tputs(tparm(cap, i0), npad, caps_putch) == OK ? 0 : -1);
}
static int terminfo_put2i(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
- unsigned npad,
- const char *cap, int i0, int i1)
+ unsigned npad, const char *cap, int i0, int i1)
{
if (!cap) return (-1);
- global_gops = gops; global_gout = go;
return (tputs(tparm(cap, i0, i1), npad, caps_putch) == OK ? 0 : -1);
}
static const union tty_capopsu terminfo_ops = { {
{ caps_release, TTY_CAPOPS },
{ terminfo_boolcap, terminfo_intcap, terminfo_strcap,
+ caps_prepout, caps_flush,
terminfo_put0, terminfo_put1i, terminfo_put2i }
} };
union tty_capsu *u = 0; struct tty *ret = 0;
int err;
- if (global_lock)
- { debug("termcap/terminfo terminal already open"); goto end; }
+ if (caps_claim()) goto end;
if (setupterm(0, fp ? fileno(fp) : -1, &err) != OK || err < 1) goto end;
XNEW(u);
u->tty.ops = &terminfo_ops.tty;
struct tty_unibislots {
unibi_term *ut;
unibi_var_t dy[26], st[26];
+ const struct gprintf_ops *gops; void *go;
+ char buf[4096]; size_t n;
+ int err;
};
struct tty_unibilium { TTY_CAPSPFX; struct tty_unibislots u; };
union tty_unibiliumu { struct tty_unibilium u; TTY_CAPSUSFX; };
struct termunibi_outctx {
struct tty_unibilium *t;
- const struct gprintf_ops *gops; void *go;
- char pad[128];
- int rc;
};
-static void termunibi_putch(void *ctx, const char *p, size_t sz)
+static void termunibi_putm(void *ctx, const char *p, size_t sz)
{
- struct termunibi_outctx *out = ctx;
+ struct tty_unibilium *t = ctx;
+ size_t n;
- if (out->gops->putm(out->go, p, sz)) out->rc = -1;
+ n = sizeof(t->u.buf) - t->u.n;
+ if (sz <= n)
+ { memcpy(t->u.buf + t->u.n, p, sz); t->u.n += sz; }
+ else {
+ if (n) { memcpy(t->u.buf + t->u.n, p, n); p += n; sz -= n; }
+ for (;;) {
+ if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
+ t->u.err = -1;
+ if (sz <= sizeof(t->u.buf)) break;
+ memcpy(t->u.buf, p, sizeof(t->u.buf));
+ p += sizeof(t->u.buf); sz -= sizeof(t->u.buf);
+ }
+ memcpy(t->u.buf, p, sz); t->u.n = sz;
+ }
}
static void termunibi_pad(void *ctx, size_t ms, int mulp, int forcep)
{
- char pad[128];
- struct termunibi_outctx *out = ctx;
- struct tty_unibilium *t = out->t;
+ struct tty_unibilium *t = ctx;
struct timeval tv;
- size_t n, nn;
+ int pc;
+ size_t sz, n;
/* Based on 7 data bits, 1 stop bit, 1 parity bit. */
#define BITS_PER_KB 9000
- if (forcep || t->tty.baud >= t->cap.pb) {
+ if (forcep || (t->tty.baud >= t->cap.pb && !t->cap.xon)) {
if (t->cap.npc) {
tv.tv_sec = ms/1000; tv.tv_usec = 1000*(ms%1000);
+ if (t->u.n) {
+ if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
+ t->u.err = -1;
+ t->u.n = 0;
+ }
if (t->tty.fpout) fflush(t->tty.fpout);
select(0, 0, 0, 0, &tv);
} else {
- n = (ms*t->tty.baud + BITS_PER_KB - 1)/BITS_PER_KB;
- while (n) {
- if (n < sizeof(out->pad)) nn = n;
- else nn = sizeof(out->pad);
- if (out->gops->putm(out->go, pad, nn)) out->rc = -1;
- n -= nn;
+ pc = t->cap.pad ? *t->cap.pad : 0;
+ sz = (ms*t->tty.baud + BITS_PER_KB - 1)/BITS_PER_KB;
+ n = sizeof(t->u.buf) - t->u.n;
+ if (sz <= n)
+ { memset(t->u.buf + t->u.n, pc, sz); t->u.n += sz; }
+ else {
+ if (n) { memset(t->u.buf + t->u.n, pc, sz); sz -= n; }
+ if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
+ t->u.err = -1;
+ if (sz < sizeof(t->u.buf))
+ memset(t->u.buf, pc, sz);
+ else {
+ memset(t->u.buf, pc, sizeof(t->u.buf));
+ do {
+ if (t->u.gops->putm(t->u.go, t->u.buf, sizeof(t->u.buf)))
+ t->u.err = -1;
+ sz -= sizeof(t->u.buf);
+ } while (sz > sizeof(t->u.buf));
+ }
+ t->u.n = sz;
}
}
}
#undef BITS_PER_KB
}
-static void setup_termunibi_outctx(struct tty_unibilium *t,
- struct termunibi_outctx *out,
- const struct gprintf_ops *gops, void *go)
+static void termunibi_prepout(struct tty *tty,
+ const struct gprintf_ops *gops, void *go)
{
- out->t = t; out->rc = 0;
- out->gops = gops; out->go = go;
- if (!t->cap.npc)
- memset(out->pad, t->cap.pad ? *t->cap.pad : 0, sizeof(out->pad));
+ struct tty_unibilium *t = (struct tty_unibilium *)tty;
+
+ assert(!t->u.n); t->u.gops = gops; t->u.go = go;
+}
+
+static int termunibi_flush(struct tty *tty)
+{
+ struct tty_unibilium *t = (struct tty_unibilium *)tty;
+ int rc = t->u.err;
+
+ if (t->u.n) {
+ if (t->u.gops->putm(t->u.go, t->u.buf, t->u.n)) rc = -1;
+ t->u.n = 0;
+ }
+ t->u.err = 0; return (rc);
}
static int termunibi_put0(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
- unsigned npad, const char *cap)
+ unsigned npad, const char *cap)
{
struct tty_unibilium *t = (struct tty_unibilium *)tty;
- struct termunibi_outctx out;
unibi_var_t arg[9];
if (!cap) return (-1);
- setup_termunibi_outctx(t, &out, gops, go);
unibi_format(t->u.dy, t->u.st, cap, arg,
- termunibi_putch, &out,
- termunibi_pad, &out);
- return (out.rc);
+ termunibi_putm, t,
+ termunibi_pad, t);
+ return (0);
}
static int termunibi_put1i(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
unsigned npad, const char *cap, int i0)
{
struct tty_unibilium *t = (struct tty_unibilium *)tty;
- struct termunibi_outctx out;
unibi_var_t arg[9];
if (!cap) return (-1);
- setup_termunibi_outctx(t, &out, gops, go);
arg[0] = unibi_var_from_num(i0);
unibi_format(t->u.dy, t->u.st, cap, arg,
- termunibi_putch, &out,
- termunibi_pad, &out);
- return (out.rc);
+ termunibi_putm, t,
+ termunibi_pad, t);
+ return (0);
}
static int termunibi_put2i(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
unsigned npad,
const char *cap, int i0, int i1)
{
struct tty_unibilium *t = (struct tty_unibilium *)tty;
- struct termunibi_outctx out;
unibi_var_t arg[9];
if (!cap) return (-1);
- setup_termunibi_outctx(t, &out, gops, go);
arg[0] = unibi_var_from_num(i0);
arg[1] = unibi_var_from_num(i1);
unibi_format(t->u.dy, t->u.st, cap, arg,
- termunibi_putch, &out,
- termunibi_pad, &out);
- return (out.rc);
+ termunibi_putm, t,
+ termunibi_pad, t);
+ return (0);
}
static void termunibi_release(struct tty *tty)
static const union tty_capopsu termunibi_ops = { {
{ termunibi_release, TTY_CAPOPS },
{ termunibi_boolcap, termunibi_intcap, termunibi_strcap,
+ termunibi_prepout, termunibi_flush,
termunibi_put0, termunibi_put1i, termunibi_put2i }
} };
XNEW(u);
u->tty.ops = &termunibi_ops.tty;
u->u.u.ut = ut; ut = 0;
+ u->u.u.n = 0; u->u.u.err = 0;
common_init(&u->tty, fp);
init_caps(&u->cap);
ret = &u->tty; u = 0;
static void ansi_release(struct tty *tty) { ; }
-#define CHECK(expr) do { if ((expr) < 0) { rc = -1; goto end; } } while (0)
-
#define PUTCH(ch) CHECK(gops->putch(go, (ch)))
#define PUTLIT(lit) CHECK(gops->putm(go, (lit), sizeof(lit) - 1))
#define SEMI do { \
const struct tty_attr *a)
{
struct tty_ansi *t = (struct tty_ansi *)tty;
- struct tty_attr aa;
uint32 diff;
int rc = 0;
unsigned z, c, f = 0;
- tty_clampattr(&aa, a, t->tty.acaps);
- diff = aa.f ^ t->tty.st.attr.f;
- if (!diff && aa.fg == t->tty.st.attr.fg && aa.bg == t->tty.st.attr.bg)
+ diff = a->f ^ t->tty.st.attr.f;
+ if (!diff && a->fg == t->tty.st.attr.fg && a->bg == t->tty.st.attr.bg)
return (0);
c = 0;
-#define CLEARP(mask) ((diff&(mask)) && !(aa.f&(mask)))
+#define CLEARP(mask) ((diff&(mask)) && !(a->f&(mask)))
if (CLEARP(TTAF_LNMASK)) c += 3;
if (CLEARP(TTAF_WTMASK)) c += 3;
- if (diff&~aa.f&TTAF_INVV) c += 3;
- if (diff&~aa.f&TTAF_STRIKE) c += 3;
- if (diff&~aa.f&TTAF_ITAL) c += 3;
+ if (diff&~a->f&TTAF_INVV) c += 3;
+ if (diff&~a->f&TTAF_STRIKE) c += 3;
+ if (diff&~a->f&TTAF_ITAL) c += 3;
if (CLEARP(TTAF_FGSPCMASK)) c += 3;
if (CLEARP(TTAF_BGSPCMASK)) c += 3;
#undef CLEARP
z = 0;
- switch ((aa.f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
+ switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
case TTLN_ULINE: z += 2; break;
case TTLN_UULINE: z += 3; break;
}
- if (aa.f&TTAF_WTMASK) z += 2;
- if (aa.f&TTAF_INVV) z += 2;
- if (aa.f&TTAF_STRIKE) z += 2;
- if (aa.f&TTAF_ITAL) z += 2;
+ if (a->f&TTAF_WTMASK) z += 2;
+ if (a->f&TTAF_INVV) z += 2;
+ if (a->f&TTAF_STRIKE) z += 2;
+ if (a->f&TTAF_ITAL) z += 2;
#define COLOURCOST(col) do { \
- switch ((aa.f&TTAF_##col##SPCMASK) >> TTAF_##col##SPCSHIFT) { \
+ switch ((a->f&TTAF_##col##SPCMASK) >> TTAF_##col##SPCSHIFT) { \
case TTCSPC_1BPC: case TTCSPC_1BPCBR: z += 3; break; \
case TTCSPC_4LPC: case TTCSPC_8LGS: z += 8; break; \
case TTCSPC_6LPC: case TTCSPC_24LGS: z += 9; break; \
PUTLIT("\33[");
- if (z <= c) { SEMI; diff = aa.f; }
+ if (z <= c)
+ { SEMI; diff = a->f; t->tty.st.attr.fg = t->tty.st.attr.bg = 0; }
if (diff&TTAF_LNMASK)
- switch ((aa.f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
+ switch ((a->f&TTAF_LNMASK) >> TTAF_LNSHIFT) {
case TTLN_NONE: SEMI; PUTLIT("24"); break;
case TTLN_ULINE: SEMI; PUTCH('4'); break;
case TTLN_UULINE: SEMI; PUTLIT("21"); break;
}
if (diff&TTAF_WTMASK)
- switch ((aa.f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
+ switch ((a->f&TTAF_WTMASK) >> TTAF_WTSHIFT) {
case TTWT_MED: SEMI; PUTLIT("22"); break;
case TTWT_BOLD: SEMI; PUTCH('1'); break;
case TTWT_DIM: SEMI; PUTCH('2'); break;
}
if (diff&TTAF_INVV)
- { SEMI; if (aa.f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
+ { SEMI; if (a->f&TTAF_INVV) PUTCH('7'); else PUTLIT("27"); }
if (diff&TTAF_STRIKE)
- { SEMI; if (aa.f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
+ { SEMI; if (a->f&TTAF_STRIKE) PUTCH('9'); else PUTLIT("29"); }
if (diff&TTAF_ITAL)
- { SEMI; if (aa.f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
+ { SEMI; if (a->f&TTAF_ITAL) PUTCH('3'); else PUTLIT("23"); }
- if (diff&TTAF_FGSPCMASK || aa.fg != tty->st.attr.fg)
+ if (diff&TTAF_FGSPCMASK || a->fg != tty->st.attr.fg)
CHECK(ansi_setcolour(t, &f, gops, go, 30, 90,
- (aa.f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, aa.fg));
- if (diff&TTAF_BGSPCMASK || aa.bg != tty->st.attr.bg)
+ (a->f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg));
+ if (diff&TTAF_BGSPCMASK || a->bg != tty->st.attr.bg)
CHECK(ansi_setcolour(t, &f, gops, go, 40, 100,
- (aa.f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, aa.bg));
+ (a->f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg));
PUTCH('m'); rc = 0;
end:
- t->tty.st.attr = aa; return (rc);
+ t->tty.st.attr = *a; return (rc);
}
if (!(orig&TTOF_YCUR)) CHECK(gprintf(gops, go, "\33[%dd", y + 1));
else if (y == -1) PUTLIT("\33[A");
else if (y < 0) CHECK(gprintf(gops, go, "\33[%dA", -y));
- else if (y == +1) PUTLIT("\33[B"); /* not `^J'! */
+ else if (y == +1) PUTLIT("\33[B"); /* not %|^J|%! */
else if (y > 1) CHECK(gprintf(gops, go, "\33[%dB", y));
if (!(orig&TTOF_XCUR)) {
if (!x)
return (rc);
}
-static int ansi_repeat(struct tty *tty,
- const struct gprintf_ops *gops, void *go,
- int ch, unsigned n)
-{
- int rc;
-
- while (n--) PUTCH(ch);
- rc = 0;
-end:
- return (rc);
-}
-
static int ansi_erase(struct tty *tty,
const struct gprintf_ops *gops, void *go,
unsigned f)
static const struct tty_ops ansi_ops = {
ansi_release,
ansi_setattr, ansi_setmodes,
- ansi_move, ansi_repeat,
- ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del,
- 0, 0, 0, 0
+ ansi_move, stupid_repeat,
+ ansi_erase, ansi_erch, ansi_ins, ansi_inch, ansi_del
};
static struct tty *ansi_init(FILE *fp)
tf |= tm->tf&~tfset;
}
- env_colour_caps(&acaps);
+ if (!(acapset&TTACF_CSPCMASK)) env_colour_caps(&acaps, ECCF_SET);
if (acaps&TTACF_CSPCMASK) ocaps |= TTCF_BGER;
XNEW(u);
else if (isatty(STDERR_FILENO)) { fp = stderr; f |= TTF_BORROW; }
else {
fp = fopen("/dev/tty", "r+"); if (!fp) goto end;
- f &= ~TTF_BORROW;
+ fpin = fp; f &= ~TTF_BORROW;
}
}
}
}
+
+int tty_resized(struct tty *tty)
+{
+ struct winsize ws;
+
+ if (!tty || !tty->fpout) { errno = ENOTTY; return (-1); }
+ else if (ioctl(fileno(tty->fpout), TIOCGWINSZ, &ws)) return (-1);
+ else if (tty->wd == ws.ws_col && tty->ht == ws.ws_row) return (0);
+ else { tty->wd = ws.ws_col; tty->ht = ws.ws_row; return (1); }
+}
+
/*----- Terminal operations -----------------------------------------------*/
+int tty_setattr(struct tty *tty, const struct tty_attr *a)
+{
+ struct tty_attr aa;
+
+ if (!tty || !tty->fpout)
+ return (-1);
+ else {
+ clamp_attr(&aa, a, tty->acaps);
+ return (tty->ops->setattr(tty, &file_printops, tty->fpout, &aa));
+ }
+}
+
int tty_setattrg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
const struct tty_attr *a)
- { return (tty->ops->setattr(tty, gops, go, a)); }
+{
+ struct tty_attr aa;
-int tty_setattr(struct tty *tty, const struct tty_attr *a)
- { return (tty->ops->setattr(tty, &file_printops, tty->fpout, a)); }
+ if (!tty)
+ return (-1);
+ else {
+ clamp_attr(&aa, a, tty->acaps);
+ return (tty->ops->setattr(tty, gops, go, &aa));
+ }
+}
+
+int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa));
+}
int tty_setattrlistg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
const struct tty_attrlist *aa)
{
+ if (!tty) return (-1);
for (;; aa++)
if ((tty->acaps&aa->cap_mask) == aa->cap_eq)
return (tty->ops->setattr(tty, gops, go, &aa->attr));
return (0);
}
-int tty_setattrlist(struct tty *tty, const struct tty_attrlist *aa)
- { return (tty_setattrlistg(tty, &file_printops, tty->fpout, aa)); }
+int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
+ modes_bic, modes_xor));
+}
int tty_setmodesg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
uint32 modes_bic, uint32 modes_xor)
- { return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->setmodes(tty, gops, go, modes_bic, modes_xor));
+}
-int tty_setmodes(struct tty *tty, uint32 modes_bic, uint32 modes_xor)
+int tty_move(struct tty *tty, unsigned orig, int y, int x)
{
- return (tty->ops->setmodes(tty, &file_printops, tty->fpout,
- modes_bic, modes_xor));
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x));
}
int tty_moveg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
unsigned orig, int y, int x)
- { return (tty->ops->move(tty, gops, go, orig, y, x)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->move(tty, gops, go, orig, y, x));
+}
-int tty_move(struct tty *tty, unsigned orig, int y, int x)
- { return (tty->ops->move(tty, &file_printops, tty->fpout, orig, y, x)); }
+int tty_repeat(struct tty *tty, int ch, unsigned n)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n));
+}
int tty_repeatg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
int ch, unsigned n)
- { return (tty->ops->repeat(tty, gops, go, ch, n)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->repeat(tty, gops, go, ch, n));
+}
-int tty_repeat(struct tty *tty, int ch, unsigned n)
- { return (tty->ops->repeat(tty, &file_printops, tty->fpout, ch, n)); }
+int tty_erase(struct tty *tty, unsigned f)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->erase(tty, &file_printops, tty->fpout, f));
+}
int tty_eraseg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
unsigned f)
- { return (tty->ops->erase(tty, gops, go, f)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->erase(tty, gops, go, f));
+}
-int tty_erase(struct tty *tty, unsigned f)
- { return (tty->ops->erase(tty, &file_printops, tty->fpout, f)); }
+int tty_erch(struct tty *tty, unsigned n)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->erch(tty, &file_printops, tty->fpout, n));
+}
int tty_erchg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
unsigned n)
- { return (tty->ops->erch(tty, gops, go, n)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->erch(tty, gops, go, n));
+}
-int tty_erch(struct tty *tty, unsigned n)
- { return (tty->ops->erch(tty, &file_printops, tty->fpout, n)); }
+int tty_ins(struct tty *tty, unsigned f, unsigned n)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n));
+}
int tty_insg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
unsigned f, unsigned n)
- { return (tty->ops->ins(tty, gops, go, f, n)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->ins(tty, gops, go, f, n));
+}
-int tty_ins(struct tty *tty, unsigned f, unsigned n)
- { return (tty->ops->ins(tty, &file_printops, tty->fpout, f, n)); }
+int tty_inch(struct tty *tty, int ch)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->inch(tty, &file_printops, tty->fpout, ch));
+}
int tty_inchg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
int ch)
- { return (tty->ops->inch(tty, gops, go, ch)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->inch(tty, gops, go, ch));
+}
-int tty_inch(struct tty *tty, int ch)
- { return (tty->ops->inch(tty, &file_printops, tty->fpout, ch)); }
+int tty_del(struct tty *tty, unsigned f, unsigned n)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty->ops->del(tty, &file_printops, tty->fpout, f, n));
+}
int tty_delg(struct tty *tty,
const struct gprintf_ops *gops, void *go,
unsigned f, unsigned n)
- { return (tty->ops->del(tty, gops, go, f, n)); }
+{
+ if (!tty) return (-1);
+ else return (tty->ops->del(tty, gops, go, f, n));
+}
-int tty_del(struct tty *tty, unsigned f, unsigned n)
- { return (tty->ops->del(tty, &file_printops, tty->fpout, f, n)); }
+int tty_restore(struct tty *tty, const struct tty_state *st)
+{
+ if (!tty || !tty->fpout) return (-1);
+ else return (tty_restoreg(tty, &file_printops, tty->fpout, st));
+}
+
+int tty_restoreg(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ const struct tty_state *st)
+{
+ int rc;
+
+ if (!tty ||
+ tty->ops->setmodes(tty, gops, go, MASK32, st->modes) ||
+ tty->ops->setattr(tty, gops, go, &st->attr))
+ { rc = -1; goto end; }
+ rc = 0;
+end:
+ return (rc);
+}
/*----- That's all, folks -------------------------------------------------*/
/* Other capabilities. */
#define TTCF_RELMV 0x00000100u /* relative cursor motion */
#define TTCF_ABSMV 0x00000200u /* absolute cursor motion */
-#define TTCF_MIXMV 0x00000400u /* mixed cursor motion */
+#define TTCF_MIXMV 0x00000400u /* mixed (y-abs, x-rel) motion */
#define TTCF_MMARG 0x00000800u /* proper magic margins */
#define TTCF_BGER 0x00001000u /* erasure uses background colour */
#define TTCF_ERCH 0x00002000u /* erase characters */
/* Terminal control state. */
struct tty {
- const struct tty_ops *ops;
- FILE *fpin, *fpout;
- unsigned baud, ht, wd, f;
-#define TTF_BORROW 1u
- uint32 acaps, ocaps;
- struct tty_state st;
+ const struct tty_ops *ops; /* operations table (opaque) */
+ FILE *fpin, *fpout; /* input and output streams */
+ unsigned baud; /* (output) baud rate (bits/s) */
+ unsigned ht, wd; /* terminal dimensions */
+ unsigned f; /* flags */
+#define TTF_BORROW 1u /* don't close the streams */
+ uint32 acaps, ocaps; /* attribute and other capabilities */
+ struct tty_state st; /* current terminal state */
};
-struct tty_ops {
- void (*release)(struct tty */*tty*/);
- int (*setattr)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- const struct tty_attr */*a*/);
- int (*setmodes)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- uint32 /*modes_bic*/, uint32 /*modes_xor*/);
- int (*move)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- unsigned /*orig*/, int /*y*/, int /*x*/);
- int (*repeat)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- int /*ch*/, unsigned /*n*/);
- int (*erase)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- unsigned /*f*/);
- int (*erch)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- unsigned /*n*/);
- int (*ins)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- unsigned /*f*/, unsigned /*n*/);
- int (*inch)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- int /*ch*/);
- int (*del)(struct tty */*tty*/,
- const struct gprintf_ops */*gops*/, void */*go*/,
- unsigned /*f*/, unsigned /*n*/);
- int (*_res0)(void), (*_res1)(void), (*_res2)(void), (*_res3)(void);
-};
+/*----- Lifecycle and maintenance -----------------------------------------*/
+
+/* --- @tty_open@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream open on terminal, or null
+ * @unsigned f@ = flags (@TTF_...@)
+ * @const unsigned *backends@ = ordered list of backends to try
+ *
+ * Returns: Pointer to terminal control block, or null on error.
+ *
+ * Use: Open a terminal and return a @struct tty *@ terminal control
+ * block pointer.
+ *
+ * If @fp@ is provided, then it will be used for terminal
+ * output. If @fp@ is @stdout@, then input (not currently
+ * supported) will come from @stdin@; otherwise, input comes
+ * from @fp@. If @fp@ is null and @TTF_OPEN@ is set, then
+ * @tty_open@ will attempt to open a terminal for itself: if
+ * @stdin@ is interactive then it used for input; if @stdout@ --
+ * or, failing that, @stderr@ -- is interactive, then it is used
+ * for output; otherwise %|/dev/tty|% is opened and used. If
+ * @fp@ is null and @TTF_OPEN@ is not set, then a usable
+ * terminal control block is still returned, but output cannot
+ * be sent directly to the terminal -- since there isn't one.
+ *
+ * If @TTF_BORROW@ is set, then the stream will not be closed by
+ * @tty_close@. (This flag is ignored if @fp@ is null.)
+ *
+ * If @beckends@ is provided, then it points to a vector of
+ * @TTBK_...@ constants describing the backends to be tried in
+ * order. The vector is terminated by @TTBK_END@; if this is
+ * found, then a null pointer is returned.
+ *
+ * A null control block pointer is valid for all @tty@
+ * functions: most will just immediately report failure, but
+ * there won't be any crashing.
+ */
-/*----- Functions provided ------------------------------------------------*/
+#define TTF_OPEN 256u /* open terminal if @fp@ is null */
+extern struct tty *tty_open(FILE */*fp*/, unsigned /*f*/,
+ const unsigned */*backends*/);
-/* --- @tty_clampattr@ --- *
+/* --- @tty_close@ --- *
*
- * Arguments: @struct tty_attr *a_out@ = selected attributes
- * @const struct tty_attr *a@ = requested attributes
- * @uint32 acaps@ = terminal's attribute capability mask
+ * Arguments: @struct tty *tty@ = control block pointer
*
* Returns: ---
*
- * Use: Select the closest approximation to the requested attributes
- * which can be accommodated by the terminal.
+ * Use: Closes a terminal, releasing the control block and any
+ * resources it held. In particular, if the terminal was opened
+ * without @TTF_BORROW@, then the output stream is closed.
*/
-extern void tty_clampattr(struct tty_attr */*a_out*/,
- const struct tty_attr */*a*/, uint32 /*acaps*/);
-
+extern void tty_close(struct tty */*tty*/);
+/* --- @tty_resized@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ *
+ * Returns: Zero if the size hasn't changed, %$+1$% if the size has
+ * changed, or %$-1$% on error.
+ *
+ * Use: Update the terminal width and height. Call this after
+ * receiving @SIGWINCH@, or otherwise periodically, to avoid
+ * making a mess.
+ */
extern int tty_resized(struct tty */*tty*/);
-#define TTF_OPEN 256u
-extern struct tty *tty_open(FILE */*fp*/, unsigned /*f*/,
- const unsigned */*backends*/);
+/*----- Output and control functions --------------------------------------*/
-extern void tty_close(struct tty */*tty*/);
+/* These functions come in pairs. The unmarked version sends output directly
+ * to the terminal's output stream, and will obviously fail (though not
+ * crash) if this is null; the version whose names ends with @...g@ accepts
+ * an additional pair of arguments @gops@ and @go@ giving an alternative
+ * destination for output.
+ */
+
+/* --- @tty_setattr@, @tty_setattrg@--- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @const struct tty_attr *a@ = pointer to attributes to set, or
+ * null
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Set the indicated formatting attributes on following output.
+ * If @a@ is null, then clear all attributes, just as if
+ * @a->f == 0@.
+ *
+ * If you only ever request attributes which are advertised in
+ * the terminals capapbility masked, then you'll always get what
+ * you asked for. Otherwise, the provided attributes will be
+ * `clamped', i.e., modified so as to accommodate the terminal's
+ * shortcomings. In simple cases, unsupported attributes may
+ * just be dropped; but they can also be substituted, e.g.,
+ * single underlining for double, or approximate colours for
+ * unsupported colours.
+ */
+extern int tty_setattr(struct tty */*tty*/, const struct tty_attr */*a*/);
extern int tty_setattrg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
const struct tty_attr */*a*/);
-extern int tty_setattr(struct tty */*tty*/, const struct tty_attr */*a*/);
+/* --- @tty_setattrlist@, @tty_setattrlistg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @const struct tty_attrlist *aa@ = pointer to attribute list
+ * `menu'
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Search the list for an entry matching the terminal's
+ * capabilities, i.e., @tty->acaps&a->cap_mask == a->cap_eq@.
+ * The attributes in the first such entry are set, as if by
+ * @tty_setattr@.
+ *
+ * The list is terminated by an entry with @cap_mask == 0@ --
+ * though it will be checked like any other before ending the
+ * search. In particular, this means that an entry with
+ * @cap_mask == cap_eq == 0@ is a `catch-all', and its
+ * attributes will be set if no earlier matching entry could be
+ * found, while an entry with @cap_mask == 0@ and @cap_eq != 0@
+ * terminates the search without setting any attributes.
+ */
+extern int tty_setattrlist(struct tty */*tty*/,
+ const struct tty_attrlist */*aa*/);
extern int tty_setattrlistg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
const struct tty_attrlist */*aa*/);
-extern int tty_setattrlist(struct tty */*tty*/,
- const struct tty_attrlist */*aa*/);
+/* --- @tty_setmodes@, @tty_setmodesg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @uint32 modes_bic, modes_xor@ = masks to apply to the modes
+ * settings
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Adjust the terminal display modes: specifically, the modes
+ * are adjusted to be @(modes&~modes_bic) ^ modes_xor@.
+ * Mode bits which aren't supported by the terminal are
+ * ignored.
+ */
+extern int tty_setmodes(struct tty */*tty*/,
+ uint32 /*modes_bic*/, uint32 /*modes_xor*/);
extern int tty_setmodesg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
uint32 /*modes_bic*/, uint32 /*modes_xor*/);
-extern int tty_setmodes(struct tty */*tty*/,
- uint32 /*modes_bic*/, uint32 /*modes_xor*/);
+/* --- @tty_move@, @tty_moveg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @unsigned orig@ = origin
+ * @int y, x@ = new cursor position
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Move the cursor. Coordinates are numbered starting with 0.
+ *
+ * The @y@ position is interpreted relative to the origin given
+ * by @orig@: if @TTOF_YCUR@ is set, then the motion is relative
+ * to the current cursor row; otherwise, it is relative to
+ * the top `home' row. Similarly, the @x@ position is
+ * interpreted relative to the current cursor column if
+ * @TTOF_XCUR is set, otherwise relative to the leftmost
+ * column.
+ *
+ * Not all terminals are capable of all kinds of motions:
+ * @TTCF_ABSMV@ is set if absolute motion is possible, and
+ * @TTCF_RELMV@ is set if relative motion is possible. The
+ * @TTCF_MIXMV@ bit indicates that the combination of absolute-y
+ * and relative-x motion is possible; note that the combination
+ * of relative-y and absolute-x is always possible if relative
+ * motion is possible at all.
+ *
+ * The above notwithstanding, all terminals are assumed capable
+ * of moving the cursor to the start of either the current line
+ * @tty_move(tty, TTOF_YCUR | TTOF_XHOME, 0, 0)@, or of the next
+ * line @tty_move(tty, TTOF_YCUR | TTOF_XHOME, +1, 0)@.
+ */
+extern int tty_move(struct tty */*tty*/,
+ unsigned /*orig*/, int /*y*/, int /*x*/);
extern int tty_moveg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*orig*/, int /*y*/, int /*x*/);
-extern int tty_move(struct tty */*tty*/,
- unsigned /*orig*/, int /*y*/, int /*x*/);
+/* --- @tty_repeat@, @tty_repeatg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @int ch@ = character to write
+ * @unsigned n@ = number of copies
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Write @n@ copies of the character @ch@ to the terminal.
+ * (Some terminals have a special control sequence for doing
+ * this.)
+ */
+extern int tty_repeat(struct tty */*tty*/, int /*ch*/, unsigned /*n*/);
extern int tty_repeatg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
int /*ch*/, unsigned /*n*/);
-extern int tty_repeat(struct tty */*tty*/, int /*ch*/, unsigned /*n*/);
+/* --- @tty_erase@, @tty_eraseg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @unsigned f@ = flags
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Erase portions of the current line or the whole display.
+ *
+ * If @TTEF_DSP@ is set, then the whole display is affected. If
+ * @TTEF_BEGIN@ is set, then the display is erased starting from
+ * the top left and ending at and including the cursor
+ * position. If @TTEF_END@ is set, then the display is erased
+ * starting from and including the cursor position, and ending
+ * at the bottom right. If both flags are set, then,
+ * additionally, the cursor is moved to its `home' position at
+ * the top left.
+ *
+ * If @TTF_DSP@ is not set, then the current line is affected.
+ * If @TTEF_BEGIN@ is set, then the line is erased starting from
+ * the left and ending at and including the cursor position. If
+ * @TTEF_END@ is set, then the line is erased starting from and
+ * including the cursor position, and ending at the right hand
+ * side.
+ *
+ * If the @TTCF_BGER@ capability is set, then the erased
+ * positions take on the current background colour; otherwise,
+ * they have the default background colour.
+ */
+extern int tty_erase(struct tty */*tty*/, unsigned /*f*/);
extern int tty_eraseg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*f*/);
-extern int tty_erase(struct tty */*tty*/, unsigned /*f*/);
+/* --- @tty_erch@, @tty_erchg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @unsigned n@ = number of characters to erase
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Erase a number of characters, starting from and including the
+ * current cursor position.
+ *
+ * If the @TTCF_BGER@ capability is set, then the erased
+ * positions take on the current background colour; otherwise,
+ * they have the default background colour.
+ */
+extern int tty_erch(struct tty */*tty*/, unsigned /*n*/);
extern int tty_erchg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*n*/);
-extern int tty_erch(struct tty */*tty*/, unsigned /*n*/);
+/* --- @tty_ins@, @tty_insg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @unsigned f@ = flags
+ * @unsigned n@ = number of items to insert
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Insert a number of blank characters or lines.
+ *
+ * If @TTIDF_LN@ is set, then insert @n@ blank lines above the
+ * current line. The cursor must be at the far left of the
+ * line.
+ *
+ * Otherwise, insert @n@ empty character spaces at the cursor
+ * position.
+ */
+extern int tty_ins(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/);
extern int tty_insg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*f*/, unsigned /*n*/);
-extern int tty_ins(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/);
+/* --- @tty_inch@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @int ch@ = character to insert
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Insert a single character.
+ *
+ * If the @TTMF_INS@ mode is advertised, then insert mode must
+ * be set before calling this function.
+ */
+extern int tty_inch(struct tty */*tty*/, int /*ch*/);
extern int tty_inchg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
int /*ch*/);
-extern int tty_inch(struct tty */*tty*/, int /*ch*/);
+/* --- @tty_del@, @tty_delg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @unsigned f@ = flags
+ * @unsigned n@ = number of items to delete
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Delete a number of characters or lines.
+ *
+ * If @TTIDF_LN@ is set, then delete @n@ blank lines, starting
+ * with the current line line. The cursor must be at the far
+ * left of the line.
+ *
+ * Otherwise, delete @n@ characters at the cursor position.
+ */
+extern int tty_del(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/);
extern int tty_delg(struct tty */*tty*/,
const struct gprintf_ops */*gops*/, void */*go*/,
unsigned /*f*/, unsigned /*n*/);
-extern int tty_del(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/);
+/* --- @tty_restore@, @tty_restoreg@ --- *
+ *
+ * Arguments: @struct tty *tty@ = control block pointer
+ * @const struct gprintf_ops *gops, void *go@ = output
+ * destination
+ * @const struct tty_state *st@ = state to restore
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Restore the terminal modes and attributes to match a
+ * state previously captured by copying @tty->st@.
+ */
+
+extern int tty_restore(struct tty */*tty*/, const struct tty_state */*st*/);
+extern int tty_restoreg(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ const struct tty_state */*st*/);
/*----- That's all, folks -------------------------------------------------*/