From 788db64b8642bf31de6930d18a62177c64163ee0 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 13 Mar 2015 12:24:02 +0200 Subject: Add grlib, fixes #5 --- grlib/widget.c | 1227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1227 insertions(+) create mode 100644 grlib/widget.c (limited to 'grlib/widget.c') 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 +#include +#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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1