xref: /freebsd/contrib/ncurses/ncurses/base/lib_mouse.c (revision 68ad2b0d7af2a3571c4abac9afa712f9b09b721c)
1 /****************************************************************************
2  * Copyright 2018-2024,2025 Thomas E. Dickey                                *
3  * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /****************************************************************************
31  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
32  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
33  *     and: Thomas E. Dickey                        1996-on                 *
34  *     and: Juergen Pfeifer                         2008                    *
35  ****************************************************************************/
36 
37 /*
38  * This module is intended to encapsulate ncurses's interface to pointing
39  * devices.
40  *
41  * The primary method used is xterm's internal mouse-tracking facility.
42  * Additional methods depend on the platform:
43  *	Alessandro Rubini's GPM server (Linux)
44  *	sysmouse (FreeBSD)
45  *	special-purpose mouse interface for OS/2 EMX.
46  *
47  * Notes for implementers of new mouse-interface methods:
48  *
49  * The code is logically split into a lower level that accepts event reports
50  * in a device-dependent format and an upper level that parses mouse gestures
51  * and filters events.  The mediating data structure is a circular queue of
52  * MEVENT structures.
53  *
54  * Functionally, the lower level's job is to pick up primitive events and
55  * put them on the circular queue.  This can happen in one of two ways:
56  * either (a) _nc_mouse_event() detects a series of incoming mouse reports
57  * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
58  * the keyboard input stream and calls _nc_mouse_inline to queue up a series
59  * of adjacent mouse reports.
60  *
61  * In either case, _nc_mouse_parse() should be called after the series is
62  * accepted to parse the digested mouse reports (low-level MEVENTs) into
63  * a gesture (a high-level or composite MEVENT).
64  *
65  * Don't be too shy about adding new event types or modifiers, if you can find
66  * room for them in the 32-bit mask.  The API is written so that users get
67  * feedback on which theoretical event types they won't see when they call
68  * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
69  * used yet, and a couple of bits open at the high end.
70  */
71 
72 #ifdef __EMX__
73 #  define  INCL_DOS
74 #  define  INCL_VIO
75 #  define  INCL_KBD
76 #  define  INCL_MOU
77 #  define  INCL_DOSPROCESS
78 #  include <os2.h>		/* Need to include before the others */
79 #endif
80 
81 #include <curses.priv.h>
82 
83 #ifndef CUR
84 #define CUR SP_TERMTYPE
85 #endif
86 
87 MODULE_ID("$Id: lib_mouse.c,v 1.219 2025/12/30 17:23:24 tom Exp $")
88 
89 #include <tic.h>
90 
91 #if USE_GPM_SUPPORT
92 #include <linux/keyboard.h>	/* defines KG_* macros */
93 
94 #ifdef HAVE_LIBDL
95 /* use dynamic loader to avoid linkage dependency */
96 #include <dlfcn.h>
97 
98 #ifdef RTLD_NOW
99 #define my_RTLD RTLD_NOW
100 #else
101 #ifdef RTLD_LAZY
102 #define my_RTLD RTLD_LAZY
103 #else
104 make an error
105 #endif
106 #endif				/* RTLD_NOW */
107 #endif				/* HAVE_LIBDL */
108 
109 #endif				/* USE_GPM_SUPPORT */
110 
111 #if USE_SYSMOUSE
112 #undef buttons			/* symbol conflict in consio.h */
113 #undef mouse_info		/* symbol conflict in consio.h */
114 #include <osreldate.h>
115 #if defined(__DragonFly_version) || (defined(__FreeBSD__) && (__FreeBSD_version >= 400017))
116 #include <sys/consio.h>
117 #include <sys/fbio.h>
118 #else
119 #include <machine/console.h>
120 #endif
121 #endif				/* use_SYSMOUSE */
122 
123 #if USE_KLIBC_MOUSE
124 #include <sys/socket.h>
125 #define pipe(handles) socketpair(AF_LOCAL, SOCK_STREAM, 0, handles)
126 #define DosWrite(hfile, pbuffer, cbwrite, pcbactual) \
127 		write(hfile, pbuffer, cbwrite)
128 #define DosExit(action, result )	/* do nothing */
129 #define DosCreateThread(ptid, pfn, param, flag, cbStack) \
130 		(*(ptid) = _beginthread(pfn, NULL, cbStack, \
131 					(void *)param), (*(ptid) == -1))
132 #endif
133 
134 #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
135 
136 #define	MASK_RELEASE(x)		(mmask_t) NCURSES_MOUSE_MASK(x, 001)
137 #define	MASK_PRESS(x)		(mmask_t) NCURSES_MOUSE_MASK(x, 002)
138 #define	MASK_CLICK(x)		(mmask_t) NCURSES_MOUSE_MASK(x, 004)
139 #define	MASK_DOUBLE_CLICK(x)	(mmask_t) NCURSES_MOUSE_MASK(x, 010)
140 #define	MASK_TRIPLE_CLICK(x)	(mmask_t) NCURSES_MOUSE_MASK(x, 020)
141 #define	MASK_RESERVED_EVENT(x)	(mmask_t) NCURSES_MOUSE_MASK(x, 040)
142 
143 #if NCURSES_MOUSE_VERSION == 1
144 
145 #define BUTTON_CLICKED        (BUTTON1_CLICKED        | BUTTON2_CLICKED        | BUTTON3_CLICKED        | BUTTON4_CLICKED)
146 #define BUTTON_PRESSED        (BUTTON1_PRESSED        | BUTTON2_PRESSED        | BUTTON3_PRESSED        | BUTTON4_PRESSED)
147 #define BUTTON_RELEASED       (BUTTON1_RELEASED       | BUTTON2_RELEASED       | BUTTON3_RELEASED       | BUTTON4_RELEASED)
148 #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED)
149 #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED)
150 
151 #define MAX_BUTTONS  4
152 
153 #else
154 
155 #define BUTTON_CLICKED        (BUTTON1_CLICKED        | BUTTON2_CLICKED        | BUTTON3_CLICKED        | BUTTON4_CLICKED        | BUTTON5_CLICKED)
156 #define BUTTON_PRESSED        (BUTTON1_PRESSED        | BUTTON2_PRESSED        | BUTTON3_PRESSED        | BUTTON4_PRESSED        | BUTTON5_PRESSED)
157 #define BUTTON_RELEASED       (BUTTON1_RELEASED       | BUTTON2_RELEASED       | BUTTON3_RELEASED       | BUTTON4_RELEASED       | BUTTON5_RELEASED)
158 #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED)
159 #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED)
160 
161 #if NCURSES_MOUSE_VERSION == 2
162 #define MAX_BUTTONS  5
163 #else
164 #define MAX_BUTTONS  11
165 #endif
166 
167 #endif
168 
169 #define INVALID_EVENT	-1
170 #define NORMAL_EVENT	0
171 
172 #define ValidEvent(ep) ((ep)->id != INVALID_EVENT)
173 #define Invalidate(ep) (ep)->id = INVALID_EVENT
174 
175 #if USE_GPM_SUPPORT
176 
177 #ifndef LIBGPM_SONAME
178 #define LIBGPM_SONAME "libgpm.so"
179 #endif
180 
181 #define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(sp->_dlopen_gpm, #name))
182 
183 #endif				/* USE_GPM_SUPPORT */
184 
185 static bool _nc_mouse_parse(SCREEN *, int);
186 static void _nc_mouse_resume(SCREEN *);
187 static void _nc_mouse_wrap(SCREEN *);
188 
189 /* maintain a circular list of mouse events */
190 
191 #define FirstEV(sp)	((sp)->_mouse_events)
192 #define LastEV(sp)	((sp)->_mouse_events + EV_MAX - 1)
193 
194 #undef  NEXT
195 #define NEXT(ep)	((ep >= LastEV(SP_PARM)) \
196 			 ? FirstEV(SP_PARM) \
197 			 : ep + 1)
198 
199 #undef  PREV
200 #define PREV(ep)	((ep <= FirstEV(SP_PARM)) \
201 			 ? LastEV(SP_PARM) \
202 			 : ep - 1)
203 
204 #define IndexEV(sp, ep)	(ep - FirstEV(sp))
205 
206 #define RunParams(sp, eventp, runp) \
207 		(long) IndexEV(sp, runp), \
208 		(long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX
209 
210 #ifdef TRACE
211 static void
_trace_slot(SCREEN * sp,const char * tag)212 _trace_slot(SCREEN *sp, const char *tag)
213 {
214     MEVENT *ep;
215 
216     _tracef("%s", tag);
217 
218     for (ep = FirstEV(sp); ep <= LastEV(sp); ep++)
219 	_tracef("mouse event queue slot %ld = %s",
220 		(long) IndexEV(sp, ep),
221 		_nc_tracemouse(sp, ep));
222 }
223 #endif
224 
225 #if USE_EMX_MOUSE
226 
227 #  define TOP_ROW          0
228 #  define LEFT_COL         0
229 
230 #  define M_FD(sp) sp->_mouse_fd
231 
232 static void
write_event(SCREEN * sp,int down,int button,int x,int y)233 write_event(SCREEN *sp, int down, int button, int x, int y)
234 {
235     char buf[6];
236     unsigned long ignore;
237 
238     _nc_STRCPY(buf, "\033[M", sizeof(buf));	/* should be the same as key_mouse */
239     buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40);
240     buf[4] = ' ' + x - LEFT_COL + 1;
241     buf[5] = ' ' + y - TOP_ROW + 1;
242     DosWrite(sp->_emxmouse_wfd, buf, 6, &ignore);
243 }
244 
245 static void
246 #if USE_KLIBC_MOUSE
mouse_server(void * param)247 mouse_server(void *param)
248 #else
249 mouse_server(unsigned long param)
250 #endif
251 {
252     SCREEN *sp = (SCREEN *) param;
253     unsigned short fWait = MOU_WAIT;
254     /* NOPTRRECT mourt = { 0,0,24,79 }; */
255     MOUEVENTINFO mouev;
256     HMOU hmou;
257     unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN;
258     int nbuttons = 3;
259     int oldstate = 0;
260     char err[80];
261     unsigned long rc;
262 
263     /* open the handle for the mouse */
264     if (MouOpen(NULL, &hmou) == 0) {
265 	rc = MouSetEventMask(&mask, hmou);
266 	if (rc) {		/* retry with 2 buttons */
267 	    mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
268 	    rc = MouSetEventMask(&mask, hmou);
269 	    nbuttons = 2;
270 	}
271 	if (rc == 0 && MouDrawPtr(hmou) == 0) {
272 	    for (;;) {
273 		/* sit and wait on the event queue */
274 		rc = MouReadEventQue(&mouev, &fWait, hmou);
275 		if (rc) {
276 		    _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err))
277 				"Error reading mouse queue, rc=%lu.\r\n", rc);
278 		    break;
279 		}
280 		if (!sp->_emxmouse_activated)
281 		    goto finish;
282 
283 		/*
284 		 * OS/2 numbers a 3-button mouse inconsistently from other
285 		 * platforms:
286 		 *      1 = left
287 		 *      2 = right
288 		 *      3 = middle.
289 		 */
290 		if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN)
291 		    write_event(sp, mouev.fs & MOUSE_BN1_DOWN,
292 				sp->_emxmouse_buttons[1], mouev.col, mouev.row);
293 		if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN)
294 		    write_event(sp, mouev.fs & MOUSE_BN2_DOWN,
295 				sp->_emxmouse_buttons[3], mouev.col, mouev.row);
296 		if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN)
297 		    write_event(sp, mouev.fs & MOUSE_BN3_DOWN,
298 				sp->_emxmouse_buttons[2], mouev.col, mouev.row);
299 
300 	      finish:
301 		oldstate = mouev.fs;
302 	    }
303 	} else {
304 	    _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err))
305 			"Error setting event mask, buttons=%d, rc=%lu.\r\n",
306 			nbuttons, rc);
307 	}
308 
309 	DosWrite(2, err, strlen(err), &rc);
310 	MouClose(hmou);
311     }
312     DosExit(EXIT_THREAD, 0L);
313 }
314 
315 #endif /* USE_EMX_MOUSE */
316 
317 #if USE_SYSMOUSE
318 static void
sysmouse_server(SCREEN * sp)319 sysmouse_server(SCREEN *sp)
320 {
321     struct mouse_info the_mouse;
322     MEVENT *work;
323 
324     the_mouse.operation = MOUSE_GETINFO;
325     if (sp != 0
326 	&& sp->_mouse_fd >= 0
327 	&& sp->_sysmouse_tail < FIFO_SIZE
328 	&& ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
329 
330 	if (sp->_sysmouse_head > sp->_sysmouse_tail) {
331 	    sp->_sysmouse_tail = 0;
332 	    sp->_sysmouse_head = 0;
333 	}
334 	work = &(sp->_sysmouse_fifo[sp->_sysmouse_tail]);
335 	memset(work, 0, sizeof(*work));
336 	work->id = NORMAL_EVENT;	/* there's only one mouse... */
337 
338 	sp->_sysmouse_old_buttons = sp->_sysmouse_new_buttons;
339 	sp->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7;
340 
341 	if (sp->_sysmouse_new_buttons) {
342 	    if (sp->_sysmouse_new_buttons & 1)
343 		work->bstate |= BUTTON1_PRESSED;
344 	    if (sp->_sysmouse_new_buttons & 2)
345 		work->bstate |= BUTTON2_PRESSED;
346 	    if (sp->_sysmouse_new_buttons & 4)
347 		work->bstate |= BUTTON3_PRESSED;
348 	} else {
349 	    if (sp->_sysmouse_old_buttons & 1)
350 		work->bstate |= BUTTON1_RELEASED;
351 	    if (sp->_sysmouse_old_buttons & 2)
352 		work->bstate |= BUTTON2_RELEASED;
353 	    if (sp->_sysmouse_old_buttons & 4)
354 		work->bstate |= BUTTON3_RELEASED;
355 	}
356 
357 	/* for cosmetic bug in syscons.c on FreeBSD 3.[34] */
358 	the_mouse.operation = MOUSE_HIDE;
359 	ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
360 	the_mouse.operation = MOUSE_SHOW;
361 	ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
362 
363 	/*
364 	 * We're only interested if the button is pressed or released.
365 	 * FIXME: implement continuous event-tracking.
366 	 */
367 	if (sp->_sysmouse_new_buttons != sp->_sysmouse_old_buttons) {
368 	    sp->_sysmouse_tail += 1;
369 	}
370 	work->x = the_mouse.u.data.x / sp->_sysmouse_char_width;
371 	work->y = the_mouse.u.data.y / sp->_sysmouse_char_height;
372     }
373 }
374 
375 static void
handle_sysmouse(int sig GCC_UNUSED)376 handle_sysmouse(int sig GCC_UNUSED)
377 {
378     sysmouse_server(CURRENT_SCREEN);
379 }
380 #endif /* USE_SYSMOUSE */
381 
382 #if !defined(_NC_WINDOWS_NATIVE) || USE_NAMED_PIPES
383 #define xterm_kmous "\033[M"
384 
385 static void
init_xterm_mouse(SCREEN * sp)386 init_xterm_mouse(SCREEN *sp)
387 {
388     sp->_mouse_type = M_XTERM;
389     sp->_mouse_format = MF_X10;
390     sp->_mouse_xtermcap = tigetstr(UserCap(XM));
391     if (VALID_STRING(sp->_mouse_xtermcap)) {
392 	char *code = strstr(sp->_mouse_xtermcap, "[?");
393 	if (code != NULL) {
394 	    code += 2;
395 	    while ((*code >= '0') && (*code <= '9')) {
396 		char *next = code;
397 		while ((*next >= '0') && (*next <= '9')) {
398 		    ++next;
399 		}
400 		if (!strncmp(code, "1006", (size_t) (next - code))) {
401 		    sp->_mouse_format = MF_SGR1006;
402 		}
403 #ifdef EXP_XTERM_1005
404 		if (!strncmp(code, "1005", (size_t) (next - code))) {
405 		    sp->_mouse_format = MF_XTERM_1005;
406 		}
407 #endif
408 		if (*next == ';') {
409 		    while (*next == ';') {
410 			++next;
411 		    }
412 		    code = next;
413 		} else {
414 		    break;
415 		}
416 	    }
417 	}
418     } else {
419 	int code = tigetnum(UserCap(XM));
420 	switch (code) {
421 #ifdef EXP_XTERM_1005
422 	case 1005:
423 	    /* see "xterm+sm+1005" */
424 	    sp->_mouse_xtermcap = "\033[?1005;1000%?%p1%{1}%=%th%el%;";
425 	    sp->_mouse_format = MF_XTERM_1005;
426 	    break;
427 #endif
428 	case 1006:
429 	    /* see "xterm+sm+1006" */
430 	    sp->_mouse_xtermcap = "\033[?1006;1000%?%p1%{1}%=%th%el%;";
431 	    sp->_mouse_format = MF_SGR1006;
432 	    break;
433 	default:
434 	    sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
435 	    break;
436 	}
437     }
438 }
439 #endif
440 
441 static void
enable_xterm_mouse(SCREEN * sp,bool enable)442 enable_xterm_mouse(SCREEN *sp, bool enable)
443 {
444     TPUTS_TRACE(enable
445 		? "xterm mouse initialization"
446 		: "xterm mouse deinitialization");
447 #if USE_EMX_MOUSE
448     sp->_emxmouse_activated = enable;
449 #else
450     NCURSES_PUTP2("xterm-mouse", TIPARM_1(sp->_mouse_xtermcap, enable));
451 #endif
452     sp->_mouse_active = enable;
453 }
454 
455 #if USE_TERM_DRIVER
456 static void
enable_win32_mouse(SCREEN * sp,bool enable)457 enable_win32_mouse(SCREEN *sp, bool enable)
458 {
459 #if USE_NAMED_PIPES
460     enable_xterm_mouse(sp, enable);
461 #else
462     sp->_mouse_active = enable;
463 #endif
464 }
465 #endif
466 
467 #if USE_GPM_SUPPORT
468 static bool
allow_gpm_mouse(SCREEN * sp GCC_UNUSED)469 allow_gpm_mouse(SCREEN *sp GCC_UNUSED)
470 {
471     bool result = FALSE;
472 
473 #if USE_WEAK_SYMBOLS
474     /* Danger Robinson: do not use dlopen for libgpm if already loaded */
475     if ((Gpm_Wgetch) != 0) {
476 	if (!sp->_mouse_gpm_loaded) {
477 	    T(("GPM library was already dlopen'd, not by us"));
478 	}
479     } else
480 #endif
481 	/* GPM does printf's without checking if stdout is a terminal */
482     if (NC_ISATTY(fileno(stdout))) {
483 	const char *list = getenv("NCURSES_GPM_TERMS");
484 	const char *env = getenv("TERM");
485 	if (list != NULL) {
486 	    if (env != NULL) {
487 		result = _nc_name_match(list, env, "|:") ? TRUE : FALSE;
488 	    }
489 	} else {
490 	    /* GPM checks the beginning of the $TERM variable to decide if it
491 	     * should pass xterm events through.  There is no real advantage in
492 	     * allowing GPM to do this.  Recent versions relax that check, and
493 	     * pretend that GPM can work with any terminal having the kmous
494 	     * capability.  Perhaps that works for someone.  If so, they can
495 	     * set the environment variable (above).
496 	     */
497 	    if (env != NULL && strstr(env, "linux") != NULL) {
498 		result = TRUE;
499 	    }
500 	}
501     }
502     return result;
503 }
504 
505 #ifdef HAVE_LIBDL
506 static void
unload_gpm_library(SCREEN * sp)507 unload_gpm_library(SCREEN *sp)
508 {
509     if (sp->_dlopen_gpm != NULL) {
510 	T(("unload GPM library"));
511 	sp->_mouse_gpm_loaded = FALSE;
512 	sp->_mouse_fd = -1;
513     }
514 }
515 
516 static void
load_gpm_library(SCREEN * sp)517 load_gpm_library(SCREEN *sp)
518 {
519     sp->_mouse_gpm_found = FALSE;
520 
521     /*
522      * If we already had a successful dlopen, reuse it.
523      */
524     if (sp->_dlopen_gpm != NULL) {
525 	sp->_mouse_gpm_found = TRUE;
526 	sp->_mouse_gpm_loaded = TRUE;
527     } else if ((sp->_dlopen_gpm = dlopen(LIBGPM_SONAME, my_RTLD)) != NULL) {
528 #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__)
529 #pragma GCC diagnostic push
530 #pragma GCC diagnostic ignored "-Wpedantic"
531 #endif
532 	if (GET_DLSYM(gpm_fd) == NULL ||
533 	    GET_DLSYM(Gpm_Open) == NULL ||
534 	    GET_DLSYM(Gpm_Close) == NULL ||
535 	    GET_DLSYM(Gpm_GetEvent) == NULL) {
536 #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__)
537 #pragma GCC diagnostic pop
538 #endif
539 	    T(("GPM initialization failed: %s", dlerror()));
540 	    unload_gpm_library(sp);
541 	    dlclose(sp->_dlopen_gpm);
542 	    sp->_dlopen_gpm = NULL;
543 	} else {
544 	    sp->_mouse_gpm_found = TRUE;
545 	    sp->_mouse_gpm_loaded = TRUE;
546 	}
547     }
548 }
549 #endif /* HAVE_LIBDL */
550 
551 static bool
enable_gpm_mouse(SCREEN * sp,bool enable)552 enable_gpm_mouse(SCREEN *sp, bool enable)
553 {
554     bool result;
555 
556     T((T_CALLED("enable_gpm_mouse(%d)"), enable));
557 
558     if (enable && !sp->_mouse_active) {
559 #ifdef HAVE_LIBDL
560 	if (sp->_mouse_gpm_found && !sp->_mouse_gpm_loaded) {
561 	    load_gpm_library(sp);
562 	}
563 #endif
564 	if (sp->_mouse_gpm_loaded) {
565 	    int code;
566 
567 	    /* GPM: initialize connection to gpm server */
568 	    sp->_mouse_gpm_connect.eventMask = GPM_DOWN | GPM_UP;
569 	    sp->_mouse_gpm_connect.defaultMask =
570 		(unsigned short) (~(sp->_mouse_gpm_connect.eventMask | GPM_HARD));
571 	    sp->_mouse_gpm_connect.minMod = 0;
572 	    sp->_mouse_gpm_connect.maxMod =
573 		(unsigned short) (~((1 << KG_SHIFT) |
574 				    (1 << KG_SHIFTL) |
575 				    (1 << KG_SHIFTR)));
576 	    /*
577 	     * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open.
578 	     * The former is recognized by wscons (SunOS), and the latter by
579 	     * xterm.  Those will not show up in ncurses' traces.
580 	     */
581 	    code = my_Gpm_Open(&sp->_mouse_gpm_connect, 0);
582 	    result = (code >= 0);
583 
584 	    /*
585 	     * GPM can return a -2 if it is trying to do something with xterm.
586 	     * Ignore that, since it conflicts with our use of stdin.
587 	     */
588 	    if (code == -2) {
589 		my_Gpm_Close();
590 	    }
591 	} else {
592 	    result = FALSE;
593 	}
594 	sp->_mouse_active = result;
595 	T(("GPM open %s", result ? "succeeded" : "failed"));
596     } else {
597 	if (!enable && sp->_mouse_active) {
598 	    /* GPM: close connection to gpm server */
599 	    my_Gpm_Close();
600 	    sp->_mouse_active = FALSE;
601 	    T(("GPM closed"));
602 	}
603 	result = enable;
604     }
605 #ifdef HAVE_LIBDL
606     if (!result) {
607 	unload_gpm_library(sp);
608     }
609 #endif
610     returnBool(result);
611 }
612 #endif /* USE_GPM_SUPPORT */
613 
614 static void
initialize_mousetype(SCREEN * sp)615 initialize_mousetype(SCREEN *sp)
616 {
617     T((T_CALLED("initialize_mousetype()")));
618 
619     /* Try gpm first, because gpm may be configured to run in xterm */
620 #if USE_GPM_SUPPORT
621     if (allow_gpm_mouse(sp)) {
622 	if (!sp->_mouse_gpm_loaded) {
623 #ifdef HAVE_LIBDL
624 	    load_gpm_library(sp);
625 #else /* !HAVE_LIBDL */
626 	    sp->_mouse_gpm_found = TRUE;
627 	    sp->_mouse_gpm_loaded = TRUE;
628 #endif
629 	}
630 
631 	/*
632 	 * The gpm_fd file-descriptor may be negative (xterm).  So we have to
633 	 * maintain our notion of whether the mouse connection is active
634 	 * without testing the file-descriptor.
635 	 */
636 	if (sp->_mouse_gpm_found && enable_gpm_mouse(sp, TRUE)) {
637 	    sp->_mouse_type = M_GPM;
638 	    sp->_mouse_fd = *(my_gpm_fd);
639 	    T(("GPM mouse_fd %d", sp->_mouse_fd));
640 	    returnVoid;
641 	}
642     }
643 #endif /* USE_GPM_SUPPORT */
644 
645     /* OS/2 VIO */
646 #if USE_EMX_MOUSE
647     if (!sp->_emxmouse_thread
648 	&& strstr(SP_TERMTYPE term_names, "xterm") == 0
649 	&& NonEmpty(key_mouse)) {
650 	int handles[2];
651 
652 	if (pipe(handles) < 0) {
653 	    perror("mouse pipe error");
654 	    returnVoid;
655 	} else {
656 	    int rc;
657 
658 	    if (!sp->_emxmouse_buttons[0]) {
659 		const char *s = getenv("MOUSE_BUTTONS_123");
660 
661 		sp->_emxmouse_buttons[0] = 1;
662 		if (s && strlen(s) >= 3) {
663 		    sp->_emxmouse_buttons[1] = s[0] - '0';
664 		    sp->_emxmouse_buttons[2] = s[1] - '0';
665 		    sp->_emxmouse_buttons[3] = s[2] - '0';
666 		} else {
667 		    sp->_emxmouse_buttons[1] = 1;
668 		    sp->_emxmouse_buttons[2] = 3;
669 		    sp->_emxmouse_buttons[3] = 2;
670 		}
671 	    }
672 	    sp->_emxmouse_wfd = handles[1];
673 	    M_FD(sp) = handles[0];
674 	    /* Needed? */
675 	    setmode(handles[0], O_BINARY);
676 	    setmode(handles[1], O_BINARY);
677 	    /* Do not use CRT functions, we may single-threaded. */
678 	    rc = DosCreateThread((unsigned long *) &sp->_emxmouse_thread,
679 				 mouse_server, (long) sp, 0, 8192);
680 	    if (rc) {
681 		printf("mouse thread error %d=%#x", rc, rc);
682 	    } else {
683 		sp->_mouse_type = M_XTERM;
684 	    }
685 	    returnVoid;
686 	}
687     }
688 #endif /* USE_EMX_MOUSE */
689 
690 #if USE_SYSMOUSE
691     {
692 	static char dev_tty[] = "/dev/tty";
693 	struct mouse_info the_mouse;
694 	char *the_device = 0;
695 
696 	if (NC_ISATTY(sp->_ifd))
697 	    the_device = ttyname(sp->_ifd);
698 	if (the_device == 0)
699 	    the_device = dev_tty;
700 
701 	sp->_mouse_fd = open(the_device, O_RDWR);
702 
703 	if (sp->_mouse_fd >= 0) {
704 	    /*
705 	     * sysmouse does not have a usable user interface for obtaining
706 	     * mouse events.  The logical way to proceed (reading data on a
707 	     * stream) only works if one opens the device as root.  Even in
708 	     * that mode, careful examination shows we lose events
709 	     * occasionally.  The interface provided for user programs is to
710 	     * establish a signal handler.  really.
711 	     *
712 	     * Take over SIGUSR2 for this purpose since SIGUSR1 is more
713 	     * likely to be used by an application.  getch() will have to
714 	     * handle the misleading EINTR's.
715 	     */
716 	    signal(SIGUSR2, SIG_IGN);
717 	    the_mouse.operation = MOUSE_MODE;
718 	    the_mouse.u.mode.mode = 0;
719 	    the_mouse.u.mode.signal = SIGUSR2;
720 	    if (ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
721 		signal(SIGUSR2, handle_sysmouse);
722 		the_mouse.operation = MOUSE_SHOW;
723 		ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
724 
725 #if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO)	/* FreeBSD > 2.x */
726 		{
727 #ifndef FBIO_GETMODE		/* FreeBSD 3.x */
728 #define FBIO_GETMODE    CONS_GET
729 #define FBIO_MODEINFO   CONS_MODEINFO
730 #endif /* FBIO_GETMODE */
731 		    video_info_t the_video;
732 
733 		    if (ioctl(sp->_mouse_fd,
734 			      FBIO_GETMODE,
735 			      &the_video.vi_mode) != -1
736 			&& ioctl(sp->_mouse_fd,
737 				 FBIO_MODEINFO,
738 				 &the_video) != -1) {
739 			sp->_sysmouse_char_width = the_video.vi_cwidth;
740 			sp->_sysmouse_char_height = the_video.vi_cheight;
741 		    }
742 		}
743 #endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */
744 
745 		if (sp->_sysmouse_char_width <= 0)
746 		    sp->_sysmouse_char_width = 8;
747 		if (sp->_sysmouse_char_height <= 0)
748 		    sp->_sysmouse_char_height = 16;
749 		sp->_mouse_type = M_SYSMOUSE;
750 		returnVoid;
751 	    }
752 	}
753     }
754 #endif /* USE_SYSMOUSE */
755 
756 #if USE_TERM_DRIVER
757     CallDriver(sp, td_initmouse);
758 #endif
759 #if !defined(_NC_WINDOWS_NATIVE) || USE_NAMED_PIPES
760     /* we know how to recognize mouse events under "xterm" */
761     if (NonEmpty(key_mouse)) {
762 	init_xterm_mouse(sp);
763     } else if (SP_TERMTYPE term_names != NULL
764 	       && strstr(SP_TERMTYPE term_names, "xterm") != NULL) {
765 	if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
766 	    init_xterm_mouse(sp);
767     }
768 #endif
769 
770     returnVoid;
771 }
772 
773 static bool
_nc_mouse_init(SCREEN * sp)774 _nc_mouse_init(SCREEN *sp)
775 /* initialize the mouse */
776 {
777     bool result = FALSE;
778 
779     T((T_CALLED("_nc_mouse_init(%p)"), (void *) sp));
780 
781     if (sp != NULL) {
782 	if (!sp->_mouse_initialized) {
783 	    int i;
784 
785 	    sp->_mouse_initialized = TRUE;
786 
787 	    TR(MY_TRACE, ("set _mouse_initialized"));
788 
789 	    sp->_mouse_readp = sp->_mouse_writep = FirstEV(sp);
790 	    for (i = 0; i < EV_MAX; i++)
791 		Invalidate(sp->_mouse_events + i);
792 
793 	    initialize_mousetype(sp);
794 
795 	    T(("set _mouse_type to %d", sp->_mouse_type));
796 	}
797 	result = sp->_mouse_initialized;
798     }
799     returnBool(result);
800 }
801 
802 /*
803  * Query to see if there is a pending mouse event.  This is called from
804  * fifo_push() in lib_getch.c
805  */
806 static bool
_nc_mouse_event(SCREEN * sp)807 _nc_mouse_event(SCREEN *sp)
808 {
809     MEVENT *eventp = sp->_mouse_writep;
810     bool result = FALSE;
811 
812     (void) eventp;
813 
814     switch (sp->_mouse_type) {
815     case M_XTERM:
816 	/* xterm: never have to query, mouse events are in the keyboard stream */
817 #if USE_EMX_MOUSE
818 	{
819 	    char kbuf[3];
820 
821 	    int i, res = read(M_FD(sp), &kbuf, 3);	/* Eat the prefix */
822 	    if (res != 3)
823 		printf("Got %d chars instead of 3 for prefix.\n", res);
824 	    for (i = 0; i < res; i++) {
825 		if (kbuf[i] != key_mouse[i])
826 		    printf("Got char %d instead of %d for prefix.\n",
827 			   (int) kbuf[i], (int) key_mouse[i]);
828 	    }
829 	    result = TRUE;
830 	}
831 #endif /* USE_EMX_MOUSE */
832 	break;
833 
834 #if USE_GPM_SUPPORT
835     case M_GPM:
836 	if (sp->_mouse_fd >= 0) {
837 	    /* query server for event, return TRUE if we find one */
838 	    Gpm_Event ev;
839 
840 	    switch (my_Gpm_GetEvent(&ev)) {
841 	    case 0:
842 		/* Connection closed, drop the mouse. */
843 		sp->_mouse_fd = -1;
844 		break;
845 	    case 1:
846 		/* there's only one mouse... */
847 		eventp->id = NORMAL_EVENT;
848 
849 		eventp->bstate = 0;
850 		switch (ev.type & 0x0f) {
851 		case (GPM_DOWN):
852 		    if (ev.buttons & GPM_B_LEFT)
853 			eventp->bstate |= BUTTON1_PRESSED;
854 		    if (ev.buttons & GPM_B_MIDDLE)
855 			eventp->bstate |= BUTTON2_PRESSED;
856 		    if (ev.buttons & GPM_B_RIGHT)
857 			eventp->bstate |= BUTTON3_PRESSED;
858 		    break;
859 		case (GPM_UP):
860 		    if (ev.buttons & GPM_B_LEFT)
861 			eventp->bstate |= BUTTON1_RELEASED;
862 		    if (ev.buttons & GPM_B_MIDDLE)
863 			eventp->bstate |= BUTTON2_RELEASED;
864 		    if (ev.buttons & GPM_B_RIGHT)
865 			eventp->bstate |= BUTTON3_RELEASED;
866 		    break;
867 		default:
868 		    eventp->bstate |= REPORT_MOUSE_POSITION;
869 		    break;
870 		}
871 
872 		eventp->x = ev.x - 1;
873 		eventp->y = ev.y - 1;
874 		eventp->z = 0;
875 
876 		/* bump the next-free pointer into the circular list */
877 		sp->_mouse_writep = NEXT(eventp);
878 		result = TRUE;
879 		break;
880 	    }
881 	}
882 	break;
883 #endif
884 
885 #if USE_SYSMOUSE
886     case M_SYSMOUSE:
887 	if (sp->_sysmouse_head < sp->_sysmouse_tail) {
888 	    *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head];
889 
890 	    /*
891 	     * Point the fifo-head to the next possible location.  If there
892 	     * are none, reset the indices.  This may be interrupted by the
893 	     * signal handler, doing essentially the same reset.
894 	     */
895 	    sp->_sysmouse_head += 1;
896 	    if (sp->_sysmouse_head == sp->_sysmouse_tail) {
897 		sp->_sysmouse_tail = 0;
898 		sp->_sysmouse_head = 0;
899 	    }
900 
901 	    /* bump the next-free pointer into the circular list */
902 	    sp->_mouse_writep = eventp = NEXT(eventp);
903 	    result = TRUE;
904 	}
905 	break;
906 #endif /* USE_SYSMOUSE */
907 
908 #if USE_TERM_DRIVER
909     case M_TERM_DRIVER:
910 	while (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
911 	    *eventp = sp->_drv_mouse_fifo[sp->_drv_mouse_head];
912 
913 	    /*
914 	     * Point the fifo-head to the next possible location.  If there
915 	     * are none, reset the indices.
916 	     */
917 	    sp->_drv_mouse_head += 1;
918 	    if (sp->_drv_mouse_head == sp->_drv_mouse_tail) {
919 		sp->_drv_mouse_tail = 0;
920 		sp->_drv_mouse_head = 0;
921 	    }
922 
923 	    /* bump the next-free pointer into the circular list */
924 	    sp->_mouse_writep = eventp = NEXT(eventp);
925 	    result = TRUE;
926 	}
927 	break;
928 #endif
929 
930     case M_NONE:
931 	break;
932     }
933 
934     return result;		/* true if we found an event */
935 }
936 
937 #if USE_EMX_MOUSE
938 #define PRESS_POSITION(n) \
939     do { \
940 	    eventp->bstate = MASK_PRESS(n); \
941 	    sp->_mouse_bstate |= MASK_PRESS(n); \
942 	    if (button & 0x40) { \
943 		    eventp->bstate = MASK_RELEASE(n); \
944 		    sp->_mouse_bstate &= ~MASK_PRESS(n); \
945 	    } \
946     } while (0)
947 #else
948 #define PRESS_POSITION(n) \
949     do { \
950 	    eventp->bstate = (mmask_t) ((sp->_mouse_bstate & MASK_PRESS(n)) \
951 				    ? REPORT_MOUSE_POSITION \
952 				    : MASK_PRESS(n)); \
953 	    sp->_mouse_bstate |= MASK_PRESS(n); \
954     } while (0)
955 #endif
956 
957 static bool
handle_wheel(SCREEN * sp,MEVENT * eventp,int button,int wheel)958 handle_wheel(SCREEN *sp, MEVENT * eventp, int button, int wheel)
959 {
960     bool result = TRUE;
961 
962     switch (button & 3) {
963     case 0:
964 	if (wheel) {
965 	    eventp->bstate = MASK_PRESS(4);
966 	    /* Do not record in sp->_mouse_bstate; there will be no
967 	     * corresponding release event.
968 	     */
969 	} else {
970 	    PRESS_POSITION(1);
971 	}
972 	break;
973     case 1:
974 	if (wheel) {
975 #if NCURSES_MOUSE_VERSION >= 2
976 	    eventp->bstate = MASK_PRESS(5);
977 	    /* See comment above for button 4 */
978 #else
979 	    /* Ignore this event as it is not a true press of the button */
980 	    eventp->bstate = REPORT_MOUSE_POSITION;
981 #endif
982 	} else {
983 	    PRESS_POSITION(2);
984 	}
985 	break;
986     case 2:
987 	if (wheel) {
988 	    /* Ignore this event as it is not a true press of the button */
989 	    eventp->bstate = REPORT_MOUSE_POSITION;
990 	} else {
991 	    PRESS_POSITION(3);
992 	}
993 	break;
994     default:
995 	/*
996 	 * case 3 is sent when the mouse buttons are released.
997 	 *
998 	 * If the terminal uses xterm mode 1003, a continuous series of
999 	 * button-release events is sent as the mouse moves around the screen,
1000 	 * or as the wheel mouse is rotated.
1001 	 *
1002 	 * Return false in this case, so that when running in X10 mode, we will
1003 	 * recalculate bstate.
1004 	 */
1005 	eventp->bstate = REPORT_MOUSE_POSITION;
1006 	result = FALSE;
1007 	break;
1008     }
1009     return result;
1010 }
1011 
1012 static bool
decode_X10_bstate(SCREEN * sp,MEVENT * eventp,unsigned intro)1013 decode_X10_bstate(SCREEN *sp, MEVENT * eventp, unsigned intro)
1014 {
1015     bool result;
1016     int button = 0;
1017     int wheel = (intro & 96) == 96;
1018 
1019     eventp->bstate = 0;
1020 
1021     if (intro >= 96) {
1022 	if (intro >= 160) {
1023 	    button = (int) (intro - 152);	/* buttons 8-11 */
1024 	} else {
1025 	    button = (int) (intro - 92);	/* buttons 4-7 */
1026 	}
1027     } else {
1028 	button = (intro & 3);
1029     }
1030 
1031     if (button > MAX_BUTTONS) {
1032 	eventp->bstate = REPORT_MOUSE_POSITION;
1033     } else if (!handle_wheel(sp, eventp, (int) intro, wheel)) {
1034 
1035 	/*
1036 	 * Release events aren't reported for individual buttons, just for
1037 	 * the button set as a whole.  However, because there are normally
1038 	 * no mouse events under xterm that intervene between press and
1039 	 * release, we can infer the button actually released by looking at
1040 	 * the previous event.
1041 	 */
1042 	if (sp->_mouse_bstate & BUTTON_PRESSED) {
1043 	    int b;
1044 
1045 	    eventp->bstate = BUTTON_RELEASED;
1046 	    for (b = 1; b <= MAX_BUTTONS; ++b) {
1047 		if (!(sp->_mouse_bstate & MASK_PRESS(b)))
1048 		    eventp->bstate &= ~MASK_RELEASE(b);
1049 	    }
1050 	    sp->_mouse_bstate = 0;
1051 	} else {
1052 	    /*
1053 	     * xterm will return a stream of release-events to let the
1054 	     * application know where the mouse is going, if private mode
1055 	     * 1002 or 1003 is enabled.
1056 	     */
1057 	    eventp->bstate = REPORT_MOUSE_POSITION;
1058 	}
1059     }
1060 
1061     if (intro & 4) {
1062 	eventp->bstate |= BUTTON_SHIFT;
1063     }
1064     if (intro & 8) {
1065 	eventp->bstate |= BUTTON_ALT;
1066     }
1067     if (intro & 16) {
1068 	eventp->bstate |= BUTTON_CTRL;
1069     }
1070     result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
1071     return result;
1072 }
1073 
1074 /* This code requires that your xterm entry contain the kmous capability and
1075  * that it be set to the \E[M documented in the Xterm Control Sequences
1076  * reference.  This is how we arrange for mouse events to be reported via a
1077  * KEY_MOUSE return value from wgetch().  After this value is received,
1078  * _nc_mouse_inline() gets called and is immediately responsible for parsing
1079  * the mouse status information following the prefix.
1080  *
1081  * The following quotes from the ctlseqs.ms document in the XTerm distribution,
1082  * describing the mouse tracking feature:
1083  *
1084  * Parameters for all mouse tracking escape sequences generated by xterm encode
1085  * numeric parameters in a single character as value+040.  For example, ! is
1086  * 1.
1087  *
1088  * On button press or release, xterm sends ESC [ M CbCxCy.  The low two bits of
1089  * Cb encode button information:  0=MB1 pressed, 1=MB2 pressed, 2=MB3 pressed,
1090  * 3=release.  The upper bits encode what modifiers were down when the button
1091  * was pressed and are added together.  4=Shift, 8=Meta, 16=Control.  Cx and Cy
1092  * are the x and y coordinates of the mouse event.  The upper left corner is
1093  * (1,1).
1094  *
1095  * (End quote) By the time we get here, we've eaten the key prefix.  FYI, the
1096  * loop below is necessary because mouse click info isn't guaranteed to present
1097  * as a single clist item.
1098  *
1099  * Wheel mice may return buttons 4 and 5 when the wheel is turned.  We encode
1100  * those as button presses.
1101  */
1102 static bool
decode_xterm_X10(SCREEN * sp,MEVENT * eventp)1103 decode_xterm_X10(SCREEN *sp, MEVENT * eventp)
1104 {
1105 #define MAX_KBUF 3
1106     unsigned char kbuf[MAX_KBUF + 1];
1107     size_t grabbed;
1108     int res;
1109     bool result;
1110 
1111     _nc_set_read_thread(TRUE);
1112     for (grabbed = 0; grabbed < MAX_KBUF; grabbed += (size_t) res) {
1113 
1114 	/* For VIO mouse we add extra bit 64 to disambiguate button-up. */
1115 	res = (int) read(
1116 #if USE_EMX_MOUSE
1117 			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
1118 #else
1119 			    sp->_ifd,
1120 #endif
1121 			    kbuf + grabbed, (size_t) (MAX_KBUF - (int) grabbed));
1122 	if (res < 0)
1123 	    break;
1124     }
1125     _nc_set_read_thread(FALSE);
1126     kbuf[MAX_KBUF] = '\0';
1127 
1128     TR(TRACE_IEVENT,
1129        ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
1130 
1131     /* there's only one mouse... */
1132     eventp->id = NORMAL_EVENT;
1133 
1134     result = decode_X10_bstate(sp, eventp, kbuf[0]);
1135 
1136     eventp->x = (kbuf[1] - ' ') - 1;
1137     eventp->y = (kbuf[2] - ' ') - 1;
1138 
1139     return result;
1140 }
1141 
1142 #ifdef EXP_XTERM_1005
1143 /*
1144  * This is identical to X10/X11 responses except that there are two UTF-8
1145  * characters storing the ordinates instead of two bytes.
1146  */
1147 static bool
decode_xterm_1005(SCREEN * sp,MEVENT * eventp)1148 decode_xterm_1005(SCREEN *sp, MEVENT * eventp)
1149 {
1150     char kbuf[80];
1151     size_t grabbed;
1152     size_t limit = (sizeof(kbuf) - 1);
1153     unsigned coords[2];
1154     bool result;
1155 
1156     coords[0] = 0;
1157     coords[1] = 0;
1158 
1159     _nc_set_read_thread(TRUE);
1160     for (grabbed = 0; grabbed < limit;) {
1161 	int res;
1162 
1163 	res = (int) read(
1164 #if USE_EMX_MOUSE
1165 			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
1166 #else
1167 			    sp->_ifd,
1168 #endif
1169 			    (kbuf + grabbed), (size_t) 1);
1170 	if (res < 0)
1171 	    break;
1172 	grabbed += (size_t) res;
1173 	if (grabbed > 1) {
1174 	    size_t check = 1;
1175 	    int n;
1176 
1177 	    for (n = 0; n < 2; ++n) {
1178 		int rc;
1179 
1180 		if (check >= grabbed)
1181 		    break;
1182 		rc = _nc_conv_to_utf32(&coords[n], kbuf + check, (unsigned)
1183 				       (grabbed - check));
1184 		if (!rc)
1185 		    break;
1186 		check += (size_t) rc;
1187 	    }
1188 	    if (n >= 2)
1189 		break;
1190 	}
1191     }
1192     _nc_set_read_thread(FALSE);
1193 
1194     TR(TRACE_IEVENT,
1195        ("_nc_mouse_inline sees the following xterm data: %s",
1196 	_nc_visbufn(kbuf, (int) grabbed)));
1197 
1198     /* there's only one mouse... */
1199     eventp->id = NORMAL_EVENT;
1200 
1201     result = decode_X10_bstate(sp, eventp, UChar(kbuf[0]));
1202 
1203     eventp->x = (int) (coords[0] - ' ') - 1;
1204     eventp->y = (int) (coords[1] - ' ') - 1;
1205 
1206     return result;
1207 }
1208 #endif /* EXP_XTERM_1005 */
1209 
1210 /*
1211  * ECMA-48 section 5.4
1212  */
1213 #define isInter(c) ((c) >= 0x20 && (c) <= 0x2f)
1214 #define isParam(c) ((c) >= 0x30 && (c) <= 0x3f)
1215 #define isFinal(c) ((c) >= 0x40 && (c) <= 0x7e)
1216 
1217 #define MAX_PARAMS 9
1218 
1219 typedef struct {
1220     int nerror;			/* nonzero if there are unexpected chars */
1221     int nparam;			/* number of numeric parameters */
1222     int params[MAX_PARAMS];
1223     int final;			/* the final-character */
1224 } SGR_DATA;
1225 
1226 static bool
read_SGR(const SCREEN * sp,SGR_DATA * result)1227 read_SGR(const SCREEN *sp, SGR_DATA * result)
1228 {
1229     char kbuf[80];		/* bigger than any possible mouse response */
1230     int grabbed = 0;
1231     int ch = 0;
1232     int now = -1;
1233     int marker = 1;
1234 
1235     memset(result, 0, sizeof(*result));
1236     _nc_set_read_thread(TRUE);
1237 
1238     do {
1239 	int res;
1240 
1241 	res = (int) read(
1242 #if USE_EMX_MOUSE
1243 			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
1244 #else
1245 			    sp->_ifd,
1246 #endif
1247 			    (kbuf + grabbed), (size_t) 1);
1248 	if (res < 0)
1249 	    break;
1250 	if ((grabbed + MAX_KBUF) >= (int) sizeof(kbuf)) {
1251 	    result->nerror++;
1252 	    break;
1253 	}
1254 	ch = UChar(kbuf[grabbed]);
1255 	kbuf[grabbed + 1] = 0;
1256 	switch (ch) {
1257 	case '0':
1258 	case '1':
1259 	case '2':
1260 	case '3':
1261 	case '4':
1262 	case '5':
1263 	case '6':
1264 	case '7':
1265 	case '8':
1266 	case '9':
1267 	    if (marker) {
1268 		++now;
1269 		result->nparam = (now + 1);
1270 	    }
1271 	    marker = 0;
1272 	    result->params[now] = (result->params[now] * 10) + (ch - '0');
1273 	    break;
1274 	case ';':
1275 	    if (marker) {
1276 		++now;
1277 		result->nparam = (now + 1);
1278 	    }
1279 	    marker = 1;
1280 	    break;
1281 	default:
1282 	    if (ch < 32 || ch > 126) {
1283 		/*
1284 		 * Technically other characters could be interspersed in the
1285 		 * response.  Ignore those for now.
1286 		 */
1287 		result->nerror++;
1288 		continue;
1289 	    } else if (isFinal(ch)) {
1290 		if (marker) {
1291 		    result->nparam++;
1292 		}
1293 		result->final = ch;
1294 	    } else {
1295 		result->nerror++;
1296 	    }
1297 	    break;
1298 	}
1299 	++grabbed;
1300     } while (!isFinal(ch));
1301     _nc_set_read_thread(FALSE);
1302 
1303     kbuf[++grabbed] = 0;
1304     TR(TRACE_IEVENT,
1305        ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
1306     return (grabbed > 0) && (result->nerror == 0);
1307 }
1308 
1309 static bool
decode_xterm_SGR1006(SCREEN * sp,MEVENT * eventp)1310 decode_xterm_SGR1006(SCREEN *sp, MEVENT * eventp)
1311 {
1312     SGR_DATA data;
1313     bool result = FALSE;
1314     if (read_SGR(sp, &data)) {
1315 	int b = data.params[0];
1316 	int b3 = 1 + (b & 3);
1317 	int wheel = ((b & 64) == 64);
1318 
1319 	if (b >= 132) {
1320 	    b3 = MAX_BUTTONS + 1;
1321 	} else if (b >= 128) {
1322 	    b3 = (b - 120);	/* buttons 8-11 */
1323 	} else if (b >= 64) {
1324 	    b3 = (b - 60);	/* buttons 6-7 */
1325 	}
1326 
1327 	eventp->id = NORMAL_EVENT;
1328 	if (data.final == 'M') {
1329 	    (void) handle_wheel(sp, eventp, b, wheel);
1330 	} else if (b3 > MAX_BUTTONS) {
1331 	    eventp->bstate = REPORT_MOUSE_POSITION;
1332 	} else {
1333 	    mmask_t pressed = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_PRESSED);
1334 	    mmask_t release = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_RELEASED);
1335 	    if (sp->_mouse_bstate & pressed) {
1336 		eventp->bstate = release;
1337 		sp->_mouse_bstate &= ~pressed;
1338 	    } else {
1339 		eventp->bstate = REPORT_MOUSE_POSITION;
1340 	    }
1341 	}
1342 	if (b & 4) {
1343 	    eventp->bstate |= BUTTON_SHIFT;
1344 	}
1345 	if (b & 8) {
1346 	    eventp->bstate |= BUTTON_ALT;
1347 	}
1348 	if (b & 16) {
1349 	    eventp->bstate |= BUTTON_CTRL;
1350 	}
1351 	result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
1352 	eventp->x = (data.params[1] ? (data.params[1] - 1) : 0);
1353 	eventp->y = (data.params[2] ? (data.params[2] - 1) : 0);
1354     }
1355     return result;
1356 }
1357 
1358 static bool
_nc_mouse_inline(SCREEN * sp)1359 _nc_mouse_inline(SCREEN *sp)
1360 /* mouse report received in the keyboard stream -- parse its info */
1361 {
1362     bool result = FALSE;
1363     MEVENT *eventp = sp->_mouse_writep;
1364 
1365     TR(MY_TRACE, (T_CALLED("_nc_mouse_inline(%p)"), (void *) sp));
1366 
1367     if (sp->_mouse_type == M_XTERM) {
1368 	switch (sp->_mouse_format) {
1369 	case MF_X10:
1370 	    result = decode_xterm_X10(sp, eventp);
1371 	    break;
1372 	case MF_SGR1006:
1373 	    result = decode_xterm_SGR1006(sp, eventp);
1374 	    break;
1375 #ifdef EXP_XTERM_1005
1376 	case MF_XTERM_1005:
1377 	    result = decode_xterm_1005(sp, eventp);
1378 	    break;
1379 #endif
1380 	}
1381 
1382 	TR(MY_TRACE,
1383 	   ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
1384 	    _nc_tracemouse(sp, eventp),
1385 	    (long) IndexEV(sp, eventp)));
1386 
1387 	/* bump the next-free pointer into the circular list */
1388 	sp->_mouse_writep = NEXT(eventp);
1389 
1390 	if (!result) {
1391 	    /* If this event is from a wheel-mouse, treat it like position
1392 	     * reports and avoid waiting for the release-events which will
1393 	     * never come.
1394 	     */
1395 	    if (eventp->bstate & BUTTON_PRESSED) {
1396 		int b;
1397 
1398 		for (b = 4; b <= MAX_BUTTONS; ++b) {
1399 		    if ((eventp->bstate & MASK_PRESS(b))) {
1400 			result = TRUE;
1401 			break;
1402 		    }
1403 		}
1404 	    }
1405 	}
1406     }
1407 
1408     returnCode(result);
1409 }
1410 
1411 static void
mouse_activate(SCREEN * sp,bool on)1412 mouse_activate(SCREEN *sp, bool on)
1413 {
1414     T((T_CALLED("mouse_activate(%p,%s)"),
1415        (void *) SP_PARM, on ? "on" : "off"));
1416 
1417     if (!on && !sp->_mouse_initialized)
1418 	returnVoid;
1419 
1420     if (!_nc_mouse_init(sp))
1421 	returnVoid;
1422 
1423     if (on) {
1424 	sp->_mouse_bstate = 0;
1425 	switch (sp->_mouse_type) {
1426 	case M_XTERM:
1427 #if NCURSES_EXT_FUNCS
1428 	    NCURSES_SP_NAME(keyok) (NCURSES_SP_ARGx KEY_MOUSE, on);
1429 #endif
1430 	    enable_xterm_mouse(sp, TRUE);
1431 	    break;
1432 #if USE_GPM_SUPPORT
1433 	case M_GPM:
1434 	    if (enable_gpm_mouse(sp, TRUE)) {
1435 		sp->_mouse_fd = *(my_gpm_fd);
1436 		T(("GPM mouse_fd %d", sp->_mouse_fd));
1437 	    }
1438 	    break;
1439 #endif
1440 #if USE_SYSMOUSE
1441 	case M_SYSMOUSE:
1442 	    signal(SIGUSR2, handle_sysmouse);
1443 	    sp->_mouse_active = TRUE;
1444 	    break;
1445 #endif
1446 #if USE_TERM_DRIVER
1447 	case M_TERM_DRIVER:
1448 	    enable_win32_mouse(sp, TRUE);
1449 	    break;
1450 #endif
1451 	case M_NONE:
1452 	    returnVoid;
1453 	default:
1454 	    T(("unexpected mouse mode"));
1455 	    break;
1456 	}
1457 	/* Make runtime binding to cut down on object size of applications that
1458 	 * do not use the mouse (e.g., 'clear').
1459 	 */
1460 	/* *INDENT-EQLS* */
1461 	sp->_mouse_event  = _nc_mouse_event;
1462 	sp->_mouse_inline = _nc_mouse_inline;
1463 	sp->_mouse_parse  = _nc_mouse_parse;
1464 	sp->_mouse_resume = _nc_mouse_resume;
1465 	sp->_mouse_wrap   = _nc_mouse_wrap;
1466     } else {
1467 
1468 	switch (sp->_mouse_type) {
1469 	case M_XTERM:
1470 	    enable_xterm_mouse(sp, FALSE);
1471 	    break;
1472 #if USE_GPM_SUPPORT
1473 	case M_GPM:
1474 	    enable_gpm_mouse(sp, FALSE);
1475 	    break;
1476 #endif
1477 #if USE_SYSMOUSE
1478 	case M_SYSMOUSE:
1479 	    signal(SIGUSR2, SIG_IGN);
1480 	    sp->_mouse_active = FALSE;
1481 	    break;
1482 #endif
1483 #if USE_TERM_DRIVER
1484 	case M_TERM_DRIVER:
1485 	    enable_win32_mouse(sp, FALSE);
1486 	    break;
1487 #endif
1488 	case M_NONE:
1489 	    returnVoid;
1490 	default:
1491 	    T(("unexpected mouse mode"));
1492 	    break;
1493 	}
1494     }
1495     NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
1496     returnVoid;
1497 }
1498 
1499 /**************************************************************************
1500  *
1501  * Device-independent code
1502  *
1503  **************************************************************************/
1504 
1505 static bool
_nc_mouse_parse(SCREEN * sp,int runcount)1506 _nc_mouse_parse(SCREEN *sp, int runcount)
1507 /* parse a run of atomic mouse events into a gesture */
1508 {
1509     MEVENT *eventp = sp->_mouse_writep;
1510     MEVENT *next, *ep;
1511     MEVENT *first_valid = NULL;
1512     MEVENT *first_invalid = NULL;
1513     int n;
1514     int b;
1515     bool merge;
1516     bool endLoop;
1517 
1518     TR(MY_TRACE, (T_CALLED("_nc_mouse_parse(%d)"), runcount));
1519 
1520     if (!sp->_maxclick
1521 	&& sp->_mouse_readp != NULL
1522 	&& ValidEvent(sp->_mouse_readp)
1523 	&& ((sp->_mouse_readp->bstate & sp->_mouse_mask) != 0)) {
1524 	returnCode(1);
1525     }
1526 
1527     /*
1528      * When we enter this routine, the event list next-free pointer
1529      * points just past a run of mouse events that we know were separated
1530      * in time by less than the critical click interval. The job of this
1531      * routine is to collapse this run into a single higher-level event
1532      * or gesture.
1533      *
1534      * We accomplish this in two passes.  The first pass merges press/release
1535      * pairs into click events.  The second merges runs of click events into
1536      * double or triple-click events.
1537      *
1538      * It's possible that the run may not resolve to a single event (for
1539      * example, if the user quadruple-clicks).  If so, leading events
1540      * in the run are ignored if user does not call getmouse in a loop (getting
1541      * them from newest to older).
1542      *
1543      * Note that this routine is independent of the format of the specific
1544      * format of the pointing-device's reports.  We can use it to parse
1545      * gestures on anything that reports press/release events on a per-
1546      * button basis, as long as the device-dependent mouse code puts stuff
1547      * on the queue in MEVENT format.
1548      */
1549 
1550     /*
1551      * Reset all events that were not set, in case the user sometimes calls
1552      * getmouse only once and other times until there are no more events in
1553      * queue.
1554      *
1555      * This also allows reaching the beginning of the run.
1556      */
1557     ep = eventp;
1558     for (n = runcount; n < EV_MAX; n++) {
1559 	Invalidate(ep);
1560 	ep = NEXT(ep);
1561     }
1562 
1563 #ifdef TRACE
1564     if (USE_TRACEF(TRACE_IEVENT)) {
1565 	_trace_slot(sp, "before mouse press/release merge:");
1566 	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1567 		RunParams(sp, eventp, ep),
1568 		runcount);
1569 	_nc_unlock_global(tracef);
1570     }
1571 #endif /* TRACE */
1572 
1573     /* first pass; merge press/release pairs */
1574     endLoop = FALSE;
1575     while (!endLoop) {
1576 	next = NEXT(ep);
1577 	if (next == eventp) {
1578 	    /* Will end the loop, but compact before */
1579 	    endLoop = TRUE;
1580 	} else {
1581 
1582 #define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \
1583 		      == !(next->bstate & MASK_RELEASE(x)))
1584 
1585 	    if (ValidEvent(ep) && ValidEvent(next)
1586 		&& ep->x == next->x && ep->y == next->y
1587 		&& (ep->bstate & BUTTON_PRESSED)
1588 		&& (!(next->bstate & BUTTON_PRESSED))) {
1589 		bool changed = TRUE;
1590 
1591 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1592 		    if (!MASK_CHANGED(b)) {
1593 			changed = FALSE;
1594 			break;
1595 		    }
1596 		}
1597 
1598 		if (changed) {
1599 		    merge = FALSE;
1600 		    for (b = 1; b <= MAX_BUTTONS; ++b) {
1601 			if ((sp->_mouse_mask2 & MASK_CLICK(b))
1602 			    && (ep->bstate & MASK_PRESS(b))) {
1603 			    next->bstate &= ~MASK_RELEASE(b);
1604 			    next->bstate |= MASK_CLICK(b);
1605 			    merge = TRUE;
1606 			}
1607 		    }
1608 		    if (merge) {
1609 			Invalidate(ep);
1610 		    }
1611 		}
1612 	    }
1613 	}
1614 
1615 	/* Compact valid events */
1616 	if (!ValidEvent(ep)) {
1617 	    if ((first_valid != NULL) && (first_invalid == NULL)) {
1618 		first_invalid = ep;
1619 	    }
1620 	} else {
1621 	    if (first_valid == NULL) {
1622 		first_valid = ep;
1623 	    } else if (first_invalid != NULL) {
1624 		*first_invalid = *ep;
1625 		Invalidate(ep);
1626 		first_invalid = NEXT(first_invalid);
1627 	    }
1628 	}
1629 
1630 	ep = next;
1631     }
1632 
1633     if (first_invalid != NULL) {
1634 	eventp = first_invalid;
1635     }
1636 #ifdef TRACE
1637     if (USE_TRACEF(TRACE_IEVENT)) {
1638 	_trace_slot(sp, "before mouse click merge:");
1639 	if (first_valid == NULL) {
1640 	    _tracef("_nc_mouse_parse: no valid event");
1641 	} else {
1642 	    _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1643 		    RunParams(sp, eventp, first_valid),
1644 		    runcount);
1645 	    _nc_unlock_global(tracef);
1646 	}
1647     }
1648 #endif /* TRACE */
1649 
1650     /*
1651      * Second pass; merge click runs.  We merge click events forward in the
1652      * queue.  For example, double click can be changed to triple click.
1653      *
1654      * NOTE: There is a problem with this design!  If the application
1655      * allows enough click events to pile up in the circular queue so
1656      * they wrap around, it will cheerfully merge the newest forward
1657      * into the oldest, creating a bogus doubleclick and confusing
1658      * the queue-traversal logic rather badly.  Generally this won't
1659      * happen, because calling getmouse() marks old events invalid and
1660      * ineligible for merges.  The true solution to this problem would
1661      * be to timestamp each MEVENT and perform the obvious sanity check,
1662      * but the timer element would have to have sub-second resolution,
1663      * which would get us into portability trouble.
1664      */
1665     first_invalid = NULL;
1666     endLoop = (first_valid == NULL);
1667     ep = first_valid;
1668     while (!endLoop) {
1669 	next = NEXT(ep);
1670 
1671 	if (next == eventp) {
1672 	    /* Will end the loop, but check event type and compact before */
1673 	    endLoop = TRUE;
1674 	} else if (!ValidEvent(next)) {
1675 	    continue;
1676 	} else {
1677 	    /* merge click events forward */
1678 	    if ((ep->bstate & BUTTON_CLICKED)
1679 		&& (next->bstate & BUTTON_CLICKED)) {
1680 		merge = FALSE;
1681 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1682 		    if ((sp->_mouse_mask2 & MASK_DOUBLE_CLICK(b))
1683 			&& (ep->bstate & MASK_CLICK(b))
1684 			&& (next->bstate & MASK_CLICK(b))) {
1685 			next->bstate &= ~MASK_CLICK(b);
1686 			next->bstate |= MASK_DOUBLE_CLICK(b);
1687 			merge = TRUE;
1688 		    }
1689 		}
1690 		if (merge) {
1691 		    Invalidate(ep);
1692 		}
1693 	    }
1694 
1695 	    /* merge double-click events forward */
1696 	    if ((ep->bstate & BUTTON_DOUBLE_CLICKED)
1697 		&& (next->bstate & BUTTON_CLICKED)) {
1698 		merge = FALSE;
1699 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1700 		    if ((sp->_mouse_mask2 & MASK_TRIPLE_CLICK(b))
1701 			&& (ep->bstate & MASK_DOUBLE_CLICK(b))
1702 			&& (next->bstate & MASK_CLICK(b))) {
1703 			next->bstate &= ~MASK_CLICK(b);
1704 			next->bstate |= MASK_TRIPLE_CLICK(b);
1705 			merge = TRUE;
1706 		    }
1707 		}
1708 		if (merge) {
1709 		    Invalidate(ep);
1710 		}
1711 	    }
1712 	}
1713 
1714 	/* Discard event if it does not match event mask */
1715 	if (!(ep->bstate & sp->_mouse_mask2)) {
1716 	    Invalidate(ep);
1717 	}
1718 
1719 	/* Compact valid events */
1720 	if (!ValidEvent(ep)) {
1721 	    if (ep == first_valid) {
1722 		first_valid = next;
1723 	    } else if (first_invalid == NULL) {
1724 		first_invalid = ep;
1725 	    }
1726 	} else if (first_invalid != NULL) {
1727 	    *first_invalid = *ep;
1728 	    Invalidate(ep);
1729 	    first_invalid = NEXT(first_invalid);
1730 	}
1731 
1732 	ep = next;
1733     }
1734 
1735     if (first_invalid == NULL) {
1736 	first_invalid = eventp;
1737     }
1738     sp->_mouse_writep = first_invalid;
1739 
1740 #ifdef TRACE
1741     if (first_valid != NULL) {
1742 	if (USE_TRACEF(TRACE_IEVENT)) {
1743 	    _trace_slot(sp, "after mouse event queue compaction:");
1744 	    _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1745 		    RunParams(sp, first_invalid, first_valid),
1746 		    runcount);
1747 	    _nc_unlock_global(tracef);
1748 	}
1749 	for (ep = first_valid; ep != first_invalid; ep = NEXT(ep)) {
1750 	    if (ValidEvent(ep))
1751 		TR(MY_TRACE,
1752 		   ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
1753 		    _nc_tracemouse(sp, ep),
1754 		    (long) IndexEV(sp, ep)));
1755 	}
1756     }
1757 #endif /* TRACE */
1758 
1759     /* after all this, do we have a valid event? */
1760     ep = PREV(first_invalid);
1761     returnCode(ValidEvent(ep) && ((ep->bstate & sp->_mouse_mask) != 0));
1762 }
1763 
1764 static void
_nc_mouse_wrap(SCREEN * sp)1765 _nc_mouse_wrap(SCREEN *sp)
1766 /* release mouse -- called by endwin() before shellout/exit */
1767 {
1768     TR(MY_TRACE, ("_nc_mouse_wrap() called"));
1769 
1770     switch (sp->_mouse_type) {
1771     case M_XTERM:
1772 	if (sp->_mouse_mask)
1773 	    mouse_activate(sp, FALSE);
1774 	break;
1775 #if USE_GPM_SUPPORT
1776 	/* GPM: pass all mouse events to next client */
1777     case M_GPM:
1778 	if (sp->_mouse_mask)
1779 	    mouse_activate(sp, FALSE);
1780 	break;
1781 #endif
1782 #if USE_SYSMOUSE
1783     case M_SYSMOUSE:
1784 	mouse_activate(sp, FALSE);
1785 	break;
1786 #endif
1787 #if USE_TERM_DRIVER
1788     case M_TERM_DRIVER:
1789 	mouse_activate(sp, FALSE);
1790 	break;
1791 #endif
1792     case M_NONE:
1793 	break;
1794     }
1795 }
1796 
1797 static void
_nc_mouse_resume(SCREEN * sp)1798 _nc_mouse_resume(SCREEN *sp)
1799 /* re-connect to mouse -- called by doupdate() after shellout */
1800 {
1801     TR(MY_TRACE, ("_nc_mouse_resume() called"));
1802 
1803     switch (sp->_mouse_type) {
1804     case M_XTERM:
1805 	/* xterm: re-enable reporting */
1806 	if (sp->_mouse_mask)
1807 	    mouse_activate(sp, TRUE);
1808 	break;
1809 
1810 #if USE_GPM_SUPPORT
1811     case M_GPM:
1812 	/* GPM: reclaim our event set */
1813 	if (sp->_mouse_mask)
1814 	    mouse_activate(sp, TRUE);
1815 	break;
1816 #endif
1817 
1818 #if USE_SYSMOUSE
1819     case M_SYSMOUSE:
1820 	mouse_activate(sp, TRUE);
1821 	break;
1822 #endif
1823 
1824 #if USE_TERM_DRIVER
1825     case M_TERM_DRIVER:
1826 	mouse_activate(sp, TRUE);
1827 	break;
1828 #endif
1829 
1830     case M_NONE:
1831 	break;
1832     }
1833 }
1834 
1835 /**************************************************************************
1836  *
1837  * Mouse interface entry points for the API
1838  *
1839  **************************************************************************/
1840 
1841 NCURSES_EXPORT(int)
NCURSES_SP_NAME(getmouse)1842 NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent)
1843 {
1844     int result = ERR;
1845     MEVENT *readp;
1846 
1847     T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));
1848 
1849     if ((aevent != NULL) &&
1850 	(SP_PARM != NULL) &&
1851 	(SP_PARM->_mouse_type != M_NONE) &&
1852 	(readp = SP_PARM->_mouse_readp) != NULL) {
1853 	/*
1854 	 * Discard events not matching mask (there could be still some if
1855 	 * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns
1856 	 * false).
1857 	 */
1858 	while (readp != SP_PARM->_mouse_writep &&
1859 	       (!ValidEvent(readp) || !(readp->bstate & SP_PARM->_mouse_mask2))) {
1860 	    Invalidate(readp);
1861 	    readp = NEXT(readp);
1862 	}
1863 	if (readp != SP_PARM->_mouse_writep && ValidEvent(readp)) {
1864 	    /* copy the event we find there */
1865 	    *aevent = *readp;
1866 
1867 	    TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
1868 			      _nc_tracemouse(SP_PARM, readp),
1869 			      (long) IndexEV(SP_PARM, readp)));
1870 
1871 	    Invalidate(readp);	/* so the queue slot becomes free */
1872 	    SP_PARM->_mouse_readp = NEXT(readp);
1873 	    result = OK;
1874 	} else {
1875 	    /* Reset the provided event */
1876 	    aevent->bstate = 0;
1877 	    Invalidate(aevent);
1878 	    aevent->x = 0;
1879 	    aevent->y = 0;
1880 	    aevent->z = 0;
1881 	}
1882     }
1883     returnCode(result);
1884 }
1885 
1886 #if NCURSES_SP_FUNCS
1887 /* grab a copy of the current mouse event */
1888 NCURSES_EXPORT(int)
getmouse(MEVENT * aevent)1889 getmouse(MEVENT * aevent)
1890 {
1891     return NCURSES_SP_NAME(getmouse) (CURRENT_SCREEN, aevent);
1892 }
1893 #endif
1894 
1895 NCURSES_EXPORT(int)
NCURSES_SP_NAME(ungetmouse)1896 NCURSES_SP_NAME(ungetmouse) (NCURSES_SP_DCLx MEVENT * aevent)
1897 {
1898     int result = ERR;
1899     MEVENT *eventp;
1900 
1901     T((T_CALLED("ungetmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));
1902 
1903     if (aevent != NULL &&
1904 	SP_PARM != NULL &&
1905 	(eventp = SP_PARM->_mouse_writep) != NULL) {
1906 
1907 	/* stick the given event in the next-free slot */
1908 	*eventp = *aevent;
1909 
1910 	/* bump the next-free pointer into the circular list */
1911 	SP_PARM->_mouse_writep = NEXT(eventp);
1912 
1913 	/* push back the notification event on the keyboard queue */
1914 	result = NCURSES_SP_NAME(ungetch) (NCURSES_SP_ARGx KEY_MOUSE);
1915     }
1916     returnCode(result);
1917 }
1918 
1919 #if NCURSES_SP_FUNCS
1920 /* enqueue a synthesized mouse event to be seen by the next wgetch() */
1921 NCURSES_EXPORT(int)
ungetmouse(MEVENT * aevent)1922 ungetmouse(MEVENT * aevent)
1923 {
1924     return NCURSES_SP_NAME(ungetmouse) (CURRENT_SCREEN, aevent);
1925 }
1926 #endif
1927 
1928 NCURSES_EXPORT(mmask_t)
NCURSES_SP_NAME(mousemask)1929 NCURSES_SP_NAME(mousemask) (NCURSES_SP_DCLx mmask_t newmask, mmask_t * oldmask)
1930 /* set the mouse event mask */
1931 {
1932     mmask_t result = 0;
1933 
1934     T((T_CALLED("mousemask(%p,%#lx,%p)"),
1935        (void *) SP_PARM,
1936        (unsigned long) newmask,
1937        (void *) oldmask));
1938 
1939     if (SP_PARM != NULL) {
1940 	if (oldmask)
1941 	    *oldmask = SP_PARM->_mouse_mask;
1942 
1943 	if (newmask || SP_PARM->_mouse_initialized) {
1944 	    _nc_mouse_init(SP_PARM);
1945 
1946 	    if (SP_PARM->_mouse_type != M_NONE) {
1947 		int b;
1948 
1949 		result = newmask &
1950 		    (REPORT_MOUSE_POSITION
1951 		     | BUTTON_ALT
1952 		     | BUTTON_CTRL
1953 		     | BUTTON_SHIFT
1954 		     | BUTTON_PRESSED
1955 		     | BUTTON_RELEASED
1956 		     | BUTTON_CLICKED
1957 		     | BUTTON_DOUBLE_CLICKED
1958 		     | BUTTON_TRIPLE_CLICKED);
1959 
1960 		mouse_activate(SP_PARM, (bool) (result != 0));
1961 
1962 		SP_PARM->_mouse_mask = result;
1963 		SP_PARM->_mouse_mask2 = result;
1964 
1965 		/*
1966 		 * Make a mask corresponding to the states we will need to
1967 		 * retain (temporarily) while building up the state that the
1968 		 * user asked for.
1969 		 */
1970 		for (b = 1; b <= MAX_BUTTONS; ++b) {
1971 		    if (SP_PARM->_mouse_mask2 & MASK_TRIPLE_CLICK(b))
1972 			SP_PARM->_mouse_mask2 |= MASK_DOUBLE_CLICK(b);
1973 		    if (SP_PARM->_mouse_mask2 & MASK_DOUBLE_CLICK(b))
1974 			SP_PARM->_mouse_mask2 |= MASK_CLICK(b);
1975 		    if (SP_PARM->_mouse_mask2 & MASK_CLICK(b))
1976 			SP_PARM->_mouse_mask2 |= (MASK_PRESS(b) |
1977 						  MASK_RELEASE(b));
1978 		}
1979 	    }
1980 	}
1981     }
1982     returnMMask(result);
1983 }
1984 
1985 #if NCURSES_SP_FUNCS
1986 NCURSES_EXPORT(mmask_t)
mousemask(mmask_t newmask,mmask_t * oldmask)1987 mousemask(mmask_t newmask, mmask_t * oldmask)
1988 {
1989     return NCURSES_SP_NAME(mousemask) (CURRENT_SCREEN, newmask, oldmask);
1990 }
1991 #endif
1992 
1993 NCURSES_EXPORT(bool)
wenclose(const WINDOW * win,int y,int x)1994 wenclose(const WINDOW *win, int y, int x)
1995 /* check to see if given window encloses given screen location */
1996 {
1997     bool result = FALSE;
1998 
1999     T((T_CALLED("wenclose(%p,%d,%d)"), (const void *) win, y, x));
2000 
2001     if (win != NULL) {
2002 	y -= win->_yoffset;
2003 	if (IS_PAD(win)) {
2004 	    if (win->_pad._pad_y >= 0 &&
2005 		win->_pad._pad_x >= 0 &&
2006 		win->_pad._pad_top >= 0 &&
2007 		win->_pad._pad_left >= 0 &&
2008 		win->_pad._pad_right >= 0 &&
2009 		win->_pad._pad_bottom >= 0) {
2010 		result = ((win->_pad._pad_top <= y &&
2011 			   win->_pad._pad_left <= x &&
2012 			   win->_pad._pad_right >= x &&
2013 			   win->_pad._pad_bottom >= y) ? TRUE : FALSE);
2014 	    }
2015 	} else {
2016 	    result = ((win->_begy <= y &&
2017 		       win->_begx <= x &&
2018 		       (win->_begx + win->_maxx) >= x &&
2019 		       (win->_begy + win->_maxy) >= y) ? TRUE : FALSE);
2020 	}
2021     }
2022     returnBool(result);
2023 }
2024 
2025 NCURSES_EXPORT(int)
NCURSES_SP_NAME(mouseinterval)2026 NCURSES_SP_NAME(mouseinterval) (NCURSES_SP_DCLx int maxclick)
2027 /* set the maximum mouse interval within which to recognize a click */
2028 {
2029     int oldval;
2030 
2031     T((T_CALLED("mouseinterval(%p,%d)"), (void *) SP_PARM, maxclick));
2032 
2033     if (SP_PARM != NULL) {
2034 	oldval = SP_PARM->_maxclick;
2035 	if (maxclick >= 0)
2036 	    SP_PARM->_maxclick = maxclick;
2037     } else {
2038 	oldval = DEFAULT_MAXCLICK;
2039     }
2040 
2041     returnCode(oldval);
2042 }
2043 
2044 #if NCURSES_SP_FUNCS
2045 NCURSES_EXPORT(int)
mouseinterval(int maxclick)2046 mouseinterval(int maxclick)
2047 {
2048     return NCURSES_SP_NAME(mouseinterval) (CURRENT_SCREEN, maxclick);
2049 }
2050 #endif
2051 
2052 /* This may be used by other routines to ask for the existence of mouse
2053    support */
2054 NCURSES_EXPORT(bool)
_nc_has_mouse(const SCREEN * sp)2055 _nc_has_mouse(const SCREEN *sp)
2056 {
2057     return (((NULL == sp) || (sp->_mouse_type == M_NONE)) ? FALSE : TRUE);
2058 }
2059 
2060 NCURSES_EXPORT(bool)
NCURSES_SP_NAME(has_mouse)2061 NCURSES_SP_NAME(has_mouse) (NCURSES_SP_DCL0)
2062 {
2063     return _nc_has_mouse(SP_PARM);
2064 }
2065 
2066 #if NCURSES_SP_FUNCS
2067 NCURSES_EXPORT(bool)
has_mouse(void)2068 has_mouse(void)
2069 {
2070     return _nc_has_mouse(CURRENT_SCREEN);
2071 }
2072 #endif
2073 
2074 NCURSES_EXPORT(bool)
wmouse_trafo(const WINDOW * win,int * pY,int * pX,bool to_screen)2075 wmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen)
2076 {
2077     bool result = FALSE;
2078 
2079     T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"),
2080        (const void *) win,
2081        (void *) pY,
2082        (void *) pX,
2083        to_screen));
2084 
2085     if (win && pY && pX) {
2086 	int y = *pY;
2087 	int x = *pX;
2088 
2089 	T(("transform input %d,%d", y, x));
2090 	if (to_screen) {
2091 	    y += win->_begy + win->_yoffset;
2092 	    x += win->_begx;
2093 	    if (wenclose(win, y, x))
2094 		result = TRUE;
2095 	} else {
2096 	    if (wenclose(win, y, x)) {
2097 		y -= (win->_begy + win->_yoffset);
2098 		x -= win->_begx;
2099 		result = TRUE;
2100 	    }
2101 	}
2102 	if (result) {
2103 	    *pX = x;
2104 	    *pY = y;
2105 	    T(("output transform %d,%d", y, x));
2106 	}
2107     }
2108     returnBool(result);
2109 }
2110