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