aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README.md3
-rwxr-xr-xconfigure65
-rw-r--r--main.c3
-rw-r--r--ui-terminal-vt100.c211
-rw-r--r--ui-terminal.c4
-rw-r--r--ui.h15
-rw-r--r--vis-cmds.c1
8 files changed, 273 insertions, 31 deletions
diff --git a/Makefile b/Makefile
index 5c92cda..3f261ae 100644
--- a/Makefile
+++ b/Makefile
@@ -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}
diff --git a/README.md b/README.md
index 7254b77..a24b1e8 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/configure b/configure
index 2c689db..29bb5c5 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/main.c b/main.c
index db6b994..c154eac 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/ui.h b/ui.h
index 41ef975..ee434fc 100644
--- a/ui.h
+++ b/ui.h
@@ -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;
diff --git a/vis-cmds.c b/vis-cmds.c
index 4f1512f..f38a96f 100644
--- a/vis-cmds.c
+++ b/vis-cmds.c
@@ -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 },