Re: Support 256 colours on wscons

nia <[email protected]>
Newsgroups gmane.os.netbsd.devel.kernel
Message-ID <[email protected]>
Hi! Thanks for working on this.

While testing this patch (with a little script that outputs all the
xterm 256 color escapes), I noticed that it only works for 32-bit
wsdisplays.

You can try out a range of colour depths using the "vesa" option
in the bios bootloader, for example, in qemu-system-i386.

I've made a few adjustments to dev/rasops so that 8/16/24-bit
wsdisplays can also use the 256-color extensions. It seems to work.

I'm not sure the reproduction on 8-bit displays is 100% perfect
(certainly the antialiasing could be prettier when using an
antialiased font), but it's definitely usable.

Updated patch and the test script are attached.
simple-256.sh (application/x-sh, 87 B)
#!/bin/sh
for i in $(seq 0 255); do
 printf "\x1b[38;5;${i}mcolour${i}\x1b[0m\n"
done
256color-2.diff (text/plain, 9.9 KB)
Index: dev/wscons/wsemul_vt100_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wsemul_vt100_subr.c,v
retrieving revision 1.34
diff -u -p -r1.34 wsemul_vt100_subr.c
--- dev/wscons/wsemul_vt100_subr.c	3 Aug 2023 22:11:41 -0000	1.34
+++ dev/wscons/wsemul_vt100_subr.c	28 Oct 2025 12:35:24 -0000
@@ -561,6 +561,26 @@ wsemul_vt100_handle_csi(struct vt100base
 				flags |= WSATTR_WSCOLORS;
 				fgcol = ARG(vd, n) - 30;
 				break;
+#define EXIST_ARG2(i) ((vd->nargs - i) >= 3)
+#define ARG2_OR_DEF(i) (EXIST_ARG2(i) ? ARG(vd, i + 2) : 0)
+			    case 38:
+				if (vd->nargs == n + 1)
+					break;
+				if (ARG(vd, n + 1) == 5) {
+					flags |= WSATTR_WSCOLORS;
+					if (vd->scrcapabilities &
+					    WSSCREEN_256COL)
+						fgcol = ARG2_OR_DEF(n);
+					n += (EXIST_ARG2(n) ? 2 : 1);
+					break;
+				}
+				if (ARG(vd, n + 1) == 2) {
+					n = (vd->nargs - n > 5 ? n + 4 :
+					    vd->nargs);
+					break;
+				}
+				n++;
+				break;
 			    case 39:
 				fgcol = vd->msgattrs.default_fg;
 				break;
@@ -570,9 +590,39 @@ wsemul_vt100_handle_csi(struct vt100base
 				flags |= WSATTR_WSCOLORS;
 				bgcol = ARG(vd, n) - 40;
 				break;
+			    case 48:
+				if (vd->nargs == n + 1)
+					break;
+				if (ARG(vd, n + 1) == 5) {
+					flags |= WSATTR_WSCOLORS;
+					if (vd->scrcapabilities &
+					    WSSCREEN_256COL)
+						bgcol = ARG2_OR_DEF(n);
+					n += (EXIST_ARG2(n) ? 2 : 1);
+					break;
+				}
+				if (ARG(vd, n + 1) == 2) {
+					n = (vd->nargs - n > 5 ? n + 4 :
+					    vd->nargs);
+					break;
+				}
+				n++;
+				break;
 			    case 49:
 				bgcol = vd->msgattrs.default_bg;
 				break;
+			    case 90: case 91: case 92: case 93:
+			    case 94: case 95: case 96: case 97:
+				/* bright foreground color */
+				flags |= WSATTR_WSCOLORS;
+				fgcol = ARG(vd, n) - 82;
+				break;
+			    case 100: case 101: case 102: case 103:
+			    case 104: case 105: case 106: case 107:
+				/* bright background color */
+				flags |= WSATTR_WSCOLORS;
+				bgcol = ARG(vd, n) - 92;
+				break;
 			    default:
 #ifdef VT100_PRINTUNKNOWN
 				printf("CSI%dm unknown\n", ARG(vd, n));
Index: dev/wscons/wsdisplayvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wsdisplayvar.h,v
retrieving revision 1.57
diff -u -p -r1.57 wsdisplayvar.h
--- dev/wscons/wsdisplayvar.h	1 Mar 2023 08:42:33 -0000	1.57
+++ dev/wscons/wsdisplayvar.h	28 Oct 2025 12:35:24 -0000
@@ -107,6 +107,7 @@ struct wsscreen_descr {
 #define WSSCREEN_RESIZE		32	/* can resize */
 #define WSSCREEN_FREE		64	/* free() this struct when deleting
 					 * internal only, do not set */
+#define WSSCREEN_256COL		128
 	void *modecookie;
 };
 
Index: dev/rasops/rasops.c
===================================================================
RCS file: /cvsroot/src/sys/dev/rasops/rasops.c,v
retrieving revision 1.128
diff -u -p -r1.128 rasops.c
--- dev/rasops/rasops.c	15 May 2022 16:43:39 -0000	1.128
+++ dev/rasops/rasops.c	28 Oct 2025 12:35:24 -0000
@@ -80,6 +80,8 @@ static const uint32_t rasops_rmask32[4 +
 	MBE(0xffffffff),
 };
 
+uint8_t rasops_ecmap[256 * 3];
+
 /* ANSI colormap (R,G,B). Upper 8 are high-intensity */
 const uint8_t rasops_cmap[256 * 3] = {
 	0x00, 0x00, 0x00, /* black */
@@ -140,9 +142,39 @@ const uint8_t rasops_cmap[256 * 3] = {
 };
 
 /* True if color is gray */
-static const uint8_t rasops_isgray[16] = {
+static const uint8_t rasops_isgray[256] = {
 	1, 0, 0, 0, 0, 0, 0, 1,
 	1, 0, 0, 0, 0, 0, 0, 1,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
 };
 
 #ifdef RASOPS_APPLE_PALETTE
@@ -535,22 +567,26 @@ rasops_reconfig(struct rasops_info *ri, 
 #endif
 #if NRASOPS8 > 0
 	case 8:
+		ri->ri_caps |= WSSCREEN_256COL;
 		rasops8_init(ri);
 		break;
 #endif
 #if (NRASOPS15 + NRASOPS16) > 0
 	case 15:
 	case 16:
+		ri->ri_caps |= WSSCREEN_256COL;
 		rasops15_init(ri);
 		break;
 #endif
 #if NRASOPS24 > 0
 	case 24:
+		ri->ri_caps |= WSSCREEN_256COL;
 		rasops24_init(ri);
 		break;
 #endif
 #if NRASOPS32 > 0
 	case 32:
+		ri->ri_caps |= WSSCREEN_256COL;
 		rasops32_init(ri);
 		break;
 #endif
@@ -619,8 +655,8 @@ rasops_allocattr_color(void *cookie, int
 		return EINVAL;
 
 #ifdef RASOPS_CLIPPING
-	fg &= 7;
-	bg &= 7;
+	fg &= 0xff;
+	bg &= 0xff;
 #endif
 
 	if ((flg & WSATTR_BLINK) != 0)
@@ -904,7 +940,7 @@ rasops_init_devcmap(struct rasops_info *
 
 	p = rasops_cmap;
 
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < ((ri->ri_caps & WSSCREEN_256COL) ? 256 : 16); i++) {
 		if (ri->ri_rnum <= 8)
 			c = (uint32_t)(*p >> (8 - ri->ri_rnum)) << ri->ri_rpos;
 		else
@@ -923,6 +959,32 @@ rasops_init_devcmap(struct rasops_info *
 			c |= (uint32_t)(*p << (ri->ri_bnum - 8)) << ri->ri_bpos;
 		p++;
 
+#define EP_BLUE_RAW(x) (48 * ((x - 16) % 6))
+#define EP_GREEN_RAW(x) (48 * (((x - 16)/6) % 6))
+#define EP_RED_RAW(x) (48 * (((x - 16)/36) % 6))
+#define EP_BLUE(x) ((EP_BLUE_RAW(x) >> (8 - ri->ri_bnum)) << ri->ri_bpos)
+#define EP_GREEN(x) ((EP_GREEN_RAW(x) >> (8 - ri->ri_gnum)) << ri->ri_gpos)
+#define EP_RED(x) ((EP_RED_RAW(x) >> (8 - ri->ri_rnum)) << ri->ri_rpos)
+#define EP_COL(x) EP_RED(x) | EP_GREEN(x) | EP_BLUE(x)
+#define EP_GREY_BYTE(x) (1 + ((x - 232) * 11))
+#define GREYSCALE_BLUE(x) ((EP_GREY_BYTE(x) >> (8 - ri->ri_bnum)) << ri->ri_bpos)
+#define GREYSCALE_GREEN(x) ((EP_GREY_BYTE(x) >> (8 - ri->ri_gnum)) << ri->ri_gpos)
+#define GREYSCALE_RED(x) ((EP_GREY_BYTE(x) >> (8 - ri->ri_rnum)) << ri->ri_rpos)
+#define EP_GREY(x) GREYSCALE_RED(x) | GREYSCALE_GREEN(x) | GREYSCALE_BLUE(x)
+		if (i >= 16) {
+			rasops_ecmap[i * 3] =
+			    (i < 232 ? EP_RED_RAW(i) : EP_GREY_BYTE(i));
+			rasops_ecmap[i * 3 + 1] =
+			    (i < 232 ? EP_GREEN_RAW(i) : EP_GREY_BYTE(i));
+			rasops_ecmap[i * 3 + 2] =
+			    (i < 232 ? EP_BLUE_RAW(i) : EP_GREY_BYTE(i));
+			c = (i < 232 ? EP_COL(i) : EP_GREY(i));
+		} else {
+			rasops_ecmap[i * 3] = rasops_cmap[i * 3];
+			rasops_ecmap[i * 3 + 1] = rasops_cmap[i * 3 + 1];
+			rasops_ecmap[i * 3 + 2] = rasops_cmap[i * 3 + 2];
+		}
+
 		/*
 		 * Swap byte order if necessary. Then, fill the word for
 		 * generic routines, which want this.
@@ -975,8 +1037,8 @@ void
 rasops_unpack_attr(long attr, int *fg, int *bg, int *underline)
 {
 
-	*fg = ((uint32_t)attr >> 24) & 0xf;
-	*bg = ((uint32_t)attr >> 16) & 0xf;
+	*fg = ((uint32_t)attr >> 24) & 0xff;
+	*bg = ((uint32_t)attr >> 16) & 0xff;
 	if (underline != NULL)
 		*underline = (uint32_t)attr & WSATTR_UNDERLINE;
 }
Index: dev/rasops/rasops.h
===================================================================
RCS file: /cvsroot/src/sys/dev/rasops/rasops.h,v
retrieving revision 1.51
diff -u -p -r1.51 rasops.h
--- dev/rasops/rasops.h	25 Jul 2025 18:19:12 -0000	1.51
+++ dev/rasops/rasops.h	28 Oct 2025 12:35:24 -0000
@@ -145,7 +145,7 @@ struct rasops_info {
 	int	ri_xorigin;	/* where ri_bits begins (x) */
 	int	ri_yorigin;	/* where ri_bits begins (y) */
 	uint32_t
-		ri_devcmap[16]; /* color -> framebuffer data */
+		ri_devcmap[256]; /* color -> framebuffer data */
 
 	/* The emulops you need to use, and the screen caps for wscons */
 	struct	wsdisplay_emulops ri_ops;
@@ -190,6 +190,7 @@ void	rasops_erasecols(void *, int, int, 
 int	rasops_get_cmap(struct rasops_info *, uint8_t *, size_t);
 
 extern const uint8_t	rasops_cmap[256 * 3];
+extern uint8_t	rasops_ecmap[256 * 3];
 
 #ifdef _RASOPS_PRIVATE
 /*
@@ -203,11 +204,11 @@ void	rasops15_init(struct rasops_info *)
 void	rasops24_init(struct rasops_info *);
 void	rasops32_init(struct rasops_info *);
 
-#define	ATTR_BG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 16) & 0xf])
-#define	ATTR_FG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 24) & 0xf])
+#define	ATTR_BG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 16) & 0xff])
+#define	ATTR_FG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 24) & 0xff])
 
-#define	ATTR_MASK_BG __BITS(16, 19)
-#define	ATTR_MASK_FG __BITS(24, 27)
+#define	ATTR_MASK_BG __BITS(16, 23)
+#define	ATTR_MASK_FG __BITS(24, 31)
 
 #define	DELTA(p, d, cast) ((p) = (cast)((uint8_t *)(p) + (d)))
 
Index: dev/rasops/rasops_putchar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/rasops/rasops_putchar.h,v
retrieving revision 1.8
diff -u -p -r1.8 rasops_putchar.h
--- dev/rasops/rasops_putchar.h	10 Aug 2019 01:24:17 -0000	1.8
+++ dev/rasops/rasops_putchar.h	28 Oct 2025 12:35:24 -0000
@@ -151,15 +151,24 @@ NAME(RASOPS_DEPTH)(void *cookie, int row
 		/*
 	 	 * This is independent to positions/lengths of RGB in pixel.
 	 	 */
-		off[0] = (((uint32_t)attr >> 16) & 0xf) * 3;
-		off[1] = (((uint32_t)attr >> 24) & 0xf) * 3;
+		off[0] = (((uint32_t)attr >> 16) & 0xff) * 3;
+		off[1] = (((uint32_t)attr >> 24) & 0xff) * 3;
 
-		r[0] = rasops_cmap[off[0]];
-		r[1] = rasops_cmap[off[1]];
-		g[0] = rasops_cmap[off[0] + 1];
-		g[1] = rasops_cmap[off[1] + 1];
-		b[0] = rasops_cmap[off[0] + 2];
-		b[1] = rasops_cmap[off[1] + 2];
+		if (ri->ri_caps & WSSCREEN_256COL) {
+			r[0] = rasops_ecmap[off[0]];
+			r[1] = rasops_ecmap[off[1]];
+			g[0] = rasops_ecmap[off[0] + 1];
+			g[1] = rasops_ecmap[off[1] + 1];
+			b[0] = rasops_ecmap[off[0] + 2];
+			b[1] = rasops_ecmap[off[1] + 2];
+		} else {
+			r[0] = rasops_cmap[off[0]];
+			r[1] = rasops_cmap[off[1]];
+			g[0] = rasops_cmap[off[0] + 1];
+			g[1] = rasops_cmap[off[1] + 1];
+			b[0] = rasops_cmap[off[0] + 2];
+			b[1] = rasops_cmap[off[1] + 2];
+		}
 #endif
 
 		while (height--) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.