From d3b4c499ab0267dbcad3d5972bb0317fdba9043e Mon Sep 17 00:00:00 2001 From: Andrew Hannam Date: Tue, 30 Oct 2012 18:18:29 +1000 Subject: [PATCH] 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. --- include/gdisp.h | 22 +- include/gdisp_emulation.c | 116 +++- include/gdisp_fonts.h | 1 - include/gwin.h | 199 ++++-- releases.txt | 6 +- src/gdisp.c | 446 +++++++------ src/gwin.c | 1258 ++++++++++++++++++++++--------------- 7 files changed, 1268 insertions(+), 780 deletions(-) diff --git a/include/gdisp.h b/include/gdisp.h index 9e3a3b77..3964e9bd 100644 --- a/include/gdisp.h +++ b/include/gdisp.h @@ -166,6 +166,9 @@ extern "C" { #endif #if GDISP_NEED_MULTITHREAD || GDISP_NEED_ASYNC + /* These routines can be hardware accelerated + * - Do not add a routine here unless it has also been added to the hardware acceleration layer + */ /* Base Functions */ bool_t gdispInit(void); @@ -253,8 +256,9 @@ extern "C" { #endif -/* Now obsolete functions */ -#define gdispBlitArea(x, y, cx, cy, buffer) gdispBlitAreaEx(x, y, cx, cy, 0, 0, cx, buffer) +/* These routines are not hardware accelerated + * - Do not add a hardware accelerated routines here. + */ /* Extra drawing functions */ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); @@ -263,17 +267,31 @@ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); #if GDISP_NEED_TEXT void gdispDrawString(coord_t x, coord_t y, const char *str, font_t font, color_t color); void gdispFillString(coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor); + 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); void gdispFillStringBox(coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgColor, justify_t justify); coord_t gdispGetFontMetric(font_t font, fontmetric_t metric); coord_t gdispGetCharWidth(char c, font_t font); coord_t gdispGetStringWidth(const char* str, font_t font); #endif +/* Extra Arc Functions */ +#if GDISP_NEED_ARC + void gdispDrawRoundedBox(coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color); + void gdispFillRoundedBox(coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color); +#endif + /* Support routine for packed pixel formats */ #ifndef gdispPackPixels void gdispPackPixels(const pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color); #endif +/* Macro definitions + * + */ + +/* Now obsolete functions */ +#define gdispBlitArea(x, y, cx, cy, buffer) gdispBlitAreaEx(x, y, cx, cy, 0, 0, cx, buffer) + /* Macro definitions for common gets and sets */ #define gdispSetPowerMode(powerMode) gdispControl(GDISP_CONTROL_POWER, (void *)(unsigned)(powerMode)) #define gdispSetOrientation(newOrientation) gdispControl(GDISP_CONTROL_ORIENTATION, (void *)(unsigned)(newOrientation)) diff --git a/include/gdisp_emulation.c b/include/gdisp_emulation.c index 34596b61..ec3e6a66 100644 --- a/include/gdisp_emulation.c +++ b/include/gdisp_emulation.c @@ -291,7 +291,7 @@ #if GDISP_NEED_ARC && !GDISP_HARDWARE_ARCS - #include + #include /* * @brief Internal helper function for gdispDrawArc() @@ -307,7 +307,7 @@ * @notapi */ static 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) { + if (/*start >= 0 && */start <= 180) { float x_maxI = x + radius*cos(start*M_PI/180); float x_minI; @@ -322,13 +322,13 @@ do { if(x-a <= x_maxI && x-a >= x_minI) - GDISP_LLD(drawpixel)(x-a, y+b, color); + GDISP_LLD(drawpixel)(x-a, y-b, color); if(x+a <= x_maxI && x+a >= x_minI) - GDISP_LLD(drawpixel)(x+a, y+b, color); + GDISP_LLD(drawpixel)(x+a, y-b, color); if(x-b <= x_maxI && x-b >= x_minI) - GDISP_LLD(drawpixel)(x-b, y+a, color); + GDISP_LLD(drawpixel)(x-b, y-a, color); if(x+b <= x_maxI && x+b >= x_minI) - GDISP_LLD(drawpixel)(x+b, y+a, color); + GDISP_LLD(drawpixel)(x+b, y-a, color); if (P < 0) { P = P + 3 + 2*a; @@ -356,13 +356,13 @@ do { if(x-a <= x_maxII && x-a >= x_minII) - GDISP_LLD(drawpixel)(x-a, y-b, color); + GDISP_LLD(drawpixel)(x-a, y+b, color); if(x+a <= x_maxII && x+a >= x_minII) - GDISP_LLD(drawpixel)(x+a, y-b, color); + GDISP_LLD(drawpixel)(x+a, y+b, color); if(x-b <= x_maxII && x-b >= x_minII) - GDISP_LLD(drawpixel)(x-b, y-a, color); + GDISP_LLD(drawpixel)(x-b, y+a, color); if(x+b <= x_maxII && x+b >= x_minII) - GDISP_LLD(drawpixel)(x+b, y-a, color); + GDISP_LLD(drawpixel)(x+b, y+a, color); if (P < 0) { P = P + 3 + 2*a; @@ -387,14 +387,96 @@ #endif #if GDISP_NEED_ARC && !GDISP_HARDWARE_ARCFILLS + /* + * @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 + */ + static 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) + GDISP_LLD(drawline)(x, y, x-a, y-b, color); + if(x+a <= x_maxI && x+a >= x_minI) + GDISP_LLD(drawline)(x, y, x+a, y-b, color); + if(x-b <= x_maxI && x-b >= x_minI) + GDISP_LLD(drawline)(x, y, x-b, y-a, color); + if(x+b <= x_maxI && x+b >= x_minI) + GDISP_LLD(drawline)(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) + GDISP_LLD(drawline)(x, y, x-a, y+b, color); + if(x+a <= x_maxII && x+a >= x_minII) + GDISP_LLD(drawline)(x, y, x+a, y+b, color); + if(x-b <= x_maxII && x-b >= x_minII) + GDISP_LLD(drawline)(x, y, x-b, y+a, color); + if(x+b <= x_maxII && x+b >= x_minII) + GDISP_LLD(drawline)(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); + } + } + void GDISP_LLD(fillarc)(coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color) { - (void)x; - (void)y; - (void)radius; - (void)startangle; - (void)endangle; - (void)color; -#warning "GDISP: FillArc Emulation Not Implemented Yet" + if(endangle < startangle) { + _fill_arc(x, y, startangle, 360, radius, color); + _fill_arc(x, y, 0, endangle, radius, color); + } else { + _fill_arc(x, y, startangle, endangle, radius, color); + } } #endif diff --git a/include/gdisp_fonts.h b/include/gdisp_fonts.h index 1a2cb3d7..4b9f2287 100644 --- a/include/gdisp_fonts.h +++ b/include/gdisp_fonts.h @@ -88,4 +88,3 @@ struct font { #endif /* _GDISP_FONTS_H */ /** @} */ - diff --git a/include/gwin.h b/include/gwin.h index 7dfa4c68..79b8371c 100644 --- a/include/gwin.h +++ b/include/gwin.h @@ -45,52 +45,119 @@ * @name GWIN more complex functionality to be compiled * @{ */ + /** + * @brief Should console functions be included. + * @details Defaults to TRUE + */ + #ifndef GWIN_NEED_CONSOLE + #define GWIN_NEED_CONSOLE TRUE + #endif + /** + * @brief Should button functions be included. + * @details Defaults to FALSE for now as implementation is not complete + */ + #ifndef GWIN_NEED_BUTTON + #define GWIN_NEED_BUTTON FALSE + #endif + /** @} */ /*===========================================================================*/ /* Low Level Driver details and error checks. */ /*===========================================================================*/ -#if !GFX_USE_GDISP +#if !defined(GFX_USE_GDISP) #error "GWIN: GFX_USE_GDISP must also be defined" #endif - #include "gdisp.h" #if !GDISP_NEED_CLIP #warning "GWIN: Drawing can occur outside the defined window as GDISP_NEED_CLIP is FALSE" #endif +#if GWIN_NEED_CONSOLE && !GDISP_NEED_TEXT + #error "GWIN: Text support (GDISP_NEED_TEXT) is required if GWIN_NEED_CONSOLE is defined." +#endif + +#if GWIN_NEED_BUTTON && !GDISP_NEED_TEXT + #error "GWIN: Text support (GDISP_NEED_TEXT) is required if GWIN_NEED_BUTTON is defined." +#endif + +#if GWIN_NEED_BUTTON + #warning "GWIN: Button support is not complete yet" +#endif + /*===========================================================================*/ /* Type definitions */ /*===========================================================================*/ -/** - * @extends BaseAsynchronousChannelVMT - * - * @brief @p GWindow virtual methods table. - */ -struct GWindowVMT { - _base_asynchronous_channel_methods -}; +typedef enum GWindowType_e { + GW_WINDOW, GW_CONSOLE, GW_BUTTON + } GWindowType; -struct GWindowText { - const struct GWindowVMT *vmt; - _base_asynchronous_channel_data - font_t font; // Current font - uint8_t fy; // Current font height - uint8_t fp; // Current font inter-character spacing - coord_t cx,cy; // Cursor position -}; - -typedef struct GWindow_t { +// A basic window +typedef struct GWindowObject_t { + GWindowType type; // What type of window is this + uint16_t flags; // Internal flags + coord_t x, y; // Screen relative position + coord_t width, height; // Dimensions of this window + color_t color, bgcolor; // Current drawing colors #if GDISP_NEED_TEXT - struct GWindowText txt; + font_t font; // Current font +#endif +} GWindowObject, * GHandle; + +#if GWIN_NEED_CONSOLE + // A console window. Supports wrapped text writing and a cursor. + typedef struct GConsoleObject_t { + GWindowObject gwin; + + struct GConsoleWindowStream_t { + const struct GConsoleWindowVMT_t *vmt; + _base_asynchronous_channel_data + } stream; + + coord_t cx,cy; // Cursor position + uint8_t fy; // Current font height + uint8_t fp; // Current font inter-character spacing + } GConsoleObject; +#endif + +#if GWIN_NEED_BUTTON + typedef enum GButtonShape_e { + GBTN_3D, GBTN_SQUARE, GBTN_ROUNDED, GBTN_ELLIPSE + } GButtonShape; + + typedef struct GButtonStyle_t { + GButtonShape shape; + color_t color_up_edge; + color_t color_up_fill; + color_t color_up_txt; + color_t color_dn_edge; + color_t color_dn_fill; + color_t color_dn_txt; + } GButtonStyle; + + typedef enum GButtonType_e { + GBTN_NORMAL, GBTN_TOGGLE + } GButtonType; + + typedef enum GButtonState_e { + GBTN_UP, GBTN_DOWN + } GButtonState; + + // A button window + typedef struct GButtonObject_t { + GWindowObject gwin; + + GButtonStyle style; + GButtonState state; + GButtonType type; + const char * txt; + void * callback; // To be fixed + void * inputsrc; // To be fixed + } GButtonObject; #endif - coord_t x, y; // Screen relative position - coord_t width, height; // Dimensions of this window - color_t color, bgcolor; // Current drawing colors -} GWindow; /*===========================================================================*/ /* External declarations. */ @@ -101,70 +168,81 @@ extern "C" { #endif /* Base Functions */ -bool_t gwinInit(GWindow *gw, coord_t x, coord_t y, coord_t width, coord_t height); +GHandle gwinCreateWindow(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height); +void gwinDestroyWindow(GHandle gh); /* Status Functions */ -#define gwinGetScreenX(gw) ((gw)->x) -#define gwinGetScreenY(gw) ((gw)->y) -#define gwinGetWidth(gw) ((gw)->width) -#define gwinGetHeight(gw) ((gw)->height) +#define gwinGetScreenX(gh) ((gh)->x) +#define gwinGetScreenY(gh) ((gh)->y) +#define gwinGetWidth(gh) ((gh)->width) +#define gwinGetHeight(gh) ((gh)->height) /* Set up for drawing */ -#define gwinSetColor(gw, clr) (gw)->color = (clr) -#define gwinSetBgColor(gw, bgclr) (gw)->bgcolor = (bgclr) +#define gwinSetColor(gh, clr) (gh)->color = (clr) +#define gwinSetBgColor(gh, bgclr) (gh)->bgcolor = (bgclr) /* Set up for text */ #if GDISP_NEED_TEXT -void gwinSetFont(GWindow *gw, font_t font); -#define gwinGetStream(gw) ((BaseSequentialStream *)gw) +void gwinSetFont(GHandle gh, font_t font); #endif /* Drawing Functions */ -void gwinClear(GWindow *gw); -void gwinDrawPixel(GWindow *gw, coord_t x, coord_t y); -void gwinDrawLine(GWindow *gw, coord_t x0, coord_t y0, coord_t x1, coord_t y1); -void gwinDrawBox(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy); -void gwinFillArea(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy); -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 gwinClear(GHandle gh); +void gwinDrawPixel(GHandle gh, coord_t x, coord_t y); +void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1); +void gwinDrawBox(GHandle gh, 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); +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); /* Circle Functions */ #if GDISP_NEED_CIRCLE -void gwinDrawCircle(GWindow *gw, coord_t x, coord_t y, coord_t radius); -void gwinFillCircle(GWindow *gw, coord_t x, coord_t y, coord_t radius); +void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius); +void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius); #endif /* Ellipse Functions */ #if GDISP_NEED_ELLIPSE -void gwinDrawEllipse(GWindow *gw, coord_t x, coord_t y, coord_t a, coord_t b); -void gwinFillEllipse(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); +void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b); #endif /* Arc Functions */ #if GDISP_NEED_ARC -void gwinDrawArc(GWindow *gw, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle); -void gwinFillArc(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); +void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle); #endif /* Read a pixel Function */ #if GDISP_NEED_PIXELREAD -color_t gwinGetPixelColor(GWindow *gw, coord_t x, coord_t y); -#endif - -/* Scrolling Function - clears the area scrolled out */ -#if GDISP_NEED_SCROLL -void gwinVerticalScroll(GWindow *gw, int lines); +color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y); #endif /* Extra Text Functions */ #if GDISP_NEED_TEXT -void gwinDrawChar(GWindow *gw, coord_t x, coord_t y, char c); -void gwinFillChar(GWindow *gw, coord_t x, coord_t y, char c); -void gwinDrawString(GWindow *gw, coord_t x, coord_t y, const char *str); -void gwinFillString(GWindow *gw, coord_t x, coord_t y, const char *str); -void gwinBoxString(GWindow *gw, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); -void gwinPutChar(GWindow *gw, char c); -void gwinPutString(GWindow *gw, const char *str); -void gwinPutCharArray(GWindow *gw, const char *str, size_t n); +void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c); +void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c); +void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str); +void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str); +void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); +void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); +#endif + +#if GWIN_NEED_CONSOLE +GHandle gwinCreateConsole(GConsoleObject *gc, coord_t x, coord_t y, coord_t width, coord_t height, font_t font); +BaseSequentialStream *gwinGetConsoleStream(GHandle gh); +void gwinPutChar(GHandle gh, char c); +void gwinPutString(GHandle gh, const char *str); +void gwinPutCharArray(GHandle gh, const char *str, size_t n); +#endif + +#if GWIN_NEED_BUTTON +GHandle gwinCreateButton(GButtonObject *gb, coord_t x, coord_t y, coord_t width, coord_t height, font_t font, GButtonType type); +void gwinSetButtonStyle(GHandle gh, const GButtonStyle *style); +void gwinSetButtonText(GHandle gh, const char *txt, bool_t useAlloc); +void gwinButtonDraw(GHandle gh); +#define gwinGetButtonState(gh) (((GButtonObject *)(gh))->state) +//void gwinSetButtonCallback(GHandle gh, ????); +//void gwinSetButtonInput(GHandle gh, ????); #endif #ifdef __cplusplus @@ -175,4 +253,3 @@ void gwinPutCharArray(GWindow *gw, const char *str, size_t n); #endif /* _GWIN_H */ /** @} */ - diff --git a/releases.txt b/releases.txt index 35d6389e..ad7cdfd5 100644 --- a/releases.txt +++ b/releases.txt @@ -9,6 +9,11 @@ current stable: 1.3 FIX: Nokia 6610 fix FEATURE: New driver: Win32 FEATURE: implementation of gdispFillArc() +FIX: Hardware accelerate Arc routines +FIX: Fix axis orientation for Arc routines +FEATURE: new gdisp rounded box routines +FEATURE: new gdispDrawStringBox() +FEATURE: GWIN infrastructure *** changes after 1.2 *** @@ -41,4 +46,3 @@ FEATURE: added SSD1963 DMA support FEATURE: added touchpad interface for storing calibration values (#define TOUCHPAD_STORE_CALIBRATION) CHANGE: replaced every GDISP_XXX macro with GDISP_XXX CHANGE: removed last digit of version number - 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 && 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 + +#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 + #if GDISP_NEED_ARC || defined(__DOXYGEN__) - -#include "math.h" - -/* - * @brief Internal helper function for gdispDrawArc() +/** + * @brief Draw a rectangular box with rounded corners + * @pre The GDISP unit must be in powerOn or powerSleep mode. * - * @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); - } -} - -/* - * @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 + * @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() +/** + * @brief Draw a filled rectangular box with rounded corners + * @pre The GDISP unit must be in powerOn or powerSleep mode. * - * @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 - * - * @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,36 +34,529 @@ #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. */ -/*===========================================================================*/ +// 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; -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ + // 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 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; + gw->height = height; + gw->color = White; + gw->bgcolor = Black; +#if GDISP_NEED_TEXT + 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 + 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. + * + * @param[in] gh The window handle + * @param[in] font The font to use for text functions + * + * @api + */ +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 + * @note Uses the current background color to clear the window + * + * @param[in] gh The window handle + * + * @api + */ +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 + * @note Uses the current foreground color to set the pixel + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * + * @api + */ +void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawPixel(gh->x+x, gh->y+y, gh->color); +} + +/** + * @brief Draw a line in the window + * @note Uses the current foreground color to draw the line + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x0,y0 The start position + * @param[in] x1,y1 The end position + * + * @api + */ +void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawLine(gh->x+x0, gh->y+y0, gh->x+x1, gh->y+y1, gh->color); +} + +/** + * @brief Draw a box in the window + * @note Uses the current foreground color to draw the box + * @note May leave GDISP clipping to this window's 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(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawBox(gh->x+x, gh->y+y, cx, cy, gh->color); +} + +/** + * @brief Fill an rectangular area in the window + * @note Uses the current foreground color to fill the box + * @note May leave GDISP clipping to this window's 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(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + 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 + * @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] 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(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(gh->x, gh->y, gh->width, gh->height); + #endif + gdispBlitAreaEx(gh->x+x, gh->y+y, cx, cy, srcx, srcy, srccx, buffer); +} + +#if GDISP_NEED_CIRCLE +/** + * @brief Draw a circle in the window. + * @note Uses the current foreground color to draw the circle + * @note May leave GDISP clipping to this window's dimensions + * + * @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(GHandle gh, coord_t x, coord_t y, coord_t radius) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawCircle(gh->x+x, gh->y+y, radius, gh->color); +} +#endif + +#if GDISP_NEED_CIRCLE +/** + * @brief Draw a filled circle in the window. + * @note Uses the current foreground color to draw the filled circle + * @note May leave GDISP clipping to this window's dimensions + * + * @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(GHandle gh, coord_t x, coord_t y, coord_t radius) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispFillCircle(gh->x+x, gh->y+y, radius, gh->color); +} +#endif + +#if GDISP_NEED_ELLIPSE +/** + * @brief Draw an ellipse. + * @note Uses the current foreground color to draw the ellipse + * @note May leave GDISP clipping to this window's dimensions + * + * @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(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawEllipse(gh->x+x, gh->y+y, a, b, gh->color); +} +#endif + +#if GDISP_NEED_ELLIPSE +/** + * @brief Draw an filled ellipse. + * @note Uses the current foreground color to draw the filled ellipse + * @note May leave GDISP clipping to this window's dimensions + * + * @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(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispFillEllipse(gh->x+x, gh->y+y, a, b, gh->color); +} +#endif + +#if GDISP_NEED_ARC +/* + * @brief Draw an arc in the window. + * @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) + * @param[in] end The end angle (0 to 360) + * + * @api + */ +void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + 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. + * @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) + * @param[in] end The end angle (0 to 360) + * + * @api + */ +void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + 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. + * @return The color of the pixel. + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position in the window + * + * @api + */ +color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + return gdispGetPixelColor(gh->x+x, gh->y+y); +} +#endif + +#if GDISP_NEED_TEXT +/** + * @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] gh The window handle + * @param[in] x,y The position for the text + * @param[in] c The character to draw + * + * @api + */ +void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c) { + if (!gh->font) return; + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawChar(gh->x+x, gh->y+y, c, gh->font, gh->color); +} +#endif + +#if GDISP_NEED_TEXT +/** + * @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 and fills the background using the background drawing color + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] c The character to draw + * + * @api + */ +void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c) { + if (!gh->font) return; + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispFillChar(gh->x+x, gh->y+y, c, gh->font, gh->color, gh->bgcolor); +} +#endif + +#if GDISP_NEED_TEXT +/** + * @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 + * @note May leave GDISP clipping to this window's dimensions + * + * @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(GHandle gh, coord_t x, coord_t y, const char *str) { + if (!gh->font) return; + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispDrawString(gh->x+x, gh->y+y, str, gh->font, gh->color); +} +#endif + +#if GDISP_NEED_TEXT +/** + * @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 and fills the background using the background drawing color + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] str The string to draw + * + * @api + */ +void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str) { + if (!gh->font) return; + #if GDISP_SET_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gdispFillString(gh->x+x, gh->y+y, str, gh->font, gh->color, gh->bgcolor); +} +#endif + +#if GDISP_NEED_TEXT +/** + * @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. + * @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] 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 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(gh->x, gh->y, gh->width, gh->height); + #endif + 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 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] 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 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(gh->x, gh->y, gh->width, gh->height); + #endif + gdispFillStringBox(gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, gh->bgcolor, justify); +} +#endif + +/*------------------------------------------------------------------------------------------------------------------------*/ + +#if GWIN_NEED_CONSOLE + /* * 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; } + +#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((GWindow *)ip, (char)b); return RDY_OK; } +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((GWindow *)ip, (char)b); 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((GWindow *)ip, (const char *)bp, n); 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; } -static const struct GWindowVMT vmt = { +struct GConsoleWindowVMT_t { + _base_asynchronous_channel_methods +}; + +static const struct GConsoleWindowVMT_t GWindowConsoleVMT = { GWinStreamWrite, GWinStreamRead, GWinStreamPut, @@ -73,546 +566,321 @@ static const struct GWindowVMT vmt = { GWinStreamWriteTimed, GWinStreamReadTimed }; -#endif - -/*===========================================================================*/ -/* High Level Driver Routines. */ -/*===========================================================================*/ /** - * @brief Initialise a window. - * @return FALSE if there is no resultant drawing area, otherwise TRUE. + * @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] 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. + * @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 */ -bool_t gwinInit(GWindow *gw, coord_t x, coord_t y, coord_t width, coord_t height) { - coord_t w, h; - - 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+width > w) width = w - x; - if (y+height > h) height = h - y; - gw->x = x; - gw->y = y; - gw->width = width; - gw->height = 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; -#endif - return TRUE; -} - -#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 - * - * @api - */ -void gwinSetFont(GWindow *gw, font_t font) { - gw->txt.font = font; - gw->txt.fy = gdispGetFontMetric(font, fontHeight); - gw->txt.fp = gdispGetFontMetric(font, fontCharPadding); -} -#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 - * - * @api - */ -void gwinClear(GWindow *gw) { - gdispFillArea(gw->x, gw->y, gw->width, gw->height, gw->bgcolor); +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 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 + * @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] gw The window + * @param[in] gh The window handle (must be a console window) * * @api */ -void gwinDrawPixel(GWindow *gw, coord_t x, coord_t y) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispDrawPixel(gw->x+x, gw->y+y, gw->color); +BaseSequentialStream *gwinGetConsoleStream(GHandle gh) { + if (gh->type != GW_CONSOLE) + return 0; + return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream); } -/** - * @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] 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) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispDrawLine(gw->x+x0, gw->y+y0, gw->x+x1, gw->y+y1, gw->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) - * - * @api - */ -void gwinDrawBox(GWindow *gw, 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); - #endif - gdispDrawBox(gw->x+x, gw->y+y, cx, cy, gw->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) - * - * @api - */ -void gwinFillArea(GWindow *gw, 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); - #endif - gdispFillArea(gw->x+x, gw->y+y, cx, cy, gw->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. - * - * @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) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispBlitAreaEx(gw->x+x, gw->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 - * - * @api - */ -void gwinDrawCircle(GWindow *gw, coord_t x, coord_t y, coord_t radius) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispDrawCircle(gw->x+x, gw->y+y, radius, gw->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 - * - * @api - */ -void gwinFillCircle(GWindow *gw, coord_t x, coord_t y, coord_t radius) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispFillCircle(gw->x+x, gw->y+y, radius, gw->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 - * - * @api - */ -void gwinDrawEllipse(GWindow *gw, 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); - #endif - gdispDrawEllipse(gw->x+x, gw->y+y, a, b, gw->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 - * - * @api - */ -void gwinFillEllipse(GWindow *gw, 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); - #endif - gdispFillEllipse(gw->x+x, gw->y+y, a, b, gw->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] x,y 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) - * - * @api - */ -void gwinDrawArc(GWindow *gw, 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); - #endif - gdispDrawArc(gw->x+x, gw->y+y, radius, startangle, endangle, gw->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] x,y 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) - * - * @api - */ -void gwinFillArc(GWindow *gw, 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); - #endif - gdispFillArc(gw->x+x, gw->y+y, radius, startangle, endangle, gw->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 - * - * @api - */ -color_t gwinGetPixelColor(GWindow *gw, coord_t x, coord_t y) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - return gdispGetPixelColor(gw->x+x, gw->y+y); -} -#endif - -#if GDISP_NEED_SCROLL -/** - * @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. - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] lines The number of lines to scroll (Can be positive or negative) - * - * @api - */ -void gwinVerticalScroll(GWindow *gw, int lines) { - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispVerticalScroll(gw->x, gw->y, gw->width, gw->height, lines, gw->bgcolor); -} -#endif - -#if GDISP_NEED_TEXT -/** - * @brief Draw a text character at the specified 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 - * @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 - * - * @api - */ -void gwinDrawChar(GWindow *gw, coord_t x, coord_t y, char c) { - if (!gw->txt.font) return; - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispDrawChar(gw->x+x, gw->y+y, c, gw->txt.font, gw->color); -} -#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. - * @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 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 - * - * @api - */ -void gwinFillChar(GWindow *gw, coord_t x, coord_t y, char c) { - if (!gw->txt.font) return; - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispFillChar(gw->x+x, gw->y+y, c, gw->txt.font, gw->color, gw->bgcolor); -} -#endif - -#if GDISP_NEED_TEXT -/** - * @brief Draw a text string 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 - * @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 - * - * @api - */ -void gwinDrawString(GWindow *gw, coord_t x, coord_t y, const char *str) { - if (!gw->txt.font) return; - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispDrawString(gw->x+x, gw->y+y, str, gw->txt.font, gw->color); -} -#endif - -#if GDISP_NEED_TEXT -/** - * @brief Draw a text string with a filled background 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 - * @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 - * - * @api - */ -void gwinFillString(GWindow *gw, coord_t x, coord_t y, const char *str) { - if (!gw->txt.font) return; - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispFillString(gw->x+x, gw->y+y, str, gw->txt.font, gw->color, gw->bgcolor); -} -#endif - -#if GDISP_NEED_TEXT -/** - * @brief Draw a text string verticly centered within the specified box. - * @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 - * @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 - * - * @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; - #if GDISP_SET_CLIP - gdispSetClip(gw->x, gw->y+y, gw->width, gw->height); - #endif - gdispFillStringBox(gw->x+x, gw->y+y, cx, cy, str, gw->txt.font, gw->color, gw->bgcolor, justify); -} -#endif - -#if GDISP_NEED_TEXT /** * @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++); +} + +/** + * @brief Put the character array at the cursor position in the window. It will wrap lines as required. + * @note Uses the current foreground color to draw the string and fills the background using the background drawing color + * + * @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(GHandle gh, const char *str, size_t n) { + while(n--) + gwinPutChar(gh, *str++); } #endif -#if GDISP_NEED_TEXT +/*------------------------------------------------------------------------------------------------------------------------*/ + +#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 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 + * @brief Create a button window. + * @return NULL if there is no resultant drawing area, otherwise a window handle. * - * @param[in] str The string to draw - * @param[in] n The number of characters to draw + * @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 */ -void gwinPutCharArray(GWindow *gw, const char *str, size_t n) { - while(n--) - gwinPutChar(gw, *str++); +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 */