1734 lines
51 KiB
C
1734 lines
51 KiB
C
/*
|
|
*********************************************************************************************************
|
|
* uC/GUI
|
|
* Universal graphic software for embedded applications
|
|
*
|
|
* (c) Copyright 2002, Micrium Inc., Weston, FL
|
|
* (c) Copyright 2002, SEGGER Microcontroller Systeme GmbH
|
|
*
|
|
* µC/GUI is protected by international copyright laws. Knowledge of the
|
|
* source code may not be used to write a similar product. This file may
|
|
* only be used in accordance with a license and should not be redistributed
|
|
* in any way. We appreciate your understanding and fairness.
|
|
*
|
|
----------------------------------------------------------------------
|
|
File : WM.C
|
|
Purpose : Windows manager core
|
|
----------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <stddef.h> /* needed for definition of NULL */
|
|
#include <string.h> /* required for memset */
|
|
|
|
#define WM_C
|
|
#include "WM_Intern_ConfDep.h"
|
|
|
|
#if GUI_WINSUPPORT /* If 0, WM will not generate any code */
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Macros for internal use
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
#define ASSIGN_IF_LESS(v0,v1) if (v1<v0) v0=v1
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Local typedefs
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
typedef struct {
|
|
GUI_RECT ClientRect;
|
|
GUI_RECT CurRect;
|
|
int Cnt;
|
|
int EntranceCnt;
|
|
} WM_IVR_CONTEXT;
|
|
|
|
/*********************************************************************
|
|
*
|
|
* global data
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
U8 WM_IsActive;
|
|
U16 WM__CreateFlags;
|
|
WM_HWIN WM__hCapture;
|
|
WM_HWIN WM__hWinFocus;
|
|
char WM__CaptureReleaseAuto;
|
|
WM_tfPollPID* WM_pfPollPID;
|
|
U8 WM__PaintCallbackCnt; /* Public for assertions only */
|
|
GUI_PID_STATE WM_PID__StateLast;
|
|
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
int WM__TransWindowCnt;
|
|
WM_HWIN WM__hATransWindow;
|
|
#endif
|
|
|
|
#if WM_SUPPORT_DIAG
|
|
void (*WM__pfShowInvalid)(WM_HWIN hWin);
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* static data
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
static WM_HWIN NextDrawWin;
|
|
static WM_IVR_CONTEXT _ClipContext;
|
|
static char _IsInited;
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Static routines
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* _CheckCriticalHandles
|
|
*
|
|
* Purpose:
|
|
* Checks the critical handles and resets the matching one
|
|
*/
|
|
static void _CheckCriticalHandles(WM_HWIN hWin) {
|
|
WM_CRITICAL_HANDLE * pCH;
|
|
for (pCH = WM__pFirstCriticalHandle; pCH; pCH = pCH->pNext) {
|
|
if (pCH->hWin == hWin) {
|
|
pCH->hWin = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _DesktopHandle2Index
|
|
*
|
|
* Function:
|
|
* Convert the given desktop window into the display index.
|
|
*
|
|
* Return value:
|
|
* Desktop index if window handle is valid.
|
|
* else: -1
|
|
*/
|
|
static int _DesktopHandle2Index(WM_HWIN hDesktop) {
|
|
#if GUI_NUM_LAYERS > 1
|
|
int i;
|
|
for (i = 0; i < GUI_NUM_LAYERS; i++) {
|
|
if (hDesktop == WM__ahDesktopWin[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
#else
|
|
if (hDesktop == WM__ahDesktopWin[0]) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _Invalidate1Abs
|
|
*
|
|
* Invalidate given window, using absolute coordinates
|
|
*/
|
|
static void _Invalidate1Abs(WM_HWIN hWin, const GUI_RECT*pRect) {
|
|
GUI_RECT r;
|
|
WM_Obj* pWin;
|
|
int Status;
|
|
pWin = WM_H2P(hWin);
|
|
Status = pWin->Status;
|
|
if ((Status & WM_SF_ISVIS) == 0) {
|
|
return; /* Window is not visible... we are done */
|
|
}
|
|
if ((Status & (WM_SF_HASTRANS | WM_SF_CONST_OUTLINE)) == WM_SF_HASTRANS) {
|
|
return; /* Window is transparent; transparency may change... we are done, since background will be invalidated */
|
|
}
|
|
if (WM__RectIsNZ(pRect) == 0) {
|
|
return; /* Nothing to do ... */
|
|
}
|
|
/* Calc affected area */
|
|
GUI__IntersectRects(&r, pRect, &pWin->Rect);
|
|
if (WM__RectIsNZ(&r)) {
|
|
#if WM_SUPPORT_NOTIFY_VIS_CHANGED
|
|
WM__SendMsgNoData(hWin, WM_NOTIFY_VIS_CHANGED); /* Notify window that visibility may have changed */
|
|
#endif
|
|
|
|
if (pWin->Status & WM_SF_INVALID) {
|
|
GUI_MergeRect(&pWin->InvalidRect, &pWin->InvalidRect, &r);
|
|
} else {
|
|
pWin->InvalidRect = r;
|
|
pWin->Status |= WM_SF_INVALID;
|
|
WM__NumInvalidWindows++;
|
|
/* Optional code: Call external routine to notify that drawing is required */
|
|
#ifdef GUI_X_REDRAW
|
|
{
|
|
GUI_RECT r;
|
|
r = pWin->Rect;
|
|
if (WM__ClipAtParentBorders(&r, hWin)) {
|
|
GUI_X_REDRAW(); /* Call hook function to signal an invalidation */
|
|
}
|
|
}
|
|
#endif
|
|
GUI_X_SIGNAL_EVENT();
|
|
}
|
|
/* Debug code: shows invalid areas */
|
|
#if (WM_SUPPORT_DIAG)
|
|
if (WM__pfShowInvalid) {
|
|
(WM__pfShowInvalid)(hWin);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _GetTopLevelWindow
|
|
*/
|
|
#if GUI_NUM_LAYERS > 1
|
|
static WM_HWIN _GetTopLevelWindow(WM_HWIN hWin) {
|
|
WM_Obj* pWin;
|
|
WM_HWIN hTop;
|
|
while (hTop = hWin, pWin = WM_H2P(hWin), (hWin = pWin->hParent) != 0) {
|
|
}
|
|
return hTop;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* ResetNextDrawWin
|
|
|
|
When drawing, we have to start at the bottom window !
|
|
*/
|
|
static void ResetNextDrawWin(void) {
|
|
NextDrawWin = WM_HWIN_NULL;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _GethDrawWin
|
|
*
|
|
* Return Window being drawn.
|
|
* Normally same as pAWin, except if overlaying transparent window is drawn
|
|
*
|
|
*/
|
|
static WM_HWIN _GethDrawWin(void) {
|
|
WM_HWIN h;
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
if (WM__hATransWindow) {
|
|
h = WM__hATransWindow;
|
|
} else
|
|
#endif
|
|
{
|
|
h = GUI_Context.hAWin;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _SetClipRectUserIntersect
|
|
*/
|
|
static void _SetClipRectUserIntersect(const GUI_RECT* prSrc) {
|
|
if (GUI_Context.WM__pUserClipRect == NULL) {
|
|
LCD_SetClipRectEx(prSrc);
|
|
} else {
|
|
GUI_RECT r;
|
|
r = *GUI_Context.WM__pUserClipRect;
|
|
WM__Client2Screen(WM_H2P(_GethDrawWin()), &r); /* Convert User ClipRect into screen coordinates */
|
|
/* Set intersection as clip rect */
|
|
GUI__IntersectRect(&r, prSrc);
|
|
LCD_SetClipRectEx(&r);
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Public routines
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* WM__ClipAtParentBorders
|
|
*
|
|
* Function:
|
|
* Iterates over the window itself and all its ancestors.
|
|
* Intersects all rectangles to
|
|
* find out which part is actually visible.
|
|
* Reduces the rectangle to the visible area.
|
|
* This routines takes into account both the rectangles of the
|
|
* ancestors as well as the WM_SF_ISVIS flag.
|
|
*
|
|
* Parameters
|
|
* hWin Obvious
|
|
* pRect Pointer to the rectangle to be clipped. May not be NULL.
|
|
* The parameter is IN/OUT.
|
|
* Note that the rectangle is clipped only if the return
|
|
* value indicates a valid rectangle remains.
|
|
*
|
|
* Return value:
|
|
* 1: Something is or may be visible.
|
|
* 0: Nothing is visible (outside of ancestors, no desktop, hidden)
|
|
*/
|
|
int WM__ClipAtParentBorders(GUI_RECT* pRect, WM_HWIN hWin) {
|
|
WM_Obj* pWin;
|
|
|
|
/* Iterate up the window hierarchy.
|
|
If the window is invisible, we are done.
|
|
Clip at parent boarders.
|
|
We are done with iterating if hWin has no parent.
|
|
*/
|
|
do {
|
|
pWin = WM_H2P(hWin);
|
|
if ((pWin->Status & WM_SF_ISVIS) == 0) {
|
|
return 0; /* Invisible */
|
|
}
|
|
GUI__IntersectRect(pRect, &pWin->Rect); /* And clip on borders */
|
|
if (pWin->hParent == 0) {
|
|
break; /* hWin is now the top level window which has no parent */
|
|
}
|
|
hWin = pWin->hParent; /* Go one level up (parent)*/
|
|
} while (1); /* Only way out is in the loop. Required for efficiency, no bug, even though some compilers may complain. */
|
|
|
|
/* Now check if the top level window is a desktop window. If it is not,
|
|
then the window is not visible.
|
|
*/
|
|
if (_DesktopHandle2Index(hWin) < 0) {
|
|
return 0; /* No desktop - (unattached) - Nothing to draw */
|
|
}
|
|
return 1; /* Something may be visible */
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__ActivateClipRect
|
|
*/
|
|
void WM__ActivateClipRect(void) {
|
|
if (WM_IsActive) {
|
|
_SetClipRectUserIntersect(&_ClipContext.CurRect);
|
|
} else { /* Window manager disabled, typically because meory device is active */
|
|
GUI_RECT r;
|
|
WM_Obj *pAWin;
|
|
pAWin = WM_H2P(GUI_Context.hAWin);
|
|
r = pAWin->Rect;
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
if (WM__hATransWindow) {
|
|
WM__ClipAtParentBorders(&r, WM__hATransWindow);
|
|
}
|
|
#endif
|
|
/* Take UserClipRect into account */
|
|
_SetClipRectUserIntersect(&r);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__InsertWindowIntoList
|
|
*
|
|
* Routine describtion
|
|
* This routine inserts the window in the list of child windows for
|
|
* a particular parent window.
|
|
* The window is placed on top of all siblings with the same level.
|
|
*/
|
|
void WM__InsertWindowIntoList(WM_HWIN hWin, WM_HWIN hParent) {
|
|
int OnTop;
|
|
WM_HWIN hi;
|
|
WM_Obj * pWin;
|
|
WM_Obj * pParent;
|
|
WM_Obj * pi;
|
|
|
|
if (hParent) {
|
|
pWin = WM_H2P(hWin);
|
|
pWin->hNext = 0;
|
|
pWin->hParent = hParent;
|
|
pParent = WM_H2P(hParent);
|
|
OnTop = pWin->Status & WM_CF_STAYONTOP;
|
|
hi = pParent->hFirstChild;
|
|
/* Put it at beginning of the list if there is no child */
|
|
if (hi == 0) { /* No child yet ... Makes things easy ! */
|
|
pParent->hFirstChild = hWin;
|
|
return; /* Early out ... We are done */
|
|
}
|
|
/* Put it at beginning of the list if first child is a TOP window and new one is not */
|
|
pi = WM_H2P(hi);
|
|
if (!OnTop) {
|
|
if (pi->Status & WM_SF_STAYONTOP) {
|
|
pWin->hNext = hi;
|
|
pParent->hFirstChild = hWin;
|
|
return; /* Early out ... We are done */
|
|
}
|
|
}
|
|
/* Put it at the end of the list or before the last non "STAY-ON-TOP" child */
|
|
do {
|
|
WM_Obj* pNext;
|
|
WM_HWIN hNext;
|
|
if ((hNext = pi->hNext) == 0) { /* End of sibling list ? */
|
|
pi->hNext = hWin; /* Then modify this last element to point to new one and we are done */
|
|
break;
|
|
}
|
|
pNext = WM_H2P(hNext);
|
|
if (!OnTop) {
|
|
if (pNext->Status & WM_SF_STAYONTOP) {
|
|
pi->hNext = hWin;
|
|
pWin->hNext = hNext;
|
|
break;
|
|
}
|
|
}
|
|
pi = pNext;
|
|
} while (1);
|
|
#if WM_SUPPORT_NOTIFY_VIS_CHANGED
|
|
WM__NotifyVisChanged(hWin, &pWin->Rect);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__RemoveWindowFromList
|
|
*/
|
|
void WM__RemoveWindowFromList(WM_HWIN hWin) {
|
|
WM_HWIN hi, hParent;
|
|
WM_Obj * pWin, * pParent, * pi;
|
|
|
|
pWin = WM_H2P(hWin);
|
|
hParent = pWin->hParent;
|
|
if (hParent) {
|
|
pParent = WM_H2P(hParent);
|
|
hi = pParent->hFirstChild;
|
|
if (hi == hWin) {
|
|
pi = WM_H2P(hi);
|
|
pParent->hFirstChild = pi->hNext;
|
|
} else {
|
|
while (hi) {
|
|
pi = WM_H2P(hi);
|
|
if (pi->hNext == hWin) {
|
|
pi->hNext = pWin->hNext;
|
|
break;
|
|
}
|
|
hi = pi->hNext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__DetachWindow
|
|
*
|
|
* Detaches the given window. The window still exists, it keeps all
|
|
* children, but it is no longer visible since it is taken out of
|
|
* the tree of the desktop window.
|
|
*/
|
|
void WM__DetachWindow(WM_HWIN hWin) {
|
|
WM_Obj* pWin;
|
|
WM_HWIN hParent;
|
|
pWin = WM_H2P(hWin);
|
|
hParent = pWin->hParent;
|
|
if (hParent) {
|
|
WM__RemoveWindowFromList(hWin);
|
|
/* Clear area used by this window */
|
|
WM_InvalidateArea(&pWin->Rect);
|
|
pWin->hParent = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _DeleteAllChildren
|
|
*/
|
|
static void _DeleteAllChildren(WM_HWIN hChild) {
|
|
while (hChild) {
|
|
WM_Obj* pChild = WM_H2P(hChild);
|
|
WM_HWIN hNext = pChild->hNext;
|
|
WM_DeleteWindow(hChild);
|
|
hChild = hNext;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Module internal routines
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* WM__Client2Screen
|
|
*/
|
|
void WM__Client2Screen(const WM_Obj* pWin, GUI_RECT *pRect) {
|
|
GUI_MoveRect(pRect, pWin->Rect.x0, pWin->Rect.y0);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__IsWindow
|
|
*/
|
|
int WM__IsWindow(WM_HWIN hWin) {
|
|
WM_HWIN iWin;
|
|
for (iWin = WM__FirstWin; iWin; iWin = WM_H2P(iWin)->hNextLin) {
|
|
if (iWin == hWin) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__InvalidateAreaBelow
|
|
|
|
Params: pRect Rectangle in Absolute coordinates
|
|
*/
|
|
void WM__InvalidateAreaBelow(const GUI_RECT* pRect, WM_HWIN StopWin) {
|
|
GUI_USE_PARA(StopWin);
|
|
WM_InvalidateArea(pRect); /* Can be optimized to spare windows above */
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_RemoveFromLinList
|
|
*/
|
|
void WM__RemoveFromLinList(WM_HWIN hWin) {
|
|
WM_Obj* piWin;
|
|
WM_HWIN hiWin;
|
|
WM_HWIN hNext;
|
|
for (hiWin = WM__FirstWin; hiWin; ) {
|
|
piWin = WM_H2P(hiWin);
|
|
hNext = piWin->hNextLin;
|
|
if (hNext == hWin) {
|
|
piWin->hNextLin = WM_H2P(hWin)->hNextLin;
|
|
break;
|
|
}
|
|
hiWin = hNext;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _AddToLinList
|
|
*/
|
|
static void _AddToLinList(WM_HWIN hNew) {
|
|
WM_Obj* pFirst;
|
|
WM_Obj* pNew;
|
|
if (WM__FirstWin) {
|
|
pFirst = WM_H2P(WM__FirstWin);
|
|
pNew = WM_H2P(hNew);
|
|
pNew->hNextLin = pFirst->hNextLin;
|
|
pFirst->hNextLin = hNew;
|
|
} else {
|
|
WM__FirstWin = hNew;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__RectIsNZ
|
|
*
|
|
Check if the rectangle has some content (is non-zero)
|
|
Returns 0 if the Rectangle has no content, else 1.
|
|
*/
|
|
int WM__RectIsNZ(const GUI_RECT* pr) {
|
|
if (pr->x0 > pr->x1)
|
|
return 0;
|
|
if (pr->y0 > pr->y1)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _Findy1
|
|
*
|
|
*/
|
|
static void _Findy1(WM_HWIN iWin, GUI_RECT* pRect, GUI_RECT* pParentRect) {
|
|
WM_Obj* pWin;
|
|
for (; iWin; iWin = pWin->hNext) {
|
|
int Status = (pWin = WM_H2P(iWin))->Status;
|
|
/* Check if this window affects us at all */
|
|
if (Status & WM_SF_ISVIS) {
|
|
GUI_RECT rWinClipped; /* Window rect, clipped to part inside of ancestors */
|
|
if (pParentRect) {
|
|
GUI__IntersectRects(&rWinClipped, &pWin->Rect, pParentRect);
|
|
} else {
|
|
rWinClipped = pWin->Rect;
|
|
}
|
|
/* Check if this window affects us at all */
|
|
if (GUI_RectsIntersect(pRect, &rWinClipped)) {
|
|
if ((Status & WM_SF_HASTRANS) == 0) {
|
|
if (pWin->Rect.y0 > pRect->y0) {
|
|
ASSIGN_IF_LESS(pRect->y1, rWinClipped.y0 - 1); /* Check upper border of window */
|
|
} else {
|
|
ASSIGN_IF_LESS(pRect->y1, rWinClipped.y1); /* Check lower border of window */
|
|
}
|
|
} else {
|
|
/* Check all children*/
|
|
WM_HWIN hChild;
|
|
WM_Obj* pChild;
|
|
for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) {
|
|
pChild = WM_H2P(hChild);
|
|
_Findy1(hChild, pRect, &rWinClipped);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _Findx0
|
|
*/
|
|
static int _Findx0(WM_HWIN hWin, GUI_RECT* pRect, GUI_RECT* pParentRect) {
|
|
WM_Obj* pWin;
|
|
int r = 0;
|
|
for (; hWin; hWin = pWin->hNext) {
|
|
int Status = (pWin = WM_H2P(hWin))->Status;
|
|
if (Status & WM_SF_ISVIS) { /* If window is not visible, it can be safely ignored */
|
|
GUI_RECT rWinClipped; /* Window rect, clipped to part inside of ancestors */
|
|
if (pParentRect) {
|
|
GUI__IntersectRects(&rWinClipped, &pWin->Rect, pParentRect);
|
|
} else {
|
|
rWinClipped = pWin->Rect;
|
|
}
|
|
/* Check if this window affects us at all */
|
|
if (GUI_RectsIntersect(pRect, &rWinClipped)) {
|
|
if ((Status & WM_SF_HASTRANS) == 0) {
|
|
pRect->x0 = rWinClipped.x1+1;
|
|
r = 1;
|
|
} else {
|
|
/* Check all children */
|
|
WM_HWIN hChild;
|
|
WM_Obj* pChild;
|
|
for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) {
|
|
pChild = WM_H2P(hChild);
|
|
if (_Findx0(hChild, pRect, &rWinClipped)) {
|
|
r = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _Findx1
|
|
*/
|
|
static void _Findx1(WM_HWIN hWin, GUI_RECT* pRect, GUI_RECT* pParentRect) {
|
|
WM_Obj* pWin;
|
|
for (; hWin; hWin = pWin->hNext) {
|
|
int Status = (pWin = WM_H2P(hWin))->Status;
|
|
if (Status & WM_SF_ISVIS) { /* If window is not visible, it can be safely ignored */
|
|
GUI_RECT rWinClipped; /* Window rect, clipped to part inside of ancestors */
|
|
if (pParentRect) {
|
|
GUI__IntersectRects(&rWinClipped, &pWin->Rect, pParentRect);
|
|
} else {
|
|
rWinClipped = pWin->Rect;
|
|
}
|
|
/* Check if this window affects us at all */
|
|
if (GUI_RectsIntersect(pRect, &rWinClipped)) {
|
|
if ((Status & WM_SF_HASTRANS) == 0) {
|
|
pRect->x1 = rWinClipped.x0-1;
|
|
} else {
|
|
/* Check all children */
|
|
WM_HWIN hChild;
|
|
WM_Obj* pChild;
|
|
for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) {
|
|
pChild = WM_H2P(hChild);
|
|
_Findx1(hChild, pRect, &rWinClipped);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Sending messages
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* WM_SendMessage
|
|
*/
|
|
void WM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg) {
|
|
if (hWin) {
|
|
WM_Obj* pWin;
|
|
WM_LOCK();
|
|
pWin = WM_H2P(hWin);
|
|
if (pWin->cb != NULL) {
|
|
pMsg->hWin = hWin;
|
|
(*pWin->cb)(pMsg);
|
|
}
|
|
WM_UNLOCK();
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__SendMsgNoData
|
|
*/
|
|
void WM__SendMsgNoData(WM_HWIN hWin, U8 MsgId) {
|
|
WM_MESSAGE Msg;
|
|
Msg.hWin = hWin;
|
|
Msg.MsgId = MsgId;
|
|
WM_SendMessage(hWin, &Msg);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__GetClientRectWin
|
|
*
|
|
Get client rectangle in windows coordinates. This means that the
|
|
upper left corner is always at (0,0).
|
|
*/
|
|
void WM__GetClientRectWin(const WM_Obj* pWin, GUI_RECT* pRect) {
|
|
pRect->x0 = pRect->y0 = 0;
|
|
pRect->x1 = pWin->Rect.x1 - pWin->Rect.x0;
|
|
pRect->y1 = pWin->Rect.y1 - pWin->Rect.y0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__GetInvalidRectAbs
|
|
*/
|
|
static void WM__GetInvalidRectAbs(WM_Obj* pWin, GUI_RECT* pRect) {
|
|
*pRect = pWin->InvalidRect;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Invalidation functions
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* WM_InvalidateRect
|
|
*
|
|
* Invalidate a section of the window. The optional rectangle
|
|
* contains client coordinates, which are independent of the
|
|
* position of the window on the logical desktop area.
|
|
*/
|
|
void WM_InvalidateRect(WM_HWIN hWin, const GUI_RECT*pRect) {
|
|
GUI_RECT r;
|
|
WM_Obj* pWin;
|
|
int Status;
|
|
if (hWin) {
|
|
WM_LOCK();
|
|
pWin = WM_H2P(hWin);
|
|
Status = pWin->Status;
|
|
if (Status & WM_SF_ISVIS) {
|
|
r = pWin->Rect;
|
|
if (pRect) {
|
|
GUI_RECT rPara;
|
|
rPara = *pRect;
|
|
WM__Client2Screen(pWin, &rPara);
|
|
GUI__IntersectRect(&r, &rPara);
|
|
}
|
|
if (WM__ClipAtParentBorders(&r, hWin)) { /* Optimization that saves invalidation if window area is not visible ... Not required */
|
|
if ((Status & (WM_SF_HASTRANS | WM_SF_CONST_OUTLINE)) == WM_SF_HASTRANS) {
|
|
WM__InvalidateAreaBelow(&r, hWin); /* Can be optimized to spare windows above */
|
|
} else {
|
|
_Invalidate1Abs(hWin, &r);
|
|
}
|
|
}
|
|
}
|
|
WM_UNLOCK();
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_InvalidateWindow
|
|
*
|
|
Invalidates an entire window.
|
|
*/
|
|
void WM_InvalidateWindow(WM_HWIN hWin) {
|
|
WM_InvalidateRect(hWin, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_InvalidateArea
|
|
|
|
Invalidate a certain section of the display. One main reason for this is
|
|
that the top window has been moved or destroyed.
|
|
The coordinates given are absolute coordinates (desktop coordinates)
|
|
*/
|
|
void WM_InvalidateArea(const GUI_RECT* pRect) {
|
|
WM_HWIN hWin;
|
|
WM_LOCK();
|
|
/* Iterate over all windows */
|
|
for (hWin = WM__FirstWin; hWin; hWin = WM_H2P(hWin)->hNextLin) {
|
|
_Invalidate1Abs(hWin, pRect);
|
|
}
|
|
WM_UNLOCK();
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* manage windows stack
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* WM_CreateWindowAsChild
|
|
*/
|
|
WM_HWIN WM_CreateWindowAsChild( int x0, int y0, int width, int height
|
|
,WM_HWIN hParent, U16 Style, WM_CALLBACK* cb
|
|
,int NumExtraBytes) {
|
|
WM_Obj* pWin;
|
|
WM_HWIN hWin;
|
|
WM_ASSERT_NOT_IN_PAINT();
|
|
WM_LOCK();
|
|
Style |= WM__CreateFlags;
|
|
/* Default parent is Desktop 0 */
|
|
if (!hParent) {
|
|
if (WM__NumWindows) {
|
|
#if GUI_NUM_LAYERS == 1
|
|
hParent = WM__ahDesktopWin[0];
|
|
#else
|
|
hParent = WM__ahDesktopWin[GUI_Context.SelLayer];
|
|
#endif
|
|
}
|
|
}
|
|
if (hParent == WM_UNATTACHED) {
|
|
hParent = WM_HWIN_NULL;
|
|
}
|
|
if (hParent) {
|
|
WM_Obj* pParent = WM_H2P(hParent);
|
|
x0 += pParent->Rect.x0;
|
|
y0 += pParent->Rect.y0;
|
|
if (width==0) {
|
|
width = pParent->Rect.x1 - pParent->Rect.x0+1;
|
|
}
|
|
if (height==0) {
|
|
height = pParent->Rect.y1 - pParent->Rect.y0+1;
|
|
}
|
|
}
|
|
if ((hWin = (WM_HWIN) GUI_ALLOC_AllocZero(NumExtraBytes + sizeof(WM_Obj))) == 0) {
|
|
GUI_DEBUG_ERROROUT("WM_CreateWindow: No memory to create window");
|
|
} else {
|
|
WM__NumWindows++;
|
|
pWin = WM_H2P(hWin);
|
|
pWin->Rect.x0 = x0;
|
|
pWin->Rect.y0 = y0;
|
|
pWin->Rect.x1 = x0 + width - 1;
|
|
pWin->Rect.y1 = y0 + height - 1;
|
|
pWin->cb = cb;
|
|
/* Copy the flags which can simply be accepted */
|
|
pWin->Status |= (Style & (WM_CF_SHOW |
|
|
WM_SF_MEMDEV |
|
|
WM_CF_MEMDEV_ON_REDRAW |
|
|
WM_SF_STAYONTOP |
|
|
WM_SF_CONST_OUTLINE |
|
|
WM_SF_HASTRANS |
|
|
WM_CF_ANCHOR_RIGHT |
|
|
WM_CF_ANCHOR_BOTTOM |
|
|
WM_CF_ANCHOR_LEFT |
|
|
WM_CF_ANCHOR_TOP |
|
|
WM_CF_LATE_CLIP));
|
|
/* Add to linked lists */
|
|
_AddToLinList(hWin);
|
|
WM__InsertWindowIntoList(hWin, hParent);
|
|
/* Activate window if WM_CF_ACTIVATE is specified */
|
|
if (Style & WM_CF_ACTIVATE) {
|
|
WM_SelectWindow(hWin); /* This is not needed if callbacks are being used, but it does not cost a lot and makes life easier ... */
|
|
}
|
|
/* Handle the Style flags, one at a time */
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
if (Style & WM_SF_HASTRANS) {
|
|
WM__TransWindowCnt++; /* Increment counter for transparency windows */
|
|
}
|
|
#endif
|
|
if (Style & WM_CF_BGND) {
|
|
WM_BringToBottom(hWin);
|
|
}
|
|
if (Style & WM_CF_SHOW) {
|
|
pWin->Status |= WM_SF_ISVIS; /* Set Visibility flag */
|
|
WM_InvalidateWindow(hWin); /* Mark content as invalid */
|
|
}
|
|
WM__SendMsgNoData(hWin, WM_CREATE);
|
|
}
|
|
WM_UNLOCK();
|
|
return hWin;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_CreateWindow
|
|
*/
|
|
WM_HWIN WM_CreateWindow(int x0, int y0, int width, int height, U16 Style, WM_CALLBACK* cb, int NumExtraBytes) {
|
|
return WM_CreateWindowAsChild(x0,y0,width,height, 0 /* No parent */, Style, cb, NumExtraBytes);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Delete window
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
/*********************************************************************
|
|
*
|
|
* WM_DeleteWindow
|
|
*/
|
|
void WM_DeleteWindow (WM_HWIN hWin) {
|
|
WM_Obj* pWin;
|
|
if (!hWin) {
|
|
return;
|
|
}
|
|
WM_ASSERT_NOT_IN_PAINT();
|
|
WM_LOCK();
|
|
if (WM__IsWindow(hWin)) {
|
|
pWin = WM_H2P(hWin);
|
|
ResetNextDrawWin(); /* Make sure the window will no longer receive drawing messages */
|
|
/* Make sure that focus is set to an existing window */
|
|
if (WM__hWinFocus == hWin) {
|
|
WM__hWinFocus = 0;
|
|
}
|
|
if (WM__hCapture == hWin) {
|
|
WM__hCapture = 0;
|
|
}
|
|
/* check if critical handles are affected. If so, reset the window handle to 0 */
|
|
_CheckCriticalHandles(hWin);
|
|
/* Inform parent */
|
|
WM_NotifyParent(hWin, WM_NOTIFICATION_CHILD_DELETED);
|
|
/* Delete all children */
|
|
_DeleteAllChildren(pWin->hFirstChild);
|
|
#if WM_SUPPORT_NOTIFY_VIS_CHANGED
|
|
WM__SendMsgNoData(hWin, WM_NOTIFY_VIS_CHANGED); /* Notify window that visibility may have changed */
|
|
#endif
|
|
/* Send WM_DELETE message to window in order to inform window itself */
|
|
WM__SendMsgNoData(hWin, WM_DELETE); /* tell window about it */
|
|
WM__DetachWindow(hWin);
|
|
/* Remove window from window stack */
|
|
WM__RemoveFromLinList(hWin);
|
|
/* Handle transparency counter if necessary */
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
if (pWin->Status & WM_SF_HASTRANS) {
|
|
WM__TransWindowCnt--;
|
|
}
|
|
#endif
|
|
/* Make sure window is no longer counted as invalid */
|
|
if (pWin->Status & WM_SF_INVALID) {
|
|
WM__NumInvalidWindows--;
|
|
}
|
|
/* Free window memory */
|
|
WM__NumWindows--;
|
|
GUI_ALLOC_Free(hWin);
|
|
/* Select a valid window */
|
|
WM_SelectWindow(WM__FirstWin);
|
|
} else {
|
|
GUI_DEBUG_WARN("WM_DeleteWindow: Invalid handle");
|
|
}
|
|
WM_UNLOCK();
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_SelectWindow
|
|
*
|
|
* Sets the active Window. The active Window is the one that is used for all
|
|
* drawing (and text) operations.
|
|
*/
|
|
WM_HWIN WM_SelectWindow(WM_HWIN hWin) {
|
|
WM_HWIN hWinPrev;
|
|
WM_Obj* pObj;
|
|
|
|
WM_ASSERT_NOT_IN_PAINT();
|
|
WM_LOCK();
|
|
hWinPrev = GUI_Context.hAWin;
|
|
if (hWin == 0) {
|
|
hWin = WM__FirstWin;
|
|
}
|
|
/* Select new window */
|
|
GUI_Context.hAWin = hWin;
|
|
#if GUI_NUM_LAYERS > 1
|
|
{
|
|
WM_HWIN hTop;
|
|
int LayerIndex;
|
|
hTop = _GetTopLevelWindow(hWin);
|
|
LayerIndex = _DesktopHandle2Index(hTop);
|
|
if (LayerIndex >= 0) {
|
|
GUI_SelectLayer(LayerIndex);
|
|
}
|
|
}
|
|
#endif
|
|
pObj = WM_H2P(hWin);
|
|
LCD_SetClipRectMax(); /* Drawing operations will clip ... If WM is deactivated, allow all */
|
|
GUI_Context.xOff = pObj->Rect.x0;
|
|
GUI_Context.yOff = pObj->Rect.y0;
|
|
WM_UNLOCK();
|
|
return hWinPrev;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_GetActiveWindow
|
|
*/
|
|
WM_HWIN WM_GetActiveWindow(void) {
|
|
return GUI_Context.hAWin;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* IVR calculation
|
|
*
|
|
**********************************************************************
|
|
|
|
IVRs are invalid rectangles. When redrawing, only the portion of the
|
|
window which is
|
|
a) within the window-rectangle
|
|
b) not covered by an other window
|
|
c) marked as invalid
|
|
is actually redrawn. Unfortunately, this section is not always
|
|
rectangular. If the window is partially covered by an other window,
|
|
it consists of the sum of multiple rectangles. In all drawing
|
|
operations, we have to iterate over every one of these rectangles in
|
|
order to make sure the window is drawn completly.
|
|
Function works as follows:
|
|
STEP 1: - Set upper left coordinates to next pixel. If end of line (right border), goto next line -> (r.x0, r.y0)
|
|
STEP 2: - Check if we are done, return if we are.
|
|
STEP 3: - If we are at the left border, find max. heigtht (r.y1) by iterating over windows above
|
|
STEP 4: - Find x0 for the given y0, y1 by iterating over windows above
|
|
STEP 5: - If r.x0 out of right border, this stripe is done. Set next stripe and goto STEP 2
|
|
STEP 6: - Find r.x1. We have to Iterate over all windows which are above
|
|
*/
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _FindNext_IVR
|
|
*/
|
|
#if WM_SUPPORT_OBSTRUCT
|
|
static int _FindNext_IVR(void) {
|
|
WM_HMEM hParent;
|
|
GUI_RECT r;
|
|
WM_Obj* pAWin;
|
|
WM_Obj* pParent;
|
|
r = _ClipContext.CurRect; /* temps so we do not have to work with pointers too much */
|
|
/*
|
|
STEP 1:
|
|
Set the next position which could be part of the next IVR
|
|
This will be the first unhandle pixel in reading order, i.e. next one to the right
|
|
or next one down if we are at the right border.
|
|
*/
|
|
if (_ClipContext.Cnt == 0) { /* First IVR starts in upper left */
|
|
r.x0 = _ClipContext.ClientRect.x0;
|
|
r.y0 = _ClipContext.ClientRect.y0;
|
|
} else {
|
|
r.x0 = _ClipContext.CurRect.x1+1;
|
|
r.y0 = _ClipContext.CurRect.y0;
|
|
if (r.x0 > _ClipContext.ClientRect.x1) {
|
|
NextStripe: /* go down to next stripe */
|
|
r.x0 = _ClipContext.ClientRect.x0;
|
|
r.y0 = _ClipContext.CurRect.y1+1;
|
|
}
|
|
}
|
|
/*
|
|
STEP 2:
|
|
Check if we are done completely.
|
|
*/
|
|
if (r.y0 >_ClipContext.ClientRect.y1) {
|
|
return 0;
|
|
}
|
|
/* STEP 3:
|
|
Find out the max. height (r.y1) if we are at the left border.
|
|
Since we are using the same height for all IVRs at the same y0,
|
|
we do this only for the leftmost one.
|
|
*/
|
|
pAWin = WM_H2P(GUI_Context.hAWin);
|
|
if (r.x0 == _ClipContext.ClientRect.x0) {
|
|
r.y1 = _ClipContext.ClientRect.y1;
|
|
r.x1 = _ClipContext.ClientRect.x1;
|
|
/* Iterate over all windows which are above */
|
|
/* Check all siblings above (Iterate over Parents and top siblings (hNext) */
|
|
for (hParent = GUI_Context.hAWin; hParent; hParent = pParent->hParent) {
|
|
pParent = WM_H2P(hParent);
|
|
_Findy1(pParent->hNext, &r, NULL);
|
|
}
|
|
/* Check all children */
|
|
_Findy1(pAWin->hFirstChild, &r, NULL);
|
|
}
|
|
/*
|
|
STEP 4
|
|
Find out x0 for the given y0, y1 by iterating over windows above.
|
|
if we find one that intersects, adjust x0 to the right.
|
|
*/
|
|
Find_x0:
|
|
r.x1 = r.x0;
|
|
/* Iterate over all windows which are above */
|
|
/* Check all siblings above (siblings of window, siblings of parents, etc ...) */
|
|
#if 0 /* This is a planned, but not yet released optimization */
|
|
if (Status & WM_SF_DONT_CLIP_SIBLINGS)
|
|
{
|
|
hParent = pAWin->hParent;
|
|
} else
|
|
#endif
|
|
{
|
|
hParent = GUI_Context.hAWin;
|
|
}
|
|
for (; hParent; hParent = pParent->hParent) {
|
|
pParent = WM_H2P(hParent);
|
|
if (_Findx0(pParent->hNext, &r, NULL)) {
|
|
goto Find_x0;
|
|
}
|
|
}
|
|
/* Check all children */
|
|
if (_Findx0(pAWin->hFirstChild, &r, NULL)) {
|
|
goto Find_x0;
|
|
}
|
|
/*
|
|
STEP 5:
|
|
If r.x0 out of right border, this stripe is done. Set next stripe and goto STEP 2
|
|
Find out x1 for the given x0, y0, y1
|
|
*/
|
|
r.x1 = _ClipContext.ClientRect.x1;
|
|
if (r.x1 < r.x0) {/* horizontal border reached ? */
|
|
_ClipContext.CurRect = r;
|
|
goto NextStripe;
|
|
}
|
|
/*
|
|
STEP 6:
|
|
Find r.x1. We have to Iterate over all windows which are above
|
|
*/
|
|
/* Check all siblings above (Iterate over Parents and top siblings (hNext) */
|
|
#if 0 /* This is a planned, but not yet released optimization */
|
|
if (Status & WM_SF_DONT_CLIP_SIBLINGS)
|
|
{
|
|
hParent = pAWin->hParent;
|
|
} else
|
|
#endif
|
|
{
|
|
hParent = GUI_Context.hAWin;
|
|
}
|
|
for (; hParent; hParent = pParent->hParent) {
|
|
pParent = WM_H2P(hParent);
|
|
_Findx1(pParent->hNext, &r, NULL);
|
|
}
|
|
/* Check all children */
|
|
_Findx1(pAWin->hFirstChild, &r, NULL);
|
|
/* We are done. Return the rectangle we found in the _ClipContext. */
|
|
if (_ClipContext.Cnt >200) {
|
|
return 0; /* error !!! This should not happen !*/
|
|
}
|
|
_ClipContext.CurRect = r;
|
|
return 1; /* IVR is valid ! */
|
|
}
|
|
|
|
#else
|
|
|
|
static int _FindNext_IVR(void) {
|
|
if (_ClipContext.Cnt ==0) {
|
|
_ClipContext.CurRect = GUI_Context.pAWin->Rect;
|
|
return 1; /* IVR is valid ! */
|
|
}
|
|
return 0; /* Nothing left to draw */
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_GetNextIVR
|
|
|
|
Sets the next clipping rectangle. If a valid one has
|
|
been found (and set), 1 is returned in order to indicate
|
|
that the drawing operation needs to be executed.
|
|
Returning 0 signals that we have iterated over all
|
|
rectangles.
|
|
|
|
Returns: 0 if no valid rectangle is found
|
|
1 if rectangle has been found
|
|
*/
|
|
int WM__GetNextIVR(void) {
|
|
#if GUI_SUPPORT_CURSOR
|
|
static char _CursorHidden;
|
|
#endif
|
|
/* If WM is not active, we have no rectangles to return */
|
|
if (WM_IsActive==0) {
|
|
return 0;
|
|
}
|
|
if (_ClipContext.EntranceCnt > 1) {
|
|
_ClipContext.EntranceCnt--;
|
|
return 0;
|
|
}
|
|
#if GUI_SUPPORT_CURSOR
|
|
if (_CursorHidden) {
|
|
_CursorHidden = 0;
|
|
(*GUI_CURSOR_pfTempUnhide) ();
|
|
}
|
|
#endif
|
|
++_ClipContext.Cnt;
|
|
/* Find next rectangle and use it as ClipRect */
|
|
if (!_FindNext_IVR()) {
|
|
_ClipContext.EntranceCnt--; /* This search is over ! */
|
|
return 0; /* Could not find an other one ! */
|
|
}
|
|
WM__ActivateClipRect();
|
|
/* Hide cursor if necessary */
|
|
#if GUI_SUPPORT_CURSOR
|
|
if (GUI_CURSOR_pfTempHide) {
|
|
_CursorHidden = (*GUI_CURSOR_pfTempHide) ( &_ClipContext.CurRect);
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__InitIVRSearch
|
|
|
|
This routine is called from the clipping level
|
|
(the WM_ITERATE_START macro) when starting an iteration over the
|
|
visible rectangles.
|
|
|
|
Return value:
|
|
0 : There is no valid rectangle (nothing to do ...)
|
|
1 : There is a valid rectangle
|
|
*/
|
|
int WM__InitIVRSearch(const GUI_RECT* pMaxRect) {
|
|
GUI_RECT r;
|
|
WM_Obj* pAWin;
|
|
GUI_ASSERT_LOCK(); /* GUI_LOCK must have been "called" before entering this (normally done indrawing routine) */
|
|
/* If WM is not active -> nothing to do, leave cliprect alone */
|
|
if (WM_IsActive==0) {
|
|
WM__ActivateClipRect();
|
|
return 1;
|
|
}
|
|
/* If we entered multiple times, leave Cliprect alone */
|
|
if (++_ClipContext.EntranceCnt > 1)
|
|
return 1;
|
|
pAWin = WM_H2P(GUI_Context.hAWin);
|
|
_ClipContext.Cnt = -1;
|
|
/* When using callback mechanism, it is legal to reduce drawing
|
|
area to the invalid area ! */
|
|
if (WM__PaintCallbackCnt) {
|
|
WM__GetInvalidRectAbs(pAWin, &r);
|
|
} else { /* Not using callback mechanism, therefor allow entire rectangle */
|
|
if (pAWin->Status & WM_SF_ISVIS) {
|
|
r = pAWin->Rect;
|
|
} else {
|
|
--_ClipContext.EntranceCnt;
|
|
return 0; /* window is not even visible ! */
|
|
}
|
|
}
|
|
/* If the drawing routine has specified a rectangle, use it to reduce the rectangle */
|
|
if (pMaxRect) {
|
|
GUI__IntersectRect(&r, pMaxRect);
|
|
}
|
|
/* If user has reduced the cliprect size, reduce the rectangle */
|
|
if (GUI_Context.WM__pUserClipRect) {
|
|
WM_Obj* pWin = pAWin;
|
|
GUI_RECT rUser = *(GUI_Context.WM__pUserClipRect);
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
if (WM__hATransWindow) {
|
|
pWin = WM_H2P(WM__hATransWindow);
|
|
}
|
|
#endif
|
|
WM__Client2Screen(pWin, &rUser);
|
|
GUI__IntersectRect(&r, &rUser);
|
|
}
|
|
/* For transparent windows, we need to further reduce the rectangle */
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
if (WM__hATransWindow) {
|
|
if (WM__ClipAtParentBorders(&r, WM__hATransWindow) == 0) {
|
|
--_ClipContext.EntranceCnt;
|
|
return 0; /* Nothing to draw */
|
|
}
|
|
}
|
|
#endif
|
|
/* Iterate over all ancestors and clip at their borders. If there is no visible part, we are done */
|
|
if (WM__ClipAtParentBorders(&r, GUI_Context.hAWin) == 0) {
|
|
--_ClipContext.EntranceCnt;
|
|
return 0; /* Nothing to draw */
|
|
}
|
|
/* Store the rectangle and find the first rectangle of the area */
|
|
_ClipContext.ClientRect = r;
|
|
return WM__GetNextIVR();
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_SetDefault
|
|
*
|
|
This routine sets the defaults for WM and the layers below.
|
|
It is used before a drawing routine is called in order to
|
|
make sure that defaults are set (in case the default settings
|
|
had been altered before by the application)
|
|
*/
|
|
void WM_SetDefault(void) {
|
|
GL_SetDefault();
|
|
GUI_Context.WM__pUserClipRect = NULL; /* No add. clipping */
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _Paint1
|
|
*/
|
|
static void _Paint1(WM_HWIN hWin, WM_Obj* pWin) {
|
|
int Status = pWin->Status;
|
|
/* Send WM_PAINT if window is visible and a callback is defined */
|
|
if ((pWin->cb != NULL) && (Status & WM_SF_ISVIS)) {
|
|
WM_MESSAGE Msg;
|
|
WM__PaintCallbackCnt++;
|
|
if (Status & WM_SF_LATE_CLIP) {
|
|
Msg.hWin = hWin;
|
|
Msg.MsgId = WM_PAINT;
|
|
Msg.Data.p = (GUI_RECT*)&pWin->InvalidRect;
|
|
WM_SetDefault();
|
|
WM_SendMessage(hWin, &Msg);
|
|
} else {
|
|
WM_ITERATE_START(&pWin->InvalidRect) {
|
|
Msg.hWin = hWin;
|
|
Msg.MsgId = WM_PAINT;
|
|
Msg.Data.p = (GUI_RECT*)&pWin->InvalidRect;
|
|
WM_SetDefault();
|
|
WM_SendMessage(hWin, &Msg);
|
|
} WM_ITERATE_END();
|
|
}
|
|
WM__PaintCallbackCnt--;
|
|
}
|
|
}
|
|
/*********************************************************************
|
|
*
|
|
* _Paint1Trans
|
|
*
|
|
* Purpose:
|
|
* Draw a transparent window as part of an other one (the active window: pAWin).
|
|
* This is required because transparent windows are drawn as part of their
|
|
* non-transparent parents.
|
|
* Return value:
|
|
* 0 if nothing was drawn (no invalid rect)
|
|
* 1 if something was drawn (invalid rect exists)
|
|
* Add. info:
|
|
* It is important to restore the modified settings, especially the invalid rectangle
|
|
* of the window. The invalid rectangle needs to be set, as it is passed as add. info
|
|
* to the callback on WM_PAINT.
|
|
* On traditional transparent windows, the transparent window is never drawn on its own,
|
|
* so there is no need to restore the invalid rectangle.
|
|
* However, with WM_SF_CONST_OUTLINE, the window itself may need to be redrawn because it
|
|
* can be invalid. Modifying the invalid rectangle would lead to not updating the window
|
|
* in the worst case.
|
|
*/
|
|
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
static int _Paint1Trans(WM_HWIN hWin, WM_Obj* pWin) {
|
|
int xPrev, yPrev;
|
|
WM_Obj* pAWin = WM_H2P(GUI_Context.hAWin);
|
|
/* Check if we need to do any drawing */
|
|
if (GUI_RectsIntersect(&pAWin->InvalidRect, &pWin->Rect)) {
|
|
/* Save old values */
|
|
xPrev = GUI_Context.xOff;
|
|
yPrev = GUI_Context.yOff;
|
|
/* Set values for the current (transparent) window, rather than the one below */
|
|
GUI__IntersectRects(&pWin->InvalidRect, &pWin->Rect, &pAWin->InvalidRect);
|
|
WM__hATransWindow = hWin;
|
|
GUI_Context.xOff = pWin->Rect.x0;
|
|
GUI_Context.yOff = pWin->Rect.y0;
|
|
/* Do the actual drawing ... */
|
|
_Paint1(hWin, pWin);
|
|
/* Restore settings */
|
|
WM__hATransWindow = 0;
|
|
GUI_Context.xOff = xPrev;
|
|
GUI_Context.yOff = yPrev;
|
|
return 1; /* Some drawing took place */
|
|
}
|
|
return 0; /* No invalid area, so nothing was drawn */
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _PaintTransChildren
|
|
*
|
|
* Purpose:
|
|
* Paint transparent children. This function is obviously required
|
|
* only if there are transparent windows.
|
|
* Function: Obvious
|
|
* Parameter: Obvious
|
|
* Returns: ---
|
|
*/
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
static void _PaintTransChildren(WM_Obj* pWin) {
|
|
WM_HWIN hChild;
|
|
WM_Obj* pChild;
|
|
if (pWin->Status & WM_SF_ISVIS) {
|
|
for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) {
|
|
pChild = WM_H2P(hChild);
|
|
if ((pChild->Status & (WM_SF_HASTRANS | WM_SF_ISVIS)) /* Transparent & visible ? */
|
|
== (WM_SF_HASTRANS | WM_SF_ISVIS)) {
|
|
/* Set invalid area of the window to draw */
|
|
if (GUI_RectsIntersect(&pChild->Rect, &pWin->InvalidRect)) {
|
|
GUI_RECT InvalidRectPrev;
|
|
InvalidRectPrev = pWin->InvalidRect;
|
|
if(_Paint1Trans(hChild, pChild)) {
|
|
_PaintTransChildren(pChild);
|
|
}
|
|
pWin->InvalidRect = InvalidRectPrev;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _PaintTransTopSiblings
|
|
*
|
|
* Purpose:
|
|
* Paint transparent top siblings. This function is obviously required
|
|
* only if there are transparent windows.
|
|
* Function: Obvious
|
|
* Parameter: Obvious
|
|
* Returns: ---
|
|
*/
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
static void _PaintTransTopSiblings(WM_HWIN hWin, WM_Obj* pWin) {
|
|
WM_HWIN hParent;
|
|
WM_Obj* pParent;
|
|
hParent = pWin->hParent;
|
|
hWin = pWin->hNext;
|
|
while (hParent) { /* Go hierarchy up to desktop window */
|
|
for (; hWin; hWin = pWin->hNext) {
|
|
pWin = WM_H2P(hWin);
|
|
/* paint window if it is transparent & visible */
|
|
if ((pWin->Status & (WM_SF_HASTRANS | WM_SF_ISVIS)) == (WM_SF_HASTRANS | WM_SF_ISVIS)) {
|
|
_Paint1Trans(hWin, pWin);
|
|
}
|
|
/* paint transparent & visible children */
|
|
_PaintTransChildren(pWin);
|
|
}
|
|
pParent = WM_H2P(hParent);
|
|
hWin = pParent->hNext;
|
|
hParent = pParent->hParent;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Callback for Paint message
|
|
*
|
|
* This callback is used by the window manger in conjunction with
|
|
* banding memory devices. A pointer to this routine is given to
|
|
* the banding memory device. This callback in turn will send the
|
|
* paint message to the window.
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM__PaintWinAndOverlays
|
|
*
|
|
* Purpose
|
|
* Paint the given window and all overlaying windows
|
|
* (transparent children and transparent top siblings)
|
|
*/
|
|
void WM__PaintWinAndOverlays(WM_PAINTINFO* pInfo) {
|
|
WM_HWIN hWin;
|
|
WM_Obj* pWin;
|
|
hWin = pInfo->hWin;
|
|
pWin = pInfo->pWin;
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
/* Transparent windows without const outline are drawn as part of the background and can be skipped. */
|
|
if ((pWin->Status & (WM_SF_HASTRANS | WM_SF_CONST_OUTLINE)) != WM_SF_HASTRANS) {
|
|
#endif
|
|
_Paint1(hWin, pWin); /* Draw the window itself */
|
|
#if WM_SUPPORT_TRANSPARENCY
|
|
}
|
|
if (WM__TransWindowCnt != 0) {
|
|
_PaintTransChildren(pWin); /* Draw all transparent children */
|
|
_PaintTransTopSiblings(hWin, pWin); /* Draw all transparent top level siblings */
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _cbPaintMemDev
|
|
*
|
|
* Purpose:
|
|
* This is the routine called by the banding memory device. It calls
|
|
* the same _cbPaint Routine which is also used when drawing directly;
|
|
* the only add. work done is adjustment of the invalid rectangle.
|
|
* This way the invalid rectangle visible by the window callback function
|
|
* is limited to the current band, allowing the callback to optimize
|
|
* better.
|
|
*/
|
|
#if GUI_SUPPORT_MEMDEV
|
|
static void _cbPaintMemDev(void* p) {
|
|
GUI_RECT Rect;
|
|
WM_Obj* pWin = WM_H2P(GUI_Context.hAWin);
|
|
Rect = pWin->InvalidRect;
|
|
pWin->InvalidRect = GUI_Context.ClipRect;
|
|
WM__PaintWinAndOverlays((WM_PAINTINFO*)p);
|
|
pWin->InvalidRect = Rect;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _Paint
|
|
Returns:
|
|
1: a window has been redrawn
|
|
0: No window has been drawn
|
|
*/
|
|
static int _Paint(WM_HWIN hWin, WM_Obj* pWin) {
|
|
int Ret = 0;
|
|
if (pWin->Status & WM_SF_INVALID) {
|
|
if (pWin->cb) {
|
|
if (WM__ClipAtParentBorders(&pWin->InvalidRect, hWin)) {
|
|
WM_PAINTINFO Info;
|
|
Info.hWin = hWin;
|
|
Info.pWin = pWin;
|
|
WM_SelectWindow(hWin);
|
|
#if GUI_SUPPORT_MEMDEV
|
|
if (pWin->Status & WM_SF_MEMDEV) {
|
|
int Flags;
|
|
GUI_RECT r = pWin->InvalidRect;
|
|
Flags = (pWin->Status & WM_SF_HASTRANS) ? GUI_MEMDEV_HASTRANS : GUI_MEMDEV_NOTRANS;
|
|
/*
|
|
* Currently we treat a desktop window as transparent, because per default it does not repaint itself.
|
|
*/
|
|
if (pWin->hParent == 0) {
|
|
Flags = GUI_MEMDEV_HASTRANS;
|
|
}
|
|
GUI_MEMDEV_Draw(&r, _cbPaintMemDev, &Info, 0, Flags);
|
|
} else
|
|
#endif
|
|
{
|
|
WM__PaintWinAndOverlays(&Info);
|
|
Ret = 1; /* Something has been done */
|
|
}
|
|
}
|
|
}
|
|
/* We purposly clear the invalid flag after painting so we can still query the invalid rectangle while painting */
|
|
pWin->Status &= ~WM_SF_INVALID; /* Clear invalid flag */
|
|
if (pWin->Status & WM_CF_MEMDEV_ON_REDRAW) {
|
|
pWin->Status |= WM_CF_MEMDEV;
|
|
}
|
|
WM__NumInvalidWindows--;
|
|
}
|
|
return Ret; /* Nothing done */
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* _DrawNext
|
|
*/
|
|
static void _DrawNext(void) {
|
|
int UpdateRem = 1;
|
|
WM_HWIN iWin = (NextDrawWin == WM_HWIN_NULL) ? WM__FirstWin : NextDrawWin;
|
|
GUI_CONTEXT ContextOld;
|
|
GUI_SaveContext(&ContextOld);
|
|
/* Make sure the next window to redraw is valid */
|
|
for (; iWin && UpdateRem; ) {
|
|
WM_Obj* pWin = WM_H2P(iWin);
|
|
if (_Paint(iWin, pWin)) {
|
|
UpdateRem--; /* Only the given number of windows at a time ... */
|
|
}
|
|
iWin = pWin->hNextLin;
|
|
}
|
|
NextDrawWin = iWin; /* Remember the window */
|
|
GUI_RestoreContext(&ContextOld);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_Exec1
|
|
*/
|
|
int WM_Exec1(void) {
|
|
/* Poll PID if necessary */
|
|
if (WM_pfPollPID) {
|
|
WM_pfPollPID();
|
|
}
|
|
if (WM_pfHandlePID) {
|
|
if (WM_pfHandlePID())
|
|
return 1; /* We have done something ... */
|
|
}
|
|
if (GUI_PollKeyMsg()) {
|
|
return 1; /* We have done something ... */
|
|
}
|
|
if (WM_IsActive && WM__NumInvalidWindows) {
|
|
WM_LOCK();
|
|
_DrawNext();
|
|
WM_UNLOCK();
|
|
return 1; /* We have done something ... */
|
|
}
|
|
return 0; /* There was nothing to do ... */
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_Exec
|
|
*/
|
|
int WM_Exec(void) {
|
|
int r = 0;
|
|
while (WM_Exec1()) {
|
|
r = 1; /* We have done something */
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* cbBackWin
|
|
*
|
|
* Purpose
|
|
* Callback for background window
|
|
*
|
|
*/
|
|
static void cbBackWin( WM_MESSAGE* pMsg) {
|
|
const WM_KEY_INFO* pKeyInfo;
|
|
switch (pMsg->MsgId) {
|
|
case WM_KEY:
|
|
pKeyInfo = (const WM_KEY_INFO*)pMsg->Data.p;
|
|
if (pKeyInfo->PressedCnt == 1) {
|
|
GUI_StoreKey(pKeyInfo->Key);
|
|
}
|
|
break;
|
|
case WM_PAINT:
|
|
{
|
|
int LayerIndex;
|
|
#if GUI_NUM_LAYERS > 1
|
|
LayerIndex = _DesktopHandle2Index(pMsg->hWin);
|
|
#else
|
|
LayerIndex = 0;
|
|
#endif
|
|
if (WM__aBkColor[LayerIndex] != GUI_INVALID_COLOR) {
|
|
GUI_SetBkColor(WM__aBkColor[LayerIndex]);
|
|
GUI_Clear();
|
|
}
|
|
}
|
|
default:
|
|
WM_DefaultProc(pMsg);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_Activate
|
|
*/
|
|
void WM_Activate(void) {
|
|
WM_IsActive = 1; /* Running */
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_Deactivate
|
|
*/
|
|
void WM_Deactivate(void) {
|
|
WM_IsActive = 0; /* No clipping performed by WM */
|
|
WM_LOCK();
|
|
LCD_SetClipRectMax();
|
|
WM_UNLOCK();
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_DefaultProc
|
|
*
|
|
* Purpose
|
|
* Default callback for windows
|
|
* Any window should call this routine in the "default" part of the
|
|
* its callback function for messages it does not handle itself.
|
|
*
|
|
*/
|
|
void WM_DefaultProc(WM_MESSAGE* pMsg) {
|
|
WM_HWIN hWin = pMsg->hWin;
|
|
const void *p = pMsg->Data.p;
|
|
WM_Obj* pWin = WM_H2P(hWin);
|
|
/* Exec message */
|
|
switch (pMsg->MsgId) {
|
|
case WM_GET_INSIDE_RECT: /* return client window in absolute (screen) coordinates */
|
|
WM__GetClientRectWin(pWin, (GUI_RECT*)p);
|
|
break;
|
|
case WM_GET_CLIENT_WINDOW: /* return handle to client window. For most windows, there is no seperate client window, so it is the same handle */
|
|
pMsg->Data.v = (int)hWin;
|
|
return; /* Message handled */
|
|
case WM_KEY:
|
|
WM_SendToParent(hWin, pMsg);
|
|
return; /* Message handled */
|
|
case WM_GET_BKCOLOR:
|
|
pMsg->Data.Color = GUI_INVALID_COLOR;
|
|
return; /* Message handled */
|
|
case WM_NOTIFY_ENABLE:
|
|
WM_InvalidateWindow(hWin);
|
|
return; /* Message handled */
|
|
}
|
|
/* Message not handled. If it queries something, we return 0 to be on the safe side. */
|
|
pMsg->Data.v = 0;
|
|
pMsg->Data.p = 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* WM_Init
|
|
*/
|
|
void WM_Init(void) {
|
|
if (!_IsInited) {
|
|
NextDrawWin = WM__FirstWin = WM_HWIN_NULL;
|
|
GUI_Context.WM__pUserClipRect = NULL;
|
|
WM__NumWindows = WM__NumInvalidWindows =0;
|
|
/* Make sure we have at least one window. This greatly simplifies the
|
|
drawing routines as they do not have to check if the window is valid.
|
|
*/
|
|
#if GUI_NUM_LAYERS == 1
|
|
WM__ahDesktopWin[0] = WM_CreateWindow(0, 0, GUI_XMAX, GUI_YMAX, WM_CF_SHOW, cbBackWin, 0);
|
|
WM__aBkColor[0] = GUI_INVALID_COLOR;
|
|
WM_InvalidateWindow(WM__ahDesktopWin[0]); /* Required because a desktop window has no parent. */
|
|
#else
|
|
{
|
|
int i;
|
|
for (i = 0; i < GUI_NUM_LAYERS; i++) {
|
|
WM__ahDesktopWin[i] = WM_CreateWindowAsChild(0, 0, GUI_XMAX, GUI_YMAX, WM_UNATTACHED, WM_CF_SHOW, cbBackWin, 0);
|
|
WM__aBkColor[i] = GUI_INVALID_COLOR;
|
|
WM_InvalidateWindow(WM__ahDesktopWin[i]); /* Required because a desktop window has no parent. */
|
|
}
|
|
}
|
|
#endif
|
|
/* Register the critical handles ... Note: This could be moved into the module setting the Window handle */
|
|
WM__AddCriticalHandle(&WM__CHWinModal);
|
|
WM__AddCriticalHandle(&WM__CHWinLast);
|
|
|
|
WM_SelectWindow(WM__ahDesktopWin[0]);
|
|
WM_Activate();
|
|
_IsInited =1;
|
|
}
|
|
}
|
|
|
|
|
|
#else
|
|
void WM(void) {} /* avoid empty object files */
|
|
#endif /* GUI_WINSUPPORT */
|
|
|
|
/*************************** End of file ****************************/
|
|
|