diff --git a/docs/releases.txt b/docs/releases.txt index fc2ef091..757ff41d 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -4,6 +4,7 @@ *** Changes After 2.1 *** FEATURE: Added nested containers demo +FEATURE: Revised GWIN redraw strategy *** Release 2.1 *** diff --git a/gfxconf.example.h b/gfxconf.example.h index 652b6a1b..f0dc2ca2 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -134,6 +134,8 @@ //#define GFX_USE_GWIN FALSE //#define GWIN_NEED_WINDOWMANAGER FALSE +// #define GWIN_REDRAW_IMMEDIATE FALSE +// #define GWIN_REDRAW_SINGLEOP FALSE //#define GWIN_NEED_CONSOLE FALSE // #define GWIN_CONSOLE_USE_HISTORY FALSE diff --git a/src/gwin/class_gwin.h b/src/gwin/class_gwin.h index 0aee1ae8..61a27447 100644 --- a/src/gwin/class_gwin.h +++ b/src/gwin/class_gwin.h @@ -194,6 +194,14 @@ GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit */ void _gwinUpdate(GHandle gh); +/** + * @brief How to flush the redraws + * @notes REDRAW_WAIT - Wait for a drawing session to be available + * @notes REDRAW_NOWAIT - Do nothing if the drawing session is not available + * @note REDRAW_INSESSION - We are already in a drawing session + */ +typedef enum GRedrawMethod { REDRAW_WAIT, REDRAW_NOWAIT, REDRAW_INSESSION } GRedrawMethod; + /** * @brief Flush any pending redraws in the system. * @@ -209,7 +217,7 @@ void _gwinUpdate(GHandle gh); * * @notapi */ -void _gwinFlushRedraws(bool_t doWait); +void _gwinFlushRedraws(GRedrawMethod how); /** * @brief Obtain a drawing session diff --git a/src/gwin/gwm.c b/src/gwin/gwm.c index ffea4afd..6b0c559c 100644 --- a/src/gwin/gwm.c +++ b/src/gwin/gwm.c @@ -36,8 +36,8 @@ return TRUE; } - void _gwinFlushRedraws(bool_t doWait) { - (void) doWait; + void _gwinFlushRedraws(GRedrawMethod how) { + (void) how; // We are always flushed } @@ -147,9 +147,6 @@ #if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER -// Do we redraw all windows at once? -#define GWIN_LONG_REDRAW TRUE - #include "src/gwin/class_gwin.h" /*----------------------------------------------- @@ -158,15 +155,18 @@ // The default window manager extern const GWindowManager GNullWindowManager; -GWindowManager * _GWINwm; +GWindowManager * _GWINwm; -static gfxSem gwinsem; -static gfxQueueASync _GWINList; -static volatile bool_t RedrawPending; -#if GFX_USE_GTIMER - static GTimer RedrawTimer; - static void RedrawTimerFn(void *param); +static gfxSem gwinsem; +static gfxQueueASync _GWINList; +#if !GWIN_REDRAW_IMMEDIATE + static GTimer RedrawTimer; + static void RedrawTimerFn(void *param); #endif +static volatile uint8_t RedrawPending; + #define DOREDRAW_INVISIBLES 0x01 + #define DOREDRAW_VISIBLES 0x02 + /*----------------------------------------------- * Window Routines @@ -176,7 +176,7 @@ void _gwmInit(void) { gfxSemInit(&gwinsem, 1, 1); gfxQueueASyncInit(&_GWINList); - #if GFX_USE_GTIMER + #if !GWIN_REDRAW_IMMEDIATE gtimerInit(&RedrawTimer); gtimerStart(&RedrawTimer, RedrawTimerFn, 0, TRUE, TIME_INFINITE); #endif @@ -192,25 +192,25 @@ void _gwmDeinit(void) gwinDestroy(gh); _GWINwm->vmt->DeInit(); - #if GFX_USE_GTIMER + #if !GWIN_REDRAW_IMMEDIATE gtimerDeinit(&RedrawTimer); #endif gfxQueueASyncDeinit(&_GWINList); gfxSemDestroy(&gwinsem); } -#if GFX_USE_GTIMER +#if GWIN_REDRAW_IMMEDIATE + #define TriggerRedraw(void) _gwinFlushRedraws(REDRAW_NOWAIT); +#else #define TriggerRedraw() gtimerJab(&RedrawTimer); static void RedrawTimerFn(void *param) { (void) param; - _gwinFlushRedraws(FALSE); + _gwinFlushRedraws(REDRAW_NOWAIT); } -#else - #define TriggerRedraw(void) _gwinFlushRedraws(FALSE); #endif -void _gwinFlushRedraws(bool_t doWait) { +void _gwinFlushRedraws(GRedrawMethod how) { GHandle gh; // Do we really need to do anything? @@ -218,19 +218,46 @@ void _gwinFlushRedraws(bool_t doWait) { return; // Obtain the drawing lock - if (doWait) + if (how == REDRAW_WAIT) gfxSemWait(&gwinsem, TIME_INFINITE); - else if (!gfxSemWait(&gwinsem, TIME_IMMEDIATE)) + else if (how == REDRAW_NOWAIT && !gfxSemWait(&gwinsem, TIME_IMMEDIATE)) // Someone is drawing - They will do the redraw when they are finished return; - // Look for something to redraw - while(RedrawPending) { - // Catch any new redraw requests from here on - RedrawPending = FALSE; + // Do loss of visibility first + while ((RedrawPending & DOREDRAW_INVISIBLES)) { + RedrawPending &= ~DOREDRAW_INVISIBLES; // Catch new requests for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if (!(gh->flags & GWIN_FLG_NEEDREDRAW)) + if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != GWIN_FLG_NEEDREDRAW) + continue; + + // Do the redraw + #if GDISP_NEED_CLIP + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + _GWINwm->vmt->Redraw(gh); + gdispGUnsetClip(gh->display); + #else + _GWINwm->vmt->Redraw(gh); + #endif + + // Postpone further redraws + #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP + if (how == REDRAW_NOWAIT) { + RedrawPending |= DOREDRAW_INVISIBLES; + TriggerRedraw(); + goto releaselock; + } + #endif + } + } + + // Do the visible windows next + while ((RedrawPending & DOREDRAW_VISIBLES)) { + RedrawPending &= ~DOREDRAW_VISIBLES; // Catch new requests + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) continue; // Do the redraw @@ -243,23 +270,28 @@ void _gwinFlushRedraws(bool_t doWait) { #endif // Postpone further redraws (if there are any and the options are set right) - #if GFX_USE_GTIMER && !GWIN_LONG_REDRAW - if (!doWait) { + #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP + if (how == REDRAW_NOWAIT) { while((gh = gwinGetNextWindow(gh))) { - if ((gh->flags & GWIN_FLG_NEEDREDRAW)) { - gtimerJab(&RedrawTimer); - RedrawPending = TRUE; + if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) { + RedrawPending |= DOREDRAW_VISIBLES; + TriggerRedraw(); break; } } - break; + goto releaselock; } #endif } } + #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP + releaselock: + #endif + // Release the lock - gfxSemSignal(&gwinsem); + if (how == REDRAW_WAIT || how == REDRAW_NOWAIT) + gfxSemSignal(&gwinsem); } void _gwinUpdate(GHandle gh) { @@ -269,7 +301,7 @@ void _gwinUpdate(GHandle gh) { // Mark for redraw gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending = TRUE; + RedrawPending |= DOREDRAW_VISIBLES; // Asynchronous redraw TriggerRedraw(); @@ -304,22 +336,7 @@ void _gwinDrawEnd(GHandle gh) { #endif // Look for something to redraw - while(RedrawPending) { - RedrawPending = FALSE; - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if (!(gh->flags & GWIN_FLG_NEEDREDRAW)) - continue; - - // Do the redraw - #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - _GWINwm->vmt->Redraw(gh); - gdispGUnsetClip(gh->display); - #else - _GWINwm->vmt->Redraw(gh); - #endif - } - } + _gwinFlushRedraws(REDRAW_INSESSION); // Release the lock gfxSemSignal(&gwinsem); @@ -365,10 +382,10 @@ void gwinRedraw(GHandle gh) { // Mark for redraw gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending = TRUE; + RedrawPending |= DOREDRAW_VISIBLES; // Synchronous redraw - _gwinFlushRedraws(TRUE); + _gwinFlushRedraws(REDRAW_WAIT); } #if GWIN_NEED_CONTAINERS @@ -386,7 +403,7 @@ void gwinRedraw(GHandle gh) { } // Mark for redraw - RedrawPending = TRUE; + RedrawPending |= DOREDRAW_VISIBLES; TriggerRedraw(); } } else { @@ -404,7 +421,7 @@ void gwinRedraw(GHandle gh) { } // Mark for redraw - no need to redraw children - RedrawPending = TRUE; + RedrawPending |= DOREDRAW_INVISIBLES; TriggerRedraw(); } } @@ -414,14 +431,14 @@ void gwinRedraw(GHandle gh) { if (visible) { if (!(gh->flags & GWIN_FLG_VISIBLE)) { gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); - RedrawPending = TRUE; + RedrawPending |= DOREDRAW_VISIBLES; TriggerRedraw(); } } else { if ((gh->flags & GWIN_FLG_VISIBLE)) { gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); - RedrawPending = TRUE; + RedrawPending |= DOREDRAW_INVISIBLES; TriggerRedraw(); } } @@ -441,14 +458,9 @@ void gwinRedraw(GHandle gh) { for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & (GWIN_FLG_SYSENABLED|GWIN_FLG_ENABLED)) == GWIN_FLG_ENABLED && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { gh->flags |= GWIN_FLG_SYSENABLED; // Fix it - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { // Mark for redraw - gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending = TRUE; - } + _gwinUpdate(gh); } } - if (RedrawPending) - TriggerRedraw(); } } else { gh->flags &= ~GWIN_FLG_ENABLED; @@ -459,14 +471,9 @@ void gwinRedraw(GHandle gh) { for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & GWIN_FLG_SYSENABLED) && (!(gh->flags & GWIN_FLG_ENABLED) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSENABLED)))) { gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { // Mark for redraw - gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending = TRUE; - } + _gwinUpdate(gh); } } - if (RedrawPending) - TriggerRedraw(); } } } @@ -701,7 +708,7 @@ static void WM_Size(GHandle gh, coord_t w, coord_t h) { } else { // We need to make this window invisible and ensure that has been drawn gwinSetVisible(gh, FALSE); - _gwinFlushRedraws(TRUE); + _gwinFlushRedraws(REDRAW_WAIT); // Resize gh->width = w; gh->height = h; @@ -771,7 +778,7 @@ static void WM_Move(GHandle gh, coord_t x, coord_t y) { if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { // We need to make this window invisible and ensure that has been drawn gwinSetVisible(gh, FALSE); - _gwinFlushRedraws(TRUE); + _gwinFlushRedraws(REDRAW_WAIT); // Do the move v = gh->x; gh->x = x; x = v; diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h index 262484ee..b1b58a68 100644 --- a/src/gwin/sys_options.h +++ b/src/gwin/sys_options.h @@ -136,6 +136,33 @@ #ifndef GWIN_FLAT_STYLING #define GWIN_FLAT_STYLING FALSE #endif + /** + * @brief Don't use a timer for redrawing windows + * @details Defaults to FALSE + * @note Normally windows and widgets are redrawn on a timer. Setting this + * option causes them to be redrawn immediately. Note that this can + * cause extended blocking times on events and saves little code. + * @note If GWIN_NEED_WINDOWMANAGER is FALSE then this setting is ignored + * as redrawing always occurs immediately. + */ + #ifndef GWIN_REDRAW_IMMEDIATE + #define GWIN_REDRAW_IMMEDIATE FALSE + #endif + /** + * @brief Redraw all windows in a single operation + * @details Defaults to FALSE + * @note Windows are normally redraw one per gtimer cycle. + * Setting this option causes all windows to be redrawn in + * a single gtimer cycle. Note that this can + * cause extended blocking times on the timer thread but may + * speed up redraw slightly. + * @note This is only relevant if GWIN_REDRAW_IMMEDIATE is FALSE. + * Everything always gets redrawn in a single operation if + * GWIN_REDRAW_IMMEDIATE is TRUE. + */ + #ifndef GWIN_REDRAW_SINGLEOP + #define GWIN_REDRAW_SINGLEOP FALSE + #endif /** * @brief Buttons should not insist the mouse is over the button on mouse release * @details Defaults to FALSE diff --git a/src/gwin/sys_rules.h b/src/gwin/sys_rules.h index ddfea3aa..911379af 100644 --- a/src/gwin/sys_rules.h +++ b/src/gwin/sys_rules.h @@ -91,6 +91,13 @@ #define GFX_USE_GQUEUE TRUE #define GQUEUE_NEED_ASYNC TRUE #endif + #if !GFX_USE_GTIMER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GFX_USE_GTIMER is required if GWIN_NEED_WINDOWMANAGER is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif #endif // Rules for individual objects @@ -116,13 +123,6 @@ #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." #endif #endif - #if GWIN_NEED_PROGRESSBAR - #if GWIN_PROGRESSBAR_AUTO - #if !GFX_USE_GTIMER - #error "GWIN: GFX_USE_GTIMER is required if GWIN_PROGRESSBAR_AUTO is TRUE." - #endif - #endif - #endif #endif #endif /* _GWIN_RULES_H */