aboutsummaryrefslogtreecommitdiffstats
path: root/src/gwin/gwin_wm.c
blob: 6bc5013b441ac5aeabffe37f44885dff9ce82c1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import, division, print_function

import abc

import six


class CipherBackend(six.with_metaclass(abc.ABCMeta)):
    @abc.abstractmethod
    def cipher_supported(self, cipher, mode):
        """
        Return True if the given cipher and mode are supported.
        """

    @abc.abstractmethod
    def create_symmetric_encryption_ctx(self, cipher, mode):
        """
        Get a CipherContext that can be used for encryption.
        """

    @abc.abstractmethod
    def create_symmetric_decryption_ctx(self, cipher, mode):
        """
        Get a CipherContext that can be used for decryption.
        """


class HashBackend(six.with_metaclass(abc.ABCMeta)):
    @abc.abstractmethod
    def hash_supported(self, algorithm):
        """
        Return True if the hash algorithm is supported by this backend.
        """

    @abc.abstractmethod
    def create_hash_ctx(self, algorithm):
        """
        Create a HashContext for calculating a message digest.
        """


class HMACBackend(six.with_metaclass(abc.ABCMeta)):
    @abc.abstractmethod
    def hmac_supported(self, algorithm):
        """
        Return True if the hash algorithm is supported for HMAC by this
        backend.
        """

    @abc.abstractmethod
    def create_hmac_ctx(self, key, algorithm):
        """
        Create a HashContext for calculating a message authentication code.
        """


class PBKDF2HMACBackend(six.with_metaclass(abc.ABCMeta)):
    @abc.abstractmethod
    def pbkdf2_hmac_supported(self, algorithm):
        """
        Return True if the hash algorithm is supported for PBKDF2 by this
        backend.
        """

    @abc.abstractmethod
    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
                           key_material):
        """
        Return length bytes derived from provided PBKDF2 parameters.
        """
='n489' href='#n489'>489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996
/*
 * 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
	gfxQueueASyncRemove(&_GWINList, &gh->wmq);
}

static void WM_Redraw(GHandle gh) {
	#if GWIN_NEED_CONTAINERS
		redo_redraw:
	#endif
	if ((gh->flags & GWIN_FLG_SYSVISIBLE)) {
		if (gh->vmt->Redraw)
			gh->vmt->Redraw(gh);
		else if ((gh->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 (!(gh->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 ((gh->flags & (GWIN_FLG_CONTAINER|GWIN_FLG_PARENTREVEAL)) == GWIN_FLG_CONTAINER) {

				// Container redraw is done
				gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);

				for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh))
					_gwinUpdate(gh);
				return;
			}
		#endif

	} else {
		if ((gh->flags & GWIN_FLG_BGREDRAW)) {
			GHandle		gx;

			#if GWIN_NEED_CONTAINERS
				if (gh->parent) {
					// Child redraw is done
					gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);


					// 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);
				}
			}

		}
	}

	// Redraw is done
	gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL);
}

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.

	gfxQueueASyncRemove(&_GWINList, &gh->wmq);
	gfxQueueASyncPut(&_GWINList, &gh->wmq);

	// Redraw the window
	_gwinUpdate(gh);
}

#endif /* GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER */
/** @} */