version 2.8
gwin_wm.c
Go to the documentation of this file.
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.org/license.html
6  */
7 
8 /**
9  * @file src/gwin/gwin_wm.c
10  * @brief GWIN sub-system window manager code
11  */
12 
13 #include "../../gfx.h"
14 
15 #if GFX_USE_GWIN && !GWIN_NEED_WINDOWMANAGER
16  /**
17  * A really nasty default implementation for the simplest of systems
18  */
19 
20 
21  #include "gwin_class.h"
22 
23  // Needed if there is no window manager
24  #define MIN_WIN_WIDTH 1
25  #define MIN_WIN_HEIGHT 1
26 
27  static gfxMutex gmutex;
28 
29  void _gwmInit(void) {
30  gfxMutexInit(&gmutex);
31  }
32 
33  void _gwmDeinit(void) {
34  gfxMutexDestroy(&gmutex);
35  }
36 
37  bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) {
38  gh->x = gh->y = gh->width = gh->height = 0;
39  gwinMove(gh, pInit->x, pInit->y);
40  gwinResize(gh, pInit->width, pInit->height);
41  return TRUE;
42  }
43 
44  void _gwinFlushRedraws(GRedrawMethod how) {
45  (void) how;
46 
47  // We are always flushed
48  }
49 
50 
51  #if GDISP_NEED_CLIP
52  static void getLock(GHandle gh) {
53  gfxMutexEnter(&gmutex);
54  gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
55  }
56  static void exitLock(GHandle gh) {
58  gfxMutexExit(&gmutex);
59  }
60  #else
61  #define getLock(gh) gfxMutexEnter(&gmutex)
62  #define exitLock(gh) gfxMutexExit(&gmutex)
63  #endif
64 
65  void _gwinUpdate(GHandle gh) {
66  if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
67  if (gh->vmt->Redraw) {
68  getLock(gh);
69  gh->vmt->Redraw(gh);
70  exitLock(gh);
71  } else if ((gh->flags & GWIN_FLG_BGREDRAW)) {
72  getLock(gh);
73  gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
74  exitLock(gh);
75  if (gh->vmt->AfterClear)
76  gh->vmt->AfterClear(gh);
77  }
78  } else if ((gh->flags & GWIN_FLG_BGREDRAW)) {
79  getLock(gh);
80  gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor());
81  exitLock(gh);
82  }
83  gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
84  }
85 
86  bool_t _gwinDrawStart(GHandle gh) {
87  if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
88  return FALSE;
89 
90  getLock(gh);
91  return TRUE;
92  }
93 
94  void _gwinDrawEnd(GHandle gh) {
95  (void) gh;
96  exitLock(gh);
97  }
98 
99  void gwinSetVisible(GHandle gh, bool_t visible) {
100  if (visible) {
101  if (!(gh->flags & GWIN_FLG_VISIBLE)) {
102  gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_BGREDRAW);
103  _gwinUpdate(gh);
104  }
105  } else {
106  if ((gh->flags & GWIN_FLG_VISIBLE)) {
107  gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE);
108  gh->flags |= GWIN_FLG_BGREDRAW;
109  _gwinUpdate(gh);
110  }
111  }
112  }
113 
114  void gwinSetEnabled(GHandle gh, bool_t enabled) {
115  if (enabled) {
116  if (!(gh->flags & GWIN_FLG_ENABLED)) {
117  gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
118  _gwinUpdate(gh);
119  }
120  } else {
121  if ((gh->flags & GWIN_FLG_ENABLED)) {
122  gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
123  _gwinUpdate(gh);
124  }
125  }
126  }
127 
128  void gwinMove(GHandle gh, coord_t x, coord_t y) {
129  gh->x = x; gh->y = y;
130  if (gh->x < 0) gh->x = 0;
131  if (gh->y < 0) gh->y = 0;
132  if (gh->x > gdispGGetWidth(gh->display)-MIN_WIN_WIDTH) gh->x = gdispGGetWidth(gh->display)-MIN_WIN_WIDTH;
133  if (gh->y > gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT) gh->y = gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT;
134  if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x;
135  if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y;
136  _gwinUpdate(gh);
137  }
138 
139  void gwinResize(GHandle gh, coord_t width, coord_t height) {
140  gh->width = width; gh->height = height;
141  if (gh->width < MIN_WIN_WIDTH) { gh->width = MIN_WIN_WIDTH; }
142  if (gh->height < MIN_WIN_HEIGHT) { gh->height = MIN_WIN_HEIGHT; }
143  if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x;
144  if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y;
145  _gwinUpdate(gh);
146  }
147 
148  void gwinRedraw(GHandle gh) {
149  _gwinUpdate(gh);
150  }
151 #endif
152 
153 #if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER
154 
155 #include "gwin_class.h"
156 
157 /*-----------------------------------------------
158  * Data
159  *-----------------------------------------------*/
160 
161 // The default window manager
162 extern const GWindowManager GNullWindowManager;
163 GWindowManager * _GWINwm;
164 bool_t _gwinFlashState;
165 static gfxSem gwinsem;
166 static gfxQueueASync _GWINList;
167 #if GWIN_NEED_FLASHING
168  static GTimer FlashTimer;
169 #endif
170 #if !GWIN_REDRAW_IMMEDIATE
171  static GTimer RedrawTimer;
172  static void RedrawTimerFn(void *param);
173 #endif
174 static volatile uint8_t RedrawPending;
175  #define DOREDRAW_INVISIBLES 0x01
176  #define DOREDRAW_VISIBLES 0x02
177  #define DOREDRAW_FLASHRUNNING 0x04
178 
179 
180 /*-----------------------------------------------
181  * Window Routines
182  *-----------------------------------------------*/
183 
184 void _gwmInit(void)
185 {
186  gfxSemInit(&gwinsem, 1, 1);
187  gfxQueueASyncInit(&_GWINList);
188  #if GWIN_NEED_FLASHING
189  gtimerInit(&FlashTimer);
190  #endif
191  #if !GWIN_REDRAW_IMMEDIATE
192  gtimerInit(&RedrawTimer);
193  gtimerStart(&RedrawTimer, RedrawTimerFn, 0, TRUE, TIME_INFINITE);
194  #endif
195  _GWINwm = (GWindowManager *)&GNullWindowManager;
196  _GWINwm->vmt->Init();
197 }
198 
199 void _gwmDeinit(void)
200 {
201  GHandle gh;
202 
203  while((gh = gwinGetNextWindow(0)))
204  gwinDestroy(gh);
205 
206  _GWINwm->vmt->DeInit();
207  #if !GWIN_REDRAW_IMMEDIATE
208  gtimerDeinit(&RedrawTimer);
209  #endif
210  gfxQueueASyncDeinit(&_GWINList);
211  gfxSemDestroy(&gwinsem);
212 }
213 
214 #if GWIN_REDRAW_IMMEDIATE
215  #define TriggerRedraw(void) _gwinFlushRedraws(REDRAW_NOWAIT);
216 #else
217  #define TriggerRedraw() gtimerJab(&RedrawTimer);
218 
219  static void RedrawTimerFn(void *param) {
220  (void) param;
221  _gwinFlushRedraws(REDRAW_NOWAIT);
222  }
223 #endif
224 
225 void _gwinFlushRedraws(GRedrawMethod how) {
226  GHandle gh;
227 
228  // Do we really need to do anything?
229  if (!RedrawPending)
230  return;
231 
232  // Obtain the drawing lock
233  if (how == REDRAW_WAIT)
234  gfxSemWait(&gwinsem, TIME_INFINITE);
235  else if (how == REDRAW_NOWAIT && !gfxSemWait(&gwinsem, TIME_IMMEDIATE))
236  // Someone is drawing - They will do the redraw when they are finished
237  return;
238 
239  // Do loss of visibility first
240  while ((RedrawPending & DOREDRAW_INVISIBLES)) {
241  RedrawPending &= ~DOREDRAW_INVISIBLES; // Catch new requests
242 
243  for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
244  if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != GWIN_FLG_NEEDREDRAW)
245  continue;
246 
247  // Do the redraw
248  #if GDISP_NEED_CLIP
249  gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
250  _GWINwm->vmt->Redraw(gh);
252  #else
253  _GWINwm->vmt->Redraw(gh);
254  #endif
255 
256  // Postpone further redraws
257  #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP
258  if (how == REDRAW_NOWAIT) {
259  RedrawPending |= DOREDRAW_INVISIBLES;
260  TriggerRedraw();
261  goto releaselock;
262  }
263  #endif
264  }
265  }
266 
267  // Do the visible windows next
268  while ((RedrawPending & DOREDRAW_VISIBLES)) {
269  RedrawPending &= ~DOREDRAW_VISIBLES; // Catch new requests
270 
271  for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
272  if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE))
273  continue;
274 
275  // Do the redraw
276  #if GDISP_NEED_CLIP
277  gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
278  _GWINwm->vmt->Redraw(gh);
280  #else
281  _GWINwm->vmt->Redraw(gh);
282  #endif
283 
284  // Postpone further redraws (if there are any and the options are set right)
285  #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP
286  if (how == REDRAW_NOWAIT) {
287  while((gh = gwinGetNextWindow(gh))) {
288  if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) {
289  RedrawPending |= DOREDRAW_VISIBLES;
290  TriggerRedraw();
291  break;
292  }
293  }
294  goto releaselock;
295  }
296  #endif
297  }
298  }
299 
300  #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP
301  releaselock:
302  #endif
303 
304  // Release the lock
305  if (how == REDRAW_WAIT || how == REDRAW_NOWAIT)
306  gfxSemSignal(&gwinsem);
307 }
308 
309 void _gwinUpdate(GHandle gh) {
310  // Only redraw if visible
311  if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
312  return;
313 
314  // Mark for redraw
315  gh->flags |= GWIN_FLG_NEEDREDRAW;
316  RedrawPending |= DOREDRAW_VISIBLES;
317 
318  // Asynchronous redraw
319  TriggerRedraw();
320 }
321 
322 #if GWIN_NEED_CONTAINERS
323  void _gwinRippleVisibility(void) {
324  GHandle gh;
325 
326  // Check each window's visibility is consistent with its parents
327  for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
328  switch(gh->flags & (GWIN_FLG_SYSVISIBLE|GWIN_FLG_VISIBLE)) {
329  case GWIN_FLG_VISIBLE:
330  if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) {
331  // We have been made visible
332  gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
333 
334  // Do we want to grab the focus
335  _gwinFixFocus(gh);
336 
337  RedrawPending |= DOREDRAW_VISIBLES;
338  }
339  break;
340  case (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE):
341  if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))
342  break;
343 
344  // Parent has been made invisible
345  gh->flags &= ~GWIN_FLG_SYSVISIBLE;
346 
347  // No focus for us anymore
348  _gwinFixFocus(gh);
349 
350  break;
351  case GWIN_FLG_SYSVISIBLE:
352  // We have been made invisible
353  gh->flags &= ~GWIN_FLG_SYSVISIBLE;
354  if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) {
355  // The parent is visible so we must clear the area we took
356  gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
357 
358  // No focus for us anymore
359  _gwinFixFocus(gh);
360 
361  RedrawPending |= DOREDRAW_INVISIBLES;
362  }
363  break;
364  }
365  }
366  }
367 #endif
368 
369 bool_t _gwinDrawStart(GHandle gh) {
370  // This test should occur inside the lock. We do this
371  // here as well as an early out (more efficient).
372  if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
373  return FALSE;
374 
375  // Obtain the drawing lock
376  gfxSemWait(&gwinsem, TIME_INFINITE);
377 
378  // Re-test visibility as we may have waited a while
379  if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) {
380  _gwinDrawEnd(gh);
381  return FALSE;
382  }
383 
384  // OK - we are ready to draw.
385  #if GDISP_NEED_CLIP
386  gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
387  #endif
388  return TRUE;
389 }
390 
391 void _gwinDrawEnd(GHandle gh) {
392  // Ensure there is no clip set
393  #if GDISP_NEED_CLIP
395  #endif
396 
397  // Look for something to redraw
398  _gwinFlushRedraws(REDRAW_INSESSION);
399 
400  // Release the lock
401  gfxSemSignal(&gwinsem);
402 }
403 
404 bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) {
405  #if GWIN_NEED_CONTAINERS
406  // Save the parent
407  gh->parent = pInit->parent;
408 
409  // Ensure the display is consistent with any parents
410  if (gh->parent && (!(gh->parent->flags & GWIN_FLG_CONTAINER) || gh->display != gh->parent->display))
411  return FALSE;
412  #endif
413 
414  // Add to the window manager
415  if (!_GWINwm->vmt->Add(gh, pInit))
416  return FALSE;
417 
418  #if GWIN_NEED_CONTAINERS
419  // Notify the parent it has been added
420  if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd)
421  ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd(gh->parent, gh);
422  #endif
423 
424  return TRUE;
425 }
426 
427 void gwinSetWindowManager(struct GWindowManager *gwm) {
428  if (!gwm)
429  gwm = (GWindowManager *)&GNullWindowManager;
430  if (_GWINwm != gwm) {
431  _GWINwm->vmt->DeInit();
432  _GWINwm = gwm;
433  _GWINwm->vmt->Init();
434  }
435 }
436 
437 void gwinRedraw(GHandle gh) {
438  // Only redraw if visible
439  if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
440  return;
441 
442  // Mark for redraw
443  gh->flags |= GWIN_FLG_NEEDREDRAW;
444  RedrawPending |= DOREDRAW_VISIBLES;
445 
446  // Synchronous redraw
447  _gwinFlushRedraws(REDRAW_WAIT);
448 }
449 
450 #if GWIN_NEED_CONTAINERS
451  void gwinSetVisible(GHandle gh, bool_t visible) {
452  if (visible) {
453  // Mark us as visible
454  gh->flags |= GWIN_FLG_VISIBLE;
455  } else {
456  // Mark us as not visible
457  gh->flags &= ~GWIN_FLG_VISIBLE;
458  }
459 
460  // Fix everything up
461  _gwinRippleVisibility();
462  if (RedrawPending)
463  TriggerRedraw();
464  }
465 #else
466  void gwinSetVisible(GHandle gh, bool_t visible) {
467  if (visible) {
468  if (!(gh->flags & GWIN_FLG_VISIBLE)) {
469  gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
470 
471  // Do we want to grab the focus
472  _gwinFixFocus(gh);
473 
474  RedrawPending |= DOREDRAW_VISIBLES;
475  TriggerRedraw();
476  }
477  } else {
478  if ((gh->flags & GWIN_FLG_VISIBLE)) {
479  gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE);
480  gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
481 
482  // No focus for us anymore
483  _gwinFixFocus(gh);
484 
485  RedrawPending |= DOREDRAW_INVISIBLES;
486  TriggerRedraw();
487  }
488  }
489  }
490 #endif
491 
492 #if GWIN_NEED_CONTAINERS
493  // These two sub-functions set/clear system enable recursively.
494  void gwinSetEnabled(GHandle gh, bool_t enabled) {
495  if (enabled) {
496  // Mark us as enabled
497  gh->flags |= GWIN_FLG_ENABLED;
498 
499  // Do we change our real enabled state
500  if (!(gh->flags & GWIN_FLG_SYSENABLED) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) {
501  // Check each window's enabled state is consistent with its parents
502  for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
503  if ((gh->flags & (GWIN_FLG_SYSENABLED|GWIN_FLG_ENABLED)) == GWIN_FLG_ENABLED && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) {
504  gh->flags |= GWIN_FLG_SYSENABLED; // Fix it
505 
506  // Do we want to grab the focus
507  _gwinFixFocus(gh);
508 
509  _gwinUpdate(gh);
510  }
511  }
512  }
513  } else {
514  gh->flags &= ~GWIN_FLG_ENABLED;
515 
516  // Do we need to change our real enabled state
517  if ((gh->flags & GWIN_FLG_SYSENABLED)) {
518  // Check each window's visibility is consistent with its parents
519  for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
520  if ((gh->flags & GWIN_FLG_SYSENABLED) && (!(gh->flags & GWIN_FLG_ENABLED) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSENABLED)))) {
521  gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it
522 
523  // No focus for us anymore
524  _gwinFixFocus(gh);
525 
526  _gwinUpdate(gh);
527  }
528  }
529  }
530  }
531  }
532 #else
533  void gwinSetEnabled(GHandle gh, bool_t enabled) {
534  if (enabled) {
535  if (!(gh->flags & GWIN_FLG_ENABLED)) {
536  gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
537 
538  // Do we want to grab the focus
539  _gwinFixFocus(gh);
540 
541  _gwinUpdate(gh);
542  }
543  } else {
544  if ((gh->flags & GWIN_FLG_ENABLED)) {
545  gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
546 
547  // No focus for us anymore
548  _gwinFixFocus(gh);
549 
550  _gwinUpdate(gh);
551  }
552  }
553  }
554 #endif
555 
556 void gwinMove(GHandle gh, coord_t x, coord_t y) {
557  _GWINwm->vmt->Move(gh, x, y);
558 }
559 
560 void gwinResize(GHandle gh, coord_t width, coord_t height) {
561  _GWINwm->vmt->Size(gh, width, height);
562 }
563 
564 void gwinSetMinMax(GHandle gh, GWindowMinMax minmax) {
565  _GWINwm->vmt->MinMax(gh, minmax);
566 }
567 
568 void gwinRaise(GHandle gh) {
569  _GWINwm->vmt->Raise(gh);
570 }
571 
573  if (gh->flags & GWIN_FLG_MINIMIZED)
574  return GWIN_MINIMIZE;
575  if (gh->flags & GWIN_FLG_MAXIMIZED)
576  return GWIN_MAXIMIZE;
577  return GWIN_NORMAL;
578 }
579 
580 void gwinRedrawDisplay(GDisplay *g, bool_t preserve) {
581  GHandle gh;
582 
583  for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
584 
585  // Skip if it is for a different display
586  if (g && gh->display != g)
587  continue;
588 
589  #if GWIN_NEED_CONTAINERS
590  // Skip if it is not a top level window (parents internally take care of their children)
591  if (gh->parent)
592  continue;
593  #endif
594 
595  // Only visible windows are to be redrawn
596  if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
597  continue;
598 
599  if (!preserve)
600  gh->flags |= GWIN_FLG_BGREDRAW;
601 
602  _gwinUpdate(gh);
603  }
604 }
605 
607  return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList);
608 }
609 
610 #if GWIN_NEED_FLASHING
611  static void FlashTimerFn(void *param) {
612  GHandle gh;
613  (void) param;
614 
615  // Assume we will be stopping
616  RedrawPending &= ~DOREDRAW_FLASHRUNNING;
617 
618  // Swap the flash state
619  _gwinFlashState = !_gwinFlashState;
620 
621  // Redraw all flashing windows
622  for(gh = (GHandle)gfxQueueASyncPeek(&_GWINList); gh; gh = (GHandle)gfxQueueASyncNext(&gh->wmq)) {
623  if ((gh->flags & GWIN_FLG_FLASHING)) {
624  RedrawPending |= DOREDRAW_FLASHRUNNING;
625  _gwinUpdate(gh);
626  }
627  }
628 
629  // Do we have no flashers left?
630  if (!(RedrawPending & DOREDRAW_FLASHRUNNING))
631  gtimerStop(&FlashTimer);
632  }
633 
634  void gwinSetFlashing(GHandle gh, bool_t flash) {
635 
636  // Start flashing?
637  if (flash) {
638  gh->flags |= GWIN_FLG_FLASHING; // A redraw will occur on the next flash period.
639 
640  // Start the flash timer if needed
641  if (!(RedrawPending & DOREDRAW_FLASHRUNNING)) {
642  RedrawPending |= DOREDRAW_FLASHRUNNING;
643 
644  // Ensure we start the timer with flash bit on
645  _gwinFlashState = FALSE;
646  FlashTimerFn(0); // First flash
647  gtimerStart(&FlashTimer, FlashTimerFn, 0, TRUE, GWIN_FLASHING_PERIOD); // Subsequent flashes
648  }
649 
650  // Stop flashing?
651  } else if ((gh->flags & GWIN_FLG_FLASHING)) {
652  gh->flags &= ~GWIN_FLG_FLASHING;
653  // We need to manually redraw as the timer is now turned off for this window
654  _gwinUpdate(gh);
655  }
656  }
657 
658  #if GWIN_NEED_WIDGET
659  const GColorSet *_gwinGetFlashedColor(GWidgetObject *gw, const GColorSet *pcol, bool_t flashOffState) {
660  // Does the flashing state affect the current colors?
661  if ((gw->g.flags & GWIN_FLG_FLASHING) && _gwinFlashState) {
662 
663  // For a pressed state show an unpressed state
664  if (pcol == &gw->pstyle->pressed)
665  pcol = &gw->pstyle->enabled;
666 
667  // For a non-pressed state (if allowed) show a pressed state
668  else if (flashOffState && pcol == &gw->pstyle->enabled)
669  pcol = &gw->pstyle->pressed;
670  }
671  return pcol;
672  }
673  #endif
674 #endif
675 
676 
677 /*-----------------------------------------------
678  * "Null" Window Manager Routines
679  *-----------------------------------------------*/
680 
681 // This is a parent reveal operation
682 #define GWIN_FLG_PARENTREVEAL (GWIN_FIRST_WM_FLAG << 0)
683 
684 // Minimum dimensions
685 #define MIN_WIN_WIDTH 3
686 #define MIN_WIN_HEIGHT 3
687 
688 
689 static void WM_Init(void);
690 static void WM_DeInit(void);
691 static bool_t WM_Add(GHandle gh, const GWindowInit *pInit);
692 static void WM_Delete(GHandle gh);
693 static void WM_Redraw(GHandle gh);
694 static void WM_Size(GHandle gh, coord_t w, coord_t h);
695 static void WM_Move(GHandle gh, coord_t x, coord_t y);
696 static void WM_Raise(GHandle gh);
697 static void WM_MinMax(GHandle gh, GWindowMinMax minmax);
698 
699 static const gwmVMT GNullWindowManagerVMT = {
700  WM_Init,
701  WM_DeInit,
702  WM_Add,
703  WM_Delete,
704  WM_Redraw,
705  WM_Size,
706  WM_Move,
707  WM_Raise,
708  WM_MinMax,
709 };
710 
711 const GWindowManager GNullWindowManager = {
712  &GNullWindowManagerVMT,
713 };
714 
715 static void WM_Init(void) {
716  // We don't need to do anything here.
717  // A full window manager would move the windows around, add borders etc
718 
719  // clear the screen
720  // cycle through the windows already defined displaying them
721  // or cut all the window areas out of the screen and clear the remainder
722 }
723 
724 static void WM_DeInit(void) {
725  // We don't need to do anything here.
726  // A full window manager would remove any borders etc
727 }
728 
729 static bool_t WM_Add(GHandle gh, const GWindowInit *pInit) {
730  // Note the window will not currently be marked as visible
731 
732  // Put it on the end of the queue
733  gfxQueueASyncPut(&_GWINList, &gh->wmq);
734 
735  // Make sure the size/position is valid - prefer position over size.
736  gh->width = MIN_WIN_WIDTH; gh->height = MIN_WIN_HEIGHT;
737  gh->x = gh->y = 0;
738  WM_Move(gh, pInit->x, pInit->y);
739  WM_Size(gh, pInit->width, pInit->height);
740  return TRUE;
741 }
742 
743 static void WM_Delete(GHandle gh) {
744  // Remove it from the window list
745  gfxSemWait(&gwinsem, TIME_INFINITE);
746  gfxQueueASyncRemove(&_GWINList, &gh->wmq);
747  gfxSemSignal(&gwinsem);
748 }
749 
750 static void WM_Redraw(GHandle gh) {
751  uint32_t flags;
752 
753  flags = gh->flags;
754  gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);
755 
756  #if GWIN_NEED_CONTAINERS
757  redo_redraw:
758  #endif
759  if ((flags & GWIN_FLG_SYSVISIBLE)) {
760  if (gh->vmt->Redraw)
761  gh->vmt->Redraw(gh);
762  else if ((flags & GWIN_FLG_BGREDRAW)) {
763  // We can't redraw but we want full coverage so just clear the area
764  gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
765 
766  // Only do an after clear if this is not a parent reveal
767  if (!(flags & GWIN_FLG_PARENTREVEAL) && gh->vmt->AfterClear)
768  gh->vmt->AfterClear(gh);
769  }
770 
771  #if GWIN_NEED_CONTAINERS
772  // If this is container but not a parent reveal, mark any visible children for redraw
773  // We redraw our children here as we have overwritten them in redrawing the parent
774  // as GDISP/GWIN doesn't support complex clipping regions.
775  if ((flags & (GWIN_FLG_CONTAINER|GWIN_FLG_PARENTREVEAL)) == GWIN_FLG_CONTAINER) {
776 
777  // Container redraw is done
778 
779  for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh))
780  _gwinUpdate(gh);
781  return;
782  }
783  #endif
784 
785  } else {
786  if ((flags & GWIN_FLG_BGREDRAW)) {
787  GHandle gx;
788 
789  #if GWIN_NEED_CONTAINERS
790  if (gh->parent) {
791  // Child redraw is done
792 
793  // Get the parent to redraw the area
794  gh = gh->parent;
795 
796  // The parent is already marked for redraw - don't do it now.
797  if ((gh->flags & GWIN_FLG_NEEDREDRAW))
798  return;
799 
800  // Use the existing clipping region and redraw now
801  gh->flags |= (GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);
802  goto redo_redraw;
803  }
804  #endif
805 
806  // Clear the area to the background color
807  gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor());
808 
809  // Now loop over all windows looking for overlaps. Redraw them if they overlap the newly exposed area.
810  for(gx = gwinGetNextWindow(0); gx; gx = gwinGetNextWindow(gx)) {
811  if ((gx->flags & GWIN_FLG_SYSVISIBLE)
812  && gx->display == gh->display
813  && gx->x < gh->x+gh->width && gx->y < gh->y+gh->height && gx->x+gx->width >= gh->x && gx->y+gx->height >= gh->y) {
814  if (gx->vmt->Redraw)
815  gx->vmt->Redraw(gx);
816  else
817  // We can't redraw this window but we want full coverage so just clear the area
818  gdispGFillArea(gx->display, gx->x, gx->y, gx->width, gx->height, gx->bgcolor);
819  }
820  }
821 
822  }
823  }
824 }
825 
826 static void WM_Size(GHandle gh, coord_t w, coord_t h) {
827  coord_t v;
828 
829  #if GWIN_NEED_CONTAINERS
830  if (gh->parent) {
831  // Clip to the container
832  v = gh->parent->x + gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent);
833  if (gh->x+w > v) w = v - gh->x;
834  v = gh->parent->y + gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent);
835  if (gh->y+h > v) h = v - gh->y;
836  }
837  #endif
838 
839  // Clip to the screen
840  v = gdispGGetWidth(gh->display);
841  if (gh->x+w > v) w = v - gh->x;
842  v = gdispGGetHeight(gh->display);
843  if (gh->y+h > v) h = v - gh->y;
844 
845  // Give it a minimum size
846  if (w < MIN_WIN_WIDTH) w = MIN_WIN_WIDTH;
847  if (h < MIN_WIN_HEIGHT) h = MIN_WIN_HEIGHT;
848 
849  // If there has been no resize just exit
850  if (gh->width == w && gh->height == h)
851  return;
852 
853  // Set the new size and redraw
854  if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
855  if (w >= gh->width && h >= gh->height) {
856 
857  // The new size is larger - just redraw
858  gh->width = w; gh->height = h;
859  _gwinUpdate(gh);
860 
861  } else {
862  // We need to make this window invisible and ensure that has been drawn
863  gwinSetVisible(gh, FALSE);
864  _gwinFlushRedraws(REDRAW_WAIT);
865 
866  // Resize
867  gh->width = w; gh->height = h;
868 
869  #if GWIN_NEED_CONTAINERS
870  // Any children outside the new area need to be moved
871  if ((gh->flags & GWIN_FLG_CONTAINER)) {
872  GHandle child;
873 
874  // Move to their old relative location. THe WM_Move() will adjust as necessary
875  for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
876  WM_Move(child, child->x-gh->x-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-gh->y-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
877  }
878  #endif
879 
880  // Mark it visible again in its new location
881  gwinSetVisible(gh, TRUE);
882  }
883  } else {
884  gh->width = w; gh->height = h;
885 
886  #if GWIN_NEED_CONTAINERS
887  // Any children outside the new area need to be moved
888  if ((gh->flags & GWIN_FLG_CONTAINER)) {
889  GHandle child;
890 
891  // Move to their old relative location. THe WM_Move() will adjust as necessary
892  for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
893  WM_Move(child, child->x-gh->x-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-gh->y-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
894  }
895  #endif
896  }
897 }
898 
899 static void WM_Move(GHandle gh, coord_t x, coord_t y) {
900  coord_t u, v;
901 
902  #if GWIN_NEED_CONTAINERS
903  if (gh->parent) {
904  // Clip to the parent size
905  u = gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent);
906  v = gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent);
907  } else
908  #endif
909  {
910  // Clip to the screen
911  u = gdispGGetWidth(gh->display);
912  v = gdispGGetHeight(gh->display);
913  }
914 
915  // Make sure we are positioned in the appropriate area
916  if (x+gh->width > u) x = u-gh->width;
917  if (x < 0) x = 0;
918  if (y+gh->height > v) y = v-gh->height;
919  if (y < 0) y = 0;
920 
921  // Make sure we don't overflow the appropriate area
922  u -= x;
923  v -= y;
924  if (gh->width < u) u = gh->width;
925  if (gh->height < v) v = gh->height;
926  if (u != gh->width || v != gh->height)
927  WM_Size(gh, u, v);
928 
929  #if GWIN_NEED_CONTAINERS
930  if (gh->parent) {
931  // Convert to a screen relative position
932  x += gh->parent->x + ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent);
933  y += gh->parent->y + ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent);
934  }
935  #endif
936 
937  // If there has been no move just exit
938  if (gh->x == x && gh->y == y)
939  return;
940 
941  // Clear the old area and then redraw
942  if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
943  // We need to make this window invisible and ensure that has been drawn
944  gwinSetVisible(gh, FALSE);
945  _gwinFlushRedraws(REDRAW_WAIT);
946 
947  // Do the move
948  u = gh->x; gh->x = x;
949  v = gh->y; gh->y = y;
950 
951  #if GWIN_NEED_CONTAINERS
952  // Any children need to be moved
953  if ((gh->flags & GWIN_FLG_CONTAINER)) {
954  GHandle child;
955 
956  // Move to their old relative location. THe WM_Move() will adjust as necessary
957  for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
958  WM_Move(child, child->x-u-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-v-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
959  }
960  #endif
961 
962  gwinSetVisible(gh, TRUE);
963  } else {
964  u = gh->x; gh->x = x;
965  v = gh->y; gh->y = y;
966 
967  #if GWIN_NEED_CONTAINERS
968  // Any children need to be moved
969  if ((gh->flags & GWIN_FLG_CONTAINER)) {
970  GHandle child;
971 
972  // Move to their old relative location. THe WM_Move() will adjust as necessary
973  for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child))
974  WM_Move(child, child->x-u-((const gcontainerVMT *)gh->vmt)->LeftBorder(gh), child->y-v-((const gcontainerVMT *)gh->vmt)->TopBorder(gh));
975  }
976  #endif
977  }
978 }
979 
980 static void WM_MinMax(GHandle gh, GWindowMinMax minmax) {
981  (void)gh; (void) minmax;
982  // We don't support minimising, maximising or restoring
983 }
984 
985 static void WM_Raise(GHandle gh) {
986  // Take it off the list and then put it back on top
987  // The order of the list then reflects the z-order.
988 
989  gfxSemWait(&gwinsem, TIME_INFINITE);
990 
991  gfxQueueASyncRemove(&_GWINList, &gh->wmq);
992  gfxQueueASyncPut(&_GWINList, &gh->wmq);
993 
994  #if GWIN_NEED_CONTAINERS
995  // Any children need to be raised too
996  if ((gh->flags & GWIN_FLG_CONTAINER)) {
997  GHandle gx;
998  GHandle child;
999  bool_t restart;
1000 
1001  // Raise the children too
1002  // Note: Children can also have their own children so after each move we have to start again.
1003  for (gx = gwinGetNextWindow(0); gx; gx = gwinGetNextWindow(gx)) {
1004  if ((gx->flags & GWIN_FLG_CONTAINER)) {
1005  restart = FALSE;
1006  for (child = gwinGetNextWindow(0); child && child != gx; child = gwinGetNextWindow(child)) {
1007  if (child->parent == gx) {
1008  // Oops - this child is behind its parent. Move it to the front.
1009  gfxQueueASyncRemove(&_GWINList, &child->wmq);
1010  gfxQueueASyncPut(&_GWINList, &child->wmq);
1011 
1012  // Restart at the front of the list for this parent container as we have moved this child
1013  // to the end of the list. We also need to restart everything once this container is done.
1014  child = 0;
1015  restart = TRUE;
1016  }
1017  }
1018  if (restart)
1019  gx = 0;
1020  }
1021  }
1022  }
1023  #endif
1024 
1025  gfxSemSignal(&gwinsem);
1026 
1027  // Redraw the window
1028  _gwinUpdate(gh);
1029 }
1030 
1031 #endif /* GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER */
1032 /** @} */
void gwinSetFlashing(GHandle gh, bool_t flash)
Set a window or widget to flash.
const struct gwinVMT * vmt
Definition: gwin.h:45
void gwinRedraw(GHandle gh)
Redraw a window.
void gwinSetMinMax(GHandle gh, GWindowMinMax minmax)
Minimize, Maximize or Restore a window.
void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit)
Initialise a Counted Semaphore.
The Virtual Method Table for a window manager.
Definition: gwin_class.h:154
The structure to initialise a GWIN.
Definition: gwin.h:75
void gtimerDeinit(GTimer *pt)
Deinitialise a timer.
Definition: gtimer.c:134
void gtimerInit(GTimer *pt)
Initialise a timer.
Definition: gtimer.c:129
GHandle gwinGetNextWindow(GHandle gh)
Get the next window in the z-order.
uint32_t flags
Definition: gwin.h:53
int16_t coord_t
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
coord_t y
Definition: gwin.h:77
A GTimer structure.
Definition: gtimer.h:52
void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy)
Clip all drawing to the defined area.
The Virtual Method Table for a container.
Definition: gwin_class.h:132
coord_t y
Definition: gwin.h:48
coord_t x
Definition: gwin.h:47
coord_t gdispGGetHeight(GDisplay *g)
Get the display height in pixels.
void gwinResize(GHandle gh, coord_t width, coord_t height)
Resize a window.
void gtimerStop(GTimer *pt)
Stop a timer (periodic or otherwise)
Definition: gtimer.c:190
void gwinRedrawDisplay(GDisplay *g, bool_t preserve)
Redraw a display.
The GColorSet structure.
Definition: gwin_widget.h:37
GWindowObject g
Definition: gwin_widget.h:119
void gwinRaise(GHandle gh)
Raise a window to the top of the z-order.
#define FALSE
Generic &#39;false&#39; boolean constant.
Definition: gfx.h:31
coord_t gdispGGetWidth(GDisplay *g)
Get the display width in pixels.
void gfxSemDestroy(gfxSem *psem)
Destroy a Counted Semaphore.
GWindowMinMax
A window&#39;s minimized, maximized or normal size.
Definition: gwin.h:90
void(* AfterClear)(GWindowObject *gh)
Definition: gwin_class.h:60
A queue.
Definition: gqueue.h:54
GColorSet pressed
Definition: gwin_widget.h:57
void gwinDestroy(GHandle gh)
Destroy a window (of any type). Releases any dynamically allocated memory.
bool_t gfxSemWait(gfxSem *psem, delaytime_t ms)
Wait on a semaphore.
void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color)
Fill an area with a color.
GColorSet enabled
Definition: gwin_widget.h:55
void gwinSetVisible(GHandle gh, bool_t visible)
Sets whether a window is visible or not.
A semaphore.
Definition: gos.h:105
color_t gwinGetDefaultBgColor(void)
Get the default background color for all new GWIN windows.
const GWidgetStyle * pstyle
Definition: gwin_widget.h:123
void gfxMutexExit(gfxMutex *pmutex)
Exit the critical code region protected by the mutex.
The GWIN Widget structure.
Definition: gwin_widget.h:118
color_t bgcolor
Definition: gwin.h:52
GDisplay * display
Definition: gwin.h:46
coord_t height
Definition: gwin.h:79
GHandle gwinGetFirstChild(GHandle gh)
Get the first child window.
coord_t x
Definition: gwin.h:76
coord_t height
Definition: gwin.h:50
void gwinSetEnabled(GHandle gh, bool_t enabled)
Enable or disable a window.
GHandle gwinGetSibling(GHandle gh)
Get the next child window in the z-order.
void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec)
Set a timer going or alter its properties if it is already going.
Definition: gtimer.c:139
void gfxMutexEnter(gfxMutex *pmutex)
Enter the critical code region protected by the mutex.
coord_t width
Definition: gwin.h:49
void gfxSemSignal(gfxSem *psem)
Signal a semaphore.
A mutex.
Definition: gos.h:111
A window object structure.
Definition: gwin.h:40
void(* Redraw)(GWindowObject *gh)
Definition: gwin_class.h:59
void gfxMutexInit(gfxMutex *pmutex)
Initialise a mutex to protect a region of code from other threads.
#define GWIN_FLASHING_PERIOD
What is the period for the flashing timer.
Definition: gwin_options.h:388
void gfxMutexDestroy(gfxMutex *pmutex)
Destroy a Mutex.
#define gdispGUnsetClip(g)
Reset the clip area to the full screen.
Definition: gdisp.h:1225
coord_t width
Definition: gwin.h:78
void gwinSetWindowManager(struct GWindowManager *gwm)
Set the window manager for the GWIN system.
#define TRUE
Generic &#39;true&#39; boolean constant.
Definition: gfx.h:38
void gwinMove(GHandle gh, coord_t x, coord_t y)
Move a window.
GWindowMinMax gwinGetMinMax(GHandle gh)
Get the Minimized/Maximized state of a window.