From d04c0e00da3a27693bbf9cc4f2d5c88e56d80f20 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sun, 7 Jul 2024 14:34:54 +0100 Subject: [PATCH] @@@ more mess Organization: Straylight/Edgeware From: Mark Wooding --- configure.ac | 118 ++++++++++- debian/changelog | 6 + test/tvec-core.c | 35 ++-- test/tvec-types.c | 135 ++++++++++-- test/tvec.3.in | 1 + test/tvec.h | 29 ++- ui/tty.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++ ui/tty.h | 277 +++++++++++++++++++++++++ ui/ttycolour.h | 2 +- utils/macros.h | 6 +- 10 files changed, 1071 insertions(+), 52 deletions(-) create mode 100644 ui/tty.c create mode 100644 ui/tty.h diff --git a/configure.ac b/configure.ac index 416b25f..2b7bcd7 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,7 @@ dnl Headers. 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 @@ -65,7 +66,9 @@ AC_SEARCH_LIBS([gethostbyname], [nsl resolv]) 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], [], @@ -90,6 +93,106 @@ mdw_PROBE_FLTFMT 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. @@ -100,11 +203,16 @@ AC_ARG_WITH([adns], [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]) @@ -125,6 +233,8 @@ case $want_adns,$have_adns in esac AM_CONDITIONAL([WITH_ADNS], [test "$use_adns" = yes]) +MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS + dnl-------------------------------------------------------------------------- dnl Timers. diff --git a/debian/changelog b/debian/changelog index a2fc30a..26dc2e4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +mlib (2.5.99~) experimental; urgency=medium + + * (placeholder for next minor version) + + -- Mark Wooding 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. diff --git a/test/tvec-core.c b/test/tvec-core.c index 4c2ac5d..6b54f0a 100644 --- a/test/tvec-core.c +++ b/test/tvec-core.c @@ -390,15 +390,17 @@ int tvec_syntax_v(struct tvec_state *tv, int ch, 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) @@ -407,19 +409,18 @@ 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@ --- * * @@ -960,7 +961,7 @@ static void check(struct tvec_state *tv, struct groupstate *g) 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; @@ -1297,6 +1298,8 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) 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. */ diff --git a/test/tvec-types.c b/test/tvec-types.c index 481363c..27381b5 100644 --- a/test/tvec-types.c +++ b/test/tvec-types.c @@ -1454,6 +1454,25 @@ static int eq_uint(const union tvec_regval *rv0, 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 @@ -1610,12 +1629,12 @@ static void dump_uint(const union tvec_regval *rv, /* 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 }; @@ -1755,7 +1774,7 @@ static void dump_size(const union tvec_regval *rv, /* 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 }; @@ -1823,6 +1842,21 @@ static int eq_float(const union tvec_regval *rv0, 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 @@ -1926,7 +1960,7 @@ static void dump_float(const union tvec_regval *rv, /* 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 }; @@ -2204,7 +2238,7 @@ static void dump_duration(const union tvec_regval *rv, /* 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 }; @@ -2319,6 +2353,25 @@ static int eq_penum(const union tvec_regval *rv0, 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 @@ -2553,7 +2606,7 @@ TVEC_MISCSLOTS(DEFDUMP_ENUM) /* 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 \ }; @@ -2769,7 +2822,7 @@ static void dump_flags(const union tvec_regval *rv, /* 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 }; @@ -3089,7 +3142,7 @@ static void dump_char(const union tvec_regval *rv, /* 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 }; @@ -3189,6 +3242,43 @@ static int eq_bytes(const union tvec_regval *rv0, 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 @@ -3478,12 +3568,12 @@ static void dump_bytes(const union tvec_regval *rv, /* 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 }; @@ -3605,7 +3695,7 @@ void tvec_alloctext(union tvec_regval *rv, size_t sz) { 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) @@ -3672,6 +3762,27 @@ static int eq_buffer(const union tvec_regval *rv0, 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 @@ -3831,7 +3942,7 @@ static void dump_buffer(const union tvec_regval *rv, /* 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 }; diff --git a/test/tvec.3.in b/test/tvec.3.in index 36f69ba..e5cd05a 100644 --- a/test/tvec.3.in +++ b/test/tvec.3.in @@ -105,6 +105,7 @@ tvec \- test vector framework .B "};" .B "#define TVRF_UNSET ..." .B "#define TVRF_OPT ..." +.B "#define TVRF_SYNTH ..." .B "#define TVRF_ID ..." .B "#define TVEC_ENDREGS ..." .PP diff --git a/test/tvec.h b/test/tvec.h index eae88a7..d5c1a3f 100644 --- a/test/tvec.h +++ b/test/tvec.h @@ -238,7 +238,8 @@ struct tvec_regdef { 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 } } @@ -286,6 +287,11 @@ struct tvec_regty { * 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 @@ -895,31 +901,22 @@ extern PRINTF_LIKE(2, 3) 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 -------------------------------------------*/ diff --git a/ui/tty.c b/ui/tty.c new file mode 100644 index 0000000..77e778a --- /dev/null +++ b/ui/tty.c @@ -0,0 +1,514 @@ +/* -*-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 +#include + +#include +#include + +#ifdef HAVE_TERMCAP +# include +#endif + +#ifdef HAVE_TERMINFO +# include +#endif + +#ifdef HAVE_UNIBILIUM +# include +#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 -------------------------------------------------*/ diff --git a/ui/tty.h b/ui/tty.h new file mode 100644 index 0000000..a82804d --- /dev/null +++ b/ui/tty.h @@ -0,0 +1,277 @@ +/* -*-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 + +#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 diff --git a/ui/ttycolour.h b/ui/ttycolour.h index 83f3d87..4fb8472 100644 --- a/ui/ttycolour.h +++ b/ui/ttycolour.h @@ -169,7 +169,7 @@ struct ttycolour_state { * 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{"="}%%, + * form %%\syntax{"="}%%, */ #define TCIF_GETENV 1u diff --git a/utils/macros.h b/utils/macros.h index 5b77225..2ca5a06 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -263,10 +263,10 @@ # 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) -- [mdw]