#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 -------------------------------------------------*/