AC_CHECK_HEADERS([float.h])
AC_CHECK_HEADERS([stdint.h])
AC_CHECK_HEADERS([valgrind/valgrind.h])
+AC_CHECK_HEADERS([wchar.h])
dnl Libraries.
mdw_ORIG_LIBS=$LIBS LIBS=$MLIB_LIBS
MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
dnl Functions.
+AC_CHECK_FUNCS([mbrtowc])
AC_CHECK_FUNCS([snprintf])
+AC_CHECK_FUNCS([wcwidth])
dnl Types.
AC_CHECK_TYPE([socklen_t], [],
dnl Set the master library list.
AC_SUBST([MLIB_LIBS])
+dnl--------------------------------------------------------------------------
+dnl Terminal characteristics.
+
+mdw_ORIG_LIBS=$LIBS LIBS=$MLIB_LIBS
+
+AC_ARG_WITH([unibilium],
+ AS_HELP_STRING([--with-unibilium], [support `unibilium' library]),
+ [want_unibilium=$withval],
+ [want_unibilium=auto])
+
+case $want_unibilium in no) ;; *) have_unibilium=check ;; esac
+case $have_unibilium in
+ check)
+ PKG_CHECK_MODULES([unibilium], [unibilium >= 2.0.0],
+ [], [have_unibilium=no])
+ ;;
+esac
+case $have_unibilium in check) have_unibilium=yes ;; esac
+
+case $want_unibilium,$have_unibilium in
+ yes,no)
+ AC_MSG_ERROR([`unibilium' library not found but explicitly requested])
+ ;;
+ yes,yes | auto,yes)
+ use_unibilium=yes
+ AC_DEFINE([HAVE_UNIBILIUM], [1],
+ [define if you have (and want to use) the `unibilium' library.])
+ ;;
+ no,* | auto,no)
+ use_unibilium=no
+ ;;
+esac
+AM_CONDITIONAL([WITH_UNIBILIUM], [test "$use_unibilium" = yes])
+
+AC_ARG_WITH([terminfo],
+ AS_HELP_STRING([--with-terminfo], [support `terminfo' library]),
+ [want_terminfo=$withval],
+ [want_terminfo=auto])
+
+case $want_terminfo in no) ;; *) have_terminfo=check ;; esac
+case $have_terminfo in
+ check) AC_CHECK_HEADERS([term.h], [], [have_terminfo=no]) ;;
+esac
+case $have_terminfo in
+ check)
+ AC_SEARCH_LIBS([tparm], [tinfo], [], [have_terminfo=no])
+ AC_CHECK_FUNCS([tiparm])
+ ;;
+esac
+case $have_terminfo in check) have_terminfo=yes ;; esac
+
+case $want_terminfo,$have_terminfo in
+ yes,no)
+ AC_MSG_ERROR([`terminfo' library not found but explicitly requested])
+ ;;
+ yes,yes | auto,yes)
+ use_terminfo=yes
+ AC_DEFINE([HAVE_TERMINFO], [1],
+ [define if you have (and want to use) the `terminfo' library.])
+ ;;
+ no,* | auto,no)
+ use_terminfo=no
+ ;;
+esac
+AM_CONDITIONAL([WITH_TERMINFO], [test "$use_terminfo" = yes])
+
+AC_ARG_WITH([termcap],
+ AS_HELP_STRING([--with-termcap], [support `termcap' library]),
+ [want_termcap=$withval],
+ [want_termcap=auto])
+
+case $want_termcap in no) ;; *) have_termcap=check ;; esac
+case $have_termcap in
+ check) AC_CHECK_HEADERS([termcap.h], [], [have_termcap=no]) ;;
+esac
+case $have_termcap in
+ check)
+ AC_SEARCH_LIBS([tgoto], [termcap], [], [have_termcap=no])
+ AC_SEARCH_LIBS([tgetstr], [termcap], [], [have_termcap=no])
+ ;;
+esac
+case $have_termcap in check) have_termcap=yes ;; esac
+
+case $want_termcap,$have_termcap in
+ yes,no)
+ AC_MSG_ERROR([`termcap' library not found but explicitly requested])
+ ;;
+ yes,yes | auto,yes)
+ use_termcap=yes
+ AC_DEFINE([HAVE_TERMCAP], [1],
+ [define if you have (and want to use) the `termcap' library.])
+ ;;
+ no,* | auto,no)
+ use_termcap=no
+ ;;
+esac
+AM_CONDITIONAL([WITH_TERMCAP], [test "$use_termcap" = yes])
+
+MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+
dnl--------------------------------------------------------------------------
dnl Name resolution.
[want_adns=auto])
mdw_ORIG_LIBS=$LIBS LIBS=$MLIB_LIBS
-case $want_adns in
- no) ;;
- *) AC_SEARCH_LIBS([adns_init], [adns], [have_adns=yes], [have_adns=no]) ;;
+
+case $want_adns in no) ;; *) have_adns=check ;; esac
+case $have_adns in
+ check) AC_CHECK_HEADERS([adns.h], [], [have_adns=no]) ;;
esac
-MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+case $have_adns in
+ check) AC_SEARCH_LIBS([adns_init], [adns], [], [have_adns=no]) ;;
+esac
+case $have_adns in check) have_adns=yes ;; esac
+
case $want_adns,$have_adns in
yes,no)
AC_MSG_ERROR([ADNS library not found but explicitly requested])
esac
AM_CONDITIONAL([WITH_ADNS], [test "$use_adns" = yes])
+MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+
dnl--------------------------------------------------------------------------
dnl Timers.
+mlib (2.5.99~) experimental; urgency=medium
+
+ * (placeholder for next minor version)
+
+ -- Mark Wooding <mdw@distorted.org.uk> Tue, 21 May 2024 14:27:56 +0100
+
mlib (2.5.0) experimental; urgency=medium
* Fix internal Python build scripts to work with Python 3.
dstr_destroy(&d); return (-1);
}
-/* --- @tvec_unkregerr@ --- *
+/* --- @tvec_unkregerr@, @tvec_dupregerr@, @tvec_synthregerr@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
* @const char *name@ = register or pseudoregister name
*
* Returns: %$-1$%.
*
- * Use: Reports an error that the register or pseudoregister is
- * unrecognized.
+ * Use: Reports an error about a misused register: @tvec_unkregerr@
+ * reports that the register is unknown, @tvec_dupregerr@ that
+ * it is already assigned, and @tvec_synthregerr@ that it is
+ * synthetic.
*/
int tvec_unkregerr(struct tvec_state *tv, const char *name)
name, tv->test->name));
}
-/* --- @tvec_dupregerr@ --- *
- *
- * Arguments: @struct tvec_state *tv@ = test-vector state
- * @const char *name@ = register or pseudoregister name
- *
- * Returns: %$-1$%.
- *
- * Use: Reports an error that the register or pseudoregister has been
- * assigned already in the current test.
- */
-
int tvec_dupregerr(struct tvec_state *tv, const char *name)
- { return (tvec_error(tv, "register `%s' is already set", name)); }
+{
+ return (tvec_error(tv, "register `%s' is already set in test `%s'",
+ name, tv->test->name));
+}
+
+int tvec_synthregerr(struct tvec_state *tv, const char *name)
+{
+ return (tvec_error(tv, "register `%s' is synthetic in test `%s' "
+ "and cannot be assigned",
+ name, tv->test->name));
+}
/* --- @tvec_skipspc@ --- *
*
if (r->f&TVRF_LIVE) {
if (rd->i < tv->cfg.nrout)
TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE;
- } else if (!(r->f&TVRF_SEEN) && !(rd->f&TVRF_OPT)) {
+ } else if (!(r->f&TVRF_SEEN) && !(rd->f&(TVRF_OPT | TVRF_SYNTH))) {
tvec_error(tv, "required register `%s' not set in test `%s'",
rd->name, t->name);
f |= f_err;
r = TVEC_REG(tv, in, rd->i);
if (r->f&TVRF_SEEN)
{ tvec_dupregerr(tv, rd->name); goto flush_line; }
+ else if (r->f&TVRF_SYNTH)
+ { tvec_synthregerr(tv, rd->name); goto flush_line; }
}
/* Now there should be a separator. */
const struct tvec_regdef *rd)
{ return (rv0->u == rv1->u); }
+/* --- @copy_int@, @copy_uint@ --- *
+ *
+ * Arguments: @union tvec_regval *rvd@ = destination register value
+ * @const union tvec_regval *rvs@ = source register value
+ * @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns: ---
+ *
+ * Use: Copy a register value.
+ */
+
+static void copy_int(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+ { rvd->i = rvs->i; }
+
+static void copy_uint(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+ { rvd->u = rvs->u; }
+
/* --- @tobuf_int@, @tobuf_uint@ --- *
*
* Arguments: @buf *b@ = buffer
/* Integer type definitions. */
const struct tvec_regty tvty_int = {
- init_int, trivial_release, eq_int,
+ init_int, trivial_release, eq_int, copy_int,
tobuf_int, frombuf_int,
parse_int, dump_int
};
const struct tvec_regty tvty_uint = {
- init_uint, trivial_release, eq_uint,
+ init_uint, trivial_release, eq_uint, copy_uint,
tobuf_uint, frombuf_uint,
parse_uint, dump_uint
};
/* Size type definitions. */
const struct tvec_regty tvty_size = {
- init_uint, trivial_release, eq_uint,
+ init_uint, trivial_release, eq_uint, copy_uint,
tobuf_uint, frombuf_uint,
parse_size, dump_size
};
const struct tvec_regdef *rd)
{ return (eqish_floating_p(rv1->f, rv0->f, rd->arg.p)); }
+/* --- @copy_float@ --- *
+ *
+ * Arguments: @union tvec_regval *rvd@ = destination register value
+ * @const union tvec_regval *rvs@ = source register value
+ * @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns: ---
+ *
+ * Use: Copy a register value.
+ */
+
+static void copy_float(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+ { rvd->f = rvs->f; }
+
/* --- @tobuf_float@ --- *
*
* Arguments: @buf *b@ = buffer
/* Floating-point type definition. */
const struct tvec_regty tvty_float = {
- init_float, trivial_release, eq_float,
+ init_float, trivial_release, eq_float, copy_float,
tobuf_float, frombuf_float,
parse_float, dump_float
};
/* Duration type definition. */
const struct tvec_regty tvty_duration = {
- init_float, trivial_release, eq_float,
+ init_float, trivial_release, eq_float, copy_float,
tobuf_float, frombuf_float,
parse_duration, dump_duration
};
const struct tvec_regdef *rd)
{ return (rv0->p == rv1->p); }
+/* --- @copy_tenum@ --- *
+ *
+ * Arguments: @union tvec_regval *rvd@ = destination register value
+ * @const union tvec_regval *rvs@ = source register value
+ * @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns: ---
+ *
+ * Use: Copy a register value.
+ */
+
+#define copy_ienum copy_int
+#define copy_uenum copy_uint
+#define copy_fenum copy_float
+
+static void copy_penum(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+ { rvd->p = rvs->p; }
+
/* --- @tobuf_tenum@ --- *
*
* Arguments: @buf *b@ = buffer
/* Enumeration type definitions. */
#define DEFTY_ENUM(tag, ty, slot) \
const struct tvec_regty tvty_##slot##enum = { \
- init_##slot##enum, trivial_release, eq_##slot##enum, \
+ init_##slot##enum, trivial_release, eq_##slot##enum, copy_##slot##enum, \
tobuf_##slot##enum, frombuf_##slot##enum, \
parse_##slot##enum, dump_##slot##enum \
};
/* Flags type definition. */
const struct tvec_regty tvty_flags = {
- init_uint, trivial_release, eq_uint,
+ init_uint, trivial_release, eq_uint, copy_uint,
tobuf_uint, frombuf_uint,
parse_flags, dump_flags
};
/* Character type definition. */
const struct tvec_regty tvty_char = {
- init_int, trivial_release, eq_int,
+ init_int, trivial_release, eq_int, copy_int,
tobuf_char, frombuf_char,
parse_char, dump_char
};
MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
}
+/* --- @copy_text@, @copy_bytes@ --- *
+ *
+ * Arguments: @union tvec_regval *rvd@ = destination register value
+ * @const union tvec_regval *rvs@ = source register value
+ * @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns: ---
+ *
+ * Use: Copy a register value.
+ */
+
+static void copy_text(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+{
+ size_t sz = rvs->text.sz;
+
+ if (!sz)
+ rvd->text.sz = 0;
+ else {
+ tvec_alloctext(rvd, sz);
+ memcpy(rvd->text.p, rvs->text.p, sz); rvd->text.p[sz] = 0;
+ }
+}
+
+static void copy_bytes(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+{
+ size_t sz = rvs->bytes.sz;
+
+ if (!sz)
+ rvd->bytes.sz = 0;
+ else {
+ tvec_alloctext(rvd, sz);
+ memcpy(rvd->bytes.p, rvs->bytes.p, sz);
+ }
+}
+
/* --- @tobuf_text@, @tobuf_bytes@ --- *
*
* Arguments: @buf *b@ = buffer
/* Text and byte string type definitions. */
const struct tvec_regty tvty_text = {
- init_text, release_text, eq_text,
+ init_text, release_text, eq_text, copy_text,
tobuf_text, frombuf_text,
parse_text, dump_text
};
const struct tvec_regty tvty_bytes = {
- init_bytes, release_bytes, eq_bytes,
+ init_bytes, release_bytes, eq_bytes, copy_bytes,
tobuf_bytes, frombuf_bytes,
parse_bytes, dump_bytes
};
{
if (rv->text.sz <= sz)
{ free(rv->text.p); rv->text.p = x_alloc(&arena_stdlib, sz + 1); }
- memset(rv->text.p, '?', sz); rv->text.sz = sz;
+ memset(rv->text.p, '?', sz); rv->text.p[sz] = 0; rv->text.sz = sz;
}
void tvec_allocbytes(union tvec_regval *rv, size_t sz)
rv0->buf.m == rv1->buf.m);
}
+/* --- @copy_buffer@ --- *
+ *
+ * Arguments: @union tvec_regval *rvd@ = destination register value
+ * @const union tvec_regval *rvs@ = source register value
+ * @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns: ---
+ *
+ * Use: Copy a register value.
+ */
+
+static void copy_buffer(union tvec_regval *rvd, const union tvec_regval *rvs,
+ const struct tvec_regdef *rd)
+{
+ if (rvd->buf.p) { free(rvd->buf.p); rvd->buf.p = 0; }
+ rvd->buf.sz = rvs->buf.sz;
+ rvd->buf.a = rvs->buf.a;
+ rvd->buf.m = rvs->buf.m;
+ rvd->buf.off = 0;
+}
+
/* --- @tobuf_buffer@ --- *
*
* Arguments: @buf *b@ = buffer
/* Buffer type definition. */
const struct tvec_regty tvty_buffer = {
- init_buffer, release_buffer, eq_buffer,
+ init_buffer, release_buffer, eq_buffer, copy_buffer,
tobuf_buffer, frombuf_buffer,
parse_buffer, dump_buffer
};
.B "};"
.B "#define TVRF_UNSET ..."
.B "#define TVRF_OPT ..."
+.B "#define TVRF_SYNTH ..."
.B "#define TVRF_ID ..."
.B "#define TVEC_ENDREGS ..."
.PP
unsigned f; /* flags */
#define TVRF_UNSET 1u /* register may be marked unset */
#define TVRF_OPT 2u /* register need not be assigned */
-#define TVRF_ID 4u /* part of test identity */
+#define TVRF_SYNTH 4u /* register must not be assigned */
+#define TVRF_ID 8u /* part of test identity */
union tvec_misc arg; /* extra detail for the type */
};
#define TVEC_ENDREGS { 0, 0, 0, 0, { 0 } }
* register as @rv0@ and the output as @rv1@.
*/
+ void (*copy)(union tvec_regval */*rvd*/,
+ const union tvec_regval */*rvs*/,
+ const struct tvec_regdef */*rd*/);
+ /* Copy the value from @rvs@ to @rvd@. */
+
int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/,
const struct tvec_regdef */*rd*/);
/* Serialize the value @*rv@, writing the result to @b@. Return zero on
extern PRINTF_LIKE(2, 3)
void tvec_info(struct tvec_state */*tv*/, const char */*msg*/, ...);
-/* --- @tvec_unkregerr@ --- *
+/* --- @tvec_unkregerr@, @tvec_dupregerr@, @tvec_synthregerr@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
* @const char *name@ = register or pseudoregister name
*
* Returns: %$-1$%.
*
- * Use: Reports an error that the register or pseudoregister is
- * unrecognized.
+ * Use: Reports an error about a misused register: @tvec_unkregerr@
+ * reports that the register is unknown, @tvec_dupregerr@ that
+ * it is already assigned, and @tvec_synthregerr@ that it is
+ * synthetic.
*/
extern int tvec_unkregerr(struct tvec_state */*tv*/, const char */*name*/);
-
-/* --- @tvec_dupregerr@ --- *
- *
- * Arguments: @struct tvec_state *tv@ = test-vector state
- * @const char *name@ = register or pseudoregister name
- *
- * Returns: %$-1$%.
- *
- * Use: Reports an error that the register or pseudoregister has been
- * assigned already in the current test.
- */
-
extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/);
+extern int tvec_synthregerr(struct tvec_state */*tv*/, const char */*name*/);
/*----- Built-in output drivers -------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Terminal handling
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <termios.h>
+
+#ifdef HAVE_TERMCAP
+# include <termcap.h>
+#endif
+
+#ifdef HAVE_TERMINFO
+# include <term.h>
+#endif
+
+#ifdef HAVE_UNIBILIUM
+# include <unibilium.h>
+#endif
+
+#include "gprintf.h"
+#include "macros.h"
+#include "tty.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+/*----- Common support machinery ------------------------------------------*/
+
+static uint32 basic_colour_map[] = {
+ /* standard black */ 0x000000,
+ /* standard red */ 0xcc0000,
+ /* standard green */ 0x00cc00,
+ /* standard yellow */ 0xcccc00,
+ /* standard blue */ 0x0000cc,
+ /* standard magenta */ 0xcc00cc,
+ /* standard cyan */ 0x00cccc,
+ /* standard white */ 0xcccccc
+}, bright_colour_map[] = {
+ /* bright black */ 0x333333,
+ /* bright red */ 0xff3333,
+ /* bright green */ 0x33ff33,
+ /* bright yellow */ 0xffff33,
+ /* bright blue */ 0x3333ff,
+ /* bright magenta */ 0xff33ff,
+ /* bright cyan */ 0x33ffff,
+ /* bright white */ 0xffffff
+};
+
+static void clamp_colours(uint32 *space_out, uint32 *colour_out,
+ uint32 space, uint32 colour, uint32 acaps)
+{
+ unsigned r, g, b, y, t, range;
+ uint32 best_colour, best_space; int best_error;
+
+#define COMMON_SCALE 3825
+
+#define CHECK_RANGE(r) do { \
+ STATIC_ASSERT(COMMON_SCALE%(r) == 0, "common scale doesn't cover " #r); \
+} while (0)
+
+#define SET_RANGE(r) do { CHECK_RANGE(r); range = (r); } while (0)
+
+ /* Check the colour space. If it's one that the terminal can handle, then
+ * return the colour unchanged. Otherwise, extract the channel components
+ * for the next step.
+ */
+ switch (space) {
+ case TTCSPC_NONE:
+ *space_out = TTCSPC_NONE; *colour_out = 0; return;
+ case TTCSPC_1BPC:
+ if (colour >= 8) goto inval;
+ if (acaps&TTACF_1BPC)
+ { *space_out = TTACF_1BPC; *colour_out = colour; return; }
+ colour = basic_colour_map[colour];
+ r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
+ SET_RANGE(255);
+ break;
+ case TTCSPC_1BPCBR:
+ if (colour >= 8) goto inval;
+ if (acaps&TTACF_1BPCBR)
+ { *space_out = TTACF_1BPCBR; *colour_out = colour; return; }
+ colour = bright_colour_map[colour];
+ r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
+ SET_RANGE(255);
+ break;
+ case TTCSPC_2BPC:
+ if (colour >= 64) goto inval;
+ if (acaps&TTACF_2BPC)
+ { *space_out = TTACF_2BPC; *colour_out = colour; return; }
+ r = TTCOL_2BR(colour); g = TTCOL_2BG(colour); b = TTCOL_2BB(colour);
+ SET_RANGE(3);
+ break;
+ case TTCSPC_8LGS:
+ if (colour >= 8) goto inval;
+ if (acaps&TTACF_8LGS)
+ { *space_out = TTACF_8LGS; *colour_out = colour; return; }
+ r = g = b = colour + 1;
+ SET_RANGE(9);
+ break;
+ case TTCSPC_6LPC:
+ if (colour >= 216) goto inval;
+ if (acaps&TTACF_6LPC)
+ { *space_out = TTACF_6LPC; *colour_out = colour; return; }
+ r = TTCOL_6LR(colour); g = TTCOL_6LG(colour); b = TTCOL_6LB(colour);
+ SET_RANGE(5);
+ break;
+ case TTCSPC_24LGS:
+ if (colour >= 24) goto inval;
+ if (acaps&TTACF_24LGS)
+ { *space_out = TTACF_24LGS; *colour_out = colour; return; }
+ r = g = b = colour + 1;
+ SET_RANGE(25);
+ break;
+ case TTCSPC_8BPC:
+ if (colour >= 0x01000000) goto inval;
+ if (acaps&TTACF_8BPC)
+ { *space_out = TTACF_8BPC; *colour_out = colour; return; }
+ r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
+ SET_RANGE(255);
+ break;
+ default:
+ goto inval;
+ }
+
+#undef SET_RANGE
+
+ /* We didn't get an exact match, so we'll have to make do with what we've
+ * got.
+ */
+ best_error = -1;
+
+#define RWT 2
+#define GWT 4
+#define BWT 1
+
+ t = COMMON_SCALE/range; r *= t; g *= t; b *= t;
+ y = (RWT*r + GWT*g + BWT*b + (RWT + GWT + BWT)/2)/(RWT + GWT + BWT);
+
+#define TRY_APPROX(red, grn, blu, spc, clr) do { \
+ int r_err = (red) - r, g_err = (grn) - g, b_err = (blu) - b; \
+ int err; \
+ \
+ if (r_err < 0) r_err = -r_err; \
+ if (g_err < 0) g_err = -g_err; \
+ if (b_err < 0) b_err = -b_err; \
+ \
+ err = r_err + g_err + b_err; \
+ if (best_error < 0 || err < best_error) \
+ { best_error = err; best_space = (spc); best_colour = (clr); } \
+} while (0)
+
+#define TRY_RGB(spc, lvls) do { \
+ CHECK_RANGE((lvls) - 1); \
+ unsigned sc = COMMON_SCALE/((lvls) - 1); \
+ unsigned rr, gg, bb, clr; \
+ \
+ rr = (r + sc/2)/sc; gg = (g + sc/2)/sc; bb = (b + sc/2)/sc; \
+ TRY_APPROX(rr*sc, gg*sc, bb*sc, (spc), (rr*(lvls) + bb)*(lvls) + gg); \
+} while (0)
+
+#define TRY_GREY(spc, lvls) do { \
+ CHECK_RANGE((lvls) + 1); \
+ unsigned sc = COMMON_SCALE/((lvls) + 1); \
+ unsigned yy, grey; \
+ \
+ yy = (y + sc/2)/sc; \
+ if (yy >= 1 && yy <= (lvls)) \
+ { grey = yy*sc; TRY_APPROX(grey, grey, grey, (spc), grey - 1); } \
+} while (0)
+
+ if (acaps&TTACF_1BPC)
+ for (i = 0; i < 8; i++) {
+ t = basic_colour_map[i];
+ TRY_APPROX(TTCOL_8BR(t), TTCOL_8BG(t), TTCOL_8BB(t), TTCSPC_1BPC, i);
+ }
+ if (acaps&TTACF_1BPCBR)
+ for (i = 0; i < 8; i++) {
+ t = bright_colour_map[i];
+ TRY_APPROX(TTCOL_8BR(t), TTCOL_8BG(t), TTCOL_8BB(t), TTCSPC_1BPCBR, i);
+ }
+ if (acaps&TTACF_2BPC) TRY_RGB(TTCSPC_2BPC, 4);
+ if (acaps&TTACF_8LGS) TRY_GREY(TTCSPC_8LGS, 8);
+ if (acaps&TTACF_6LPC) TRY_RGB(TTCSPC_6LPC, 6);
+ if (acaps&TTACF_24LGS) TRY_GREY(TTCSPC_24LGS, 24);
+ if (acaps&TTACF_8BPC) TRY_RGB(TTCSPC_8BPC, 256);
+
+#undef TRY_APPROX
+#undef TRY_RGB
+#undef TRY_GREY
+
+#undef RWT
+#undef GWT
+#undef BWT
+
+ *space_out = best_space; *colour_out = best_colour;
+ return;
+
+inval:
+ *space_out = TTCSPC_NONE; *colour_out = 0;
+}
+
+void tty_clampattr(struct tty_attr *a_out, const struct tty_attr *a,
+ uint32 acaps)
+{
+ uint32 ff = 0, f = a->f, t;
+
+ t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
+ switch (t) {
+ case TTLN_NONE:
+ break;
+ case TTLN_ULINE:
+ if (!acaps&TTACF_ULINE) t = TTLN_NONE;
+ break;
+ case TTLN_UULINE:
+ if (acaps&TTACF_UULINE) ;
+ else if (acaps&TTACF_ULINE) t = TTLN_ULNE;
+ else t = TTLN_NONE;
+ break;
+ case TTLN_STRIKE:
+ if (!acaps&TTACF_STRIKE) t = TTLN_NONE;
+ break;
+ default:
+ t = TTLN_NONE;
+ }
+ ff |= t << TTAF_LNSHIFT;
+
+ t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
+ switch (t) {
+ case TTWT_MED: break;
+ case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
+ case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
+ default: t = TTWD_MED; break;
+ }
+ ff |= t << TTAF_WTSHIFT;
+
+ if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
+ if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
+
+ if (acaps&TTACF_FG) {
+ clamp_colours(&t, &a_out->fg,
+ (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg);
+ ff |= t << TTAF_FGSPCSHIFT;
+ }
+ if (acaps&TTACF_BG) {
+ clamp_colours(&t, &a_out->bg,
+ (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg);
+ ff |= t << TTAF_BGSPCSHIFT;
+ }
+
+ a_out->f = ff; a_out->_res = 0;
+}
+
+/*----- Common machinery for `termcap' and `terminfo' ---------------------*/
+
+#if defined(HAVE_TERMINFO) || \
+ defined(HAVE_TERMCAP) || \
+ defined(HAVE_UNIBILIUM)
+
+#if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
+
+static const struct gprintf_ops *global_gops;
+static void *global_gout;
+
+static int term_putch(int ch)
+ { return (global_gops->putch(global_gout, ch)); }
+
+#endif
+
+#ifdef HAVE_UNIBILIUM
+# define UNIBI_(x) unibi_##x
+#else
+# define UNIBI_(x) 0
+#endif
+
+#define BASICCAPS(_bool, _int, _str) \
+ _str(carriage_return, cr, cr) \
+ _int(lines, lines, li) _int(columns, cols, co)
+
+#define ATTRCAPS(_bool, _int, _str) \
+ _str(exit_attribute_mode, sgr0, me) \
+ _str(enter_underline_mode, smul, us) _str(exit_underline_mode, rmul, ue) \
+ _str(enter_italics_mode, sitm, ZH) _str(exit_italics_mode, ritm, ZR) \
+ _str(enter_bold_mode, bold, md) _str(enter_dim_mode, dim, mh) \
+ _str(enter_reverse_mode, rev, mr) \
+ COLOURCAPS(_bool, _int, _str)
+
+#define COLOURCAPS(_bool, _int, _str) \
+ _str(set_a_foreground, setaf, AF) _str(set_a_background, setab, AB) \
+ _str(orig_pair, op, op) _int(max_colors, colors, Co)
+
+#define MODECAPS(_bool, _int, _str) \
+ _str(enter_am_mode, smam, SA) _str(exit_am_mode, rmam, RA) \
+ _str(enter_ca_mode, smcup, ti) _str(exit_ca_mode, rmcup, te) \
+ _str(enter_insert_mode, smir, im) _str(exit_insert_mode, rmir, ei)
+
+#define MOVECAPS(_bool, _int, _str) \
+ _str(carriage_return, cr, cr) \
+ _str(cursor_home, home, ho) \
+ _str(cusor_address, cup, cm) \
+ _str(row_address, vpa, cv) _str(column_address, hpa, ch) \
+ _str(cursor_left, cub1, le) _(parm_left_cursor, cub, LE) \
+ _str(cursor_right, cuf1, nd) _(parm_right_cursor, cuf, RI) \
+ _str(cursor_up, cuu1, up) _str(parm_up_cursor, cuu, UP) \
+ _str(cursor_down, cud1, do) _str(parm_down_cursor, cud, DO)
+
+#define SCROLLCAPS(_bool, _int, _str) \
+ _str(change_scroll_region, csr, cs) \
+ _str(scroll_forward, ind, sf) _str(parm_index, indn, SF) \
+ _str(scroll_reverse, ri, sr) _str(parm_rindex, rin, SR)
+
+#define ERASECAPS(_bool, _int, _str) \
+ _str(clr_bol, el1, cb) \
+ _str(clr_eol, el, ce) \
+ _str(clr_eos, ed, cd)
+
+#define INSDELCAPS(_bool, _int, _str) \
+ _str(insert_character, ich1, ic) _str(parm_ich, ich, IC) \
+ _str(insert_line, il1, al) _str(parm_insert_line, il, AL) \
+ _str(delete_character, dch1, dc) _str(parm_dch, dch, DC) \
+ _str(delete_line, dl1, dl) _str(parm_delete_line, dl, DL)
+
+#define STORECAPS(_bool, _int, _str) \
+ ATTRCAPS(_bool, _int, _str) \
+ MODECAPS(_bool, _int, _str) \
+ MOVECAPS(_bool, _int, _str) \
+ SCROLLCAPS(_bool, _int, _str) \
+ ERASECAPS(_bool, _int, _str) \
+ INSDELCAPS(_bool, _int, _str)
+
+#define CAP_XMC magic_cookie_glitch, xmc, sg
+#define CAP_BCE back_color_erase, bce, ut
+#define CAP_XHPA row_addr_glitch, xvpa, YD
+#define CAP_XVPA col_addr_glitch, xhpa, YA
+#define CAP_MIR move_insert_mode, mir, mi
+#define CAP_MSGR move_standout_mode, msgr, ms
+#define CAP_NPC no_pad_char, npc, NP
+#define CAP_AM auto_left_margin, am, am
+#define CAP_XENL eat_newline_glitch, xenl, xn
+
+#define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
+
+struct ttycaps {
+#define DEF_STRCAP(info, cap) const char *cap;
+#define DEF_INTCAP(info, cap) int cap;
+ STORECAPS(DEF_STRCAP, DEF_INTCAP)
+#undef DEF_STRCAP
+#undef DEF_INTCAP
+};
+
+typedef int boolcapfn(int uix, const char *info, const char *cap, void *arg);
+typedef int intcapfn(int uix, const char *info, const char *cap, void *arg);
+typedef const char *strcapfn(int uix, const char *info, const char *cap,
+ void *arg);
+
+#define DEFINE_CAPISH(PRE, pre)
+
+static int init_caps(struct tty *tty, struct tty_caps *caps,
+ boolcapfn *boolcap, intcapfn *intcap, strcapfn *strcap,
+ void *arg)
+{
+ tty->acaps = tty->ocaps = 0;
+ tty->st.modes = 0;
+ tty->st.attr.f = 0;
+
+ /* Inhale all of the interesting terminal capabilities. */
+#define GETBOOL(var, info, cap) \
+ caps->info = boolcap(CAPREF(var, info, cap), arg);
+#define GETINT(var, info, cap) \
+ caps->info = intcap(CAPREF(var, info, cap), arg);
+#define GETSTR(var, info, cap) \
+ caps->info = strcap(CAPREF(var, info, cap), arg);
+ STORECAPS(GETBOOL, GETINT, GETSTR)
+#undef GETBOOL
+#undef GETINT
+#undef GETSTR
+
+#define CLEARCAP(var, info, cap) caps->info = 0;
+#define CLEAR_CAPS(caplist) do { caplist(CLEARCAP) } while (0)
+
+ /* Basic capabilities. */
+ if (!caps->cr) caps->cr = "\r";
+
+ /* Attribute capabilities. */
+ if (intcap(CAPREF(CAP_XMC)) || !caps->sgr0)
+ CLEAR_CAPS(ATTRCAPS);
+ else {
+ if (caps->smul) tty->acaps |= TTACF_ULINE;
+ if (caps->bold) tty->acaps |= TTACF_BOLD;
+ if (caps->dim) tty->acaps |= TTACF_DIM;
+ if (caps->sitm) tty->acaps |= TTACF_ITAL;
+ if (caps->rev) tty->acaps |= TTACF_INVV;
+
+ if (!caps->colors >= 8 || (!caps->setaf && !caps->setbf))
+ CLEAR_CAPS(COLOURCAPS);
+ else {
+ if (caps->setaf) tty->acaps |= TTACF_FG;
+ if (caps->setab) tty->acaps |= TTACF_BG;
+ tty->acaps |= TTACF_1BPC;
+ if (caps->colors >= 16) tty->acaps |= TTACF_1BPCBR;
+ if (caps->colors == 88) tty->acaps |= TTACF_2BPC | TTACF_8LGS;
+ else if (caps->colors >= 256) tty->acaps |= TTACF_6LPC | TTACF_24LGS;
+ if (caps->colors >= 16777216) tty->acaps |= TTACF_8BPC;
+ if (boolcap(CAPREF(CAP_BCE))) tty->ocaps |= TTCF_BGER;
+ }
+ }
+
+ /* Motion capabilities. */
+ if (boolcap(CAPREF(CAP_XVPA))) caps->vpa = 0;
+ if (boolcap(CAPREF(CAP_XHPA))) caps->hpa = 0;
+ if (!caps->cub1) caps->cub1 = "\b";
+ if (!caps->cud1) caps->cud1 = "\n";
+ if ((caps->cuf || caps->cuf1) && (caps->cuu || caps->cuu1)) {
+ tty->ocaps |= TTCF_RELMV;
+ if (caps->vpa) tty->ocaps |= TTCF_MIXMV;
+ }
+ if (caps->cup || (caps->hpa && caps->vpa)) tty->ocaps |= TTCF_ABSMV;
+
+ /* Mode capabilities. */
+ if (caps->smam && caps->rmam) tty->ocaps |= TTMF_AUTOM;
+ if (caps->smir && caps->rmir) tty->ocaps |= TTMF_INS;
+ if (boolcap(CAPREF(CAP_AM))) {
+ tty->st.modes |= TTMF_AUTOM;
+ if (boolcap(CAPREF(CAP_XENL))) tty->ocaps |= TTCF_MMARG;
+ }
+
+ /* Scrolling. */
+ if (caps->csr) tty->ocaps |= TTCF_SCRGN;
+ if ((caps->ind || caps->indn) && (caps->ri || caps->rin))
+ tty->ocaps |= TTCF_SCROLL;
+
+ /* Erasure. */
+ if (caps->ech) tty->ocaps |= TTCF_ERCH;
+ if (caps->el1) tty->ocaps |= TTCF_ERBOL;
+ if (caps->el) tty->ocaps |= TTCF_EREOL;
+ if (caps->ed) tty->ocaps |= TTCF_EREOD;
+
+ /* Insertion and deletion. */
+ if (caps->ich || caps->ich1) tty->ocaps |= TTCF_INSCH;
+ if (caps->il || caps->il1) tty->ocaps |= TTCF_INSLN;
+ if (caps->dch || caps->dch1) tty->ocaps |= TTCF_DELCH;
+ if (caps->dl || caps->dl1) tty->ocaps |= TTCF_DELLN;
+}
+
+static int caps_setattr(struct tty *tty, const struct tty_caps *caps,
+ const struct gprintf_ops *gops, void *go,
+ const struct tty_attr *a)
+{
+ struct tty_attr aa;
+
+ tty_clampattr(&aa, a, tty->acaps);
+
+}
+
+static int caps_setmodes(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ uint32 modes_bic, uint32 modes_xor);
+static int caps_move(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ unsigned orig, int y, int x);
+static int caps_repeat(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ int ch, unsigned n);
+static int caps_erase(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ unsigned f);
+static int caps_erch(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ unsigned n);
+static int caps_ins(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ unsigned f, unsigned n);
+static int caps_del(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ unsigned f, unsigned n);
+static int caps_setscrgn(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ unsigned y0, unsigned y1);
+static int caps_scroll(struct tty *tty,
+ const struct gprintf_ops *gops, void *go,
+ int n);
+
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Terminal handling
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef MLIB_TTY_H
+#define MLIB_TTY_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+
+#ifndef MLIB_BITS_H
+# include "bits.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* Attributes. */
+#define TTAF_LNMASK 0x0003u /* line style mask */
+#define TTAF_LNSHIFT 0 /* line style shift */
+enum {
+ TTLN_NONE, /* no line */
+ TTLN_ULINE, /* underline */
+ TTLN_UULINE, /* double underline */
+ TTLN_STRIKE, /* strikeout */
+ TTLN_LIMIT
+};
+#define TTAF_WTMASK 0x000cu /* weight mask */
+#define TTAG_WTSHIFT 2 /* weight shift */
+enum {
+ TTWT_MED, /* medium */
+ TTWT_BOLD, /* bold/bright */
+ TTWT_DIM, /* light/dim */
+ TTWT_LIMIT
+};
+#define TTAF_ITAL 0x0010u /* italic/oblique */
+#define TTAF_INVV 0x0020u /* inverse video */
+#define TTAF_FGSPCMASK 0x1c00u /* foreground space mask */
+#define TTAF_FGSPCSHIFT 10 /* foreground space shift */
+#define TTAF_BGSPCMASK 0xe000u /* background space mask */
+#define TTAF_BGSPCSHIFT 13 /* background space shift */
+enum {
+ TTCSPC_NONE, /* no colour */
+ TTCSPC_1BPC, /* one bit per channel */
+ TTCSPC_1BPCBR, /* one bit per channel, brighter */
+ TTCSPC_2BPC, /* two bits levels per channel */
+ TTCSPC_8LGS, /* eight levels greyscale */
+ TTCSPC_6LPC, /* six levels per channel */
+ TTCSPC_24LGS, /* 24 levels greyscale */
+ TTCSPC_8BPC /* eight bits per channel */
+ TTCSPC_LIMIT
+};
+
+/* Colours. */
+#define TT1BC_RED 1u
+#define TT1BC_GREEN 2u
+#define TT1BC_BLUE 4u
+#define TT1BC_BRIGHT 8u
+#define TTCOL_BLACK 0u
+#define TTCOL_RED (TT1BC_RED)
+#define TTCOL_GREEN (TT1BC_GREEN)
+#define TTCOL_YELLOW (TT1BC_RED | TT1BC_GREEN)
+#define TTCOL_BLUE (TT1BC_BLUE)
+#define TTCOL_MAGENTA (TT1BC_RED | TT1BC_BLUE)
+#define TTCOL_CYAN (TT1BC_GREEN | TT1BC_BLUE)
+#define TTCOL_WHITE (TT1BC_RED | TT1BC_GREEN | TT1BC_BLUE)
+#define TTCOL_BRBLACK (TTCOL_BLACK | TT1BC_BRIGHT)
+#define TTCOL_BRRED (TTCOL_RED | TT1BC_BRIGHT)
+#define TTCOL_BRGREEN (TTCOL_GREEN | TT1BC_BRIGHT)
+#define TTCOL_BRYELLOW (TTCOL_YELLOW | TT1BC_BRIGHT)
+#define TTCOL_BRBLUE (TTCOL_BLUE | TT1BC_BRIGHT)
+#define TTCOL_BRMAGENTA (TTCOL_MAGENTA | TT1BC_BRIGHT)
+#define TTCOL_BRCYAN (TTCOL_CYAN | TT1BC_BRIGHT)
+#define TTCOL_BRWHITE (TTCOL_WHITE | TT1BC_BRIGHT)
+
+#define TTCOL_MK2B(r, g, b) (((r) << 4) | ((g) << 2) | ((b) << 0))
+#define TTCOL_2BR(col) (((col) >> 4)&0x03)
+#define TTCOL_2BG(col) (((col) >> 2)&0x03)
+#define TTCOL_2BB(col) (((col) >> 0)&0x03)
+
+#define TTCOL_MK6L(r, g, b) (36*(r) + 6*(g) + (b))
+#define TTCOL_6LR(col) ((col)/36)
+#define TTCOL_6LG(col) (((col)/6)%6)
+#define TTCOL_6LB(col) ((col)%6)
+
+#define TTCOL_MK8B(r, g, b) (((r) << 16) | ((g) << 8) | ((b) << 0))
+#define TTCOL_8BR(col) (((col) >> 16)&0xff)
+#define TTCOL_8BG(col) (((col) >> 8)&0xff)
+#define TTCOL_8BB(col) (((col) >> 0)&0xff)
+
+struct tty_attr {
+ uint32 f, _res0; /* attribute flags, reserved */
+ uint32 fg, bg; /* foreground/background colours */
+};
+
+/* Mode settings. */
+#define TTMF_AUTOM 0x00000001u /* automatic margins */
+#define TTMF_FSCRM 0x00000002u /* full-screen mode */
+#define TTMF_INS 0x00000004u /* insert mode */
+
+/* Attribute capabilities. */
+#define TTACF_ULINE 0x00000001u /* underline */
+#define TTACF_UULINE 0x00000002u /* double underline */
+#define TTACF_STRIKE 0x00000004u /* strikeout */
+#define TTACF_BOLD 0x00000008u /* bold/bright */
+#define TTACF_DIM 0x00000010u /* light/dim */
+#define TTACF_ITAL 0x00000020u /* italic/oblique */
+#define TTACF_INVV 0x00000040u /* inverse video */
+#define TTACF_FG 0x00000080u /* set foreground colour */
+#define TTACF_BG 0x00000100u /* set background colour */
+#define TTACF_1BPC 0x00000200u /* one-bit-per-channel space */
+#define TTACF_1BPCBR 0x00000400u /* one-bit-per-channel bright space */
+#define TTACF_2BPC 0x00000800u /* two-bits-per-channel space */
+#define TTACF_8LGS 0x00001000u /* 8-levels-greyscale space */
+#define TTACF_6LPC 0x00002000u /* six-levels-per-channel space */
+#define TTACF_24LGS 0x00004000u /* 24-levels-greyscale space */
+#define TTACF_8BPC 0x00008000u /* eight-bits-per-channel space */
+
+/* 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_MVINS 0x00000800u /* motion preserves insert mode */
+#define TTCF_MVATTR 0x00001000u /* motion preserves attributes */
+#define TTCF_MMARG 0x00002000u /* proper magic margins */
+#define TTCF_SCRGN 0x00004000u /* scroll regions */
+#define TTCF_SCROLL 0x00008000u /* explicit scrolling */
+#define TTCF_BGER 0x00010000u /* erasure uses background colour */
+#define TTCF_ERCH 0x00020000u /* erase characters */
+#define TTCF_ERBOL 0x00040000u /* erase from beginning of line */
+#define TTCF_EREOL 0x00080000u /* erase to end of line */
+#define TTCF_ERBOD 0x00100000u /* erase from beginning of display */
+#define TTCF_EREOD 0x00200000u /* erase to end of display */
+#define TTCF_ERDSP 0x00400000u /* erase display */
+#define TTCF_INSCH 0x00800000u /* insert character */
+#define TTCF_INSLN 0x01000000u /* insert line */
+#define TTCF_DELCH 0x02000000u /* delete character */
+#define TTCF_DELLN 0x04000000u /* delete line */
+
+struct tty_attrlist {
+ uint32 cap_mask, cap_eq; /* capabilities to select */
+ struct tty_attr attr; /* attributes to set */
+};
+#define TTY_ATTRLIST_END { 0, 0, { 0, 0, 0, 0 } }
+
+/* Terminal capability backend libraries. */
+enum {
+ TTLIB_TERMCAP, /* traditional `termcap' */
+ TTLIB_TERMINFO, /* standard `terminfo' */
+ TTLIB_UNIBILIUM, /* `Unibiliium' */
+ TTLIB_XTERM, /* assume modern terminal conventions */
+ TTLIB_LIMIT
+};
+
+struct tty_state {
+ uint32 modes;
+ struct tty_attr attr;
+};
+
+struct tty {
+ const struct tty_ops *ops;
+ FILE *fp;
+ unsigned baud, ht, wd;
+ uint32 acaps, ocaps;
+ struct tty_state st;
+};
+
+/* Motion origin. */
+#define TTOF_XHOME 0u /* absolute horizontal motion */
+#define TTOF_XCUR 1u /* relative horizontal motion */
+#define TTOF_YHOME 0u /* absolute vertical motion */
+#define TTOF_YCUR 2u /* relative vertical motion */
+
+/* Erasure scope. */
+#define TTEF_LINE 0u /* erase within line */
+#define TTEF_DSP 1u /* erase whole display */
+#define TTEF_BEGIN 2u /* erase from beginning */
+#define TTEF_END 4u /* erase to end */
+
+/* Insertion and deletion. */
+#define TTIDF_CH 0u /* insert/delete characters */
+#define TTIDF_LN 1u /* insert/delete lines */
+
+struct tty_ops {
+ int (*setattr)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ const struct tty_attr */*old*/,
+ const struct tty_attr */*new*/);
+ 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 (*del)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*f*/, unsigned /*n*/);
+ int (*setscrgn)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ unsigned /*y0*/, unsigned /*y1*/);
+ int (*scroll)(struct tty */*tty*/,
+ const struct gprintf_ops */*gops*/, void */*go*/,
+ int /*n*/);
+};
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* caps
+ *
+ * RA auto-wrap off
+ * SA auto-wrap on
+
+ * AB ansi background
+ * AF ansi foreground
+ * md bold
+ * me clear text highlighting
+ * mr inverse video
+ * op default colour pair
+
+ * up cursor up 1 line
+
+ * cd clear to end-of-display
+ * ce clear to end-of-line
+
+ * cr carriage return
+ * nw newline
+
+ * pc pad character
+ */
+
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
* named by @user@; otherwise, the user string is @user@ itself.
* The user string consists of a sequence of capability
* defintiions separated by colons. Each definition has the
- * form %%\syntax{<tok>"="<codes>}%%,
+ * form %%\syntax{<tok>"="<codes>}%%,
*/
#define TCIF_GETENV 1u
# define LAUNDER(x) \
({ __typeof__(x) _y; __asm__("" : "=g"(_y) : "0"(x)); _y; })
# define ADMIRE(x) \
- ({ __asm__("" : : "g"(x)); })
+ ({ __asm__("" :: "g"(x)); })
# define ADMIRE_BUF(p, sz) \
- ({ __asm__("" : : "g"(p), "g"(sz)); })
-# define RELAX do __asm__ __volatile__("" ::: "memory"); while (0)
+ ({ __asm__("" :: "m"(*(unsigned char *)p), "g"(sz) : "memory"); })
+# define RELAX do __asm__(""); while (0)
#endif
#if CLANG_VERSION_P(3, 3)