1033 lines
28 KiB
C
1033 lines
28 KiB
C
/*
|
|
* This file is subject to the terms of the GFX License. If a copy of
|
|
* the license was not distributed with this file, you can obtain one at:
|
|
*
|
|
* http://ugfx.org/license.html
|
|
*/
|
|
|
|
/**
|
|
* @file src/gwin/gwin_wm.c
|
|
* @brief GWIN sub-system window manager code
|
|
*/
|
|
|
|
#include "../../gfx.h"
|
|
|
|
#if GFX_USE_GWIN && !GWIN_NEED_WINDOWMANAGER
|
|
/**
|
|
* A really nasty default implementation for the simplest of systems
|
|
*/
|
|
|
|
|
|
#include "gwin_class.h"
|
|
|
|
// Needed if there is no window manager
|
|
#define MIN_WIN_WIDTH 1
|
|
#define MIN_WIN_HEIGHT 1
|
|
|
|
static gfxMutex gmutex;
|
|
|
|
void _gwmInit(void) {
|
|
gfxMutexInit(&gmutex);
|
|
}
|
|
|
|
void _gwmDeinit(void) {
|
|
gfxMutexDestroy(&gmutex);
|
|
}
|
|
|
|
bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) {
|
|
gh->x = gh->y = gh->width = gh->height = 0;
|
|
gwinMove(gh, pInit->x, pInit->y);
|
|
gwinResize(gh, pInit->width, pInit->height);
|
|
return TRUE;
|
|
}
|
|
|
|
void _gwinFlushRedraws(GRedrawMethod how) {
|
|
(void) how;
|
|
|
|
// We are always flushed
|
|
}
|
|
|
|
|
|
#if GDISP_NEED_CLIP
|
|
static void getLock(GHandle gh) {
|
|
gfxMutexEnter(&gmutex);
|
|
gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
|
|
}
|
|
static void exitLock(GHandle gh) {
|
|
gdispGUnsetClip(gh->display);
|
|
gfxMutexExit(&gmutex);
|
|
}
|
|
#else
|
|
#define getLock(gh) gfxMutexEnter(&gmutex)
|
|
#define exitLock(gh) gfxMutexExit(&gmutex)
|
|
#endif
|
|
|
|
void _gwinUpdate(GHandle gh) {
|
|
if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
|
|
if (gh->vmt->Redraw) {
|
|
getLock(gh);
|
|
gh->vmt->Redraw(gh);
|
|
exitLock(gh);
|
|
} else if ((gh->flags & GWIN_FLG_BGREDRAW)) {
|
|
getLock(gh);
|
|
gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
|
|
exitLock(gh);
|
|
if (gh->vmt->AfterClear)
|
|
gh->vmt->AfterClear(gh);
|
|
}
|
|
} else if ((gh->flags & GWIN_FLG_BGREDRAW)) {
|
|
getLock(gh);
|
|
gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor());
|
|
exitLock(gh);
|
|
}
|
|
gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
|
|
}
|
|
|
|
bool_t _gwinDrawStart(GHandle gh) {
|
|
if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
|
|
return FALSE;
|
|
|
|
getLock(gh);
|
|
return TRUE;
|
|
}
|
|
|
|
void _gwinDrawEnd(GHandle gh) {
|
|
(void) gh;
|
|
exitLock(gh);
|
|
}
|
|
|
|
void gwinSetVisible(GHandle gh, bool_t visible) {
|
|
if (visible) {
|
|
if (!(gh->flags & GWIN_FLG_VISIBLE)) {
|
|
gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_BGREDRAW);
|
|
_gwinUpdate(gh);
|
|
}
|
|
} else {
|
|
if ((gh->flags & GWIN_FLG_VISIBLE)) {
|
|
gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE);
|
|
gh->flags |= GWIN_FLG_BGREDRAW;
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
}
|
|
|
|
void gwinSetEnabled(GHandle gh, bool_t enabled) {
|
|
if (enabled) {
|
|
if (!(gh->flags & GWIN_FLG_ENABLED)) {
|
|
gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
|
|
_gwinUpdate(gh);
|
|
}
|
|
} else {
|
|
if ((gh->flags & GWIN_FLG_ENABLED)) {
|
|
gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
}
|
|
|
|
void gwinMove(GHandle gh, coord_t x, coord_t y) {
|
|
gh->x = x; gh->y = y;
|
|
if (gh->x < 0) gh->x = 0;
|
|
if (gh->y < 0) gh->y = 0;
|
|
if (gh->x > gdispGGetWidth(gh->display)-MIN_WIN_WIDTH) gh->x = gdispGGetWidth(gh->display)-MIN_WIN_WIDTH;
|
|
if (gh->y > gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT) gh->y = gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT;
|
|
if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x;
|
|
if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y;
|
|
_gwinUpdate(gh);
|
|
}
|
|
|
|
void gwinResize(GHandle gh, coord_t width, coord_t height) {
|
|
gh->width = width; gh->height = height;
|
|
if (gh->width < MIN_WIN_WIDTH) { gh->width = MIN_WIN_WIDTH; }
|
|
if (gh->height < MIN_WIN_HEIGHT) { gh->height = MIN_WIN_HEIGHT; }
|
|
if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x;
|
|
if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y;
|
|
_gwinUpdate(gh);
|
|
}
|
|
|
|
void gwinRedraw(GHandle gh) {
|
|
_gwinUpdate(gh);
|
|
}
|
|
#endif
|
|
|
|
#if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER
|
|
|
|
#include "gwin_class.h"
|
|
|
|
/*-----------------------------------------------
|
|
* Data
|
|
*-----------------------------------------------*/
|
|
|
|
// The default window manager
|
|
extern const GWindowManager GNullWindowManager;
|
|
GWindowManager * _GWINwm;
|
|
bool_t _gwinFlashState;
|
|
static gfxSem gwinsem;
|
|
static gfxQueueASync _GWINList;
|
|
#if GWIN_NEED_FLASHING
|
|
static GTimer FlashTimer;
|
|
#endif
|
|
#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
|
|
#define DOREDRAW_FLASHRUNNING 0x04
|
|
|
|
|
|
/*-----------------------------------------------
|
|
* Window Routines
|
|
*-----------------------------------------------*/
|
|
|
|
void _gwmInit(void)
|
|
{
|
|
gfxSemInit(&gwinsem, 1, 1);
|
|
gfxQueueASyncInit(&_GWINList);
|
|
#if GWIN_NEED_FLASHING
|
|
gtimerInit(&FlashTimer);
|
|
#endif
|
|
#if !GWIN_REDRAW_IMMEDIATE
|
|
gtimerInit(&RedrawTimer);
|
|
gtimerStart(&RedrawTimer, RedrawTimerFn, 0, TRUE, TIME_INFINITE);
|
|
#endif
|
|
_GWINwm = (GWindowManager *)&GNullWindowManager;
|
|
_GWINwm->vmt->Init();
|
|
}
|
|
|
|
void _gwmDeinit(void)
|
|
{
|
|
GHandle gh;
|
|
|
|
while((gh = gwinGetNextWindow(0)))
|
|
gwinDestroy(gh);
|
|
|
|
_GWINwm->vmt->DeInit();
|
|
#if !GWIN_REDRAW_IMMEDIATE
|
|
gtimerDeinit(&RedrawTimer);
|
|
#endif
|
|
gfxQueueASyncDeinit(&_GWINList);
|
|
gfxSemDestroy(&gwinsem);
|
|
}
|
|
|
|
#if GWIN_REDRAW_IMMEDIATE
|
|
#define TriggerRedraw(void) _gwinFlushRedraws(REDRAW_NOWAIT);
|
|
#else
|
|
#define TriggerRedraw() gtimerJab(&RedrawTimer);
|
|
|
|
static void RedrawTimerFn(void *param) {
|
|
(void) param;
|
|
_gwinFlushRedraws(REDRAW_NOWAIT);
|
|
}
|
|
#endif
|
|
|
|
void _gwinFlushRedraws(GRedrawMethod how) {
|
|
GHandle gh;
|
|
|
|
// Do we really need to do anything?
|
|
if (!RedrawPending)
|
|
return;
|
|
|
|
// Obtain the drawing lock
|
|
if (how == REDRAW_WAIT)
|
|
gfxSemWait(&gwinsem, TIME_INFINITE);
|
|
else if (how == REDRAW_NOWAIT && !gfxSemWait(&gwinsem, TIME_IMMEDIATE))
|
|
// Someone is drawing - They will do the redraw when they are finished
|
|
return;
|
|
|
|
// 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|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
|
|
#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 there are any and the options are set right)
|
|
#if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP
|
|
if (how == REDRAW_NOWAIT) {
|
|
while((gh = gwinGetNextWindow(gh))) {
|
|
if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) {
|
|
RedrawPending |= DOREDRAW_VISIBLES;
|
|
TriggerRedraw();
|
|
break;
|
|
}
|
|
}
|
|
goto releaselock;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP
|
|
releaselock:
|
|
#endif
|
|
|
|
// Release the lock
|
|
if (how == REDRAW_WAIT || how == REDRAW_NOWAIT)
|
|
gfxSemSignal(&gwinsem);
|
|
}
|
|
|
|
void _gwinUpdate(GHandle gh) {
|
|
// Only redraw if visible
|
|
if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
|
|
return;
|
|
|
|
// Mark for redraw
|
|
gh->flags |= GWIN_FLG_NEEDREDRAW;
|
|
RedrawPending |= DOREDRAW_VISIBLES;
|
|
|
|
// Asynchronous redraw
|
|
TriggerRedraw();
|
|
}
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
void _gwinRippleVisibility(void) {
|
|
GHandle gh;
|
|
|
|
// Check each window's visibility is consistent with its parents
|
|
for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
|
|
switch(gh->flags & (GWIN_FLG_SYSVISIBLE|GWIN_FLG_VISIBLE)) {
|
|
case GWIN_FLG_VISIBLE:
|
|
if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) {
|
|
// We have been made visible
|
|
gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
|
|
|
|
// Do we want to grab the focus
|
|
_gwinFixFocus(gh);
|
|
|
|
RedrawPending |= DOREDRAW_VISIBLES;
|
|
}
|
|
break;
|
|
case (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE):
|
|
if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))
|
|
break;
|
|
|
|
// Parent has been made invisible
|
|
gh->flags &= ~GWIN_FLG_SYSVISIBLE;
|
|
|
|
// No focus for us anymore
|
|
_gwinFixFocus(gh);
|
|
|
|
break;
|
|
case GWIN_FLG_SYSVISIBLE:
|
|
// We have been made invisible
|
|
gh->flags &= ~GWIN_FLG_SYSVISIBLE;
|
|
if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) {
|
|
// The parent is visible so we must clear the area we took
|
|
gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
|
|
|
|
// No focus for us anymore
|
|
_gwinFixFocus(gh);
|
|
|
|
RedrawPending |= DOREDRAW_INVISIBLES;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool_t _gwinDrawStart(GHandle gh) {
|
|
// This test should occur inside the lock. We do this
|
|
// here as well as an early out (more efficient).
|
|
if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
|
|
return FALSE;
|
|
|
|
// Obtain the drawing lock
|
|
gfxSemWait(&gwinsem, TIME_INFINITE);
|
|
|
|
// Re-test visibility as we may have waited a while
|
|
if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) {
|
|
_gwinDrawEnd(gh);
|
|
return FALSE;
|
|
}
|
|
|
|
// OK - we are ready to draw.
|
|
#if GDISP_NEED_CLIP
|
|
gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
void _gwinDrawEnd(GHandle gh) {
|
|
// Ensure there is no clip set
|
|
#if GDISP_NEED_CLIP
|
|
gdispGUnsetClip(gh->display);
|
|
#endif
|
|
|
|
// Look for something to redraw
|
|
_gwinFlushRedraws(REDRAW_INSESSION);
|
|
|
|
// Release the lock
|
|
gfxSemSignal(&gwinsem);
|
|
}
|
|
|
|
bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) {
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Save the parent
|
|
gh->parent = pInit->parent;
|
|
|
|
// Ensure the display is consistent with any parents
|
|
if (gh->parent && (!(gh->parent->flags & GWIN_FLG_CONTAINER) || gh->display != gh->parent->display))
|
|
return FALSE;
|
|
#endif
|
|
|
|
// Add to the window manager
|
|
if (!_GWINwm->vmt->Add(gh, pInit))
|
|
return FALSE;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Notify the parent it has been added
|
|
if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd)
|
|
((gcontainerVMT *)gh->parent->vmt)->NotifyAdd(gh->parent, gh);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gwinSetWindowManager(struct GWindowManager *gwm) {
|
|
if (!gwm)
|
|
gwm = (GWindowManager *)&GNullWindowManager;
|
|
if (_GWINwm != gwm) {
|
|
_GWINwm->vmt->DeInit();
|
|
_GWINwm = gwm;
|
|
_GWINwm->vmt->Init();
|
|
}
|
|
}
|
|
|
|
void gwinRedraw(GHandle gh) {
|
|
// Only redraw if visible
|
|
if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
|
|
return;
|
|
|
|
// Mark for redraw
|
|
gh->flags |= GWIN_FLG_NEEDREDRAW;
|
|
RedrawPending |= DOREDRAW_VISIBLES;
|
|
|
|
// Synchronous redraw
|
|
_gwinFlushRedraws(REDRAW_WAIT);
|
|
}
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
void gwinSetVisible(GHandle gh, bool_t visible) {
|
|
if (visible) {
|
|
// Mark us as visible
|
|
gh->flags |= GWIN_FLG_VISIBLE;
|
|
} else {
|
|
// Mark us as not visible
|
|
gh->flags &= ~GWIN_FLG_VISIBLE;
|
|
}
|
|
|
|
// Fix everything up
|
|
_gwinRippleVisibility();
|
|
if (RedrawPending)
|
|
TriggerRedraw();
|
|
}
|
|
#else
|
|
void gwinSetVisible(GHandle gh, bool_t visible) {
|
|
if (visible) {
|
|
if (!(gh->flags & GWIN_FLG_VISIBLE)) {
|
|
gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
|
|
|
|
// Do we want to grab the focus
|
|
_gwinFixFocus(gh);
|
|
|
|
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);
|
|
|
|
// No focus for us anymore
|
|
_gwinFixFocus(gh);
|
|
|
|
RedrawPending |= DOREDRAW_INVISIBLES;
|
|
TriggerRedraw();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// These two sub-functions set/clear system enable recursively.
|
|
void gwinSetEnabled(GHandle gh, bool_t enabled) {
|
|
if (enabled) {
|
|
// Mark us as enabled
|
|
gh->flags |= GWIN_FLG_ENABLED;
|
|
|
|
// Do we change our real enabled state
|
|
if (!(gh->flags & GWIN_FLG_SYSENABLED) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) {
|
|
// Check each window's enabled state is consistent with its parents
|
|
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
|
|
|
|
// Do we want to grab the focus
|
|
_gwinFixFocus(gh);
|
|
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
gh->flags &= ~GWIN_FLG_ENABLED;
|
|
|
|
// Do we need to change our real enabled state
|
|
if ((gh->flags & GWIN_FLG_SYSENABLED)) {
|
|
// Check each window's visibility is consistent with its parents
|
|
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
|
|
|
|
// No focus for us anymore
|
|
_gwinFixFocus(gh);
|
|
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
void gwinSetEnabled(GHandle gh, bool_t enabled) {
|
|
if (enabled) {
|
|
if (!(gh->flags & GWIN_FLG_ENABLED)) {
|
|
gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
|
|
|
|
// Do we want to grab the focus
|
|
_gwinFixFocus(gh);
|
|
|
|
_gwinUpdate(gh);
|
|
}
|
|
} else {
|
|
if ((gh->flags & GWIN_FLG_ENABLED)) {
|
|
gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
|
|
|
|
// No focus for us anymore
|
|
_gwinFixFocus(gh);
|
|
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void gwinMove(GHandle gh, coord_t x, coord_t y) {
|
|
_GWINwm->vmt->Move(gh, x, y);
|
|
}
|
|
|
|
void gwinResize(GHandle gh, coord_t width, coord_t height) {
|
|
_GWINwm->vmt->Size(gh, width, height);
|
|
}
|
|
|
|
void gwinSetMinMax(GHandle gh, GWindowMinMax minmax) {
|
|
_GWINwm->vmt->MinMax(gh, minmax);
|
|
}
|
|
|
|
void gwinRaise(GHandle gh) {
|
|
_GWINwm->vmt->Raise(gh);
|
|
}
|
|
|
|
GWindowMinMax gwinGetMinMax(GHandle gh) {
|
|
if (gh->flags & GWIN_FLG_MINIMIZED)
|
|
return GWIN_MINIMIZE;
|
|
if (gh->flags & GWIN_FLG_MAXIMIZED)
|
|
return GWIN_MAXIMIZE;
|
|
return GWIN_NORMAL;
|
|
}
|
|
|
|
void gwinRedrawDisplay(GDisplay *g, bool_t preserve) {
|
|
GHandle gh;
|
|
|
|
for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
|
|
|
|
// Skip if it is for a different display
|
|
if (g && gh->display != g)
|
|
continue;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Skip if it is not a top level window (parents internally take care of their children)
|
|
if (gh->parent)
|
|
continue;
|
|
#endif
|
|
|
|
// Only visible windows are to be redrawn
|
|
if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
|
|
continue;
|
|
|
|
if (!preserve)
|
|
gh->flags |= GWIN_FLG_BGREDRAW;
|
|
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
|
|
GHandle gwinGetNextWindow(GHandle gh) {
|
|
return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList);
|
|
}
|
|
|
|
#if GWIN_NEED_FLASHING
|
|
static void FlashTimerFn(void *param) {
|
|
GHandle gh;
|
|
(void) param;
|
|
|
|
// Assume we will be stopping
|
|
RedrawPending &= ~DOREDRAW_FLASHRUNNING;
|
|
|
|
// Swap the flash state
|
|
_gwinFlashState = !_gwinFlashState;
|
|
|
|
// Redraw all flashing windows
|
|
for(gh = (GHandle)gfxQueueASyncPeek(&_GWINList); gh; gh = (GHandle)gfxQueueASyncNext(&gh->wmq)) {
|
|
if ((gh->flags & GWIN_FLG_FLASHING)) {
|
|
RedrawPending |= DOREDRAW_FLASHRUNNING;
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
|
|
// Do we have no flashers left?
|
|
if (!(RedrawPending & DOREDRAW_FLASHRUNNING))
|
|
gtimerStop(&FlashTimer);
|
|
}
|
|
|
|
void gwinSetFlashing(GHandle gh, bool_t flash) {
|
|
|
|
// Start flashing?
|
|
if (flash) {
|
|
gh->flags |= GWIN_FLG_FLASHING; // A redraw will occur on the next flash period.
|
|
|
|
// Start the flash timer if needed
|
|
if (!(RedrawPending & DOREDRAW_FLASHRUNNING)) {
|
|
RedrawPending |= DOREDRAW_FLASHRUNNING;
|
|
|
|
// Ensure we start the timer with flash bit on
|
|
_gwinFlashState = FALSE;
|
|
FlashTimerFn(0); // First flash
|
|
gtimerStart(&FlashTimer, FlashTimerFn, 0, TRUE, GWIN_FLASHING_PERIOD); // Subsequent flashes
|
|
}
|
|
|
|
// Stop flashing?
|
|
} else if ((gh->flags & GWIN_FLG_FLASHING)) {
|
|
gh->flags &= ~GWIN_FLG_FLASHING;
|
|
// We need to manually redraw as the timer is now turned off for this window
|
|
_gwinUpdate(gh);
|
|
}
|
|
}
|
|
|
|
#if GWIN_NEED_WIDGET
|
|
const GColorSet *_gwinGetFlashedColor(GWidgetObject *gw, const GColorSet *pcol, bool_t flashOffState) {
|
|
// Does the flashing state affect the current colors?
|
|
if ((gw->g.flags & GWIN_FLG_FLASHING) && _gwinFlashState) {
|
|
|
|
// For a pressed state show an unpressed state
|
|
if (pcol == &gw->pstyle->pressed)
|
|
pcol = &gw->pstyle->enabled;
|
|
|
|
// For a non-pressed state (if allowed) show a pressed state
|
|
else if (flashOffState && pcol == &gw->pstyle->enabled)
|
|
pcol = &gw->pstyle->pressed;
|
|
}
|
|
return pcol;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/*-----------------------------------------------
|
|
* "Null" Window Manager Routines
|
|
*-----------------------------------------------*/
|
|
|
|
// This is a parent reveal operation
|
|
#define GWIN_FLG_PARENTREVEAL (GWIN_FIRST_WM_FLAG << 0)
|
|
|
|
// Minimum dimensions
|
|
#define MIN_WIN_WIDTH 3
|
|
#define MIN_WIN_HEIGHT 3
|
|
|
|
|
|
static void WM_Init(void);
|
|
static void WM_DeInit(void);
|
|
static bool_t WM_Add(GHandle gh, const GWindowInit *pInit);
|
|
static void WM_Delete(GHandle gh);
|
|
static void WM_Redraw(GHandle gh);
|
|
static void WM_Size(GHandle gh, coord_t w, coord_t h);
|
|
static void WM_Move(GHandle gh, coord_t x, coord_t y);
|
|
static void WM_Raise(GHandle gh);
|
|
static void WM_MinMax(GHandle gh, GWindowMinMax minmax);
|
|
|
|
static const gwmVMT GNullWindowManagerVMT = {
|
|
WM_Init,
|
|
WM_DeInit,
|
|
WM_Add,
|
|
WM_Delete,
|
|
WM_Redraw,
|
|
WM_Size,
|
|
WM_Move,
|
|
WM_Raise,
|
|
WM_MinMax,
|
|
};
|
|
|
|
const GWindowManager GNullWindowManager = {
|
|
&GNullWindowManagerVMT,
|
|
};
|
|
|
|
static void WM_Init(void) {
|
|
// We don't need to do anything here.
|
|
// A full window manager would move the windows around, add borders etc
|
|
|
|
// clear the screen
|
|
// cycle through the windows already defined displaying them
|
|
// or cut all the window areas out of the screen and clear the remainder
|
|
}
|
|
|
|
static void WM_DeInit(void) {
|
|
// We don't need to do anything here.
|
|
// A full window manager would remove any borders etc
|
|
}
|
|
|
|
static bool_t WM_Add(GHandle gh, const GWindowInit *pInit) {
|
|
// Note the window will not currently be marked as visible
|
|
|
|
// Put it on the end of the queue
|
|
gfxQueueASyncPut(&_GWINList, &gh->wmq);
|
|
|
|
// Make sure the size/position is valid - prefer position over size.
|
|
gh->width = MIN_WIN_WIDTH; gh->height = MIN_WIN_HEIGHT;
|
|
gh->x = gh->y = 0;
|
|
WM_Move(gh, pInit->x, pInit->y);
|
|
WM_Size(gh, pInit->width, pInit->height);
|
|
return TRUE;
|
|
}
|
|
|
|
static void WM_Delete(GHandle gh) {
|
|
// Remove it from the window list
|
|
gfxSemWait(&gwinsem, TIME_INFINITE);
|
|
gfxQueueASyncRemove(&_GWINList, &gh->wmq);
|
|
gfxSemSignal(&gwinsem);
|
|
}
|
|
|
|
static void WM_Redraw(GHandle gh) {
|
|
uint32_t flags;
|
|
|
|
flags = gh->flags;
|
|
gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
redo_redraw:
|
|
#endif
|
|
if ((flags & GWIN_FLG_SYSVISIBLE)) {
|
|
if (gh->vmt->Redraw)
|
|
gh->vmt->Redraw(gh);
|
|
else if ((flags & GWIN_FLG_BGREDRAW)) {
|
|
// We can't redraw but we want full coverage so just clear the area
|
|
gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
|
|
|
|
// Only do an after clear if this is not a parent reveal
|
|
if (!(flags & GWIN_FLG_PARENTREVEAL) && gh->vmt->AfterClear)
|
|
gh->vmt->AfterClear(gh);
|
|
}
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// If this is container but not a parent reveal, mark any visible children for redraw
|
|
// We redraw our children here as we have overwritten them in redrawing the parent
|
|
// as GDISP/GWIN doesn't support complex clipping regions.
|
|
if ((flags & (GWIN_FLG_CONTAINER|GWIN_FLG_PARENTREVEAL)) == GWIN_FLG_CONTAINER) {
|
|
|
|
// Container redraw is done
|
|
|
|
for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh))
|
|
_gwinUpdate(gh);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
if ((flags & GWIN_FLG_BGREDRAW)) {
|
|
GHandle gx;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
if (gh->parent) {
|
|
// Child redraw is done
|
|
|
|
// Get the parent to redraw the area
|
|
gh = gh->parent;
|
|
|
|
// The parent is already marked for redraw - don't do it now.
|
|
if ((gh->flags & GWIN_FLG_NEEDREDRAW))
|
|
return;
|
|
|
|
// Use the existing clipping region and redraw now
|
|
gh->flags |= (GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);
|
|
goto redo_redraw;
|
|
}
|
|
#endif
|
|
|
|
// Clear the area to the background color
|
|
gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor());
|
|
|
|
// Now loop over all windows looking for overlaps. Redraw them if they overlap the newly exposed area.
|
|
for(gx = gwinGetNextWindow(0); gx; gx = gwinGetNextWindow(gx)) {
|
|
if ((gx->flags & GWIN_FLG_SYSVISIBLE)
|
|
&& gx->display == gh->display
|
|
&& gx->x < gh->x+gh->width && gx->y < gh->y+gh->height && gx->x+gx->width >= gh->x && gx->y+gx->height >= gh->y) {
|
|
if (gx->vmt->Redraw)
|
|
gx->vmt->Redraw(gx);
|
|
else
|
|
// We can't redraw this window but we want full coverage so just clear the area
|
|
gdispGFillArea(gx->display, gx->x, gx->y, gx->width, gx->height, gx->bgcolor);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
static void WM_Size(GHandle gh, coord_t w, coord_t h) {
|
|
coord_t v;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
if (gh->parent) {
|
|
// Clip to the container
|
|
v = gh->parent->x + gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent);
|
|
if (gh->x+w > v) w = v - gh->x;
|
|
v = gh->parent->y + gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent);
|
|
if (gh->y+h > v) h = v - gh->y;
|
|
}
|
|
#endif
|
|
|
|
// Clip to the screen
|
|
v = gdispGGetWidth(gh->display);
|
|
if (gh->x+w > v) w = v - gh->x;
|
|
v = gdispGGetHeight(gh->display);
|
|
if (gh->y+h > v) h = v - gh->y;
|
|
|
|
// Give it a minimum size
|
|
if (w < MIN_WIN_WIDTH) w = MIN_WIN_WIDTH;
|
|
if (h < MIN_WIN_HEIGHT) h = MIN_WIN_HEIGHT;
|
|
|
|
// If there has been no resize just exit
|
|
if (gh->width == w && gh->height == h)
|
|
return;
|
|
|
|
// Set the new size and redraw
|
|
if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
|
|
if (w >= gh->width && h >= gh->height) {
|
|
|
|
// The new size is larger - just redraw
|
|
gh->width = w; gh->height = h;
|
|
_gwinUpdate(gh);
|
|
|
|
} else {
|
|
// We need to make this window invisible and ensure that has been drawn
|
|
gwinSetVisible(gh, FALSE);
|
|
_gwinFlushRedraws(REDRAW_WAIT);
|
|
|
|
// Resize
|
|
gh->width = w; gh->height = h;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Any children outside the new area need to be moved
|
|
if ((gh->flags & GWIN_FLG_CONTAINER)) {
|
|
GHandle child;
|
|
|
|
// Move to their old relative location. THe WM_Move() will adjust as necessary
|
|
for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
|
|
WM_Move(child, child->x-gh->x-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-gh->y-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
|
|
}
|
|
#endif
|
|
|
|
// Mark it visible again in its new location
|
|
gwinSetVisible(gh, TRUE);
|
|
}
|
|
} else {
|
|
gh->width = w; gh->height = h;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Any children outside the new area need to be moved
|
|
if ((gh->flags & GWIN_FLG_CONTAINER)) {
|
|
GHandle child;
|
|
|
|
// Move to their old relative location. THe WM_Move() will adjust as necessary
|
|
for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
|
|
WM_Move(child, child->x-gh->x-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-gh->y-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void WM_Move(GHandle gh, coord_t x, coord_t y) {
|
|
coord_t u, v;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
if (gh->parent) {
|
|
// Clip to the parent size
|
|
u = gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent);
|
|
v = gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent);
|
|
} else
|
|
#endif
|
|
{
|
|
// Clip to the screen
|
|
u = gdispGGetWidth(gh->display);
|
|
v = gdispGGetHeight(gh->display);
|
|
}
|
|
|
|
// Make sure we are positioned in the appropriate area
|
|
if (x+gh->width > u) x = u-gh->width;
|
|
if (x < 0) x = 0;
|
|
if (y+gh->height > v) y = v-gh->height;
|
|
if (y < 0) y = 0;
|
|
|
|
// Make sure we don't overflow the appropriate area
|
|
u -= x;
|
|
v -= y;
|
|
if (gh->width < u) u = gh->width;
|
|
if (gh->height < v) v = gh->height;
|
|
if (u != gh->width || v != gh->height)
|
|
WM_Size(gh, u, v);
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
if (gh->parent) {
|
|
// Convert to a screen relative position
|
|
x += gh->parent->x + ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent);
|
|
y += gh->parent->y + ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent);
|
|
}
|
|
#endif
|
|
|
|
// If there has been no move just exit
|
|
if (gh->x == x && gh->y == y)
|
|
return;
|
|
|
|
// Clear the old area and then redraw
|
|
if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
|
|
// We need to make this window invisible and ensure that has been drawn
|
|
gwinSetVisible(gh, FALSE);
|
|
_gwinFlushRedraws(REDRAW_WAIT);
|
|
|
|
// Do the move
|
|
u = gh->x; gh->x = x;
|
|
v = gh->y; gh->y = y;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Any children need to be moved
|
|
if ((gh->flags & GWIN_FLG_CONTAINER)) {
|
|
GHandle child;
|
|
|
|
// Move to their old relative location. THe WM_Move() will adjust as necessary
|
|
for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
|
|
WM_Move(child, child->x-u-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-v-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
|
|
}
|
|
#endif
|
|
|
|
gwinSetVisible(gh, TRUE);
|
|
} else {
|
|
u = gh->x; gh->x = x;
|
|
v = gh->y; gh->y = y;
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Any children need to be moved
|
|
if ((gh->flags & GWIN_FLG_CONTAINER)) {
|
|
GHandle child;
|
|
|
|
// Move to their old relative location. THe WM_Move() will adjust as necessary
|
|
for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
|
|
WM_Move(child, child->x-u-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-v-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void WM_MinMax(GHandle gh, GWindowMinMax minmax) {
|
|
(void)gh; (void) minmax;
|
|
// We don't support minimising, maximising or restoring
|
|
}
|
|
|
|
static void WM_Raise(GHandle gh) {
|
|
// Take it off the list and then put it back on top
|
|
// The order of the list then reflects the z-order.
|
|
|
|
gfxSemWait(&gwinsem, TIME_INFINITE);
|
|
|
|
gfxQueueASyncRemove(&_GWINList, &gh->wmq);
|
|
gfxQueueASyncPut(&_GWINList, &gh->wmq);
|
|
|
|
#if GWIN_NEED_CONTAINERS
|
|
// Any children need to be raised too
|
|
if ((gh->flags & GWIN_FLG_CONTAINER)) {
|
|
GHandle gx;
|
|
GHandle child;
|
|
bool_t restart;
|
|
|
|
// Raise the children too
|
|
// Note: Children can also have their own children so after each move we have to start again.
|
|
for (gx = gwinGetNextWindow(0); gx; gx = gwinGetNextWindow(gx)) {
|
|
if ((gx->flags & GWIN_FLG_CONTAINER)) {
|
|
restart = FALSE;
|
|
for (child = gwinGetNextWindow(0); child && child != gx; child = gwinGetNextWindow(child)) {
|
|
if (child->parent == gx) {
|
|
// Oops - this child is behind its parent. Move it to the front.
|
|
gfxQueueASyncRemove(&_GWINList, &child->wmq);
|
|
gfxQueueASyncPut(&_GWINList, &child->wmq);
|
|
|
|
// Restart at the front of the list for this parent container as we have moved this child
|
|
// to the end of the list. We also need to restart everything once this container is done.
|
|
child = 0;
|
|
restart = TRUE;
|
|
}
|
|
}
|
|
if (restart)
|
|
gx = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
gfxSemSignal(&gwinsem);
|
|
|
|
// Redraw the window
|
|
_gwinUpdate(gh);
|
|
}
|
|
|
|
#endif /* GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER */
|
|
/** @} */
|