From c1105485079fba97a62df62baeff3a4dcc7686ef Mon Sep 17 00:00:00 2001 From: inmarket Date: Tue, 4 Mar 2014 14:14:15 +1000 Subject: [PATCH] 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