Merge branch 'master' into gwin
This commit is contained in:
commit
07f940e799
7 changed files with 481 additions and 124 deletions
162
codingstyle.txt
162
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.
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
|
|
||||||
/* The following are optional depending on your hardware */
|
/* 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 */
|
/* GDISP fonts to include */
|
||||||
#define GDISP_INCLUDE_FONT_UI2 TRUE
|
#define GDISP_INCLUDE_FONT_UI2 TRUE
|
||||||
|
|
|
@ -124,7 +124,9 @@
|
||||||
#define GWIN_NEED_WINDOWMANAGER FALSE
|
#define GWIN_NEED_WINDOWMANAGER FALSE
|
||||||
|
|
||||||
#define GWIN_NEED_CONSOLE FALSE
|
#define GWIN_NEED_CONSOLE FALSE
|
||||||
#define GWIN_CONSOLE_USE_HISTORY TRUE
|
#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_BASESTREAM FALSE
|
||||||
#define GWIN_CONSOLE_USE_FLOAT FALSE
|
#define GWIN_CONSOLE_USE_FLOAT FALSE
|
||||||
#define GWIN_NEED_GRAPH FALSE
|
#define GWIN_NEED_GRAPH FALSE
|
||||||
|
@ -159,7 +161,7 @@
|
||||||
#define GFX_USE_GTIMER FALSE
|
#define GFX_USE_GTIMER FALSE
|
||||||
|
|
||||||
#define GTIMER_THREAD_PRIORITY HIGH_PRIORITY
|
#define GTIMER_THREAD_PRIORITY HIGH_PRIORITY
|
||||||
#define GTIMER_THREAD_WORKAREA_SIZE 1024
|
#define GTIMER_THREAD_WORKAREA_SIZE 2048
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -33,9 +33,8 @@ typedef struct GConsoleObject {
|
||||||
|
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
#if GWIN_CONSOLE_USE_HISTORY
|
||||||
char * buffer; // buffer to store console content
|
char * buffer; // buffer to store console content
|
||||||
uint16_t last_char; // the last rendered character
|
size_t bufsize; // size of buffer
|
||||||
size_t size; // size of buffer
|
size_t bufpos; // the position of the next char
|
||||||
bool_t store; // shall PutChar() store into buffer
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
|
#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
|
#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
|
* @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] gh The window handle (must be a console window)
|
||||||
* @param[in] buffer The pointer of the buffer that shall be used. Buffer will be
|
* @param[in] onoff If TRUE a buffer is allocated to maintain console text
|
||||||
* dynamically allocated when this is NULL.
|
* when the console is obscured or invisible. If FALSE, then
|
||||||
* @param[in] size Size of the buffer that has been passed. If buffer is NULL, this
|
* any existing buffer is deallocated.
|
||||||
* will be the size of the dynamically allocated buffer.
|
* @note When the history buffer is turned on, scrolling is implemented using the
|
||||||
|
* history buffer.
|
||||||
*
|
*
|
||||||
* @return TRUE on success
|
* @return TRUE if the history buffer is now turned on.
|
||||||
*/
|
*/
|
||||||
bool_t gwinConsoleSetBuffer(GHandle gh, void* buffer, size_t size);
|
bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -120,16 +120,52 @@
|
||||||
#define GWIN_BUTTON_LAZY_RELEASE FALSE
|
#define GWIN_BUTTON_LAZY_RELEASE FALSE
|
||||||
#endif
|
#endif
|
||||||
/**
|
/**
|
||||||
* @brief Should the content of the console be logged or not
|
* @brief Should the content of the console be saved for redrawing.
|
||||||
* @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
|
* @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
|
#ifndef GWIN_CONSOLE_USE_HISTORY
|
||||||
#define GWIN_CONSOLE_USE_HISTORY FALSE
|
#define GWIN_CONSOLE_USE_HISTORY FALSE
|
||||||
#endif
|
#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
|
* @brief Console Windows need floating point support in @p gwinPrintf
|
||||||
* @details Defaults to FALSE
|
* @details Defaults to FALSE
|
||||||
|
|
|
@ -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 */
|
/* 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 */
|
/* Cycle through the lines */
|
||||||
for(;cy;cy--, y++) {
|
for(;cy;cy--, y++) {
|
||||||
|
@ -119,7 +119,7 @@ gdispImageError gdispImageGDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x,
|
||||||
// Read the data
|
// Read the data
|
||||||
len = img->io.fns->read(&img->io,
|
len = img->io.fns->read(&img->io,
|
||||||
img->priv->buf,
|
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);
|
/ sizeof(pixel_t);
|
||||||
if (!len)
|
if (!len)
|
||||||
return GDISP_IMAGE_ERR_BADDATA;
|
return GDISP_IMAGE_ERR_BADDATA;
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
|
|
||||||
#include "gwin/class_gwin.h"
|
#include "gwin/class_gwin.h"
|
||||||
|
|
||||||
#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE
|
#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE // Clear each line before using it
|
||||||
#define GWIN_CONSOLE_USE_FILLED_CHARS FALSE
|
#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
|
// Our control flags
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
#define GCONSOLE_FLG_NOSTORE (GWIN_FIRST_CONTROL_FLAG<<0)
|
||||||
#warning "This feature is work in progress and does currently contain a lot of bugs."
|
#define GCONSOLE_FLG_OVERRUN (GWIN_FIRST_CONTROL_FLAG<<1)
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stream interface implementation. The interface is write only
|
* Stream interface implementation. The interface is write only
|
||||||
|
@ -59,43 +59,163 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
#if GWIN_CONSOLE_USE_HISTORY
|
||||||
static void CustomRedraw(GWindowObject *gh) {
|
static void HistoryDestroy(GWindowObject *gh) {
|
||||||
#define gcw ((GConsoleObject *)gh)
|
#define gcw ((GConsoleObject *)gh)
|
||||||
|
|
||||||
uint16_t i;
|
// Deallocate the history buffer if required.
|
||||||
|
if (gcw->buffer) {
|
||||||
// loop through buffer and don't add it again
|
gfxFree(gcw->buffer);
|
||||||
gcw->store = FALSE;
|
gcw->buffer = 0;
|
||||||
for (i = 0; i < gcw->last_char; i++) {
|
|
||||||
gwinPutChar(gh, gcw->buffer[i]);
|
|
||||||
}
|
}
|
||||||
gcw->store = TRUE;
|
|
||||||
|
|
||||||
#undef gcw
|
#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
|
#endif
|
||||||
|
|
||||||
static void AfterClear(GWindowObject *gh) {
|
static void AfterClear(GWindowObject *gh) {
|
||||||
#define gcw ((GConsoleObject *)gh)
|
#define gcw ((GConsoleObject *)gh)
|
||||||
gcw->cx = 0;
|
gcw->cx = 0;
|
||||||
gcw->cy = 0;
|
gcw->cy = 0;
|
||||||
|
clearBuffer(gcw);
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
|
||||||
// issue an overflow, this is some kind
|
|
||||||
// of emptying the buffer
|
|
||||||
gcw->last_char = gcw->size;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef gcw
|
#undef gcw
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gwinVMT consoleVMT = {
|
static const gwinVMT consoleVMT = {
|
||||||
"Console", // The classname
|
"Console", // The classname
|
||||||
sizeof(GConsoleObject), // The object size
|
sizeof(GConsoleObject), // The object size
|
||||||
0, // The destroy routine
|
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
#if GWIN_CONSOLE_USE_HISTORY
|
||||||
CustomRedraw, // The redraw routine (custom)
|
HistoryDestroy, // The destroy routine (custom)
|
||||||
|
HistoryRedraw, // The redraw routine (custom)
|
||||||
#else
|
#else
|
||||||
|
0, // The destroy routine
|
||||||
0, // The redraw routine (default)
|
0, // The redraw routine (default)
|
||||||
#endif
|
#endif
|
||||||
AfterClear, // The after-clear routine
|
AfterClear, // The after-clear routine
|
||||||
|
@ -111,8 +231,9 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p
|
||||||
|
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
#if GWIN_CONSOLE_USE_HISTORY
|
||||||
gc->buffer = 0;
|
gc->buffer = 0;
|
||||||
gc->size = 0;
|
#if GWIN_CONSOLE_HISTORY_ATCREATE
|
||||||
gc->last_char = 0;
|
gwinConsoleSetBuffer(&gc->g, TRUE);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gc->cx = 0;
|
gc->cx = 0;
|
||||||
|
@ -133,39 +254,43 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
#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)
|
#define gcw ((GConsoleObject *)gh)
|
||||||
|
|
||||||
uint8_t buf_width, buf_height, fp, fy;
|
|
||||||
|
|
||||||
if (gh->vmt != &consoleVMT)
|
if (gh->vmt != &consoleVMT)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
// assign buffer or allocate new one
|
// Do we want the buffer turned off?
|
||||||
if (buffer == 0) {
|
if (!onoff) {
|
||||||
(void)size;
|
if (gcw->buffer) {
|
||||||
|
gfxFree(gcw->buffer);
|
||||||
// calculate buffer size
|
gcw->buffer = 0;
|
||||||
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;
|
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;
|
// 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;
|
return TRUE;
|
||||||
|
|
||||||
#undef gcw
|
#undef gcw
|
||||||
|
@ -174,75 +299,106 @@ GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *p
|
||||||
|
|
||||||
void gwinPutChar(GHandle gh, char c) {
|
void gwinPutChar(GHandle gh, char c) {
|
||||||
#define gcw ((GConsoleObject *)gh)
|
#define gcw ((GConsoleObject *)gh)
|
||||||
uint8_t width, fy, fp;
|
uint8_t width, fy;
|
||||||
|
|
||||||
if (gh->vmt != &consoleVMT || !gh->font)
|
if (gh->vmt != &consoleVMT || !gh->font)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if GWIN_CONSOLE_USE_HISTORY
|
// only render new character if the console is visible
|
||||||
// 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))
|
if (!gwinGetVisible(gh))
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
fy = gdispGetFontMetric(gh->font, fontHeight);
|
fy = gdispGetFontMetric(gh->font, fontHeight);
|
||||||
fp = gdispGetFontMetric(gh->font, fontCharPadding);
|
|
||||||
|
|
||||||
#if GDISP_NEED_CLIP
|
#if GDISP_NEED_CLIP
|
||||||
gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
|
gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
|
||||||
#endif
|
#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->cx = 0;
|
||||||
gcw->cy += fy;
|
gcw->cy += fy;
|
||||||
|
putCharInBuffer(gcw, '\n');
|
||||||
// We use lazy scrolling here and only scroll when the next char arrives
|
// We use lazy scrolling here and only scroll when the next char arrives
|
||||||
} else if (c == '\r') {
|
return;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
// gcw->cx = 0;
|
// gcw->cx = 0;
|
||||||
} else {
|
return;
|
||||||
width = gdispGetCharWidth(c, gh->font) + fp;
|
}
|
||||||
|
|
||||||
|
// Characters with no width are ignored
|
||||||
|
if (!(width = gdispGetCharWidth(c, gh->font)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Do we need to go to the next line to fit this character?
|
||||||
if (gcw->cx + width >= gh->width) {
|
if (gcw->cx + width >= gh->width) {
|
||||||
gcw->cx = 0;
|
gcw->cx = 0;
|
||||||
gcw->cy += fy;
|
gcw->cy += fy;
|
||||||
|
putCharInBuffer(gcw, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do we need to scroll to fit this character?
|
||||||
if (gcw->cy + fy > gh->height) {
|
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
|
#if GDISP_NEED_SCROLL
|
||||||
/* scroll the console */
|
{
|
||||||
|
// Scroll the console using hardware
|
||||||
|
scrollBuffer(gcw);
|
||||||
gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor);
|
gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor);
|
||||||
/* reset the cursor to the start of the last line */
|
|
||||||
|
// Set the cursor to the start of the last line
|
||||||
gcw->cx = 0;
|
gcw->cx = 0;
|
||||||
gcw->cy = (((coord_t)(gh->height/fy))-1)*fy;
|
gcw->cy = (((coord_t)(gh->height/fy))-1)*fy;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
/* clear the console */
|
{
|
||||||
|
// Clear the console and reset the cursor
|
||||||
|
clearBuffer(gcw);
|
||||||
gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
|
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->cx = 0;
|
||||||
gcw->cy = 0;
|
gcw->cy = 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are at the beginning of a new line clear the line
|
||||||
#if GWIN_CONSOLE_USE_CLEAR_LINES
|
#if GWIN_CONSOLE_USE_CLEAR_LINES
|
||||||
/* clear to the end of the line */
|
|
||||||
if (gcw->cx == 0)
|
if (gcw->cx == 0)
|
||||||
gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor);
|
gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Draw the character
|
||||||
#if GWIN_CONSOLE_USE_FILLED_CHARS
|
#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, gh->color, gh->bgcolor);
|
||||||
#else
|
#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, gh->color);
|
||||||
#endif
|
#endif
|
||||||
|
putCharInBuffer(gcw, c);
|
||||||
|
|
||||||
|
// Update the cursor
|
||||||
|
gcw->cx += width + gdispGetFontMetric(gh->font, fontCharPadding);
|
||||||
|
|
||||||
/* update cursor */
|
|
||||||
gcw->cx += width;
|
|
||||||
}
|
|
||||||
#undef gcw
|
#undef gcw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue