150276Speter/****************************************************************************
2184989Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey                        1996-on                 *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter * This module is intended to encapsulate ncurses's interface to pointing
3750276Speter * devices.
3850276Speter *
39166124Srafan * The primary method used is xterm's internal mouse-tracking facility.
40166124Srafan * Additional methods depend on the platform:
41166124Srafan *	Alessandro Rubini's GPM server (Linux)
42166124Srafan *	sysmouse (FreeBSD)
43166124Srafan *	special-purpose mouse interface for OS/2 EMX.
4450276Speter *
4550276Speter * Notes for implementors of new mouse-interface methods:
4650276Speter *
4750276Speter * The code is logically split into a lower level that accepts event reports
4850276Speter * in a device-dependent format and an upper level that parses mouse gestures
4950276Speter * and filters events.  The mediating data structure is a circular queue of
5050276Speter * MEVENT structures.
5150276Speter *
5250276Speter * Functionally, the lower level's job is to pick up primitive events and
5350276Speter * put them on the circular queue.  This can happen in one of two ways:
5450276Speter * either (a) _nc_mouse_event() detects a series of incoming mouse reports
5550276Speter * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
5650276Speter * the keyboard input stream and calls _nc_mouse_inline to queue up a series
5750276Speter * of adjacent mouse reports.
5850276Speter *
5950276Speter * In either case, _nc_mouse_parse() should be called after the series is
6050276Speter * accepted to parse the digested mouse reports (low-level MEVENTs) into
6150276Speter * a gesture (a high-level or composite MEVENT).
6250276Speter *
6350276Speter * Don't be too shy about adding new event types or modifiers, if you can find
6450276Speter * room for them in the 32-bit mask.  The API is written so that users get
6550276Speter * feedback on which theoretical event types they won't see when they call
6650276Speter * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
6750276Speter * used yet, and a couple of bits open at the high end.
6850276Speter */
6950276Speter
7050276Speter#ifdef __EMX__
7162449Speter#  include <io.h>
7250276Speter#  define  INCL_DOS
7350276Speter#  define  INCL_VIO
7450276Speter#  define  INCL_KBD
7550276Speter#  define  INCL_MOU
7650276Speter#  define  INCL_DOSPROCESS
7750276Speter#  include <os2.h>		/* Need to include before the others */
7850276Speter#endif
7950276Speter
8050276Speter#include <curses.priv.h>
81166124Srafan
82184989SrafanMODULE_ID("$Id: lib_mouse.c,v 1.102 2008/10/18 21:48:55 tom Exp $")
83166124Srafan
8450276Speter#include <term.h>
85166124Srafan#include <tic.h>
8650276Speter
8750276Speter#if USE_GPM_SUPPORT
8850276Speter#include <linux/keyboard.h>	/* defines KG_* macros */
89166124Srafan
90166124Srafan#ifdef HAVE_LIBDL
91166124Srafan/* use dynamic loader to avoid linkage dependency */
92166124Srafan#include <dlfcn.h>
93166124Srafan
94166124Srafan#ifdef RTLD_NOW
95166124Srafan#define my_RTLD RTLD_NOW
96166124Srafan#else
97166124Srafan#ifdef RTLD_LAZY
98166124Srafan#define my_RTLD RTLD_LAZY
99166124Srafan#else
100166124Srafanmake an error
10150276Speter#endif
102166124Srafan#endif				/* RTLD_NOW */
103166124Srafan#endif				/* HAVE_LIBDL */
104166124Srafan
105166124Srafan#endif				/* USE_GPM_SUPPORT */
106166124Srafan
107166124Srafan#if USE_SYSMOUSE
108166124Srafan#undef buttons			/* symbol conflict in consio.h */
109166124Srafan#undef mouse_info		/* symbol conflict in consio.h */
110166124Srafan#include <osreldate.h>
111166124Srafan#if (__FreeBSD_version >= 400017)
112166124Srafan#include <sys/consio.h>
113166124Srafan#include <sys/fbio.h>
114166124Srafan#else
115166124Srafan#include <machine/console.h>
11650276Speter#endif
117166124Srafan#endif				/* use_SYSMOUSE */
11850276Speter
11950276Speter#define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
12050276Speter
121166124Srafan#define	MASK_RELEASE(x)		NCURSES_MOUSE_MASK(x, 001)
122166124Srafan#define	MASK_PRESS(x)		NCURSES_MOUSE_MASK(x, 002)
123166124Srafan#define	MASK_CLICK(x)		NCURSES_MOUSE_MASK(x, 004)
124166124Srafan#define	MASK_DOUBLE_CLICK(x)	NCURSES_MOUSE_MASK(x, 010)
125166124Srafan#define	MASK_TRIPLE_CLICK(x)	NCURSES_MOUSE_MASK(x, 020)
126166124Srafan#define	MASK_RESERVED_EVENT(x)	NCURSES_MOUSE_MASK(x, 040)
127166124Srafan
128166124Srafan#if NCURSES_MOUSE_VERSION == 1
129166124Srafan#define BUTTON_CLICKED        (BUTTON1_CLICKED        | BUTTON2_CLICKED        | BUTTON3_CLICKED        | BUTTON4_CLICKED)
130166124Srafan#define BUTTON_PRESSED        (BUTTON1_PRESSED        | BUTTON2_PRESSED        | BUTTON3_PRESSED        | BUTTON4_PRESSED)
131166124Srafan#define BUTTON_RELEASED       (BUTTON1_RELEASED       | BUTTON2_RELEASED       | BUTTON3_RELEASED       | BUTTON4_RELEASED)
132166124Srafan#define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED)
133166124Srafan#define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED)
134166124Srafan#define MAX_BUTTONS  4
135166124Srafan#else
136166124Srafan#define BUTTON_CLICKED        (BUTTON1_CLICKED        | BUTTON2_CLICKED        | BUTTON3_CLICKED        | BUTTON4_CLICKED        | BUTTON5_CLICKED)
137166124Srafan#define BUTTON_PRESSED        (BUTTON1_PRESSED        | BUTTON2_PRESSED        | BUTTON3_PRESSED        | BUTTON4_PRESSED        | BUTTON5_PRESSED)
138166124Srafan#define BUTTON_RELEASED       (BUTTON1_RELEASED       | BUTTON2_RELEASED       | BUTTON3_RELEASED       | BUTTON4_RELEASED       | BUTTON5_RELEASED)
139166124Srafan#define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED)
140166124Srafan#define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED)
141166124Srafan#define MAX_BUTTONS  5
142166124Srafan#endif
143166124Srafan
14450276Speter#define INVALID_EVENT	-1
145166124Srafan#define NORMAL_EVENT	0
14650276Speter
147166124Srafan#if USE_GPM_SUPPORT
14850276Speter
149166124Srafan#ifndef LIBGPM_SONAME
150166124Srafan#define LIBGPM_SONAME "libgpm.so"
15150276Speter#endif
15250276Speter
153184989Srafan#define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(SP->_dlopen_gpm, #name))
15450276Speter
155166124Srafan#endif				/* USE_GPM_SUPPORT */
156166124Srafan
157184989Srafanstatic bool _nc_mouse_parse(SCREEN *, int);
15850276Speterstatic void _nc_mouse_resume(SCREEN *);
15950276Speterstatic void _nc_mouse_wrap(SCREEN *);
16050276Speter
16150276Speter/* maintain a circular list of mouse events */
16250276Speter
163184989Srafan#define FirstEV(sp)	((sp)->_mouse_events)
164184989Srafan#define LastEV(sp)	((sp)->_mouse_events + EV_MAX - 1)
165184989Srafan
166166124Srafan#undef  NEXT
167184989Srafan#define NEXT(ep)	((ep >= LastEV(sp)) \
168184989Srafan			 ? FirstEV(sp) \
169166124Srafan			 : ep + 1)
17050276Speter
171166124Srafan#undef  PREV
172184989Srafan#define PREV(ep)	((ep <= FirstEV(sp)) \
173184989Srafan			 ? LastEV(sp) \
174166124Srafan			 : ep - 1)
175166124Srafan
176184989Srafan#define IndexEV(sp, ep)	(ep - FirstEV(sp))
177184989Srafan
178184989Srafan#define RunParams(sp, eventp, runp) \
179184989Srafan		(long) IndexEV(sp, runp), \
180184989Srafan		(long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX
181184989Srafan
18250276Speter#ifdef TRACE
18362449Speterstatic void
184184989Srafan_trace_slot(SCREEN *sp, const char *tag)
18550276Speter{
18662449Speter    MEVENT *ep;
18750276Speter
18862449Speter    _tracef(tag);
18950276Speter
190184989Srafan    for (ep = FirstEV(sp); ep <= LastEV(sp); ep++)
19162449Speter	_tracef("mouse event queue slot %ld = %s",
192184989Srafan		(long) IndexEV(sp, ep),
193184989Srafan		_nc_tracemouse(sp, ep));
19450276Speter}
19550276Speter#endif
19650276Speter
197166124Srafan#if USE_EMX_MOUSE
19850276Speter
19950276Speter#  define TOP_ROW          0
20050276Speter#  define LEFT_COL         0
20150276Speter
20250276Speter#  define M_FD(sp) sp->_mouse_fd
20350276Speter
20450276Speterstatic void
205184989Srafanwrite_event(SCREEN *sp, int down, int button, int x, int y)
20650276Speter{
20750276Speter    char buf[6];
20850276Speter    unsigned long ignore;
20950276Speter
21066963Speter    strncpy(buf, key_mouse, 3);	/* should be "\033[M" */
21150276Speter    buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40);
21250276Speter    buf[4] = ' ' + x - LEFT_COL + 1;
21350276Speter    buf[5] = ' ' + y - TOP_ROW + 1;
214184989Srafan    DosWrite(sp->_emxmouse_wfd, buf, 6, &ignore);
21550276Speter}
21650276Speter
21750276Speterstatic void
218184989Srafanmouse_server(unsigned long param)
21950276Speter{
220184989Srafan    SCREEN *sp = (SCREEN *) param;
22150276Speter    unsigned short fWait = MOU_WAIT;
22250276Speter    /* NOPTRRECT mourt = { 0,0,24,79 }; */
22350276Speter    MOUEVENTINFO mouev;
22450276Speter    HMOU hmou;
22550276Speter    unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN;
22666963Speter    int nbuttons = 3;
22750276Speter    int oldstate = 0;
22866963Speter    char err[80];
22966963Speter    unsigned long rc;
23050276Speter
23150276Speter    /* open the handle for the mouse */
23262449Speter    if (MouOpen(NULL, &hmou) == 0) {
23366963Speter	rc = MouSetEventMask(&mask, hmou);
23466963Speter	if (rc) {		/* retry with 2 buttons */
23566963Speter	    mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
23666963Speter	    rc = MouSetEventMask(&mask, hmou);
23766963Speter	    nbuttons = 2;
23866963Speter	}
23966963Speter	if (rc == 0 && MouDrawPtr(hmou) == 0) {
24050276Speter	    for (;;) {
24150276Speter		/* sit and wait on the event queue */
24266963Speter		rc = MouReadEventQue(&mouev, &fWait, hmou);
24366963Speter		if (rc) {
24466963Speter		    sprintf(err, "Error reading mouse queue, rc=%lu.\r\n", rc);
24562449Speter		    break;
24666963Speter		}
247184989Srafan		if (!sp->_emxmouse_activated)
24850276Speter		    goto finish;
24950276Speter
25050276Speter		/*
25150276Speter		 * OS/2 numbers a 3-button mouse inconsistently from other
25250276Speter		 * platforms:
25362449Speter		 *      1 = left
25462449Speter		 *      2 = right
25562449Speter		 *      3 = middle.
25650276Speter		 */
25750276Speter		if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN)
258184989Srafan		    write_event(sp, mouev.fs & MOUSE_BN1_DOWN,
259184989Srafan				sp->_emxmouse_buttons[1], mouev.col, mouev.row);
26050276Speter		if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN)
261184989Srafan		    write_event(sp, mouev.fs & MOUSE_BN2_DOWN,
262184989Srafan				sp->_emxmouse_buttons[3], mouev.col, mouev.row);
26350276Speter		if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN)
264184989Srafan		    write_event(sp, mouev.fs & MOUSE_BN3_DOWN,
265184989Srafan				sp->_emxmouse_buttons[2], mouev.col, mouev.row);
26650276Speter
26750276Speter	      finish:
26850276Speter		oldstate = mouev.fs;
26950276Speter	    }
27066963Speter	} else
27166963Speter	    sprintf(err, "Error setting event mask, buttons=%d, rc=%lu.\r\n",
27266963Speter		    nbuttons, rc);
27350276Speter
27466963Speter	DosWrite(2, err, strlen(err), &rc);
27550276Speter	MouClose(hmou);
27650276Speter    }
27762449Speter    DosExit(EXIT_THREAD, 0L);
27850276Speter}
27966963Speter
280166124Srafan#endif /* USE_EMX_MOUSE */
281166124Srafan
282166124Srafan#if USE_SYSMOUSE
28350276Speterstatic void
284184989Srafansysmouse_server(SCREEN *sp)
285166124Srafan{
286166124Srafan    struct mouse_info the_mouse;
287166124Srafan    MEVENT *work;
288166124Srafan
289166124Srafan    the_mouse.operation = MOUSE_GETINFO;
290184989Srafan    if (sp != 0
291184989Srafan	&& sp->_mouse_fd >= 0
292184989Srafan	&& sp->_sysmouse_tail < FIFO_SIZE
293184989Srafan	&& ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
294166124Srafan
295184989Srafan	if (sp->_sysmouse_head > sp->_sysmouse_tail) {
296184989Srafan	    sp->_sysmouse_tail = 0;
297184989Srafan	    sp->_sysmouse_head = 0;
298166124Srafan	}
299184989Srafan	work = &(sp->_sysmouse_fifo[sp->_sysmouse_tail]);
300166124Srafan	memset(work, 0, sizeof(*work));
301166124Srafan	work->id = NORMAL_EVENT;	/* there's only one mouse... */
302166124Srafan
303184989Srafan	sp->_sysmouse_old_buttons = sp->_sysmouse_new_buttons;
304184989Srafan	sp->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7;
305166124Srafan
306184989Srafan	if (sp->_sysmouse_new_buttons) {
307184989Srafan	    if (sp->_sysmouse_new_buttons & 1)
308166124Srafan		work->bstate |= BUTTON1_PRESSED;
309184989Srafan	    if (sp->_sysmouse_new_buttons & 2)
310166124Srafan		work->bstate |= BUTTON2_PRESSED;
311184989Srafan	    if (sp->_sysmouse_new_buttons & 4)
312166124Srafan		work->bstate |= BUTTON3_PRESSED;
313166124Srafan	} else {
314184989Srafan	    if (sp->_sysmouse_old_buttons & 1)
315166124Srafan		work->bstate |= BUTTON1_RELEASED;
316184989Srafan	    if (sp->_sysmouse_old_buttons & 2)
317166124Srafan		work->bstate |= BUTTON2_RELEASED;
318184989Srafan	    if (sp->_sysmouse_old_buttons & 4)
319166124Srafan		work->bstate |= BUTTON3_RELEASED;
320166124Srafan	}
321166124Srafan
322166124Srafan	/* for cosmetic bug in syscons.c on FreeBSD 3.[34] */
323166124Srafan	the_mouse.operation = MOUSE_HIDE;
324184989Srafan	ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
325166124Srafan	the_mouse.operation = MOUSE_SHOW;
326184989Srafan	ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
327166124Srafan
328166124Srafan	/*
329166124Srafan	 * We're only interested if the button is pressed or released.
330166124Srafan	 * FIXME: implement continuous event-tracking.
331166124Srafan	 */
332184989Srafan	if (sp->_sysmouse_new_buttons != sp->_sysmouse_old_buttons) {
333184989Srafan	    sp->_sysmouse_tail += 1;
334166124Srafan	}
335184989Srafan	work->x = the_mouse.u.data.x / sp->_sysmouse_char_width;
336184989Srafan	work->y = the_mouse.u.data.y / sp->_sysmouse_char_height;
337166124Srafan    }
33850276Speter}
339184989Srafan
340184989Srafanstatic void
341184989Srafanhandle_sysmouse(int sig GCC_UNUSED)
342184989Srafan{
343184989Srafan    sysmouse_server(SP);
344184989Srafan}
345166124Srafan#endif /* USE_SYSMOUSE */
34650276Speter
347166124Srafanstatic void
348184989Srafaninit_xterm_mouse(SCREEN *sp)
349166124Srafan{
350184989Srafan    sp->_mouse_type = M_XTERM;
351184989Srafan    sp->_mouse_xtermcap = tigetstr("XM");
352184989Srafan    if (!VALID_STRING(sp->_mouse_xtermcap))
353184989Srafan	sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
354166124Srafan}
355166124Srafan
356166124Srafanstatic void
357184989Srafanenable_xterm_mouse(SCREEN *sp, int enable)
358166124Srafan{
359166124Srafan#if USE_EMX_MOUSE
360184989Srafan    sp->_emxmouse_activated = enable;
361166124Srafan#else
362184989Srafan    putp(TPARM_1(sp->_mouse_xtermcap, enable));
36350276Speter#endif
364184989Srafan    sp->_mouse_active = enable;
365166124Srafan}
36650276Speter
367166124Srafan#if USE_GPM_SUPPORT
368184989Srafanstatic bool
369166124Srafanallow_gpm_mouse(void)
370166124Srafan{
371184989Srafan    bool result = FALSE;
372184989Srafan
373166124Srafan    /* GPM does printf's without checking if stdout is a terminal */
374166124Srafan    if (isatty(fileno(stdout))) {
375184989Srafan	char *list = getenv("NCURSES_GPM_TERMS");
376166124Srafan	char *env = getenv("TERM");
377184989Srafan	if (list != 0) {
378184989Srafan	    if (env != 0) {
379184989Srafan		result = _nc_name_match(list, env, "|:");
380184989Srafan	    }
381184989Srafan	} else {
382184989Srafan	    /* GPM checks the beginning of the $TERM variable to decide if it
383184989Srafan	     * should pass xterm events through.  There is no real advantage in
384184989Srafan	     * allowing GPM to do this.  Recent versions relax that check, and
385184989Srafan	     * pretend that GPM can work with any terminal having the kmous
386184989Srafan	     * capability.  Perhaps that works for someone.  If so, they can
387184989Srafan	     * set the environment variable (above).
388184989Srafan	     */
389184989Srafan	    if (env != 0 && strstr(env, "linux") != 0) {
390184989Srafan		result = TRUE;
391184989Srafan	    }
392184989Srafan	}
393166124Srafan    }
394184989Srafan    return result;
395166124Srafan}
39650276Speter
397184989Srafan#ifdef HAVE_LIBDL
398184989Srafanstatic void
399184989Srafanunload_gpm_library(SCREEN *sp)
400184989Srafan{
401184989Srafan    if (SP->_dlopen_gpm != 0) {
402184989Srafan	T(("unload GPM library"));
403184989Srafan	sp->_mouse_gpm_loaded = FALSE;
404184989Srafan	sp->_mouse_fd = -1;
405184989Srafan	dlclose(sp->_dlopen_gpm);
406184989Srafan	sp->_dlopen_gpm = 0;
407184989Srafan    }
408184989Srafan}
409184989Srafan
410184989Srafanstatic void
411184989Srafanload_gpm_library(SCREEN *sp)
412184989Srafan{
413184989Srafan    sp->_mouse_gpm_found = FALSE;
414184989Srafan    if ((sp->_dlopen_gpm = dlopen(LIBGPM_SONAME, my_RTLD)) != 0) {
415184989Srafan	if (GET_DLSYM(gpm_fd) == 0 ||
416184989Srafan	    GET_DLSYM(Gpm_Open) == 0 ||
417184989Srafan	    GET_DLSYM(Gpm_Close) == 0 ||
418184989Srafan	    GET_DLSYM(Gpm_GetEvent) == 0) {
419184989Srafan	    T(("GPM initialization failed: %s", dlerror()));
420184989Srafan	    unload_gpm_library(sp);
421184989Srafan	} else {
422184989Srafan	    sp->_mouse_gpm_found = TRUE;
423184989Srafan	    sp->_mouse_gpm_loaded = TRUE;
424184989Srafan	}
425184989Srafan    }
426184989Srafan}
427184989Srafan#endif
428184989Srafan
429166124Srafanstatic bool
430184989Srafanenable_gpm_mouse(SCREEN *sp, bool enable)
431166124Srafan{
432166124Srafan    bool result;
433166124Srafan
434166124Srafan    T((T_CALLED("enable_gpm_mouse(%d)"), enable));
435166124Srafan
436184989Srafan    if (enable && !sp->_mouse_active) {
437184989Srafan#ifdef HAVE_LIBDL
438184989Srafan	if (sp->_mouse_gpm_found && !sp->_mouse_gpm_loaded) {
439184989Srafan	    load_gpm_library(sp);
440184989Srafan	}
441184989Srafan#endif
442184989Srafan	if (sp->_mouse_gpm_loaded) {
443184989Srafan	    /* GPM: initialize connection to gpm server */
444184989Srafan	    sp->_mouse_gpm_connect.eventMask = GPM_DOWN | GPM_UP;
445184989Srafan	    sp->_mouse_gpm_connect.defaultMask =
446184989Srafan		(unsigned short) (~(sp->_mouse_gpm_connect.eventMask | GPM_HARD));
447184989Srafan	    sp->_mouse_gpm_connect.minMod = 0;
448184989Srafan	    sp->_mouse_gpm_connect.maxMod =
449184989Srafan		(unsigned short) (~((1 << KG_SHIFT) |
450184989Srafan				    (1 << KG_SHIFTL) |
451184989Srafan				    (1 << KG_SHIFTR)));
452184989Srafan	    /*
453184989Srafan	     * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open.
454184989Srafan	     * The former is recognized by wscons (SunOS), and the latter by
455184989Srafan	     * xterm.  Those will not show up in ncurses' traces.
456184989Srafan	     */
457184989Srafan	    result = (my_Gpm_Open(&sp->_mouse_gpm_connect, 0) >= 0);
458184989Srafan	} else {
459184989Srafan	    result = FALSE;
460184989Srafan	}
461184989Srafan	sp->_mouse_active = result;
462166124Srafan	T(("GPM open %s", result ? "succeeded" : "failed"));
463166124Srafan    } else {
464184989Srafan	if (!enable && sp->_mouse_active) {
465166124Srafan	    /* GPM: close connection to gpm server */
466166124Srafan	    my_Gpm_Close();
467184989Srafan	    sp->_mouse_active = FALSE;
468166124Srafan	    T(("GPM closed"));
469166124Srafan	}
470184989Srafan	result = enable;
471166124Srafan    }
472184989Srafan#ifdef HAVE_LIBDL
473184989Srafan    if (!result) {
474184989Srafan	unload_gpm_library(sp);
475184989Srafan    }
476184989Srafan#endif
477166124Srafan    returnBool(result);
478166124Srafan}
479166124Srafan#endif /* USE_GPM_SUPPORT */
480166124Srafan
481174993Srafan#define xterm_kmous "\033[M"
482174993Srafan
48362449Speterstatic void
484184989Srafaninitialize_mousetype(SCREEN *sp)
48550276Speter{
486166124Srafan    T((T_CALLED("initialize_mousetype()")));
487166124Srafan
48862449Speter    /* Try gpm first, because gpm may be configured to run in xterm */
48962449Speter#if USE_GPM_SUPPORT
490166124Srafan    if (allow_gpm_mouse()) {
491184989Srafan	if (!sp->_mouse_gpm_loaded) {
492166124Srafan#ifdef HAVE_LIBDL
493184989Srafan	    load_gpm_library(sp);
494166124Srafan#else /* !HAVE_LIBDL */
495184989Srafan	    sp->_mouse_gpm_found = TRUE;
496184989Srafan	    sp->_mouse_gpm_loaded = TRUE;
49750276Speter#endif
498166124Srafan	}
49950276Speter
500166124Srafan	/*
501166124Srafan	 * The gpm_fd file-descriptor may be negative (xterm).  So we have to
502166124Srafan	 * maintain our notion of whether the mouse connection is active
503166124Srafan	 * without testing the file-descriptor.
504166124Srafan	 */
505184989Srafan	if (sp->_mouse_gpm_found && enable_gpm_mouse(sp, TRUE)) {
506184989Srafan	    sp->_mouse_type = M_GPM;
507184989Srafan	    sp->_mouse_fd = *(my_gpm_fd);
508184989Srafan	    T(("GPM mouse_fd %d", sp->_mouse_fd));
509166124Srafan	    returnVoid;
510166124Srafan	}
511166124Srafan    }
512166124Srafan#endif /* USE_GPM_SUPPORT */
513166124Srafan
51450276Speter    /* OS/2 VIO */
515166124Srafan#if USE_EMX_MOUSE
516184989Srafan    if (!sp->_emxmouse_thread
51762449Speter	&& strstr(cur_term->type.term_names, "xterm") == 0
51862449Speter	&& key_mouse) {
51950276Speter	int handles[2];
52066963Speter
52150276Speter	if (pipe(handles) < 0) {
52250276Speter	    perror("mouse pipe error");
523166124Srafan	    returnVoid;
52450276Speter	} else {
52550276Speter	    int rc;
52650276Speter
527184989Srafan	    if (!sp->_emxmouse_buttons[0]) {
52850276Speter		char *s = getenv("MOUSE_BUTTONS_123");
52950276Speter
530184989Srafan		sp->_emxmouse_buttons[0] = 1;
53150276Speter		if (s && strlen(s) >= 3) {
532184989Srafan		    sp->_emxmouse_buttons[1] = s[0] - '0';
533184989Srafan		    sp->_emxmouse_buttons[2] = s[1] - '0';
534184989Srafan		    sp->_emxmouse_buttons[3] = s[2] - '0';
535166124Srafan		} else {
536184989Srafan		    sp->_emxmouse_buttons[1] = 1;
537184989Srafan		    sp->_emxmouse_buttons[2] = 3;
538184989Srafan		    sp->_emxmouse_buttons[3] = 2;
53950276Speter		}
54050276Speter	    }
541184989Srafan	    sp->_emxmouse_wfd = handles[1];
542184989Srafan	    M_FD(sp) = handles[0];
54350276Speter	    /* Needed? */
54450276Speter	    setmode(handles[0], O_BINARY);
54550276Speter	    setmode(handles[1], O_BINARY);
54650276Speter	    /* Do not use CRT functions, we may single-threaded. */
547184989Srafan	    rc = DosCreateThread((unsigned long *) &sp->_emxmouse_thread,
548184989Srafan				 mouse_server, (long) sp, 0, 8192);
54962449Speter	    if (rc) {
55050276Speter		printf("mouse thread error %d=%#x", rc, rc);
55162449Speter	    } else {
552184989Srafan		sp->_mouse_type = M_XTERM;
55362449Speter	    }
554166124Srafan	    returnVoid;
55550276Speter	}
55650276Speter    }
557166124Srafan#endif /* USE_EMX_MOUSE */
55850276Speter
559166124Srafan#if USE_SYSMOUSE
560166124Srafan    {
561166124Srafan	struct mouse_info the_mouse;
562166124Srafan	char *the_device = 0;
563166124Srafan
564184989Srafan	if (isatty(sp->_ifd))
565184989Srafan	    the_device = ttyname(sp->_ifd);
566166124Srafan	if (the_device == 0)
567166124Srafan	    the_device = "/dev/tty";
568166124Srafan
569184989Srafan	sp->_mouse_fd = open(the_device, O_RDWR);
570166124Srafan
571184989Srafan	if (sp->_mouse_fd >= 0) {
572166124Srafan	    /*
573166124Srafan	     * sysmouse does not have a usable user interface for obtaining
574166124Srafan	     * mouse events.  The logical way to proceed (reading data on a
575166124Srafan	     * stream) only works if one opens the device as root.  Even in
576166124Srafan	     * that mode, careful examination shows we lose events
577166124Srafan	     * occasionally.  The interface provided for user programs is to
578166124Srafan	     * establish a signal handler.  really.
579166124Srafan	     *
580166124Srafan	     * Take over SIGUSR2 for this purpose since SIGUSR1 is more
581166124Srafan	     * likely to be used by an application.  getch() will have to
582166124Srafan	     * handle the misleading EINTR's.
583166124Srafan	     */
584166124Srafan	    signal(SIGUSR2, SIG_IGN);
585166124Srafan	    the_mouse.operation = MOUSE_MODE;
586166124Srafan	    the_mouse.u.mode.mode = 0;
587166124Srafan	    the_mouse.u.mode.signal = SIGUSR2;
588184989Srafan	    if (ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
589166124Srafan		signal(SIGUSR2, handle_sysmouse);
590166124Srafan		the_mouse.operation = MOUSE_SHOW;
591184989Srafan		ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse);
592166124Srafan
593166124Srafan#if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO)	/* FreeBSD > 2.x */
594166124Srafan		{
595166124Srafan#ifndef FBIO_GETMODE		/* FreeBSD 3.x */
596166124Srafan#define FBIO_GETMODE    CONS_GET
597166124Srafan#define FBIO_MODEINFO   CONS_MODEINFO
598166124Srafan#endif /* FBIO_GETMODE */
599166124Srafan		    video_info_t the_video;
600166124Srafan
601184989Srafan		    if (ioctl(sp->_mouse_fd,
602166124Srafan			      FBIO_GETMODE,
603166124Srafan			      &the_video.vi_mode) != -1
604184989Srafan			&& ioctl(sp->_mouse_fd,
605166124Srafan				 FBIO_MODEINFO,
606166124Srafan				 &the_video) != -1) {
607184989Srafan			sp->_sysmouse_char_width = the_video.vi_cwidth;
608184989Srafan			sp->_sysmouse_char_height = the_video.vi_cheight;
609166124Srafan		    }
610166124Srafan		}
611166124Srafan#endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */
612166124Srafan
613184989Srafan		if (sp->_sysmouse_char_width <= 0)
614184989Srafan		    sp->_sysmouse_char_width = 8;
615184989Srafan		if (sp->_sysmouse_char_height <= 0)
616184989Srafan		    sp->_sysmouse_char_height = 16;
617184989Srafan		sp->_mouse_type = M_SYSMOUSE;
618166124Srafan		returnVoid;
619166124Srafan	    }
620166124Srafan	}
621166124Srafan    }
622166124Srafan#endif /* USE_SYSMOUSE */
623166124Srafan
62462449Speter    /* we know how to recognize mouse events under "xterm" */
62562449Speter    if (key_mouse != 0) {
626166124Srafan	if (!strcmp(key_mouse, xterm_kmous)
627166124Srafan	    || strstr(cur_term->type.term_names, "xterm") != 0) {
628184989Srafan	    init_xterm_mouse(sp);
62962449Speter	}
63062449Speter    } else if (strstr(cur_term->type.term_names, "xterm") != 0) {
631184989Srafan	if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
632184989Srafan	    init_xterm_mouse(sp);
63362449Speter    }
634166124Srafan    returnVoid;
63550276Speter}
63650276Speter
637166124Srafanstatic bool
638184989Srafan_nc_mouse_init(SCREEN *sp)
63962449Speter/* initialize the mouse */
64062449Speter{
641166124Srafan    bool result = FALSE;
64262449Speter    int i;
64362449Speter
644184989Srafan    if (sp != 0) {
645184989Srafan	if (!sp->_mouse_initialized) {
646184989Srafan	    sp->_mouse_initialized = TRUE;
64762449Speter
648166124Srafan	    TR(MY_TRACE, ("_nc_mouse_init() called"));
64962449Speter
650184989Srafan	    sp->_mouse_eventp = FirstEV(sp);
651166124Srafan	    for (i = 0; i < EV_MAX; i++)
652184989Srafan		sp->_mouse_events[i].id = INVALID_EVENT;
65362449Speter
654184989Srafan	    initialize_mousetype(sp);
65562449Speter
656184989Srafan	    T(("_nc_mouse_init() set mousetype to %d", sp->_mouse_type));
657166124Srafan	}
658184989Srafan	result = sp->_mouse_initialized;
65962449Speter    }
660166124Srafan    return result;
66162449Speter}
66262449Speter
663166124Srafan/*
664166124Srafan * Query to see if there is a pending mouse event.  This is called from
665166124Srafan * fifo_push() in lib_getch.c
666166124Srafan */
66762449Speterstatic bool
668166124Srafan_nc_mouse_event(SCREEN *sp GCC_UNUSED)
66950276Speter{
670184989Srafan    MEVENT *eventp = sp->_mouse_eventp;
671166124Srafan    bool result = FALSE;
67250276Speter
673166124Srafan    (void) eventp;
67450276Speter
675184989Srafan    switch (sp->_mouse_type) {
676166124Srafan    case M_XTERM:
677166124Srafan	/* xterm: never have to query, mouse events are in the keyboard stream */
678166124Srafan#if USE_EMX_MOUSE
679166124Srafan	{
680166124Srafan	    char kbuf[3];
681166124Srafan
682166124Srafan	    int i, res = read(M_FD(sp), &kbuf, 3);	/* Eat the prefix */
683166124Srafan	    if (res != 3)
684166124Srafan		printf("Got %d chars instead of 3 for prefix.\n", res);
685166124Srafan	    for (i = 0; i < res; i++) {
686166124Srafan		if (kbuf[i] != key_mouse[i])
687166124Srafan		    printf("Got char %d instead of %d for prefix.\n",
688166124Srafan			   (int) kbuf[i], (int) key_mouse[i]);
689166124Srafan	    }
690166124Srafan	    result = TRUE;
69150276Speter	}
692166124Srafan#endif /* USE_EMX_MOUSE */
693166124Srafan	break;
69450276Speter
695166124Srafan#if USE_GPM_SUPPORT
696166124Srafan    case M_GPM:
697166124Srafan	{
698166124Srafan	    /* query server for event, return TRUE if we find one */
699166124Srafan	    Gpm_Event ev;
70050276Speter
701166124Srafan	    if (my_Gpm_GetEvent(&ev) == 1) {
702166124Srafan		/* there's only one mouse... */
703166124Srafan		eventp->id = NORMAL_EVENT;
704166124Srafan
705166124Srafan		eventp->bstate = 0;
706166124Srafan		switch (ev.type & 0x0f) {
707166124Srafan		case (GPM_DOWN):
708166124Srafan		    if (ev.buttons & GPM_B_LEFT)
709166124Srafan			eventp->bstate |= BUTTON1_PRESSED;
710166124Srafan		    if (ev.buttons & GPM_B_MIDDLE)
711166124Srafan			eventp->bstate |= BUTTON2_PRESSED;
712166124Srafan		    if (ev.buttons & GPM_B_RIGHT)
713166124Srafan			eventp->bstate |= BUTTON3_PRESSED;
714166124Srafan		    break;
715166124Srafan		case (GPM_UP):
716166124Srafan		    if (ev.buttons & GPM_B_LEFT)
717166124Srafan			eventp->bstate |= BUTTON1_RELEASED;
718166124Srafan		    if (ev.buttons & GPM_B_MIDDLE)
719166124Srafan			eventp->bstate |= BUTTON2_RELEASED;
720166124Srafan		    if (ev.buttons & GPM_B_RIGHT)
721166124Srafan			eventp->bstate |= BUTTON3_RELEASED;
722166124Srafan		    break;
723166124Srafan		default:
724166124Srafan		    break;
725166124Srafan		}
726166124Srafan
727166124Srafan		eventp->x = ev.x - 1;
728166124Srafan		eventp->y = ev.y - 1;
729166124Srafan		eventp->z = 0;
730166124Srafan
731166124Srafan		/* bump the next-free pointer into the circular list */
732184989Srafan		sp->_mouse_eventp = eventp = NEXT(eventp);
733166124Srafan		result = TRUE;
734166124Srafan	    }
735166124Srafan	}
736166124Srafan	break;
73750276Speter#endif
73850276Speter
739166124Srafan#if USE_SYSMOUSE
740166124Srafan    case M_SYSMOUSE:
741184989Srafan	if (sp->_sysmouse_head < sp->_sysmouse_tail) {
742184989Srafan	    *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head];
74366963Speter
744166124Srafan	    /*
745166124Srafan	     * Point the fifo-head to the next possible location.  If there
746166124Srafan	     * are none, reset the indices.  This may be interrupted by the
747166124Srafan	     * signal handler, doing essentially the same reset.
748166124Srafan	     */
749184989Srafan	    sp->_sysmouse_head += 1;
750184989Srafan	    if (sp->_sysmouse_head == sp->_sysmouse_tail) {
751184989Srafan		sp->_sysmouse_tail = 0;
752184989Srafan		sp->_sysmouse_head = 0;
753166124Srafan	    }
754166124Srafan
755166124Srafan	    /* bump the next-free pointer into the circular list */
756184989Srafan	    sp->_mouse_eventp = eventp = NEXT(eventp);
757166124Srafan	    result = TRUE;
75866963Speter	}
759166124Srafan	break;
760166124Srafan#endif /* USE_SYSMOUSE */
761166124Srafan
762166124Srafan    case M_NONE:
763166124Srafan	break;
76466963Speter    }
76566963Speter
766166124Srafan    return result;		/* true if we found an event */
76750276Speter}
76850276Speter
76962449Speterstatic bool
770166124Srafan_nc_mouse_inline(SCREEN *sp)
77150276Speter/* mouse report received in the keyboard stream -- parse its info */
77250276Speter{
773166124Srafan    int b;
774166124Srafan    bool result = FALSE;
775184989Srafan    MEVENT *eventp = sp->_mouse_eventp;
776166124Srafan
77750276Speter    TR(MY_TRACE, ("_nc_mouse_inline() called"));
77850276Speter
779184989Srafan    if (sp->_mouse_type == M_XTERM) {
78062449Speter	unsigned char kbuf[4];
781166124Srafan	mmask_t prev;
78262449Speter	size_t grabbed;
78362449Speter	int res;
78450276Speter
78550276Speter	/* This code requires that your xterm entry contain the kmous
78650276Speter	 * capability and that it be set to the \E[M documented in the
78750276Speter	 * Xterm Control Sequences reference.  This is how we
78850276Speter	 * arrange for mouse events to be reported via a KEY_MOUSE
78950276Speter	 * return value from wgetch().  After this value is received,
79050276Speter	 * _nc_mouse_inline() gets called and is immediately
79150276Speter	 * responsible for parsing the mouse status information
79250276Speter	 * following the prefix.
79350276Speter	 *
79450276Speter	 * The following quotes from the ctrlseqs.ms document in the
79550276Speter	 * X distribution, describing the X mouse tracking feature:
79650276Speter	 *
79750276Speter	 * Parameters for all mouse tracking escape sequences
79850276Speter	 * generated by xterm encode numeric parameters in a single
79950276Speter	 * character as value+040.  For example, !  is 1.
80050276Speter	 *
80150276Speter	 * On button press or release, xterm sends ESC [ M CbCxCy.
80250276Speter	 * The low two bits of Cb encode button information: 0=MB1
80350276Speter	 * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release.  The
80450276Speter	 * upper bits encode what modifiers were down when the
80550276Speter	 * button was pressed and are added together.  4=Shift,
80650276Speter	 * 8=Meta, 16=Control.  Cx and Cy are the x and y coordinates
80750276Speter	 * of the mouse event.  The upper left corner is (1,1).
80850276Speter	 *
80950276Speter	 * (End quote)  By the time we get here, we've eaten the
81050276Speter	 * key prefix.  FYI, the loop below is necessary because
81150276Speter	 * mouse click info isn't guaranteed to present as a
812166124Srafan	 * single clist item.
813166124Srafan	 *
814166124Srafan	 * Wheel mice may return buttons 4 and 5 when the wheel is turned.
815166124Srafan	 * We encode those as button presses.
81650276Speter	 */
817184989Srafan	for (grabbed = 0; grabbed < 3; grabbed += (size_t) res) {
81850276Speter
81962449Speter	    /* For VIO mouse we add extra bit 64 to disambiguate button-up. */
820166124Srafan#if USE_EMX_MOUSE
82162449Speter	    res = read(M_FD(sp) >= 0 ? M_FD(sp) : sp->_ifd, &kbuf, 3);
82250276Speter#else
82362449Speter	    res = read(sp->_ifd, kbuf + grabbed, 3 - grabbed);
82450276Speter#endif
82562449Speter	    if (res == -1)
82662449Speter		break;
82750276Speter	}
82850276Speter	kbuf[3] = '\0';
82950276Speter
83062449Speter	TR(TRACE_IEVENT,
83166963Speter	   ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
83250276Speter
833166124Srafan	/* there's only one mouse... */
834166124Srafan	eventp->id = NORMAL_EVENT;
83550276Speter
83650276Speter	/* processing code goes here */
83750276Speter	eventp->bstate = 0;
838166124Srafan	prev = PREV(eventp)->bstate;
839166124Srafan
840166124Srafan#if USE_EMX_MOUSE
841166124Srafan#define PRESS_POSITION(n) \
842166124Srafan	eventp->bstate = MASK_PRESS(n); \
843166124Srafan	if (kbuf[0] & 0x40) \
844166124Srafan	    eventp->bstate = MASK_RELEASE(n)
845166124Srafan#else
846166124Srafan#define PRESS_POSITION(n) \
847184989Srafan	eventp->bstate = (mmask_t) (prev & MASK_PRESS(n) \
848184989Srafan				    ? REPORT_MOUSE_POSITION \
849184989Srafan				    : MASK_PRESS(n))
850166124Srafan#endif
851166124Srafan
85262449Speter	switch (kbuf[0] & 0x3) {
85350276Speter	case 0x0:
854166124Srafan	    if (kbuf[0] & 64)
855166124Srafan		eventp->bstate = MASK_PRESS(4);
856166124Srafan	    else
857166124Srafan		PRESS_POSITION(1);
85850276Speter	    break;
85950276Speter
86050276Speter	case 0x1:
861166124Srafan#if NCURSES_MOUSE_VERSION == 2
862166124Srafan	    if (kbuf[0] & 64)
863166124Srafan		eventp->bstate = MASK_PRESS(5);
864166124Srafan	    else
86550276Speter#endif
866166124Srafan		PRESS_POSITION(2);
86750276Speter	    break;
86850276Speter
86950276Speter	case 0x2:
870166124Srafan	    PRESS_POSITION(3);
87150276Speter	    break;
87250276Speter
87350276Speter	case 0x3:
87450276Speter	    /*
875166124Srafan	     * Release events aren't reported for individual buttons, just for
876166124Srafan	     * the button set as a whole.  However, because there are normally
877166124Srafan	     * no mouse events under xterm that intervene between press and
878166124Srafan	     * release, we can infer the button actually released by looking at
879166124Srafan	     * the previous event.
88050276Speter	     */
881166124Srafan	    if (prev & (BUTTON_PRESSED | BUTTON_RELEASED)) {
882166124Srafan		eventp->bstate = BUTTON_RELEASED;
883166124Srafan		for (b = 1; b <= MAX_BUTTONS; ++b) {
884166124Srafan		    if (!(prev & MASK_PRESS(b)))
885166124Srafan			eventp->bstate &= ~MASK_RELEASE(b);
886166124Srafan		}
887166124Srafan	    } else {
888166124Srafan		/*
889166124Srafan		 * XFree86 xterm will return a stream of release-events to
890166124Srafan		 * let the application know where the mouse is going, if the
891166124Srafan		 * private mode 1002 or 1003 is enabled.
892166124Srafan		 */
893166124Srafan		eventp->bstate = REPORT_MOUSE_POSITION;
894166124Srafan	    }
89550276Speter	    break;
89650276Speter	}
897166124Srafan	result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
89850276Speter
89950276Speter	if (kbuf[0] & 4) {
90050276Speter	    eventp->bstate |= BUTTON_SHIFT;
90150276Speter	}
90250276Speter	if (kbuf[0] & 8) {
90350276Speter	    eventp->bstate |= BUTTON_ALT;
90450276Speter	}
90550276Speter	if (kbuf[0] & 16) {
90650276Speter	    eventp->bstate |= BUTTON_CTRL;
90750276Speter	}
90850276Speter
90950276Speter	eventp->x = (kbuf[1] - ' ') - 1;
91050276Speter	eventp->y = (kbuf[2] - ' ') - 1;
91162449Speter	TR(MY_TRACE,
91266963Speter	   ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
913184989Srafan	    _nc_tracemouse(sp, eventp),
914184989Srafan	    (long) IndexEV(sp, eventp)));
91550276Speter
91650276Speter	/* bump the next-free pointer into the circular list */
917184989Srafan	sp->_mouse_eventp = NEXT(eventp);
91862449Speter#if 0				/* this return would be needed for QNX's mods to lib_getch.c */
91962449Speter	return (TRUE);
92050276Speter#endif
92150276Speter    }
92250276Speter
923166124Srafan    return (result);
92450276Speter}
92550276Speter
92662449Speterstatic void
927184989Srafanmouse_activate(SCREEN *sp, bool on)
92850276Speter{
929184989Srafan    if (!on && !sp->_mouse_initialized)
93050276Speter	return;
93150276Speter
932184989Srafan    if (!_nc_mouse_init(sp))
933166124Srafan	return;
93450276Speter
93550276Speter    if (on) {
93650276Speter
937184989Srafan	switch (sp->_mouse_type) {
93850276Speter	case M_XTERM:
93966963Speter#if NCURSES_EXT_FUNCS
94050276Speter	    keyok(KEY_MOUSE, on);
94150276Speter#endif
94250276Speter	    TPUTS_TRACE("xterm mouse initialization");
943184989Srafan	    enable_xterm_mouse(sp, 1);
94450276Speter	    break;
94550276Speter#if USE_GPM_SUPPORT
94650276Speter	case M_GPM:
947184989Srafan	    if (enable_gpm_mouse(sp, TRUE)) {
948184989Srafan		sp->_mouse_fd = *(my_gpm_fd);
949184989Srafan		T(("GPM mouse_fd %d", sp->_mouse_fd));
950166124Srafan	    }
95150276Speter	    break;
95250276Speter#endif
953166124Srafan#if USE_SYSMOUSE
954166124Srafan	case M_SYSMOUSE:
955166124Srafan	    signal(SIGUSR2, handle_sysmouse);
956184989Srafan	    sp->_mouse_active = TRUE;
957166124Srafan	    break;
958166124Srafan#endif
959166124Srafan	case M_NONE:
960166124Srafan	    return;
96150276Speter	}
96250276Speter	/* Make runtime binding to cut down on object size of applications that
96350276Speter	 * do not use the mouse (e.g., 'clear').
96450276Speter	 */
965184989Srafan	sp->_mouse_event = _nc_mouse_event;
966184989Srafan	sp->_mouse_inline = _nc_mouse_inline;
967184989Srafan	sp->_mouse_parse = _nc_mouse_parse;
968184989Srafan	sp->_mouse_resume = _nc_mouse_resume;
969184989Srafan	sp->_mouse_wrap = _nc_mouse_wrap;
97050276Speter    } else {
97150276Speter
972184989Srafan	switch (sp->_mouse_type) {
97350276Speter	case M_XTERM:
97450276Speter	    TPUTS_TRACE("xterm mouse deinitialization");
975184989Srafan	    enable_xterm_mouse(sp, 0);
97650276Speter	    break;
97750276Speter#if USE_GPM_SUPPORT
97850276Speter	case M_GPM:
979184989Srafan	    enable_gpm_mouse(sp, FALSE);
98050276Speter	    break;
98150276Speter#endif
982166124Srafan#if USE_SYSMOUSE
983166124Srafan	case M_SYSMOUSE:
984166124Srafan	    signal(SIGUSR2, SIG_IGN);
985184989Srafan	    sp->_mouse_active = FALSE;
986166124Srafan	    break;
987166124Srafan#endif
988166124Srafan	case M_NONE:
989166124Srafan	    return;
99050276Speter	}
99150276Speter    }
99256639Speter    _nc_flush();
99350276Speter}
99450276Speter
99550276Speter/**************************************************************************
99650276Speter *
99750276Speter * Device-independent code
99850276Speter *
99950276Speter **************************************************************************/
100050276Speter
100162449Speterstatic bool
1002184989Srafan_nc_mouse_parse(SCREEN *sp, int runcount)
100350276Speter/* parse a run of atomic mouse events into a gesture */
100450276Speter{
1005184989Srafan    MEVENT *eventp = sp->_mouse_eventp;
100662449Speter    MEVENT *ep, *runp, *next, *prev = PREV(eventp);
100762449Speter    int n;
1008166124Srafan    int b;
100962449Speter    bool merge;
101050276Speter
101150276Speter    TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));
101250276Speter
101350276Speter    /*
101450276Speter     * When we enter this routine, the event list next-free pointer
101550276Speter     * points just past a run of mouse events that we know were separated
101650276Speter     * in time by less than the critical click interval. The job of this
1017166124Srafan     * routine is to collapse this run into a single higher-level event
101850276Speter     * or gesture.
101950276Speter     *
102050276Speter     * We accomplish this in two passes.  The first pass merges press/release
102150276Speter     * pairs into click events.  The second merges runs of click events into
102250276Speter     * double or triple-click events.
102350276Speter     *
102450276Speter     * It's possible that the run may not resolve to a single event (for
102550276Speter     * example, if the user quadruple-clicks).  If so, leading events
102650276Speter     * in the run are ignored.
102750276Speter     *
102850276Speter     * Note that this routine is independent of the format of the specific
102950276Speter     * format of the pointing-device's reports.  We can use it to parse
103050276Speter     * gestures on anything that reports press/release events on a per-
103150276Speter     * button basis, as long as the device-dependent mouse code puts stuff
103250276Speter     * on the queue in MEVENT format.
103350276Speter     */
103462449Speter    if (runcount == 1) {
103562449Speter	TR(MY_TRACE,
103666963Speter	   ("_nc_mouse_parse: returning simple mouse event %s at slot %ld",
1037184989Srafan	    _nc_tracemouse(sp, prev),
1038184989Srafan	    (long) IndexEV(sp, prev)));
1039166124Srafan	return (prev->id >= NORMAL_EVENT)
1040184989Srafan	    ? ((prev->bstate & sp->_mouse_mask) ? TRUE : FALSE)
104162449Speter	    : FALSE;
104250276Speter    }
104350276Speter
104450276Speter    /* find the start of the run */
104550276Speter    runp = eventp;
104650276Speter    for (n = runcount; n > 0; n--) {
104750276Speter	runp = PREV(runp);
104850276Speter    }
104950276Speter
105050276Speter#ifdef TRACE
1051174993Srafan    if (USE_TRACEF(TRACE_IEVENT)) {
1052184989Srafan	_trace_slot(sp, "before mouse press/release merge:");
105350276Speter	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1054184989Srafan		RunParams(sp, eventp, runp),
105566963Speter		runcount);
1056174993Srafan	_nc_unlock_global(tracef);
105750276Speter    }
105850276Speter#endif /* TRACE */
105950276Speter
106050276Speter    /* first pass; merge press/release pairs */
106150276Speter    do {
106250276Speter	merge = FALSE;
106397049Speter	for (ep = runp; (next = NEXT(ep)) != eventp; ep = next) {
1064166124Srafan
1065166124Srafan#define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \
1066166124Srafan		      == !(next->bstate & MASK_RELEASE(x)))
1067166124Srafan
106850276Speter	    if (ep->x == next->x && ep->y == next->y
1069166124Srafan		&& (ep->bstate & BUTTON_PRESSED)
1070166124Srafan		&& MASK_CHANGED(1)
1071166124Srafan		&& MASK_CHANGED(2)
1072166124Srafan		&& MASK_CHANGED(3)
1073166124Srafan		&& MASK_CHANGED(4)
1074166124Srafan#if NCURSES_MOUSE_VERSION == 2
1075166124Srafan		&& MASK_CHANGED(5)
1076166124Srafan#endif
107762449Speter		) {
1078166124Srafan		for (b = 1; b <= MAX_BUTTONS; ++b) {
1079184989Srafan		    if ((sp->_mouse_mask & MASK_CLICK(b))
1080166124Srafan			&& (ep->bstate & MASK_PRESS(b))) {
1081166124Srafan			ep->bstate &= ~MASK_PRESS(b);
1082166124Srafan			ep->bstate |= MASK_CLICK(b);
1083166124Srafan			merge = TRUE;
1084166124Srafan		    }
108550276Speter		}
108650276Speter		if (merge)
108750276Speter		    next->id = INVALID_EVENT;
108850276Speter	    }
108950276Speter	}
109050276Speter    } while
109150276Speter	(merge);
109250276Speter
109350276Speter#ifdef TRACE
1094174993Srafan    if (USE_TRACEF(TRACE_IEVENT)) {
1095184989Srafan	_trace_slot(sp, "before mouse click merge:");
109650276Speter	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1097184989Srafan		RunParams(sp, eventp, runp),
109866963Speter		runcount);
1099174993Srafan	_nc_unlock_global(tracef);
110050276Speter    }
110150276Speter#endif /* TRACE */
110250276Speter
110350276Speter    /*
110450276Speter     * Second pass; merge click runs.  At this point, click events are
110550276Speter     * each followed by one invalid event. We merge click events
110650276Speter     * forward in the queue.
110750276Speter     *
110850276Speter     * NOTE: There is a problem with this design!  If the application
110950276Speter     * allows enough click events to pile up in the circular queue so
111050276Speter     * they wrap around, it will cheerfully merge the newest forward
111150276Speter     * into the oldest, creating a bogus doubleclick and confusing
111250276Speter     * the queue-traversal logic rather badly.  Generally this won't
111350276Speter     * happen, because calling getmouse() marks old events invalid and
111450276Speter     * ineligible for merges.  The true solution to this problem would
111550276Speter     * be to timestamp each MEVENT and perform the obvious sanity check,
111650276Speter     * but the timer element would have to have sub-second resolution,
111750276Speter     * which would get us into portability trouble.
111850276Speter     */
111950276Speter    do {
112062449Speter	MEVENT *follower;
112150276Speter
112250276Speter	merge = FALSE;
112397049Speter	for (ep = runp; (next = NEXT(ep)) != eventp; ep = next)
112462449Speter	    if (ep->id != INVALID_EVENT) {
112550276Speter		if (next->id != INVALID_EVENT)
112650276Speter		    continue;
112750276Speter		follower = NEXT(next);
112850276Speter		if (follower->id == INVALID_EVENT)
112950276Speter		    continue;
113050276Speter
113150276Speter		/* merge click events forward */
1132166124Srafan		if ((ep->bstate & BUTTON_CLICKED)
1133166124Srafan		    && (follower->bstate & BUTTON_CLICKED)) {
1134166124Srafan		    for (b = 1; b <= MAX_BUTTONS; ++b) {
1135184989Srafan			if ((sp->_mouse_mask & MASK_DOUBLE_CLICK(b))
1136166124Srafan			    && (follower->bstate & MASK_CLICK(b))) {
1137166124Srafan			    follower->bstate &= ~MASK_CLICK(b);
1138166124Srafan			    follower->bstate |= MASK_DOUBLE_CLICK(b);
1139166124Srafan			    merge = TRUE;
1140166124Srafan			}
114150276Speter		    }
114250276Speter		    if (merge)
114350276Speter			ep->id = INVALID_EVENT;
114450276Speter		}
114550276Speter
114650276Speter		/* merge double-click events forward */
1147166124Srafan		if ((ep->bstate & BUTTON_DOUBLE_CLICKED)
1148166124Srafan		    && (follower->bstate & BUTTON_CLICKED)) {
1149166124Srafan		    for (b = 1; b <= MAX_BUTTONS; ++b) {
1150184989Srafan			if ((sp->_mouse_mask & MASK_TRIPLE_CLICK(b))
1151166124Srafan			    && (follower->bstate & MASK_CLICK(b))) {
1152166124Srafan			    follower->bstate &= ~MASK_CLICK(b);
1153166124Srafan			    follower->bstate |= MASK_TRIPLE_CLICK(b);
1154166124Srafan			    merge = TRUE;
1155166124Srafan			}
115650276Speter		    }
115750276Speter		    if (merge)
115850276Speter			ep->id = INVALID_EVENT;
115950276Speter		}
116050276Speter	    }
116150276Speter    } while
116250276Speter	(merge);
116350276Speter
116450276Speter#ifdef TRACE
1165174993Srafan    if (USE_TRACEF(TRACE_IEVENT)) {
1166184989Srafan	_trace_slot(sp, "before mouse event queue compaction:");
116750276Speter	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1168184989Srafan		RunParams(sp, eventp, runp),
116966963Speter		runcount);
1170174993Srafan	_nc_unlock_global(tracef);
117150276Speter    }
117250276Speter#endif /* TRACE */
117350276Speter
117450276Speter    /*
117550276Speter     * Now try to throw away trailing events flagged invalid, or that
117650276Speter     * don't match the current event mask.
117750276Speter     */
117850276Speter    for (; runcount; prev = PREV(eventp), runcount--)
1179184989Srafan	if (prev->id == INVALID_EVENT || !(prev->bstate & sp->_mouse_mask)) {
1180184989Srafan	    sp->_mouse_eventp = eventp = prev;
118150276Speter	}
118250276Speter#ifdef TRACE
1183174993Srafan    if (USE_TRACEF(TRACE_IEVENT)) {
1184184989Srafan	_trace_slot(sp, "after mouse event queue compaction:");
118550276Speter	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1186184989Srafan		RunParams(sp, eventp, runp),
118766963Speter		runcount);
1188174993Srafan	_nc_unlock_global(tracef);
118950276Speter    }
119050276Speter    for (ep = runp; ep != eventp; ep = NEXT(ep))
119150276Speter	if (ep->id != INVALID_EVENT)
119262449Speter	    TR(MY_TRACE,
119366963Speter	       ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
1194184989Srafan		_nc_tracemouse(sp, ep),
1195184989Srafan		(long) IndexEV(sp, ep)));
119650276Speter#endif /* TRACE */
119750276Speter
119850276Speter    /* after all this, do we have a valid event? */
119962449Speter    return (PREV(eventp)->id != INVALID_EVENT);
120050276Speter}
120150276Speter
120262449Speterstatic void
1203184989Srafan_nc_mouse_wrap(SCREEN *sp)
120450276Speter/* release mouse -- called by endwin() before shellout/exit */
120550276Speter{
120650276Speter    TR(MY_TRACE, ("_nc_mouse_wrap() called"));
120750276Speter
1208184989Srafan    switch (sp->_mouse_type) {
120950276Speter    case M_XTERM:
1210184989Srafan	if (sp->_mouse_mask)
1211184989Srafan	    mouse_activate(sp, FALSE);
121250276Speter	break;
121350276Speter#if USE_GPM_SUPPORT
121450276Speter	/* GPM: pass all mouse events to next client */
121562449Speter    case M_GPM:
1216184989Srafan	if (sp->_mouse_mask)
1217184989Srafan	    mouse_activate(sp, FALSE);
121862449Speter	break;
121950276Speter#endif
1220166124Srafan#if USE_SYSMOUSE
1221166124Srafan    case M_SYSMOUSE:
1222184989Srafan	mouse_activate(sp, FALSE);
1223166124Srafan	break;
1224166124Srafan#endif
1225166124Srafan    case M_NONE:
1226166124Srafan	break;
122750276Speter    }
122850276Speter}
122950276Speter
123062449Speterstatic void
1231184989Srafan_nc_mouse_resume(SCREEN *sp)
123250276Speter/* re-connect to mouse -- called by doupdate() after shellout */
123350276Speter{
123450276Speter    TR(MY_TRACE, ("_nc_mouse_resume() called"));
123550276Speter
1236184989Srafan    switch (sp->_mouse_type) {
1237166124Srafan    case M_XTERM:
1238166124Srafan	/* xterm: re-enable reporting */
1239184989Srafan	if (sp->_mouse_mask)
1240184989Srafan	    mouse_activate(sp, TRUE);
1241166124Srafan	break;
1242166124Srafan
1243166124Srafan#if USE_GPM_SUPPORT
1244166124Srafan    case M_GPM:
1245166124Srafan	/* GPM: reclaim our event set */
1246184989Srafan	if (sp->_mouse_mask)
1247184989Srafan	    mouse_activate(sp, TRUE);
1248166124Srafan	break;
1249166124Srafan#endif
1250166124Srafan
1251166124Srafan#if USE_SYSMOUSE
1252166124Srafan    case M_SYSMOUSE:
1253184989Srafan	mouse_activate(sp, TRUE);
1254166124Srafan	break;
1255166124Srafan#endif
1256166124Srafan    case M_NONE:
1257166124Srafan	break;
1258166124Srafan    }
125950276Speter}
126050276Speter
126150276Speter/**************************************************************************
126250276Speter *
126350276Speter * Mouse interface entry points for the API
126450276Speter *
126550276Speter **************************************************************************/
126650276Speter
1267184989Srafanstatic int
1268184989Srafan_nc_getmouse(SCREEN *sp, MEVENT * aevent)
126950276Speter{
127050276Speter    T((T_CALLED("getmouse(%p)"), aevent));
127150276Speter
1272184989Srafan    if ((aevent != 0) && (sp != 0) && (sp->_mouse_type != M_NONE)) {
1273184989Srafan	MEVENT *eventp = sp->_mouse_eventp;
127450276Speter	/* compute the current-event pointer */
127562449Speter	MEVENT *prev = PREV(eventp);
127650276Speter
127750276Speter	/* copy the event we find there */
127850276Speter	*aevent = *prev;
127950276Speter
128050276Speter	TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
1281184989Srafan			  _nc_tracemouse(sp, prev),
1282184989Srafan			  (long) IndexEV(sp, prev)));
128350276Speter
128450276Speter	prev->id = INVALID_EVENT;	/* so the queue slot becomes free */
128550276Speter	returnCode(OK);
128650276Speter    }
128750276Speter    returnCode(ERR);
128850276Speter}
128950276Speter
1290184989Srafan/* grab a copy of the current mouse event */
129176726SpeterNCURSES_EXPORT(int)
1292184989Srafangetmouse(MEVENT * aevent)
129350276Speter{
1294184989Srafan    return _nc_getmouse(SP, aevent);
1295184989Srafan}
1296184989Srafan
1297184989Srafanstatic int
1298184989Srafan_nc_ungetmouse(SCREEN *sp, MEVENT * aevent)
1299184989Srafan{
1300166124Srafan    int result = ERR;
130150276Speter
1302166124Srafan    T((T_CALLED("ungetmouse(%p)"), aevent));
130350276Speter
1304184989Srafan    if (aevent != 0 && sp != 0) {
1305184989Srafan	MEVENT *eventp = sp->_mouse_eventp;
1306166124Srafan
1307166124Srafan	/* stick the given event in the next-free slot */
1308166124Srafan	*eventp = *aevent;
1309166124Srafan
1310166124Srafan	/* bump the next-free pointer into the circular list */
1311184989Srafan	sp->_mouse_eventp = NEXT(eventp);
1312166124Srafan
1313166124Srafan	/* push back the notification event on the keyboard queue */
1314184989Srafan	result = _nc_ungetch(sp, KEY_MOUSE);
1315166124Srafan    }
1316166124Srafan    returnCode(result);
131750276Speter}
131850276Speter
1319184989Srafan/* enqueue a synthesized mouse event to be seen by the next wgetch() */
1320184989SrafanNCURSES_EXPORT(int)
1321184989Srafanungetmouse(MEVENT * aevent)
1322184989Srafan{
1323184989Srafan    return _nc_ungetmouse(SP, aevent);
1324184989Srafan}
1325184989Srafan
132676726SpeterNCURSES_EXPORT(mmask_t)
132762449Spetermousemask(mmask_t newmask, mmask_t * oldmask)
132850276Speter/* set the mouse event mask */
132950276Speter{
133050276Speter    mmask_t result = 0;
133150276Speter
1332166124Srafan    T((T_CALLED("mousemask(%#lx,%p)"), (unsigned long) newmask, oldmask));
133350276Speter
1334166124Srafan    if (SP != 0) {
1335166124Srafan	if (oldmask)
1336166124Srafan	    *oldmask = SP->_mouse_mask;
133750276Speter
1338166124Srafan	if (newmask || SP->_mouse_initialized) {
1339184989Srafan	    _nc_mouse_init(SP);
1340166124Srafan	    if (SP->_mouse_type != M_NONE) {
1341166124Srafan		result = newmask &
1342166124Srafan		    (REPORT_MOUSE_POSITION
1343166124Srafan		     | BUTTON_ALT
1344166124Srafan		     | BUTTON_CTRL
1345166124Srafan		     | BUTTON_SHIFT
1346166124Srafan		     | BUTTON_PRESSED
1347166124Srafan		     | BUTTON_RELEASED
1348166124Srafan		     | BUTTON_CLICKED
1349166124Srafan		     | BUTTON_DOUBLE_CLICKED
1350166124Srafan		     | BUTTON_TRIPLE_CLICKED);
135150276Speter
1352184989Srafan		mouse_activate(SP, (bool) (result != 0));
135350276Speter
1354166124Srafan		SP->_mouse_mask = result;
1355166124Srafan	    }
1356166124Srafan	}
135750276Speter    }
1358166124Srafan    returnBits(result);
135950276Speter}
136050276Speter
136176726SpeterNCURSES_EXPORT(bool)
136262449Speterwenclose(const WINDOW *win, int y, int x)
136350276Speter/* check to see if given window encloses given screen location */
136450276Speter{
1365166124Srafan    bool result = FALSE;
1366166124Srafan
1367166124Srafan    T((T_CALLED("wenclose(%p,%d,%d)"), win, y, x));
1368166124Srafan
1369166124Srafan    if (win != 0) {
137050276Speter	y -= win->_yoffset;
1371166124Srafan	result = ((win->_begy <= y &&
1372166124Srafan		   win->_begx <= x &&
1373166124Srafan		   (win->_begx + win->_maxx) >= x &&
1374166124Srafan		   (win->_begy + win->_maxy) >= y) ? TRUE : FALSE);
137550276Speter    }
1376166124Srafan    returnBool(result);
137750276Speter}
137850276Speter
137976726SpeterNCURSES_EXPORT(int)
138062449Spetermouseinterval(int maxclick)
138150276Speter/* set the maximum mouse interval within which to recognize a click */
138250276Speter{
138350276Speter    int oldval;
138450276Speter
1385166124Srafan    T((T_CALLED("mouseinterval(%d)"), maxclick));
1386166124Srafan
138750276Speter    if (SP != 0) {
138850276Speter	oldval = SP->_maxclick;
138950276Speter	if (maxclick >= 0)
139050276Speter	    SP->_maxclick = maxclick;
139150276Speter    } else {
139250276Speter	oldval = DEFAULT_MAXCLICK;
139350276Speter    }
139450276Speter
1395166124Srafan    returnCode(oldval);
139650276Speter}
139750276Speter
139850276Speter/* This may be used by other routines to ask for the existence of mouse
139950276Speter   support */
140076726SpeterNCURSES_EXPORT(int)
140162449Speter_nc_has_mouse(void)
140262449Speter{
1403166124Srafan    return (SP->_mouse_type == M_NONE ? 0 : 1);
140450276Speter}
140550276Speter
140676726SpeterNCURSES_EXPORT(bool)
1407166124Srafanwmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen)
140850276Speter{
140962449Speter    bool result = FALSE;
141050276Speter
1411166124Srafan    T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"), win, pY, pX, to_screen));
1412166124Srafan
141362449Speter    if (win && pY && pX) {
141462449Speter	int y = *pY;
141562449Speter	int x = *pX;
141650276Speter
141762449Speter	if (to_screen) {
141862449Speter	    y += win->_begy + win->_yoffset;
141962449Speter	    x += win->_begx;
142062449Speter	    if (wenclose(win, y, x))
142162449Speter		result = TRUE;
142262449Speter	} else {
142362449Speter	    if (wenclose(win, y, x)) {
142462449Speter		y -= (win->_begy + win->_yoffset);
142562449Speter		x -= win->_begx;
142662449Speter		result = TRUE;
142750276Speter	    }
142850276Speter	}
142962449Speter	if (result) {
143062449Speter	    *pX = x;
143162449Speter	    *pY = y;
143250276Speter	}
143350276Speter    }
1434166124Srafan    returnBool(result);
143550276Speter}
1436