| /**************************************************************************** |
| * Copyright 2018-2020,2021 Thomas E. Dickey * |
| * Copyright 1998-2016,2017 Free Software Foundation, Inc. * |
| * * |
| * Permission is hereby granted, free of charge, to any person obtaining a * |
| * copy of this software and associated documentation files (the * |
| * "Software"), to deal in the Software without restriction, including * |
| * without limitation the rights to use, copy, modify, merge, publish, * |
| * distribute, distribute with modifications, sublicense, and/or sell * |
| * copies of the Software, and to permit persons to whom the Software is * |
| * furnished to do so, subject to the following conditions: * |
| * * |
| * The above copyright notice and this permission notice shall be included * |
| * in all copies or substantial portions of the Software. * |
| * * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * |
| * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * |
| * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * |
| * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * |
| * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * |
| * * |
| * Except as contained in this notice, the name(s) of the above copyright * |
| * holders shall not be used in advertising or otherwise to promote the * |
| * sale, use or other dealings in this Software without prior written * |
| * authorization. * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * |
| * and: Eric S. Raymond <esr@snark.thyrsus.com> * |
| * and: Thomas E. Dickey 1996-on * |
| * and: Juergen Pfeifer 2008 * |
| ****************************************************************************/ |
| |
| /* |
| * This module is intended to encapsulate ncurses's interface to pointing |
| * devices. |
| * |
| * The primary method used is xterm's internal mouse-tracking facility. |
| * Additional methods depend on the platform: |
| * Alessandro Rubini's GPM server (Linux) |
| * sysmouse (FreeBSD) |
| * special-purpose mouse interface for OS/2 EMX. |
| * |
| * Notes for implementors of new mouse-interface methods: |
| * |
| * The code is logically split into a lower level that accepts event reports |
| * in a device-dependent format and an upper level that parses mouse gestures |
| * and filters events. The mediating data structure is a circular queue of |
| * MEVENT structures. |
| * |
| * Functionally, the lower level's job is to pick up primitive events and |
| * put them on the circular queue. This can happen in one of two ways: |
| * either (a) _nc_mouse_event() detects a series of incoming mouse reports |
| * and queues them, or (b) code in lib_getch.c detects the kmous prefix in |
| * the keyboard input stream and calls _nc_mouse_inline to queue up a series |
| * of adjacent mouse reports. |
| * |
| * In either case, _nc_mouse_parse() should be called after the series is |
| * accepted to parse the digested mouse reports (low-level MEVENTs) into |
| * a gesture (a high-level or composite MEVENT). |
| * |
| * Don't be too shy about adding new event types or modifiers, if you can find |
| * room for them in the 32-bit mask. The API is written so that users get |
| * feedback on which theoretical event types they won't see when they call |
| * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being |
| * used yet, and a couple of bits open at the high end. |
| */ |
| |
| #ifdef __EMX__ |
| # include <io.h> |
| # define INCL_DOS |
| # define INCL_VIO |
| # define INCL_KBD |
| # define INCL_MOU |
| # define INCL_DOSPROCESS |
| # include <os2.h> /* Need to include before the others */ |
| #endif |
| |
| #include <curses.priv.h> |
| |
| #ifndef CUR |
| #define CUR SP_TERMTYPE |
| #endif |
| |
| MODULE_ID("$Id: lib_mouse.c,v 1.193 2021/03/20 12:56:32 tom Exp $") |
| |
| #include <tic.h> |
| |
| #if USE_GPM_SUPPORT |
| #include <linux/keyboard.h> /* defines KG_* macros */ |
| |
| #ifdef HAVE_LIBDL |
| /* use dynamic loader to avoid linkage dependency */ |
| #include <dlfcn.h> |
| |
| #ifdef RTLD_NOW |
| #define my_RTLD RTLD_NOW |
| #else |
| #ifdef RTLD_LAZY |
| #define my_RTLD RTLD_LAZY |
| #else |
| make an error |
| #endif |
| #endif /* RTLD_NOW */ |
| #endif /* HAVE_LIBDL */ |
| |
| #endif /* USE_GPM_SUPPORT */ |
| |
| #if USE_SYSMOUSE |
| #undef buttons /* symbol conflict in consio.h */ |
| #undef mouse_info /* symbol conflict in consio.h */ |
| #include <osreldate.h> |
| #if defined(__DragonFly_version) || (defined(__FreeBSD__) && (__FreeBSD_version >= 400017)) |
| #include <sys/consio.h> |
| #include <sys/fbio.h> |
| #else |
| #include <machine/console.h> |
| #endif |
| #endif /* use_SYSMOUSE */ |
| |
| #if USE_KLIBC_MOUSE |
| #include <sys/socket.h> |
| #define pipe(handles) socketpair(AF_LOCAL, SOCK_STREAM, 0, handles) |
| #define DosWrite(hfile, pbuffer, cbwrite, pcbactual) \ |
| write(hfile, pbuffer, cbwrite) |
| #define DosExit(action, result ) /* do nothing */ |
| #define DosCreateThread(ptid, pfn, param, flag, cbStack) \ |
| (*(ptid) = _beginthread(pfn, NULL, cbStack, \ |
| (void *)param), (*(ptid) == -1)) |
| #endif |
| |
| #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT |
| |
| #define MASK_RELEASE(x) (mmask_t) NCURSES_MOUSE_MASK(x, 001) |
| #define MASK_PRESS(x) (mmask_t) NCURSES_MOUSE_MASK(x, 002) |
| #define MASK_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 004) |
| #define MASK_DOUBLE_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 010) |
| #define MASK_TRIPLE_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 020) |
| #define MASK_RESERVED_EVENT(x) (mmask_t) NCURSES_MOUSE_MASK(x, 040) |
| |
| #if NCURSES_MOUSE_VERSION == 1 |
| |
| #define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED) |
| #define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED) |
| #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED) |
| #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED) |
| #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED) |
| |
| #define MAX_BUTTONS 4 |
| |
| #else |
| |
| #define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED | BUTTON5_CLICKED) |
| #define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED | BUTTON5_PRESSED) |
| #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED | BUTTON5_RELEASED) |
| #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED) |
| #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED) |
| |
| #if NCURSES_MOUSE_VERSION == 2 |
| #define MAX_BUTTONS 5 |
| #else |
| #define MAX_BUTTONS 11 |
| #endif |
| |
| #endif |
| |
| #define INVALID_EVENT -1 |
| #define NORMAL_EVENT 0 |
| |
| #define ValidEvent(ep) ((ep)->id != INVALID_EVENT) |
| #define Invalidate(ep) (ep)->id = INVALID_EVENT |
| |
| #if USE_GPM_SUPPORT |
| |
| #ifndef LIBGPM_SONAME |
| #define LIBGPM_SONAME "libgpm.so" |
| #endif |
| |
| #define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(sp->_dlopen_gpm, #name)) |
| |
| #endif /* USE_GPM_SUPPORT */ |
| |
| static bool _nc_mouse_parse(SCREEN *, int); |
| static void _nc_mouse_resume(SCREEN *); |
| static void _nc_mouse_wrap(SCREEN *); |
| |
| /* maintain a circular list of mouse events */ |
| |
| #define FirstEV(sp) ((sp)->_mouse_events) |
| #define LastEV(sp) ((sp)->_mouse_events + EV_MAX - 1) |
| |
| #undef NEXT |
| #define NEXT(ep) ((ep >= LastEV(SP_PARM)) \ |
| ? FirstEV(SP_PARM) \ |
| : ep + 1) |
| |
| #undef PREV |
| #define PREV(ep) ((ep <= FirstEV(SP_PARM)) \ |
| ? LastEV(SP_PARM) \ |
| : ep - 1) |
| |
| #define IndexEV(sp, ep) (ep - FirstEV(sp)) |
| |
| #define RunParams(sp, eventp, runp) \ |
| (long) IndexEV(sp, runp), \ |
| (long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX |
| |
| #ifdef TRACE |
| static void |
| _trace_slot(SCREEN *sp, const char *tag) |
| { |
| MEVENT *ep; |
| |
| _tracef("%s", tag); |
| |
| for (ep = FirstEV(sp); ep <= LastEV(sp); ep++) |
| _tracef("mouse event queue slot %ld = %s", |
| (long) IndexEV(sp, ep), |
| _nc_tracemouse(sp, ep)); |
| } |
| #endif |
| |
| #if USE_EMX_MOUSE |
| |
| # define TOP_ROW 0 |
| # define LEFT_COL 0 |
| |
| # define M_FD(sp) sp->_mouse_fd |
| |
| static void |
| write_event(SCREEN *sp, int down, int button, int x, int y) |
| { |
| char buf[6]; |
| unsigned long ignore; |
| |
| _nc_STRCPY(buf, "\033[M", sizeof(buf)); /* should be the same as key_mouse */ |
| buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40); |
| buf[4] = ' ' + x - LEFT_COL + 1; |
| buf[5] = ' ' + y - TOP_ROW + 1; |
| DosWrite(sp->_emxmouse_wfd, buf, 6, &ignore); |
| } |
| |
| static void |
| #if USE_KLIBC_MOUSE |
| mouse_server(void *param) |
| #else |
| mouse_server(unsigned long param) |
| #endif |
| { |
| SCREEN *sp = (SCREEN *) param; |
| unsigned short fWait = MOU_WAIT; |
| /* NOPTRRECT mourt = { 0,0,24,79 }; */ |
| MOUEVENTINFO mouev; |
| HMOU hmou; |
| unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN; |
| int nbuttons = 3; |
| int oldstate = 0; |
| char err[80]; |
| unsigned long rc; |
| |
| /* open the handle for the mouse */ |
| if (MouOpen(NULL, &hmou) == 0) { |
| rc = MouSetEventMask(&mask, hmou); |
| if (rc) { /* retry with 2 buttons */ |
| mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN; |
| rc = MouSetEventMask(&mask, hmou); |
| nbuttons = 2; |
| } |
| if (rc == 0 && MouDrawPtr(hmou) == 0) { |
| for (;;) { |
| /* sit and wait on the event queue */ |
| rc = MouReadEventQue(&mouev, &fWait, hmou); |
| if (rc) { |
| _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err)) |
| "Error reading mouse queue, rc=%lu.\r\n", rc); |
| break; |
| } |
| if (!sp->_emxmouse_activated) |
| goto finish; |
| |
| /* |
| * OS/2 numbers a 3-button mouse inconsistently from other |
| * platforms: |
| * 1 = left |
| * 2 = right |
| * 3 = middle. |
| */ |
| if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN) |
| write_event(sp, mouev.fs & MOUSE_BN1_DOWN, |
| sp->_emxmouse_buttons[1], mouev.col, mouev.row); |
| if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN) |
| write_event(sp, mouev.fs & MOUSE_BN2_DOWN, |
| sp->_emxmouse_buttons[3], mouev.col, mouev.row); |
| if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN) |
| write_event(sp, mouev.fs & MOUSE_BN3_DOWN, |
| sp->_emxmouse_buttons[2], mouev.col, mouev.row); |
| |
| finish: |
| oldstate = mouev.fs; |
| } |
| } else { |
| _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err)) |
| "Error setting event mask, buttons=%d, rc=%lu.\r\n", |
| nbuttons, rc); |
| } |
| |
| DosWrite(2, err, strlen(err), &rc); |
| MouClose(hmou); |
| } |
| DosExit(EXIT_THREAD, 0L); |
| } |
| |
| #endif /* USE_EMX_MOUSE */ |
| |
| #if USE_SYSMOUSE |
| static void |
| sysmouse_server(SCREEN *sp) |
| { |
| struct mouse_info the_mouse; |
| MEVENT *work; |
| |
| the_mouse.operation = MOUSE_GETINFO; |
| if (sp != 0 |
| && sp->_mouse_fd >= 0 |
| && sp->_sysmouse_tail < FIFO_SIZE |
| && ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { |
| |
| if (sp->_sysmouse_head > sp->_sysmouse_tail) { |
| sp->_sysmouse_tail = 0; |
| sp->_sysmouse_head = 0; |
| } |
| work = &(sp->_sysmouse_fifo[sp->_sysmouse_tail]); |
| memset(work, 0, sizeof(*work)); |
| work->id = NORMAL_EVENT; /* there's only one mouse... */ |
| |
| sp->_sysmouse_old_buttons = sp->_sysmouse_new_buttons; |
| sp->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7; |
| |
| if (sp->_sysmouse_new_buttons) { |
| if (sp->_sysmouse_new_buttons & 1) |
| work->bstate |= BUTTON1_PRESSED; |
| if (sp->_sysmouse_new_buttons & 2) |
| work->bstate |= BUTTON2_PRESSED; |
| if (sp->_sysmouse_new_buttons & 4) |
| work->bstate |= BUTTON3_PRESSED; |
| } else { |
| if (sp->_sysmouse_old_buttons & 1) |
| work->bstate |= BUTTON1_RELEASED; |
| if (sp->_sysmouse_old_buttons & 2) |
| work->bstate |= BUTTON2_RELEASED; |
| if (sp->_sysmouse_old_buttons & 4) |
| work->bstate |= BUTTON3_RELEASED; |
| } |
| |
| /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */ |
| the_mouse.operation = MOUSE_HIDE; |
| ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); |
| the_mouse.operation = MOUSE_SHOW; |
| ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); |
| |
| /* |
| * We're only interested if the button is pressed or released. |
| * FIXME: implement continuous event-tracking. |
| */ |
| if (sp->_sysmouse_new_buttons != sp->_sysmouse_old_buttons) { |
| sp->_sysmouse_tail += 1; |
| } |
| work->x = the_mouse.u.data.x / sp->_sysmouse_char_width; |
| work->y = the_mouse.u.data.y / sp->_sysmouse_char_height; |
| } |
| } |
| |
| static void |
| handle_sysmouse(int sig GCC_UNUSED) |
| { |
| sysmouse_server(CURRENT_SCREEN); |
| } |
| #endif /* USE_SYSMOUSE */ |
| |
| #ifndef USE_TERM_DRIVER |
| #define xterm_kmous "\033[M" |
| |
| static void |
| init_xterm_mouse(SCREEN *sp) |
| { |
| sp->_mouse_type = M_XTERM; |
| sp->_mouse_format = MF_X10; |
| sp->_mouse_xtermcap = tigetstr("XM"); |
| if (VALID_STRING(sp->_mouse_xtermcap)) { |
| char *code = strstr(sp->_mouse_xtermcap, "[?"); |
| if (code != 0) { |
| code += 2; |
| while ((*code >= '0') && (*code <= '9')) { |
| char *next = code; |
| while ((*next >= '0') && (*next <= '9')) { |
| ++next; |
| } |
| if (!strncmp(code, "1006", (size_t) (next - code))) { |
| sp->_mouse_format = MF_SGR1006; |
| } |
| #ifdef EXP_XTERM_1005 |
| if (!strncmp(code, "1005", (size_t) (next - code))) { |
| sp->_mouse_format = MF_XTERM_1005; |
| } |
| #endif |
| if (*next == ';') { |
| while (*next == ';') { |
| ++next; |
| } |
| code = next; |
| } else { |
| break; |
| } |
| } |
| } |
| } else { |
| int code = tigetnum("XM"); |
| switch (code) { |
| #ifdef EXP_XTERM_1005 |
| case 1005: |
| /* see "xterm+sm+1005" */ |
| sp->_mouse_xtermcap = "\033[?1005;1000%?%p1%{1}%=%th%el%;"; |
| sp->_mouse_format = MF_XTERM_1005; |
| break; |
| #endif |
| case 1006: |
| /* see "xterm+sm+1006" */ |
| sp->_mouse_xtermcap = "\033[?1006;1000%?%p1%{1}%=%th%el%;"; |
| sp->_mouse_format = MF_SGR1006; |
| break; |
| default: |
| sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;"; |
| break; |
| } |
| } |
| } |
| #endif |
| |
| static void |
| enable_xterm_mouse(SCREEN *sp, int enable) |
| { |
| #if USE_EMX_MOUSE |
| sp->_emxmouse_activated = enable; |
| #else |
| NCURSES_PUTP2("xterm-mouse", TIPARM_1(sp->_mouse_xtermcap, enable)); |
| #endif |
| sp->_mouse_active = enable; |
| } |
| |
| #if USE_GPM_SUPPORT |
| static bool |
| allow_gpm_mouse(SCREEN *sp GCC_UNUSED) |
| { |
| bool result = FALSE; |
| |
| #if USE_WEAK_SYMBOLS |
| /* Danger Robinson: do not use dlopen for libgpm if already loaded */ |
| if ((Gpm_Wgetch) != 0) { |
| if (!sp->_mouse_gpm_loaded) { |
| T(("GPM library was already dlopen'd, not by us")); |
| } |
| } else |
| #endif |
| /* GPM does printf's without checking if stdout is a terminal */ |
| if (NC_ISATTY(fileno(stdout))) { |
| const char *list = getenv("NCURSES_GPM_TERMS"); |
| const char *env = getenv("TERM"); |
| if (list != 0) { |
| if (env != 0) { |
| result = _nc_name_match(list, env, "|:"); |
| } |
| } else { |
| /* GPM checks the beginning of the $TERM variable to decide if it |
| * should pass xterm events through. There is no real advantage in |
| * allowing GPM to do this. Recent versions relax that check, and |
| * pretend that GPM can work with any terminal having the kmous |
| * capability. Perhaps that works for someone. If so, they can |
| * set the environment variable (above). |
| */ |
| if (env != 0 && strstr(env, "linux") != 0) { |
| result = TRUE; |
| } |
| } |
| } |
| return result; |
| } |
| |
| #ifdef HAVE_LIBDL |
| static void |
| unload_gpm_library(SCREEN *sp) |
| { |
| if (sp->_dlopen_gpm != 0) { |
| T(("unload GPM library")); |
| sp->_mouse_gpm_loaded = FALSE; |
| sp->_mouse_fd = -1; |
| } |
| } |
| |
| static void |
| load_gpm_library(SCREEN *sp) |
| { |
| sp->_mouse_gpm_found = FALSE; |
| |
| /* |
| * If we already had a successful dlopen, reuse it. |
| */ |
| if (sp->_dlopen_gpm != 0) { |
| sp->_mouse_gpm_found = TRUE; |
| sp->_mouse_gpm_loaded = TRUE; |
| } else if ((sp->_dlopen_gpm = dlopen(LIBGPM_SONAME, my_RTLD)) != 0) { |
| #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__) |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wpedantic" |
| #endif |
| if (GET_DLSYM(gpm_fd) == 0 || |
| GET_DLSYM(Gpm_Open) == 0 || |
| GET_DLSYM(Gpm_Close) == 0 || |
| GET_DLSYM(Gpm_GetEvent) == 0) { |
| #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__) |
| #pragma GCC diagnostic pop |
| #endif |
| T(("GPM initialization failed: %s", dlerror())); |
| unload_gpm_library(sp); |
| dlclose(sp->_dlopen_gpm); |
| sp->_dlopen_gpm = 0; |
| } else { |
| sp->_mouse_gpm_found = TRUE; |
| sp->_mouse_gpm_loaded = TRUE; |
| } |
| } |
| } |
| #endif /* HAVE_LIBDL */ |
| |
| static bool |
| enable_gpm_mouse(SCREEN *sp, bool enable) |
| { |
| bool result; |
| |
| T((T_CALLED("enable_gpm_mouse(%d)"), enable)); |
| |
| if (enable && !sp->_mouse_active) { |
| #ifdef HAVE_LIBDL |
| if (sp->_mouse_gpm_found && !sp->_mouse_gpm_loaded) { |
| load_gpm_library(sp); |
| } |
| #endif |
| if (sp->_mouse_gpm_loaded) { |
| int code; |
| |
| /* GPM: initialize connection to gpm server */ |
| sp->_mouse_gpm_connect.eventMask = GPM_DOWN | GPM_UP; |
| sp->_mouse_gpm_connect.defaultMask = |
| (unsigned short) (~(sp->_mouse_gpm_connect.eventMask | GPM_HARD)); |
| sp->_mouse_gpm_connect.minMod = 0; |
| sp->_mouse_gpm_connect.maxMod = |
| (unsigned short) (~((1 << KG_SHIFT) | |
| (1 << KG_SHIFTL) | |
| (1 << KG_SHIFTR))); |
| /* |
| * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open. |
| * The former is recognized by wscons (SunOS), and the latter by |
| * xterm. Those will not show up in ncurses' traces. |
| */ |
| code = my_Gpm_Open(&sp->_mouse_gpm_connect, 0); |
| result = (code >= 0); |
| |
| /* |
| * GPM can return a -2 if it is trying to do something with xterm. |
| * Ignore that, since it conflicts with our use of stdin. |
| */ |
| if (code == -2) { |
| my_Gpm_Close(); |
| } |
| } else { |
| result = FALSE; |
| } |
| sp->_mouse_active = result; |
| T(("GPM open %s", result ? "succeeded" : "failed")); |
| } else { |
| if (!enable && sp->_mouse_active) { |
| /* GPM: close connection to gpm server */ |
| my_Gpm_Close(); |
| sp->_mouse_active = FALSE; |
| T(("GPM closed")); |
| } |
| result = enable; |
| } |
| #ifdef HAVE_LIBDL |
| if (!result) { |
| unload_gpm_library(sp); |
| } |
| #endif |
| returnBool(result); |
| } |
| #endif /* USE_GPM_SUPPORT */ |
| |
| static void |
| initialize_mousetype(SCREEN *sp) |
| { |
| T((T_CALLED("initialize_mousetype()"))); |
| |
| /* Try gpm first, because gpm may be configured to run in xterm */ |
| #if USE_GPM_SUPPORT |
| if (allow_gpm_mouse(sp)) { |
| if (!sp->_mouse_gpm_loaded) { |
| #ifdef HAVE_LIBDL |
| load_gpm_library(sp); |
| #else /* !HAVE_LIBDL */ |
| sp->_mouse_gpm_found = TRUE; |
| sp->_mouse_gpm_loaded = TRUE; |
| #endif |
| } |
| |
| /* |
| * The gpm_fd file-descriptor may be negative (xterm). So we have to |
| * maintain our notion of whether the mouse connection is active |
| * without testing the file-descriptor. |
| */ |
| if (sp->_mouse_gpm_found && enable_gpm_mouse(sp, TRUE)) { |
| sp->_mouse_type = M_GPM; |
| sp->_mouse_fd = *(my_gpm_fd); |
| T(("GPM mouse_fd %d", sp->_mouse_fd)); |
| returnVoid; |
| } |
| } |
| #endif /* USE_GPM_SUPPORT */ |
| |
| /* OS/2 VIO */ |
| #if USE_EMX_MOUSE |
| if (!sp->_emxmouse_thread |
| && strstr(SP_TERMTYPE term_names, "xterm") == 0 |
| && NonEmpty(key_mouse)) { |
| int handles[2]; |
| |
| if (pipe(handles) < 0) { |
| perror("mouse pipe error"); |
| returnVoid; |
| } else { |
| int rc; |
| |
| if (!sp->_emxmouse_buttons[0]) { |
| const char *s = getenv("MOUSE_BUTTONS_123"); |
| |
| sp->_emxmouse_buttons[0] = 1; |
| if (s && strlen(s) >= 3) { |
| sp->_emxmouse_buttons[1] = s[0] - '0'; |
| sp->_emxmouse_buttons[2] = s[1] - '0'; |
| sp->_emxmouse_buttons[3] = s[2] - '0'; |
| } else { |
| sp->_emxmouse_buttons[1] = 1; |
| sp->_emxmouse_buttons[2] = 3; |
| sp->_emxmouse_buttons[3] = 2; |
| } |
| } |
| sp->_emxmouse_wfd = handles[1]; |
| M_FD(sp) = handles[0]; |
| /* Needed? */ |
| setmode(handles[0], O_BINARY); |
| setmode(handles[1], O_BINARY); |
| /* Do not use CRT functions, we may single-threaded. */ |
| rc = DosCreateThread((unsigned long *) &sp->_emxmouse_thread, |
| mouse_server, (long) sp, 0, 8192); |
| if (rc) { |
| printf("mouse thread error %d=%#x", rc, rc); |
| } else { |
| sp->_mouse_type = M_XTERM; |
| } |
| returnVoid; |
| } |
| } |
| #endif /* USE_EMX_MOUSE */ |
| |
| #if USE_SYSMOUSE |
| { |
| static char dev_tty[] = "/dev/tty"; |
| struct mouse_info the_mouse; |
| char *the_device = 0; |
| |
| if (NC_ISATTY(sp->_ifd)) |
| the_device = ttyname(sp->_ifd); |
| if (the_device == 0) |
| the_device = dev_tty; |
| |
| sp->_mouse_fd = open(the_device, O_RDWR); |
| |
| if (sp->_mouse_fd >= 0) { |
| /* |
| * sysmouse does not have a usable user interface for obtaining |
| * mouse events. The logical way to proceed (reading data on a |
| * stream) only works if one opens the device as root. Even in |
| * that mode, careful examination shows we lose events |
| * occasionally. The interface provided for user programs is to |
| * establish a signal handler. really. |
| * |
| * Take over SIGUSR2 for this purpose since SIGUSR1 is more |
| * likely to be used by an application. getch() will have to |
| * handle the misleading EINTR's. |
| */ |
| signal(SIGUSR2, SIG_IGN); |
| the_mouse.operation = MOUSE_MODE; |
| the_mouse.u.mode.mode = 0; |
| the_mouse.u.mode.signal = SIGUSR2; |
| if (ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { |
| signal(SIGUSR2, handle_sysmouse); |
| the_mouse.operation = MOUSE_SHOW; |
| ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); |
| |
| #if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) /* FreeBSD > 2.x */ |
| { |
| #ifndef FBIO_GETMODE /* FreeBSD 3.x */ |
| #define FBIO_GETMODE CONS_GET |
| #define FBIO_MODEINFO CONS_MODEINFO |
| #endif /* FBIO_GETMODE */ |
| video_info_t the_video; |
| |
| if (ioctl(sp->_mouse_fd, |
| FBIO_GETMODE, |
| &the_video.vi_mode) != -1 |
| && ioctl(sp->_mouse_fd, |
| FBIO_MODEINFO, |
| &the_video) != -1) { |
| sp->_sysmouse_char_width = the_video.vi_cwidth; |
| sp->_sysmouse_char_height = the_video.vi_cheight; |
| } |
| } |
| #endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */ |
| |
| if (sp->_sysmouse_char_width <= 0) |
| sp->_sysmouse_char_width = 8; |
| if (sp->_sysmouse_char_height <= 0) |
| sp->_sysmouse_char_height = 16; |
| sp->_mouse_type = M_SYSMOUSE; |
| returnVoid; |
| } |
| } |
| } |
| #endif /* USE_SYSMOUSE */ |
| |
| #ifdef USE_TERM_DRIVER |
| CallDriver(sp, td_initmouse); |
| #else |
| /* we know how to recognize mouse events under "xterm" */ |
| if (NonEmpty(key_mouse)) { |
| init_xterm_mouse(sp); |
| } else if (strstr(SP_TERMTYPE term_names, "xterm") != 0) { |
| if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK) |
| init_xterm_mouse(sp); |
| } |
| #endif |
| |
| returnVoid; |
| } |
| |
| static bool |
| _nc_mouse_init(SCREEN *sp) |
| /* initialize the mouse */ |
| { |
| bool result = FALSE; |
| |
| if (sp != 0) { |
| if (!sp->_mouse_initialized) { |
| int i; |
| |
| sp->_mouse_initialized = TRUE; |
| |
| TR(MY_TRACE, ("_nc_mouse_init() called")); |
| |
| sp->_mouse_eventp = FirstEV(sp); |
| for (i = 0; i < EV_MAX; i++) |
| Invalidate(sp->_mouse_events + i); |
| |
| initialize_mousetype(sp); |
| |
| T(("_nc_mouse_init() set mousetype to %d", sp->_mouse_type)); |
| } |
| result = sp->_mouse_initialized; |
| } |
| return result; |
| } |
| |
| /* |
| * Query to see if there is a pending mouse event. This is called from |
| * fifo_push() in lib_getch.c |
| */ |
| static bool |
| _nc_mouse_event(SCREEN *sp) |
| { |
| MEVENT *eventp = sp->_mouse_eventp; |
| bool result = FALSE; |
| |
| (void) eventp; |
| |
| switch (sp->_mouse_type) { |
| case M_XTERM: |
| /* xterm: never have to query, mouse events are in the keyboard stream */ |
| #if USE_EMX_MOUSE |
| { |
| char kbuf[3]; |
| |
| int i, res = read(M_FD(sp), &kbuf, 3); /* Eat the prefix */ |
| if (res != 3) |
| printf("Got %d chars instead of 3 for prefix.\n", res); |
| for (i = 0; i < res; i++) { |
| if (kbuf[i] != key_mouse[i]) |
| printf("Got char %d instead of %d for prefix.\n", |
| (int) kbuf[i], (int) key_mouse[i]); |
| } |
| result = TRUE; |
| } |
| #endif /* USE_EMX_MOUSE */ |
| break; |
| |
| #if USE_GPM_SUPPORT |
| case M_GPM: |
| if (sp->_mouse_fd >= 0) { |
| /* query server for event, return TRUE if we find one */ |
| Gpm_Event ev; |
| |
| switch (my_Gpm_GetEvent(&ev)) { |
| case 0: |
| /* Connection closed, drop the mouse. */ |
| sp->_mouse_fd = -1; |
| break; |
| case 1: |
| /* there's only one mouse... */ |
| eventp->id = NORMAL_EVENT; |
| |
| eventp->bstate = 0; |
| switch (ev.type & 0x0f) { |
| case (GPM_DOWN): |
| if (ev.buttons & GPM_B_LEFT) |
| eventp->bstate |= BUTTON1_PRESSED; |
| if (ev.buttons & GPM_B_MIDDLE) |
| eventp->bstate |= BUTTON2_PRESSED; |
| if (ev.buttons & GPM_B_RIGHT) |
| eventp->bstate |= BUTTON3_PRESSED; |
| break; |
| case (GPM_UP): |
| if (ev.buttons & GPM_B_LEFT) |
| eventp->bstate |= BUTTON1_RELEASED; |
| if (ev.buttons & GPM_B_MIDDLE) |
| eventp->bstate |= BUTTON2_RELEASED; |
| if (ev.buttons & GPM_B_RIGHT) |
| eventp->bstate |= BUTTON3_RELEASED; |
| break; |
| default: |
| eventp->bstate |= REPORT_MOUSE_POSITION; |
| break; |
| } |
| |
| eventp->x = ev.x - 1; |
| eventp->y = ev.y - 1; |
| eventp->z = 0; |
| |
| /* bump the next-free pointer into the circular list */ |
| sp->_mouse_eventp = NEXT(eventp); |
| result = TRUE; |
| break; |
| } |
| } |
| break; |
| #endif |
| |
| #if USE_SYSMOUSE |
| case M_SYSMOUSE: |
| if (sp->_sysmouse_head < sp->_sysmouse_tail) { |
| *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head]; |
| |
| /* |
| * Point the fifo-head to the next possible location. If there |
| * are none, reset the indices. This may be interrupted by the |
| * signal handler, doing essentially the same reset. |
| */ |
| sp->_sysmouse_head += 1; |
| if (sp->_sysmouse_head == sp->_sysmouse_tail) { |
| sp->_sysmouse_tail = 0; |
| sp->_sysmouse_head = 0; |
| } |
| |
| /* bump the next-free pointer into the circular list */ |
| sp->_mouse_eventp = eventp = NEXT(eventp); |
| result = TRUE; |
| } |
| break; |
| #endif /* USE_SYSMOUSE */ |
| |
| #ifdef USE_TERM_DRIVER |
| case M_TERM_DRIVER: |
| while (sp->_drv_mouse_head < sp->_drv_mouse_tail) { |
| *eventp = sp->_drv_mouse_fifo[sp->_drv_mouse_head]; |
| |
| /* |
| * Point the fifo-head to the next possible location. If there |
| * are none, reset the indices. |
| */ |
| sp->_drv_mouse_head += 1; |
| if (sp->_drv_mouse_head == sp->_drv_mouse_tail) { |
| sp->_drv_mouse_tail = 0; |
| sp->_drv_mouse_head = 0; |
| } |
| |
| /* bump the next-free pointer into the circular list */ |
| sp->_mouse_eventp = eventp = NEXT(eventp); |
| result = TRUE; |
| } |
| break; |
| #endif |
| |
| case M_NONE: |
| break; |
| } |
| |
| return result; /* true if we found an event */ |
| } |
| |
| #if USE_EMX_MOUSE |
| #define PRESS_POSITION(n) \ |
| do { \ |
| eventp->bstate = MASK_PRESS(n); \ |
| sp->_mouse_bstate |= MASK_PRESS(n); \ |
| if (button & 0x40) { \ |
| eventp->bstate = MASK_RELEASE(n); \ |
| sp->_mouse_bstate &= ~MASK_PRESS(n); \ |
| } \ |
| } while (0) |
| #else |
| #define PRESS_POSITION(n) \ |
| do { \ |
| eventp->bstate = (mmask_t) ((sp->_mouse_bstate & MASK_PRESS(n)) \ |
| ? REPORT_MOUSE_POSITION \ |
| : MASK_PRESS(n)); \ |
| sp->_mouse_bstate |= MASK_PRESS(n); \ |
| } while (0) |
| #endif |
| |
| static bool |
| handle_wheel(SCREEN *sp, MEVENT * eventp, int button, int wheel) |
| { |
| bool result = TRUE; |
| |
| switch (button & 3) { |
| case 0: |
| if (wheel) { |
| eventp->bstate = MASK_PRESS(4); |
| /* Do not record in sp->_mouse_bstate; there will be no |
| * corresponding release event. |
| */ |
| } else { |
| PRESS_POSITION(1); |
| } |
| break; |
| case 1: |
| if (wheel) { |
| #if NCURSES_MOUSE_VERSION >= 2 |
| eventp->bstate = MASK_PRESS(5); |
| /* See comment above for button 4 */ |
| #else |
| /* Ignore this event as it is not a true press of the button */ |
| eventp->bstate = REPORT_MOUSE_POSITION; |
| #endif |
| } else { |
| PRESS_POSITION(2); |
| } |
| break; |
| case 2: |
| PRESS_POSITION(3); |
| break; |
| default: |
| result = FALSE; |
| break; |
| } |
| return result; |
| } |
| |
| static bool |
| decode_X10_bstate(SCREEN *sp, MEVENT * eventp, unsigned intro) |
| { |
| bool result; |
| int button = 0; |
| int wheel = (intro & 96) == 96; |
| |
| eventp->bstate = 0; |
| |
| if (intro >= 96) { |
| if (intro >= 160) { |
| button = (int) (intro - 152); /* buttons 8-11 */ |
| } else { |
| button = (int) (intro - 92); /* buttons 4-7 */ |
| } |
| } else { |
| button = (intro & 3); |
| } |
| |
| if (button > MAX_BUTTONS) { |
| eventp->bstate = REPORT_MOUSE_POSITION; |
| } else if (!handle_wheel(sp, eventp, (int) intro, wheel)) { |
| |
| /* |
| * Release events aren't reported for individual buttons, just for |
| * the button set as a whole. However, because there are normally |
| * no mouse events under xterm that intervene between press and |
| * release, we can infer the button actually released by looking at |
| * the previous event. |
| */ |
| if (sp->_mouse_bstate & BUTTON_PRESSED) { |
| int b; |
| |
| eventp->bstate = BUTTON_RELEASED; |
| for (b = 1; b <= MAX_BUTTONS; ++b) { |
| if (!(sp->_mouse_bstate & MASK_PRESS(b))) |
| eventp->bstate &= ~MASK_RELEASE(b); |
| } |
| sp->_mouse_bstate = 0; |
| } else { |
| /* |
| * xterm will return a stream of release-events to let the |
| * application know where the mouse is going, if private mode |
| * 1002 or 1003 is enabled. |
| */ |
| eventp->bstate = REPORT_MOUSE_POSITION; |
| } |
| } |
| |
| if (intro & 4) { |
| eventp->bstate |= BUTTON_SHIFT; |
| } |
| if (intro & 8) { |
| eventp->bstate |= BUTTON_ALT; |
| } |
| if (intro & 16) { |
| eventp->bstate |= BUTTON_CTRL; |
| } |
| result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE; |
| return result; |
| } |
| |
| /* This code requires that your xterm entry contain the kmous capability and |
| * that it be set to the \E[M documented in the Xterm Control Sequences |
| * reference. This is how we arrange for mouse events to be reported via a |
| * KEY_MOUSE return value from wgetch(). After this value is received, |
| * _nc_mouse_inline() gets called and is immediately responsible for parsing |
| * the mouse status information following the prefix. |
| * |
| * The following quotes from the ctlseqs.ms document in the XTerm distribution, |
| * describing the mouse tracking feature: |
| * |
| * Parameters for all mouse tracking escape sequences generated by xterm encode |
| * numeric parameters in a single character as value+040. For example, ! is |
| * 1. |
| * |
| * On button press or release, xterm sends ESC [ M CbCxCy. The low two bits of |
| * Cb encode button information: 0=MB1 pressed, 1=MB2 pressed, 2=MB3 pressed, |
| * 3=release. The upper bits encode what modifiers were down when the button |
| * was pressed and are added together. 4=Shift, 8=Meta, 16=Control. Cx and Cy |
| * are the x and y coordinates of the mouse event. The upper left corner is |
| * (1,1). |
| * |
| * (End quote) By the time we get here, we've eaten the key prefix. FYI, the |
| * loop below is necessary because mouse click info isn't guaranteed to present |
| * as a single clist item. |
| * |
| * Wheel mice may return buttons 4 and 5 when the wheel is turned. We encode |
| * those as button presses. |
| */ |
| static bool |
| decode_xterm_X10(SCREEN *sp, MEVENT * eventp) |
| { |
| #define MAX_KBUF 3 |
| unsigned char kbuf[MAX_KBUF + 1]; |
| size_t grabbed; |
| int res; |
| bool result; |
| |
| # if USE_PTHREADS_EINTR |
| # if USE_WEAK_SYMBOLS |
| if ((pthread_self) && (pthread_kill) && (pthread_equal)) |
| # endif |
| _nc_globals.read_thread = pthread_self(); |
| # endif |
| for (grabbed = 0; grabbed < MAX_KBUF; grabbed += (size_t) res) { |
| |
| /* For VIO mouse we add extra bit 64 to disambiguate button-up. */ |
| res = (int) read( |
| #if USE_EMX_MOUSE |
| (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd, |
| #else |
| sp->_ifd, |
| #endif |
| kbuf + grabbed, (size_t) (MAX_KBUF - (int) grabbed)); |
| if (res == -1) |
| break; |
| } |
| #if USE_PTHREADS_EINTR |
| _nc_globals.read_thread = 0; |
| #endif |
| kbuf[MAX_KBUF] = '\0'; |
| |
| TR(TRACE_IEVENT, |
| ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf)); |
| |
| /* there's only one mouse... */ |
| eventp->id = NORMAL_EVENT; |
| |
| result = decode_X10_bstate(sp, eventp, kbuf[0]); |
| |
| eventp->x = (kbuf[1] - ' ') - 1; |
| eventp->y = (kbuf[2] - ' ') - 1; |
| |
| return result; |
| } |
| |
| #ifdef EXP_XTERM_1005 |
| /* |
| * This is identical to X10/X11 responses except that there are two UTF-8 |
| * characters storing the ordinates instead of two bytes. |
| */ |
| static bool |
| decode_xterm_1005(SCREEN *sp, MEVENT * eventp) |
| { |
| char kbuf[80]; |
| size_t grabbed; |
| size_t limit = (sizeof(kbuf) - 1); |
| unsigned coords[2]; |
| bool result; |
| |
| coords[0] = 0; |
| coords[1] = 0; |
| |
| # if USE_PTHREADS_EINTR |
| # if USE_WEAK_SYMBOLS |
| if ((pthread_self) && (pthread_kill) && (pthread_equal)) |
| # endif |
| _nc_globals.read_thread = pthread_self(); |
| # endif |
| for (grabbed = 0; grabbed < limit;) { |
| int res; |
| |
| res = (int) read( |
| #if USE_EMX_MOUSE |
| (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd, |
| #else |
| sp->_ifd, |
| #endif |
| (kbuf + grabbed), (size_t) 1); |
| if (res == -1) |
| break; |
| grabbed += (size_t) res; |
| if (grabbed > 1) { |
| size_t check = 1; |
| int n; |
| |
| for (n = 0; n < 2; ++n) { |
| int rc; |
| |
| if (check >= grabbed) |
| break; |
| rc = _nc_conv_to_utf32(&coords[n], kbuf + check, (unsigned) |
| (grabbed - check)); |
| if (!rc) |
| break; |
| check += (size_t) rc; |
| } |
| if (n >= 2) |
| break; |
| } |
| } |
| #if USE_PTHREADS_EINTR |
| _nc_globals.read_thread = 0; |
| #endif |
| |
| TR(TRACE_IEVENT, |
| ("_nc_mouse_inline sees the following xterm data: %s", |
| _nc_visbufn(kbuf, (int) grabbed))); |
| |
| /* there's only one mouse... */ |
| eventp->id = NORMAL_EVENT; |
| |
| result = decode_X10_bstate(sp, eventp, UChar(kbuf[0])); |
| |
| eventp->x = (int) (coords[0] - ' ') - 1; |
| eventp->y = (int) (coords[1] - ' ') - 1; |
| |
| return result; |
| } |
| #endif /* EXP_XTERM_1005 */ |
| |
| /* |
| * ECMA-48 section 5.4 |
| */ |
| #define isInter(c) ((c) >= 0x20 && (c) <= 0x2f) |
| #define isParam(c) ((c) >= 0x30 && (c) <= 0x3f) |
| #define isFinal(c) ((c) >= 0x40 && (c) <= 0x7e) |
| |
| #define MAX_PARAMS 9 |
| |
| typedef struct { |
| int nerror; /* nonzero if there are unexpected chars */ |
| int nparam; /* number of numeric parameters */ |
| int params[MAX_PARAMS]; |
| int final; /* the final-character */ |
| } SGR_DATA; |
| |
| static bool |
| read_SGR(SCREEN *sp, SGR_DATA * result) |
| { |
| char kbuf[80]; /* bigger than any possible mouse response */ |
| int grabbed = 0; |
| int ch = 0; |
| int now = -1; |
| int marker = 1; |
| |
| memset(result, 0, sizeof(*result)); |
| # if USE_PTHREADS_EINTR |
| # if USE_WEAK_SYMBOLS |
| if ((pthread_self) && (pthread_kill) && (pthread_equal)) |
| # endif |
| _nc_globals.read_thread = pthread_self(); |
| # endif |
| |
| do { |
| int res; |
| |
| res = (int) read( |
| #if USE_EMX_MOUSE |
| (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd, |
| #else |
| sp->_ifd, |
| #endif |
| (kbuf + grabbed), (size_t) 1); |
| if (res == -1) |
| break; |
| if ((grabbed + MAX_KBUF) >= (int) sizeof(kbuf)) { |
| result->nerror++; |
| break; |
| } |
| ch = UChar(kbuf[grabbed]); |
| kbuf[grabbed + 1] = 0; |
| switch (ch) { |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| if (marker) { |
| ++now; |
| result->nparam = (now + 1); |
| } |
| marker = 0; |
| result->params[now] = (result->params[now] * 10) + (ch - '0'); |
| break; |
| case ';': |
| if (marker) { |
| ++now; |
| result->nparam = (now + 1); |
| } |
| marker = 1; |
| break; |
| default: |
| if (ch < 32 || ch > 126) { |
| /* |
| * Technically other characters could be interspersed in the |
| * response. Ignore those for now. |
| */ |
| result->nerror++; |
| continue; |
| } else if (isFinal(ch)) { |
| if (marker) { |
| result->nparam++; |
| } |
| result->final = ch; |
| } else { |
| result->nerror++; |
| } |
| break; |
| } |
| ++grabbed; |
| } while (!isFinal(ch)); |
| #if USE_PTHREADS_EINTR |
| _nc_globals.read_thread = 0; |
| #endif |
| |
| kbuf[++grabbed] = 0; |
| TR(TRACE_IEVENT, |
| ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf)); |
| return (grabbed > 0) && (result->nerror == 0); |
| } |
| |
| static bool |
| decode_xterm_SGR1006(SCREEN *sp, MEVENT * eventp) |
| { |
| SGR_DATA data; |
| bool result = FALSE; |
| if (read_SGR(sp, &data)) { |
| int b = data.params[0]; |
| int b3 = 1 + (b & 3); |
| int wheel = ((b & 64) == 64); |
| |
| if (b >= 132) { |
| b3 = MAX_BUTTONS + 1; |
| } else if (b >= 128) { |
| b3 = (b - 120); /* buttons 8-11 */ |
| } else if (b >= 64) { |
| b3 = (b - 60); /* buttons 6-7 */ |
| } |
| |
| eventp->id = NORMAL_EVENT; |
| if (data.final == 'M') { |
| (void) handle_wheel(sp, eventp, b, wheel); |
| } else if (b3 > MAX_BUTTONS) { |
| eventp->bstate = REPORT_MOUSE_POSITION; |
| } else { |
| mmask_t pressed = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_PRESSED); |
| mmask_t release = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_RELEASED); |
| if (sp->_mouse_bstate & pressed) { |
| eventp->bstate = release; |
| sp->_mouse_bstate &= ~pressed; |
| } else { |
| eventp->bstate = REPORT_MOUSE_POSITION; |
| } |
| } |
| if (b & 4) { |
| eventp->bstate |= BUTTON_SHIFT; |
| } |
| if (b & 8) { |
| eventp->bstate |= BUTTON_ALT; |
| } |
| if (b & 16) { |
| eventp->bstate |= BUTTON_CTRL; |
| } |
| result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE; |
| eventp->x = (data.params[1] ? (data.params[1] - 1) : 0); |
| eventp->y = (data.params[2] ? (data.params[2] - 1) : 0); |
| } |
| return result; |
| } |
| |
| static bool |
| _nc_mouse_inline(SCREEN *sp) |
| /* mouse report received in the keyboard stream -- parse its info */ |
| { |
| bool result = FALSE; |
| MEVENT *eventp = sp->_mouse_eventp; |
| |
| TR(MY_TRACE, ("_nc_mouse_inline() called")); |
| |
| if (sp->_mouse_type == M_XTERM) { |
| switch (sp->_mouse_format) { |
| case MF_X10: |
| result = decode_xterm_X10(sp, eventp); |
| break; |
| case MF_SGR1006: |
| result = decode_xterm_SGR1006(sp, eventp); |
| break; |
| #ifdef EXP_XTERM_1005 |
| case MF_XTERM_1005: |
| result = decode_xterm_1005(sp, eventp); |
| break; |
| #endif |
| } |
| |
| TR(MY_TRACE, |
| ("_nc_mouse_inline: primitive mouse-event %s has slot %ld", |
| _nc_tracemouse(sp, eventp), |
| (long) IndexEV(sp, eventp))); |
| |
| /* bump the next-free pointer into the circular list */ |
| sp->_mouse_eventp = NEXT(eventp); |
| |
| if (!result) { |
| /* If this event is from a wheel-mouse, treat it like position |
| * reports and avoid waiting for the release-events which will |
| * never come. |
| */ |
| if (eventp->bstate & BUTTON_PRESSED) { |
| int b; |
| |
| for (b = 4; b <= MAX_BUTTONS; ++b) { |
| if ((eventp->bstate & MASK_PRESS(b))) { |
| result = TRUE; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| return (result); |
| } |
| |
| static void |
| mouse_activate(SCREEN *sp, int on) |
| { |
| if (!on && !sp->_mouse_initialized) |
| return; |
| |
| if (!_nc_mouse_init(sp)) |
| return; |
| |
| if (on) { |
| sp->_mouse_bstate = 0; |
| switch (sp->_mouse_type) { |
| case M_XTERM: |
| #if NCURSES_EXT_FUNCS |
| NCURSES_SP_NAME(keyok) (NCURSES_SP_ARGx KEY_MOUSE, on); |
| #endif |
| TPUTS_TRACE("xterm mouse initialization"); |
| enable_xterm_mouse(sp, 1); |
| break; |
| #if USE_GPM_SUPPORT |
| case M_GPM: |
| if (enable_gpm_mouse(sp, TRUE)) { |
| sp->_mouse_fd = *(my_gpm_fd); |
| T(("GPM mouse_fd %d", sp->_mouse_fd)); |
| } |
| break; |
| #endif |
| #if USE_SYSMOUSE |
| case M_SYSMOUSE: |
| signal(SIGUSR2, handle_sysmouse); |
| sp->_mouse_active = TRUE; |
| break; |
| #endif |
| #ifdef USE_TERM_DRIVER |
| case M_TERM_DRIVER: |
| sp->_mouse_active = TRUE; |
| break; |
| #endif |
| case M_NONE: |
| return; |
| } |
| /* Make runtime binding to cut down on object size of applications that |
| * do not use the mouse (e.g., 'clear'). |
| */ |
| sp->_mouse_event = _nc_mouse_event; |
| sp->_mouse_inline = _nc_mouse_inline; |
| sp->_mouse_parse = _nc_mouse_parse; |
| sp->_mouse_resume = _nc_mouse_resume; |
| sp->_mouse_wrap = _nc_mouse_wrap; |
| } else { |
| |
| switch (sp->_mouse_type) { |
| case M_XTERM: |
| TPUTS_TRACE("xterm mouse deinitialization"); |
| enable_xterm_mouse(sp, 0); |
| break; |
| #if USE_GPM_SUPPORT |
| case M_GPM: |
| enable_gpm_mouse(sp, FALSE); |
| break; |
| #endif |
| #if USE_SYSMOUSE |
| case M_SYSMOUSE: |
| signal(SIGUSR2, SIG_IGN); |
| sp->_mouse_active = FALSE; |
| break; |
| #endif |
| #ifdef USE_TERM_DRIVER |
| case M_TERM_DRIVER: |
| sp->_mouse_active = FALSE; |
| break; |
| #endif |
| case M_NONE: |
| return; |
| } |
| } |
| NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG); |
| } |
| |
| /************************************************************************** |
| * |
| * Device-independent code |
| * |
| **************************************************************************/ |
| |
| static bool |
| _nc_mouse_parse(SCREEN *sp, int runcount) |
| /* parse a run of atomic mouse events into a gesture */ |
| { |
| MEVENT *eventp = sp->_mouse_eventp; |
| MEVENT *next, *ep; |
| MEVENT *first_valid = NULL; |
| MEVENT *first_invalid = NULL; |
| int n; |
| int b; |
| bool merge; |
| bool endLoop; |
| |
| TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount)); |
| |
| /* |
| * When we enter this routine, the event list next-free pointer |
| * points just past a run of mouse events that we know were separated |
| * in time by less than the critical click interval. The job of this |
| * routine is to collapse this run into a single higher-level event |
| * or gesture. |
| * |
| * We accomplish this in two passes. The first pass merges press/release |
| * pairs into click events. The second merges runs of click events into |
| * double or triple-click events. |
| * |
| * It's possible that the run may not resolve to a single event (for |
| * example, if the user quadruple-clicks). If so, leading events |
| * in the run are ignored if user does not call getmouse in a loop (getting |
| * them from newest to older). |
| * |
| * Note that this routine is independent of the format of the specific |
| * format of the pointing-device's reports. We can use it to parse |
| * gestures on anything that reports press/release events on a per- |
| * button basis, as long as the device-dependent mouse code puts stuff |
| * on the queue in MEVENT format. |
| */ |
| |
| /* |
| * Reset all events that were not set, in case the user sometimes calls |
| * getmouse only once and other times until there are no more events in |
| * queue. |
| * |
| * This also allows reaching the beginning of the run. |
| */ |
| ep = eventp; |
| for (n = runcount; n < EV_MAX; n++) { |
| Invalidate(ep); |
| ep = NEXT(ep); |
| } |
| |
| #ifdef TRACE |
| if (USE_TRACEF(TRACE_IEVENT)) { |
| _trace_slot(sp, "before mouse press/release merge:"); |
| _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", |
| RunParams(sp, eventp, ep), |
| runcount); |
| _nc_unlock_global(tracef); |
| } |
| #endif /* TRACE */ |
| |
| /* first pass; merge press/release pairs */ |
| endLoop = FALSE; |
| while (!endLoop) { |
| next = NEXT(ep); |
| if (next == eventp) { |
| /* Will end the loop, but compact before */ |
| endLoop = TRUE; |
| } else { |
| |
| #define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \ |
| == !(next->bstate & MASK_RELEASE(x))) |
| |
| if (ValidEvent(ep) && ValidEvent(next) |
| && ep->x == next->x && ep->y == next->y |
| && (ep->bstate & BUTTON_PRESSED) |
| && (!(next->bstate & BUTTON_PRESSED))) { |
| bool changed = TRUE; |
| |
| for (b = 1; b <= MAX_BUTTONS; ++b) { |
| if (!MASK_CHANGED(b)) { |
| changed = FALSE; |
| break; |
| } |
| } |
| |
| if (changed) { |
| merge = FALSE; |
| for (b = 1; b <= MAX_BUTTONS; ++b) { |
| if ((sp->_mouse_mask & MASK_CLICK(b)) |
| && (ep->bstate & MASK_PRESS(b))) { |
| next->bstate &= ~MASK_RELEASE(b); |
| next->bstate |= MASK_CLICK(b); |
| merge = TRUE; |
| } |
| } |
| if (merge) { |
| Invalidate(ep); |
| } |
| } |
| } |
| } |
| |
| /* Compact valid events */ |
| if (!ValidEvent(ep)) { |
| if ((first_valid != NULL) && (first_invalid == NULL)) { |
| first_invalid = ep; |
| } |
| } else { |
| if (first_valid == NULL) { |
| first_valid = ep; |
| } else if (first_invalid != NULL) { |
| *first_invalid = *ep; |
| Invalidate(ep); |
| first_invalid = NEXT(first_invalid); |
| } |
| } |
| |
| ep = next; |
| } |
| |
| if (first_invalid != NULL) { |
| eventp = first_invalid; |
| } |
| #ifdef TRACE |
| if (USE_TRACEF(TRACE_IEVENT)) { |
| _trace_slot(sp, "before mouse click merge:"); |
| if (first_valid == NULL) { |
| _tracef("_nc_mouse_parse: no valid event"); |
| } else { |
| _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", |
| RunParams(sp, eventp, first_valid), |
| runcount); |
| _nc_unlock_global(tracef); |
| } |
| } |
| #endif /* TRACE */ |
| |
| /* |
| * Second pass; merge click runs. We merge click events forward in the |
| * queue. For example, double click can be changed to triple click. |
| * |
| * NOTE: There is a problem with this design! If the application |
| * allows enough click events to pile up in the circular queue so |
| * they wrap around, it will cheerfully merge the newest forward |
| * into the oldest, creating a bogus doubleclick and confusing |
| * the queue-traversal logic rather badly. Generally this won't |
| * happen, because calling getmouse() marks old events invalid and |
| * ineligible for merges. The true solution to this problem would |
| * be to timestamp each MEVENT and perform the obvious sanity check, |
| * but the timer element would have to have sub-second resolution, |
| * which would get us into portability trouble. |
| */ |
| first_invalid = NULL; |
| endLoop = (first_valid == NULL); |
| ep = first_valid; |
| while (!endLoop) { |
| next = NEXT(ep); |
| |
| if (next == eventp) { |
| /* Will end the loop, but check event type and compact before */ |
| endLoop = TRUE; |
| } else if (!ValidEvent(next)) { |
| continue; |
| } else { |
| /* merge click events forward */ |
| if ((ep->bstate & BUTTON_CLICKED) |
| && (next->bstate & BUTTON_CLICKED)) { |
| merge = FALSE; |
| for (b = 1; b <= MAX_BUTTONS; ++b) { |
| if ((sp->_mouse_mask & MASK_DOUBLE_CLICK(b)) |
| && (ep->bstate & MASK_CLICK(b)) |
| && (next->bstate & MASK_CLICK(b))) { |
| next->bstate &= ~MASK_CLICK(b); |
| next->bstate |= MASK_DOUBLE_CLICK(b); |
| merge = TRUE; |
| } |
| } |
| if (merge) { |
| Invalidate(ep); |
| } |
| } |
| |
| /* merge double-click events forward */ |
| if ((ep->bstate & BUTTON_DOUBLE_CLICKED) |
| && (next->bstate & BUTTON_CLICKED)) { |
| merge = FALSE; |
| for (b = 1; b <= MAX_BUTTONS; ++b) { |
| if ((sp->_mouse_mask & MASK_TRIPLE_CLICK(b)) |
| && (ep->bstate & MASK_DOUBLE_CLICK(b)) |
| && (next->bstate & MASK_CLICK(b))) { |
| next->bstate &= ~MASK_CLICK(b); |
| next->bstate |= MASK_TRIPLE_CLICK(b); |
| merge = TRUE; |
| } |
| } |
| if (merge) { |
| Invalidate(ep); |
| } |
| } |
| } |
| |
| /* Discard event if it does not match event mask */ |
| if (!(ep->bstate & sp->_mouse_mask2)) { |
| Invalidate(ep); |
| } |
| |
| /* Compact valid events */ |
| if (!ValidEvent(ep)) { |
| if (ep == first_valid) { |
| first_valid = next; |
| } else if (first_invalid == NULL) { |
| first_invalid = ep; |
| } |
| } else if (first_invalid != NULL) { |
| *first_invalid = *ep; |
| Invalidate(ep); |
| first_invalid = NEXT(first_invalid); |
| } |
| |
| ep = next; |
| } |
| |
| if (first_invalid == NULL) { |
| first_invalid = eventp; |
| } |
| sp->_mouse_eventp = first_invalid; |
| |
| #ifdef TRACE |
| if (first_valid != NULL) { |
| if (USE_TRACEF(TRACE_IEVENT)) { |
| _trace_slot(sp, "after mouse event queue compaction:"); |
| _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", |
| RunParams(sp, first_invalid, first_valid), |
| runcount); |
| _nc_unlock_global(tracef); |
| } |
| for (ep = first_valid; ep != first_invalid; ep = NEXT(ep)) { |
| if (ValidEvent(ep)) |
| TR(MY_TRACE, |
| ("_nc_mouse_parse: returning composite mouse event %s at slot %ld", |
| _nc_tracemouse(sp, ep), |
| (long) IndexEV(sp, ep))); |
| } |
| } |
| #endif /* TRACE */ |
| |
| /* after all this, do we have a valid event? */ |
| return ValidEvent(PREV(first_invalid)); |
| } |
| |
| static void |
| _nc_mouse_wrap(SCREEN *sp) |
| /* release mouse -- called by endwin() before shellout/exit */ |
| { |
| TR(MY_TRACE, ("_nc_mouse_wrap() called")); |
| |
| switch (sp->_mouse_type) { |
| case M_XTERM: |
| if (sp->_mouse_mask) |
| mouse_activate(sp, FALSE); |
| break; |
| #if USE_GPM_SUPPORT |
| /* GPM: pass all mouse events to next client */ |
| case M_GPM: |
| if (sp->_mouse_mask) |
| mouse_activate(sp, FALSE); |
| break; |
| #endif |
| #if USE_SYSMOUSE |
| case M_SYSMOUSE: |
| mouse_activate(sp, FALSE); |
| break; |
| #endif |
| #ifdef USE_TERM_DRIVER |
| case M_TERM_DRIVER: |
| mouse_activate(sp, FALSE); |
| break; |
| #endif |
| case M_NONE: |
| break; |
| } |
| } |
| |
| static void |
| _nc_mouse_resume(SCREEN *sp) |
| /* re-connect to mouse -- called by doupdate() after shellout */ |
| { |
| TR(MY_TRACE, ("_nc_mouse_resume() called")); |
| |
| switch (sp->_mouse_type) { |
| case M_XTERM: |
| /* xterm: re-enable reporting */ |
| if (sp->_mouse_mask) |
| mouse_activate(sp, TRUE); |
| break; |
| |
| #if USE_GPM_SUPPORT |
| case M_GPM: |
| /* GPM: reclaim our event set */ |
| if (sp->_mouse_mask) |
| mouse_activate(sp, TRUE); |
| break; |
| #endif |
| |
| #if USE_SYSMOUSE |
| case M_SYSMOUSE: |
| mouse_activate(sp, TRUE); |
| break; |
| #endif |
| |
| #ifdef USE_TERM_DRIVER |
| case M_TERM_DRIVER: |
| mouse_activate(sp, TRUE); |
| break; |
| #endif |
| |
| case M_NONE: |
| break; |
| } |
| } |
| |
| /************************************************************************** |
| * |
| * Mouse interface entry points for the API |
| * |
| **************************************************************************/ |
| |
| NCURSES_EXPORT(int) |
| NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent) |
| { |
| int result = ERR; |
| MEVENT *eventp; |
| |
| T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent)); |
| |
| if ((aevent != 0) && |
| (SP_PARM != 0) && |
| (SP_PARM->_mouse_type != M_NONE) && |
| (eventp = SP_PARM->_mouse_eventp) != 0) { |
| /* compute the current-event pointer */ |
| MEVENT *prev = PREV(eventp); |
| |
| /* |
| * Discard events not matching mask (there could be still some if |
| * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns |
| * false). |
| */ |
| while (ValidEvent(prev) && (!(prev->bstate & SP_PARM->_mouse_mask2))) { |
| Invalidate(prev); |
| prev = PREV(prev); |
| } |
| if (ValidEvent(prev)) { |
| /* copy the event we find there */ |
| *aevent = *prev; |
| |
| TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld", |
| _nc_tracemouse(SP_PARM, prev), |
| (long) IndexEV(SP_PARM, prev))); |
| |
| Invalidate(prev); /* so the queue slot becomes free */ |
| SP_PARM->_mouse_eventp = prev; |
| result = OK; |
| } else { |
| /* Reset the provided event */ |
| aevent->bstate = 0; |
| Invalidate(aevent); |
| aevent->x = 0; |
| aevent->y = 0; |
| aevent->z = 0; |
| } |
| } |
| returnCode(result); |
| } |
| |
| #if NCURSES_SP_FUNCS |
| /* grab a copy of the current mouse event */ |
| NCURSES_EXPORT(int) |
| getmouse(MEVENT * aevent) |
| { |
| return NCURSES_SP_NAME(getmouse) (CURRENT_SCREEN, aevent); |
| } |
| #endif |
| |
| NCURSES_EXPORT(int) |
| NCURSES_SP_NAME(ungetmouse) (NCURSES_SP_DCLx MEVENT * aevent) |
| { |
| int result = ERR; |
| MEVENT *eventp; |
| |
| T((T_CALLED("ungetmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent)); |
| |
| if (aevent != 0 && |
| SP_PARM != 0 && |
| (eventp = SP_PARM->_mouse_eventp) != 0) { |
| |
| /* stick the given event in the next-free slot */ |
| *eventp = *aevent; |
| |
| /* bump the next-free pointer into the circular list */ |
| SP_PARM->_mouse_eventp = NEXT(eventp); |
| |
| /* push back the notification event on the keyboard queue */ |
| result = NCURSES_SP_NAME(ungetch) (NCURSES_SP_ARGx KEY_MOUSE); |
| } |
| returnCode(result); |
| } |
| |
| #if NCURSES_SP_FUNCS |
| /* enqueue a synthesized mouse event to be seen by the next wgetch() */ |
| NCURSES_EXPORT(int) |
| ungetmouse(MEVENT * aevent) |
| { |
| return NCURSES_SP_NAME(ungetmouse) (CURRENT_SCREEN, aevent); |
| } |
| #endif |
| |
| NCURSES_EXPORT(mmask_t) |
| NCURSES_SP_NAME(mousemask) (NCURSES_SP_DCLx mmask_t newmask, mmask_t * oldmask) |
| /* set the mouse event mask */ |
| { |
| mmask_t result = 0; |
| |
| T((T_CALLED("mousemask(%p,%#lx,%p)"), |
| (void *) SP_PARM, |
| (unsigned long) newmask, |
| (void *) oldmask)); |
| |
| if (SP_PARM != 0) { |
| if (oldmask) |
| *oldmask = SP_PARM->_mouse_mask; |
| |
| if (newmask || SP_PARM->_mouse_initialized) { |
| _nc_mouse_init(SP_PARM); |
| |
| if (SP_PARM->_mouse_type != M_NONE) { |
| int b; |
| |
| result = newmask & |
| (REPORT_MOUSE_POSITION |
| | BUTTON_ALT |
| | BUTTON_CTRL |
| | BUTTON_SHIFT |
| | BUTTON_PRESSED |
| | BUTTON_RELEASED |
| | BUTTON_CLICKED |
| | BUTTON_DOUBLE_CLICKED |
| | BUTTON_TRIPLE_CLICKED); |
| |
| mouse_activate(SP_PARM, (bool) (result != 0)); |
| |
| SP_PARM->_mouse_mask = result; |
| SP_PARM->_mouse_mask2 = result; |
| |
| /* |
| * Make a mask corresponding to the states we will need to |
| * retain (temporarily) while building up the state that the |
| * user asked for. |
| */ |
| for (b = 1; b <= MAX_BUTTONS; ++b) { |
| if (SP_PARM->_mouse_mask2 & MASK_TRIPLE_CLICK(b)) |
| SP_PARM->_mouse_mask2 |= MASK_DOUBLE_CLICK(b); |
| if (SP_PARM->_mouse_mask2 & MASK_DOUBLE_CLICK(b)) |
| SP_PARM->_mouse_mask2 |= MASK_CLICK(b); |
| if (SP_PARM->_mouse_mask2 & MASK_CLICK(b)) |
| SP_PARM->_mouse_mask2 |= (MASK_PRESS(b) | |
| MASK_RELEASE(b)); |
| } |
| } |
| } |
| } |
| returnMMask(result); |
| } |
| |
| #if NCURSES_SP_FUNCS |
| NCURSES_EXPORT(mmask_t) |
| mousemask(mmask_t newmask, mmask_t * oldmask) |
| { |
| return NCURSES_SP_NAME(mousemask) (CURRENT_SCREEN, newmask, oldmask); |
| } |
| #endif |
| |
| NCURSES_EXPORT(bool) |
| wenclose(const WINDOW *win, int y, int x) |
| /* check to see if given window encloses given screen location */ |
| { |
| bool result = FALSE; |
| |
| T((T_CALLED("wenclose(%p,%d,%d)"), (const void *) win, y, x)); |
| |
| if (win != 0) { |
| y -= win->_yoffset; |
| result = ((win->_begy <= y && |
| win->_begx <= x && |
| (win->_begx + win->_maxx) >= x && |
| (win->_begy + win->_maxy) >= y) ? TRUE : FALSE); |
| } |
| returnBool(result); |
| } |
| |
| NCURSES_EXPORT(int) |
| NCURSES_SP_NAME(mouseinterval) (NCURSES_SP_DCLx int maxclick) |
| /* set the maximum mouse interval within which to recognize a click */ |
| { |
| int oldval; |
| |
| T((T_CALLED("mouseinterval(%p,%d)"), (void *) SP_PARM, maxclick)); |
| |
| if (SP_PARM != 0) { |
| oldval = SP_PARM->_maxclick; |
| if (maxclick >= 0) |
| SP_PARM->_maxclick = maxclick; |
| } else { |
| oldval = DEFAULT_MAXCLICK; |
| } |
| |
| returnCode(oldval); |
| } |
| |
| #if NCURSES_SP_FUNCS |
| NCURSES_EXPORT(int) |
| mouseinterval(int maxclick) |
| { |
| return NCURSES_SP_NAME(mouseinterval) (CURRENT_SCREEN, maxclick); |
| } |
| #endif |
| |
| /* This may be used by other routines to ask for the existence of mouse |
| support */ |
| NCURSES_EXPORT(bool) |
| _nc_has_mouse(SCREEN *sp) |
| { |
| return (((0 == sp) || (sp->_mouse_type == M_NONE)) ? FALSE : TRUE); |
| } |
| |
| NCURSES_EXPORT(bool) |
| NCURSES_SP_NAME(has_mouse) (NCURSES_SP_DCL0) |
| { |
| return _nc_has_mouse(SP_PARM); |
| } |
| |
| #if NCURSES_SP_FUNCS |
| NCURSES_EXPORT(bool) |
| has_mouse(void) |
| { |
| return _nc_has_mouse(CURRENT_SCREEN); |
| } |
| #endif |
| |
| NCURSES_EXPORT(bool) |
| wmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen) |
| { |
| bool result = FALSE; |
| |
| T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"), |
| (const void *) win, |
| (void *) pY, |
| (void *) pX, |
| to_screen)); |
| |
| if (win && pY && pX) { |
| int y = *pY; |
| int x = *pX; |
| |
| if (to_screen) { |
| y += win->_begy + win->_yoffset; |
| x += win->_begx; |
| if (wenclose(win, y, x)) |
| result = TRUE; |
| } else { |
| if (wenclose(win, y, x)) { |
| y -= (win->_begy + win->_yoffset); |
| x -= win->_begx; |
| result = TRUE; |
| } |
| } |
| if (result) { |
| *pX = x; |
| *pY = y; |
| } |
| } |
| returnBool(result); |
| } |