Merge branch 'gwin' of bitbucket.org:Tectu/ugfx into gwin

This commit is contained in:
Joel Bodenmann 2014-01-18 20:28:33 +01:00
commit d5c52f342f
8 changed files with 482 additions and 125 deletions

View File

@ -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.

View File

@ -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

View File

@ -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
#define GWIN_NEED_WIDGET FALSE
@ -159,7 +161,7 @@
#define GFX_USE_GTIMER FALSE
#define GTIMER_THREAD_PRIORITY HIGH_PRIORITY
#define GTIMER_THREAD_WORKAREA_SIZE 1024
#define GTIMER_THREAD_WORKAREA_SIZE 2048
///////////////////////////////////////////////////////////////////////////

View File

@ -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
/**

View File

@ -45,7 +45,7 @@ typedef struct GWindowObject {
coord_t x, y; // @< Position relative to parent
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

View File

@ -120,16 +120,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

View File

@ -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;

View File

@ -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
}