xref: /openbsd/lib/libcurses/base/lib_mouse.c (revision c7ef0cfc)
1*c7ef0cfcSnicm /* $OpenBSD: lib_mouse.c,v 1.15 2023/10/17 09:52:08 nicm Exp $ */
292dd1ec0Smillert 
392dd1ec0Smillert /****************************************************************************
4*c7ef0cfcSnicm  * Copyright 2018-2022,2023 Thomas E. Dickey                                *
5*c7ef0cfcSnicm  * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
692dd1ec0Smillert  *                                                                          *
792dd1ec0Smillert  * Permission is hereby granted, free of charge, to any person obtaining a  *
892dd1ec0Smillert  * copy of this software and associated documentation files (the            *
992dd1ec0Smillert  * "Software"), to deal in the Software without restriction, including      *
1092dd1ec0Smillert  * without limitation the rights to use, copy, modify, merge, publish,      *
1192dd1ec0Smillert  * distribute, distribute with modifications, sublicense, and/or sell       *
1292dd1ec0Smillert  * copies of the Software, and to permit persons to whom the Software is    *
1392dd1ec0Smillert  * furnished to do so, subject to the following conditions:                 *
1492dd1ec0Smillert  *                                                                          *
1592dd1ec0Smillert  * The above copyright notice and this permission notice shall be included  *
1692dd1ec0Smillert  * in all copies or substantial portions of the Software.                   *
1792dd1ec0Smillert  *                                                                          *
1892dd1ec0Smillert  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1992dd1ec0Smillert  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
2092dd1ec0Smillert  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
2192dd1ec0Smillert  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
2292dd1ec0Smillert  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2392dd1ec0Smillert  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2492dd1ec0Smillert  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2592dd1ec0Smillert  *                                                                          *
2692dd1ec0Smillert  * Except as contained in this notice, the name(s) of the above copyright   *
2792dd1ec0Smillert  * holders shall not be used in advertising or otherwise to promote the     *
2892dd1ec0Smillert  * sale, use or other dealings in this Software without prior written       *
2992dd1ec0Smillert  * authorization.                                                           *
3092dd1ec0Smillert  ****************************************************************************/
3192dd1ec0Smillert 
3292dd1ec0Smillert /****************************************************************************
3392dd1ec0Smillert  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3492dd1ec0Smillert  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
3581d8c4e1Snicm  *     and: Thomas E. Dickey                        1996-on                 *
36*c7ef0cfcSnicm  *     and: Juergen Pfeifer                         2008                    *
3792dd1ec0Smillert  ****************************************************************************/
3892dd1ec0Smillert 
3992dd1ec0Smillert /*
4092dd1ec0Smillert  * This module is intended to encapsulate ncurses's interface to pointing
4192dd1ec0Smillert  * devices.
4292dd1ec0Smillert  *
4381d8c4e1Snicm  * The primary method used is xterm's internal mouse-tracking facility.
4481d8c4e1Snicm  * Additional methods depend on the platform:
4581d8c4e1Snicm  *	Alessandro Rubini's GPM server (Linux)
4681d8c4e1Snicm  *	sysmouse (FreeBSD)
4781d8c4e1Snicm  *	special-purpose mouse interface for OS/2 EMX.
4892dd1ec0Smillert  *
4992dd1ec0Smillert  * Notes for implementors of new mouse-interface methods:
5092dd1ec0Smillert  *
5192dd1ec0Smillert  * The code is logically split into a lower level that accepts event reports
5292dd1ec0Smillert  * in a device-dependent format and an upper level that parses mouse gestures
5392dd1ec0Smillert  * and filters events.  The mediating data structure is a circular queue of
5492dd1ec0Smillert  * MEVENT structures.
5592dd1ec0Smillert  *
5692dd1ec0Smillert  * Functionally, the lower level's job is to pick up primitive events and
5792dd1ec0Smillert  * put them on the circular queue.  This can happen in one of two ways:
5892dd1ec0Smillert  * either (a) _nc_mouse_event() detects a series of incoming mouse reports
5992dd1ec0Smillert  * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
6092dd1ec0Smillert  * the keyboard input stream and calls _nc_mouse_inline to queue up a series
6192dd1ec0Smillert  * of adjacent mouse reports.
6292dd1ec0Smillert  *
6392dd1ec0Smillert  * In either case, _nc_mouse_parse() should be called after the series is
6492dd1ec0Smillert  * accepted to parse the digested mouse reports (low-level MEVENTs) into
6592dd1ec0Smillert  * a gesture (a high-level or composite MEVENT).
6692dd1ec0Smillert  *
6792dd1ec0Smillert  * Don't be too shy about adding new event types or modifiers, if you can find
6892dd1ec0Smillert  * room for them in the 32-bit mask.  The API is written so that users get
6992dd1ec0Smillert  * feedback on which theoretical event types they won't see when they call
7092dd1ec0Smillert  * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
7192dd1ec0Smillert  * used yet, and a couple of bits open at the high end.
7292dd1ec0Smillert  */
7392dd1ec0Smillert 
7492dd1ec0Smillert #ifdef __EMX__
75b373e8e7Smillert #  include <io.h>
7692dd1ec0Smillert #  define  INCL_DOS
7792dd1ec0Smillert #  define  INCL_VIO
7892dd1ec0Smillert #  define  INCL_KBD
7992dd1ec0Smillert #  define  INCL_MOU
8092dd1ec0Smillert #  define  INCL_DOSPROCESS
8192dd1ec0Smillert #  include <os2.h>		/* Need to include before the others */
8292dd1ec0Smillert #endif
8392dd1ec0Smillert 
8492dd1ec0Smillert #include <curses.priv.h>
8581d8c4e1Snicm 
86*c7ef0cfcSnicm #ifndef CUR
87*c7ef0cfcSnicm #define CUR SP_TERMTYPE
88*c7ef0cfcSnicm #endif
8981d8c4e1Snicm 
90*c7ef0cfcSnicm MODULE_ID("$Id: lib_mouse.c,v 1.15 2023/10/17 09:52:08 nicm Exp $")
91*c7ef0cfcSnicm 
9281d8c4e1Snicm #include <tic.h>
9392dd1ec0Smillert 
9492dd1ec0Smillert #if USE_GPM_SUPPORT
95ed0e49f0Smillert #include <linux/keyboard.h>	/* defines KG_* macros */
9692dd1ec0Smillert 
9781d8c4e1Snicm #ifdef HAVE_LIBDL
9881d8c4e1Snicm /* use dynamic loader to avoid linkage dependency */
9981d8c4e1Snicm #include <dlfcn.h>
10081d8c4e1Snicm 
10181d8c4e1Snicm #ifdef RTLD_NOW
10281d8c4e1Snicm #define my_RTLD RTLD_NOW
10381d8c4e1Snicm #else
10481d8c4e1Snicm #ifdef RTLD_LAZY
10581d8c4e1Snicm #define my_RTLD RTLD_LAZY
10681d8c4e1Snicm #else
10781d8c4e1Snicm make an error
10881d8c4e1Snicm #endif
10981d8c4e1Snicm #endif				/* RTLD_NOW */
11081d8c4e1Snicm #endif				/* HAVE_LIBDL */
11181d8c4e1Snicm 
11281d8c4e1Snicm #endif				/* USE_GPM_SUPPORT */
11381d8c4e1Snicm 
11481d8c4e1Snicm #if USE_SYSMOUSE
11581d8c4e1Snicm #undef buttons			/* symbol conflict in consio.h */
11681d8c4e1Snicm #undef mouse_info		/* symbol conflict in consio.h */
11781d8c4e1Snicm #include <osreldate.h>
118*c7ef0cfcSnicm #if defined(__DragonFly_version) || (defined(__FreeBSD__) && (__FreeBSD_version >= 400017))
11981d8c4e1Snicm #include <sys/consio.h>
12081d8c4e1Snicm #include <sys/fbio.h>
12181d8c4e1Snicm #else
12281d8c4e1Snicm #include <machine/console.h>
12381d8c4e1Snicm #endif
12481d8c4e1Snicm #endif				/* use_SYSMOUSE */
12592dd1ec0Smillert 
126*c7ef0cfcSnicm #if USE_KLIBC_MOUSE
127*c7ef0cfcSnicm #include <sys/socket.h>
128*c7ef0cfcSnicm #define pipe(handles) socketpair(AF_LOCAL, SOCK_STREAM, 0, handles)
129*c7ef0cfcSnicm #define DosWrite(hfile, pbuffer, cbwrite, pcbactual) \
130*c7ef0cfcSnicm 		write(hfile, pbuffer, cbwrite)
131*c7ef0cfcSnicm #define DosExit(action, result )	/* do nothing */
132*c7ef0cfcSnicm #define DosCreateThread(ptid, pfn, param, flag, cbStack) \
133*c7ef0cfcSnicm 		(*(ptid) = _beginthread(pfn, NULL, cbStack, \
134*c7ef0cfcSnicm 					(void *)param), (*(ptid) == -1))
135*c7ef0cfcSnicm #endif
136*c7ef0cfcSnicm 
13792dd1ec0Smillert #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
13892dd1ec0Smillert 
139*c7ef0cfcSnicm #define	MASK_RELEASE(x)		(mmask_t) NCURSES_MOUSE_MASK(x, 001)
140*c7ef0cfcSnicm #define	MASK_PRESS(x)		(mmask_t) NCURSES_MOUSE_MASK(x, 002)
141*c7ef0cfcSnicm #define	MASK_CLICK(x)		(mmask_t) NCURSES_MOUSE_MASK(x, 004)
142*c7ef0cfcSnicm #define	MASK_DOUBLE_CLICK(x)	(mmask_t) NCURSES_MOUSE_MASK(x, 010)
143*c7ef0cfcSnicm #define	MASK_TRIPLE_CLICK(x)	(mmask_t) NCURSES_MOUSE_MASK(x, 020)
144*c7ef0cfcSnicm #define	MASK_RESERVED_EVENT(x)	(mmask_t) NCURSES_MOUSE_MASK(x, 040)
14592dd1ec0Smillert 
14681d8c4e1Snicm #if NCURSES_MOUSE_VERSION == 1
147*c7ef0cfcSnicm 
14881d8c4e1Snicm #define BUTTON_CLICKED        (BUTTON1_CLICKED        | BUTTON2_CLICKED        | BUTTON3_CLICKED        | BUTTON4_CLICKED)
14981d8c4e1Snicm #define BUTTON_PRESSED        (BUTTON1_PRESSED        | BUTTON2_PRESSED        | BUTTON3_PRESSED        | BUTTON4_PRESSED)
15081d8c4e1Snicm #define BUTTON_RELEASED       (BUTTON1_RELEASED       | BUTTON2_RELEASED       | BUTTON3_RELEASED       | BUTTON4_RELEASED)
15181d8c4e1Snicm #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED)
15281d8c4e1Snicm #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED)
153*c7ef0cfcSnicm 
15481d8c4e1Snicm #define MAX_BUTTONS  4
155*c7ef0cfcSnicm 
15681d8c4e1Snicm #else
157*c7ef0cfcSnicm 
15881d8c4e1Snicm #define BUTTON_CLICKED        (BUTTON1_CLICKED        | BUTTON2_CLICKED        | BUTTON3_CLICKED        | BUTTON4_CLICKED        | BUTTON5_CLICKED)
15981d8c4e1Snicm #define BUTTON_PRESSED        (BUTTON1_PRESSED        | BUTTON2_PRESSED        | BUTTON3_PRESSED        | BUTTON4_PRESSED        | BUTTON5_PRESSED)
16081d8c4e1Snicm #define BUTTON_RELEASED       (BUTTON1_RELEASED       | BUTTON2_RELEASED       | BUTTON3_RELEASED       | BUTTON4_RELEASED       | BUTTON5_RELEASED)
16181d8c4e1Snicm #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED)
16281d8c4e1Snicm #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED)
163*c7ef0cfcSnicm 
164*c7ef0cfcSnicm #if NCURSES_MOUSE_VERSION == 2
16581d8c4e1Snicm #define MAX_BUTTONS  5
166*c7ef0cfcSnicm #else
167*c7ef0cfcSnicm #define MAX_BUTTONS  11
168*c7ef0cfcSnicm #endif
169*c7ef0cfcSnicm 
17081d8c4e1Snicm #endif
17181d8c4e1Snicm 
17281d8c4e1Snicm #define INVALID_EVENT	-1
17381d8c4e1Snicm #define NORMAL_EVENT	0
17492dd1ec0Smillert 
175*c7ef0cfcSnicm #define ValidEvent(ep) ((ep)->id != INVALID_EVENT)
176*c7ef0cfcSnicm #define Invalidate(ep) (ep)->id = INVALID_EVENT
177*c7ef0cfcSnicm 
17892dd1ec0Smillert #if USE_GPM_SUPPORT
17981d8c4e1Snicm 
18081d8c4e1Snicm #ifndef LIBGPM_SONAME
18181d8c4e1Snicm #define LIBGPM_SONAME "libgpm.so"
18292dd1ec0Smillert #endif
18392dd1ec0Smillert 
184*c7ef0cfcSnicm #define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(sp->_dlopen_gpm, #name))
18592dd1ec0Smillert 
18681d8c4e1Snicm #endif				/* USE_GPM_SUPPORT */
18781d8c4e1Snicm 
18881d8c4e1Snicm static bool _nc_mouse_parse(SCREEN *, int);
18992dd1ec0Smillert static void _nc_mouse_resume(SCREEN *);
19092dd1ec0Smillert static void _nc_mouse_wrap(SCREEN *);
19192dd1ec0Smillert 
19292dd1ec0Smillert /* maintain a circular list of mouse events */
19392dd1ec0Smillert 
19481d8c4e1Snicm #define FirstEV(sp)	((sp)->_mouse_events)
19581d8c4e1Snicm #define LastEV(sp)	((sp)->_mouse_events + EV_MAX - 1)
19681d8c4e1Snicm 
19781d8c4e1Snicm #undef  NEXT
198*c7ef0cfcSnicm #define NEXT(ep)	((ep >= LastEV(SP_PARM)) \
199*c7ef0cfcSnicm 			 ? FirstEV(SP_PARM) \
20081d8c4e1Snicm 			 : ep + 1)
20181d8c4e1Snicm 
20281d8c4e1Snicm #undef  PREV
203*c7ef0cfcSnicm #define PREV(ep)	((ep <= FirstEV(SP_PARM)) \
204*c7ef0cfcSnicm 			 ? LastEV(SP_PARM) \
20581d8c4e1Snicm 			 : ep - 1)
20681d8c4e1Snicm 
20781d8c4e1Snicm #define IndexEV(sp, ep)	(ep - FirstEV(sp))
20881d8c4e1Snicm 
20981d8c4e1Snicm #define RunParams(sp, eventp, runp) \
21081d8c4e1Snicm 		(long) IndexEV(sp, runp), \
21181d8c4e1Snicm 		(long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX
21292dd1ec0Smillert 
21392dd1ec0Smillert #ifdef TRACE
21423bb66c4Smillert static void
_trace_slot(SCREEN * sp,const char * tag)21581d8c4e1Snicm _trace_slot(SCREEN *sp, const char *tag)
21692dd1ec0Smillert {
21792dd1ec0Smillert     MEVENT *ep;
21892dd1ec0Smillert 
219*c7ef0cfcSnicm     _tracef("%s", tag);
22092dd1ec0Smillert 
22181d8c4e1Snicm     for (ep = FirstEV(sp); ep <= LastEV(sp); ep++)
22292dd1ec0Smillert 	_tracef("mouse event queue slot %ld = %s",
22381d8c4e1Snicm 		(long) IndexEV(sp, ep),
22481d8c4e1Snicm 		_nc_tracemouse(sp, ep));
22592dd1ec0Smillert }
22692dd1ec0Smillert #endif
22792dd1ec0Smillert 
22881d8c4e1Snicm #if USE_EMX_MOUSE
22992dd1ec0Smillert 
23092dd1ec0Smillert #  define TOP_ROW          0
23192dd1ec0Smillert #  define LEFT_COL         0
23292dd1ec0Smillert 
23392dd1ec0Smillert #  define M_FD(sp) sp->_mouse_fd
23492dd1ec0Smillert 
23592dd1ec0Smillert static void
write_event(SCREEN * sp,int down,int button,int x,int y)23681d8c4e1Snicm write_event(SCREEN *sp, int down, int button, int x, int y)
23792dd1ec0Smillert {
23892dd1ec0Smillert     char buf[6];
23992dd1ec0Smillert     unsigned long ignore;
24092dd1ec0Smillert 
241*c7ef0cfcSnicm     _nc_STRCPY(buf, "\033[M", sizeof(buf));	/* should be the same as key_mouse */
24292dd1ec0Smillert     buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40);
24392dd1ec0Smillert     buf[4] = ' ' + x - LEFT_COL + 1;
24492dd1ec0Smillert     buf[5] = ' ' + y - TOP_ROW + 1;
24581d8c4e1Snicm     DosWrite(sp->_emxmouse_wfd, buf, 6, &ignore);
24692dd1ec0Smillert }
24792dd1ec0Smillert 
24892dd1ec0Smillert static void
249*c7ef0cfcSnicm #if USE_KLIBC_MOUSE
mouse_server(void * param)250*c7ef0cfcSnicm mouse_server(void *param)
251*c7ef0cfcSnicm #else
25281d8c4e1Snicm mouse_server(unsigned long param)
253*c7ef0cfcSnicm #endif
25492dd1ec0Smillert {
25581d8c4e1Snicm     SCREEN *sp = (SCREEN *) param;
25692dd1ec0Smillert     unsigned short fWait = MOU_WAIT;
25792dd1ec0Smillert     /* NOPTRRECT mourt = { 0,0,24,79 }; */
25892dd1ec0Smillert     MOUEVENTINFO mouev;
25992dd1ec0Smillert     HMOU hmou;
26092dd1ec0Smillert     unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN;
26177c4fa26Smillert     int nbuttons = 3;
26292dd1ec0Smillert     int oldstate = 0;
26377c4fa26Smillert     char err[80];
26477c4fa26Smillert     unsigned long rc;
26592dd1ec0Smillert 
26692dd1ec0Smillert     /* open the handle for the mouse */
26792dd1ec0Smillert     if (MouOpen(NULL, &hmou) == 0) {
26877c4fa26Smillert 	rc = MouSetEventMask(&mask, hmou);
26977c4fa26Smillert 	if (rc) {		/* retry with 2 buttons */
27077c4fa26Smillert 	    mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
27177c4fa26Smillert 	    rc = MouSetEventMask(&mask, hmou);
27277c4fa26Smillert 	    nbuttons = 2;
27377c4fa26Smillert 	}
27477c4fa26Smillert 	if (rc == 0 && MouDrawPtr(hmou) == 0) {
27592dd1ec0Smillert 	    for (;;) {
27692dd1ec0Smillert 		/* sit and wait on the event queue */
27777c4fa26Smillert 		rc = MouReadEventQue(&mouev, &fWait, hmou);
27877c4fa26Smillert 		if (rc) {
279*c7ef0cfcSnicm 		    _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err))
280*c7ef0cfcSnicm 				"Error reading mouse queue, rc=%lu.\r\n", rc);
28192dd1ec0Smillert 		    break;
28277c4fa26Smillert 		}
28381d8c4e1Snicm 		if (!sp->_emxmouse_activated)
28492dd1ec0Smillert 		    goto finish;
28592dd1ec0Smillert 
28692dd1ec0Smillert 		/*
28792dd1ec0Smillert 		 * OS/2 numbers a 3-button mouse inconsistently from other
28892dd1ec0Smillert 		 * platforms:
28992dd1ec0Smillert 		 *      1 = left
29092dd1ec0Smillert 		 *      2 = right
29192dd1ec0Smillert 		 *      3 = middle.
29292dd1ec0Smillert 		 */
29392dd1ec0Smillert 		if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN)
29481d8c4e1Snicm 		    write_event(sp, mouev.fs & MOUSE_BN1_DOWN,
29581d8c4e1Snicm 				sp->_emxmouse_buttons[1], mouev.col, mouev.row);
29692dd1ec0Smillert 		if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN)
29781d8c4e1Snicm 		    write_event(sp, mouev.fs & MOUSE_BN2_DOWN,
29881d8c4e1Snicm 				sp->_emxmouse_buttons[3], mouev.col, mouev.row);
29992dd1ec0Smillert 		if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN)
30081d8c4e1Snicm 		    write_event(sp, mouev.fs & MOUSE_BN3_DOWN,
30181d8c4e1Snicm 				sp->_emxmouse_buttons[2], mouev.col, mouev.row);
30292dd1ec0Smillert 
30392dd1ec0Smillert 	      finish:
30492dd1ec0Smillert 		oldstate = mouev.fs;
30592dd1ec0Smillert 	    }
306*c7ef0cfcSnicm 	} else {
307*c7ef0cfcSnicm 	    _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err))
308*c7ef0cfcSnicm 			"Error setting event mask, buttons=%d, rc=%lu.\r\n",
30977c4fa26Smillert 			nbuttons, rc);
310*c7ef0cfcSnicm 	}
31192dd1ec0Smillert 
31277c4fa26Smillert 	DosWrite(2, err, strlen(err), &rc);
31392dd1ec0Smillert 	MouClose(hmou);
31492dd1ec0Smillert     }
31592dd1ec0Smillert     DosExit(EXIT_THREAD, 0L);
31692dd1ec0Smillert }
31777c4fa26Smillert 
31881d8c4e1Snicm #endif /* USE_EMX_MOUSE */
31981d8c4e1Snicm 
32081d8c4e1Snicm #if USE_SYSMOUSE
32192dd1ec0Smillert static void
sysmouse_server(SCREEN * sp)32281d8c4e1Snicm sysmouse_server(SCREEN *sp)
32381d8c4e1Snicm {
32481d8c4e1Snicm     struct mouse_info the_mouse;
32581d8c4e1Snicm     MEVENT *work;
32681d8c4e1Snicm 
32781d8c4e1Snicm     the_mouse.operation = MOUSE_GETINFO;
32881d8c4e1Snicm     if (sp != 0
32981d8c4e1Snicm 	&& sp->_mouse_fd >= 0
33081d8c4e1Snicm 	&& sp->_sysmouse_tail < FIFO_SIZE
33181d8c4e1Snicm 	&& ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
33281d8c4e1Snicm 
33381d8c4e1Snicm 	if (sp->_sysmouse_head > sp->_sysmouse_tail) {
33481d8c4e1Snicm 	    sp->_sysmouse_tail = 0;
33581d8c4e1Snicm 	    sp->_sysmouse_head = 0;
33681d8c4e1Snicm 	}
33781d8c4e1Snicm 	work = &(sp->_sysmouse_fifo[sp->_sysmouse_tail]);
33881d8c4e1Snicm 	memset(work, 0, sizeof(*work));
33981d8c4e1Snicm 	work->id = NORMAL_EVENT;	/* there's only one mouse... */
34081d8c4e1Snicm 
34181d8c4e1Snicm 	sp->_sysmouse_old_buttons = sp->_sysmouse_new_buttons;
34281d8c4e1Snicm 	sp->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7;
34381d8c4e1Snicm 
34481d8c4e1Snicm 	if (sp->_sysmouse_new_buttons) {
34581d8c4e1Snicm 	    if (sp->_sysmouse_new_buttons & 1)
34681d8c4e1Snicm 		work->bstate |= BUTTON1_PRESSED;
34781d8c4e1Snicm 	    if (sp->_sysmouse_new_buttons & 2)
34881d8c4e1Snicm 		work->bstate |= BUTTON2_PRESSED;
34981d8c4e1Snicm 	    if (sp->_sysmouse_new_buttons & 4)
35081d8c4e1Snicm 		work->bstate |= BUTTON3_PRESSED;
35181d8c4e1Snicm 	} else {
35281d8c4e1Snicm 	    if (sp->_sysmouse_old_buttons & 1)
35381d8c4e1Snicm 		work->bstate |= BUTTON1_RELEASED;
35481d8c4e1Snicm 	    if (sp->_sysmouse_old_buttons & 2)
35581d8c4e1Snicm 		work->bstate |= BUTTON2_RELEASED;
35681d8c4e1Snicm 	    if (sp->_sysmouse_old_buttons & 4)
35781d8c4e1Snicm 		work->bstate |= BUTTON3_RELEASED;
35892dd1ec0Smillert 	}
35992dd1ec0Smillert 
36081d8c4e1Snicm 	/* for cosmetic bug in syscons.c on FreeBSD 3.[34] */
36181d8c4e1Snicm 	the_mouse.operation = MOUSE_HIDE;
36281d8c4e1Snicm 	ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
36381d8c4e1Snicm 	the_mouse.operation = MOUSE_SHOW;
36481d8c4e1Snicm 	ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
36592dd1ec0Smillert 
36681d8c4e1Snicm 	/*
36781d8c4e1Snicm 	 * We're only interested if the button is pressed or released.
36881d8c4e1Snicm 	 * FIXME: implement continuous event-tracking.
36981d8c4e1Snicm 	 */
37081d8c4e1Snicm 	if (sp->_sysmouse_new_buttons != sp->_sysmouse_old_buttons) {
37181d8c4e1Snicm 	    sp->_sysmouse_tail += 1;
37281d8c4e1Snicm 	}
37381d8c4e1Snicm 	work->x = the_mouse.u.data.x / sp->_sysmouse_char_width;
37481d8c4e1Snicm 	work->y = the_mouse.u.data.y / sp->_sysmouse_char_height;
37581d8c4e1Snicm     }
37681d8c4e1Snicm }
37792dd1ec0Smillert 
37823bb66c4Smillert static void
handle_sysmouse(int sig GCC_UNUSED)37981d8c4e1Snicm handle_sysmouse(int sig GCC_UNUSED)
38092dd1ec0Smillert {
381*c7ef0cfcSnicm     sysmouse_server(CURRENT_SCREEN);
38281d8c4e1Snicm }
38381d8c4e1Snicm #endif /* USE_SYSMOUSE */
38481d8c4e1Snicm 
385*c7ef0cfcSnicm #if !defined(USE_TERM_DRIVER) || defined(EXP_WIN32_DRIVER)
386*c7ef0cfcSnicm #define xterm_kmous "\033[M"
387*c7ef0cfcSnicm 
38881d8c4e1Snicm static void
init_xterm_mouse(SCREEN * sp)38981d8c4e1Snicm init_xterm_mouse(SCREEN *sp)
39081d8c4e1Snicm {
39181d8c4e1Snicm     sp->_mouse_type = M_XTERM;
392*c7ef0cfcSnicm     sp->_mouse_format = MF_X10;
39381d8c4e1Snicm     sp->_mouse_xtermcap = tigetstr("XM");
394*c7ef0cfcSnicm     if (VALID_STRING(sp->_mouse_xtermcap)) {
395*c7ef0cfcSnicm 	char *code = strstr(sp->_mouse_xtermcap, "[?");
396*c7ef0cfcSnicm 	if (code != 0) {
397*c7ef0cfcSnicm 	    code += 2;
398*c7ef0cfcSnicm 	    while ((*code >= '0') && (*code <= '9')) {
399*c7ef0cfcSnicm 		char *next = code;
400*c7ef0cfcSnicm 		while ((*next >= '0') && (*next <= '9')) {
401*c7ef0cfcSnicm 		    ++next;
40281d8c4e1Snicm 		}
403*c7ef0cfcSnicm 		if (!strncmp(code, "1006", (size_t) (next - code))) {
404*c7ef0cfcSnicm 		    sp->_mouse_format = MF_SGR1006;
405*c7ef0cfcSnicm 		}
406*c7ef0cfcSnicm #ifdef EXP_XTERM_1005
407*c7ef0cfcSnicm 		if (!strncmp(code, "1005", (size_t) (next - code))) {
408*c7ef0cfcSnicm 		    sp->_mouse_format = MF_XTERM_1005;
409*c7ef0cfcSnicm 		}
410*c7ef0cfcSnicm #endif
411*c7ef0cfcSnicm 		if (*next == ';') {
412*c7ef0cfcSnicm 		    while (*next == ';') {
413*c7ef0cfcSnicm 			++next;
414*c7ef0cfcSnicm 		    }
415*c7ef0cfcSnicm 		    code = next;
416*c7ef0cfcSnicm 		} else {
417*c7ef0cfcSnicm 		    break;
418*c7ef0cfcSnicm 		}
419*c7ef0cfcSnicm 	    }
420*c7ef0cfcSnicm 	}
421*c7ef0cfcSnicm     } else {
422*c7ef0cfcSnicm 	int code = tigetnum("XM");
423*c7ef0cfcSnicm 	switch (code) {
424*c7ef0cfcSnicm #ifdef EXP_XTERM_1005
425*c7ef0cfcSnicm 	case 1005:
426*c7ef0cfcSnicm 	    /* see "xterm+sm+1005" */
427*c7ef0cfcSnicm 	    sp->_mouse_xtermcap = "\033[?1005;1000%?%p1%{1}%=%th%el%;";
428*c7ef0cfcSnicm 	    sp->_mouse_format = MF_XTERM_1005;
429*c7ef0cfcSnicm 	    break;
430*c7ef0cfcSnicm #endif
431*c7ef0cfcSnicm 	case 1006:
432*c7ef0cfcSnicm 	    /* see "xterm+sm+1006" */
433*c7ef0cfcSnicm 	    sp->_mouse_xtermcap = "\033[?1006;1000%?%p1%{1}%=%th%el%;";
434*c7ef0cfcSnicm 	    sp->_mouse_format = MF_SGR1006;
435*c7ef0cfcSnicm 	    break;
436*c7ef0cfcSnicm 	default:
437*c7ef0cfcSnicm 	    sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
438*c7ef0cfcSnicm 	    break;
439*c7ef0cfcSnicm 	}
440*c7ef0cfcSnicm     }
441*c7ef0cfcSnicm }
442*c7ef0cfcSnicm #endif
44381d8c4e1Snicm 
44481d8c4e1Snicm static void
enable_xterm_mouse(SCREEN * sp,int enable)44581d8c4e1Snicm enable_xterm_mouse(SCREEN *sp, int enable)
44681d8c4e1Snicm {
447*c7ef0cfcSnicm     TPUTS_TRACE(enable
448*c7ef0cfcSnicm 		? "xterm mouse initialization"
449*c7ef0cfcSnicm 		: "xterm mouse deinitialization");
45081d8c4e1Snicm #if USE_EMX_MOUSE
45181d8c4e1Snicm     sp->_emxmouse_activated = enable;
45281d8c4e1Snicm #else
453*c7ef0cfcSnicm     NCURSES_PUTP2("xterm-mouse", TIPARM_1(sp->_mouse_xtermcap, enable));
45481d8c4e1Snicm #endif
45581d8c4e1Snicm     sp->_mouse_active = enable;
45681d8c4e1Snicm }
45781d8c4e1Snicm 
458*c7ef0cfcSnicm #if defined(USE_TERM_DRIVER)
459*c7ef0cfcSnicm static void
enable_win32_mouse(SCREEN * sp,int enable)460*c7ef0cfcSnicm enable_win32_mouse(SCREEN *sp, int enable)
461*c7ef0cfcSnicm {
462*c7ef0cfcSnicm #if defined(EXP_WIN32_DRIVER)
463*c7ef0cfcSnicm     enable_xterm_mouse(sp, enable);
464*c7ef0cfcSnicm #else
465*c7ef0cfcSnicm     sp->_mouse_active = enable;
466*c7ef0cfcSnicm #endif
467*c7ef0cfcSnicm }
468*c7ef0cfcSnicm #endif
469*c7ef0cfcSnicm 
47081d8c4e1Snicm #if USE_GPM_SUPPORT
47181d8c4e1Snicm static bool
allow_gpm_mouse(SCREEN * sp GCC_UNUSED)472*c7ef0cfcSnicm allow_gpm_mouse(SCREEN *sp GCC_UNUSED)
47381d8c4e1Snicm {
47481d8c4e1Snicm     bool result = FALSE;
47581d8c4e1Snicm 
476*c7ef0cfcSnicm #if USE_WEAK_SYMBOLS
477*c7ef0cfcSnicm     /* Danger Robinson: do not use dlopen for libgpm if already loaded */
478*c7ef0cfcSnicm     if ((Gpm_Wgetch) != 0) {
479*c7ef0cfcSnicm 	if (!sp->_mouse_gpm_loaded) {
480*c7ef0cfcSnicm 	    T(("GPM library was already dlopen'd, not by us"));
481*c7ef0cfcSnicm 	}
482*c7ef0cfcSnicm     } else
483*c7ef0cfcSnicm #endif
48481d8c4e1Snicm 	/* GPM does printf's without checking if stdout is a terminal */
485*c7ef0cfcSnicm     if (NC_ISATTY(fileno(stdout))) {
486*c7ef0cfcSnicm 	const char *list = getenv("NCURSES_GPM_TERMS");
487*c7ef0cfcSnicm 	const char *env = getenv("TERM");
48881d8c4e1Snicm 	if (list != 0) {
48981d8c4e1Snicm 	    if (env != 0) {
49081d8c4e1Snicm 		result = _nc_name_match(list, env, "|:");
49181d8c4e1Snicm 	    }
49281d8c4e1Snicm 	} else {
49381d8c4e1Snicm 	    /* GPM checks the beginning of the $TERM variable to decide if it
49481d8c4e1Snicm 	     * should pass xterm events through.  There is no real advantage in
49581d8c4e1Snicm 	     * allowing GPM to do this.  Recent versions relax that check, and
49681d8c4e1Snicm 	     * pretend that GPM can work with any terminal having the kmous
49781d8c4e1Snicm 	     * capability.  Perhaps that works for someone.  If so, they can
49881d8c4e1Snicm 	     * set the environment variable (above).
49981d8c4e1Snicm 	     */
50081d8c4e1Snicm 	    if (env != 0 && strstr(env, "linux") != 0) {
50181d8c4e1Snicm 		result = TRUE;
50281d8c4e1Snicm 	    }
50381d8c4e1Snicm 	}
50481d8c4e1Snicm     }
50581d8c4e1Snicm     return result;
50681d8c4e1Snicm }
50781d8c4e1Snicm 
50881d8c4e1Snicm #ifdef HAVE_LIBDL
50981d8c4e1Snicm static void
unload_gpm_library(SCREEN * sp)51081d8c4e1Snicm unload_gpm_library(SCREEN *sp)
51181d8c4e1Snicm {
512*c7ef0cfcSnicm     if (sp->_dlopen_gpm != 0) {
51381d8c4e1Snicm 	T(("unload GPM library"));
51481d8c4e1Snicm 	sp->_mouse_gpm_loaded = FALSE;
51581d8c4e1Snicm 	sp->_mouse_fd = -1;
51681d8c4e1Snicm     }
51781d8c4e1Snicm }
51881d8c4e1Snicm 
51981d8c4e1Snicm static void
load_gpm_library(SCREEN * sp)52081d8c4e1Snicm load_gpm_library(SCREEN *sp)
52181d8c4e1Snicm {
52281d8c4e1Snicm     sp->_mouse_gpm_found = FALSE;
523*c7ef0cfcSnicm 
524*c7ef0cfcSnicm     /*
525*c7ef0cfcSnicm      * If we already had a successful dlopen, reuse it.
526*c7ef0cfcSnicm      */
527*c7ef0cfcSnicm     if (sp->_dlopen_gpm != 0) {
528*c7ef0cfcSnicm 	sp->_mouse_gpm_found = TRUE;
529*c7ef0cfcSnicm 	sp->_mouse_gpm_loaded = TRUE;
530*c7ef0cfcSnicm     } else if ((sp->_dlopen_gpm = dlopen(LIBGPM_SONAME, my_RTLD)) != 0) {
531*c7ef0cfcSnicm #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__)
532*c7ef0cfcSnicm #pragma GCC diagnostic push
533*c7ef0cfcSnicm #pragma GCC diagnostic ignored "-Wpedantic"
534*c7ef0cfcSnicm #endif
53581d8c4e1Snicm 	if (GET_DLSYM(gpm_fd) == 0 ||
53681d8c4e1Snicm 	    GET_DLSYM(Gpm_Open) == 0 ||
53781d8c4e1Snicm 	    GET_DLSYM(Gpm_Close) == 0 ||
53881d8c4e1Snicm 	    GET_DLSYM(Gpm_GetEvent) == 0) {
539*c7ef0cfcSnicm #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__)
540*c7ef0cfcSnicm #pragma GCC diagnostic pop
541*c7ef0cfcSnicm #endif
54281d8c4e1Snicm 	    T(("GPM initialization failed: %s", dlerror()));
54381d8c4e1Snicm 	    unload_gpm_library(sp);
544*c7ef0cfcSnicm 	    dlclose(sp->_dlopen_gpm);
545*c7ef0cfcSnicm 	    sp->_dlopen_gpm = 0;
54681d8c4e1Snicm 	} else {
54781d8c4e1Snicm 	    sp->_mouse_gpm_found = TRUE;
54881d8c4e1Snicm 	    sp->_mouse_gpm_loaded = TRUE;
54981d8c4e1Snicm 	}
55081d8c4e1Snicm     }
55181d8c4e1Snicm }
552*c7ef0cfcSnicm #endif /* HAVE_LIBDL */
55381d8c4e1Snicm 
55481d8c4e1Snicm static bool
enable_gpm_mouse(SCREEN * sp,bool enable)55581d8c4e1Snicm enable_gpm_mouse(SCREEN *sp, bool enable)
55681d8c4e1Snicm {
55781d8c4e1Snicm     bool result;
55881d8c4e1Snicm 
55981d8c4e1Snicm     T((T_CALLED("enable_gpm_mouse(%d)"), enable));
56081d8c4e1Snicm 
56181d8c4e1Snicm     if (enable && !sp->_mouse_active) {
56281d8c4e1Snicm #ifdef HAVE_LIBDL
56381d8c4e1Snicm 	if (sp->_mouse_gpm_found && !sp->_mouse_gpm_loaded) {
56481d8c4e1Snicm 	    load_gpm_library(sp);
56581d8c4e1Snicm 	}
56681d8c4e1Snicm #endif
56781d8c4e1Snicm 	if (sp->_mouse_gpm_loaded) {
568*c7ef0cfcSnicm 	    int code;
569*c7ef0cfcSnicm 
57081d8c4e1Snicm 	    /* GPM: initialize connection to gpm server */
57181d8c4e1Snicm 	    sp->_mouse_gpm_connect.eventMask = GPM_DOWN | GPM_UP;
57281d8c4e1Snicm 	    sp->_mouse_gpm_connect.defaultMask =
57381d8c4e1Snicm 		(unsigned short) (~(sp->_mouse_gpm_connect.eventMask | GPM_HARD));
57481d8c4e1Snicm 	    sp->_mouse_gpm_connect.minMod = 0;
57581d8c4e1Snicm 	    sp->_mouse_gpm_connect.maxMod =
57681d8c4e1Snicm 		(unsigned short) (~((1 << KG_SHIFT) |
57781d8c4e1Snicm 				    (1 << KG_SHIFTL) |
57881d8c4e1Snicm 				    (1 << KG_SHIFTR)));
57981d8c4e1Snicm 	    /*
58081d8c4e1Snicm 	     * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open.
58181d8c4e1Snicm 	     * The former is recognized by wscons (SunOS), and the latter by
58281d8c4e1Snicm 	     * xterm.  Those will not show up in ncurses' traces.
58381d8c4e1Snicm 	     */
584*c7ef0cfcSnicm 	    code = my_Gpm_Open(&sp->_mouse_gpm_connect, 0);
585*c7ef0cfcSnicm 	    result = (code >= 0);
586*c7ef0cfcSnicm 
587*c7ef0cfcSnicm 	    /*
588*c7ef0cfcSnicm 	     * GPM can return a -2 if it is trying to do something with xterm.
589*c7ef0cfcSnicm 	     * Ignore that, since it conflicts with our use of stdin.
590*c7ef0cfcSnicm 	     */
591*c7ef0cfcSnicm 	    if (code == -2) {
592*c7ef0cfcSnicm 		my_Gpm_Close();
593*c7ef0cfcSnicm 	    }
59481d8c4e1Snicm 	} else {
59581d8c4e1Snicm 	    result = FALSE;
59681d8c4e1Snicm 	}
59781d8c4e1Snicm 	sp->_mouse_active = result;
59881d8c4e1Snicm 	T(("GPM open %s", result ? "succeeded" : "failed"));
59981d8c4e1Snicm     } else {
60081d8c4e1Snicm 	if (!enable && sp->_mouse_active) {
60181d8c4e1Snicm 	    /* GPM: close connection to gpm server */
60281d8c4e1Snicm 	    my_Gpm_Close();
60381d8c4e1Snicm 	    sp->_mouse_active = FALSE;
60481d8c4e1Snicm 	    T(("GPM closed"));
60581d8c4e1Snicm 	}
60681d8c4e1Snicm 	result = enable;
60781d8c4e1Snicm     }
60881d8c4e1Snicm #ifdef HAVE_LIBDL
60981d8c4e1Snicm     if (!result) {
61081d8c4e1Snicm 	unload_gpm_library(sp);
61181d8c4e1Snicm     }
61281d8c4e1Snicm #endif
61381d8c4e1Snicm     returnBool(result);
61481d8c4e1Snicm }
61581d8c4e1Snicm #endif /* USE_GPM_SUPPORT */
61681d8c4e1Snicm 
61781d8c4e1Snicm static void
initialize_mousetype(SCREEN * sp)61881d8c4e1Snicm initialize_mousetype(SCREEN *sp)
61981d8c4e1Snicm {
62081d8c4e1Snicm     T((T_CALLED("initialize_mousetype()")));
62192dd1ec0Smillert 
62223bb66c4Smillert     /* Try gpm first, because gpm may be configured to run in xterm */
62392dd1ec0Smillert #if USE_GPM_SUPPORT
624*c7ef0cfcSnicm     if (allow_gpm_mouse(sp)) {
62581d8c4e1Snicm 	if (!sp->_mouse_gpm_loaded) {
62681d8c4e1Snicm #ifdef HAVE_LIBDL
62781d8c4e1Snicm 	    load_gpm_library(sp);
62881d8c4e1Snicm #else /* !HAVE_LIBDL */
62981d8c4e1Snicm 	    sp->_mouse_gpm_found = TRUE;
63081d8c4e1Snicm 	    sp->_mouse_gpm_loaded = TRUE;
63192dd1ec0Smillert #endif
63281d8c4e1Snicm 	}
63381d8c4e1Snicm 
63481d8c4e1Snicm 	/*
63581d8c4e1Snicm 	 * The gpm_fd file-descriptor may be negative (xterm).  So we have to
63681d8c4e1Snicm 	 * maintain our notion of whether the mouse connection is active
63781d8c4e1Snicm 	 * without testing the file-descriptor.
63881d8c4e1Snicm 	 */
63981d8c4e1Snicm 	if (sp->_mouse_gpm_found && enable_gpm_mouse(sp, TRUE)) {
64081d8c4e1Snicm 	    sp->_mouse_type = M_GPM;
64181d8c4e1Snicm 	    sp->_mouse_fd = *(my_gpm_fd);
64281d8c4e1Snicm 	    T(("GPM mouse_fd %d", sp->_mouse_fd));
64381d8c4e1Snicm 	    returnVoid;
64481d8c4e1Snicm 	}
64581d8c4e1Snicm     }
64681d8c4e1Snicm #endif /* USE_GPM_SUPPORT */
64792dd1ec0Smillert 
64892dd1ec0Smillert     /* OS/2 VIO */
64981d8c4e1Snicm #if USE_EMX_MOUSE
65081d8c4e1Snicm     if (!sp->_emxmouse_thread
651*c7ef0cfcSnicm 	&& strstr(SP_TERMTYPE term_names, "xterm") == 0
652*c7ef0cfcSnicm 	&& NonEmpty(key_mouse)) {
65392dd1ec0Smillert 	int handles[2];
65477c4fa26Smillert 
65592dd1ec0Smillert 	if (pipe(handles) < 0) {
65692dd1ec0Smillert 	    perror("mouse pipe error");
65781d8c4e1Snicm 	    returnVoid;
65892dd1ec0Smillert 	} else {
65992dd1ec0Smillert 	    int rc;
66092dd1ec0Smillert 
66181d8c4e1Snicm 	    if (!sp->_emxmouse_buttons[0]) {
662*c7ef0cfcSnicm 		const char *s = getenv("MOUSE_BUTTONS_123");
66392dd1ec0Smillert 
66481d8c4e1Snicm 		sp->_emxmouse_buttons[0] = 1;
66592dd1ec0Smillert 		if (s && strlen(s) >= 3) {
66681d8c4e1Snicm 		    sp->_emxmouse_buttons[1] = s[0] - '0';
66781d8c4e1Snicm 		    sp->_emxmouse_buttons[2] = s[1] - '0';
66881d8c4e1Snicm 		    sp->_emxmouse_buttons[3] = s[2] - '0';
66981d8c4e1Snicm 		} else {
67081d8c4e1Snicm 		    sp->_emxmouse_buttons[1] = 1;
67181d8c4e1Snicm 		    sp->_emxmouse_buttons[2] = 3;
67281d8c4e1Snicm 		    sp->_emxmouse_buttons[3] = 2;
67392dd1ec0Smillert 		}
67492dd1ec0Smillert 	    }
67581d8c4e1Snicm 	    sp->_emxmouse_wfd = handles[1];
67681d8c4e1Snicm 	    M_FD(sp) = handles[0];
67792dd1ec0Smillert 	    /* Needed? */
67892dd1ec0Smillert 	    setmode(handles[0], O_BINARY);
67992dd1ec0Smillert 	    setmode(handles[1], O_BINARY);
68092dd1ec0Smillert 	    /* Do not use CRT functions, we may single-threaded. */
68181d8c4e1Snicm 	    rc = DosCreateThread((unsigned long *) &sp->_emxmouse_thread,
68281d8c4e1Snicm 				 mouse_server, (long) sp, 0, 8192);
68323bb66c4Smillert 	    if (rc) {
68492dd1ec0Smillert 		printf("mouse thread error %d=%#x", rc, rc);
68523bb66c4Smillert 	    } else {
68681d8c4e1Snicm 		sp->_mouse_type = M_XTERM;
68781d8c4e1Snicm 	    }
68881d8c4e1Snicm 	    returnVoid;
68981d8c4e1Snicm 	}
69081d8c4e1Snicm     }
69181d8c4e1Snicm #endif /* USE_EMX_MOUSE */
69281d8c4e1Snicm 
69381d8c4e1Snicm #if USE_SYSMOUSE
69481d8c4e1Snicm     {
695*c7ef0cfcSnicm 	static char dev_tty[] = "/dev/tty";
69681d8c4e1Snicm 	struct mouse_info the_mouse;
69781d8c4e1Snicm 	char *the_device = 0;
69881d8c4e1Snicm 
699*c7ef0cfcSnicm 	if (NC_ISATTY(sp->_ifd))
70081d8c4e1Snicm 	    the_device = ttyname(sp->_ifd);
70181d8c4e1Snicm 	if (the_device == 0)
702*c7ef0cfcSnicm 	    the_device = dev_tty;
70381d8c4e1Snicm 
70481d8c4e1Snicm 	sp->_mouse_fd = open(the_device, O_RDWR);
70581d8c4e1Snicm 
70681d8c4e1Snicm 	if (sp->_mouse_fd >= 0) {
70781d8c4e1Snicm 	    /*
70881d8c4e1Snicm 	     * sysmouse does not have a usable user interface for obtaining
70981d8c4e1Snicm 	     * mouse events.  The logical way to proceed (reading data on a
71081d8c4e1Snicm 	     * stream) only works if one opens the device as root.  Even in
71181d8c4e1Snicm 	     * that mode, careful examination shows we lose events
71281d8c4e1Snicm 	     * occasionally.  The interface provided for user programs is to
71381d8c4e1Snicm 	     * establish a signal handler.  really.
71481d8c4e1Snicm 	     *
71581d8c4e1Snicm 	     * Take over SIGUSR2 for this purpose since SIGUSR1 is more
71681d8c4e1Snicm 	     * likely to be used by an application.  getch() will have to
71781d8c4e1Snicm 	     * handle the misleading EINTR's.
71881d8c4e1Snicm 	     */
71981d8c4e1Snicm 	    signal(SIGUSR2, SIG_IGN);
72081d8c4e1Snicm 	    the_mouse.operation = MOUSE_MODE;
72181d8c4e1Snicm 	    the_mouse.u.mode.mode = 0;
72281d8c4e1Snicm 	    the_mouse.u.mode.signal = SIGUSR2;
72381d8c4e1Snicm 	    if (ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
72481d8c4e1Snicm 		signal(SIGUSR2, handle_sysmouse);
72581d8c4e1Snicm 		the_mouse.operation = MOUSE_SHOW;
72681d8c4e1Snicm 		ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
72781d8c4e1Snicm 
72881d8c4e1Snicm #if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO)	/* FreeBSD > 2.x */
72981d8c4e1Snicm 		{
73081d8c4e1Snicm #ifndef FBIO_GETMODE		/* FreeBSD 3.x */
73181d8c4e1Snicm #define FBIO_GETMODE    CONS_GET
73281d8c4e1Snicm #define FBIO_MODEINFO   CONS_MODEINFO
73381d8c4e1Snicm #endif /* FBIO_GETMODE */
73481d8c4e1Snicm 		    video_info_t the_video;
73581d8c4e1Snicm 
73681d8c4e1Snicm 		    if (ioctl(sp->_mouse_fd,
73781d8c4e1Snicm 			      FBIO_GETMODE,
73881d8c4e1Snicm 			      &the_video.vi_mode) != -1
73981d8c4e1Snicm 			&& ioctl(sp->_mouse_fd,
74081d8c4e1Snicm 				 FBIO_MODEINFO,
74181d8c4e1Snicm 				 &the_video) != -1) {
74281d8c4e1Snicm 			sp->_sysmouse_char_width = the_video.vi_cwidth;
74381d8c4e1Snicm 			sp->_sysmouse_char_height = the_video.vi_cheight;
74481d8c4e1Snicm 		    }
74581d8c4e1Snicm 		}
74681d8c4e1Snicm #endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */
74781d8c4e1Snicm 
74881d8c4e1Snicm 		if (sp->_sysmouse_char_width <= 0)
74981d8c4e1Snicm 		    sp->_sysmouse_char_width = 8;
75081d8c4e1Snicm 		if (sp->_sysmouse_char_height <= 0)
75181d8c4e1Snicm 		    sp->_sysmouse_char_height = 16;
75281d8c4e1Snicm 		sp->_mouse_type = M_SYSMOUSE;
75381d8c4e1Snicm 		returnVoid;
75423bb66c4Smillert 	    }
75592dd1ec0Smillert 	}
75692dd1ec0Smillert     }
75781d8c4e1Snicm #endif /* USE_SYSMOUSE */
75892dd1ec0Smillert 
759*c7ef0cfcSnicm #ifdef USE_TERM_DRIVER
760*c7ef0cfcSnicm     CallDriver(sp, td_initmouse);
761*c7ef0cfcSnicm #endif
762*c7ef0cfcSnicm #if !defined(USE_TERM_DRIVER) || defined(EXP_WIN32_DRIVER)
76323bb66c4Smillert     /* we know how to recognize mouse events under "xterm" */
764*c7ef0cfcSnicm     if (NonEmpty(key_mouse)) {
76581d8c4e1Snicm 	init_xterm_mouse(sp);
766*c7ef0cfcSnicm     } else if (strstr(SP_TERMTYPE term_names, "xterm") != 0) {
76781d8c4e1Snicm 	if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
76881d8c4e1Snicm 	    init_xterm_mouse(sp);
76923bb66c4Smillert     }
770*c7ef0cfcSnicm #endif
771*c7ef0cfcSnicm 
77281d8c4e1Snicm     returnVoid;
77323bb66c4Smillert }
77423bb66c4Smillert 
77523bb66c4Smillert static bool
_nc_mouse_init(SCREEN * sp)77681d8c4e1Snicm _nc_mouse_init(SCREEN *sp)
77781d8c4e1Snicm /* initialize the mouse */
77892dd1ec0Smillert {
77981d8c4e1Snicm     bool result = FALSE;
780*c7ef0cfcSnicm 
781*c7ef0cfcSnicm     T((T_CALLED("_nc_mouse_init(%p)"), (void *)sp));
78281d8c4e1Snicm 
78381d8c4e1Snicm     if (sp != 0) {
78481d8c4e1Snicm 	if (!sp->_mouse_initialized) {
785*c7ef0cfcSnicm 	    int i;
786*c7ef0cfcSnicm 
78781d8c4e1Snicm 	    sp->_mouse_initialized = TRUE;
78881d8c4e1Snicm 
789*c7ef0cfcSnicm 	    TR(MY_TRACE, ("set _mouse_initialized"));
79081d8c4e1Snicm 
79181d8c4e1Snicm 	    sp->_mouse_eventp = FirstEV(sp);
79281d8c4e1Snicm 	    for (i = 0; i < EV_MAX; i++)
793*c7ef0cfcSnicm 		Invalidate(sp->_mouse_events + i);
79481d8c4e1Snicm 
79581d8c4e1Snicm 	    initialize_mousetype(sp);
79681d8c4e1Snicm 
797*c7ef0cfcSnicm 	    T(("set _mouse_type to %d", sp->_mouse_type));
79881d8c4e1Snicm 	}
79981d8c4e1Snicm 	result = sp->_mouse_initialized;
80081d8c4e1Snicm     }
801*c7ef0cfcSnicm     returnCode(result);
80281d8c4e1Snicm }
80381d8c4e1Snicm 
80481d8c4e1Snicm /*
80581d8c4e1Snicm  * Query to see if there is a pending mouse event.  This is called from
80681d8c4e1Snicm  * fifo_push() in lib_getch.c
80781d8c4e1Snicm  */
80881d8c4e1Snicm static bool
_nc_mouse_event(SCREEN * sp)809*c7ef0cfcSnicm _nc_mouse_event(SCREEN *sp)
81081d8c4e1Snicm {
81181d8c4e1Snicm     MEVENT *eventp = sp->_mouse_eventp;
81281d8c4e1Snicm     bool result = FALSE;
81381d8c4e1Snicm 
81481d8c4e1Snicm     (void) eventp;
81581d8c4e1Snicm 
81681d8c4e1Snicm     switch (sp->_mouse_type) {
81781d8c4e1Snicm     case M_XTERM:
81881d8c4e1Snicm 	/* xterm: never have to query, mouse events are in the keyboard stream */
81981d8c4e1Snicm #if USE_EMX_MOUSE
82081d8c4e1Snicm 	{
82181d8c4e1Snicm 	    char kbuf[3];
82281d8c4e1Snicm 
82381d8c4e1Snicm 	    int i, res = read(M_FD(sp), &kbuf, 3);	/* Eat the prefix */
82481d8c4e1Snicm 	    if (res != 3)
82581d8c4e1Snicm 		printf("Got %d chars instead of 3 for prefix.\n", res);
82681d8c4e1Snicm 	    for (i = 0; i < res; i++) {
82781d8c4e1Snicm 		if (kbuf[i] != key_mouse[i])
82881d8c4e1Snicm 		    printf("Got char %d instead of %d for prefix.\n",
82981d8c4e1Snicm 			   (int) kbuf[i], (int) key_mouse[i]);
83081d8c4e1Snicm 	    }
83181d8c4e1Snicm 	    result = TRUE;
83281d8c4e1Snicm 	}
83381d8c4e1Snicm #endif /* USE_EMX_MOUSE */
83481d8c4e1Snicm 	break;
83581d8c4e1Snicm 
83692dd1ec0Smillert #if USE_GPM_SUPPORT
83781d8c4e1Snicm     case M_GPM:
838*c7ef0cfcSnicm 	if (sp->_mouse_fd >= 0) {
83981d8c4e1Snicm 	    /* query server for event, return TRUE if we find one */
84092dd1ec0Smillert 	    Gpm_Event ev;
84192dd1ec0Smillert 
842*c7ef0cfcSnicm 	    switch (my_Gpm_GetEvent(&ev)) {
843*c7ef0cfcSnicm 	    case 0:
844*c7ef0cfcSnicm 		/* Connection closed, drop the mouse. */
845*c7ef0cfcSnicm 		sp->_mouse_fd = -1;
846*c7ef0cfcSnicm 		break;
847*c7ef0cfcSnicm 	    case 1:
84881d8c4e1Snicm 		/* there's only one mouse... */
84981d8c4e1Snicm 		eventp->id = NORMAL_EVENT;
85092dd1ec0Smillert 
85192dd1ec0Smillert 		eventp->bstate = 0;
85223bb66c4Smillert 		switch (ev.type & 0x0f) {
85392dd1ec0Smillert 		case (GPM_DOWN):
85423bb66c4Smillert 		    if (ev.buttons & GPM_B_LEFT)
85523bb66c4Smillert 			eventp->bstate |= BUTTON1_PRESSED;
85623bb66c4Smillert 		    if (ev.buttons & GPM_B_MIDDLE)
85723bb66c4Smillert 			eventp->bstate |= BUTTON2_PRESSED;
85823bb66c4Smillert 		    if (ev.buttons & GPM_B_RIGHT)
85923bb66c4Smillert 			eventp->bstate |= BUTTON3_PRESSED;
86092dd1ec0Smillert 		    break;
86192dd1ec0Smillert 		case (GPM_UP):
86223bb66c4Smillert 		    if (ev.buttons & GPM_B_LEFT)
86323bb66c4Smillert 			eventp->bstate |= BUTTON1_RELEASED;
86423bb66c4Smillert 		    if (ev.buttons & GPM_B_MIDDLE)
86523bb66c4Smillert 			eventp->bstate |= BUTTON2_RELEASED;
86623bb66c4Smillert 		    if (ev.buttons & GPM_B_RIGHT)
86723bb66c4Smillert 			eventp->bstate |= BUTTON3_RELEASED;
86892dd1ec0Smillert 		    break;
86992dd1ec0Smillert 		default:
870*c7ef0cfcSnicm 		    eventp->bstate |= REPORT_MOUSE_POSITION;
87192dd1ec0Smillert 		    break;
87292dd1ec0Smillert 		}
87392dd1ec0Smillert 
87492dd1ec0Smillert 		eventp->x = ev.x - 1;
87592dd1ec0Smillert 		eventp->y = ev.y - 1;
87692dd1ec0Smillert 		eventp->z = 0;
87792dd1ec0Smillert 
87892dd1ec0Smillert 		/* bump the next-free pointer into the circular list */
879*c7ef0cfcSnicm 		sp->_mouse_eventp = NEXT(eventp);
88081d8c4e1Snicm 		result = TRUE;
881*c7ef0cfcSnicm 		break;
88292dd1ec0Smillert 	    }
88381d8c4e1Snicm 	}
88481d8c4e1Snicm 	break;
88592dd1ec0Smillert #endif
88692dd1ec0Smillert 
88781d8c4e1Snicm #if USE_SYSMOUSE
88881d8c4e1Snicm     case M_SYSMOUSE:
88981d8c4e1Snicm 	if (sp->_sysmouse_head < sp->_sysmouse_tail) {
89081d8c4e1Snicm 	    *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head];
89177c4fa26Smillert 
89281d8c4e1Snicm 	    /*
89381d8c4e1Snicm 	     * Point the fifo-head to the next possible location.  If there
89481d8c4e1Snicm 	     * are none, reset the indices.  This may be interrupted by the
89581d8c4e1Snicm 	     * signal handler, doing essentially the same reset.
89681d8c4e1Snicm 	     */
89781d8c4e1Snicm 	    sp->_sysmouse_head += 1;
89881d8c4e1Snicm 	    if (sp->_sysmouse_head == sp->_sysmouse_tail) {
89981d8c4e1Snicm 		sp->_sysmouse_tail = 0;
90081d8c4e1Snicm 		sp->_sysmouse_head = 0;
90177c4fa26Smillert 	    }
90277c4fa26Smillert 
90381d8c4e1Snicm 	    /* bump the next-free pointer into the circular list */
90481d8c4e1Snicm 	    sp->_mouse_eventp = eventp = NEXT(eventp);
90581d8c4e1Snicm 	    result = TRUE;
90681d8c4e1Snicm 	}
90781d8c4e1Snicm 	break;
90881d8c4e1Snicm #endif /* USE_SYSMOUSE */
90981d8c4e1Snicm 
910*c7ef0cfcSnicm #ifdef USE_TERM_DRIVER
911*c7ef0cfcSnicm     case M_TERM_DRIVER:
912*c7ef0cfcSnicm 	while (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
913*c7ef0cfcSnicm 	    *eventp = sp->_drv_mouse_fifo[sp->_drv_mouse_head];
914*c7ef0cfcSnicm 
915*c7ef0cfcSnicm 	    /*
916*c7ef0cfcSnicm 	     * Point the fifo-head to the next possible location.  If there
917*c7ef0cfcSnicm 	     * are none, reset the indices.
918*c7ef0cfcSnicm 	     */
919*c7ef0cfcSnicm 	    sp->_drv_mouse_head += 1;
920*c7ef0cfcSnicm 	    if (sp->_drv_mouse_head == sp->_drv_mouse_tail) {
921*c7ef0cfcSnicm 		sp->_drv_mouse_tail = 0;
922*c7ef0cfcSnicm 		sp->_drv_mouse_head = 0;
923*c7ef0cfcSnicm 	    }
924*c7ef0cfcSnicm 
925*c7ef0cfcSnicm 	    /* bump the next-free pointer into the circular list */
926*c7ef0cfcSnicm 	    sp->_mouse_eventp = eventp = NEXT(eventp);
927*c7ef0cfcSnicm 	    result = TRUE;
928*c7ef0cfcSnicm 	}
929*c7ef0cfcSnicm 	break;
930*c7ef0cfcSnicm #endif
931*c7ef0cfcSnicm 
93281d8c4e1Snicm     case M_NONE:
93381d8c4e1Snicm 	break;
93481d8c4e1Snicm     }
93581d8c4e1Snicm 
93681d8c4e1Snicm     return result;		/* true if we found an event */
93792dd1ec0Smillert }
93892dd1ec0Smillert 
93981d8c4e1Snicm #if USE_EMX_MOUSE
94081d8c4e1Snicm #define PRESS_POSITION(n) \
941*c7ef0cfcSnicm     do { \
94281d8c4e1Snicm 	    eventp->bstate = MASK_PRESS(n); \
943*c7ef0cfcSnicm 	    sp->_mouse_bstate |= MASK_PRESS(n); \
944*c7ef0cfcSnicm 	    if (button & 0x40) { \
945*c7ef0cfcSnicm 		    eventp->bstate = MASK_RELEASE(n); \
946*c7ef0cfcSnicm 		    sp->_mouse_bstate &= ~MASK_PRESS(n); \
947*c7ef0cfcSnicm 	    } \
948*c7ef0cfcSnicm     } while (0)
94981d8c4e1Snicm #else
95081d8c4e1Snicm #define PRESS_POSITION(n) \
951*c7ef0cfcSnicm     do { \
952*c7ef0cfcSnicm 	    eventp->bstate = (mmask_t) ((sp->_mouse_bstate & MASK_PRESS(n)) \
95381d8c4e1Snicm 				    ? REPORT_MOUSE_POSITION \
954*c7ef0cfcSnicm 				    : MASK_PRESS(n)); \
955*c7ef0cfcSnicm 	    sp->_mouse_bstate |= MASK_PRESS(n); \
956*c7ef0cfcSnicm     } while (0)
95781d8c4e1Snicm #endif
95881d8c4e1Snicm 
959*c7ef0cfcSnicm static bool
handle_wheel(SCREEN * sp,MEVENT * eventp,int button,int wheel)960*c7ef0cfcSnicm handle_wheel(SCREEN *sp, MEVENT * eventp, int button, int wheel)
961*c7ef0cfcSnicm {
962*c7ef0cfcSnicm     bool result = TRUE;
963*c7ef0cfcSnicm 
964*c7ef0cfcSnicm     switch (button & 3) {
965*c7ef0cfcSnicm     case 0:
966*c7ef0cfcSnicm 	if (wheel) {
96781d8c4e1Snicm 	    eventp->bstate = MASK_PRESS(4);
968*c7ef0cfcSnicm 	    /* Do not record in sp->_mouse_bstate; there will be no
969*c7ef0cfcSnicm 	     * corresponding release event.
970*c7ef0cfcSnicm 	     */
971*c7ef0cfcSnicm 	} else {
97281d8c4e1Snicm 	    PRESS_POSITION(1);
973*c7ef0cfcSnicm 	}
97492dd1ec0Smillert 	break;
975*c7ef0cfcSnicm     case 1:
976*c7ef0cfcSnicm 	if (wheel) {
977*c7ef0cfcSnicm #if NCURSES_MOUSE_VERSION >= 2
97881d8c4e1Snicm 	    eventp->bstate = MASK_PRESS(5);
979*c7ef0cfcSnicm 	    /* See comment above for button 4 */
980*c7ef0cfcSnicm #else
981*c7ef0cfcSnicm 	    /* Ignore this event as it is not a true press of the button */
982*c7ef0cfcSnicm 	    eventp->bstate = REPORT_MOUSE_POSITION;
98392dd1ec0Smillert #endif
984*c7ef0cfcSnicm 	} else {
98581d8c4e1Snicm 	    PRESS_POSITION(2);
986*c7ef0cfcSnicm 	}
98792dd1ec0Smillert 	break;
988*c7ef0cfcSnicm     case 2:
98981d8c4e1Snicm 	PRESS_POSITION(3);
99092dd1ec0Smillert 	break;
991*c7ef0cfcSnicm     default:
992*c7ef0cfcSnicm 	/*
993*c7ef0cfcSnicm 	 * case 3 is sent when the mouse buttons are released.
994*c7ef0cfcSnicm 	 *
995*c7ef0cfcSnicm 	 * If the terminal uses xterm mode 1003, a continuous series of
996*c7ef0cfcSnicm 	 * button-release events is sent as the mouse moves around the screen,
997*c7ef0cfcSnicm 	 * or as the wheel mouse is rotated.
998*c7ef0cfcSnicm 	 *
999*c7ef0cfcSnicm 	 * Return false in this case, so that when running in X10 mode, we will
1000*c7ef0cfcSnicm 	 * recalculate bstate.
1001*c7ef0cfcSnicm 	 */
1002*c7ef0cfcSnicm 	eventp->bstate = REPORT_MOUSE_POSITION;
1003*c7ef0cfcSnicm 	result = FALSE;
1004*c7ef0cfcSnicm 	break;
1005*c7ef0cfcSnicm     }
1006*c7ef0cfcSnicm     return result;
1007*c7ef0cfcSnicm }
100892dd1ec0Smillert 
1009*c7ef0cfcSnicm static bool
decode_X10_bstate(SCREEN * sp,MEVENT * eventp,unsigned intro)1010*c7ef0cfcSnicm decode_X10_bstate(SCREEN *sp, MEVENT * eventp, unsigned intro)
1011*c7ef0cfcSnicm {
1012*c7ef0cfcSnicm     bool result;
1013*c7ef0cfcSnicm     int button = 0;
1014*c7ef0cfcSnicm     int wheel = (intro & 96) == 96;
1015*c7ef0cfcSnicm 
1016*c7ef0cfcSnicm     eventp->bstate = 0;
1017*c7ef0cfcSnicm 
1018*c7ef0cfcSnicm     if (intro >= 96) {
1019*c7ef0cfcSnicm 	if (intro >= 160) {
1020*c7ef0cfcSnicm 	    button = (int) (intro - 152);	/* buttons 8-11 */
1021*c7ef0cfcSnicm 	} else {
1022*c7ef0cfcSnicm 	    button = (int) (intro - 92);	/* buttons 4-7 */
1023*c7ef0cfcSnicm 	}
1024*c7ef0cfcSnicm     } else {
1025*c7ef0cfcSnicm 	button = (intro & 3);
1026*c7ef0cfcSnicm     }
1027*c7ef0cfcSnicm 
1028*c7ef0cfcSnicm     if (button > MAX_BUTTONS) {
1029*c7ef0cfcSnicm 	eventp->bstate = REPORT_MOUSE_POSITION;
1030*c7ef0cfcSnicm     } else if (!handle_wheel(sp, eventp, (int) intro, wheel)) {
1031*c7ef0cfcSnicm 
103292dd1ec0Smillert 	/*
103381d8c4e1Snicm 	 * Release events aren't reported for individual buttons, just for
103481d8c4e1Snicm 	 * the button set as a whole.  However, because there are normally
103581d8c4e1Snicm 	 * no mouse events under xterm that intervene between press and
103681d8c4e1Snicm 	 * release, we can infer the button actually released by looking at
103781d8c4e1Snicm 	 * the previous event.
103892dd1ec0Smillert 	 */
1039*c7ef0cfcSnicm 	if (sp->_mouse_bstate & BUTTON_PRESSED) {
1040*c7ef0cfcSnicm 	    int b;
1041*c7ef0cfcSnicm 
104281d8c4e1Snicm 	    eventp->bstate = BUTTON_RELEASED;
104381d8c4e1Snicm 	    for (b = 1; b <= MAX_BUTTONS; ++b) {
1044*c7ef0cfcSnicm 		if (!(sp->_mouse_bstate & MASK_PRESS(b)))
104581d8c4e1Snicm 		    eventp->bstate &= ~MASK_RELEASE(b);
104681d8c4e1Snicm 	    }
1047*c7ef0cfcSnicm 	    sp->_mouse_bstate = 0;
104881d8c4e1Snicm 	} else {
104992dd1ec0Smillert 	    /*
1050*c7ef0cfcSnicm 	     * xterm will return a stream of release-events to let the
1051*c7ef0cfcSnicm 	     * application know where the mouse is going, if private mode
1052*c7ef0cfcSnicm 	     * 1002 or 1003 is enabled.
105392dd1ec0Smillert 	     */
105481d8c4e1Snicm 	    eventp->bstate = REPORT_MOUSE_POSITION;
105581d8c4e1Snicm 	}
105692dd1ec0Smillert     }
105792dd1ec0Smillert 
1058*c7ef0cfcSnicm     if (intro & 4) {
105992dd1ec0Smillert 	eventp->bstate |= BUTTON_SHIFT;
106092dd1ec0Smillert     }
1061*c7ef0cfcSnicm     if (intro & 8) {
106292dd1ec0Smillert 	eventp->bstate |= BUTTON_ALT;
106392dd1ec0Smillert     }
1064*c7ef0cfcSnicm     if (intro & 16) {
106592dd1ec0Smillert 	eventp->bstate |= BUTTON_CTRL;
106692dd1ec0Smillert     }
1067*c7ef0cfcSnicm     result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
1068*c7ef0cfcSnicm     return result;
1069*c7ef0cfcSnicm }
1070*c7ef0cfcSnicm 
1071*c7ef0cfcSnicm /* This code requires that your xterm entry contain the kmous capability and
1072*c7ef0cfcSnicm  * that it be set to the \E[M documented in the Xterm Control Sequences
1073*c7ef0cfcSnicm  * reference.  This is how we arrange for mouse events to be reported via a
1074*c7ef0cfcSnicm  * KEY_MOUSE return value from wgetch().  After this value is received,
1075*c7ef0cfcSnicm  * _nc_mouse_inline() gets called and is immediately responsible for parsing
1076*c7ef0cfcSnicm  * the mouse status information following the prefix.
1077*c7ef0cfcSnicm  *
1078*c7ef0cfcSnicm  * The following quotes from the ctlseqs.ms document in the XTerm distribution,
1079*c7ef0cfcSnicm  * describing the mouse tracking feature:
1080*c7ef0cfcSnicm  *
1081*c7ef0cfcSnicm  * Parameters for all mouse tracking escape sequences generated by xterm encode
1082*c7ef0cfcSnicm  * numeric parameters in a single character as value+040.  For example, ! is
1083*c7ef0cfcSnicm  * 1.
1084*c7ef0cfcSnicm  *
1085*c7ef0cfcSnicm  * On button press or release, xterm sends ESC [ M CbCxCy.  The low two bits of
1086*c7ef0cfcSnicm  * Cb encode button information:  0=MB1 pressed, 1=MB2 pressed, 2=MB3 pressed,
1087*c7ef0cfcSnicm  * 3=release.  The upper bits encode what modifiers were down when the button
1088*c7ef0cfcSnicm  * was pressed and are added together.  4=Shift, 8=Meta, 16=Control.  Cx and Cy
1089*c7ef0cfcSnicm  * are the x and y coordinates of the mouse event.  The upper left corner is
1090*c7ef0cfcSnicm  * (1,1).
1091*c7ef0cfcSnicm  *
1092*c7ef0cfcSnicm  * (End quote) By the time we get here, we've eaten the key prefix.  FYI, the
1093*c7ef0cfcSnicm  * loop below is necessary because mouse click info isn't guaranteed to present
1094*c7ef0cfcSnicm  * as a single clist item.
1095*c7ef0cfcSnicm  *
1096*c7ef0cfcSnicm  * Wheel mice may return buttons 4 and 5 when the wheel is turned.  We encode
1097*c7ef0cfcSnicm  * those as button presses.
1098*c7ef0cfcSnicm  */
1099*c7ef0cfcSnicm static bool
decode_xterm_X10(SCREEN * sp,MEVENT * eventp)1100*c7ef0cfcSnicm decode_xterm_X10(SCREEN *sp, MEVENT * eventp)
1101*c7ef0cfcSnicm {
1102*c7ef0cfcSnicm #define MAX_KBUF 3
1103*c7ef0cfcSnicm     unsigned char kbuf[MAX_KBUF + 1];
1104*c7ef0cfcSnicm     size_t grabbed;
1105*c7ef0cfcSnicm     int res;
1106*c7ef0cfcSnicm     bool result;
1107*c7ef0cfcSnicm 
1108*c7ef0cfcSnicm     _nc_set_read_thread(TRUE);
1109*c7ef0cfcSnicm     for (grabbed = 0; grabbed < MAX_KBUF; grabbed += (size_t) res) {
1110*c7ef0cfcSnicm 
1111*c7ef0cfcSnicm 	/* For VIO mouse we add extra bit 64 to disambiguate button-up. */
1112*c7ef0cfcSnicm 	res = (int) read(
1113*c7ef0cfcSnicm #if USE_EMX_MOUSE
1114*c7ef0cfcSnicm 			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
1115*c7ef0cfcSnicm #else
1116*c7ef0cfcSnicm 			    sp->_ifd,
1117*c7ef0cfcSnicm #endif
1118*c7ef0cfcSnicm 			    kbuf + grabbed, (size_t) (MAX_KBUF - (int) grabbed));
1119*c7ef0cfcSnicm 	if (res == -1)
1120*c7ef0cfcSnicm 	    break;
1121*c7ef0cfcSnicm     }
1122*c7ef0cfcSnicm     _nc_set_read_thread(FALSE);
1123*c7ef0cfcSnicm     kbuf[MAX_KBUF] = '\0';
1124*c7ef0cfcSnicm 
1125*c7ef0cfcSnicm     TR(TRACE_IEVENT,
1126*c7ef0cfcSnicm        ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
1127*c7ef0cfcSnicm 
1128*c7ef0cfcSnicm     /* there's only one mouse... */
1129*c7ef0cfcSnicm     eventp->id = NORMAL_EVENT;
1130*c7ef0cfcSnicm 
1131*c7ef0cfcSnicm     result = decode_X10_bstate(sp, eventp, kbuf[0]);
113292dd1ec0Smillert 
113392dd1ec0Smillert     eventp->x = (kbuf[1] - ' ') - 1;
113492dd1ec0Smillert     eventp->y = (kbuf[2] - ' ') - 1;
1135*c7ef0cfcSnicm 
1136*c7ef0cfcSnicm     return result;
1137*c7ef0cfcSnicm }
1138*c7ef0cfcSnicm 
1139*c7ef0cfcSnicm #ifdef EXP_XTERM_1005
1140*c7ef0cfcSnicm /*
1141*c7ef0cfcSnicm  * This is identical to X10/X11 responses except that there are two UTF-8
1142*c7ef0cfcSnicm  * characters storing the ordinates instead of two bytes.
1143*c7ef0cfcSnicm  */
1144*c7ef0cfcSnicm static bool
decode_xterm_1005(SCREEN * sp,MEVENT * eventp)1145*c7ef0cfcSnicm decode_xterm_1005(SCREEN *sp, MEVENT * eventp)
1146*c7ef0cfcSnicm {
1147*c7ef0cfcSnicm     char kbuf[80];
1148*c7ef0cfcSnicm     size_t grabbed;
1149*c7ef0cfcSnicm     size_t limit = (sizeof(kbuf) - 1);
1150*c7ef0cfcSnicm     unsigned coords[2];
1151*c7ef0cfcSnicm     bool result;
1152*c7ef0cfcSnicm 
1153*c7ef0cfcSnicm     coords[0] = 0;
1154*c7ef0cfcSnicm     coords[1] = 0;
1155*c7ef0cfcSnicm 
1156*c7ef0cfcSnicm     _nc_set_read_thread(TRUE);
1157*c7ef0cfcSnicm     for (grabbed = 0; grabbed < limit;) {
1158*c7ef0cfcSnicm 	int res;
1159*c7ef0cfcSnicm 
1160*c7ef0cfcSnicm 	res = (int) read(
1161*c7ef0cfcSnicm #if USE_EMX_MOUSE
1162*c7ef0cfcSnicm 			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
1163*c7ef0cfcSnicm #else
1164*c7ef0cfcSnicm 			    sp->_ifd,
1165*c7ef0cfcSnicm #endif
1166*c7ef0cfcSnicm 			    (kbuf + grabbed), (size_t) 1);
1167*c7ef0cfcSnicm 	if (res == -1)
1168*c7ef0cfcSnicm 	    break;
1169*c7ef0cfcSnicm 	grabbed += (size_t) res;
1170*c7ef0cfcSnicm 	if (grabbed > 1) {
1171*c7ef0cfcSnicm 	    size_t check = 1;
1172*c7ef0cfcSnicm 	    int n;
1173*c7ef0cfcSnicm 
1174*c7ef0cfcSnicm 	    for (n = 0; n < 2; ++n) {
1175*c7ef0cfcSnicm 		int rc;
1176*c7ef0cfcSnicm 
1177*c7ef0cfcSnicm 		if (check >= grabbed)
1178*c7ef0cfcSnicm 		    break;
1179*c7ef0cfcSnicm 		rc = _nc_conv_to_utf32(&coords[n], kbuf + check, (unsigned)
1180*c7ef0cfcSnicm 				       (grabbed - check));
1181*c7ef0cfcSnicm 		if (!rc)
1182*c7ef0cfcSnicm 		    break;
1183*c7ef0cfcSnicm 		check += (size_t) rc;
1184*c7ef0cfcSnicm 	    }
1185*c7ef0cfcSnicm 	    if (n >= 2)
1186*c7ef0cfcSnicm 		break;
1187*c7ef0cfcSnicm 	}
1188*c7ef0cfcSnicm     }
1189*c7ef0cfcSnicm     _nc_set_read_thread(FALSE);
1190*c7ef0cfcSnicm 
1191*c7ef0cfcSnicm     TR(TRACE_IEVENT,
1192*c7ef0cfcSnicm        ("_nc_mouse_inline sees the following xterm data: %s",
1193*c7ef0cfcSnicm 	_nc_visbufn(kbuf, (int) grabbed)));
1194*c7ef0cfcSnicm 
1195*c7ef0cfcSnicm     /* there's only one mouse... */
1196*c7ef0cfcSnicm     eventp->id = NORMAL_EVENT;
1197*c7ef0cfcSnicm 
1198*c7ef0cfcSnicm     result = decode_X10_bstate(sp, eventp, UChar(kbuf[0]));
1199*c7ef0cfcSnicm 
1200*c7ef0cfcSnicm     eventp->x = (int) (coords[0] - ' ') - 1;
1201*c7ef0cfcSnicm     eventp->y = (int) (coords[1] - ' ') - 1;
1202*c7ef0cfcSnicm 
1203*c7ef0cfcSnicm     return result;
1204*c7ef0cfcSnicm }
1205*c7ef0cfcSnicm #endif /* EXP_XTERM_1005 */
1206*c7ef0cfcSnicm 
1207*c7ef0cfcSnicm /*
1208*c7ef0cfcSnicm  * ECMA-48 section 5.4
1209*c7ef0cfcSnicm  */
1210*c7ef0cfcSnicm #define isInter(c) ((c) >= 0x20 && (c) <= 0x2f)
1211*c7ef0cfcSnicm #define isParam(c) ((c) >= 0x30 && (c) <= 0x3f)
1212*c7ef0cfcSnicm #define isFinal(c) ((c) >= 0x40 && (c) <= 0x7e)
1213*c7ef0cfcSnicm 
1214*c7ef0cfcSnicm #define MAX_PARAMS 9
1215*c7ef0cfcSnicm 
1216*c7ef0cfcSnicm typedef struct {
1217*c7ef0cfcSnicm     int nerror;			/* nonzero if there are unexpected chars */
1218*c7ef0cfcSnicm     int nparam;			/* number of numeric parameters */
1219*c7ef0cfcSnicm     int params[MAX_PARAMS];
1220*c7ef0cfcSnicm     int final;			/* the final-character */
1221*c7ef0cfcSnicm } SGR_DATA;
1222*c7ef0cfcSnicm 
1223*c7ef0cfcSnicm static bool
read_SGR(SCREEN * sp,SGR_DATA * result)1224*c7ef0cfcSnicm read_SGR(SCREEN *sp, SGR_DATA * result)
1225*c7ef0cfcSnicm {
1226*c7ef0cfcSnicm     char kbuf[80];		/* bigger than any possible mouse response */
1227*c7ef0cfcSnicm     int grabbed = 0;
1228*c7ef0cfcSnicm     int ch = 0;
1229*c7ef0cfcSnicm     int now = -1;
1230*c7ef0cfcSnicm     int marker = 1;
1231*c7ef0cfcSnicm 
1232*c7ef0cfcSnicm     memset(result, 0, sizeof(*result));
1233*c7ef0cfcSnicm     _nc_set_read_thread(TRUE);
1234*c7ef0cfcSnicm 
1235*c7ef0cfcSnicm     do {
1236*c7ef0cfcSnicm 	int res;
1237*c7ef0cfcSnicm 
1238*c7ef0cfcSnicm 	res = (int) read(
1239*c7ef0cfcSnicm #if USE_EMX_MOUSE
1240*c7ef0cfcSnicm 			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
1241*c7ef0cfcSnicm #else
1242*c7ef0cfcSnicm 			    sp->_ifd,
1243*c7ef0cfcSnicm #endif
1244*c7ef0cfcSnicm 			    (kbuf + grabbed), (size_t) 1);
1245*c7ef0cfcSnicm 	if (res == -1)
1246*c7ef0cfcSnicm 	    break;
1247*c7ef0cfcSnicm 	if ((grabbed + MAX_KBUF) >= (int) sizeof(kbuf)) {
1248*c7ef0cfcSnicm 	    result->nerror++;
1249*c7ef0cfcSnicm 	    break;
1250*c7ef0cfcSnicm 	}
1251*c7ef0cfcSnicm 	ch = UChar(kbuf[grabbed]);
1252*c7ef0cfcSnicm 	kbuf[grabbed + 1] = 0;
1253*c7ef0cfcSnicm 	switch (ch) {
1254*c7ef0cfcSnicm 	case '0':
1255*c7ef0cfcSnicm 	case '1':
1256*c7ef0cfcSnicm 	case '2':
1257*c7ef0cfcSnicm 	case '3':
1258*c7ef0cfcSnicm 	case '4':
1259*c7ef0cfcSnicm 	case '5':
1260*c7ef0cfcSnicm 	case '6':
1261*c7ef0cfcSnicm 	case '7':
1262*c7ef0cfcSnicm 	case '8':
1263*c7ef0cfcSnicm 	case '9':
1264*c7ef0cfcSnicm 	    if (marker) {
1265*c7ef0cfcSnicm 		++now;
1266*c7ef0cfcSnicm 		result->nparam = (now + 1);
1267*c7ef0cfcSnicm 	    }
1268*c7ef0cfcSnicm 	    marker = 0;
1269*c7ef0cfcSnicm 	    result->params[now] = (result->params[now] * 10) + (ch - '0');
1270*c7ef0cfcSnicm 	    break;
1271*c7ef0cfcSnicm 	case ';':
1272*c7ef0cfcSnicm 	    if (marker) {
1273*c7ef0cfcSnicm 		++now;
1274*c7ef0cfcSnicm 		result->nparam = (now + 1);
1275*c7ef0cfcSnicm 	    }
1276*c7ef0cfcSnicm 	    marker = 1;
1277*c7ef0cfcSnicm 	    break;
1278*c7ef0cfcSnicm 	default:
1279*c7ef0cfcSnicm 	    if (ch < 32 || ch > 126) {
1280*c7ef0cfcSnicm 		/*
1281*c7ef0cfcSnicm 		 * Technically other characters could be interspersed in the
1282*c7ef0cfcSnicm 		 * response.  Ignore those for now.
1283*c7ef0cfcSnicm 		 */
1284*c7ef0cfcSnicm 		result->nerror++;
1285*c7ef0cfcSnicm 		continue;
1286*c7ef0cfcSnicm 	    } else if (isFinal(ch)) {
1287*c7ef0cfcSnicm 		if (marker) {
1288*c7ef0cfcSnicm 		    result->nparam++;
1289*c7ef0cfcSnicm 		}
1290*c7ef0cfcSnicm 		result->final = ch;
1291*c7ef0cfcSnicm 	    } else {
1292*c7ef0cfcSnicm 		result->nerror++;
1293*c7ef0cfcSnicm 	    }
1294*c7ef0cfcSnicm 	    break;
1295*c7ef0cfcSnicm 	}
1296*c7ef0cfcSnicm 	++grabbed;
1297*c7ef0cfcSnicm     } while (!isFinal(ch));
1298*c7ef0cfcSnicm     _nc_set_read_thread(FALSE);
1299*c7ef0cfcSnicm 
1300*c7ef0cfcSnicm     kbuf[++grabbed] = 0;
1301*c7ef0cfcSnicm     TR(TRACE_IEVENT,
1302*c7ef0cfcSnicm        ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
1303*c7ef0cfcSnicm     return (grabbed > 0) && (result->nerror == 0);
1304*c7ef0cfcSnicm }
1305*c7ef0cfcSnicm 
1306*c7ef0cfcSnicm static bool
decode_xterm_SGR1006(SCREEN * sp,MEVENT * eventp)1307*c7ef0cfcSnicm decode_xterm_SGR1006(SCREEN *sp, MEVENT * eventp)
1308*c7ef0cfcSnicm {
1309*c7ef0cfcSnicm     SGR_DATA data;
1310*c7ef0cfcSnicm     bool result = FALSE;
1311*c7ef0cfcSnicm     if (read_SGR(sp, &data)) {
1312*c7ef0cfcSnicm 	int b = data.params[0];
1313*c7ef0cfcSnicm 	int b3 = 1 + (b & 3);
1314*c7ef0cfcSnicm 	int wheel = ((b & 64) == 64);
1315*c7ef0cfcSnicm 
1316*c7ef0cfcSnicm 	if (b >= 132) {
1317*c7ef0cfcSnicm 	    b3 = MAX_BUTTONS + 1;
1318*c7ef0cfcSnicm 	} else if (b >= 128) {
1319*c7ef0cfcSnicm 	    b3 = (b - 120);	/* buttons 8-11 */
1320*c7ef0cfcSnicm 	} else if (b >= 64) {
1321*c7ef0cfcSnicm 	    b3 = (b - 60);	/* buttons 6-7 */
1322*c7ef0cfcSnicm 	}
1323*c7ef0cfcSnicm 
1324*c7ef0cfcSnicm 	eventp->id = NORMAL_EVENT;
1325*c7ef0cfcSnicm 	if (data.final == 'M') {
1326*c7ef0cfcSnicm 	    (void) handle_wheel(sp, eventp, b, wheel);
1327*c7ef0cfcSnicm 	} else if (b3 > MAX_BUTTONS) {
1328*c7ef0cfcSnicm 	    eventp->bstate = REPORT_MOUSE_POSITION;
1329*c7ef0cfcSnicm 	} else {
1330*c7ef0cfcSnicm 	    mmask_t pressed = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_PRESSED);
1331*c7ef0cfcSnicm 	    mmask_t release = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_RELEASED);
1332*c7ef0cfcSnicm 	    if (sp->_mouse_bstate & pressed) {
1333*c7ef0cfcSnicm 		eventp->bstate = release;
1334*c7ef0cfcSnicm 		sp->_mouse_bstate &= ~pressed;
1335*c7ef0cfcSnicm 	    } else {
1336*c7ef0cfcSnicm 		eventp->bstate = REPORT_MOUSE_POSITION;
1337*c7ef0cfcSnicm 	    }
1338*c7ef0cfcSnicm 	}
1339*c7ef0cfcSnicm 	if (b & 4) {
1340*c7ef0cfcSnicm 	    eventp->bstate |= BUTTON_SHIFT;
1341*c7ef0cfcSnicm 	}
1342*c7ef0cfcSnicm 	if (b & 8) {
1343*c7ef0cfcSnicm 	    eventp->bstate |= BUTTON_ALT;
1344*c7ef0cfcSnicm 	}
1345*c7ef0cfcSnicm 	if (b & 16) {
1346*c7ef0cfcSnicm 	    eventp->bstate |= BUTTON_CTRL;
1347*c7ef0cfcSnicm 	}
1348*c7ef0cfcSnicm 	result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
1349*c7ef0cfcSnicm 	eventp->x = (data.params[1] ? (data.params[1] - 1) : 0);
1350*c7ef0cfcSnicm 	eventp->y = (data.params[2] ? (data.params[2] - 1) : 0);
1351*c7ef0cfcSnicm     }
1352*c7ef0cfcSnicm     return result;
1353*c7ef0cfcSnicm }
1354*c7ef0cfcSnicm 
1355*c7ef0cfcSnicm static bool
_nc_mouse_inline(SCREEN * sp)1356*c7ef0cfcSnicm _nc_mouse_inline(SCREEN *sp)
1357*c7ef0cfcSnicm /* mouse report received in the keyboard stream -- parse its info */
1358*c7ef0cfcSnicm {
1359*c7ef0cfcSnicm     bool result = FALSE;
1360*c7ef0cfcSnicm     MEVENT *eventp = sp->_mouse_eventp;
1361*c7ef0cfcSnicm 
1362*c7ef0cfcSnicm     TR(MY_TRACE, ("_nc_mouse_inline() called"));
1363*c7ef0cfcSnicm 
1364*c7ef0cfcSnicm     if (sp->_mouse_type == M_XTERM) {
1365*c7ef0cfcSnicm 	switch (sp->_mouse_format) {
1366*c7ef0cfcSnicm 	case MF_X10:
1367*c7ef0cfcSnicm 	    result = decode_xterm_X10(sp, eventp);
1368*c7ef0cfcSnicm 	    break;
1369*c7ef0cfcSnicm 	case MF_SGR1006:
1370*c7ef0cfcSnicm 	    result = decode_xterm_SGR1006(sp, eventp);
1371*c7ef0cfcSnicm 	    break;
1372*c7ef0cfcSnicm #ifdef EXP_XTERM_1005
1373*c7ef0cfcSnicm 	case MF_XTERM_1005:
1374*c7ef0cfcSnicm 	    result = decode_xterm_1005(sp, eventp);
1375*c7ef0cfcSnicm 	    break;
1376*c7ef0cfcSnicm #endif
1377*c7ef0cfcSnicm 	}
1378*c7ef0cfcSnicm 
137923bb66c4Smillert 	TR(MY_TRACE,
138023bb66c4Smillert 	   ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
138181d8c4e1Snicm 	    _nc_tracemouse(sp, eventp),
138281d8c4e1Snicm 	    (long) IndexEV(sp, eventp)));
138392dd1ec0Smillert 
138492dd1ec0Smillert 	/* bump the next-free pointer into the circular list */
138581d8c4e1Snicm 	sp->_mouse_eventp = NEXT(eventp);
1386*c7ef0cfcSnicm 
1387*c7ef0cfcSnicm 	if (!result) {
1388*c7ef0cfcSnicm 	    /* If this event is from a wheel-mouse, treat it like position
1389*c7ef0cfcSnicm 	     * reports and avoid waiting for the release-events which will
1390*c7ef0cfcSnicm 	     * never come.
1391*c7ef0cfcSnicm 	     */
1392*c7ef0cfcSnicm 	    if (eventp->bstate & BUTTON_PRESSED) {
1393*c7ef0cfcSnicm 		int b;
1394*c7ef0cfcSnicm 
1395*c7ef0cfcSnicm 		for (b = 4; b <= MAX_BUTTONS; ++b) {
1396*c7ef0cfcSnicm 		    if ((eventp->bstate & MASK_PRESS(b))) {
1397*c7ef0cfcSnicm 			result = TRUE;
1398*c7ef0cfcSnicm 			break;
1399*c7ef0cfcSnicm 		    }
1400*c7ef0cfcSnicm 		}
1401*c7ef0cfcSnicm 	    }
1402*c7ef0cfcSnicm 	}
140392dd1ec0Smillert     }
140492dd1ec0Smillert 
140581d8c4e1Snicm     return (result);
140692dd1ec0Smillert }
140792dd1ec0Smillert 
140823bb66c4Smillert static void
mouse_activate(SCREEN * sp,int on)1409*c7ef0cfcSnicm mouse_activate(SCREEN *sp, int on)
141092dd1ec0Smillert {
1411*c7ef0cfcSnicm     T((T_CALLED("mouse_activate(%p,%s)"),
1412*c7ef0cfcSnicm        (void *) SP_PARM, on ? "on" : "off"));
1413*c7ef0cfcSnicm 
141481d8c4e1Snicm     if (!on && !sp->_mouse_initialized)
1415*c7ef0cfcSnicm 	returnVoid;
141692dd1ec0Smillert 
141781d8c4e1Snicm     if (!_nc_mouse_init(sp))
1418*c7ef0cfcSnicm 	returnVoid;
141992dd1ec0Smillert 
142092dd1ec0Smillert     if (on) {
1421*c7ef0cfcSnicm 	sp->_mouse_bstate = 0;
142281d8c4e1Snicm 	switch (sp->_mouse_type) {
142392dd1ec0Smillert 	case M_XTERM:
142491421ef5Smillert #if NCURSES_EXT_FUNCS
1425*c7ef0cfcSnicm 	    NCURSES_SP_NAME(keyok) (NCURSES_SP_ARGx KEY_MOUSE, on);
142692dd1ec0Smillert #endif
142781d8c4e1Snicm 	    enable_xterm_mouse(sp, 1);
142892dd1ec0Smillert 	    break;
142992dd1ec0Smillert #if USE_GPM_SUPPORT
143092dd1ec0Smillert 	case M_GPM:
143181d8c4e1Snicm 	    if (enable_gpm_mouse(sp, TRUE)) {
143281d8c4e1Snicm 		sp->_mouse_fd = *(my_gpm_fd);
143381d8c4e1Snicm 		T(("GPM mouse_fd %d", sp->_mouse_fd));
143481d8c4e1Snicm 	    }
143592dd1ec0Smillert 	    break;
143692dd1ec0Smillert #endif
143781d8c4e1Snicm #if USE_SYSMOUSE
143881d8c4e1Snicm 	case M_SYSMOUSE:
143981d8c4e1Snicm 	    signal(SIGUSR2, handle_sysmouse);
144081d8c4e1Snicm 	    sp->_mouse_active = TRUE;
144181d8c4e1Snicm 	    break;
144281d8c4e1Snicm #endif
1443*c7ef0cfcSnicm #ifdef USE_TERM_DRIVER
1444*c7ef0cfcSnicm 	case M_TERM_DRIVER:
1445*c7ef0cfcSnicm 	    enable_win32_mouse(sp, TRUE);
1446*c7ef0cfcSnicm 	    break;
1447*c7ef0cfcSnicm #endif
144881d8c4e1Snicm 	case M_NONE:
1449*c7ef0cfcSnicm 	    returnVoid;
1450*c7ef0cfcSnicm 	default:
1451*c7ef0cfcSnicm 	    T(("unexpected mouse mode"));
1452*c7ef0cfcSnicm 	    break;
145392dd1ec0Smillert 	}
145492dd1ec0Smillert 	/* Make runtime binding to cut down on object size of applications that
145592dd1ec0Smillert 	 * do not use the mouse (e.g., 'clear').
145692dd1ec0Smillert 	 */
1457*c7ef0cfcSnicm 	/* *INDENT-EQLS* */
145881d8c4e1Snicm 	sp->_mouse_event  = _nc_mouse_event;
145981d8c4e1Snicm 	sp->_mouse_inline = _nc_mouse_inline;
146081d8c4e1Snicm 	sp->_mouse_parse  = _nc_mouse_parse;
146181d8c4e1Snicm 	sp->_mouse_resume = _nc_mouse_resume;
146281d8c4e1Snicm 	sp->_mouse_wrap   = _nc_mouse_wrap;
146392dd1ec0Smillert     } else {
146492dd1ec0Smillert 
146581d8c4e1Snicm 	switch (sp->_mouse_type) {
146692dd1ec0Smillert 	case M_XTERM:
146781d8c4e1Snicm 	    enable_xterm_mouse(sp, 0);
146892dd1ec0Smillert 	    break;
146992dd1ec0Smillert #if USE_GPM_SUPPORT
147092dd1ec0Smillert 	case M_GPM:
147181d8c4e1Snicm 	    enable_gpm_mouse(sp, FALSE);
147292dd1ec0Smillert 	    break;
147392dd1ec0Smillert #endif
147481d8c4e1Snicm #if USE_SYSMOUSE
147581d8c4e1Snicm 	case M_SYSMOUSE:
147681d8c4e1Snicm 	    signal(SIGUSR2, SIG_IGN);
147781d8c4e1Snicm 	    sp->_mouse_active = FALSE;
147881d8c4e1Snicm 	    break;
147981d8c4e1Snicm #endif
1480*c7ef0cfcSnicm #ifdef USE_TERM_DRIVER
1481*c7ef0cfcSnicm 	case M_TERM_DRIVER:
1482*c7ef0cfcSnicm 	    enable_win32_mouse(sp, FALSE);
1483*c7ef0cfcSnicm 	    break;
1484*c7ef0cfcSnicm #endif
148581d8c4e1Snicm 	case M_NONE:
1486*c7ef0cfcSnicm 	    returnVoid;
1487*c7ef0cfcSnicm 	default:
1488*c7ef0cfcSnicm 	    T(("unexpected mouse mode"));
1489*c7ef0cfcSnicm 	    break;
149092dd1ec0Smillert 	}
149192dd1ec0Smillert     }
1492*c7ef0cfcSnicm     NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
1493*c7ef0cfcSnicm     returnVoid;
149492dd1ec0Smillert }
149592dd1ec0Smillert 
149692dd1ec0Smillert /**************************************************************************
149792dd1ec0Smillert  *
149892dd1ec0Smillert  * Device-independent code
149992dd1ec0Smillert  *
150092dd1ec0Smillert  **************************************************************************/
150192dd1ec0Smillert 
150223bb66c4Smillert static bool
_nc_mouse_parse(SCREEN * sp,int runcount)150381d8c4e1Snicm _nc_mouse_parse(SCREEN *sp, int runcount)
150492dd1ec0Smillert /* parse a run of atomic mouse events into a gesture */
150592dd1ec0Smillert {
150681d8c4e1Snicm     MEVENT *eventp = sp->_mouse_eventp;
1507*c7ef0cfcSnicm     MEVENT *next, *ep;
1508*c7ef0cfcSnicm     MEVENT *first_valid = NULL;
1509*c7ef0cfcSnicm     MEVENT *first_invalid = NULL;
151092dd1ec0Smillert     int n;
151181d8c4e1Snicm     int b;
151292dd1ec0Smillert     bool merge;
1513*c7ef0cfcSnicm     bool endLoop;
151492dd1ec0Smillert 
151592dd1ec0Smillert     TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));
151692dd1ec0Smillert 
151792dd1ec0Smillert     /*
151892dd1ec0Smillert      * When we enter this routine, the event list next-free pointer
151992dd1ec0Smillert      * points just past a run of mouse events that we know were separated
152092dd1ec0Smillert      * in time by less than the critical click interval. The job of this
152181d8c4e1Snicm      * routine is to collapse this run into a single higher-level event
152292dd1ec0Smillert      * or gesture.
152392dd1ec0Smillert      *
152492dd1ec0Smillert      * We accomplish this in two passes.  The first pass merges press/release
152592dd1ec0Smillert      * pairs into click events.  The second merges runs of click events into
152692dd1ec0Smillert      * double or triple-click events.
152792dd1ec0Smillert      *
152892dd1ec0Smillert      * It's possible that the run may not resolve to a single event (for
152992dd1ec0Smillert      * example, if the user quadruple-clicks).  If so, leading events
1530*c7ef0cfcSnicm      * in the run are ignored if user does not call getmouse in a loop (getting
1531*c7ef0cfcSnicm      * them from newest to older).
153292dd1ec0Smillert      *
153392dd1ec0Smillert      * Note that this routine is independent of the format of the specific
153492dd1ec0Smillert      * format of the pointing-device's reports.  We can use it to parse
153592dd1ec0Smillert      * gestures on anything that reports press/release events on a per-
153692dd1ec0Smillert      * button basis, as long as the device-dependent mouse code puts stuff
153792dd1ec0Smillert      * on the queue in MEVENT format.
153892dd1ec0Smillert      */
153992dd1ec0Smillert 
1540*c7ef0cfcSnicm     /*
1541*c7ef0cfcSnicm      * Reset all events that were not set, in case the user sometimes calls
1542*c7ef0cfcSnicm      * getmouse only once and other times until there are no more events in
1543*c7ef0cfcSnicm      * queue.
1544*c7ef0cfcSnicm      *
1545*c7ef0cfcSnicm      * This also allows reaching the beginning of the run.
1546*c7ef0cfcSnicm      */
1547*c7ef0cfcSnicm     ep = eventp;
1548*c7ef0cfcSnicm     for (n = runcount; n < EV_MAX; n++) {
1549*c7ef0cfcSnicm 	Invalidate(ep);
1550*c7ef0cfcSnicm 	ep = NEXT(ep);
155192dd1ec0Smillert     }
155292dd1ec0Smillert 
155392dd1ec0Smillert #ifdef TRACE
155481d8c4e1Snicm     if (USE_TRACEF(TRACE_IEVENT)) {
155581d8c4e1Snicm 	_trace_slot(sp, "before mouse press/release merge:");
155692dd1ec0Smillert 	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1557*c7ef0cfcSnicm 		RunParams(sp, eventp, ep),
155892dd1ec0Smillert 		runcount);
155981d8c4e1Snicm 	_nc_unlock_global(tracef);
156092dd1ec0Smillert     }
156192dd1ec0Smillert #endif /* TRACE */
156292dd1ec0Smillert 
156392dd1ec0Smillert     /* first pass; merge press/release pairs */
1564*c7ef0cfcSnicm     endLoop = FALSE;
1565*c7ef0cfcSnicm     while (!endLoop) {
1566*c7ef0cfcSnicm 	next = NEXT(ep);
1567*c7ef0cfcSnicm 	if (next == eventp) {
1568*c7ef0cfcSnicm 	    /* Will end the loop, but compact before */
1569*c7ef0cfcSnicm 	    endLoop = TRUE;
1570*c7ef0cfcSnicm 	} else {
157181d8c4e1Snicm 
157281d8c4e1Snicm #define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \
157381d8c4e1Snicm 		      == !(next->bstate & MASK_RELEASE(x)))
157481d8c4e1Snicm 
1575*c7ef0cfcSnicm 	    if (ValidEvent(ep) && ValidEvent(next)
1576*c7ef0cfcSnicm 		&& ep->x == next->x && ep->y == next->y
157781d8c4e1Snicm 		&& (ep->bstate & BUTTON_PRESSED)
1578*c7ef0cfcSnicm 		&& (!(next->bstate & BUTTON_PRESSED))) {
1579*c7ef0cfcSnicm 		bool changed = TRUE;
1580*c7ef0cfcSnicm 
158181d8c4e1Snicm 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1582*c7ef0cfcSnicm 		    if (!MASK_CHANGED(b)) {
1583*c7ef0cfcSnicm 			changed = FALSE;
1584*c7ef0cfcSnicm 			break;
1585*c7ef0cfcSnicm 		    }
1586*c7ef0cfcSnicm 		}
1587*c7ef0cfcSnicm 
1588*c7ef0cfcSnicm 		if (changed) {
1589*c7ef0cfcSnicm 		    merge = FALSE;
1590*c7ef0cfcSnicm 		    for (b = 1; b <= MAX_BUTTONS; ++b) {
1591*c7ef0cfcSnicm 			if ((sp->_mouse_mask2 & MASK_CLICK(b))
159281d8c4e1Snicm 			    && (ep->bstate & MASK_PRESS(b))) {
1593*c7ef0cfcSnicm 			    next->bstate &= ~MASK_RELEASE(b);
1594*c7ef0cfcSnicm 			    next->bstate |= MASK_CLICK(b);
159592dd1ec0Smillert 			    merge = TRUE;
159692dd1ec0Smillert 			}
159792dd1ec0Smillert 		    }
1598*c7ef0cfcSnicm 		    if (merge) {
1599*c7ef0cfcSnicm 			Invalidate(ep);
160092dd1ec0Smillert 		    }
160192dd1ec0Smillert 		}
1602*c7ef0cfcSnicm 	    }
1603*c7ef0cfcSnicm 	}
160492dd1ec0Smillert 
1605*c7ef0cfcSnicm 	/* Compact valid events */
1606*c7ef0cfcSnicm 	if (!ValidEvent(ep)) {
1607*c7ef0cfcSnicm 	    if ((first_valid != NULL) && (first_invalid == NULL)) {
1608*c7ef0cfcSnicm 		first_invalid = ep;
1609*c7ef0cfcSnicm 	    }
1610*c7ef0cfcSnicm 	} else {
1611*c7ef0cfcSnicm 	    if (first_valid == NULL) {
1612*c7ef0cfcSnicm 		first_valid = ep;
1613*c7ef0cfcSnicm 	    } else if (first_invalid != NULL) {
1614*c7ef0cfcSnicm 		*first_invalid = *ep;
1615*c7ef0cfcSnicm 		Invalidate(ep);
1616*c7ef0cfcSnicm 		first_invalid = NEXT(first_invalid);
1617*c7ef0cfcSnicm 	    }
1618*c7ef0cfcSnicm 	}
1619*c7ef0cfcSnicm 
1620*c7ef0cfcSnicm 	ep = next;
1621*c7ef0cfcSnicm     }
1622*c7ef0cfcSnicm 
1623*c7ef0cfcSnicm     if (first_invalid != NULL) {
1624*c7ef0cfcSnicm 	eventp = first_invalid;
1625*c7ef0cfcSnicm     }
162692dd1ec0Smillert #ifdef TRACE
162781d8c4e1Snicm     if (USE_TRACEF(TRACE_IEVENT)) {
162881d8c4e1Snicm 	_trace_slot(sp, "before mouse click merge:");
1629*c7ef0cfcSnicm 	if (first_valid == NULL) {
1630*c7ef0cfcSnicm 	    _tracef("_nc_mouse_parse: no valid event");
1631*c7ef0cfcSnicm 	} else {
163292dd1ec0Smillert 	    _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1633*c7ef0cfcSnicm 		    RunParams(sp, eventp, first_valid),
163492dd1ec0Smillert 		    runcount);
163581d8c4e1Snicm 	    _nc_unlock_global(tracef);
163692dd1ec0Smillert 	}
1637*c7ef0cfcSnicm     }
163892dd1ec0Smillert #endif /* TRACE */
163992dd1ec0Smillert 
164092dd1ec0Smillert     /*
1641*c7ef0cfcSnicm      * Second pass; merge click runs.  We merge click events forward in the
1642*c7ef0cfcSnicm      * queue.  For example, double click can be changed to triple click.
164392dd1ec0Smillert      *
164492dd1ec0Smillert      * NOTE: There is a problem with this design!  If the application
164592dd1ec0Smillert      * allows enough click events to pile up in the circular queue so
164692dd1ec0Smillert      * they wrap around, it will cheerfully merge the newest forward
164792dd1ec0Smillert      * into the oldest, creating a bogus doubleclick and confusing
164892dd1ec0Smillert      * the queue-traversal logic rather badly.  Generally this won't
164992dd1ec0Smillert      * happen, because calling getmouse() marks old events invalid and
165092dd1ec0Smillert      * ineligible for merges.  The true solution to this problem would
165192dd1ec0Smillert      * be to timestamp each MEVENT and perform the obvious sanity check,
165292dd1ec0Smillert      * but the timer element would have to have sub-second resolution,
165392dd1ec0Smillert      * which would get us into portability trouble.
165492dd1ec0Smillert      */
1655*c7ef0cfcSnicm     first_invalid = NULL;
1656*c7ef0cfcSnicm     endLoop = (first_valid == NULL);
1657*c7ef0cfcSnicm     ep = first_valid;
1658*c7ef0cfcSnicm     while (!endLoop) {
1659*c7ef0cfcSnicm 	next = NEXT(ep);
166092dd1ec0Smillert 
1661*c7ef0cfcSnicm 	if (next == eventp) {
1662*c7ef0cfcSnicm 	    /* Will end the loop, but check event type and compact before */
1663*c7ef0cfcSnicm 	    endLoop = TRUE;
1664*c7ef0cfcSnicm 	} else if (!ValidEvent(next)) {
166592dd1ec0Smillert 	    continue;
1666*c7ef0cfcSnicm 	} else {
166792dd1ec0Smillert 	    /* merge click events forward */
166881d8c4e1Snicm 	    if ((ep->bstate & BUTTON_CLICKED)
1669*c7ef0cfcSnicm 		&& (next->bstate & BUTTON_CLICKED)) {
1670*c7ef0cfcSnicm 		merge = FALSE;
167181d8c4e1Snicm 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1672*c7ef0cfcSnicm 		    if ((sp->_mouse_mask2 & MASK_DOUBLE_CLICK(b))
1673*c7ef0cfcSnicm 			&& (ep->bstate & MASK_CLICK(b))
1674*c7ef0cfcSnicm 			&& (next->bstate & MASK_CLICK(b))) {
1675*c7ef0cfcSnicm 			next->bstate &= ~MASK_CLICK(b);
1676*c7ef0cfcSnicm 			next->bstate |= MASK_DOUBLE_CLICK(b);
167792dd1ec0Smillert 			merge = TRUE;
167892dd1ec0Smillert 		    }
167992dd1ec0Smillert 		}
1680*c7ef0cfcSnicm 		if (merge) {
1681*c7ef0cfcSnicm 		    Invalidate(ep);
1682*c7ef0cfcSnicm 		}
168392dd1ec0Smillert 	    }
168492dd1ec0Smillert 
168592dd1ec0Smillert 	    /* merge double-click events forward */
168681d8c4e1Snicm 	    if ((ep->bstate & BUTTON_DOUBLE_CLICKED)
1687*c7ef0cfcSnicm 		&& (next->bstate & BUTTON_CLICKED)) {
1688*c7ef0cfcSnicm 		merge = FALSE;
168981d8c4e1Snicm 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1690*c7ef0cfcSnicm 		    if ((sp->_mouse_mask2 & MASK_TRIPLE_CLICK(b))
1691*c7ef0cfcSnicm 			&& (ep->bstate & MASK_DOUBLE_CLICK(b))
1692*c7ef0cfcSnicm 			&& (next->bstate & MASK_CLICK(b))) {
1693*c7ef0cfcSnicm 			next->bstate &= ~MASK_CLICK(b);
1694*c7ef0cfcSnicm 			next->bstate |= MASK_TRIPLE_CLICK(b);
169592dd1ec0Smillert 			merge = TRUE;
169692dd1ec0Smillert 		    }
169792dd1ec0Smillert 		}
1698*c7ef0cfcSnicm 		if (merge) {
1699*c7ef0cfcSnicm 		    Invalidate(ep);
170092dd1ec0Smillert 		}
170192dd1ec0Smillert 	    }
1702*c7ef0cfcSnicm 	}
1703*c7ef0cfcSnicm 
1704*c7ef0cfcSnicm 	/* Discard event if it does not match event mask */
1705*c7ef0cfcSnicm 	if (!(ep->bstate & sp->_mouse_mask2)) {
1706*c7ef0cfcSnicm 	    Invalidate(ep);
1707*c7ef0cfcSnicm 	}
1708*c7ef0cfcSnicm 
1709*c7ef0cfcSnicm 	/* Compact valid events */
1710*c7ef0cfcSnicm 	if (!ValidEvent(ep)) {
1711*c7ef0cfcSnicm 	    if (ep == first_valid) {
1712*c7ef0cfcSnicm 		first_valid = next;
1713*c7ef0cfcSnicm 	    } else if (first_invalid == NULL) {
1714*c7ef0cfcSnicm 		first_invalid = ep;
1715*c7ef0cfcSnicm 	    }
1716*c7ef0cfcSnicm 	} else if (first_invalid != NULL) {
1717*c7ef0cfcSnicm 	    *first_invalid = *ep;
1718*c7ef0cfcSnicm 	    Invalidate(ep);
1719*c7ef0cfcSnicm 	    first_invalid = NEXT(first_invalid);
1720*c7ef0cfcSnicm 	}
1721*c7ef0cfcSnicm 
1722*c7ef0cfcSnicm 	ep = next;
1723*c7ef0cfcSnicm     }
1724*c7ef0cfcSnicm 
1725*c7ef0cfcSnicm     if (first_invalid == NULL) {
1726*c7ef0cfcSnicm 	first_invalid = eventp;
1727*c7ef0cfcSnicm     }
1728*c7ef0cfcSnicm     sp->_mouse_eventp = first_invalid;
172992dd1ec0Smillert 
173092dd1ec0Smillert #ifdef TRACE
1731*c7ef0cfcSnicm     if (first_valid != NULL) {
173281d8c4e1Snicm 	if (USE_TRACEF(TRACE_IEVENT)) {
173381d8c4e1Snicm 	    _trace_slot(sp, "after mouse event queue compaction:");
173492dd1ec0Smillert 	    _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1735*c7ef0cfcSnicm 		    RunParams(sp, first_invalid, first_valid),
173692dd1ec0Smillert 		    runcount);
173781d8c4e1Snicm 	    _nc_unlock_global(tracef);
173892dd1ec0Smillert 	}
1739*c7ef0cfcSnicm 	for (ep = first_valid; ep != first_invalid; ep = NEXT(ep)) {
1740*c7ef0cfcSnicm 	    if (ValidEvent(ep))
174123bb66c4Smillert 		TR(MY_TRACE,
174223bb66c4Smillert 		   ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
174381d8c4e1Snicm 		    _nc_tracemouse(sp, ep),
174481d8c4e1Snicm 		    (long) IndexEV(sp, ep)));
1745*c7ef0cfcSnicm 	}
1746*c7ef0cfcSnicm     }
174792dd1ec0Smillert #endif /* TRACE */
174892dd1ec0Smillert 
174992dd1ec0Smillert     /* after all this, do we have a valid event? */
1750*c7ef0cfcSnicm     ep = PREV(first_invalid);
1751*c7ef0cfcSnicm     return ValidEvent(ep) && ((ep->bstate & sp->_mouse_mask) != 0);
175292dd1ec0Smillert }
175392dd1ec0Smillert 
175423bb66c4Smillert static void
_nc_mouse_wrap(SCREEN * sp)175581d8c4e1Snicm _nc_mouse_wrap(SCREEN *sp)
175692dd1ec0Smillert /* release mouse -- called by endwin() before shellout/exit */
175792dd1ec0Smillert {
175892dd1ec0Smillert     TR(MY_TRACE, ("_nc_mouse_wrap() called"));
175992dd1ec0Smillert 
176081d8c4e1Snicm     switch (sp->_mouse_type) {
176192dd1ec0Smillert     case M_XTERM:
176281d8c4e1Snicm 	if (sp->_mouse_mask)
176381d8c4e1Snicm 	    mouse_activate(sp, FALSE);
176492dd1ec0Smillert 	break;
176592dd1ec0Smillert #if USE_GPM_SUPPORT
176692dd1ec0Smillert 	/* GPM: pass all mouse events to next client */
176792dd1ec0Smillert     case M_GPM:
176881d8c4e1Snicm 	if (sp->_mouse_mask)
176981d8c4e1Snicm 	    mouse_activate(sp, FALSE);
177092dd1ec0Smillert 	break;
177192dd1ec0Smillert #endif
177281d8c4e1Snicm #if USE_SYSMOUSE
177381d8c4e1Snicm     case M_SYSMOUSE:
177481d8c4e1Snicm 	mouse_activate(sp, FALSE);
177581d8c4e1Snicm 	break;
177681d8c4e1Snicm #endif
1777*c7ef0cfcSnicm #ifdef USE_TERM_DRIVER
1778*c7ef0cfcSnicm     case M_TERM_DRIVER:
1779*c7ef0cfcSnicm 	mouse_activate(sp, FALSE);
1780*c7ef0cfcSnicm 	break;
1781*c7ef0cfcSnicm #endif
178281d8c4e1Snicm     case M_NONE:
178381d8c4e1Snicm 	break;
178492dd1ec0Smillert     }
178592dd1ec0Smillert }
178692dd1ec0Smillert 
178723bb66c4Smillert static void
_nc_mouse_resume(SCREEN * sp)178881d8c4e1Snicm _nc_mouse_resume(SCREEN *sp)
178992dd1ec0Smillert /* re-connect to mouse -- called by doupdate() after shellout */
179092dd1ec0Smillert {
179192dd1ec0Smillert     TR(MY_TRACE, ("_nc_mouse_resume() called"));
179292dd1ec0Smillert 
179381d8c4e1Snicm     switch (sp->_mouse_type) {
179481d8c4e1Snicm     case M_XTERM:
179592dd1ec0Smillert 	/* xterm: re-enable reporting */
179681d8c4e1Snicm 	if (sp->_mouse_mask)
179781d8c4e1Snicm 	    mouse_activate(sp, TRUE);
179881d8c4e1Snicm 	break;
179992dd1ec0Smillert 
180081d8c4e1Snicm #if USE_GPM_SUPPORT
180181d8c4e1Snicm     case M_GPM:
180292dd1ec0Smillert 	/* GPM: reclaim our event set */
180381d8c4e1Snicm 	if (sp->_mouse_mask)
180481d8c4e1Snicm 	    mouse_activate(sp, TRUE);
180581d8c4e1Snicm 	break;
180681d8c4e1Snicm #endif
180781d8c4e1Snicm 
180881d8c4e1Snicm #if USE_SYSMOUSE
180981d8c4e1Snicm     case M_SYSMOUSE:
181081d8c4e1Snicm 	mouse_activate(sp, TRUE);
181181d8c4e1Snicm 	break;
181281d8c4e1Snicm #endif
1813*c7ef0cfcSnicm 
1814*c7ef0cfcSnicm #ifdef USE_TERM_DRIVER
1815*c7ef0cfcSnicm     case M_TERM_DRIVER:
1816*c7ef0cfcSnicm 	mouse_activate(sp, TRUE);
1817*c7ef0cfcSnicm 	break;
1818*c7ef0cfcSnicm #endif
1819*c7ef0cfcSnicm 
182081d8c4e1Snicm     case M_NONE:
182181d8c4e1Snicm 	break;
182281d8c4e1Snicm     }
182392dd1ec0Smillert }
182492dd1ec0Smillert 
182592dd1ec0Smillert /**************************************************************************
182692dd1ec0Smillert  *
182792dd1ec0Smillert  * Mouse interface entry points for the API
182892dd1ec0Smillert  *
182992dd1ec0Smillert  **************************************************************************/
183092dd1ec0Smillert 
1831*c7ef0cfcSnicm NCURSES_EXPORT(int)
NCURSES_SP_NAME(getmouse)1832*c7ef0cfcSnicm NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent)
183392dd1ec0Smillert {
1834*c7ef0cfcSnicm     int result = ERR;
1835*c7ef0cfcSnicm     MEVENT *eventp;
183692dd1ec0Smillert 
1837*c7ef0cfcSnicm     T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));
1838*c7ef0cfcSnicm 
1839*c7ef0cfcSnicm     if ((aevent != 0) &&
1840*c7ef0cfcSnicm 	(SP_PARM != 0) &&
1841*c7ef0cfcSnicm 	(SP_PARM->_mouse_type != M_NONE) &&
1842*c7ef0cfcSnicm 	(eventp = SP_PARM->_mouse_eventp) != 0) {
184392dd1ec0Smillert 	/* compute the current-event pointer */
184492dd1ec0Smillert 	MEVENT *prev = PREV(eventp);
184592dd1ec0Smillert 
1846*c7ef0cfcSnicm 	/*
1847*c7ef0cfcSnicm 	 * Discard events not matching mask (there could be still some if
1848*c7ef0cfcSnicm 	 * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns
1849*c7ef0cfcSnicm 	 * false).
1850*c7ef0cfcSnicm 	 */
1851*c7ef0cfcSnicm 	while (ValidEvent(prev) && (!(prev->bstate & SP_PARM->_mouse_mask2))) {
1852*c7ef0cfcSnicm 	    Invalidate(prev);
1853*c7ef0cfcSnicm 	    prev = PREV(prev);
1854*c7ef0cfcSnicm 	}
1855*c7ef0cfcSnicm 	if (ValidEvent(prev)) {
185692dd1ec0Smillert 	    /* copy the event we find there */
185792dd1ec0Smillert 	    *aevent = *prev;
185892dd1ec0Smillert 
185992dd1ec0Smillert 	    TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
1860*c7ef0cfcSnicm 			      _nc_tracemouse(SP_PARM, prev),
1861*c7ef0cfcSnicm 			      (long) IndexEV(SP_PARM, prev)));
186292dd1ec0Smillert 
1863*c7ef0cfcSnicm 	    Invalidate(prev);	/* so the queue slot becomes free */
1864*c7ef0cfcSnicm 	    SP_PARM->_mouse_eventp = prev;
1865*c7ef0cfcSnicm 	    result = OK;
1866*c7ef0cfcSnicm 	} else {
1867*c7ef0cfcSnicm 	    /* Reset the provided event */
1868*c7ef0cfcSnicm 	    aevent->bstate = 0;
1869*c7ef0cfcSnicm 	    Invalidate(aevent);
1870*c7ef0cfcSnicm 	    aevent->x = 0;
1871*c7ef0cfcSnicm 	    aevent->y = 0;
1872*c7ef0cfcSnicm 	    aevent->z = 0;
187392dd1ec0Smillert 	}
1874*c7ef0cfcSnicm     }
1875*c7ef0cfcSnicm     returnCode(result);
187692dd1ec0Smillert }
187792dd1ec0Smillert 
1878*c7ef0cfcSnicm #if NCURSES_SP_FUNCS
187981d8c4e1Snicm /* grab a copy of the current mouse event */
188084af20ceSmillert NCURSES_EXPORT(int)
getmouse(MEVENT * aevent)188181d8c4e1Snicm getmouse(MEVENT * aevent)
188292dd1ec0Smillert {
1883*c7ef0cfcSnicm     return NCURSES_SP_NAME(getmouse) (CURRENT_SCREEN, aevent);
188481d8c4e1Snicm }
1885*c7ef0cfcSnicm #endif
188681d8c4e1Snicm 
1887*c7ef0cfcSnicm NCURSES_EXPORT(int)
NCURSES_SP_NAME(ungetmouse)1888*c7ef0cfcSnicm NCURSES_SP_NAME(ungetmouse) (NCURSES_SP_DCLx MEVENT * aevent)
188981d8c4e1Snicm {
189081d8c4e1Snicm     int result = ERR;
1891*c7ef0cfcSnicm     MEVENT *eventp;
189281d8c4e1Snicm 
1893*c7ef0cfcSnicm     T((T_CALLED("ungetmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));
189481d8c4e1Snicm 
1895*c7ef0cfcSnicm     if (aevent != 0 &&
1896*c7ef0cfcSnicm 	SP_PARM != 0 &&
1897*c7ef0cfcSnicm 	(eventp = SP_PARM->_mouse_eventp) != 0) {
189881d8c4e1Snicm 
189992dd1ec0Smillert 	/* stick the given event in the next-free slot */
190092dd1ec0Smillert 	*eventp = *aevent;
190192dd1ec0Smillert 
190292dd1ec0Smillert 	/* bump the next-free pointer into the circular list */
1903*c7ef0cfcSnicm 	SP_PARM->_mouse_eventp = NEXT(eventp);
190492dd1ec0Smillert 
190592dd1ec0Smillert 	/* push back the notification event on the keyboard queue */
1906*c7ef0cfcSnicm 	result = NCURSES_SP_NAME(ungetch) (NCURSES_SP_ARGx KEY_MOUSE);
190781d8c4e1Snicm     }
190881d8c4e1Snicm     returnCode(result);
190981d8c4e1Snicm }
191081d8c4e1Snicm 
1911*c7ef0cfcSnicm #if NCURSES_SP_FUNCS
191281d8c4e1Snicm /* enqueue a synthesized mouse event to be seen by the next wgetch() */
191381d8c4e1Snicm NCURSES_EXPORT(int)
ungetmouse(MEVENT * aevent)191481d8c4e1Snicm ungetmouse(MEVENT * aevent)
191581d8c4e1Snicm {
1916*c7ef0cfcSnicm     return NCURSES_SP_NAME(ungetmouse) (CURRENT_SCREEN, aevent);
191792dd1ec0Smillert }
1918*c7ef0cfcSnicm #endif
191992dd1ec0Smillert 
192084af20ceSmillert NCURSES_EXPORT(mmask_t)
NCURSES_SP_NAME(mousemask)1921*c7ef0cfcSnicm NCURSES_SP_NAME(mousemask) (NCURSES_SP_DCLx mmask_t newmask, mmask_t * oldmask)
192292dd1ec0Smillert /* set the mouse event mask */
192392dd1ec0Smillert {
192492dd1ec0Smillert     mmask_t result = 0;
192592dd1ec0Smillert 
1926*c7ef0cfcSnicm     T((T_CALLED("mousemask(%p,%#lx,%p)"),
1927*c7ef0cfcSnicm        (void *) SP_PARM,
1928*c7ef0cfcSnicm        (unsigned long) newmask,
1929*c7ef0cfcSnicm        (void *) oldmask));
193092dd1ec0Smillert 
1931*c7ef0cfcSnicm     if (SP_PARM != 0) {
193292dd1ec0Smillert 	if (oldmask)
1933*c7ef0cfcSnicm 	    *oldmask = SP_PARM->_mouse_mask;
193492dd1ec0Smillert 
1935*c7ef0cfcSnicm 	if (newmask || SP_PARM->_mouse_initialized) {
1936*c7ef0cfcSnicm 	    _nc_mouse_init(SP_PARM);
1937*c7ef0cfcSnicm 
1938*c7ef0cfcSnicm 	    if (SP_PARM->_mouse_type != M_NONE) {
1939*c7ef0cfcSnicm 		int b;
1940*c7ef0cfcSnicm 
194181d8c4e1Snicm 		result = newmask &
194281d8c4e1Snicm 		    (REPORT_MOUSE_POSITION
194381d8c4e1Snicm 		     | BUTTON_ALT
194481d8c4e1Snicm 		     | BUTTON_CTRL
194581d8c4e1Snicm 		     | BUTTON_SHIFT
194681d8c4e1Snicm 		     | BUTTON_PRESSED
194781d8c4e1Snicm 		     | BUTTON_RELEASED
194881d8c4e1Snicm 		     | BUTTON_CLICKED
194981d8c4e1Snicm 		     | BUTTON_DOUBLE_CLICKED
195081d8c4e1Snicm 		     | BUTTON_TRIPLE_CLICKED);
195192dd1ec0Smillert 
1952*c7ef0cfcSnicm 		mouse_activate(SP_PARM, (bool) (result != 0));
195392dd1ec0Smillert 
1954*c7ef0cfcSnicm 		SP_PARM->_mouse_mask = result;
1955*c7ef0cfcSnicm 		SP_PARM->_mouse_mask2 = result;
1956*c7ef0cfcSnicm 
1957*c7ef0cfcSnicm 		/*
1958*c7ef0cfcSnicm 		 * Make a mask corresponding to the states we will need to
1959*c7ef0cfcSnicm 		 * retain (temporarily) while building up the state that the
1960*c7ef0cfcSnicm 		 * user asked for.
1961*c7ef0cfcSnicm 		 */
1962*c7ef0cfcSnicm 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1963*c7ef0cfcSnicm 		    if (SP_PARM->_mouse_mask2 & MASK_TRIPLE_CLICK(b))
1964*c7ef0cfcSnicm 			SP_PARM->_mouse_mask2 |= MASK_DOUBLE_CLICK(b);
1965*c7ef0cfcSnicm 		    if (SP_PARM->_mouse_mask2 & MASK_DOUBLE_CLICK(b))
1966*c7ef0cfcSnicm 			SP_PARM->_mouse_mask2 |= MASK_CLICK(b);
1967*c7ef0cfcSnicm 		    if (SP_PARM->_mouse_mask2 & MASK_CLICK(b))
1968*c7ef0cfcSnicm 			SP_PARM->_mouse_mask2 |= (MASK_PRESS(b) |
1969*c7ef0cfcSnicm 						  MASK_RELEASE(b));
197092dd1ec0Smillert 		}
197181d8c4e1Snicm 	    }
197281d8c4e1Snicm 	}
197392dd1ec0Smillert     }
1974*c7ef0cfcSnicm     returnMMask(result);
1975*c7ef0cfcSnicm }
1976*c7ef0cfcSnicm 
1977*c7ef0cfcSnicm #if NCURSES_SP_FUNCS
1978*c7ef0cfcSnicm NCURSES_EXPORT(mmask_t)
mousemask(mmask_t newmask,mmask_t * oldmask)1979*c7ef0cfcSnicm mousemask(mmask_t newmask, mmask_t * oldmask)
1980*c7ef0cfcSnicm {
1981*c7ef0cfcSnicm     return NCURSES_SP_NAME(mousemask) (CURRENT_SCREEN, newmask, oldmask);
1982*c7ef0cfcSnicm }
1983*c7ef0cfcSnicm #endif
198492dd1ec0Smillert 
198584af20ceSmillert NCURSES_EXPORT(bool)
wenclose(const WINDOW * win,int y,int x)198623bb66c4Smillert wenclose(const WINDOW *win, int y, int x)
198792dd1ec0Smillert /* check to see if given window encloses given screen location */
198892dd1ec0Smillert {
198981d8c4e1Snicm     bool result = FALSE;
199081d8c4e1Snicm 
1991*c7ef0cfcSnicm     T((T_CALLED("wenclose(%p,%d,%d)"), (const void *) win, y, x));
199281d8c4e1Snicm 
199381d8c4e1Snicm     if (win != 0) {
199492dd1ec0Smillert 	y -= win->_yoffset;
199581d8c4e1Snicm 	result = ((win->_begy <= y &&
199692dd1ec0Smillert 		   win->_begx <= x &&
199792dd1ec0Smillert 		   (win->_begx + win->_maxx) >= x &&
199892dd1ec0Smillert 		   (win->_begy + win->_maxy) >= y) ? TRUE : FALSE);
199992dd1ec0Smillert     }
200081d8c4e1Snicm     returnBool(result);
200192dd1ec0Smillert }
200292dd1ec0Smillert 
200384af20ceSmillert NCURSES_EXPORT(int)
NCURSES_SP_NAME(mouseinterval)2004*c7ef0cfcSnicm NCURSES_SP_NAME(mouseinterval) (NCURSES_SP_DCLx int maxclick)
200592dd1ec0Smillert /* set the maximum mouse interval within which to recognize a click */
200692dd1ec0Smillert {
200792dd1ec0Smillert     int oldval;
200892dd1ec0Smillert 
2009*c7ef0cfcSnicm     T((T_CALLED("mouseinterval(%p,%d)"), (void *) SP_PARM, maxclick));
201081d8c4e1Snicm 
2011*c7ef0cfcSnicm     if (SP_PARM != 0) {
2012*c7ef0cfcSnicm 	oldval = SP_PARM->_maxclick;
201392dd1ec0Smillert 	if (maxclick >= 0)
2014*c7ef0cfcSnicm 	    SP_PARM->_maxclick = maxclick;
201592dd1ec0Smillert     } else {
201692dd1ec0Smillert 	oldval = DEFAULT_MAXCLICK;
201792dd1ec0Smillert     }
201892dd1ec0Smillert 
201981d8c4e1Snicm     returnCode(oldval);
202092dd1ec0Smillert }
202192dd1ec0Smillert 
2022*c7ef0cfcSnicm #if NCURSES_SP_FUNCS
2023*c7ef0cfcSnicm NCURSES_EXPORT(int)
mouseinterval(int maxclick)2024*c7ef0cfcSnicm mouseinterval(int maxclick)
2025*c7ef0cfcSnicm {
2026*c7ef0cfcSnicm     return NCURSES_SP_NAME(mouseinterval) (CURRENT_SCREEN, maxclick);
2027*c7ef0cfcSnicm }
2028*c7ef0cfcSnicm #endif
2029*c7ef0cfcSnicm 
203092dd1ec0Smillert /* This may be used by other routines to ask for the existence of mouse
203192dd1ec0Smillert    support */
2032*c7ef0cfcSnicm NCURSES_EXPORT(bool)
_nc_has_mouse(SCREEN * sp)2033*c7ef0cfcSnicm _nc_has_mouse(SCREEN *sp)
203423bb66c4Smillert {
2035*c7ef0cfcSnicm     return (((0 == sp) || (sp->_mouse_type == M_NONE)) ? FALSE : TRUE);
203692dd1ec0Smillert }
203792dd1ec0Smillert 
203884af20ceSmillert NCURSES_EXPORT(bool)
NCURSES_SP_NAME(has_mouse)2039*c7ef0cfcSnicm NCURSES_SP_NAME(has_mouse) (NCURSES_SP_DCL0)
2040*c7ef0cfcSnicm {
2041*c7ef0cfcSnicm     return _nc_has_mouse(SP_PARM);
2042*c7ef0cfcSnicm }
2043*c7ef0cfcSnicm 
2044*c7ef0cfcSnicm #if NCURSES_SP_FUNCS
2045*c7ef0cfcSnicm NCURSES_EXPORT(bool)
has_mouse(void)2046*c7ef0cfcSnicm has_mouse(void)
2047*c7ef0cfcSnicm {
2048*c7ef0cfcSnicm     return _nc_has_mouse(CURRENT_SCREEN);
2049*c7ef0cfcSnicm }
2050*c7ef0cfcSnicm #endif
2051*c7ef0cfcSnicm 
2052*c7ef0cfcSnicm NCURSES_EXPORT(bool)
wmouse_trafo(const WINDOW * win,int * pY,int * pX,bool to_screen)205381d8c4e1Snicm wmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen)
205492dd1ec0Smillert {
205592dd1ec0Smillert     bool result = FALSE;
205692dd1ec0Smillert 
2057*c7ef0cfcSnicm     T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"),
2058*c7ef0cfcSnicm        (const void *) win,
2059*c7ef0cfcSnicm        (void *) pY,
2060*c7ef0cfcSnicm        (void *) pX,
2061*c7ef0cfcSnicm        to_screen));
206281d8c4e1Snicm 
206323bb66c4Smillert     if (win && pY && pX) {
206423bb66c4Smillert 	int y = *pY;
206523bb66c4Smillert 	int x = *pX;
206692dd1ec0Smillert 
206723bb66c4Smillert 	if (to_screen) {
206892dd1ec0Smillert 	    y += win->_begy + win->_yoffset;
206992dd1ec0Smillert 	    x += win->_begx;
207092dd1ec0Smillert 	    if (wenclose(win, y, x))
207192dd1ec0Smillert 		result = TRUE;
207223bb66c4Smillert 	} else {
207323bb66c4Smillert 	    if (wenclose(win, y, x)) {
207492dd1ec0Smillert 		y -= (win->_begy + win->_yoffset);
207592dd1ec0Smillert 		x -= win->_begx;
207692dd1ec0Smillert 		result = TRUE;
207792dd1ec0Smillert 	    }
207892dd1ec0Smillert 	}
207923bb66c4Smillert 	if (result) {
208092dd1ec0Smillert 	    *pX = x;
208192dd1ec0Smillert 	    *pY = y;
208292dd1ec0Smillert 	}
208392dd1ec0Smillert     }
208481d8c4e1Snicm     returnBool(result);
208592dd1ec0Smillert }
2086