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