nuttx-apps/graphics/nxwidgets/src/cwidgetcontrol.cxx
Xiang Xiao 857158451b Unify the void cast usage
1.Remove void cast for function because many place ignore the returned value witout cast
2.Replace void cast for variable with UNUSED macro

Change-Id: Ie644129a563244a6397036789c4c3ea83c4e9b09
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2020-01-02 23:21:01 +08:00

1006 lines
25 KiB
C++

/****************************************************************************
* apps/graphics/nxwidgets/src/cwidgetcontrol.cxx
*
* Copyright (C) 2012-2013, 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
* me be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <cstdint>
#include <cstdbool>
#include <cstring>
#include <csched>
#include <cerrno>
#include "graphics/nxwidgets/nxconfig.hxx"
#include "graphics/nxwidgets/cnxserver.hxx"
#include "graphics/nxwidgets/cnxwidget.hxx"
#include "graphics/nxwidgets/cnxfont.hxx"
#include "graphics/nxwidgets/cwidgetstyle.hxx"
#include "graphics/nxwidgets/cnxtimer.hxx"
#include "graphics/nxwidgets/cgraphicsport.hxx"
#include "graphics/nxwidgets/cwidgetcontrol.hxx"
#include "graphics/nxwidgets/singletons.hxx"
/****************************************************************************
* Pre-Processor Definitions
****************************************************************************/
/****************************************************************************
* Global Static Data
****************************************************************************/
using namespace NXWidgets;
/****************************************************************************
* Method Implementations
****************************************************************************/
/**
* CWidgetControl constructor (for the case where the display size is known)
*
* @param style The style that the widget should use. If this is not
* specified, the widget will use the values stored in the global
* defaultCWidgetStyle object. The widget will copy the properties of
* the style into its own internal style object.
*/
CWidgetControl::CWidgetControl(FAR const CWidgetStyle *style)
{
// Initialize state
m_port = (CGraphicsPort *)NULL;
m_haveGeometry = false;
m_clickedWidget = (CNxWidget *)NULL;
m_focusedWidget = (CNxWidget *)NULL;
// Initialize data that we will get from the position callback
m_hWindow = NULL;
m_size.h = 0;
m_size.w = 0;
m_pos.x = 0;
m_pos.y = 0;
m_bounds.pt1.x = 0;
m_bounds.pt1.y = 0;
m_bounds.pt2.y = 0;
m_bounds.pt2.y = 0;
// Initialize the mouse/touchscreen event and keyboard data structures
#ifdef CONFIG_NX_XYINPUT
memset(&m_xyinput, 0, sizeof(struct SXYInput));
#endif
m_nCh = 0;
m_nCc = 0;
// Intialize semaphores:
//
// m_waitSem. The semaphore that will wake up the external logic on mouse events,
// keypress events, or widget deletion events.
// m_geoSem. The semaphore that will synchronize window size and position
// information.
#ifdef CONFIG_NXWIDGET_EVENTWAIT
m_waiting = false;
sem_init(&m_waitSem, 0, 0);
#endif
sem_init(&m_boundsSem, 0, 0);
sem_init(&m_geoSem, 0, 0);
// Do we need to fetch the default style?
if (style == (CWidgetStyle *)NULL)
{
// Get the style from the controlling widget
copyWidgetStyle(&m_style, g_defaultWidgetStyle);
}
else
{
// Use specified style
copyWidgetStyle(&m_style, style);
}
}
/**
* Destructor.
*/
CWidgetControl::~CWidgetControl(void)
{
// Notify any external waiters... this should not happen because it
// it is probably already too late
#ifdef CONFIG_NXWIDGET_EVENTWAIT
postWindowEvent();
#endif
// Delete any contained instances
if (m_port)
{
delete m_port;
}
// Flush the delete queue
processDeleteQueue();
// Delete controlled widget list
while (m_widgets.size() > 0)
{
m_widgets[0]->destroy();
}
}
/**
* Wait for an interesting window event to occur (like a mouse or keyboard event).
* Caller's should exercise care to assure that the test for waiting and this
* call are "atomic" .. perhaps by locking the scheduler like:
*
* sched_lock();
* ...
* if (no interesting events)
* {
* window->waitForWindowEvent();
* }
* sched_unlock();
*/
#ifdef CONFIG_NXWIDGET_EVENTWAIT
void CWidgetControl::waitForWindowEvent(void)
{
m_waiting = true;
sem_wait(&m_waitSem);
m_waiting = false;
}
#endif
/**
* Wake up external logic waiting for a window event
*/
#ifdef CONFIG_NXWIDGET_EVENTWAIT
void CWidgetControl::postWindowEvent(void)
{
if (m_waiting)
{
sem_post(&m_waitSem);
}
}
#endif
/**
* Run all code that needs to take place on a periodic basis.
* This method normally called externally... either periodically
* or when a window event is detected. If CONFIG_NXWIDGET_EVENTWAIT
* is defined, then external logic want call waitWindow event and
* when awakened, they chould call this function. As an example:
*
* for (;;)
* {
* sched_lock(); // Make the sequence atomic
* if (!window->pollEvents(0))
* {
* window->waitWindowEvent();
* }
*
* sched_unlock();
* }
*
* This method is just a wrapper simply calls the following methods.
* It can easily be replace with custom, external logic.
*
* processDeleteQueue()
* pollMouseEvents(widget)
* pollKeyboardEvents()
* pollCursorControlEvents()
*
* @param widget. Specific widget to poll. Use NULL to run the
* all widgets in the window.
* @return True means some interesting event occurred
*/
bool CWidgetControl::pollEvents(CNxWidget *widget)
{
// Delete any queued widgets
processDeleteQueue();
// Handle mouse input
bool mouseEvent = pollMouseEvents(widget);
// Handle keyboard input
bool keyboardEvent = pollKeyboardEvents();
// Handle cursor control input
bool cursorControlEvent = pollCursorControlEvents();
return mouseEvent || keyboardEvent || cursorControlEvent;
}
/**
* Get the index of the specified controlled widget.
*
* @param widget The widget to get the index of.
* @return The index of the widget. -1 if the widget is not found.
*/
const int CWidgetControl::getWidgetIndex(const CNxWidget *widget) const
{
for (int i = 0; i < m_widgets.size(); i++)
{
if (m_widgets[i] == widget)
{
return i;
}
}
return -1;
}
/**
* Remove a controlled widget
*
* @param widget The widget to be removed
*/
void CWidgetControl::removeControlledWidget(CNxWidget *widget)
{
// Locate the widget
int index = getWidgetIndex(widget);
if (index >= 0)
{
m_widgets.erase(index);
}
}
/**
* Add a widget to the list of widgets to be deleted.
* Must never be called by anything other than the framework itself.
*
* @param widget The widget to add to the delete queue.
*/
void CWidgetControl::addToDeleteQueue(CNxWidget *widget)
{
// Add the widget to the delete queue
m_deleteQueue.push_back(widget);
// Then wake up logic that may be waiting for a window event
#ifdef CONFIG_NXWIDGET_EVENTWAIT
postWindowEvent();
#endif
}
/**
* Set the clicked widget pointer. Note that this should not be
* called by code other than within the CWidgetControl library itself.
*
* @param widget The new clicked widget.
*/
void CWidgetControl::setClickedWidget(CNxWidget *widget)
{
// Do we have a clicked widget already?
if (m_clickedWidget != (CNxWidget *)NULL)
{
// Ensure that the existing clicked widget is released *outside* its bounds
m_clickedWidget->release(m_clickedWidget->getX() - 10, 0);
}
// Update the pointer
m_clickedWidget = widget;
}
/**
* Set the focused widget that will receive keyboard input.
*
* @param widget The new focused widget.
*/
void CWidgetControl::setFocusedWidget(CNxWidget *widget)
{
if (m_focusedWidget != widget)
{
// This widget will now receive focus
m_focusedWidget = widget;
// Make sure that the widget knows that is has focus
widget->focus();
}
}
/**
* This event is called from CCallback instance to provide
* notifications of certain NX-server related events. This event,
* in particular, will occur when the position or size of the underlying
* window occurs.
*
* @param hWindow The window handle that should be used to communicate
* with the window
* @param pos The position of the window in the physical device space.
* @param size The size of the window.
* @param bounds The size of the underlying display (pixels x rows)
*/
void CWidgetControl::geometryEvent(NXHANDLE hWindow,
FAR const struct nxgl_size_s *size,
FAR const struct nxgl_point_s *pos,
FAR const struct nxgl_rect_s *bounds)
{
// Disable pre-emption so that we can be assured that the following
// operations are atomic
sched_lock();
// Save positional data that may change dynamically
m_pos.x = pos->x;
m_pos.y = pos->y;
m_size.h = size->h;
m_size.w = size->w;
// The first callback is important. This is the handshake that proves
// that we are truly communicating with the servier. This is also
// a critical point because this is when we know the physical
// dimensions of the underlying window.
if (!m_hWindow)
{
// Save one-time server specific information
m_hWindow = hWindow;
nxgl_rectcopy(&m_bounds, bounds);
giveBoundsSem();
}
// In the normal start up sequence, the window is created with zero size
// at position 0,0. The safe thing to do is to set the position (still
// with size 0, then then set the size. Assuming that is what is being
// done, we will not report that we have valid geometry until the size
// becomes nonzero (or actually over 1).
if (!m_haveGeometry && size->h > 1 && size->w > 1)
{
// Wake up any threads waiting for initial position information.
// REVISIT: If the window is moved or repositioned, then the
// position and size data will be incorrect for a period of time.
// That case should be handled here as well.
m_haveGeometry = true;
giveGeoSem();
}
m_eventHandlers.raiseGeometryEvent();
sched_unlock();
}
/**
* This event is called from CCallback instance to provide
* notifications of certain NX-server related events. This event,
* in particular, will occur when the a portion of the window that was
* previously obscured is now exposed.
*
* @param nxRect The region in the window that must be redrawn.
* @param more True means that more re-draw requests will follow
*/
void CWidgetControl::redrawEvent(FAR const struct nxgl_rect_s *nxRect, bool more)
{
m_eventHandlers.raiseRedrawEvent(nxRect, more);
}
/**
* This event is called from CCallback instance to provide notifications of
* certain NX-server related events. This event, in particular, means that
* new mouse data is available for the window.
*
* @param pos The (x,y) position of the mouse.
* @param buttons See NX_MOUSE_* definitions.
*/
#ifdef CONFIG_NX_XYINPUT
void CWidgetControl::newMouseEvent(FAR const struct nxgl_point_s *pos, uint8_t buttons)
{
// Save the mouse X/Y position
m_xyinput.x = pos->x;
m_xyinput.y = pos->y;
// Update button press states
clearMouseEvents();
if ((buttons & NX_MOUSE_LEFTBUTTON) != 0)
{
// Handle left button press events. leftHeld means that the left mouse
// button was pressed on the previous sample as well.
if (m_xyinput.leftHeld)
{
m_xyinput.leftDrag = 1;
}
else
{
// New left button press
m_xyinput.leftPressed = 1;
clock_gettime(CLOCK_REALTIME, &m_xyinput.leftPressTime);
// Check for double click event
if (elapsedTime(&m_xyinput.leftReleaseTime) <= CONFIG_NXWIDGETS_DOUBLECLICK_TIME)
{
m_xyinput.doubleClick = 1;
}
}
m_xyinput.leftHeld = 1;
}
else
{
// Handle left button release events
if (m_xyinput.leftHeld)
{
// New left button release
m_xyinput.leftReleased = 1;
clock_gettime(CLOCK_REALTIME, &m_xyinput.leftReleaseTime);
}
m_xyinput.leftHeld = 0;
}
#if 0 // Center and right buttons not used
if ((buttons & NX_MOUSE_CENTERBUTTON) != 0)
{
// Handle center button press events. centerHeld means that the center mouse
// button was pressed on the previous sample as well.
if (m_xyinput.centerHeld)
{
m_xyinput.centerDrag = 1;
}
else
{
// New center button press
m_xyinput.centerPressed = 1;
}
m_xyinput.centerHeld = 1;
}
else
{
// Handle center button release events
if (m_xyinput.centerHeld)
{
// New center button release
m_xyinput.centerReleased = 1;
}
m_xyinput.centerHeld = 0;
}
if ((buttons & NX_MOUSE_RIGHTBUTTON) != 0)
{
// Handle right button press events. rightHeld means that the right mouse
// button was pressed on the previous sample as well.
if (m_xyinput.rightHeld)
{
m_xyinput.rightDrag = 1;
}
else
{
// New right button press
m_xyinput.rightPressed = 1;
}
m_xyinput.rightHeld = 1;
}
else
{
// Handle right button release events
if (m_xyinput.rightHeld)
{
// New right button release
m_xyinput.rightReleased = 1;
}
m_xyinput.rightHeld = 0;
}
#endif
// Notify any external logic that a mouse event has occurred
m_eventHandlers.raiseMouseEvent(pos, buttons);
#ifdef CONFIG_NXWIDGET_EVENTWAIT
// Then wake up logic that may be waiting for a window event
postWindowEvent();
#endif
}
#endif
/**
* This event is called from CCallback instance to provide notifications of
* certain NX-server related events. This event, in particular, means that
* keyboard/keypad data is available for the window.
*
* @param nCh The number of characters that are available in pStr[].
* @param pStr The array of characters.
*/
#ifdef CONFIG_NX_KBD
void CWidgetControl::newKeyboardEvent(uint8_t nCh, FAR const uint8_t *pStr)
{
FAR uint8_t *pBuffer = &m_kbdbuf[m_nCh];
// Append each new character to keyboard character buffer
for (uint8_t i = 0;
i < nCh && m_nCh < CONFIG_NXWIDGETS_KBDBUFFER_SIZE;
i++, m_nCh++)
{
*pBuffer++ = *pStr++;
}
// Notify any external logic that a keyboard event has occurred
m_eventHandlers.raiseKeyboardEvent();
#ifdef CONFIG_NXWIDGET_EVENTWAIT
// Then wake up logic that may be waiting for a window event
postWindowEvent();
#endif
}
#endif
/**
* This event means that cursor control data is available for the window.
*
* @param cursorControl The cursor control code received.
*/
void CWidgetControl::newCursorControlEvent(ECursorControl cursorControl)
{
// Append the new cursor control
if (m_nCc < CONFIG_NXWIDGETS_CURSORCONTROL_SIZE)
{
m_controls[m_nCc] = (uint8_t)cursorControl;
m_nCc++;
}
// Then wake up logic that may be waiting for a window event
#ifdef CONFIG_NXWIDGET_EVENTWAIT
postWindowEvent();
#endif
}
/**
* The creation sequence is:
*
* 1) Create a dumb CWigetControl instance
* 2) Pass the dumb CWidgetControl instance to the window constructor
* that inherits from INxWindow.
* 3) The call this method with the static_cast to INxWindow to,
* finally, create the CGraphicsPort for this window.
* 4) After that, the fully smartened CWidgetControl instance can
* be used to generate additional widgets.
*
* @param window The instance of INxWindow needed to construct the
* CGraphicsPort instance
*/
bool CWidgetControl::createGraphicsPort(INxWindow *window)
{
#ifdef CONFIG_NX_WRITEONLY
m_port = new CGraphicsPort(window, m_style.colors.background);
#else
m_port = new CGraphicsPort(window);
#endif
return m_port != (CGraphicsPort *)NULL;
}
/**
* Copy a widget style
*
* @param dest The destination style
* @param src The source to use
*/
void CWidgetControl::copyWidgetStyle(CWidgetStyle *dest, const CWidgetStyle *src)
{
dest->colors.background = src->colors.background;
dest->colors.selectedBackground = src->colors.selectedBackground;
dest->colors.shineEdge = src->colors.shineEdge;
dest->colors.shadowEdge = src->colors.shadowEdge;
dest->colors.highlight = src->colors.highlight;
dest->colors.disabledText = src->colors.disabledText;
dest->colors.enabledText = src->colors.enabledText;
dest->colors.selectedText = src->colors.selectedText;
dest->font = src->font;
}
/**
* Return the elapsed time in milliseconds
*
* @param tp A time in the past from which to compute the elapsed time.
* @return The elapsed time since tp
*/
uint32_t CWidgetControl::elapsedTime(FAR const struct timespec *startTime)
{
struct timespec endTime;
clock_gettime(CLOCK_REALTIME, &endTime);
if (startTime->tv_sec <= endTime.tv_sec)
{
// Get the elapsed seconds
uint32_t seconds = endTime.tv_sec - startTime->tv_sec;
// Get the elapsed nanoseconds, borrowing from the seconds if necessary
int32_t endNanoSeconds = endTime.tv_nsec;
if (startTime->tv_nsec > endNanoSeconds)
{
// Are there any seconds to borrow from?
if (seconds < 1)
{
// startTime is in the future???
return 0;
}
// Borrow from the seconds
seconds--;
endNanoSeconds += 1000000000;
}
uint32_t nanoseconds = endNanoSeconds - startTime->tv_nsec;
// Then return the elapsed time in milliseconds
return seconds * 1000000 + nanoseconds / 1000;
}
// startTime is in the future???
return 0;
}
/**
* Pass clicks to the widget hierarchy. If a single widget
* is supplied, only that widget is sent the click.
*
* @param x Click xcoordinate.
* @param y Click ycoordinate.
* @param widget Pointer to a specific widget or NULL.
* @return True if is a widget responds to the left click
*/
bool CWidgetControl::handleLeftClick(nxgl_coord_t x, nxgl_coord_t y,
CNxWidget *widget)
{
// Working with a specific widget or the whole structure?
if (widget == (CNxWidget *)NULL)
{
// All widgets
for (int i = m_widgets.size() - 1; i > -1; i--)
{
if (m_widgets[i]->click(x, y))
{
return true;
}
}
}
else
{
// One widget
return widget->click(x, y);
}
return false;
}
/**
* Delete any widgets in the deletion queue.
*/
void CWidgetControl::processDeleteQueue(void)
{
int i = 0;
while (i < m_deleteQueue.size())
{
m_deleteQueue[i]->destroy();
i++;
}
m_deleteQueue.clear();
}
/**
* Process mouse/touchscreen events and send throughout the hierarchy.
*
* @param widget. Specific widget to poll. Use NULL to run the
* all widgets in the window.
* @return True means an interesting mouse event occurred
*/
bool CWidgetControl::pollMouseEvents(CNxWidget *widget)
{
#ifdef CONFIG_NX_XYINPUT
bool mouseEvent = false; // Assume that no interesting mouse event occurred
// Left click event for all widgets
if (m_xyinput.leftPressed)
{
// Handle a new left button press event
if (handleLeftClick(m_xyinput.x, m_xyinput.y, widget))
{
mouseEvent = true;
}
}
// Drag event for the clicked widget
if (!mouseEvent && m_xyinput.leftDrag)
{
// The left button is still being held down
if (m_clickedWidget != (CNxWidget *)NULL)
{
// Handle a mouse drag event
m_clickedWidget->drag(m_xyinput.x, m_xyinput.y,
m_xyinput.x - m_xyinput.lastX,
m_xyinput.y - m_xyinput.lastY);
mouseEvent = true;
}
}
// Check for release event on the clicked widget
if (!mouseEvent && m_clickedWidget != (CNxWidget *)NULL)
{
// Mouse left button release event
m_clickedWidget->release(m_xyinput.x, m_xyinput.y);
mouseEvent = true;
}
// Clear all press and release events once they have been processed
clearMouseEvents();
// Save the mouse position for the next poll
m_xyinput.lastX = m_xyinput.x;
m_xyinput.lastY = m_xyinput.y;
return mouseEvent;
#else
return false;
#endif
}
/**
* Process keypad events and send throughout the hierarchy.
*
* @return True means a keyboard event occurred
*/
bool CWidgetControl::pollKeyboardEvents(void)
{
bool keyboardEvent = false; // Assume no interesting keyboard events
// Keyboard presses with no focused widget is not an interesting
// event
if (m_focusedWidget != (CNxWidget *)NULL)
{
// Forward each character to the widget with the focus
for (uint8_t i = 0; i < m_nCh; i++)
{
m_focusedWidget->keyPress((nxwidget_char_t)m_kbdbuf[i]);
keyboardEvent = true;
}
}
// All of the buffered characters have been consumed
m_nCh = 0;
return keyboardEvent;
}
/**
* Process cursor control events and send throughout the hierarchy.
*
* @return True means a cursor control event was processes
*/
bool CWidgetControl::pollCursorControlEvents(void)
{
bool cursorControlEvent = false; // Assume no interesting cursor events
// Cursor controls with no focused widget is not an interesting
// event
if (m_focusedWidget != (CNxWidget *)NULL)
{
// Forward each cursor control to the widget with the focus
for (uint8_t i = 0; i < m_nCc; i++)
{
m_focusedWidget->cursorControl((ECursorControl)m_controls[i]);
cursorControlEvent = true;
}
}
// All of the buffered events have been consumed
m_nCc = 0;
return cursorControlEvent;
}
/**
* Take the geometry semaphore (handling signal interruptions)
*/
void CWidgetControl::takeGeoSem(void)
{
// Take the geometry semaphore. Retry if an error occurs (only if
// the error is due to a signal interruption).
int ret;
do
{
ret = sem_wait(&m_geoSem);
}
while (ret < 0 && errno == EINTR);
}
/**
* Check if geomtry data is available. If not, [re-]request the
* geomtry data and wait for it to become valid.
*
* CAREFUL: This assumes that if we already have geometry data, then
* it is valid. This might not be true if the size position was
* recently changed.
*
* REVISIT: Ideally m_haveGeometry would be set false an any calls to
* reposition or resize the window.
*/
void CWidgetControl::waitGeoData(void)
{
// Check if we already have geometry data available.
while (!m_haveGeometry)
{
// No.. it is probably on its way, but to avoid any possibility
// of a deadlock wait, request the position data again.
int ret = nx_getposition(m_hWindow);
if (ret < 0)
{
gerr("ERROR: nx_getposition failed: %d\n", errno);
}
takeGeoSem();
}
// Put the semaphore count back to 1 for the next guy
giveGeoSem();
}
/**
* Take the bounds semaphore (handling signal interruptions)
*/
void CWidgetControl::takeBoundsSem(void)
{
// Take the bounds semaphore. Retry is an error occurs (only if
// the error is due to a signal interruption).
int ret;
do
{
ret = sem_wait(&m_boundsSem);
}
while (ret < 0 && errno == EINTR);
}
/**
* Clear all mouse events
*/
#ifdef CONFIG_NX_XYINPUT
void CWidgetControl::clearMouseEvents(void)
{
// Clear all press and release events once they have been processed
m_xyinput.leftPressed = 0;
m_xyinput.leftReleased = 0;
m_xyinput.leftDrag = 0;
#if 0 // Center and right buttons are not used
m_xyinput.centerPressed = 0;
m_xyinput.centerReleased = 0;
m_xyinput.centerDrag = 0;
m_xyinput.rightPressed = 0;
m_xyinput.rightReleased = 0;
m_xyinput.rightDrag = 0;
#endif
m_xyinput.doubleClick = 0;
}
#endif