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