From b0adc0bed3d03b36553ddf245a0f568cae61bc89 Mon Sep 17 00:00:00 2001 From: inmarket Date: Thu, 9 Jan 2014 08:36:25 +1000 Subject: [PATCH 1/6] New codingstyle.txt --- codingstyle.txt | 162 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/codingstyle.txt b/codingstyle.txt index d2c1c3dc..5a397aeb 100644 --- a/codingstyle.txt +++ b/codingstyle.txt @@ -1,2 +1,162 @@ -ToDo + uGFX coding style + +This is a short document describing the preferred coding style for uGFX. + + Chapter 1: Indentation + +Tabs are 4 characters, and thus indentations are also 4 characters. + +Rationale: We like 4 character tabs much better than 8 character tabs. +It is more readable. + + Chapter 2: Placing Braces + +The preferred way, as shown to us by the prophets Kernighan and Ritchie, +is to put the opening brace last on the line, and put the closing brace first, +thusly: + + if (x is true) { + we do y + } + +However, there is one special case, namely functions: they have the +opening brace at the beginning of the next line, thus: + + int function(int x) + { + body of function + } + +We will however accept braces in the general block style for functions +but not the other way around. General blocks MUST have their opening brace +on the same line as the conditional statement. + +Note that the closing brace is empty on a line of its own, _except_ in +the cases where it is followed by a continuation of the same statement, +ie a "while" in a do-statement or an "else" in an if-statement, like +this: + + do { + body of do-loop + } while (condition); + +and + + if (x == y) { + .. + } else if (x > y) { + ... + } else { + .... + } + +Note that closing brace is indented to the level of the start of the block. +Structure definitions are an optional exception. Both of the below style are +acceptable: + + typedef struct { + int a; + ... + } mytype; + + struct mystruct { + int a; + ... + } + + Chapter 3: Naming + +C is a Spartan language, and so should your naming be. Unlike Modula-2 +and Pascal programmers, C programmers do not use cute names like +ThisVariableIsATemporaryCounter. A C programmer would call that +variable "tmp", which is much easier to write, and a lot less +difficult to understand. + +HOWEVER, while long mixed-case names are frowned upon, descriptive names for +global variables are a must. To call a global function "foo" is a +shooting offense. + +GLOBAL variables (to be used only if you _really_ need them) need to +have descriptive names, as do global functions. If you have a function +that counts the number of active users, you should call that +"countActiveUsers()" or similar, you should _not_ call it "cntusr()". + +WHERE long names are required as described above, we prefer the use of +capitalisation on subsequent words (but not the first) rather than underscores +to seperate the words. For example "countActiveUsers()" is preferred to +"count_active_users()" as it is at least as readable and is shorter. + +Encoding the type of a function into the name (so-called Hungarian +notation) is brain damaged - the compiler knows the types anyway and can +check those, and it only confuses the programmer. + +LOCAL variable names should be short, and to the point. If you have +some random integer loop counter, it should probably be called "i". +Calling it "loopCounter" is non-productive, if there is no chance of it +being mis-understood. Similarly, "tmp" can be just about any type of +variable that is used to hold a temporary value. + + Chapter 4: Functions + +Functions should be short and sweet, and do just one thing. + +The maximum length of a function is inversely proportional to the +complexity and indentation level of that function. So, if you have a +conceptually simple function that is just one long (but simple) +case-statement, where you have to do lots of small things for a lot of +different cases, it's OK to have a longer function. + +However, if you have a complex function, and you suspect that a +less-than-gifted first-year high-school student might not even +understand what the function is all about, you should adhere to the +maximum limits all the more closely. Use helper functions with +descriptive names (you can ask the compiler to in-line them if you think +it's performance-critical, and it will probably do a better job of it +that you would have done). + +Another measure of the function is the number of local variables. They +shouldn't exceed 5-10, or you're possibly doing something wrong. Re-think the +function, and split it into smaller pieces. A human brain can +generally easily keep track of about 7 different things, anything more +and it gets confused. You need to understand what you did 2 weeks from now. + +Because uGFX is intended for embedded platforms there are other considerations +that may cause exceptions or emphasise the above. For example, stack space is +a premium. This means that the number of local variables should be minimised as +should the number of parameters. Passing through multiple levels of functions +with lots of parameters is very bad indeed and this can override the desire to +keep functions short and sweet. Clarity however is still essential. + + + Chapter 5: Commenting + +Comments are good, but there is also a danger of over-commenting. NEVER +try to explain HOW your code works in a comment: it's much better to +write the code so that the _working_ is obvious, and it's a waste of +time to explain badly written code. Generally, you want your comments to tell +WHAT your code does, not HOW. + +We use doxygen to document the system. That means that most public functions +are documented in the header defintion file. We do not put doxygen comments in +the source file itself. + +Within the source file, comments should be used to seperate blocks of functions +or definitions within the file. This is to provide clarity to the structure of +the source file itself. An example could be: + /*************************** + * Drawing Functions + ***************************/ + +Single line comments using "//" to start the comment should be used for just that +purpose, to assist in the understanding of that single line. Mutliple single line +comments should never be used to create a block comment. For example, + // This is a very long + // comment spanning several + // lines +is a very bad use of comments. + +Comments within function bodies should be small comments to note or warn +about something particularly clever (or ugly), but try to avoid excess. +Instead, put the comments at the head of a block of code to explain the block +rather than a comment on each line. From 1791bec3483a27843dd279c51023526ced836aa3 Mon Sep 17 00:00:00 2001 From: inmarket Date: Thu, 9 Jan 2014 08:39:08 +1000 Subject: [PATCH 2/6] doc --- releases.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/releases.txt b/releases.txt index 505bc763..dc896f61 100644 --- a/releases.txt +++ b/releases.txt @@ -11,6 +11,7 @@ DEPRECATE: TDISP module removed FIX: Console does not execute gwinPrintf() anymore if not visible FEATURE: Added gwinGetColor() and gwinGetBgColor() FEATURE: Console does now have an optional buffer (GWIN_CONSOLE_USE_HISTORY) +FIX: Updated codingstyle.txt *** changes after 1.9 *** From f8f85b6c43ae032463f66a933fdbec1b6d83a9a3 Mon Sep 17 00:00:00 2001 From: inmarket Date: Thu, 9 Jan 2014 08:52:26 +1000 Subject: [PATCH 3/6] Increase size of gwin flags to 32 bits. --- include/gwin/gwin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gwin/gwin.h b/include/gwin/gwin.h index e964bc3a..f97919e5 100644 --- a/include/gwin/gwin.h +++ b/include/gwin/gwin.h @@ -42,7 +42,7 @@ typedef struct GWindowObject { coord_t x, y; // @< Screen relative position coord_t width, height; // @< Dimensions of this window color_t color, bgcolor; // @< The current drawing colors - uint16_t flags; // @< Window flags (the meaning is private to the GWIN class) + uint32_t flags; // @< Window flags (the meaning is private to the GWIN class) #if GDISP_NEED_TEXT font_t font; // @< The current font #endif From 1d059a619c9634cd681192c6ce06df3568593c35 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 10 Jan 2014 18:38:00 +0100 Subject: [PATCH 4/6] Revert "doc" This reverts commit 1791bec3483a27843dd279c51023526ced836aa3. --- releases.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/releases.txt b/releases.txt index dc896f61..505bc763 100644 --- a/releases.txt +++ b/releases.txt @@ -11,7 +11,6 @@ DEPRECATE: TDISP module removed FIX: Console does not execute gwinPrintf() anymore if not visible FEATURE: Added gwinGetColor() and gwinGetBgColor() FEATURE: Console does now have an optional buffer (GWIN_CONSOLE_USE_HISTORY) -FIX: Updated codingstyle.txt *** changes after 1.9 *** From 1f3f8bdbe6714c2c8ba02f9a819db5bcf80500a3 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 17 Jan 2014 08:04:35 +0100 Subject: [PATCH 5/6] fixes in gdispImage nativ format by Marc Pignat --- src/gdisp/image_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gdisp/image_native.c b/src/gdisp/image_native.c index 72ae8b61..7f249ae8 100644 --- a/src/gdisp/image_native.c +++ b/src/gdisp/image_native.c @@ -107,7 +107,7 @@ gdispImageError gdispImageGDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, } /* For this image decoder we cheat and just seek straight to the region we want to display */ - pos = FRAME0POS + (img->width * sy + cx) * sizeof(pixel_t); + pos = FRAME0POS + (img->width * sy + sx) * sizeof(pixel_t); /* Cycle through the lines */ for(;cy;cy--, y++) { @@ -119,7 +119,7 @@ gdispImageError gdispImageGDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, // Read the data len = img->io.fns->read(&img->io, img->priv->buf, - mx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mx * sizeof(pixel_t))) + mcx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mcx * sizeof(pixel_t))) / sizeof(pixel_t); if (!len) return GDISP_IMAGE_ERR_BADDATA; From 199b89e4dc0b781310f40d0e5743bf355077f9ab Mon Sep 17 00:00:00 2001 From: inmarket Date: Fri, 17 Jan 2014 18:36:28 +1000 Subject: [PATCH 6/6] Updates to console history. This now works well. --- demos/modules/gwin/widgets/gfxconf.h | 5 +- gfxconf.example.h | 8 +- include/gwin/console.h | 24 +- include/gwin/options.h | 46 +++- src/gwin/console.c | 356 +++++++++++++++++++-------- 5 files changed, 318 insertions(+), 121 deletions(-) diff --git a/demos/modules/gwin/widgets/gfxconf.h b/demos/modules/gwin/widgets/gfxconf.h index 111979e3..b22af659 100644 --- a/demos/modules/gwin/widgets/gfxconf.h +++ b/demos/modules/gwin/widgets/gfxconf.h @@ -54,7 +54,10 @@ #define GDISP_DEFAULT_ORIENTATION GDISP_ROTATE_LANDSCAPE /* The following are optional depending on your hardware */ -//#define GDISP_NEED_SCROLL TRUE +//#define GDISP_NEED_SCROLL TRUE +//#define GWIN_CONSOLE_USE_HISTORY TRUE +//#define GWIN_CONSOLE_HISTORY_AVERAGING TRUE +//#define GWIN_CONSOLE_HISTORY_ATCREATE TRUE /* GDISP fonts to include */ #define GDISP_INCLUDE_FONT_UI2 TRUE diff --git a/gfxconf.example.h b/gfxconf.example.h index 4631cddc..7e633978 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -124,8 +124,10 @@ #define GWIN_NEED_WINDOWMANAGER FALSE #define GWIN_NEED_CONSOLE FALSE - #define GWIN_CONSOLE_USE_HISTORY TRUE - #define GWIN_CONSOLE_USE_BASESTREAM FALSE + #define GWIN_CONSOLE_USE_HISTORY FALSE + #define GWIN_CONSOLE_HISTORY_AVERAGING FALSE + #define GWIN_CONSOLE_HISTORY_ATCREATE FALSE + #define GWIN_CONSOLE_USE_BASESTREAM FALSE #define GWIN_CONSOLE_USE_FLOAT FALSE #define GWIN_NEED_GRAPH FALSE @@ -158,7 +160,7 @@ #define GFX_USE_GTIMER FALSE #define GTIMER_THREAD_PRIORITY HIGH_PRIORITY -#define GTIMER_THREAD_WORKAREA_SIZE 1024 +#define GTIMER_THREAD_WORKAREA_SIZE 2048 /////////////////////////////////////////////////////////////////////////// diff --git a/include/gwin/console.h b/include/gwin/console.h index ec108984..252b627e 100644 --- a/include/gwin/console.h +++ b/include/gwin/console.h @@ -32,10 +32,9 @@ typedef struct GConsoleObject { coord_t cx, cy; // Cursor position #if GWIN_CONSOLE_USE_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 + char * buffer; // buffer to store console content + size_t bufsize; // size of buffer + size_t bufpos; // the position of the next char #endif #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM @@ -91,18 +90,19 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p #if GWIN_CONSOLE_USE_HISTORY /** - * @brief Assing a buffer to keep track of the content while the widget is invisible. + * @brief Assign a buffer to keep track of the content while the widget is invisible. * @pre GWIN_CONSOLE_USE_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. + * @param[in] onoff If TRUE a buffer is allocated to maintain console text + * when the console is obscured or invisible. If FALSE, then + * any existing buffer is deallocated. + * @note When the history buffer is turned on, scrolling is implemented using the + * history buffer. * - * @return TRUE on success - */ - bool_t gwinConsoleSetBuffer(GHandle gh, void* buffer, size_t size); + * @return TRUE if the history buffer is now turned on. + */ + bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff); #endif /** diff --git a/include/gwin/options.h b/include/gwin/options.h index e6d2a81e..882db572 100644 --- a/include/gwin/options.h +++ b/include/gwin/options.h @@ -106,16 +106,52 @@ #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. + * @brief Should the content of the console be saved for redrawing. * @details Defaults to FALSE + * @details If this feature is enabled, the contents of the console will be saved + * as it is written. If a redraw is required it will be redrawn from the + * history. Scrolling will also use the history buffer if it is turned on. + * @note Using this option allocates the amount of memory to store the + * history based on the minimum character width in the current font + * at the time the history is turned on. Using a fixed width font is a good + * idea to minimize memory usage. + * @note If you change the size of the window or you change the font being displayed + * you should turn off the history and then turn it back on in order to get + * a new buffer of the correct size for the window/font combination. Strange + * redrawing and scrolling effects can occur if the buffer is too small to + * save a complete screen of data. Note the system tries to optimize storage + * so this may only be evident in very limited situations eg with a console + * with many characters in it. + * @note @p gwinConsoleSetBuffer() can be used to turn the history buffer off and on. */ #ifndef GWIN_CONSOLE_USE_HISTORY #define GWIN_CONSOLE_USE_HISTORY FALSE #endif + /** + * @brief Use font width averaging for the history buffer allocation. + * @details Defaults to FALSE + * @details If this feature is enabled, the width one third of the way between + * the font's character width minimum and maximum will be used instead + * of the font's minimum width. + * @note This option reduces the memory allocation for a variable width font's + * history buffer. Note that strange + * redrawing and scrolling effects can occur if the buffer is too small to + * save a complete screen of data. The system tries to optimize storage + * so this may only be evident in very limited situations eg with a console + * with many characters in it. + */ + #ifndef GWIN_CONSOLE_HISTORY_AVERAGING + #define GWIN_CONSOLE_HISTORY_AVERAGING FALSE + #endif + /** + * @brief Should the history be turned on for all console windows when they are first created. + * @details Defaults to FALSE + * @note @p gwinConsoleSetBuffer() can be used to turn the history buffer off and on at + * any time. + */ + #ifndef GWIN_CONSOLE_HISTORY_ATCREATE + #define GWIN_CONSOLE_HISTORY_ATCREATE 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 56e1abc6..ef374220 100644 --- a/src/gwin/console.c +++ b/src/gwin/console.c @@ -18,13 +18,13 @@ #include "gwin/class_gwin.h" -#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE -#define GWIN_CONSOLE_USE_FILLED_CHARS FALSE +#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE // Clear each line before using it +#define GWIN_CONSOLE_USE_FILLED_CHARS FALSE // Use filled characters instead of drawn characters +#define GWIN_CONSOLE_BUFFER_SCROLLING TRUE // Use the history buffer to scroll when it is available -// some temporary warning -#if GWIN_CONSOLE_USE_HISTORY - #warning "This feature is work in progress and does currently contain a lot of bugs." -#endif +// Our control flags +#define GCONSOLE_FLG_NOSTORE (GWIN_FIRST_CONTROL_FLAG<<0) +#define GCONSOLE_FLG_OVERRUN (GWIN_FIRST_CONTROL_FLAG<<1) /* * Stream interface implementation. The interface is write only @@ -59,43 +59,163 @@ #endif #if GWIN_CONSOLE_USE_HISTORY - static void CustomRedraw(GWindowObject *gh) { + static void HistoryDestroy(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]); + // Deallocate the history buffer if required. + if (gcw->buffer) { + gfxFree(gcw->buffer); + gcw->buffer = 0; } - gcw->store = TRUE; - + #undef gcw } + + static void HistoryRedraw(GWindowObject *gh) { + #define gcw ((GConsoleObject *)gh) + + // No redrawing if there is no history + if (!gcw->buffer) + return; + + // We are printing the buffer - don't store it again + gh->flags |= GCONSOLE_FLG_NOSTORE; + + #if !GWIN_CONSOLE_USE_CLEAR_LINES + // Clear the screen + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + #endif + + // Reset the cursor + gcw->cx = 0; + gcw->cy = 0; + + // Print the buffer + gwinPutCharArray(gh, gcw->buffer, gcw->bufpos); + + #if !GWIN_CONSOLE_USE_CLEAR_LINES + // Clear the remaining space + if (gcw->cy + fy < gh->height) + gdispGFillArea(gh->display, gh->x, gh->y+gcw->cy+fy, gh->width, gh->height-(gcw->cy+fy), gh->bgcolor); + #endif + + // Turn back on storing of buffer contents + gh->flags &= ~GCONSOLE_FLG_NOSTORE; + + #undef gcw + } + + /** + * Put a character into our history buffer + */ + static void putCharInBuffer(GConsoleObject *gcw, char c) { + // Only store if we need to + if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) + return; + + // Do we have enough space in the buffer + if (gcw->bufpos >= gcw->bufsize) { + char * p; + size_t dp; + + /** + * This should never really happen except if the user has changed the window + * size without turning off and then on the buffer. Even then it is unlikely + * because of our conservative allocation strategy. + * If it really is needed we scroll one line to make some space. We also mark + * it is an overrun so that if asked to really scroll later we know we already have. + * Note we only use one bit to indicate an overrun, so an overrun of more + * than one line will lead to some interesting scrolling and refreshing + * effects. + */ + + // Remove one line from the start + for(p = gcw->buffer; *p && *p != '\n'; p++); + + // Was there a newline? + if (*p != '\n') + p = gcw->buffer; // Oops - no newline, just delete one char + else + gcw->g.flags |= GCONSOLE_FLG_OVERRUN; // Mark the overrun + + // Delete the data + dp = ++p - gcw->buffer; // Calculate the amount to to be removed + gcw->bufpos -= dp; // Calculate the new size + if (gcw->bufpos) + memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data + } + + // Save the character + gcw->buffer[gcw->bufpos++] = c; + } + + /** + * Scroll the history buffer by one line + */ + static void scrollBuffer(GConsoleObject *gcw) { + char * p; + size_t dp; + + // Only scroll if we need to + if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) + return; + + // If a buffer overrun has been marked don't scroll as we have already + if ((gcw->g.flags & GCONSOLE_FLG_OVERRUN)) { + gcw->g.flags &= ~GCONSOLE_FLG_OVERRUN; + return; + } + + // Remove one line from the start + for(p = gcw->buffer; *p && *p != '\n'; p++); + + // Was there a newline, if not delete everything. + if (*p != '\n') { + gcw->bufpos = 0; + return; + } + + // Delete the data + dp = ++p - gcw->buffer; // Calculate the amount to to be removed + gcw->bufpos -= dp; // Calculate the new size + if (gcw->bufpos) + memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data + } + + /** + * Clear the history buffer + */ + static void clearBuffer(GConsoleObject *gcw) { + + // Only clear if we need to + if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) + return; + + gcw->bufpos = 0; + } + +#else + #define putCharInBuffer(gcw, c) + #define scrollBuffer(gcw) + #define clearBuffer(gcw) #endif static void AfterClear(GWindowObject *gh) { #define gcw ((GConsoleObject *)gh) gcw->cx = 0; gcw->cy = 0; - - #if GWIN_CONSOLE_USE_HISTORY - // issue an overflow, this is some kind - // of emptying the buffer - gcw->last_char = gcw->size; - #endif - + clearBuffer(gcw); #undef gcw } static const gwinVMT consoleVMT = { "Console", // The classname sizeof(GConsoleObject), // The object size - 0, // The destroy routine #if GWIN_CONSOLE_USE_HISTORY - CustomRedraw, // The redraw routine (custom) + HistoryDestroy, // The destroy routine (custom) + HistoryRedraw, // The redraw routine (custom) #else + 0, // The destroy routine 0, // The redraw routine (default) #endif AfterClear, // The after-clear routine @@ -111,8 +231,9 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p #if GWIN_CONSOLE_USE_HISTORY gc->buffer = 0; - gc->size = 0; - gc->last_char = 0; + #if GWIN_CONSOLE_HISTORY_ATCREATE + gwinConsoleSetBuffer(&gc->g, TRUE); + #endif #endif gc->cx = 0; @@ -133,39 +254,43 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p #endif #if GWIN_CONSOLE_USE_HISTORY - bool_t gwinConsoleSetBuffer(GHandle gh, void* buffer, size_t size) { + bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff) { #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; - + // Do we want the buffer turned off? + if (!onoff) { + if (gcw->buffer) { + gfxFree(gcw->buffer); + gcw->buffer = 0; + } + return FALSE; } - gcw->last_char = 0; + // Is the buffer already on? + if (gcw->buffer) + return TRUE; + // Get the number of characters that fit in the x direction + #if GWIN_CONSOLE_HISTORY_AVERAGING + gcw->bufsize = gh->width / ((2*gdispGetFontMetric(gh->font, fontMinWidth)+gdispGetFontMetric(gh->font, fontMaxWidth))/3); + #else + gcw->bufsize = gh->width / gdispGetFontMetric(gh->font, fontMinWidth); + #endif + gcw->bufsize++; // Allow space for a newline on each line. + + // Multiply by the number of lines + gcw->bufsize *= gh->height / gdispGetFontMetric(gh->font, fontHeight); + + // Allocate the buffer + if (!(gcw->buffer = (char*)gfxAlloc(gcw->bufsize))) + return FALSE; + + // All good! + gh->flags &= ~GCONSOLE_FLG_OVERRUN; + gcw->bufpos = 0; return TRUE; #undef gcw @@ -174,75 +299,106 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p void gwinPutChar(GHandle gh, char c) { #define gcw ((GConsoleObject *)gh) - uint8_t width, fy, fp; + uint8_t width, fy; if (gh->vmt != &consoleVMT || !gh->font) return; - #if GWIN_CONSOLE_USE_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 + // only render new character if the console is visible + if (!gwinGetVisible(gh)) + return; fy = gdispGetFontMetric(gh->font, fontHeight); - fp = gdispGetFontMetric(gh->font, fontCharPadding); #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif - if (c == '\n') { + /** + * 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. + * + * All other characters are treated as printable. + */ + switch (c) { + case '\n': + // clear to the end of the line + #if GWIN_CONSOLE_USE_CLEAR_LINES + if (gcw->cx == 0 && gcw->cy+fy < gh->height) + gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor); + #endif + // update the cursor gcw->cx = 0; gcw->cy += fy; + putCharInBuffer(gcw, '\n'); // We use lazy scrolling here and only scroll when the next char arrives - } else if (c == '\r') { + return; + + case '\r': // gcw->cx = 0; - } else { - width = gdispGetCharWidth(c, gh->font) + fp; - if (gcw->cx + width >= gh->width) { - gcw->cx = 0; - gcw->cy += fy; - } + return; + } - if (gcw->cy + fy > gh->height) { -#if GDISP_NEED_SCROLL - /* scroll the console */ - gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor); - /* reset the cursor to the start of the last line */ - gcw->cx = 0; - gcw->cy = (((coord_t)(gh->height/fy))-1)*fy; -#else - /* clear the console */ - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - /* reset the cursor to the top of the window */ - gcw->cx = 0; - gcw->cy = 0; -#endif - } + // Characters with no width are ignored + if (!(width = gdispGetCharWidth(c, gh->font))) + return; -#if GWIN_CONSOLE_USE_CLEAR_LINES - /* clear to the end of the line */ + // Do we need to go to the next line to fit this character? + if (gcw->cx + width >= gh->width) { + gcw->cx = 0; + gcw->cy += fy; + putCharInBuffer(gcw, '\n'); + } + + // Do we need to scroll to fit this character? + if (gcw->cy + fy > gh->height) { + #if GWIN_CONSOLE_USE_HISTORY && GWIN_CONSOLE_BUFFER_SCROLLING + if (gcw->buffer) { + // Scroll the buffer and then redraw using the buffer + scrollBuffer(gcw); + HistoryRedraw(gh); + } else + #endif + #if GDISP_NEED_SCROLL + { + // Scroll the console using hardware + scrollBuffer(gcw); + gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor); + + // Set the cursor to the start of the last line + gcw->cx = 0; + gcw->cy = (((coord_t)(gh->height/fy))-1)*fy; + } + #else + { + // 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; + } + #endif + } + + // If we are at the beginning of a new line clear the line + #if GWIN_CONSOLE_USE_CLEAR_LINES if (gcw->cx == 0) gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor); -#endif -#if GWIN_CONSOLE_USE_FILLED_CHARS - gdispGFillChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color, gh->bgcolor); -#else - gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color); -#endif + #endif + + // 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); + #else + gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color); + #endif + putCharInBuffer(gcw, c); + + // Update the cursor + gcw->cx += width + gdispGetFontMetric(gh->font, fontCharPadding); - /* update cursor */ - gcw->cx += width; - } #undef gcw }