304 lines
8.8 KiB
Diff
304 lines
8.8 KiB
Diff
|
|
From 68932004a8eb935f18732913ca904e466f320753 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Mark Nudelman <markn@greenwoodsoftware.com>
|
||
|
|
Date: Mon, 10 Oct 2022 12:31:36 -0700
|
||
|
|
Subject: [PATCH 11/48] lesstest: handle colored text with less -R.
|
||
|
|
|
||
|
|
---
|
||
|
|
lesstest/display.c | 33 ++++++++++++---------
|
||
|
|
lesstest/lt_screen.c | 68 ++++++++++++++++++++++++++------------------
|
||
|
|
lesstest/lt_types.h | 6 ++++
|
||
|
|
lesstest/runtest | 3 +-
|
||
|
|
4 files changed, 68 insertions(+), 42 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/lesstest/display.c b/lesstest/display.c
|
||
|
|
index 5c9355e..a7e1954 100644
|
||
|
|
--- a/lesstest/display.c
|
||
|
|
+++ b/lesstest/display.c
|
||
|
|
@@ -5,8 +5,6 @@ extern TermInfo terminfo;
|
||
|
|
|
||
|
|
static void display_attr(Attr attr) {
|
||
|
|
static Attr prev_attr = 0;
|
||
|
|
- if (attr == prev_attr)
|
||
|
|
- return;
|
||
|
|
if (prev_attr & ATTR_STANDOUT)
|
||
|
|
printf("%s", terminfo.exit_standout);
|
||
|
|
if (prev_attr & ATTR_BLINK)
|
||
|
|
@@ -26,8 +24,11 @@ static void display_attr(Attr attr) {
|
||
|
|
prev_attr = attr;
|
||
|
|
}
|
||
|
|
|
||
|
|
-static void display_color(Color fg_color, Color bg_color) {
|
||
|
|
-printf("{%x/%x}", fg_color, bg_color);
|
||
|
|
+static void display_color(Color color) {
|
||
|
|
+ if (color == NULL_COLOR)
|
||
|
|
+ printf("\33[m");
|
||
|
|
+ else
|
||
|
|
+ printf("\33[%dm", color);
|
||
|
|
}
|
||
|
|
|
||
|
|
void display_screen(const byte* img, int imglen, int screen_width, int screen_height, int move_cursor) {
|
||
|
|
@@ -36,22 +37,28 @@ void display_screen(const byte* img, int imglen, int screen_width, int screen_he
|
||
|
|
int cursor_x = 0;
|
||
|
|
int cursor_y = 0;
|
||
|
|
int literal = 0;
|
||
|
|
+ Attr curr_attr = 0;
|
||
|
|
+ Color curr_color = NULL_COLOR;
|
||
|
|
while (imglen-- > 0) {
|
||
|
|
wchar ch = load_wchar(&img);
|
||
|
|
if (!literal) {
|
||
|
|
- if (ch == '\\') {
|
||
|
|
+ switch (ch) {
|
||
|
|
+ case '\\':
|
||
|
|
literal = 1;
|
||
|
|
continue;
|
||
|
|
- } else if (ch == '@') {
|
||
|
|
- Attr attr = *img++;
|
||
|
|
- display_attr(attr);
|
||
|
|
+ case LTS_CHAR_ATTR:
|
||
|
|
+ curr_attr = *img++;
|
||
|
|
+ display_attr(curr_attr);
|
||
|
|
+ if (curr_color != NULL_COLOR)
|
||
|
|
+ display_color(curr_color);
|
||
|
|
continue;
|
||
|
|
- } else if (ch == '$') {
|
||
|
|
- Color fg_color = *img++;
|
||
|
|
- Color bg_color = *img++;
|
||
|
|
- display_color(fg_color, bg_color);
|
||
|
|
+ case LTS_CHAR_COLOR:
|
||
|
|
+ curr_color = *img++;
|
||
|
|
+ display_color(curr_color);
|
||
|
|
+ if (curr_attr != 0)
|
||
|
|
+ display_attr(curr_attr);
|
||
|
|
continue;
|
||
|
|
- } else if (ch == '#') {
|
||
|
|
+ case LTS_CHAR_CURSOR:
|
||
|
|
cursor_x = x;
|
||
|
|
cursor_y = y;
|
||
|
|
continue;
|
||
|
|
diff --git a/lesstest/lt_screen.c b/lesstest/lt_screen.c
|
||
|
|
index 3574320..6b1fde4 100644
|
||
|
|
--- a/lesstest/lt_screen.c
|
||
|
|
+++ b/lesstest/lt_screen.c
|
||
|
|
@@ -9,7 +9,7 @@
|
||
|
|
static const char version[] = "lt_screen|v=1";
|
||
|
|
|
||
|
|
int usage(void) {
|
||
|
|
- fprintf(stderr, "usage: lt_screen\n");
|
||
|
|
+ fprintf(stderr, "usage: lt_screen [-w width] [-h height] [-qv]\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -20,8 +20,7 @@ int usage(void) {
|
||
|
|
typedef struct ScreenChar {
|
||
|
|
wchar ch;
|
||
|
|
Attr attr;
|
||
|
|
- Color fg_color;
|
||
|
|
- Color bg_color;
|
||
|
|
+ Color color;
|
||
|
|
} ScreenChar;
|
||
|
|
|
||
|
|
typedef struct ScreenState {
|
||
|
|
@@ -31,8 +30,7 @@ typedef struct ScreenState {
|
||
|
|
int cx;
|
||
|
|
int cy;
|
||
|
|
Attr curr_attr;
|
||
|
|
- Color curr_fg_color;
|
||
|
|
- Color curr_bg_color;
|
||
|
|
+ Color curr_color;
|
||
|
|
int param_top;
|
||
|
|
int params[MAX_PARAMS+1];
|
||
|
|
int in_esc;
|
||
|
|
@@ -53,7 +51,7 @@ static void screen_init(void) {
|
||
|
|
screen.cy = 0;
|
||
|
|
screen.in_esc = 0;
|
||
|
|
screen.curr_attr = 0;
|
||
|
|
- screen.curr_fg_color = screen.curr_bg_color = 0;
|
||
|
|
+ screen.curr_color = NULL_COLOR;
|
||
|
|
screen.param_top = -1;
|
||
|
|
screen.params[0] = 0;
|
||
|
|
}
|
||
|
|
@@ -74,7 +72,7 @@ static void param_push(int v) {
|
||
|
|
|
||
|
|
static int param_pop(void){
|
||
|
|
if (screen.param_top < 0)
|
||
|
|
- return 0; // missing param is assumed to be 0
|
||
|
|
+ return -1; // missing param
|
||
|
|
return screen.params[screen.param_top--];
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -107,17 +105,16 @@ static int screen_incr(int* px, int* py) {
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
-static void screen_char_set(int x, int y, wchar ch, Attr attr, Color fg_color, Color bg_color) {
|
||
|
|
+static void screen_char_set(int x, int y, wchar ch, Attr attr, Color color) {
|
||
|
|
ScreenChar* sc = screen_char(x, y);
|
||
|
|
sc->ch = ch;
|
||
|
|
sc->attr = attr;
|
||
|
|
- sc->fg_color = fg_color;
|
||
|
|
- sc->bg_color = bg_color;
|
||
|
|
+ sc->color = color;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int screen_clear(int x, int y, int count) {
|
||
|
|
while (count-- > 0) {
|
||
|
|
- screen_char_set(x, y, '_', 0, 0, 0);
|
||
|
|
+ screen_char_set(x, y, '_', 0, NULL_COLOR);
|
||
|
|
screen_incr(&x, &y);
|
||
|
|
}
|
||
|
|
return 1;
|
||
|
|
@@ -125,28 +122,25 @@ static int screen_clear(int x, int y, int count) {
|
||
|
|
|
||
|
|
static int screen_read(int x, int y, int count) {
|
||
|
|
//write(ttyout, "$|", 2);
|
||
|
|
- int attr = 0;
|
||
|
|
- int fg_color = 0;
|
||
|
|
- int bg_color = 0;
|
||
|
|
+ Attr attr = 0;
|
||
|
|
+ int color = NULL_COLOR;
|
||
|
|
while (count-- > 0) {
|
||
|
|
byte buf[32];
|
||
|
|
byte* bufp = buf;
|
||
|
|
ScreenChar* sc = screen_char(x, y);
|
||
|
|
if (sc->attr != attr) {
|
||
|
|
attr = sc->attr;
|
||
|
|
- *bufp++ = '@';
|
||
|
|
+ *bufp++ = LTS_CHAR_ATTR;
|
||
|
|
*bufp++ = attr;
|
||
|
|
}
|
||
|
|
- if (sc->fg_color != fg_color || sc->bg_color != bg_color) {
|
||
|
|
- fg_color = sc->fg_color;
|
||
|
|
- bg_color = sc->bg_color;
|
||
|
|
- *bufp++ = '$';
|
||
|
|
- *bufp++ = fg_color;
|
||
|
|
- *bufp++ = bg_color;
|
||
|
|
+ if (sc->color != color) {
|
||
|
|
+ color = sc->color;
|
||
|
|
+ *bufp++ = LTS_CHAR_COLOR;
|
||
|
|
+ *bufp++ = color;
|
||
|
|
}
|
||
|
|
if (x == screen.cx && y == screen.cy)
|
||
|
|
- *bufp++ = '#';
|
||
|
|
- if (sc->ch == '@' || sc->ch == '$' || sc->ch == '\\' || sc->ch == '#')
|
||
|
|
+ *bufp++ = LTS_CHAR_CURSOR;
|
||
|
|
+ if (sc->ch == '\\' || sc->ch == LTS_CHAR_ATTR || sc->ch == LTS_CHAR_COLOR || sc->ch == LTS_CHAR_CURSOR)
|
||
|
|
*bufp++ = '\\';
|
||
|
|
store_wchar(&bufp, sc->ch);
|
||
|
|
write(ttyout, buf, bufp-buf);
|
||
|
|
@@ -189,12 +183,20 @@ static int screen_rscroll(void) {
|
||
|
|
|
||
|
|
static int screen_set_attr(int attr) {
|
||
|
|
screen.curr_attr |= attr;
|
||
|
|
- return 0;
|
||
|
|
+ if (verbose) fprintf(stderr, "[%d,%d] set_attr(%d)=%d\n", screen.cx, screen.cy, attr, screen.curr_attr);
|
||
|
|
+ return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int screen_clear_attr(int attr) {
|
||
|
|
screen.curr_attr &= ~attr;
|
||
|
|
- return 0;
|
||
|
|
+ if (verbose) fprintf(stderr, "[%d,%d] clr_attr(%d)=%d\n", screen.cx, screen.cy, attr, screen.curr_attr);
|
||
|
|
+ return 1;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+static int screen_set_color(int color) {
|
||
|
|
+ screen.curr_color = (color >= 0) ? color : NULL_COLOR;
|
||
|
|
+ if (verbose) fprintf(stderr, "[%d,%d] set_color(%d)=%d\n", screen.cx, screen.cy, color, screen.curr_color);
|
||
|
|
+ return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ------------------------------------------------------------------
|
||
|
|
@@ -223,10 +225,15 @@ static int exec_esc(wchar ch) {
|
||
|
|
count = param_pop();
|
||
|
|
y = param_pop();
|
||
|
|
x = param_pop();
|
||
|
|
+ if (x < 0) x = 0;
|
||
|
|
+ if (y < 0) y = 0;
|
||
|
|
+ if (count < 0) count = 0;
|
||
|
|
return screen_read(x, y, count);
|
||
|
|
case 'j': // jump cursor to (N1,N2)
|
||
|
|
y = param_pop();
|
||
|
|
x = param_pop();
|
||
|
|
+ if (x < 0) x = 0;
|
||
|
|
+ if (y < 0) y = 0;
|
||
|
|
return screen_move(x, y);
|
||
|
|
case 'g': // visual bell
|
||
|
|
return 0;
|
||
|
|
@@ -256,6 +263,8 @@ static int exec_esc(wchar ch) {
|
||
|
|
return screen_clear_attr(ATTR_BLINK);
|
||
|
|
case 'E': // exit bold/blink
|
||
|
|
return screen_clear_attr(ATTR_BOLD|ATTR_BLINK);
|
||
|
|
+ case 'm': // set color
|
||
|
|
+ return screen_set_color(param_pop());
|
||
|
|
case '?': // print version string
|
||
|
|
write(ttyout, version, strlen(version));
|
||
|
|
return 1;
|
||
|
|
@@ -266,10 +275,10 @@ static int exec_esc(wchar ch) {
|
||
|
|
|
||
|
|
static int add_char(wchar ch) {
|
||
|
|
//if (verbose) fprintf(stderr, "add (%c) %lx at %d,%d\n", (char)ch, (long)ch, screen.cx, screen.cy);
|
||
|
|
- screen_char_set(screen.cx, screen.cy, ch, screen.curr_attr, screen.curr_fg_color, screen.curr_bg_color);
|
||
|
|
+ screen_char_set(screen.cx, screen.cy, ch, screen.curr_attr, screen.curr_color);
|
||
|
|
int fits = screen_incr(&screen.cx, &screen.cy);
|
||
|
|
if (fits && is_wide_char(ch)) {
|
||
|
|
- screen_char_set(screen.cx, screen.cy, 0, 0, 0, 0);
|
||
|
|
+ screen_char_set(screen.cx, screen.cy, 0, 0, NULL_COLOR);
|
||
|
|
fits = screen_incr(&screen.cx, &screen.cy);
|
||
|
|
}
|
||
|
|
if (!fits) { // Wrap at bottom of screen = scroll
|
||
|
|
@@ -284,9 +293,12 @@ static int process_char(wchar ch) {
|
||
|
|
int ok = 1;
|
||
|
|
if (screen.in_esc) {
|
||
|
|
if (ch >= '0' && ch <= '9') {
|
||
|
|
- param_push(10 * param_pop() + ch - '0');
|
||
|
|
+ int d = (screen.param_top < 0) ? 0 : screen.params[screen.param_top--];
|
||
|
|
+ param_push(10 * d + ch - '0');
|
||
|
|
} else if (ch == ';') {
|
||
|
|
param_push(0);
|
||
|
|
+ } else if (ch == '[') {
|
||
|
|
+ ; // ANSI sequence
|
||
|
|
} else {
|
||
|
|
screen.in_esc = 0;
|
||
|
|
ok = exec_esc(ch);
|
||
|
|
diff --git a/lesstest/lt_types.h b/lesstest/lt_types.h
|
||
|
|
index 2b3c1bc..3f98376 100644
|
||
|
|
--- a/lesstest/lt_types.h
|
||
|
|
+++ b/lesstest/lt_types.h
|
||
|
|
@@ -5,6 +5,8 @@ typedef unsigned char byte;
|
||
|
|
typedef unsigned char Attr;
|
||
|
|
typedef unsigned char Color;
|
||
|
|
|
||
|
|
+#define NULL_COLOR ((Color)0xff)
|
||
|
|
+
|
||
|
|
#define ATTR_BOLD (1<<0)
|
||
|
|
#define ATTR_UNDERLINE (1<<1)
|
||
|
|
#define ATTR_STANDOUT (1<<2)
|
||
|
|
@@ -18,6 +20,10 @@ typedef unsigned char Color;
|
||
|
|
#define RUN_OK 0
|
||
|
|
#define RUN_ERR 1
|
||
|
|
|
||
|
|
+#define LTS_CHAR_ATTR '@'
|
||
|
|
+#define LTS_CHAR_COLOR '$'
|
||
|
|
+#define LTS_CHAR_CURSOR '#'
|
||
|
|
+
|
||
|
|
#define is_ascii(ch) ((ch) >= ' ' && (ch) < 0x7f)
|
||
|
|
#define pr_ascii(ch) (is_ascii(ch) ? ((char)ch) : '.')
|
||
|
|
|
||
|
|
diff --git a/lesstest/runtest b/lesstest/runtest
|
||
|
|
index fe7fcab..2a39047 100755
|
||
|
|
--- a/lesstest/runtest
|
||
|
|
+++ b/lesstest/runtest
|
||
|
|
@@ -59,7 +59,8 @@ sub run {
|
||
|
|
print "ERR cannot open $file\n";
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
- print "TEST $file\n";
|
||
|
|
+ my ($basename) = $file =~ m|^.*/([^/]+)$|;
|
||
|
|
+ print "TEST $basename\n";
|
||
|
|
my $cmd = "$lesstest $lt_opts -s '$lt_screen' -t '$file' '$less'";
|
||
|
|
my $err = system $cmd;
|
||
|
|
if ($err) {
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|