diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rwxr-xr-x | configure | 65 | ||||
| -rw-r--r-- | main.c | 3 | ||||
| -rw-r--r-- | ui-terminal-vt100.c | 211 | ||||
| -rw-r--r-- | ui-terminal.c | 4 | ||||
| -rw-r--r-- | ui.h | 15 | ||||
| -rw-r--r-- | vis-cmds.c | 1 |
8 files changed, 273 insertions, 31 deletions
@@ -21,6 +21,7 @@ MANPREFIX ?= ${PREFIX}/man VERSION = $(shell git describe --always --dirty 2>/dev/null || echo "0.2-git") CONFIG_HELP ?= 1 +CONFIG_CURSES ?= 1 CONFIG_LUA ?= 1 CONFIG_LPEG ?= 0 CONFIG_TRE ?= 0 @@ -36,6 +37,7 @@ CFLAGS_VIS = $(CFLAGS_AUTO) $(CFLAGS_TERMKEY) $(CFLAGS_CURSES) $(CFLAGS_ACL) \ CFLAGS_VIS += -DVIS_PATH=\"${SHAREPREFIX}/vis\" CFLAGS_VIS += -DCONFIG_HELP=${CONFIG_HELP} +CFLAGS_VIS += -DCONFIG_CURSES=${CONFIG_CURSES} CFLAGS_VIS += -DCONFIG_LUA=${CONFIG_LUA} CFLAGS_VIS += -DCONFIG_LPEG=${CONFIG_LPEG} CFLAGS_VIS += -DCONFIG_TRE=${CONFIG_TRE} @@ -49,9 +49,8 @@ In order to build vis you will need a compiler, a [POSIX.1-2008](http://pubs.opengroup.org/onlinepubs/9699919799/) compatible environment as well as: - * [libcurses](http://www.gnu.org/software/ncurses/), preferably in the - wide-character version * [libtermkey](http://www.leonerd.org.uk/code/libtermkey/) + * [curses](https://en.wikipedia.org/wiki/Curses_(programming_library)) (recommended) * [Lua](http://www.lua.org/) >= 5.2 (optional) * [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/) >= 0.12 (optional runtime dependency required for syntax highlighting) @@ -23,6 +23,7 @@ Fine tuning of the installation directories: --mandir=DIR man pages [PREFIX/share/man] Optional features: + --enable-curses build with Curses terminal output [yes] --enable-lua build with Lua support [auto] --enable-lpeg build with support for statically linking to LPeg [auto] --enable-tre build with TRE regex support [auto] @@ -116,6 +117,7 @@ SHAREDIR='$(PREFIX)/share' MANDIR='$(PREFIX)/share/man' help=yes +curses=yes lua=auto lpeg=auto tre=auto @@ -135,6 +137,8 @@ case "$arg" in --static) static=yes ;; --enable-help|--enable-help=yes) help=yes ;; --disable-help|--enable-help=no) help=no ;; +--enable-curses|--enable-curses=yes) curses=yes ;; +--disable-curses|--enable-curses=no) curses=no ;; --enable-lua|--enable-lua=yes) lua=yes ;; --disable-lua|--enable-lua=no) lua=no ;; --enable-lpeg|--enable-lpeg=yes) lpeg=yes ;; @@ -293,9 +297,12 @@ else CONFIG_HELP=0 fi -# libcurses is a mandatory dependency +CONFIG_CURSES=0 + +if test "$curses" != "no" ; then + + printf "checking for libcurses...\n" -printf "checking for libcurses...\n" cat > "$tmpc" <<EOF #include <curses.h> @@ -306,36 +313,37 @@ int main(int argc, char *argv[]) { } EOF -CONFIG_CURSES=0 + for libcurses in ncursesw ncurses libcurses; do + printf " checking for %s... " "$libcurses" -for curses in ncursesw ncurses curses; do - printf " checking for %s... " "$curses" + if test "$have_pkgconfig" = "yes" ; then + CFLAGS_CURSES=$(pkg-config --cflags $libcurses 2>/dev/null) + LDFLAGS_CURSES=$(pkg-config --libs $libcurses 2>/dev/null) + if test $? -eq 0 && $CC $CFLAGS $CFLAGS_CURSES "$tmpc" \ + $LDFLAGS $LDFLAGS_CURSES -o "$tmpo" >/dev/null 2>&1 ; then + CONFIG_CURSES=1 + printf "yes\n" + break + fi + fi - if test "$have_pkgconfig" = "yes" ; then - CFLAGS_CURSES=$(pkg-config --cflags $curses 2>/dev/null) - LDFLAGS_CURSES=$(pkg-config --libs $curses 2>/dev/null) - if test $? -eq 0 && $CC $CFLAGS $CFLAGS_CURSES "$tmpc" \ + CFLAGS_CURSES="" + LDFLAGS_CURSES="-l$libcurses" + + if $CC $CFLAGS $CFLAGS_CURSES "$tmpc" \ $LDFLAGS $LDFLAGS_CURSES -o "$tmpo" >/dev/null 2>&1 ; then CONFIG_CURSES=1 printf "yes\n" break + else + CFLAGS_CURSES="" + LDFLAGS_CURSES="" + printf "no\n" fi - fi - - CFLAGS_CURSES="" - LDFLAGS_CURSES="-l$curses" - - if $CC $CFLAGS $CFLAGS_CURSES "$tmpc" \ - $LDFLAGS $LDFLAGS_CURSES -o "$tmpo" >/dev/null 2>&1 ; then - CONFIG_CURSES=1 - printf "yes\n" - break - else - printf "no\n" - fi -done + done -test $CONFIG_CURSES -ne 1 && fail "$0: cannot find libcurses" + test "$curses" = "yes" -a $CONFIG_CURSES -ne 1 && fail "$0: cannot find libcurses" +fi # libtermkey is a mandatory dependency @@ -356,7 +364,7 @@ fi if test -z "$LDFLAGS_TERMKEY"; then CFLAGS_TERMKEY="" - LDFLAGS_TERMKEY="-ltermkey" + LDFLAGS_TERMKEY="-ltermkey -lncursesw" fi if $CC $CFLAGS $CFLAGS_TERMKEY "$tmpc" $LDFLAGS $LDFLAGS_TERMKEY $LDFLAGS_CURSES \ @@ -591,11 +599,12 @@ printf "completing config.mk... " exec 3>&1 1>>config.mk cat << EOF -CFLAGS_CURSES = $CFLAGS_CURSES -LDFLAGS_CURSES = $LDFLAGS_CURSES +CONFIG_HELP = $CONFIG_HELP CFLAGS_TERMKEY = $CFLAGS_TERMKEY LDFLAGS_TERMKEY = $LDFLAGS_TERMKEY -CONFIG_HELP = $CONFIG_HELP +CONFIG_CURSES = $CONFIG_CURSES +CFLAGS_CURSES = $CFLAGS_CURSES +LDFLAGS_CURSES = $LDFLAGS_CURSES REGEX_SRC = $REGEX_SRC CONFIG_TRE = $CONFIG_TRE CFLAGS_TRE = $CFLAGS_TRE @@ -2061,7 +2061,8 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[i], "--") == 0) { break; } else if (strcmp(argv[i], "-v") == 0) { - printf("vis %s%s%s%s%s%s\n", VERSION, + printf("vis %s%s%s%s%s%s%s\n", VERSION, + CONFIG_CURSES ? " +curses" : "", CONFIG_LUA ? " +lua" : "", CONFIG_LPEG ? " +lpeg" : "", CONFIG_TRE ? " +tre" : "", diff --git a/ui-terminal-vt100.c b/ui-terminal-vt100.c new file mode 100644 index 0000000..8a73bb7 --- /dev/null +++ b/ui-terminal-vt100.c @@ -0,0 +1,211 @@ +/* This file is included from ui-terminal.c + * + * The goal is *not* to reimplement curses. Instead we aim to provide the + * simplest possible drawing backend for VT-100 compatible terminals. + * This is useful for debugging and fuzzing purposes as well as for environments + * with no curses support. + * + * Currently no attempt is made to optimize terminal output. The amount of + * flickering will depend on the smartness of your terminal emulator. + * + * The following terminal escape sequences are used: + * + * - CSI ? 1049 h Save cursor and use Alternate Screen Buffer (DECSET) + * - CSI ? 1049 l Use Normal Screen Buffer and restore cursor (DECRST) + * - CSI ? 25 l Hide Cursor (DECTCEM) + * - CSI ? 25 h Show Cursor (DECTCEM) + * - CSI 2 J Erase in Display (ED) + * - CSI row ; column H Cursor Position (CUP) + * - CSI ... m Character Attributes (SGR) + * - CSI 0 m Normal + * - CSI 1 m Bold + * - CSI 3 m Italicized + * - CSI 4 m Underlined + * - CSI 5 m Blink + * - CSI 7 m Inverse + * - CSI 22 m Normal (not bold) + * - CSI 23 m Not italicized + * - CSI 24 m Not underlined + * - CSI 25 m Not blinking + * - CSI 27 m Not inverse + * - CSI 30-37,39 Set foreground color + * - CSI 38 ; 2 ; R ; G ; B m Set RGB foreground color + * - CSI 40-47,49 Set background color + * - CSI 48 ; 2 ; R ; G ; B m Set RGB background color + * + * See http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt + * for further information. + */ +#include <stdio.h> +#include "buffer.h" + +#define ui_term_backend_init ui_vt100_init +#define ui_term_backend_blit ui_vt100_blit +#define ui_term_backend_clear ui_vt100_clear +#define ui_term_backend_colors ui_vt100_colors +#define ui_term_backend_resize ui_vt100_resize +#define ui_term_backend_save ui_vt100_save +#define ui_term_backend_restore ui_vt100_restore +#define ui_term_backend_suspend ui_vt100_suspend +#define ui_term_backend_resume ui_vt100_resume +#define ui_term_backend_new ui_vt100_new +#define ui_term_backend_free ui_vt100_free + +#define CELL_COLOR_BLACK (CellColor){ .index = 0 } +#define CELL_COLOR_RED (CellColor){ .index = 1 } +#define CELL_COLOR_GREEN (CellColor){ .index = 2 } +#define CELL_COLOR_YELLOW (CellColor){ .index = 3 } +#define CELL_COLOR_BLUE (CellColor){ .index = 4 } +#define CELL_COLOR_MAGENTA (CellColor){ .index = 5 } +#define CELL_COLOR_CYAN (CellColor){ .index = 6 } +#define CELL_COLOR_WHITE (CellColor){ .index = 7 } +#define CELL_COLOR_DEFAULT (CellColor){ .index = 9 } + +#define CELL_ATTR_NORMAL 0 +#define CELL_ATTR_UNDERLINE (1 << 0) +#define CELL_ATTR_REVERSE (1 << 1) +#define CELL_ATTR_BLINK (1 << 2) +#define CELL_ATTR_BOLD (1 << 3) +#define CELL_ATTR_ITALIC (1 << 4) + +typedef struct { + UiTerm uiterm; + Buffer buf; +} UiVt100; + +static CellColor color_rgb(UiTerm *ui, uint8_t r, uint8_t g, uint8_t b) { + return (CellColor){ .r = r, .g = g, .b = b, .index = (uint8_t)-1 }; +} + +static CellColor color_terminal(UiTerm *ui, uint8_t index) { + return (CellColor){ .r = 0, .g = 0, .b = 0, .index = index }; +} + + +static void output(const char *data, size_t len) { + fwrite(data, len, 1, stderr); + fflush(stderr); +} + +static void output_literal(const char *data) { + output(data, strlen(data)); +} + +static void screen_alternate(bool alternate) { + output_literal(alternate ? "\x1b[?1049h" : "\x1b[0m" "\x1b[?1049l"); +} + +static void cursor_visible(bool visible) { + output_literal(visible ? "\x1b[?25h" : "\x1b[?25l"); +} + +static void ui_vt100_blit(UiTerm *tui) { + Buffer *buf = &((UiVt100*)tui)->buf; + buffer_clear(buf); + CellAttr attr = CELL_ATTR_NORMAL; + CellColor fg = CELL_COLOR_DEFAULT, bg = CELL_COLOR_DEFAULT; + int w = tui->width, h = tui->height; + Cell *cell = tui->cells; + /* erase screen, reposition cursor, reset attributes */ + buffer_append0(buf, "\x1b[2J" "\x1b[H" "\x1b[0m"); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + CellStyle *style = &cell->style; + if (style->attr != attr) { + + static struct { + CellAttr attr; + char on[4], off[4]; + } cell_attrs[] = { + { CELL_ATTR_BOLD, "1", "22" }, + { CELL_ATTR_ITALIC, "3", "23" }, + { CELL_ATTR_UNDERLINE, "4", "24" }, + { CELL_ATTR_BLINK, "5", "25" }, + { CELL_ATTR_REVERSE, "7", "27" }, + }; + + for (size_t i = 0; i < LENGTH(cell_attrs); i++) { + CellAttr a = cell_attrs[i].attr; + if ((style->attr & a) == (attr & a)) + continue; + buffer_appendf(buf, "\x1b[%sm", + style->attr & a ? + cell_attrs[i].on : + cell_attrs[i].off); + } + + attr = style->attr; + } + + if (!cell_color_equal(fg, style->fg)) { + fg = style->fg; + if (fg.index != (uint8_t)-1) { + buffer_appendf(buf, "\x1b[%dm", 30 + fg.index); + } else { + buffer_appendf(buf, "\x1b[38;2;%d;%d;%dm", + fg.r, fg.g, fg.b); + } + } + + if (!cell_color_equal(bg, style->bg)) { + bg = style->bg; + if (bg.index != (uint8_t)-1) { + buffer_appendf(buf, "\x1b[%dm", 40 + bg.index); + } else { + buffer_appendf(buf, "\x1b[48;2;%d;%d;%dm", + bg.r, bg.g, bg.b); + } + } + + buffer_append0(buf, cell->data); + cell++; + } + } + output(buffer_content(buf), buffer_length0(buf)); +} + +static void ui_vt100_clear(UiTerm *tui) { } + +static void ui_vt100_resize(UiTerm *tui, int width, int height) { } + +static void ui_vt100_save(UiTerm *tui) { + cursor_visible(true); +} + +static void ui_vt100_restore(UiTerm *tui) { + cursor_visible(false); +} + +static int ui_vt100_colors(Ui *ui) { + char *term = getenv("TERM"); + return (term && strstr(term, "-256color")) ? 256 : 16; +} + +static void ui_vt100_suspend(UiTerm *term) { + cursor_visible(true); + screen_alternate(false); +} + +static void ui_vt100_resume(UiTerm *term) { + screen_alternate(true); + cursor_visible(false); +} + +static bool ui_vt100_init(UiTerm *tui, const char *term) { + ui_vt100_resume(tui); + return true; +} + +static UiTerm *ui_vt100_new(void) { + UiVt100 *vtui = calloc(1, sizeof *vtui); + if (!vtui) + return NULL; + buffer_init(&vtui->buf); + return (UiTerm*)vtui; +} + +static void ui_vt100_free(UiTerm *tui) { + UiVt100 *vtui = (UiVt100*)tui; + ui_vt100_suspend(tui); + buffer_release(&vtui->buf); +} diff --git a/ui-terminal.c b/ui-terminal.c index 67cabba..3b9ac3b 100644 --- a/ui-terminal.c +++ b/ui-terminal.c @@ -62,7 +62,11 @@ struct UiTermWin { enum UiOption options; /* display settings for this window */ }; +#if CONFIG_CURSES #include "ui-terminal-curses.c" +#else +#include "ui-terminal-vt100.c" +#endif __attribute__((noreturn)) static void ui_die(Ui *ui, const char *msg, va_list ap) { UiTerm *tui = (UiTerm*)ui; @@ -50,12 +50,27 @@ enum UiStyle { UI_STYLE_MAX, }; +#if CONFIG_CURSES typedef uint64_t CellAttr; typedef short CellColor; static inline bool cell_color_equal(CellColor c1, CellColor c2) { return c1 == c2; } +#else +typedef uint8_t CellAttr; +typedef struct { + uint8_t r, g, b; + uint8_t index; +} CellColor; + +static inline bool cell_color_equal(CellColor c1, CellColor c2) { + if (c1.index != (uint8_t)-1 || c2.index != (uint8_t)-1) + return c1.index == c2.index; + return c1.r == c2.r && c1.g == c2.g && c1.b == c2.b; +} + +#endif typedef struct { CellAttr attr; @@ -755,6 +755,7 @@ static bool cmd_help(Vis *vis, Win *win, Command *cmd, const char *argv[], Curso const char *name; bool enabled; } configs[] = { + { "Curses support: ", CONFIG_CURSES }, { "Lua support: ", CONFIG_LUA }, { "Lua LPeg statically built-in: ", CONFIG_LPEG }, { "TRE based regex support: ", CONFIG_TRE }, |
