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