commit 66c80ccd610e7d1da50ddbc2ec61c7e356b6afc8 Author: G. Branden Robinson AuthorDate: Thu Feb 8 21:37:31 2024 -0600 Commit: G. Branden Robinson CommitDate: Wed Feb 21 11:56:15 2024 -0600 [grotty]: Add terminfo support (3/x). diff --git a/configure.ac b/configure.ac index 2493fcebf..ff1730253 100644 --- a/configure.ac +++ b/configure.ac @@ -158,6 +158,9 @@ GROFF_URW_FONTS_PATH # use groff's own malloc-based allocator for C++ new/delete operators GROFF_USE_GROFF_ALLOCATOR +# grotty uses curses +GROFF_CURSES + # other random stuff GROFF_BROKEN_SPOOLER_FLAGS GROFF_PAGE diff --git a/m4/groff.m4 b/m4/groff.m4 index ebbe60d52..7fb58266a 100644 --- a/m4/groff.m4 +++ b/m4/groff.m4 @@ -1934,3 +1934,13 @@ [test "$enableval" = yes && groff_use_own_allocator=yes], [groff_use_own_allocator=no]) ]) + + +# Search for curses/terminfo library used by grotty. +# +# TODO: This needs be to generalized to check for other curses +# implementations, not just ncurses. + +AC_DEFUN([GROFF_CURSES], [ + PKG_CHECK_MODULES([CURSES], [ncurses]) +]) diff --git a/src/devices/grotty/grotty.am b/src/devices/grotty/grotty.am index a9d2fe1c1..9e0540686 100644 --- a/src/devices/grotty/grotty.am +++ b/src/devices/grotty/grotty.am @@ -21,7 +21,7 @@ grotty_LDADD = $(LIBM) \ libdriver.a \ libgroff.a \ lib/libgnu.a \ - -lcurses + $(CURSES_LIBS) man1_MANS += src/devices/grotty/grotty.1 EXTRA_DIST += \ src/devices/grotty/grotty.1.man \ commit 6f002fe31706dc911d4036d73ae12f04654f8325 Author: G. Branden Robinson AuthorDate: Thu Feb 8 01:08:40 2024 -0600 Commit: G. Branden Robinson CommitDate: Wed Feb 21 11:56:15 2024 -0600 [grotty]: Add terminfo support (2/x). diff --git a/src/devices/grotty/grotty.am b/src/devices/grotty/grotty.am index 14921c562..a9d2fe1c1 100644 --- a/src/devices/grotty/grotty.am +++ b/src/devices/grotty/grotty.am @@ -20,7 +20,8 @@ grotty_SOURCES = src/devices/grotty/tty.cpp grotty_LDADD = $(LIBM) \ libdriver.a \ libgroff.a \ - lib/libgnu.a + lib/libgnu.a \ + -lcurses man1_MANS += src/devices/grotty/grotty.1 EXTRA_DIST += \ src/devices/grotty/grotty.1.man \ diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp index 9501c656a..22db1a933 100644 --- a/src/devices/grotty/tty.cpp +++ b/src/devices/grotty/tty.cpp @@ -21,6 +21,9 @@ along with this program. If not, see . */ #include "device.h" #include "ptable.h" +#include +#include + typedef signed char schar; declare_ptable(schar) @@ -56,8 +59,9 @@ static bool do_sgr_italics; static bool want_reverse_video_for_italics = false; static bool do_reverse_video; static bool use_overstriking_drawing_scheme = false; +static bool want_capability_warnings = false; -static void update_options(); +static void initialize_terminal(); static void usage(FILE *stream); static int hline_char = '-'; @@ -916,8 +920,86 @@ printer *make_printer() return new tty_printer(); } -static void update_options() +static void initialize_terminal() { + int err; + + char *terminal_type = strdup(getenv("TERM")); + int ret = setupterm(NULL /* use TERM */, STDOUT_FILENO, &err); + size_t len = strlen(terminal_type); + + if (len == 0) + fatal("TERM environment variable must be set; aborting"); + + if (OK == ret) { + ; + } + else if (ERR == ret) { + const char msg1[] = "entry for '"; + const char msg2[] = "' not found in terminal database"; + const int bufsz = len + sizeof msg1 + sizeof msg2 + 1 /* `\0` */; + char *errbuf = static_cast(calloc(bufsz, sizeof (char))); + (void) snprintf(errbuf, bufsz, "%s%s%s", msg1, terminal_type, msg2); + const char no_database[] = "terminfo database entry not found"; + + switch (err) { + case -1: + fatal(no_database); + break; + case 0: + fatal(errbuf); + case 1: + // POSIX calls this case "Success". As an extension, to ncurses + // it means a hardcopy terminal. While fatal for many terminfo + // applications, we're fine with it. + break; + default: + fatal("terminfo interface is nonstandard; aborting"); + } + } + else + fatal("terminfo interface is nonstandard; aborting"); + + if (tigetflag("hc")) { + // hardcopy terminal + do_sgr_italics = false; + do_reverse_video = false; + + if (want_sgr_italics) { + if (want_capability_warnings) + warning("terminal type %1 is incapable of SGR italics", + terminal_type); + want_sgr_italics = false; + } + } + else + if (want_sgr_italics) + if ((tigetstr("sitm") == 0 /* nullptr */) + || (tigetstr("ritm") == 0 /* nullptr */)) { + if (want_capability_warnings) + warning("terminal type %1 is incapable of SGR italics", + terminal_type); + want_sgr_italics = false; + } + + // X/Open Curses Issue 7 defines "os" as "Terminal overstrikes on + // hard-copy terminal". However, ncurses's terminfo database defines + // it for graphical terminals as well; see entries for wy99gt-tek, + // wy370-tek, tek, tek4013, gt40, and so on. We therefore regard it + // as generally relevant. + if (tigetflag("os")) { + // can overstrike + do_bold = want_emboldening_by_overstriking; + do_underline = want_italics_by_underlining; + } + else + if (want_emboldening_by_overstriking + || want_italics_by_underlining + || want_glyph_composition_by_overstriking) + if (want_capability_warnings) + warning("terminal type %1 is incapable of overstriking", + terminal_type); + if (use_overstriking_drawing_scheme) { do_sgr_italics = false; do_reverse_video = false; @@ -932,6 +1014,8 @@ static void update_options() do_bold = true; do_underline = true; } + + free(terminal_type); } int main(int argc, char **argv) @@ -948,8 +1032,8 @@ int main(int argc, char **argv) { "version", no_argument, 0, 'v' }, { NULL, 0, 0, 0 } }; - while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL)) - != EOF) + while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUvw", long_options, + NULL)) != EOF) switch(c) { case 'v': printf("GNU grotty (groff) version %s\n", Version_string); @@ -1004,6 +1088,9 @@ int main(int argc, char **argv) // Ignore \D commands. allow_drawing_commands = false; break; + case 'w': + want_capability_warnings = true; + break; case CHAR_MAX + 1: // --help usage(stdout); break; @@ -1014,7 +1101,7 @@ int main(int argc, char **argv) default: assert(0 == "unhandled getopt_long return value"); } - update_options(); + initialize_terminal(); if (optind >= argc) do_file("-"); else { commit b28f4ba70ae8610b34a12f5c249b2601b12a6a3e Author: G. Branden Robinson AuthorDate: Thu Feb 8 01:04:28 2024 -0600 Commit: G. Branden Robinson CommitDate: Wed Feb 21 11:56:15 2024 -0600 [grotty]: Add terminfo support (1/x). C++ name spacing does not help us here because ncurses defines `lines` as a preprocessor macro. (Curses exposes _every_ terminal capability as a global symbol; see Curses Issue 7, ยง7.1.3, "Defined Capabilities", p. 340 or term_variables(3ncurses).) diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp index c9f386e37..9501c656a 100644 --- a/src/devices/grotty/tty.cpp +++ b/src/devices/grotty/tty.cpp @@ -172,7 +172,7 @@ public: class tty_printer : public printer { - tty_glyph **lines; + tty_glyph **grotty_lines; int nlines; int cached_v; int cached_vpos; @@ -271,16 +271,16 @@ tty_printer::tty_printer() : cached_v(0) (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5); (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6); nlines = 66; - lines = new tty_glyph *[nlines]; + grotty_lines = new tty_glyph *[nlines]; for (int i = 0; i < nlines; i++) - lines[i] = 0; + grotty_lines[i] = 0; is_continuously_underlining = false; } tty_printer::~tty_printer() { current_lineno = 0; // At this point, we've read all the input. - delete[] lines; + delete[] grotty_lines; } void tty_printer::make_underline(int w) @@ -373,11 +373,11 @@ void tty_printer::add_char(output_character c, int w, " quantum"); vpos = v / font::vert; if (vpos > nlines) { - tty_glyph **old_lines = lines; - lines = new tty_glyph *[vpos + 1]; - memcpy(lines, old_lines, nlines * sizeof(tty_glyph *)); + tty_glyph **old_lines = grotty_lines; + grotty_lines = new tty_glyph *[vpos + 1]; + memcpy(grotty_lines, old_lines, nlines * sizeof(tty_glyph *)); for (int i = nlines; i <= vpos; i++) - lines[i] = 0; + grotty_lines[i] = 0; delete[] old_lines; nlines = vpos + 1; } @@ -404,7 +404,7 @@ void tty_printer::add_char(output_character c, int w, // at each hpos, and otherwise in order of occurrence. tty_glyph **pp; - for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next) + for (pp = grotty_lines + (vpos - 1); *pp; pp = &(*pp)->next) if ((*pp)->hpos < hpos || ((*pp)->hpos == hpos && (*pp)->order() >= g->order())) break; @@ -743,24 +743,24 @@ void tty_printer::end_page(int page_length) int lines_per_page = page_length / font::vert; int last_line; for (last_line = nlines; last_line > 0; last_line--) - if (lines[last_line - 1]) + if (grotty_lines[last_line - 1]) break; #if 0 if (last_line > lines_per_page) { error("characters past last line discarded"); do { --last_line; - while (lines[last_line]) { - tty_glyph *tem = lines[last_line]; - lines[last_line] = tem->next; + while (grotty_lines[last_line]) { + tty_glyph *tem = grotty_lines[last_line]; + grotty_lines[last_line] = tem->next; delete tem; } } while (last_line > lines_per_page); } #endif for (int i = 0; i < last_line; i++) { - tty_glyph *p = lines[i]; - lines[i] = 0; + tty_glyph *p = grotty_lines[i]; + grotty_lines[i] = 0; tty_glyph *g = 0; while (p) { tty_glyph *tem = p->next;