summaryrefslogtreecommitdiff
path: root/grlib/widget.c
diff options
context:
space:
mode:
authorYuval Adam <yuval@y3xz.com>2015-03-13 12:24:02 +0200
committerYuval Adam <yuval@y3xz.com>2015-03-13 12:24:02 +0200
commit788db64b8642bf31de6930d18a62177c64163ee0 (patch)
tree38e8291d1fcfceac7ad2a393ad74396ed6453d24 /grlib/widget.c
parentc686407d271989d5c4bcc1b3c4f5bb6fcd1846a8 (diff)
Add grlib, fixes #5
Diffstat (limited to 'grlib/widget.c')
-rw-r--r--grlib/widget.c1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/grlib/widget.c b/grlib/widget.c
new file mode 100644
index 0000000..d09ac9e
--- /dev/null
+++ b/grlib/widget.c
@@ -0,0 +1,1227 @@
+//*****************************************************************************
+//
+// widget.c - Generic widget tree handling code.
+//
+// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the Tiva Graphics Library.
+//
+//*****************************************************************************
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "driverlib/debug.h"
+#include "grlib/grlib.h"
+#include "grlib/widget.h"
+
+//*****************************************************************************
+//
+//! \addtogroup widget_api
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Flags that indicate how messages from the message queue are processed. They
+// can be sent via either a pre-order or post-order search, and can optionally
+// be sent to no other widgets once one accepts the message.
+//
+//*****************************************************************************
+#define MQ_FLAG_POST_ORDER 1
+#define MQ_FLAG_STOP_ON_SUCCESS 2
+
+//*****************************************************************************
+//
+// The size of the message queue. In order to make the queue pointer
+// arithmetic more efficient, this should be a power of two.
+//
+//*****************************************************************************
+#define QUEUE_SIZE 16
+
+#ifdef DEBUG_MSGQ
+//*****************************************************************************
+//
+// In debug builds, keep track of the number of cases where a message was
+// lost due to the queue being full. We count the following occurrences:
+//
+// 1. All messages discarded due to queue overflow (g_ui32MQOverflow)
+// 2. Messages other than WIDGET_MSG_PTR_MOVE discarded due to queue
+// overflow (g_ui32MQNonMouseOverflow). In this case, we also remember the
+// last message that was discarded (g_ui32MQLastLostMsg).
+// 3. The number of calls to WidgetMessageQueueAdd that fail due to the queue
+// mutex already being held.
+// 4. The number of cases where WidgetMessageQueueAdd reused an unread
+// WIDGET_MSG_PTR_MOVE message when a second one arrived before the previous
+// one had been processed.
+//
+//*****************************************************************************
+uint32_t g_ui32MQOverflow = 0;
+uint32_t g_ui32MQNonMouseOverflow = 0;
+uint32_t g_ui32MQLastLostMsg = 0;
+uint32_t g_ui32MQMutexClash = 0;
+uint32_t g_ui32MQMoveOverwrite = 0;
+#endif
+
+//*****************************************************************************
+//
+// This structure describes the message queue used to hold widget messages.
+//
+//*****************************************************************************
+typedef struct
+{
+ //
+ // The flags that describe how this message should be processed; this is
+ // defined by the MQ_FLAG_xxx flags.
+ //
+ uint32_t ui32Flags;
+
+ //
+ // The widget (or widget tree) to which the message should be sent.
+ //
+ tWidget *psWidget;
+
+ //
+ // The message to be sent.
+ //
+ uint32_t ui32Message;
+
+ //
+ // The first parameter to the message.
+ //
+ uint32_t ui32Param1;
+
+ //
+ // The second parameter to the message.
+ //
+ uint32_t ui32Param2;
+}
+tWidgetMessageQueue;
+
+//*****************************************************************************
+//
+// The root of the widget tree. This is the widget used when no parent is
+// specified when adding a widget, or when no widget is specified when sending
+// a message. The parent and sibling of this widget are always zero. This
+// should not be directly referenced by applications; WIDGET_ROOT should be
+// used instead.
+//
+//*****************************************************************************
+tWidget g_sRoot =
+{
+ sizeof(tWidget),
+ 0,
+ 0,
+ 0,
+ 0,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ },
+ WidgetDefaultMsgProc
+};
+
+//*****************************************************************************
+//
+// The widget that has captured pointer messages. When a pointer down message
+// is accepted by a widget, that widget is saved in this variable and all
+// subsequent pointer move and pointer up messages are sent directly to this
+// widget.
+//
+//*****************************************************************************
+static tWidget *g_psPointerWidget = 0;
+
+//*****************************************************************************
+//
+// The message queue that holds messages that are waiting to be processed.
+//
+//*****************************************************************************
+static volatile tWidgetMessageQueue g_psMQ[QUEUE_SIZE];
+
+//*****************************************************************************
+//
+// The offset to the next message to be read from the message queue. The
+// message queue is empty when this has the same value as g_ui32MQWrite.
+//
+//*****************************************************************************
+static uint32_t g_ui32MQRead = 0;
+
+//*****************************************************************************
+//
+// The offset to the next message to be written to the message queue. The
+// message queue is full when this value is one less than g_ui32MQRead (modulo
+// the queue size).
+//
+//*****************************************************************************
+static volatile uint32_t g_ui32MQWrite = 0;
+
+//*****************************************************************************
+//
+// The mutex used to protect access to the message queue.
+//
+//*****************************************************************************
+static uint8_t g_ui8MQMutex = 0;
+
+//*****************************************************************************
+//
+//! Initializes a mutex to the unowned state.
+//!
+//! \param pi8Mutex is a pointer to mutex that is to be initialized.
+//!
+//! This function initializes a mutual exclusion semaphore (mutex) to its
+//! unowned state in preparation for use with WidgetMutexGet() and
+//! WidgetMutexPut(). A mutex is a two state object typically used to
+//! serialize access to a shared resource. An application will call
+//! WidgetMutexGet() to request ownership of the mutex. If ownership is
+//! granted, the caller may safely access the resource then release the mutex
+//! using WidgetMutexPut() once it is finished. If ownership is not granted,
+//! the caller knows that some other context is currently modifying the shared
+//! resource and it must not access the resource at that time.
+//!
+//! Note that this function must not be called if the mutex passed in
+//! \e pi8Mutex is already in use since this will have the effect of releasing
+//! the lock even if some caller currently owns it.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+WidgetMutexInit(uint8_t *pi8Mutex)
+{
+ //
+ // Catch NULL pointers in a debug build.
+ //
+ ASSERT(pi8Mutex);
+
+ //
+ // Clear the mutex location to set it to the unowned state.
+ //
+ *pi8Mutex = 0;
+}
+
+//*****************************************************************************
+//
+//! Attempts to acquire a mutex.
+//!
+//! \param pi8Mutex is a pointer to mutex that is to be acquired.
+//!
+//! This function attempts to acquire a mutual exclusion semaphore (mutex) on
+//! behalf of the caller. If the mutex is not already held, 0 is returned to
+//! indicate that the caller may safely access whichever resource the mutex is
+//! protecting. If the mutex is already held, 1 is returned and the caller
+//! must not access the shared resource.
+//!
+//! When access to the shared resource is complete, the mutex owner should call
+//! WidgetMutexPut() to release the mutex and relinquish ownership of the
+//! shared resource.
+//!
+//! \return Returns 0 if the mutex is acquired successfully or 1 if it is
+//! already held by another caller.
+//
+//*****************************************************************************
+#if defined(ewarm) || defined(DOXYGEN)
+uint32_t
+WidgetMutexGet(uint8_t *pi8Mutex)
+{
+ //
+ // Acquire the mutex if possible.
+ //
+ __asm(" mov r1, #1\n"
+ " ldrexb r2, [r0]\n"
+ " cmp r2, #0\n"
+ " it eq\n"
+ " strexb r2, r1, [r0]\n"
+ " mov r0, r2\n");
+
+ //
+ // "Warning[Pe940]: missing return statement at end of non-void function"
+ // is suppressed here to avoid putting a "bx lr" in the inline assembly
+ // above and a superfluous return statement here.
+ //
+#pragma diag_suppress=Pe940
+}
+#pragma diag_default=Pe940
+#endif
+#if defined(codered) || defined(gcc) || defined(sourcerygxx)
+uint32_t __attribute__((naked))
+WidgetMutexGet(uint8_t *pi8Mutex)
+{
+ uint32_t ui32Ret;
+
+ //
+ // Acquire the mutex if possible.
+ //
+ __asm(" mov r1, #1\n"
+ " ldrexb r2, [r0]\n"
+ " cmp r2, #0\n"
+ " it eq\n"
+ " strexbeq r2, r1, [r0]\n"
+ " mov r0, r2\n"
+ " bx lr\n"
+ : "=r" (ui32Ret));
+
+ //
+ // The return is handled in the inline assembly, but the compiler will
+ // still complain if there is not an explicit return here (despite the fact
+ // that this does not result in any code being produced because of the
+ // naked attribute).
+ //
+ return(ui32Ret);
+}
+#endif
+#if defined(rvmdk) || defined(__ARMCC_VERSION)
+__asm uint32_t
+WidgetMutexGet(uint8_t *pi8Mutex)
+{
+ mov r1, #1
+ ldrexb r2, [r0]
+ cmp r2, #0
+ it eq
+ strexbeq r2, r1, [r0]
+ mov r0, r2
+ bx lr
+}
+#endif
+//
+// For CCS implement this function in pure assembly. This prevents the TI
+// compiler from doing funny things with the optimizer.
+//
+#if defined(ccs)
+ __asm(" .sect \".text:WidgetMutexGet\"\n"
+ " .clink\n"
+ " .thumbfunc WidgetMutexGet\n"
+ " .thumb\n"
+ " .global WidgetMutexGet\n"
+ "WidgetMutexGet:\n"
+ " mov r1, #1\n"
+ " ldrexb r2, [r0]\n"
+ " cmp r2, #0\n"
+ " it EQ\n" // TI assembler requires upper case cond
+ " strexbeq r2, r1, [r0]\n"
+ " mov r0, r2\n"
+ " bx lr\n");
+#endif
+
+
+//*****************************************************************************
+//
+//! Release a mutex.
+//!
+//! \param pi8Mutex is a pointer to mutex that is to be released.
+//!
+//! This function releases a mutual exclusion semaphore (mutex), leaving it in
+//! the unowned state.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+WidgetMutexPut(uint8_t *pi8Mutex)
+{
+ //
+ // Release the mutex.
+ //
+ *pi8Mutex = 0;
+}
+
+//*****************************************************************************
+//
+// Determines if a widget exists in the tree below a given point.
+//
+// \param psWidget is a pointer to the widget tree.
+// \param psFind is a pointer to the widget that is being searched for.
+//
+// This function searches the widget tree below psWidget to determine whether
+// or not the widget pointed to by \e psFind exists in the subtree.
+//
+// \return Returns \b true if \e psFind exists in the subtree or \b false if it
+// does not.
+//
+//*****************************************************************************
+static bool
+WidgetIsInTree(tWidget *psWidget, tWidget *psFind)
+{
+ tWidget *psTemp;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+ ASSERT(psFind);
+
+ //
+ // Loop through the tree under the widget until every widget is searched.
+ //
+ for(psTemp = psWidget; psTemp != psWidget->psParent; )
+ {
+ //
+ // See if this widget has a child.
+ //
+ if(psTemp->psChild)
+ {
+ //
+ // Go to this widget's child first.
+ //
+ psTemp = psTemp->psChild;
+ }
+
+ //
+ // This widget does not have a child, so either a sibling or a parent
+ // must be checked. When moving back to the parent, another move must
+ // be performed as well to avoid getting stuck in a loop (since the
+ // parent's children have already been searched.
+ //
+ else
+ {
+ //
+ // Loop until returning to the parent of the starting widget. This
+ // loop will be explicitly broken out of if an intervening widget
+ // is encountered that has not been searched.
+ //
+ while(psTemp != psWidget->psParent)
+ {
+ if(psTemp == psFind)
+ {
+ return(true);
+ }
+
+ //
+ // See if this widget has a sibling.
+ //
+ if(psTemp->psNext)
+ {
+ //
+ // Visit the sibling of this widget.
+ //
+ psTemp = psTemp->psNext;
+
+ //
+ // Since this widget has not been searched yet, break out
+ // of the controlling loop.
+ //
+ break;
+ }
+ else
+ {
+ //
+ // This widget has no siblings, so go to its parent. Since
+ // the parent has already been searched, the same sibling
+ // vs. parent decision must be made on this widget as well.
+ //
+ psTemp = psTemp->psParent;
+ }
+ }
+ }
+ }
+
+ //
+ // The widget could not be found.
+ //
+ return(false);
+}
+
+//*****************************************************************************
+//
+//! Handles widget messages.
+//!
+//! \param psWidget is a pointer to the widget.
+//! \param ui32Message is the message to be processed.
+//! \param ui32Param1 is the first parameter to the message.
+//! \param ui32Param2 is the second parameter to the message.
+//!
+//! This function is a default handler for widget messages; it simply ignores
+//! all messages sent to it. This is used as the message handler for the root
+//! widget, and should be called by the message handler for other widgets when
+//! they do not explicitly handle the provided message (in case new messages
+//! are added that require some default but override-able processing).
+//!
+//! \return Always returns 0.
+//
+//*****************************************************************************
+int32_t
+WidgetDefaultMsgProc(tWidget *psWidget, uint32_t ui32Message,
+ uint32_t ui32Param1, uint32_t ui32Param2)
+{
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Return zero for all messages.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! Adds a widget to the widget tree.
+//!
+//! \param psParent is the parent for the widget. To add to the root of the tree
+//! set this parameter to \b WIDGET_ROOT.
+//! \param psWidget is the widget to add.
+//!
+//! This function adds a widget to the widget tree at the given position within
+//! the tree. The widget will become the last child of its parent, and will
+//! therefore be searched after the existing children.
+//!
+//! The added widget can be a full widget tree, allowing addition of an entire
+//! heirarchy all at once (for example, adding an entire screen to the widget
+//! tree all at once). In this case, it is the responsibility of the caller to
+//! ensure that the psParent field of each widget in the added tree is correctly
+//! set (in other words, only the widget pointed to by \e psWidget is updated to
+//! properly reside in the tree).
+//!
+//! It is the responsibility of the caller to initialize the psNext and psChild
+//! field of the added widget; either of these fields being non-zero results in
+//! a pre-defined tree of widgets being added instead of a single one.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+WidgetAdd(tWidget *psParent, tWidget *psWidget)
+{
+ //
+ // Check the arguments.
+ //
+ ASSERT(psParent);
+ ASSERT(psWidget);
+
+ //
+ // Make this widget be a child of its parent.
+ //
+ psWidget->psParent = psParent;
+
+ //
+ // See if this parent already has children.
+ //
+ if(psParent->psChild)
+ {
+ //
+ // Find the last child of this parent and also check that widget is not
+ // already present at this level of the tree.
+ //
+ for(psParent = psParent->psChild; psParent->psNext;
+ psParent = psParent->psNext)
+ {
+ //
+ // If we find this widget here already, just return. If we don't
+ // do this, we allow errant programs to add the same child twice
+ // resulting in looping on message processing.
+ //
+ if(psParent == psWidget)
+ {
+ return;
+ }
+ }
+
+ //
+ // We perform one final check to see if we are about to add the widget
+ // twice. We need this to catch the case of a single child which
+ // causes the previous loop to exit before performing the widget check.
+ //
+ if(psParent == psWidget)
+ {
+ return;
+ }
+
+ //
+ // Add this widget to the end of the list of children of this parent.
+ //
+ psParent->psNext = psWidget;
+ }
+ else
+ {
+ //
+ // Make this widget be the first (and only) child of this parent.
+ //
+ psParent->psChild = psWidget;
+ }
+}
+
+//*****************************************************************************
+//
+//! Removes a widget from the widget tree.
+//!
+//! \param psWidget is the widget to be removed.
+//!
+//! This function removes a widget from the widget tree. The removed widget
+//! can be a full widget tree, allowing removal of an entire heirarchy all at
+//! once (for example, removing an entire screen from the widget tree).
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+WidgetRemove(tWidget *psWidget)
+{
+ tWidget *psTemp;
+
+ //
+ // Check the argument.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Make sure that the supplied widget is actually in the tree section
+ // owned by its parent and, hence, removeable.
+ //
+ if(!psWidget->psParent || !WidgetIsInTree(psWidget->psParent, psWidget))
+ {
+ return;
+ }
+
+ //
+ // See if this widget is the first child of its parent.
+ //
+ if(psWidget->psParent->psChild == psWidget)
+ {
+ //
+ // Make the first child of this widgets parent be this widget's
+ // sibling.
+ //
+ psWidget->psParent->psChild = psWidget->psNext;
+ }
+ else
+ {
+ //
+ // Find the sibling directly before this widget.
+ //
+ for(psTemp = psWidget->psParent->psChild; psTemp->psNext != psWidget;
+ psTemp = psTemp->psNext)
+ {
+ }
+
+ //
+ // Make the previous sibling point to the next sibling, removing this
+ // widget from the sibling chain.
+ //
+ psTemp->psNext = psWidget->psNext;
+ }
+
+ //
+ // Check to see if the widget which currently owns the pointer has just
+ // been removed and, if so, clear the pointer focus.
+ //
+ if(g_psPointerWidget && !WidgetIsInTree(&g_sRoot, g_psPointerWidget))
+ {
+ g_psPointerWidget = 0;
+ }
+
+ //
+ // Clear the next pointer of the widget.
+ //
+ psWidget->psNext = 0;
+}
+
+//*****************************************************************************
+//
+//! Sends a message to a widget tree via a pre-order, depth-first search.
+//!
+//! \param psWidget is a pointer to the widget tree.
+//! \param ui32Message is the message to send.
+//! \param ui32Param1 is the first parameter to the message.
+//! \param ui32Param2 is the second parameter to the message.
+//! \param bStopOnSuccess is \b true if the search should be stopped when the
+//! first widget is found that returns success in response to the message.
+//!
+//! This function performs a pre-order, depth-first search of the widget tree,
+//! sending a message to each widget encountered. In a depth-first search, the
+//! children of a widget are searched before its siblings (preferring to go
+//! deeper into the tree, hence the name depth-first). A pre-order search
+//! means that the message is sent to a widget before any of its children are
+//! searched.
+//!
+//! An example use of the pre-order search is for paint messages; the larger
+//! enclosing widgets should be drawn on the screen before the smaller widgets
+//! that reside within the parent widget (otherwise, the children would be
+//! overwritten by the parent).
+//!
+//! \return Returns 0 if \e bStopOnSuccess is false or no widget returned
+//! success in response to the message, or the value returned by the first
+//! widget to successfully process the message.
+//
+//*****************************************************************************
+uint32_t
+WidgetMessageSendPreOrder(tWidget *psWidget, uint32_t ui32Message,
+ uint32_t ui32Param1, uint32_t ui32Param2,
+ bool bStopOnSuccess)
+{
+ uint32_t ui32Ret;
+ tWidget *psTemp;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Send the message to the initial widget and return if it succeeded and
+ // the search should stop on success.
+ //
+ ui32Ret = psWidget->pfnMsgProc(psWidget, ui32Message, ui32Param1,
+ ui32Param2);
+ if((ui32Ret != 0) && bStopOnSuccess)
+ {
+ return(ui32Ret);
+ }
+
+ //
+ // Return if the widget does not have any children.
+ //
+ if(!psWidget->psChild)
+ {
+ return(0);
+ }
+
+ //
+ // Loop through the tree under the widget until every widget is searched.
+ //
+ for(psTemp = psWidget->psChild; psTemp != psWidget; )
+ {
+ //
+ // Send the message to this widget and return if it succeeded and the
+ // search should stop on success.
+ //
+ ui32Ret = psTemp->pfnMsgProc(psTemp, ui32Message, ui32Param1,
+ ui32Param2);
+ if((ui32Ret != 0) && bStopOnSuccess)
+ {
+ return(ui32Ret);
+ }
+
+ //
+ // Find the next widget to examine. If this widget has a child, then
+ // that is the next widget to examine.
+ //
+ if(psTemp->psChild)
+ {
+ psTemp = psTemp->psChild;
+ }
+
+ //
+ // This widget does not have a child, so either a sibling or a parent
+ // must be checked. When moving back to the parent, another move must
+ // be performed as well to avoid getting stuck in a loop (since the
+ // parent's children have already been searched).
+ //
+ else
+ {
+ //
+ // Loop until returning to the starting widget. This loop will be
+ // explicitly broken out of if an intervening widget is encountered
+ // that has not be searched.
+ //
+ while(psTemp != psWidget)
+ {
+ //
+ // See if this widget has a sibling.
+ //
+ if(psTemp->psNext)
+ {
+ //
+ // Visit the sibling of this widget.
+ //
+ psTemp = psTemp->psNext;
+
+ //
+ // Since this widget has not been searched yet, break out
+ // of the controlling loop.
+ //
+ break;
+ }
+ else
+ {
+ //
+ // This widget has no siblings, so go to its parent. Since
+ // the parent has already been searched, the same sibling
+ // vs. parent decision must be made on this widget as well.
+ //
+ psTemp = psTemp->psParent;
+ }
+ }
+ }
+ }
+
+ //
+ // No widget returned success for the message, or bStopOnSuccess was zero,
+ // so return zero.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! Sends a message to a widget tree via a post-order, depth-first search.
+//!
+//! \param psWidget is a pointer to the widget tree; if this is zero then the
+//! root of the widget tree willb e used.
+//! \param ui32Message is the message to send.
+//! \param ui32Param1 is the first parameter to the message.
+//! \param ui32Param2 is the second parameter to the message.
+//! \param bStopOnSuccess is \b true if the search should be stopped when the
+//! first widget is found that returns success in response to the message.
+//!
+//! This function performs a post-order, depth-first search of the widget tree,
+//! sending a message to each widget encountered. In a depth-first search, the
+//! children of a widget are searched before its sibling (preferring to go
+//! deeper into the tree, hence the name depth-first). A post-order search
+//! means that the message is sent to a widget after all of its children are
+//! searched.
+//!
+//! An example use of the post-order search is for pointer-related messages;
+//! those messages should be delivered to the lowest widget in the tree before
+//! its parents (in other words, the widget deepest in the tree that has a hit
+//! should get the message, not the higher up widgets that also include the hit
+//! location).
+//!
+//! Special handling is performed for pointer-related messages. The widget
+//! that accepts \b #WIDGET_MSG_PTR_DOWN is remembered and subsequent
+//! \b #WIDGET_MSG_PTR_MOVE and \b #WIDGET_MSG_PTR_UP messages are sent
+//! directly to that widget.
+//!
+//! \return Returns 0 if \e bStopOnSuccess is \b false or no widget returned
+//! success in response to the message, or the value returned by the first
+//! widget to successfully process the message.
+//
+//*****************************************************************************
+uint32_t
+WidgetMessageSendPostOrder(tWidget *psWidget, uint32_t ui32Message,
+ uint32_t ui32Param1, uint32_t ui32Param2,
+ bool bStopOnSuccess)
+{
+ uint32_t ui32Ret;
+ tWidget *psTemp;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // See if this is a pointer move or up message.
+ //
+ if((ui32Message == WIDGET_MSG_PTR_MOVE) ||
+ (ui32Message == WIDGET_MSG_PTR_UP))
+ {
+ //
+ // If there is not a widget that has captured pointer messages, then
+ // simply drop this message.
+ //
+ if(!g_psPointerWidget)
+ {
+ return(0);
+ }
+
+ //
+ // Send the message directly to the widget that has captured pointer
+ // messages.
+ //
+ ui32Ret = g_psPointerWidget->pfnMsgProc(g_psPointerWidget, ui32Message,
+ ui32Param1, ui32Param2);
+
+ //
+ // See if this is a pointer up message.
+ //
+ if(ui32Message == WIDGET_MSG_PTR_UP)
+ {
+ //
+ // Since this was a pointer up, the widget no longer has pointer
+ // messages captured.
+ //
+ g_psPointerWidget = 0;
+ }
+
+ //
+ // Return the value returned by the pointer capture widget.
+ //
+ return(ui32Ret);
+ }
+
+ //
+ // Loop through the tree under the widget until every widget is searched.
+ //
+ for(psTemp = psWidget; psTemp != psWidget->psParent; )
+ {
+ //
+ // See if this widget has a child.
+ //
+ if(psTemp->psChild)
+ {
+ //
+ // Go to this widget's child first.
+ //
+ psTemp = psTemp->psChild;
+ }
+
+ //
+ // This widget does not have a child, so either a sibling or a parent
+ // must be checked. When moving back to the parent, another move must
+ // be performed as well to avoid getting stuck in a loop (since the
+ // parent's children have already been searched.
+ //
+ else
+ {
+ //
+ // Loop until returning to the parent of the starting widget. This
+ // loop will be explicitly broken out of if an intervening widget
+ // is encountered that has not been searched.
+ //
+ while(psTemp != psWidget->psParent)
+ {
+ //
+ // Send the message to this widget.
+ //
+ ui32Ret = psTemp->pfnMsgProc(psTemp, ui32Message, ui32Param1,
+ ui32Param2);
+
+ //
+ // If this is a pointer down message, the widget accepted the
+ // message and the handler didn't modify the tree such that
+ // this widget is no longer present, then save a pointer to the
+ // widget for subsequent pointer move or pointer up messages.
+ //
+ if((ui32Message == WIDGET_MSG_PTR_DOWN) && (ui32Ret != 0))
+ {
+ //
+ // Is the current widget still in the tree?
+ //
+ if(WidgetIsInTree(&g_sRoot, psTemp))
+ {
+ //
+ // The widget is still in the tree so save it for later
+ // use.
+ //
+ g_psPointerWidget = psTemp;
+ }
+ else
+ {
+ //
+ // Although this widget handled the PTR_DOWN message,
+ // it's message handler rearranged the widget tree and
+ // removed itself so we don't want to send any more
+ // messages directly to it after all.
+ //
+ g_psPointerWidget = 0;
+ }
+ }
+
+ //
+ // If the widget returned success and the search should stop on
+ // success then return immediately.
+ //
+ if((ui32Ret != 0) && bStopOnSuccess)
+ {
+ return(ui32Ret);
+ }
+
+ //
+ // See if this widget has a sibling.
+ //
+ if(psTemp->psNext)
+ {
+ //
+ // Visit the sibling of this widget.
+ //
+ psTemp = psTemp->psNext;
+
+ //
+ // Since this widget has not been searched yet, break out
+ // of the controlling loop.
+ //
+ break;
+ }
+ else
+ {
+ //
+ // This widget has no siblings, so go to its parent. Since
+ // the parent has already been searched, the same sibling
+ // vs. parent decision must be made on this widget as well.
+ //
+ psTemp = psTemp->psParent;
+ }
+ }
+ }
+ }
+
+ //
+ // No widget returned success for the message, or bStopOnSuccess was zero,
+ // so return zero.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! Adds a message to the widget message queue.
+//!
+//! \param psWidget is the widget to which the message should be sent.
+//! \param ui32Message is the message to be sent.
+//! \param ui32Param1 is the first parameter to the message.
+//! \param ui32Param2 is the second parameter to the message.
+//! \param bPostOrder is \b true if the message should be sent via a post-order
+//! search, and \b false if it should be sent via a pre-order search.
+//! \param bStopOnSuccess is \b true if the message should be sent to widgets
+//! until one returns success, and \b false if it should be sent to all
+//! widgets.
+//!
+//! This function places a widget message into the message queue for later
+//! processing. The messages are removed from the queue by
+//! WidgetMessageQueueProcess() and sent to the appropriate place.
+//!
+//! It is safe for code which interrupts WidgetMessageQueueProcess() (or called
+//! by it) to call this function to send a message. It is not safe for code
+//! which interrupts this function to call this function as well; it is up to
+//! the caller to guarantee that the later sequence never occurs.
+//!
+//! \return Returns 1 if the message was added to the queue, and 0 if it could
+//! not be added since either the queue is full or another context is currently
+//! adding a message to the queue.
+//
+//*****************************************************************************
+int32_t
+WidgetMessageQueueAdd(tWidget *psWidget, uint32_t ui32Message,
+ uint32_t ui32Param1, uint32_t ui32Param2,
+ bool bPostOrder, bool bStopOnSuccess)
+{
+ uint32_t ui32Next;
+ uint32_t ui32Owned;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Get the mutex we use to protect access to the message queue.
+ //
+ ui32Owned = WidgetMutexGet(&g_ui8MQMutex);
+ if(ui32Owned)
+ {
+ //
+ // The mutex is already being held by some other caller so return a
+ // failure.
+ //
+#ifdef DEBUG_MSGQ
+ g_ui32MQMutexClash++;
+#endif
+ return(0);
+ }
+
+ //
+ // Compute the next value for the write pointer.
+ //
+ ui32Next = (g_ui32MQWrite + 1) % QUEUE_SIZE;
+
+ //
+ // If the queue is not empty, and this is a pointer move message, see if
+ // the previous message was also a move and, if so, replace the
+ // coordinates. Without this, the message queue can very quickly overflow
+ // if the application is busy doing something while the user keeps pressing
+ // the display.
+ //
+ if(ui32Message == WIDGET_MSG_PTR_MOVE)
+ {
+ //
+ // Is the message queue empty?
+ //
+ if(g_ui32MQRead != g_ui32MQWrite)
+ {
+ //
+ // No - what is the index of the previous message?
+ //
+ ui32Owned = (g_ui32MQWrite == 0) ? (QUEUE_SIZE - 1) :
+ (g_ui32MQWrite - 1);
+
+ //
+ // Was this a pointer move message?
+ //
+ if(g_psMQ[g_ui32MQWrite].ui32Message == WIDGET_MSG_PTR_MOVE)
+ {
+ //
+ // Yes - overwrite this message with the new
+ // coordinate information.
+ //
+ g_psMQ[ui32Owned].ui32Param1 = ui32Param1;
+ g_psMQ[ui32Owned].ui32Param2 = ui32Param2;
+#ifdef DEBUG_MSGQ
+ g_ui32MQMoveOverwrite++;
+#endif
+
+ //
+ // Release the message queue mutex.
+ //
+ WidgetMutexPut(&g_ui8MQMutex);
+
+ //
+ // Success.
+ //
+ return(1);
+ }
+ }
+ }
+
+ //
+ // Return a failure if the message queue is full.
+ //
+ if(ui32Next == g_ui32MQRead)
+ {
+#ifdef DEBUG_MSGQ
+ g_ui32MQOverflow++;
+ if(ui32Message != WIDGET_MSG_PTR_MOVE)
+ {
+ g_ui32MQNonMouseOverflow++;
+ g_ui32MQLastLostMsg = ui32Message;
+ }
+#endif
+ //
+ // Release the message queue mutex.
+ //
+ WidgetMutexPut(&g_ui8MQMutex);
+
+ return(0);
+ }
+
+ //
+ // Write this message into the next location in the message queue.
+ //
+ g_psMQ[g_ui32MQWrite].ui32Flags = ((bPostOrder ? MQ_FLAG_POST_ORDER : 0) |
+ (bStopOnSuccess ? MQ_FLAG_STOP_ON_SUCCESS :
+ 0));
+ g_psMQ[g_ui32MQWrite].psWidget = psWidget;
+ g_psMQ[g_ui32MQWrite].ui32Message = ui32Message;
+ g_psMQ[g_ui32MQWrite].ui32Param1 = ui32Param1;
+ g_psMQ[g_ui32MQWrite].ui32Param2 = ui32Param2;
+
+ //
+ // Update the message queue write pointer.
+ //
+ g_ui32MQWrite = ui32Next;
+
+ //
+ // Release the message queue mutex.
+ //
+ WidgetMutexPut(&g_ui8MQMutex);
+
+ //
+ // Success.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! Processes the messages in the widget message queue.
+//!
+//! This function extracts messages from the widget message queue one at a time
+//! and processes them. If the processing of a widget message requires that a
+//! new message be sent, it is acceptable to call WidgetMessageQueueAdd(). It
+//! is also acceptable for code which interrupts this function to call
+//! WidgetMessageQueueAdd() to send more messages. In both cases, the newly
+//! added message will also be processed before this function returns.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+WidgetMessageQueueProcess(void)
+{
+ tWidget *psWidget;
+ uint32_t ui32Flags, ui32Message, ui32Param1, ui32Param2;
+
+ //
+ // Loop while there are more messages in the message queue.
+ //
+ while(g_ui32MQRead != g_ui32MQWrite)
+ {
+ //
+ // Copy the contents of this message into local variables.
+ //
+ psWidget = g_psMQ[g_ui32MQRead].psWidget;
+ ui32Flags = g_psMQ[g_ui32MQRead].ui32Flags;
+ ui32Message = g_psMQ[g_ui32MQRead].ui32Message;
+ ui32Param1 = g_psMQ[g_ui32MQRead].ui32Param1;
+ ui32Param2 = g_psMQ[g_ui32MQRead].ui32Param2;
+
+ //
+ // Remove this message from the queue.
+ //
+ g_ui32MQRead = (g_ui32MQRead + 1) % QUEUE_SIZE;
+
+ //
+ // See if this message should be sent via a post-order or pre-order
+ // search.
+ //
+ if(ui32Flags & MQ_FLAG_POST_ORDER)
+ {
+ //
+ // Send this message with a post-order search of the widget tree.
+ //
+ WidgetMessageSendPostOrder(psWidget, ui32Message, ui32Param1,
+ ui32Param2,
+ ((ui32Flags & MQ_FLAG_STOP_ON_SUCCESS) ?
+ true : false));
+ }
+ else
+ {
+ //
+ // Send this message with a pre-order search of the widget tree.
+ //
+ WidgetMessageSendPreOrder(psWidget, ui32Message, ui32Param1,
+ ui32Param2,
+ ((ui32Flags & MQ_FLAG_STOP_ON_SUCCESS) ?
+ true : false));
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! Sends a pointer message.
+//!
+//! \param ui32Message is the pointer message to be sent.
+//! \param i32X is the X coordinate associated with the message.
+//! \param i32Y is the Y coordinate associated with the message.
+//!
+//! This function sends a pointer message to the root widget. A pointer driver
+//! (such as a touch screen driver) can use this function to deliver pointer
+//! activity to the widget tree without having to have direct knowledge of the
+//! structure of the widget framework.
+//!
+//! \return Returns 1 if the message was added to the queue, and 0 if it could
+//! not be added since the queue is full.
+//
+//*****************************************************************************
+int32_t
+WidgetPointerMessage(uint32_t ui32Message, int32_t i32X, int32_t i32Y)
+{
+ //
+ // Add the message to the widget message queue.
+ //
+ return(WidgetMessageQueueAdd(WIDGET_ROOT, ui32Message, i32X, i32Y, true,
+ true));
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************