From d3b4c499ab0267dbcad3d5972bb0317fdba9043e Mon Sep 17 00:00:00 2001 From: Andrew Hannam Date: Tue, 30 Oct 2012 18:18:29 +1000 Subject: GDISP fixes and new routines. Many GWIN changes. GDISP: Fix gdisp???Arc to use (possibly) hardware accelerated routines. Fix Arc orientation so 0 degrees is on x axis and 90 degrees points to the top of screen (instead of the bottom). Add rounded box routines (if ARC support is turned on). Add a gdispDrawStringBox to match the gdispFillStringBox routine. Repair prototypes in wrong place in gdisp.h GWIN: Extract the concept of a Window Handle to allow many new features. Allow dynamic creation of window objects as well as static initialisation. Seperate the console code into a console specific window type. Add buttons as a specific window type. The drawing code is complete, the input (touch or mouse) is still to be implemented. --- src/gdisp.c | 444 +++++++++++++++++++---------------- src/gwin.c | 752 +++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 752 insertions(+), 444 deletions(-) (limited to 'src') diff --git a/src/gdisp.c b/src/gdisp.c index 82130b7c..5a3aff13 100644 --- a/src/gdisp.c +++ b/src/gdisp.c @@ -494,220 +494,124 @@ } #endif -#if GDISP_NEED_ARC || defined(__DOXYGEN__) - -#include "math.h" +#if (GDISP_NEED_ARC && GDISP_NEED_MULTITHREAD) || defined(__DOXYGEN__) + /* + * @brief Draw an arc. + * @pre The GDISP must be in powerOn or powerSleep mode. + * + * @param[in] x0,y0 The center point + * @param[in] radius The radius of the arc + * @param[in] start The start angle (0 to 360) + * @param[in] end The end angle (0 to 360) + * @param[in] color The color of the arc + * + * @api + */ + void gdispDrawArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) { + chMtxLock(&gdispMutex); + GDISP_LLD(drawarc)(x, y, radius, start, end, color); + chMtxUnlock(); + } +#elif GDISP_NEED_ARC && GDISP_NEED_ASYNC + void gdispDrawArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) { + gdisp_lld_msg_t *p = gdispAllocMsg(GDISP_LLD_MSG_DRAWARC); + p->drawarc.x = x; + p->drawarc.y = y; + p->drawarc.radius = radius; + p->drawarc.start = start; + p->drawarc.end = end; + p->drawarc.color = color; + chMBPost(&gdispMailbox, (msg_t)p, TIME_INFINITE); + } +#endif -/* - * @brief Internal helper function for gdispDrawArc() - * - * @note DO NOT USE DIRECTLY! - * - * @param[in] x, y The middle point of the arc - * @param[in] start The start angle of the arc - * @param[in] end The end angle of the arc - * @param[in] radius The radius of the arc - * @param[in] color The color in which the arc will be drawn - * - * @notapi - */ -void _draw_arc(coord_t x, coord_t y, uint16_t start, uint16_t end, uint16_t radius, color_t color) { - if(start > 0 && start <= 180) { - float x_maxI = x + radius*cos(start*M_PI/180); - float x_minI; - - if (end > 180) - x_minI = x - radius; - else - x_minI = x + radius*cos(end*M_PI/180); - - int a = 0; - int b = radius; - int P = 1 - radius; - - do { - if(x-a <= x_maxI && x-a >= x_minI) - gdispDrawPixel(x-a, y+b, color); - if(x+a <= x_maxI && x+a >= x_minI) - gdispDrawPixel(x+a, y+b, color); - if(x-b <= x_maxI && x-b >= x_minI) - gdispDrawPixel(x-b, y+a, color); - if(x+b <= x_maxI && x+b >= x_minI) - gdispDrawPixel(x+b, y+a, color); - - if (P < 0) { - P = P + 3 + 2*a; - a = a + 1; - } else { - P = P + 5 + 2*(a - b); - a = a + 1; - b = b - 1; - } - } while(a <= b); - } - - if (end > 180 && end <= 360) { - float x_maxII = x+radius*cos(end*M_PI/180); - float x_minII; - - if(start <= 180) - x_minII = x - radius; - else - x_minII = x+radius*cos(start*M_PI/180); - - int a = 0; - int b = radius; - int P = 1 - radius; - - do { - if(x-a <= x_maxII && x-a >= x_minII) - gdispDrawPixel(x-a, y-b, color); - if(x+a <= x_maxII && x+a >= x_minII) - gdispDrawPixel(x+a, y-b, color); - if(x-b <= x_maxII && x-b >= x_minII) - gdispDrawPixel(x-b, y-a, color); - if(x+b <= x_maxII && x+b >= x_minII) - gdispDrawPixel(x+b, y-a, color); - - if (P < 0) { - P = P + 3 + 2*a; - a = a + 1; - } else { - P = P + 5 + 2*(a - b); - a = a + 1; - b = b - 1; - } - } while (a <= b); - } -} +#if (GDISP_NEED_ARC && GDISP_NEED_MULTITHREAD) || defined(__DOXYGEN__) + /* + * @brief Draw a filled arc. + * @pre The GDISP must be in powerOn or powerSleep mode. + * @note Not very efficient currently - does lots of overdrawing + * + * @param[in] x0,y0 The center point + * @param[in] radius The radius of the arc + * @param[in] start The start angle (0 to 360) + * @param[in] end The end angle (0 to 360) + * @param[in] color The color of the arc + * + * @api + */ + void gdispFillArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) { + chMtxLock(&gdispMutex); + GDISP_LLD(fillarc)(x, y, radius, start, end, color); + chMtxUnlock(); + } +#elif GDISP_NEED_ARC && GDISP_NEED_ASYNC + void gdispFillArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) { + gdisp_lld_msg_t *p = gdispAllocMsg(GDISP_LLD_MSG_FILLARC); + p->fillarc.x = x; + p->fillarc.y = y; + p->fillarc.radius = radius; + p->fillarc.start = start; + p->fillarc.end = end; + p->fillarc.color = color; + chMBPost(&gdispMailbox, (msg_t)p, TIME_INFINITE); + } +#endif -/* - * @brief Draw an arc. - * @pre The GDISP must be in powerOn or powerSleep mode. +#if GDISP_NEED_ARC || defined(__DOXYGEN__) +/** + * @brief Draw a rectangular box with rounded corners + * @pre The GDISP unit must be in powerOn or powerSleep mode. * - * @param[in] x0,y0 The center point - * @param[in] radius The radius of the arc - * @param[in] start The start angle (0 to 360) - * @param[in] end The end angle (0 to 360) - * @param[in] color The color of the arc + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] radius The radius of the rounded corners + * @param[in] color The color to use * * @api */ -void gdispDrawArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) { - if(end < start) { - _draw_arc(x, y, start, 360, radius, color); - _draw_arc(x, y, 0, end, radius, color); - } else { - _draw_arc(x, y, start, end, radius, color); +void gdispDrawRoundedBox(coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + if (2*radius > cx || 2*radius > cy) { + gdispDrawBox(x, y, cx, cy, color); + return; } + gdispDrawArc(x+radius, y+radius, radius, 90, 180, color); + gdispDrawLine(x+radius+1, y, x+cx-2-radius, y, color); + gdispDrawArc(x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispDrawLine(x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); + gdispDrawArc(x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispDrawLine(x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); + gdispDrawArc(x+radius, y+cy-1-radius, radius, 180, 270, color); + gdispDrawLine(x, y+radius+1, x, y+cy-2-radius, color); } #endif #if GDISP_NEED_ARC || defined(__DOXYGEN__) -/* - * @brief Internal helper function for gdispFillArc() - * - * @note DO NOT USE DIRECTLY! - * @note Not very efficient currently - does lots of overdrawing - * - * @param[in] x, y The middle point of the arc - * @param[in] start The start angle of the arc - * @param[in] end The end angle of the arc - * @param[in] radius The radius of the arc - * @param[in] color The color in which the arc will be drawn - * - * @notapi - */ -void _fill_arc(coord_t x, coord_t y, uint16_t start, uint16_t end, uint16_t radius, color_t color) { - if(start > 0 && start <= 180) { - float x_maxI = x + radius*cos(start*M_PI/180); - float x_minI; - - if (end > 180) - x_minI = x - radius; - else - x_minI = x + radius*cos(end*M_PI/180); - - int a = 0; - int b = radius; - int P = 1 - radius; - - do { - if(x-a <= x_maxI && x-a >= x_minI) - gdispDrawLine(x, y, x-a, y+b, color); - if(x+a <= x_maxI && x+a >= x_minI) - gdispDrawLine(x, y, x+a, y+b, color); - if(x-b <= x_maxI && x-b >= x_minI) - gdispDrawLine(x, y, x-b, y+a, color); - if(x+b <= x_maxI && x+b >= x_minI) - gdispDrawLine(x, y, x+b, y+a, color); - - if (P < 0) { - P = P + 3 + 2*a; - a = a + 1; - } else { - P = P + 5 + 2*(a - b); - a = a + 1; - b = b - 1; - } - } while(a <= b); - } - - if (end > 180 && end <= 360) { - float x_maxII = x+radius*cos(end*M_PI/180); - float x_minII; - - if(start <= 180) - x_minII = x - radius; - else - x_minII = x+radius*cos(start*M_PI/180); - - int a = 0; - int b = radius; - int P = 1 - radius; - - do { - if(x-a <= x_maxII && x-a >= x_minII) - gdispDrawLine(x, y, x-a, y-b, color); - if(x+a <= x_maxII && x+a >= x_minII) - gdispDrawLine(x, y, x+a, y-b, color); - if(x-b <= x_maxII && x-b >= x_minII) - gdispDrawLine(x, y, x-b, y-a, color); - if(x+b <= x_maxII && x+b >= x_minII) - gdispDrawLine(x, y, x+b, y-a, color); - - if (P < 0) { - P = P + 3 + 2*a; - a = a + 1; - } else { - P = P + 5 + 2*(a - b); - a = a + 1; - b = b - 1; - } - } while (a <= b); - } -} - -/* - * @brief Draw a filled arc. - * @pre The GDISP must be in powerOn or powerSleep mode. - * @note Not very efficient currently - does lots of overdrawing +/** + * @brief Draw a filled rectangular box with rounded corners + * @pre The GDISP unit must be in powerOn or powerSleep mode. * - * @param[in] x0,y0 The center point - * @param[in] radius The radius of the arc - * @param[in] start The start angle (0 to 360) - * @param[in] end The end angle (0 to 360) - * @param[in] color The color of the arc + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] radius The radius of the rounded corners + * @param[in] color The color to use * * @api */ -void gdispFillArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) { - if(end < start) { - _fill_arc(x, y, start, 360, radius, color); - _fill_arc(x, y, 0, end, radius, color); - } else { - _fill_arc(x, y, start, end, radius, color); +void gdispFillRoundedBox(coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + coord_t radius2; + + radius2 = radius*2; + if (radius2 > cx || radius2 > cy) { + gdispFillArea(x, y, cx, cy, color); + return; } + gdispFillArc(x+radius, y+radius, radius, 90, 180, color); + gdispFillArea(x+radius+1, y, cx-radius2, radius, color); + gdispFillArc(x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispFillArc(x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispFillArea(x+radius+1, y+cy-radius, cx-radius2, radius, color); + gdispFillArc(x+radius, y+cy-1-radius, radius, 180, 270, color); + gdispFillArea(x, y+radius, cx, cy-radius2, color); } #endif @@ -877,10 +781,9 @@ void gdispFillArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t * @brief Draw a rectangular box. * @pre The GDISP unit must be in powerOn or powerSleep mode. * - * @param[in] x0,y0 The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * @param[in] color The color to use - * @param[in] filled Should the box should be filled + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] color The color to use * * @api */ @@ -928,6 +831,8 @@ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { char c; int first; + if (!str) return; + first = 1; p = font->charPadding * font->xscale; while(*str) { @@ -969,6 +874,8 @@ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { char c; int first; + if (!str) return; + first = 1; h = font->height * font->yscale; p = font->charPadding * font->xscale; @@ -998,6 +905,137 @@ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { /** * @brief Draw a text string verticly centered within the specified box. * @pre The GDISP unit must be in powerOn or powerSleep mode. + * + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] str The string to draw + * @param[in] color The color to use + * @param[in] justify Justify the text left, center or right within the box + * + * @api + */ + void gdispDrawStringBox(coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { + /* No mutex required as we only call high level functions which have their own mutex */ + coord_t w, h, p, ypos, xpos; + char c; + int first; + const char *rstr; + + if (!str) str = ""; + + h = font->height * font->yscale; + p = font->charPadding * font->xscale; + + /* Oops - font too large for the area */ + if (h > cy) return; + + /* See if we need to fill above the font */ + ypos = (cy - h + 1)/2; + if (ypos > 0) { + y += ypos; + cy -= ypos; + } + + /* See if we need to fill below the font */ + ypos = cy - h; + if (ypos > 0) + cy -= ypos; + + /* get the start of the printable string and the xpos */ + switch(justify) { + case justifyCenter: + /* Get the length of the entire string */ + w = gdispGetStringWidth(str, font); + if (w <= cx) + xpos = x + (cx - w)/2; + else { + /* Calculate how much of the string we need to get rid of */ + ypos = (w - cx)/2; + xpos = 0; + first = 1; + while(*str) { + /* Get the next printable character */ + c = *str++; + w = _getCharWidth(font, c) * font->xscale; + if (!w) continue; + + /* Handle inter-character padding */ + if (p) { + if (!first) { + xpos += p; + if (xpos > ypos) break; + } else + first = 0; + } + + /* Print the character */ + xpos += w; + if (xpos > ypos) break; + } + xpos = ypos - xpos + x; + } + break; + case justifyRight: + /* Find the end of the string */ + for(rstr = str; *str; str++); + xpos = x+cx - 2; + first = 1; + for(str--; str >= rstr; str--) { + /* Get the next printable character */ + c = *str; + w = _getCharWidth(font, c) * font->xscale; + if (!w) continue; + + /* Handle inter-character padding */ + if (p) { + if (!first) { + if (xpos - p < x) break; + xpos -= p; + } else + first = 0; + } + + /* Print the character */ + if (xpos - w < x) break; + xpos -= w; + } + str++; + break; + case justifyLeft: + /* Fall through */ + default: + xpos = x+1; + break; + } + + /* Print characters until we run out of room */ + first = 1; + while(*str) { + /* Get the next printable character */ + c = *str++; + w = _getCharWidth(font, c) * font->xscale; + if (!w) continue; + + /* Handle inter-character padding */ + if (p) { + if (!first) { + if (xpos + p > x+cx) break; + xpos += p; + } else + first = 0; + } + + /* Print the character */ + if (xpos + w > x+cx) break; + gdispDrawChar(xpos, y, c, font, color); + xpos += w; + } + } +#endif + +#if GDISP_NEED_TEXT || defined(__DOXYGEN__) + /** + * @brief Draw a text string verticly centered within the specified box. The box background is filled with the specified background color. + * @pre The GDISP unit must be in powerOn or powerSleep mode. * @note The entire box is filled * * @param[in] x,y The position for the text (need to define top-right or base-line - check code) @@ -1015,6 +1053,8 @@ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { int first; const char *rstr; + if (!str) str = ""; + h = font->height * font->yscale; p = font->charPadding * font->xscale; diff --git a/src/gwin.c b/src/gwin.c index 1319af1f..1c987204 100644 --- a/src/gwin.c +++ b/src/gwin.c @@ -34,75 +34,34 @@ #if GFX_USE_GWIN || defined(__DOXYGEN__) -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ +#include -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ +#define GWIN_FLG_DYNAMIC 0x0001 +#define GWIN_FIRST_CONTROL_FLAG 0x0002 +#define GBTN_FLG_ALLOCTXT (GWIN_FIRST_CONTROL_FLAG<<0) -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -#if GDISP_NEED_TEXT -/* - * Stream interface implementation. The interface is write only - */ -static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray((GWindow *)ip, (const char *)bp, n); return RDY_OK; } -static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; } -static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar((GWindow *)ip, (char)b); return RDY_OK; } -static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; } -static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar((GWindow *)ip, (char)b); return RDY_OK; } -static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; } -static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray((GWindow *)ip, (const char *)bp, n); return RDY_OK; } -static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; } - -static const struct GWindowVMT vmt = { - GWinStreamWrite, - GWinStreamRead, - GWinStreamPut, - GWinStreamGet, - GWinStreamPutTimed, - GWinStreamGetTimed, - GWinStreamWriteTimed, - GWinStreamReadTimed -}; -#endif - -/*===========================================================================*/ -/* High Level Driver Routines. */ -/*===========================================================================*/ - -/** - * @brief Initialise a window. - * @return FALSE if there is no resultant drawing area, otherwise TRUE. - * - * @param[in] gw The window to initialise - * @param[in] x,y The screen co-ordinates for the bottom left corner of the window - * @param[in] width The width of the window - * @param[in] height The height of the window - * @note The drawing color gets set to White and the background drawing color to Black. - * @note No default font is set so make sure to set one before drawing any text. - * @note The dimensions and position may be changed to fit on the real screen. - * - * @api - */ -bool_t gwinInit(GWindow *gw, coord_t x, coord_t y, coord_t width, coord_t height) { +// Initialise a window creating it dynamicly if required. +static GHandle gwinInit(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height, size_t size) { coord_t w, h; + // Check the window size against the screen size w = gdispGetWidth(); h = gdispGetHeight(); if (x < 0) { width += x; x = 0; } if (y < 0) { height += y; y = 0; } - if (x >= w || y >= h) return FALSE; + if (x >= w || y >= h) return 0; if (x+width > w) width = w - x; if (y+height > h) height = h - y; + + // Allocate the structure if necessary + if (!gw) { + if (!(gw = (GWindowObject *)malloc(size))) + return 0; + gw->flags = GWIN_FLG_DYNAMIC; + } else + gw->flags = 0; + + // Initialise all basic fields (except the type) gw->x = x; gw->y = y; gw->width = width; @@ -110,231 +69,278 @@ bool_t gwinInit(GWindow *gw, coord_t x, coord_t y, coord_t width, coord_t height gw->color = White; gw->bgcolor = Black; #if GDISP_NEED_TEXT - gw->txt.font = 0; - gw->txt.fy = 0; - gw->txt.fp = 0; - gw->txt.cx = 0; - gw->txt.cy = 0; - gw->txt.vmt = &vmt; + gw->font = 0; +#endif + return (GHandle)gw; +} + +/** + * @brief Create a basic window. + * @return NULL if there is no resultant drawing area, otherwise a window handle. + * + * @param[in] gw The window structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] x,y The screen co-ordinates for the bottom left corner of the window + * @param[in] width The width of the window + * @param[in] height The height of the window + * @note The default drawing color gets set to White and the background drawing color to Black. + * @note No default font is set so make sure to set one before drawing any text. + * @note The dimensions and position may be changed to fit on the real screen. + * @note The window is not automatically cleared on creation. You must do that by calling gwinClear() (possibly after changing your background color) + * + * @api + */ +GHandle gwinCreateWindow(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height) { + if (!(gw = (GWindowObject *)gwinInit((GWindowObject *)gw, x, y, width, height, sizeof(GWindowObject)))) + return 0; + gw->type = GW_WINDOW; + return (GHandle)gw; +} + +/** + * @brief Destroy a window (of any type). Releases any dynamicly allocated memory. + * + * @param[in] gh The window handle + * + * @api + */ +void gwinDestroyWindow(GHandle gh) { + // Clean up any type specific dynamic memory allocations + switch(gh->type) { +#if GWIN_NEED_BUTTON + case GW_BUTTON: + if ((gh->flags & GBTN_FLG_ALLOCTXT)) { + gh->flags &= ~GBTN_FLG_ALLOCTXT; // To be sure, to be sure + free((char *)((GButtonObject *)gh)->txt); + } + break; #endif - return TRUE; + default: + break; + } + + // Clean up the structure + if (gh->flags & GWIN_FLG_DYNAMIC) { + gh->flags = 0; // To be sure, to be sure + free(gh); + } } #if GDISP_NEED_TEXT /** * @brief Set the current font for this window. - * @pre The window must be initialised. * - * @param[in] gw The window - * @param[in] font The font to use for text functions + * @param[in] gh The window handle + * @param[in] font The font to use for text functions * * @api */ -void gwinSetFont(GWindow *gw, font_t font) { - gw->txt.font = font; - gw->txt.fy = gdispGetFontMetric(font, fontHeight); - gw->txt.fp = gdispGetFontMetric(font, fontCharPadding); +void gwinSetFont(GHandle gh, font_t font) { + gh->font = font; +#if GWIN_NEED_CONSOLE + if (font && gh->type == GW_CONSOLE) { + ((GConsoleObject *)gh)->fy = gdispGetFontMetric(font, fontHeight); + ((GConsoleObject *)gh)->fp = gdispGetFontMetric(font, fontCharPadding); + } +#endif } #endif /** * @brief Clear the window - * @pre The window must be initialised. * @note Uses the current background color to clear the window * - * @param[in] gw The window + * @param[in] gh The window handle * * @api */ -void gwinClear(GWindow *gw) { - gdispFillArea(gw->x, gw->y, gw->width, gw->height, gw->bgcolor); +void gwinClear(GHandle gh) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispFillArea(gh->x, gh->y, gh->width, gh->height, gh->bgcolor); } /** * @brief Set a pixel in the window - * @pre The window must be initialised. * @note Uses the current foreground color to set the pixel * @note May leave GDISP clipping to this window's dimensions * - * @param[in] gw The window + * @param[in] gh The window handle * * @api */ -void gwinDrawPixel(GWindow *gw, coord_t x, coord_t y) { +void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawPixel(gw->x+x, gw->y+y, gw->color); + gdispDrawPixel(gh->x+x, gh->y+y, gh->color); } /** * @brief Draw a line in the window - * @pre The window must be initialised. * @note Uses the current foreground color to draw the line * @note May leave GDISP clipping to this window's dimensions * - * @param[in] gw The window + * @param[in] gh The window handle * @param[in] x0,y0 The start position * @param[in] x1,y1 The end position * * @api */ -void gwinDrawLine(GWindow *gw, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { +void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawLine(gw->x+x0, gw->y+y0, gw->x+x1, gw->y+y1, gw->color); + gdispDrawLine(gh->x+x0, gh->y+y0, gh->x+x1, gh->y+y1, gh->color); } /** * @brief Draw a box in the window - * @pre The window must be initialised. * @note Uses the current foreground color to draw the box * @note May leave GDISP clipping to this window's dimensions * - * @param[in] gw The window - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] gh The window handle + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) * * @api */ -void gwinDrawBox(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy) { +void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawBox(gw->x+x, gw->y+y, cx, cy, gw->color); + gdispDrawBox(gh->x+x, gh->y+y, cx, cy, gh->color); } /** * @brief Fill an rectangular area in the window - * @pre The window must be initialised. * @note Uses the current foreground color to fill the box * @note May leave GDISP clipping to this window's dimensions * - * @param[in] gw The window - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] gh The window handle + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) * * @api */ -void gwinFillArea(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy) { +void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillArea(gw->x+x, gw->y+y, cx, cy, gw->color); + gdispFillArea(gh->x+x, gh->y+y, cx, cy, gh->color); } /** * @brief Fill an area in the window using the supplied bitmap. * @details The bitmap is in the pixel format specified by the low level driver - * @pre The window must be initialised. * @note If GDISP_NEED_ASYNC is defined then the buffer must be static * or at least retained until this call has finished the blit. You can * tell when all graphics drawing is finished by @p gdispIsBusy() going FALSE. * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x, y The start filled area - * @param[in] cx, cy The width and height to be filled - * @param[in] srcx, srcy The bitmap position to start the fill from - * @param[in] srccx The width of a line in the bitmap. - * @param[in] buffer The pixels to use to fill the area. + * @param[in] gh The window handle + * @param[in] x, y The start filled area + * @param[in] cx, cy The width and height to be filled + * @param[in] srcx, srcy The bitmap position to start the fill from + * @param[in] srccx The width of a line in the bitmap. + * @param[in] buffer The pixels to use to fill the area. * * @api */ -void gwinBlitArea(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { +void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispBlitAreaEx(gw->x+x, gw->y+y, cx, cy, srcx, srcy, srccx, buffer); + gdispBlitAreaEx(gh->x+x, gh->y+y, cx, cy, srcx, srcy, srccx, buffer); } #if GDISP_NEED_CIRCLE /** * @brief Draw a circle in the window. - * @pre The window must be initialised. * @note Uses the current foreground color to draw the circle * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x, y The center of the circle - * @param[in] radius The radius of the circle + * @param[in] gh The window handle + * @param[in] x, y The center of the circle + * @param[in] radius The radius of the circle * * @api */ -void gwinDrawCircle(GWindow *gw, coord_t x, coord_t y, coord_t radius) { +void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawCircle(gw->x+x, gw->y+y, radius, gw->color); + gdispDrawCircle(gh->x+x, gh->y+y, radius, gh->color); } #endif #if GDISP_NEED_CIRCLE /** * @brief Draw a filled circle in the window. - * @pre The window must be initialised. * @note Uses the current foreground color to draw the filled circle * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x, y The center of the circle - * @param[in] radius The radius of the circle + * @param[in] gh The window handle + * @param[in] x, y The center of the circle + * @param[in] radius The radius of the circle * * @api */ -void gwinFillCircle(GWindow *gw, coord_t x, coord_t y, coord_t radius) { +void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillCircle(gw->x+x, gw->y+y, radius, gw->color); + gdispFillCircle(gh->x+x, gh->y+y, radius, gh->color); } #endif #if GDISP_NEED_ELLIPSE /** * @brief Draw an ellipse. - * @pre The window must be initialised. * @note Uses the current foreground color to draw the ellipse * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The center of the ellipse - * @param[in] a,b The dimensions of the ellipse + * @param[in] gh The window handle + * @param[in] x,y The center of the ellipse + * @param[in] a,b The dimensions of the ellipse * * @api */ -void gwinDrawEllipse(GWindow *gw, coord_t x, coord_t y, coord_t a, coord_t b) { +void gwinDrawEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawEllipse(gw->x+x, gw->y+y, a, b, gw->color); + gdispDrawEllipse(gh->x+x, gh->y+y, a, b, gh->color); } #endif #if GDISP_NEED_ELLIPSE /** * @brief Draw an filled ellipse. - * @pre The window must be initialised. * @note Uses the current foreground color to draw the filled ellipse * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The center of the ellipse - * @param[in] a,b The dimensions of the ellipse + * @param[in] gh The window handle + * @param[in] x,y The center of the ellipse + * @param[in] a,b The dimensions of the ellipse * * @api */ -void gwinFillEllipse(GWindow *gw, coord_t x, coord_t y, coord_t a, coord_t b) { +void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillEllipse(gw->x+x, gw->y+y, a, b, gw->color); + gdispFillEllipse(gh->x+x, gh->y+y, a, b, gh->color); } #endif #if GDISP_NEED_ARC /* * @brief Draw an arc in the window. - * @pre The window must be initialised. * @note Uses the current foreground color to draw the arc * @note May leave GDISP clipping to this window's dimensions * + * @param[in] gh The window handle * @param[in] x,y The center point * @param[in] radius The radius of the arc * @param[in] start The start angle (0 to 360) @@ -342,21 +348,21 @@ void gwinFillEllipse(GWindow *gw, coord_t x, coord_t y, coord_t a, coord_t b) { * * @api */ -void gwinDrawArc(GWindow *gw, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { +void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawArc(gw->x+x, gw->y+y, radius, startangle, endangle, gw->color); + gdispDrawArc(gh->x+x, gh->y+y, radius, startangle, endangle, gh->color); } #endif #if GDISP_NEED_ARC /* * @brief Draw a filled arc in the window. - * @pre The window must be initialised. * @note Uses the current foreground color to draw the filled arc * @note May leave GDISP clipping to this window's dimensions * + * @param[in] gh The window handle * @param[in] x,y The center point * @param[in] radius The radius of the arc * @param[in] start The start angle (0 to 360) @@ -364,255 +370,517 @@ void gwinDrawArc(GWindow *gw, coord_t x, coord_t y, coord_t radius, coord_t star * * @api */ -void gwinFillArc(GWindow *gw, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { +void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillArc(gw->x+x, gw->y+y, radius, startangle, endangle, gw->color); + gdispFillArc(gh->x+x, gh->y+y, radius, startangle, endangle, gh->color); } #endif #if GDISP_NEED_PIXELREAD /** * @brief Get the color of a pixel in the window. - * @pre The window must be initialised. * @return The color of the pixel. * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The position in the window + * @param[in] gh The window handle + * @param[in] x,y The position in the window * * @api */ -color_t gwinGetPixelColor(GWindow *gw, coord_t x, coord_t y) { +color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - return gdispGetPixelColor(gw->x+x, gw->y+y); + return gdispGetPixelColor(gh->x+x, gh->y+y); } #endif -#if GDISP_NEED_SCROLL +#if GDISP_NEED_TEXT /** - * @brief Scroll vertically a section of the window. - * @pre The window must be initialised. - * @note If lines is >= cy, it is equivelent to a area fill with the current background drawing color. + * @brief Draw a text character at the specified position in the window. + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character * @note May leave GDISP clipping to this window's dimensions * - * @param[in] lines The number of lines to scroll (Can be positive or negative) + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] c The character to draw * * @api */ -void gwinVerticalScroll(GWindow *gw, int lines) { +void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c) { + if (!gh->font) return; #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispVerticalScroll(gw->x, gw->y, gw->width, gw->height, lines, gw->bgcolor); + gdispDrawChar(gh->x+x, gh->y+y, c, gh->font, gh->color); } #endif #if GDISP_NEED_TEXT /** - * @brief Draw a text character at the specified position in the window. - * @pre The window must be initialised. + * @brief Draw a text character with a filled background at the specified position in the window. * @pre The font must have been set. - * @note Uses the current foreground color to draw the character + * @note Uses the current foreground color to draw the character and fills the background using the background drawing color * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The position for the text - * @param[in] c The character to draw + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] c The character to draw * * @api */ -void gwinDrawChar(GWindow *gw, coord_t x, coord_t y, char c) { - if (!gw->txt.font) return; +void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c) { + if (!gh->font) return; #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawChar(gw->x+x, gw->y+y, c, gw->txt.font, gw->color); + gdispFillChar(gh->x+x, gh->y+y, c, gh->font, gh->color, gh->bgcolor); } #endif #if GDISP_NEED_TEXT /** - * @brief Draw a text character with a filled background at the specified position in the window. - * @pre The window must be initialised. + * @brief Draw a text string in the window * @pre The font must have been set. - * @note Uses the current foreground color to draw the character and fills the background using the background drawing color + * @note Uses the current foreground color to draw the character * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The position for the text - * @param[in] c The character to draw + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] str The string to draw * * @api */ -void gwinFillChar(GWindow *gw, coord_t x, coord_t y, char c) { - if (!gw->txt.font) return; +void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str) { + if (!gh->font) return; #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillChar(gw->x+x, gw->y+y, c, gw->txt.font, gw->color, gw->bgcolor); + gdispDrawString(gh->x+x, gh->y+y, str, gh->font, gh->color); } #endif #if GDISP_NEED_TEXT /** - * @brief Draw a text string in the window - * @pre The window must be initialised. + * @brief Draw a text string with a filled background in the window * @pre The font must have been set. - * @note Uses the current foreground color to draw the character + * @note Uses the current foreground color to draw the character and fills the background using the background drawing color * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The position for the text - * @param[in] str The string to draw + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] str The string to draw * * @api */ -void gwinDrawString(GWindow *gw, coord_t x, coord_t y, const char *str) { - if (!gw->txt.font) return; +void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str) { + if (!gh->font) return; #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispDrawString(gw->x+x, gw->y+y, str, gw->txt.font, gw->color); + gdispFillString(gh->x+x, gh->y+y, str, gh->font, gh->color, gh->bgcolor); } #endif #if GDISP_NEED_TEXT /** - * @brief Draw a text string with a filled background in the window - * @pre The window must be initialised. + * @brief Draw a text string verticly centered within the specified box. * @pre The font must have been set. - * @note Uses the current foreground color to draw the character and fills the background using the background drawing color + * @note Uses the current foreground color to draw the character. + * @note The specified box does not need to align with the window box * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The position for the text - * @param[in] str The string to draw + * @param[in] gh The window handle + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] str The string to draw + * @param[in] justify Justify the text left, center or right within the box * * @api */ -void gwinFillString(GWindow *gw, coord_t x, coord_t y, const char *str) { - if (!gw->txt.font) return; +void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { + if (!gh->font) return; #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillString(gw->x+x, gw->y+y, str, gw->txt.font, gw->color, gw->bgcolor); + gdispDrawStringBox(gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, justify); } #endif #if GDISP_NEED_TEXT /** - * @brief Draw a text string verticly centered within the specified box. - * @pre The window must be initialised. + * @brief Draw a text string verticly centered within the specified filled box. * @pre The font must have been set. * @note Uses the current foreground color to draw the character and fills the background using the background drawing color * @note The entire box is filled. Note this box does not need to align with the window box * @note May leave GDISP clipping to this window's dimensions * - * @param[in] x,y The position for the text (need to define top-right or base-line - check code) - * @param[in] str The string to draw - * @param[in] justify Justify the text left, center or right within the box + * @param[in] gh The window handle + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] str The string to draw + * @param[in] justify Justify the text left, center or right within the box * * @api */ -void gwinBoxString(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { - if (!gw->txt.font) return; +void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { + if (!gh->font) return; #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); + gdispSetClip(gh->x, gh->y, gh->width, gh->height); #endif - gdispFillStringBox(gw->x+x, gw->y+y, cx, cy, str, gw->txt.font, gw->color, gw->bgcolor, justify); + gdispFillStringBox(gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, gh->bgcolor, justify); } #endif -#if GDISP_NEED_TEXT +/*------------------------------------------------------------------------------------------------------------------------*/ + +#if GWIN_NEED_CONSOLE + +/* + * Stream interface implementation. The interface is write only + */ + +#define Stream2GWindow(ip) ((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream)))) + +static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } +static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; } +static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; } +static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; } +static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; } +static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; } +static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } +static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; } + +struct GConsoleWindowVMT_t { + _base_asynchronous_channel_methods +}; + +static const struct GConsoleWindowVMT_t GWindowConsoleVMT = { + GWinStreamWrite, + GWinStreamRead, + GWinStreamPut, + GWinStreamGet, + GWinStreamPutTimed, + GWinStreamGetTimed, + GWinStreamWriteTimed, + GWinStreamReadTimed +}; + +/** + * @brief Create a console window. + * @details A console window allows text to be written using chprintf() (and the console functions defined here). + * @brief Text in a console window supports newlines and will wrap text as required. + * @return NULL if there is no resultant drawing area, otherwise a window handle. + * + * @param[in] gc The GConsoleObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] x,y The screen co-ordinates for the bottom left corner of the window + * @param[in] width The width of the window + * @param[in] height The height of the window + * @param[in] font The font to use + * @note The console is not automatically cleared on creation. You must do that by calling gwinClear() (possibly after changing your background color) + * @note If the dispay does not support scrolling, the window will be cleared when the bottom line is reached. + * @note The default drawing color gets set to White and the background drawing color to Black. + * @note The dimensions and position may be changed to fit on the real screen. + * + * @api + */ +GHandle gwinCreateConsole(GConsoleObject *gc, coord_t x, coord_t y, coord_t width, coord_t height, font_t font) { + if (!(gc = (GConsoleObject *)gwinInit((GWindowObject *)gc, x, y, width, height, sizeof(GConsoleObject)))) + return 0; + gc->gwin.type = GW_CONSOLE; + gwinSetFont(&gc->gwin, font); + gc->stream.vmt = &GWindowConsoleVMT; + gc->cx = 0; + gc->cy = 0; + return (GHandle)gc; +} + +/** + * @brief Get a stream from a console window suitable for use with chprintf(). + * @return The stream handle or NULL if this is not a console window. + * + * @param[in] gh The window handle (must be a console window) + * + * @api + */ +BaseSequentialStream *gwinGetConsoleStream(GHandle gh) { + if (gh->type != GW_CONSOLE) + return 0; + return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream); +} + /** * @brief Put a character at the cursor position in the window. - * @pre The window must be initialised. - * @pre The font must have been set. * @note Uses the current foreground color to draw the character and fills the background using the background drawing color * - * @param[in] c The character to draw + * @param[in] gh The window handle (must be a console window) + * @param[in] c The character to draw * * @api */ -void gwinPutChar(GWindow *gw, char c) { - uint8_t width; +void gwinPutChar(GHandle gh, char c) { + uint8_t width; + #define gcw ((GConsoleObject *)gh) - if (!gw->txt.font) return; + if (gh->type != GW_CONSOLE || !gh->font) return; + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + if (c == '\n') { /* clear the text at the end of the line */ - if (gw->txt.cx < gw->width) - gdispFillArea(gw->x + gw->txt.cx, gw->y + gw->txt.cy, gw->width - gw->txt.cx, gw->txt.fy, gw->bgcolor); - gw->txt.cx = 0; - gw->txt.cy += gw->txt.fy; + if (gcw->cx < gh->width) + gdispFillArea(gh->x + gcw->cx, gh->y + gcw->cy, gh->width - gcw->cx, gcw->fy, gh->bgcolor); + gcw->cx = 0; + gcw->cy += gcw->fy; } else if (c == '\r') { - // gw->cx = 0; + // gcw->cx = 0; } else { - width = gdispGetCharWidth(c, gw->txt.font) + gw->txt.fp; - if (gw->txt.cx + width >= gw->width) { + width = gdispGetCharWidth(c, gh->font) + gcw->fp; + if (gcw->cx + width >= gh->width) { /* clear the text at the end of the line */ - if (gw->txt.cy + gw->txt.fy <= gw->height) - gdispFillArea(gw->x + gw->txt.cx, gw->y + gw->txt.cy, gw->width - (gw->txt.cx + width), gw->txt.fy, gw->bgcolor); - gw->txt.cx = 0; - gw->txt.cy += gw->txt.fy; + if (gcw->cy + gcw->fy <= gh->height) + gdispFillArea(gh->x + gcw->cx, gh->y + gcw->cy, gh->width - (gcw->cx + width), gcw->fy, gh->bgcolor); + gcw->cx = 0; + gcw->cy += gcw->fy; } - if (gw->txt.cy + gw->txt.fy > gw->height) { + if (gcw->cy + gcw->fy > gh->height) { #if GDISP_NEED_SCROLL /* scroll the console */ - gdispVerticalScroll(gw->x, gw->y, gw->width, gw->height, gw->txt.fy, gw->bgcolor); + gdispVerticalScroll(gh->x, gh->y, gh->width, gh->height, gcw->fy, gh->bgcolor); /* reset the cursor to the start of the last line */ - gw->txt.cx = 0; - gw->txt.cy = (((int16_t)(gw->height/gw->txt.fy))-1)*gw->txt.fy; + gcw->cx = 0; + gcw->cy = (((coord_t)(gh->height/gcw->fy))-1)*gcw->fy; #else /* clear the console */ - gdispFillArea(gw->x, gw->y, gw->width, gw->height, gw->bgcolor); + gdispFillArea(gh->x, gh->y, gh->width, gh->height, gh->bgcolor); /* reset the cursor to the top of the window */ - gw->txt.cx = 0; - gw->txt.cy = 0; + gcw->cx = 0; + gcw->cy = 0; #endif } - gdispDrawChar(gw->x + gw->txt.cx, gw->y + gw->txt.cy, c, gw->txt.font, gw->color); + gdispDrawChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color); /* update cursor */ - gw->txt.cx += width; + gcw->cx += width; } + #undef gcw } -#endif -#if GDISP_NEED_TEXT /** * @brief Put a string at the cursor position in the window. It will wrap lines as required. - * @pre The window must be initialised. - * @pre The font must have been set. * @note Uses the current foreground color to draw the string and fills the background using the background drawing color * - * @param[in] str The string to draw + * @param[in] gh The window handle (must be a console window) + * @param[in] str The string to draw * * @api */ -void gwinPutString(GWindow *gw, const char *str) { +void gwinPutString(GHandle gh, const char *str) { while(*str) - gwinPutChar(gw, *str++); + gwinPutChar(gh, *str++); } -#endif -#if GDISP_NEED_TEXT /** * @brief Put the character array at the cursor position in the window. It will wrap lines as required. - * @pre The window must be initialised. - * @pre The font must have been set. * @note Uses the current foreground color to draw the string and fills the background using the background drawing color * - * @param[in] str The string to draw - * @param[in] n The number of characters to draw + * @param[in] gh The window handle (must be a console window) + * @param[in] str The string to draw + * @param[in] n The number of characters to draw * * @api */ -void gwinPutCharArray(GWindow *gw, const char *str, size_t n) { +void gwinPutCharArray(GHandle gh, const char *str, size_t n) { while(n--) - gwinPutChar(gw, *str++); + gwinPutChar(gh, *str++); +} +#endif + +/*------------------------------------------------------------------------------------------------------------------------*/ + +#if GWIN_NEED_BUTTON + +static const GButtonStyle GButtonDefaultStyle = { + GBTN_3D, + HTML2COLOR(0x404040), // color_up_edge; + HTML2COLOR(0xE0E0E0), // color_up_fill; + HTML2COLOR(0x000000), // color_up_txt; + HTML2COLOR(0x404040), // color_dn_edge; + HTML2COLOR(0x808080), // color_dn_fill; + HTML2COLOR(0x404040), // color_dn_txt; + }; + +/** + * @brief Create a button window. + * @return NULL if there is no resultant drawing area, otherwise a window handle. + * + * @param[in] gb The GConsoleWindow structure to initialise + * @param[in] x,y The screen co-ordinates for the bottom left corner of the window + * @param[in] width The width of the window + * @param[in] height The height of the window + * @param[in] font The font to use + * @param[in] type The type of button + * @note The drawing color gets set to White and the background drawing color to Black. + * @note The dimensions and position may be changed to fit on the real screen. + * @note The button is not automatically drawn. Call gwinButtonDraw() after changing the button style or setting the text. + * + * @api + */ +GHandle gwinCreateButton(GButtonObject *gb, coord_t x, coord_t y, coord_t width, coord_t height, font_t font, GButtonType type) { + if (!(gb = (GButtonObject *)gwinInit((GWindowObject *)gb, x, y, width, height, sizeof(GButtonObject)))) + return 0; + gb->gwin.type = GW_BUTTON; + gwinSetFont(&gb->gwin, font); + gwinSetButtonStyle(&gb->gwin, &GButtonDefaultStyle); + gb->type = type; + gb->state = GBTN_UP; + gb->txt = ""; + gb->callback = 0; + gb->inputsrc = 0; + return (GHandle)gb; } + +/** + * @brief Set the style of a button. + * @details The button style is defined by its shape and colours. + * + * @param[in] gh The window handle (must be a button window) + * @param[in] style The button style to set. + * @note The button is not automatically redrawn. Call gwinButtonDraw() after changing the button style + * + * @api + */ +void gwinSetButtonStyle(GHandle gh, const GButtonStyle *style) { + #define gbw ((GButtonObject *)gh) + if (gh->type != GW_BUTTON) + return; + + gbw->style.shape = style->shape; + gbw->style.color_up_edge = style->color_up_edge; + gbw->style.color_up_fill = style->color_up_fill; + gbw->style.color_dn_edge = style->color_dn_edge; + gbw->style.color_dn_fill = style->color_dn_fill; + gbw->style.color_up_txt = style->color_up_txt; + gbw->style.color_dn_txt = style->color_dn_txt; + #undef gbw +} + +/** + * @brief Set the text of a button. + * + * @param[in] gh The window handle (must be a button window) + * @param[in] txt The button text to set. This must be a constant string unless useAlloc is set. + * @param[in] useAlloc If TRUE the string specified will be copied into dynamically allocated memory. + * @note The button is not automatically redrawn. Call gwinButtonDraw() after changing the button text. + * + * @api + */ +void gwinSetButtonText(GHandle gh, const char *txt, bool_t useAlloc) { + #define gbw ((GButtonObject *)gh) + if (gh->type != GW_BUTTON) + return; + + // Dispose of the old string + if ((gh->flags & GBTN_FLG_ALLOCTXT)) { + gh->flags &= ~GBTN_FLG_ALLOCTXT; + if (gbw->txt) { + free((char *)gbw->txt); + gbw->txt = ""; + } + } + // Alloc the new text if required + if (txt && useAlloc) { + char *str; + + if ((str = (char *)malloc(strlen(txt)+1))) { + gh->flags |= GBTN_FLG_ALLOCTXT; + strcpy(str, txt); + } + txt = (const char *)str; + } + + gbw->txt = txt ? txt : ""; + #undef gbw +} + +/** + * @brief Redraw the button. + * + * @param[in] gh The window handle (must be a button window) + * + * @api + */ +void gwinButtonDraw(GHandle gh) { + color_t cedge; + color_t cfill; + color_t ctxt; + const char * txt; + #define gbw ((GButtonObject *)gh) + #define RND_CNR_SIZE 5 + + if (gh->type != GW_BUTTON) + return; + + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + + // Get the text (safely) + txt = gh->font && gbw->txt ? gbw->txt : ""; + + // Determine the colors to use + switch(gbw->state) { + case GBTN_DOWN: + cedge = gbw->style.color_dn_edge; + cfill = gbw->style.color_dn_fill; + ctxt = gbw->style.color_dn_txt; + break; + case GBTN_UP: default: + cedge = gbw->style.color_up_edge; + cfill = gbw->style.color_up_fill; + ctxt = gbw->style.color_up_txt; + break; + } + + // Draw according to the shape specified. + switch(gbw->style.shape) { +#if GDISP_NEED_ARC + case GBTN_ROUNDED: + if (gh->width >= 2*RND_CNR_SIZE+10) { + gdispFillRoundedBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, RND_CNR_SIZE-1, cfill); + gdispDrawStringBox(gh->x+1, gh->y+RND_CNR_SIZE, gh->width-2, gh->height-(2*RND_CNR_SIZE), txt, gh->font, ctxt, justifyCenter); + gdispDrawRoundedBox(gh->x, gh->y, gh->width, gh->height, RND_CNR_SIZE, cedge); + break; + } + /* Fall Through */ +#endif + case GBTN_SQUARE: + gdispFillStringBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, txt, gh->font, ctxt, cfill, justifyCenter); + gdispDrawBox(gh->x, gh->y, gh->width, gh->height, cedge); + break; +#if GDISP_NEED_ELLIPSE + case GBTN_ELLIPSE: + gdispFillEllipse(gh->x+1, gh->y+1, gh->width/2-1, gh->height/2-1, cfill); + gdispDrawStringBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, txt, gh->font, ctxt, justifyCenter); + gdispDrawEllipse(gh->x, gh->y, gh->width/2, gh->height/2, cedge); + break; +#endif + case GBTN_3D: default: + gdispFillStringBox(gh->x, gh->y, gh->width-1, gh->height-1, txt, gh->font, ctxt, cfill, justifyCenter); + gdispDrawLine(gh->x+gh->width-1, gh->y, gh->x+gh->width-1, gh->y+gh->height-1, cedge); + gdispDrawLine(gh->x, gh->y+gh->height-1, gh->x+gh->width-2, gh->y+gh->height-1, cedge); + break; + } + #undef gbw +} + +//void gwinSetButtonCallback(GHandle gh, ????); +//void gwinSetButtonInput(GHandle gh, ????); #endif #endif /* GFX_USE_GWIN */ -- cgit v1.2.3