nuttx-apps/graphics/pdcurs34/pdcurses/pdc_mouse.c

576 lines
17 KiB
C

/****************************************************************************
* apps/graphics/pdcurses/pdc_mouse.c
* Public Domain Curses
* RCSID("$Id: mouse.c,v 1.45 2008/07/13 16:08:18 wmcbrine Exp $")
*
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
* Adapted by: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted from the original public domain pdcurses by Gregory Nutt and
* released as part of NuttX under the 3-clause BSD license:
*
* 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 nor the names of its contributors may 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.
*
****************************************************************************/
/* Name: mouse
*
* Synopsis:
* int mouse_set(unsigned long mbe);
* int mouse_on(unsigned long mbe);
* int mouse_off(unsigned long mbe);
* int request_mouse_pos(void);
* int map_button(unsigned long button);
* void wmouse_position(WINDOW *win, int *y, int *x);
* unsigned long getmouse(void);
* unsigned long getbmap(void);
*
* int mouseinterval(int wait);
* bool wenclose(const WINDOW *win, int y, int x);
* bool wmouse_trafo(const WINDOW *win, int *y, int *x, bool to_screen);
* bool mouse_trafo(int *y, int *x, bool to_screen);
* mmask_t mousemask(mmask_t mask, mmask_t *oldmask);
* int nc_getmouse(MEVENT *event);
* int ungetmouse(MEVENT *event);
*
* Description:
* As of PDCurses 3.0, there are two separate mouse interfaces: the
* classic interface, which is based on the undocumented Sys V
* mouse functions; and an ncurses-compatible interface. Both are
* active at all times, and you can mix and match functions from
* each, though it's not recommended. The ncurses interface is
* essentially an emulation layer built on top of the classic
* interface; it's here to allow easier porting of ncurses apps.
*
* The classic interface: mouse_set(), mouse_on(), mouse_off(),
* request_mouse_pos(), map_button(), wmouse_position(),
* getmouse(), and getbmap(). An application using this interface
* would start by calling mouse_set() or mouse_on() with a non-zero
* value, often ALL_MOUSE_EVENTS. Then it would check for a
* KEY_MOUSE return from getch(). If found, it would call
* request_mouse_pos() to get the current mouse status.
*
* mouse_set(), mouse_on() and mouse_off() are analogous to
* attrset(), attron() and attroff(). These functions set the
* mouse button events to trap. The button masks used in these
* functions are defined in curses.h and can be or'ed together.
* They are the group of masks starting with BUTTON1_RELEASED.
*
* request_mouse_pos() requests curses to fill in the Mouse_status
* structure with the current state of the mouse.
*
* map_button() enables the specified mouse action to activate the
* Soft Label Keys if the action occurs over the area of the screen
* where the Soft Label Keys are displayed. The mouse actions are
* defined in curses.h in the group that starts with BUTTON_RELEASED.
*
* wmouse_position() determines if the current mouse position is
* within the window passed as an argument. If the mouse is
* outside the current window, -1 is returned in the y and x
* arguments; otherwise the y and x coordinates of the mouse
* (relative to the top left corner of the window) are returned in
* y and x.
*
* getmouse() returns the current status of the trapped mouse
* buttons as set by mouse_set() or mouse_on().
*
* getbmap() returns the current status of the button action used
* to map a mouse action to the Soft Label Keys as set by the
* map_button() function.
*
* The ncurses interface: mouseinterval(), wenclose(),
* wmouse_trafo(), mouse_trafo(), mousemask(), nc_getmouse(), and
* ungetmouse(). A typical application using this interface would
* start by calling mousemask() with a non-zero value, often
* ALL_MOUSE_EVENTS. Then it would check for a KEY_MOUSE return
* from getch(). If found, it would call nc_getmouse() to get the
* current mouse status.
*
* mouseinterval() sets the timeout for a mouse click. On all
* current platforms, PDCurses receives mouse button press and
* release events, but must synthesize click events. It does this
* by checking whether a release event is queued up after a press
* event. If it gets a press event, and there are no more events
* waiting, it will wait for the timeout interval, then check again
* for a release. A press followed by a release is reported as
* BUTTON_CLICKED; otherwise it's passed through as BUTTON_PRESSED.
* The default timeout is 150ms; valid values are 0 (no clicks
* reported) through 1000ms. In x11, the timeout can also be set
* via the clickPeriod resource. The return value from
* mouseinterval() is the old timeout. To check the old value
* without setting a new one, call it with a parameter of -1. Note
* that although there's no classic equivalent for this function
* (apart from the clickPeriod resource), the value set applies in
* both interfaces.
*
* wenclose() reports whether the given screen-relative y, x
* coordinates fall within the given window.
*
* wmouse_trafo() converts between screen-relative and window-
* relative coordinates. A to_screen parameter of true means to
* convert from window to screen; otherwise the reverse. The
* function returns false if the coordinates aren't within the
* window, or if any of the parameters are NULL. The coordinates
* have been converted when the function returns true.
*
* mouse_trafo() is the stdscr version of wmouse_trafo().
*
* mousemask() is nearly equivalent to mouse_set(), but instead of
* OK/ERR, it returns the value of the mask after setting it. (This
* isn't necessarily the same value passed in, since the mask could
* be altered on some platforms.) And if the second parameter is a
* non-null pointer, mousemask() stores the previous mask value
* there. Also, since the ncurses interface doesn't work with
* PDCurses' BUTTON_MOVED events, mousemask() filters them out.
*
* nc_getmouse() returns the current mouse status in an MEVENT
* struct. This is equivalent to ncurses' getmouse(), renamed to
* avoid conflict with PDCurses' getmouse(). But if you define
* NCURSES_MOUSE_VERSION (preferably as 2) before including
* curses.h, it defines getmouse() to nc_getmouse(), along with a
* few other redefinitions needed for compatibility with ncurses
* code. nc_getmouse() calls request_mouse_pos(), which (not
* getmouse()) is the classic equivalent.
*
* ungetmouse() is the mouse equivalent of ungetch(). However,
* PDCurses doesn't maintain a queue of mouse events; only one can
* be pushed back, and it can overwrite or be overwritten by real
* mouse events.
*
* Portability X/Open BSD SYS V
* mouse_set - - 4.0
* mouse_on - - 4.0
* mouse_off - - 4.0
* request_mouse_pos - - 4.0
* map_button - - 4.0
* wmouse_position - - 4.0
* getmouse - - 4.0
* getbmap - - 4.0
* mouseinterval - - -
* wenclose - - -
* wmouse_trafo - - -
* mouse_trafo - - -
* mousemask - - -
* nc_getmouse - - -
* ungetmouse - - -
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <string.h>
#include "curspriv.h"
/****************************************************************************
* Private Data
****************************************************************************/
#ifndef CONFIG_PDCURSES_MULTITHREAD
static bool ungot = false;
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
int mouse_set(unsigned long mbe)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("mouse_set() - called: event %x\n", mbe));
SP->_trap_mbe = mbe;
return PDC_mouse_set();
}
int mouse_on(unsigned long mbe)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("mouse_on() - called: event %x\n", mbe));
SP->_trap_mbe |= mbe;
return PDC_mouse_set();
}
int mouse_off(unsigned long mbe)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("mouse_off() - called: event %x\n", mbe));
SP->_trap_mbe &= ~mbe;
return PDC_mouse_set();
}
int map_button(unsigned long button)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("map_button() - called: button %x\n", button));
/* This does nothing at the moment */
SP->_map_mbe_to_key = button;
return OK;
}
int request_mouse_pos(void)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("request_mouse_pos() - called\n"));
Mouse_status = pdc_mouse_status;
return OK;
}
void wmouse_position(WINDOW *win, int *y, int *x)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("wmouse_position() - called\n"));
if (win && wenclose(win, MOUSE_Y_POS, MOUSE_X_POS))
{
if (y)
{
*y = MOUSE_Y_POS - win->_begy;
}
if (x)
{
*x = MOUSE_X_POS - win->_begx;
}
}
else
{
if (y)
{
*y = -1;
}
if (x)
{
*x = -1;
}
}
}
unsigned long getmouse(void)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("getmouse() - called\n"));
return SP->_trap_mbe;
}
unsigned long getbmap(void)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("getbmap() - called\n"));
return SP->_map_mbe_to_key;
}
/* ncurses mouse interface */
int mouseinterval(int wait)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
int old_wait;
PDC_LOG(("mouseinterval() - called: %d\n", wait));
old_wait = SP->mouse_wait;
if (wait >= 0 && wait <= 1000)
{
SP->mouse_wait = wait;
}
return old_wait;
}
bool wenclose(const WINDOW *win, int y, int x)
{
PDC_LOG(("wenclose() - called: %p %d %d\n", win, y, x));
return (win && y >= win->_begy && y < win->_begy + win->_maxy &&
x >= win->_begx && x < win->_begx + win->_maxx);
}
bool wmouse_trafo(const WINDOW *win, int *y, int *x, bool to_screen)
{
int newy, newx;
PDC_LOG(("wmouse_trafo() - called\n"));
if (!win || !y || !x)
{
return false;
}
newy = *y;
newx = *x;
if (to_screen)
{
newy += win->_begy;
newx += win->_begx;
if (!wenclose(win, newy, newx))
{
return false;
}
}
else
{
if (wenclose(win, newy, newx))
{
newy -= win->_begy;
newx -= win->_begx;
}
else
{
return false;
}
}
*y = newy;
*x = newx;
return true;
}
bool mouse_trafo(int *y, int *x, bool to_screen)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("mouse_trafo() - called\n"));
return wmouse_trafo(stdscr, y, x, to_screen);
}
mmask_t mousemask(mmask_t mask, mmask_t * oldmask)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("mousemask() - called\n"));
if (oldmask)
{
*oldmask = SP->_trap_mbe;
}
/* The ncurses interface doesn't work with our move events, so filter them
* here */
mask &= ~(BUTTON1_MOVED | BUTTON2_MOVED | BUTTON3_MOVED);
mouse_set(mask);
return SP->_trap_mbe;
}
int nc_getmouse(MEVENT * event)
{
int i;
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
mmask_t bstate = 0;
PDC_LOG(("nc_getmouse() - called\n"));
if (!event)
{
return ERR;
}
ungot = false;
request_mouse_pos();
event->id = 0;
event->x = Mouse_status.x;
event->y = Mouse_status.y;
event->z = 0;
for (i = 0; i < 3; i++)
{
if (Mouse_status.changes & (1 << i))
{
int shf = i * 5;
short button = Mouse_status.button[i] & BUTTON_ACTION_MASK;
if (button == BUTTON_RELEASED)
{
bstate |= (BUTTON1_RELEASED << shf);
}
else if (button == BUTTON_PRESSED)
{
bstate |= (BUTTON1_PRESSED << shf);
}
else if (button == BUTTON_CLICKED)
{
bstate |= (BUTTON1_CLICKED << shf);
}
else if (button == BUTTON_DOUBLE_CLICKED)
{
bstate |= (BUTTON1_DOUBLE_CLICKED << shf);
}
button = Mouse_status.button[i] & BUTTON_MODIFIER_MASK;
if (button & PDC_BUTTON_SHIFT)
{
bstate |= BUTTON_MODIFIER_SHIFT;
}
if (button & PDC_BUTTON_CONTROL)
{
bstate |= BUTTON_MODIFIER_CONTROL;
}
if (button & PDC_BUTTON_ALT)
{
bstate |= BUTTON_MODIFIER_ALT;
}
}
}
if (MOUSE_WHEEL_UP)
{
bstate |= BUTTON4_PRESSED;
}
else if (MOUSE_WHEEL_DOWN)
{
bstate |= BUTTON5_PRESSED;
}
/* extra filter pass -- mainly for button modifiers */
event->bstate = bstate & SP->_trap_mbe;
return OK;
}
int ungetmouse(MEVENT * event)
{
int i;
unsigned long bstate;
#ifdef CONFIG_PDCURSES_MULTITHREAD
FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
PDC_LOG(("ungetmouse() - called\n"));
if (!event || ungot)
{
return ERR;
}
ungot = true;
pdc_mouse_status.x = event->x;
pdc_mouse_status.y = event->y;
pdc_mouse_status.changes = 0;
bstate = event->bstate;
for (i = 0; i < 3; i++)
{
int shf = i * 5;
short button = 0;
if (bstate & ((BUTTON1_RELEASED | BUTTON1_PRESSED |
BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED) << shf))
{
pdc_mouse_status.changes |= 1 << i;
if (bstate & (BUTTON1_PRESSED << shf))
{
button = BUTTON_PRESSED;
}
if (bstate & (BUTTON1_CLICKED << shf))
{
button = BUTTON_CLICKED;
}
if (bstate & (BUTTON1_DOUBLE_CLICKED << shf))
{
button = BUTTON_DOUBLE_CLICKED;
}
if (bstate & BUTTON_MODIFIER_SHIFT)
{
button |= PDC_BUTTON_SHIFT;
}
if (bstate & BUTTON_MODIFIER_CONTROL)
{
button |= PDC_BUTTON_CONTROL;
}
if (bstate & BUTTON_MODIFIER_ALT)
{
button |= PDC_BUTTON_ALT;
}
}
pdc_mouse_status.button[i] = button;
}
if (bstate & BUTTON4_PRESSED)
{
pdc_mouse_status.changes |= PDC_MOUSE_WHEEL_UP;
}
else if (bstate & BUTTON5_PRESSED)
{
pdc_mouse_status.changes |= PDC_MOUSE_WHEEL_DOWN;
}
return ungetch(KEY_MOUSE);
}