From c1105485079fba97a62df62baeff3a4dcc7686ef Mon Sep 17 00:00:00 2001
From: inmarket <andrewh@inmarket.com.au>
Date: Tue, 4 Mar 2014 14:14:15 +1000
Subject: Add support for ANSI like escape sequences to control display color
 and attributes in a GWIN console. Updated the console demo to demonstrate
 this.

---
 demos/modules/gwin/console/gfxconf.h |   1 +
 demos/modules/gwin/console/main.c    |   6 +-
 gfxconf.example.h                    |   1 +
 src/gwin/console.c                   | 176 +++++++++++++++++++++++++++++++++--
 src/gwin/console.h                   |   6 ++
 src/gwin/sys_options.h               |  19 ++++
 6 files changed, 199 insertions(+), 10 deletions(-)

diff --git a/demos/modules/gwin/console/gfxconf.h b/demos/modules/gwin/console/gfxconf.h
index 3ada3b89..e1b4db61 100644
--- a/demos/modules/gwin/console/gfxconf.h
+++ b/demos/modules/gwin/console/gfxconf.h
@@ -52,6 +52,7 @@
 /* Features for the GWIN subsystem. */
 #define GWIN_NEED_WINDOWMANAGER	TRUE
 #define GWIN_NEED_CONSOLE		TRUE
+#define GWIN_CONSOLE_ESCSEQ		TRUE
 
 #endif /* _GFXCONF_H */
 
diff --git a/demos/modules/gwin/console/main.c b/demos/modules/gwin/console/main.c
index 83753c6c..e9165cac 100644
--- a/demos/modules/gwin/console/main.c
+++ b/demos/modules/gwin/console/main.c
@@ -75,17 +75,17 @@ int main(void) {
 
 	/* Output some data on the first console */
 	for(i = 0; i < 10; i++) {
-		gwinPrintf(GW1, "Hello uGFX!\r\n");
+		gwinPrintf(GW1, "Hello \033buGFX\033B!\r\n");
 	}
 
 	/* Output some data on the second console */
 	for(i = 0; i < 16; i++) {
-		gwinPrintf(GW2, "Message Nr.: %d\r\n", i+1);
+		gwinPrintf(GW2, "Message Nr.: \0331\033b%d\033B\033C\r\n", i+1);
 	}
 
 	/* Output some data on the third console */
 	for(i = 0; i < 18; i++) {
-		gwinPrintf(GW3, "Message Nr.: %d\r\n", i+1);
+		gwinPrintf(GW3, "Message Nr.: \033u%d\033U\r\n", i+1);
 	}
 
 	while(TRUE) {
diff --git a/gfxconf.example.h b/gfxconf.example.h
index d9dcf88b..b7a8255a 100644
--- a/gfxconf.example.h
+++ b/gfxconf.example.h
@@ -129,6 +129,7 @@
     #define GWIN_CONSOLE_USE_HISTORY                 FALSE
 		#define GWIN_CONSOLE_HISTORY_AVERAGING       FALSE
 		#define GWIN_CONSOLE_HISTORY_ATCREATE        FALSE
+		#define GWIN_CONSOLE_ESCSEQ                  FALSE
 	#define GWIN_CONSOLE_USE_BASESTREAM              FALSE
     #define GWIN_CONSOLE_USE_FLOAT                   FALSE
 #define GWIN_NEED_GRAPH                              FALSE
diff --git a/src/gwin/console.c b/src/gwin/console.c
index 0fe4b722..53557fd6 100644
--- a/src/gwin/console.c
+++ b/src/gwin/console.c
@@ -26,6 +26,14 @@
 #define GCONSOLE_FLG_NOSTORE					(GWIN_FIRST_CONTROL_FLAG<<0)
 #define GCONSOLE_FLG_OVERRUN					(GWIN_FIRST_CONTROL_FLAG<<1)
 
+// Meaning of our attribute bits.
+#define	ESC_REDBIT		0x01
+#define	ESC_GREENBIT	0x02
+#define	ESC_BLUEBIT		0x04
+#define ESC_USECOLOR	0x08
+#define ESC_UNDERLINE	0x10
+#define ESC_BOLD		0x20
+
 /*
  * Stream interface implementation. The interface is write only
  */
@@ -58,6 +66,68 @@
 	};
 #endif
 
+#if GWIN_CONSOLE_ESCSEQ
+	// Convert escape sequences to attributes
+	static bool_t ESCtoAttr(char c, uint8_t *pattr) {
+		uint8_t		attr;
+
+		attr = pattr[0];
+		switch(c) {
+		case '0': case '1': case '2': case '3':
+		case '4': case '5': case '6': case '7':
+			attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT);
+			attr |= (c - '0') | ESC_USECOLOR;
+			break;
+		case 'C':
+			attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR);
+			break;
+		case 'u':
+			attr |= ESC_UNDERLINE;
+			break;
+		case 'U':
+			attr &= ~ESC_UNDERLINE;
+			break;
+		case 'b':
+			attr |= ESC_BOLD;
+			break;
+		case 'B':
+			attr &= ~ESC_BOLD;
+			break;
+		default:
+			return FALSE;
+		}
+		if (attr == pattr[0])
+			return FALSE;
+		pattr[0] = attr;
+		return TRUE;
+	}
+
+	static color_t ESCPrintColor(GConsoleObject *gcw) {
+		switch(gcw->currattr & (ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR)) {
+		case (ESC_USECOLOR):
+			return Black;
+		case (ESC_USECOLOR|ESC_REDBIT):
+			return Red;
+		case (ESC_USECOLOR|ESC_GREENBIT):
+			return Green;
+		case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT):
+			return Yellow;
+		case (ESC_USECOLOR|ESC_BLUEBIT):
+			return Blue;
+		case (ESC_USECOLOR|ESC_REDBIT|ESC_BLUEBIT):
+			return Magenta;
+		case (ESC_USECOLOR|ESC_GREENBIT|ESC_BLUEBIT):
+			return Cyan;
+		case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT):
+			return White;
+		default:
+			return gcw->g.color;
+		}
+	}
+#else
+	#define ESCPrintColor(gcw)		((gcw)->g.color)
+#endif
+
 #if GWIN_CONSOLE_USE_HISTORY
 	static void HistoryDestroy(GWindowObject *gh) {
 		#define gcw		((GConsoleObject *)gh)
@@ -90,6 +160,11 @@
 		gcw->cx = 0;
 		gcw->cy = 0;
 
+		// Reset the current attributes
+		#if GWIN_CONSOLE_ESCSEQ
+			gcw->currattr = gcw->startattr;
+		#endif
+
 		// Print the buffer
 		gwinPutCharArray(gh, gcw->buffer, gcw->bufpos);
 
@@ -115,7 +190,7 @@
 
 		// Do we have enough space in the buffer
 		if (gcw->bufpos >= gcw->bufsize) {
-			char *	p;
+			char	*p, *ep;
 			size_t	dp;
 
 			/**
@@ -130,7 +205,13 @@
 			 */
 
 			// Remove one line from the start
-			for(p = gcw->buffer; *p && *p != '\n'; p++);
+			ep = gcw->buffer+gcw->bufpos;
+			for(p = gcw->buffer; p < ep && *p != '\n'; p++) {
+				#if GWIN_CONSOLE_ESCSEQ
+					if (*p == 27)
+						ESCtoAttr(p[1], &gcw->startattr);
+				#endif
+			}
 
 			// Was there a newline?
 			if (*p != '\n')
@@ -153,7 +234,7 @@
 	 * Scroll the history buffer by one line
 	 */
 	static void scrollBuffer(GConsoleObject *gcw) {
-		char *	p;
+		char	*p, *ep;
 		size_t	dp;
 
 		// Only scroll if we need to
@@ -167,7 +248,13 @@
 		}
 
 		// Remove one line from the start
-		for(p = gcw->buffer; *p && *p != '\n'; p++);
+		ep = gcw->buffer+gcw->bufpos;
+		for(p = gcw->buffer; p < ep && *p != '\n'; p++) {
+			#if GWIN_CONSOLE_ESCSEQ
+				if (*p == 27)
+					ESCtoAttr(p[1], &gcw->startattr);
+			#endif
+		}
 
 		// Was there a newline, if not delete everything.
 		if (*p != '\n') {
@@ -205,6 +292,9 @@ static void AfterClear(GWindowObject *gh) {
 	gcw->cx = 0;
 	gcw->cy = 0;
 	clearBuffer(gcw);
+	#if GWIN_CONSOLE_ESCSEQ
+		gcw->startattr = gcw->currattr;
+	#endif
 	#undef gcw		
 }
 
@@ -239,6 +329,11 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p
 	gc->cx = 0;
 	gc->cy = 0;
 
+	#if GWIN_CONSOLE_ESCSEQ
+		gc->startattr = gc->currattr = 0;
+		gc->escstate = 0;
+	#endif
+
 	gwinSetVisible((GHandle)gc, pInit->show);
 
 	return (GHandle)gc;
@@ -313,13 +408,54 @@ void gwinPutChar(GHandle gh, char c) {
 	#if GDISP_NEED_CLIP
 		gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
 	#endif
-	
+
+	#if GWIN_CONSOLE_ESCSEQ
+		/**
+		 * Handle escape sequences
+		 * 			ESC color		Change subsequent text color
+		 * 							color:	"0" = black, "1" = red, "2" = green, "3" = yellow, "4" = blue,
+		 * 									"5" = magenta, "6" = cyan, "7" = white
+		 * 			ESC C			Revert subsequent text color to the window default
+		 * 			ESC u			Turn on underline
+		 * 			ESC U			Turn off underline
+		 * 			ESC b			Turn on bold
+		 * 			ESC B			Turn off bold
+		 * 			ESC J			Clear the window
+		 */
+		switch (gcw->escstate) {
+		case 1:
+			gcw->escstate = 0;
+			if (ESCtoAttr(c, &gcw->currattr)) {
+				if (gcw->cx == 0 && gcw->cy == 0)
+					gcw->startattr = gcw->currattr;
+				else {
+					putCharInBuffer(gcw, 27);
+					putCharInBuffer(gcw, c);
+				}
+			} else {
+				switch(c) {
+				case 'J':
+					// Clear the console and reset the cursor
+					clearBuffer(gcw);
+					gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
+					gcw->cx = 0;
+					gcw->cy = 0;
+					gcw->startattr = gcw->currattr;
+					break;
+				}
+			}
+			return;
+		}
+	#endif
+
 	/**
 	 * Special Characters:
 	 *
 	 * Carriage returns and line feeds (\r & \n) are handled in unix terminal cooked mode; that is,
 	 * line feeds perform both actions and carriage-returns are ignored.
 	 *
+	 * if GWIN_CONSOLE_ESCSEQ is turned on then ESC is trapped ready for the escape command.
+	 *
 	 * All other characters are treated as printable.
 	 */
 	switch (c) {
@@ -339,12 +475,24 @@ void gwinPutChar(GHandle gh, char c) {
 	case '\r':
 		// gcw->cx = 0;
 		return;
+
+	#if GWIN_CONSOLE_ESCSEQ
+		case 27:		// ESC
+			gcw->escstate = 1;
+			return;
+	#endif
 	}
 
 	// Characters with no width are ignored
 	if (!(width = gdispGetCharWidth(c, gh->font)))
 		return;
 
+	// Allow space for (very crude) bold
+	#if GWIN_CONSOLE_ESCSEQ
+		if ((gcw->currattr & ESC_BOLD))
+			width++;
+	#endif
+
 	// Do we need to go to the next line to fit this character?
 	if (gcw->cx + width >= gh->width) {
 		gcw->cx = 0;
@@ -378,6 +526,9 @@ void gwinPutChar(GHandle gh, char c) {
 				gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
 				gcw->cx = 0;
 				gcw->cy = 0;
+				#if GWIN_CONSOLE_ESCSEQ
+					gcw->startattr = gcw->currattr;
+				#endif
 			}
 		#endif
 	}
@@ -390,12 +541,23 @@ void gwinPutChar(GHandle gh, char c) {
 
 	// Draw the character
 	#if GWIN_CONSOLE_USE_FILLED_CHARS
-		gdispGFillChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color, gh->bgcolor);
+		gdispGFillChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw), gh->bgcolor);
 	#else
-		gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color);
+		gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw));
 	#endif
 	putCharInBuffer(gcw, c);
 
+	#if GWIN_CONSOLE_ESCSEQ
+		// Draw the underline
+		if ((gcw->currattr & ESC_UNDERLINE))
+			gdispGDrawLine(gh->display, gh->x + gcw->cx, gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight),
+										gh->x + gcw->cx + width + gdispGetFontMetric(gh->font, fontCharPadding), gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight),
+										ESCPrintColor(gcw));
+		// Bold (very crude)
+		if ((gcw->currattr & ESC_BOLD))
+			gdispGDrawChar(gh->display, gh->x + gcw->cx + 1, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw));
+	#endif
+
 	// Update the cursor
 	gcw->cx += width + gdispGetFontMetric(gh->font, fontCharPadding);
 
diff --git a/src/gwin/console.h b/src/gwin/console.h
index 252b627e..14bc7eb3 100644
--- a/src/gwin/console.h
+++ b/src/gwin/console.h
@@ -31,6 +31,12 @@ typedef struct GConsoleObject {
 	GWindowObject	g;
 	coord_t			cx, cy;			// Cursor position
 
+	#if GWIN_CONSOLE_ESCSEQ
+		uint8_t		startattr;		// ANSI-like escape sequences
+		uint8_t		currattr;
+		uint16_t	escstate;
+	#endif
+
 	#if GWIN_CONSOLE_USE_HISTORY
 		char *		buffer;			// buffer to store console content
 		size_t		bufsize;		// size of buffer
diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h
index 02467916..e7bb93b4 100644
--- a/src/gwin/sys_options.h
+++ b/src/gwin/sys_options.h
@@ -159,6 +159,25 @@
 	#ifndef GWIN_CONSOLE_USE_FLOAT
 		#define GWIN_CONSOLE_USE_FLOAT			FALSE
 	#endif
+	/**
+	 * @brief   Console windows support escape sequences to control display
+	 * @details	Defaults to FALSE
+	 *
+	 * @note
+	 * 		Currently supported:
+	 * 			ESC color		Change subsequent text color
+	 * 							color:	"0" = black, "1" = red, "2" = green, "3" = yellow, "4" = blue,
+	 * 									"5" = magenta, "6" = cyan, "7" = white
+	 * 			ESC C			Revert subsequent text color to the window default
+	 * 			ESC u			Turn on underline
+	 * 			ESC U			Turn off underline
+	 * 			ESC b			Turn on bold
+	 * 			ESC B			Turn off bold
+	 * 			ESC J			Clear the window
+	 */
+	#ifndef GWIN_CONSOLE_ESCSEQ
+		#define GWIN_CONSOLE_ESCSEQ				FALSE
+	#endif
 	/**
 	 * @brief   Console Windows need BaseStreamSequential support (ChibiOS only)
 	 * @details	Defaults to FALSE
-- 
cgit v1.2.3