diff --git a/include/gwin/console.h b/include/gwin/console.h index a2dde96b..850c5a57 100644 --- a/include/gwin/console.h +++ b/include/gwin/console.h @@ -31,6 +31,13 @@ typedef struct GConsoleObject { GWindowObject g; coord_t cx, cy; // Cursor position + #if GWIN_CONSOLE_NEED_HISTORY + char* buffer; // buffer to store console content + uint16_t last_char; // the last rendered character + size_t size; // size of buffer + bool_t store; // shall PutChar() store into buffer + #endif + #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM struct GConsoleWindowStream_t { const struct GConsoleWindowVMT_t *vmt; @@ -82,6 +89,22 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p BaseSequentialStream *gwinConsoleGetStream(GHandle gh); #endif +#if GWIN_CONSOLE_NEED_HISTORY + /** + * @brief Assing a buffer to keep track of the content while the widget is invisible. + * @pre GWIN_CONSOLE_NEED_HISTORY must be set to TRUE in your gfxconf.h + * + * @param[in] gh The window handle (must be a console window) + * @param[in] buffer The pointer of the buffer that shall be used. Buffer will be + * dynamically allocated when this is NULL. + * @param[in] size Size of the buffer that has been passed. If buffer is NULL, this + * will be the size of the dynamically allocated buffer. + * + * @return TRUE on success + */ + bool_t gwinConsoleSetBuffer(GHandle gh, void* buffer, size_t size); +#endif + /** * @brief Put a character at the cursor position in the window. * @note Uses the current foreground color to draw the character and fills the background using the background drawing color diff --git a/include/gwin/options.h b/include/gwin/options.h index 6e39d54d..c6fe14b8 100644 --- a/include/gwin/options.h +++ b/include/gwin/options.h @@ -105,6 +105,17 @@ #ifndef GWIN_BUTTON_LAZY_RELEASE #define GWIN_BUTTON_LAZY_RELEASE FALSE #endif + /** + * @brief Should the content of the console be logged or not + * @details If this feature is enable, the content of the console will be stored. + * Every content that gets printed to the console while being invisible + * will be rendered once the console is visible again. All previous written + * content will be restored too. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_CONSOLE_HISTORY + #define GWIN_NEED_CONSOLE_HISTORY FALSE + #endif /** * @brief Console Windows need floating point support in @p gwinPrintf * @details Defaults to FALSE diff --git a/src/gwin/console.c b/src/gwin/console.c index 7966b6ff..71d46f4f 100644 --- a/src/gwin/console.c +++ b/src/gwin/console.c @@ -53,28 +53,68 @@ }; #endif +#if GWIN_CONSOLE_NEED_HISTORY + static void CustomRedraw(GWindowObject *gh) { + #define gcw ((GConsoleObject *)gh) + + uint16_t i; + + // loop through buffer and don't add it again + gcw->store = FALSE; + for (i = 0; i < gcw->last_char; i++) { + gwinPutChar(gh, gcw->buffer[i]); + } + gcw->store = TRUE; + + #undef gcw + } +#endif + static void AfterClear(GWindowObject *gh) { - ((GConsoleObject *)gh)->cx = 0; - ((GConsoleObject *)gh)->cy = 0; + #define gcw ((GConsoleObject *)gh) + gcw->cx = 0; + gcw->cy = 0; + + #if GWIN_CONSOLE_NEED_HISTORY + // issue an overflow, this is some kind + // of emptying the buffer + gcw->last_char = gcw->size; + #endif + + #undef gcw } static const gwinVMT consoleVMT = { - "Console", // The classname - sizeof(GConsoleObject), // The object size - 0, // The destroy routine - 0, // The redraw routine - AfterClear, // The after-clear routine + "Console", // The classname + sizeof(GConsoleObject), // The object size + 0, // The destroy routine + #if GWIN_CONSOLE_NEED_HISTORY + CustomRedraw, // The redraw routine (custom) + #else + 0, // The redraw routine (default) + #endif + AfterClear, // The after-clear routine }; GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *pInit) { if (!(gc = (GConsoleObject *)_gwindowCreate(g, &gc->g, pInit, &consoleVMT, 0))) return 0; + #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM gc->stream.vmt = &GWindowConsoleVMT; #endif + + #if GWIN_CONSOLE_NEED_HISTORY + gc->buffer = 0; + gc->size = 0; + gc->last_char = 0; + #endif + gc->cx = 0; gc->cy = 0; + gwinSetVisible((GHandle)gc, pInit->show); + return (GHandle)gc; } @@ -82,20 +122,72 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p BaseSequentialStream *gwinConsoleGetStream(GHandle gh) { if (gh->vmt != &consoleVMT) return 0; + return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream); } #endif +#if GWIN_CONSOLE_NEED_HISTORY + bool_t gwinConsoleSetBuffer(GHandle gh, void* buffer, size_t size) { + #define gcw ((GConsoleObject *)gh) + + uint8_t buf_width, buf_height, fp, fy; + + if (gh->vmt != &consoleVMT) + return FALSE; + + // assign buffer or allocate new one + if (buffer == 0) { + (void)size; + + // calculate buffer size + fy = gdispGetFontMetric(gh->font, fontHeight); + fp = gdispGetFontMetric(gh->font, fontMinWidth); + buf_height = (gh->height / fy); + buf_width = (gh->width / fp); + + if ((gcw->buffer = (char*)gfxAlloc(buf_width * buf_height)) == 0) + return FALSE; + gcw->size = buf_width * buf_height; + + } else { + + if (size <= 0) + return FALSE; + gcw->buffer = (char*)buffer; + gcw->size = size; + + } + + gcw->last_char = 0; + + return TRUE; + + #undef gcw + } +#endif + void gwinPutChar(GHandle gh, char c) { #define gcw ((GConsoleObject *)gh) uint8_t width, fy, fp; - if (!gwinGetVisible(gh)) - return; - if (gh->vmt != &consoleVMT || !gh->font) return; + #if GWIN_CONSOLE_NEED_HISTORY + // buffer overflow check + if (gcw->last_char >= gcw->size) + gcw->last_char = 0; + + // store new character in buffer + if (gcw->store && gcw->buffer != 0) + gcw->buffer[gcw->last_char++] = c; + + // only render new character and don't issue a complete redraw (performance...) + if (!gwinGetVisible(gh)) + return; + #endif + fy = gdispGetFontMetric(gh->font, fontHeight); fp = gdispGetFontMetric(gh->font, fontCharPadding); @@ -150,17 +242,11 @@ void gwinPutChar(GHandle gh, char c) { } void gwinPutString(GHandle gh, const char *str) { - if (!gwinGetVisible(gh)) - return; - while(*str) gwinPutChar(gh, *str++); } void gwinPutCharArray(GHandle gh, const char *str, size_t n) { - if (!gwinGetVisible(gh)) - return; - while(n--) gwinPutChar(gh, *str++); } @@ -220,9 +306,6 @@ void gwinPrintf(GHandle gh, const char *fmt, ...) { char tmpbuf[MAX_FILLER + 1]; #endif - if (!gwinGetVisible(gh)) - return; - if (gh->vmt != &consoleVMT || !gh->font) return; diff --git a/src/gwin/gwin.c b/src/gwin/gwin.c index dc9208e8..6b9cb81e 100644 --- a/src/gwin/gwin.c +++ b/src/gwin/gwin.c @@ -251,15 +251,24 @@ void gwinRedraw(GHandle gh) { #endif void gwinClear(GHandle gh) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) - return; + /* + * Don't render anything when the window is not visible but + * still call the AfterClear() routine as some widgets will + * need this to clear internal buffers or similar + */ + if (!((gh->flags & GWIN_FLG_VISIBLE))) { + if (gh->vmt->AfterClear) + gh->vmt->AfterClear(gh); + } else { #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); if (gh->vmt->AfterClear) gh->vmt->AfterClear(gh); + } } void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) {