]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/base/lib_mouse.c
ncurses 5.4
[ncurses.git] / ncurses / base / lib_mouse.c
1 /****************************************************************************
2  * Copyright (c) 1998-2002,2003 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-2003                                      *
33  ****************************************************************************/
34
35 /*
36  * This module is intended to encapsulate ncurses's interface to pointing
37  * devices.
38  *
39  * The first method used is xterm's internal mouse-tracking facility.
40  * The second is Alessandro Rubini's GPM server.
41  *
42  * Notes for implementors of new mouse-interface methods:
43  *
44  * The code is logically split into a lower level that accepts event reports
45  * in a device-dependent format and an upper level that parses mouse gestures
46  * and filters events.  The mediating data structure is a circular queue of
47  * MEVENT structures.
48  *
49  * Functionally, the lower level's job is to pick up primitive events and
50  * put them on the circular queue.  This can happen in one of two ways:
51  * either (a) _nc_mouse_event() detects a series of incoming mouse reports
52  * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
53  * the keyboard input stream and calls _nc_mouse_inline to queue up a series
54  * of adjacent mouse reports.
55  *
56  * In either case, _nc_mouse_parse() should be called after the series is
57  * accepted to parse the digested mouse reports (low-level MEVENTs) into
58  * a gesture (a high-level or composite MEVENT).
59  *
60  * Don't be too shy about adding new event types or modifiers, if you can find
61  * room for them in the 32-bit mask.  The API is written so that users get
62  * feedback on which theoretical event types they won't see when they call
63  * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
64  * used yet, and a couple of bits open at the high end.
65  */
66
67 #ifdef __EMX__
68 #  include <io.h>
69 #  define  INCL_DOS
70 #  define  INCL_VIO
71 #  define  INCL_KBD
72 #  define  INCL_MOU
73 #  define  INCL_DOSPROCESS
74 #  include <os2.h>              /* Need to include before the others */
75 #endif
76
77 #include <curses.priv.h>
78
79 MODULE_ID("$Id: lib_mouse.c,v 1.68 2003/11/08 21:50:50 tom Exp $")
80
81 #include <term.h>
82 #include <tic.h>
83
84 #if USE_GPM_SUPPORT
85 #ifndef LINT                    /* don't need this for llib-lncurses */
86 #undef buttons                  /* term.h defines this, and gpm uses it! */
87 #include <gpm.h>
88 #include <linux/keyboard.h>     /* defines KG_* macros */
89 #endif
90 #endif
91
92 #if USE_SYSMOUSE
93 #undef buttons                  /* symbol conflict in consio.h */
94 #undef mouse_info               /* symbol conflict in consio.h */
95 #include <osreldate.h>
96 #if (__FreeBSD_version >= 400017)
97 #include <sys/consio.h>
98 #include <sys/fbio.h>
99 #else
100 #include <machine/console.h>
101 #endif
102 #endif /* use_SYSMOUSE */
103
104 #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
105
106 #define MASK_RELEASE(x)         ((001 << (6 * ((x) - 1))))
107 #define MASK_PRESS(x)           ((002 << (6 * ((x) - 1))))
108 #define MASK_CLICK(x)           ((004 << (6 * ((x) - 1))))
109 #define MASK_DOUBLE_CLICK(x)    ((010 << (6 * ((x) - 1))))
110 #define MASK_TRIPLE_CLICK(x)    ((020 << (6 * ((x) - 1))))
111 #define MASK_RESERVED_EVENT(x)  ((040 << (6 * ((x) - 1))))
112
113 #define BUTTON_CLICKED  (BUTTON1_CLICKED  | BUTTON2_CLICKED  | BUTTON3_CLICKED)
114 #define BUTTON_PRESSED  (BUTTON1_PRESSED  | BUTTON2_PRESSED  | BUTTON3_PRESSED)
115 #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED)
116
117 #define INVALID_EVENT   -1
118 #define NORMAL_EVENT    0
119
120 #if USE_GPM_SUPPORT
121 #ifndef LINT
122 static Gpm_Connect gpm_connect;
123 #endif
124 #endif
125
126 static mmask_t eventmask;       /* current event mask */
127
128 static bool _nc_mouse_parse(int);
129 static void _nc_mouse_resume(SCREEN *);
130 static void _nc_mouse_wrap(SCREEN *);
131
132 /* maintain a circular list of mouse events */
133
134 /* The definition of the circular list size (EV_MAX), is in curses.priv.h, so
135  * wgetch() may refer to the size and call _nc_mouse_parse() before circular
136  * list overflow.
137  */
138 static MEVENT events[EV_MAX];   /* hold the last mouse event seen */
139 static MEVENT *eventp = events; /* next free slot in event queue */
140
141 #undef  NEXT
142 #define NEXT(ep)        ((ep == events + EV_MAX - 1) ? events : ep + 1)
143
144 #undef  PREV
145 #define PREV(ep)        ((ep == events) ? events + EV_MAX - 1 : ep - 1)
146
147 #ifdef TRACE
148 static void
149 _trace_slot(const char *tag)
150 {
151     MEVENT *ep;
152
153     _tracef(tag);
154
155     for (ep = events; ep < events + EV_MAX; ep++)
156         _tracef("mouse event queue slot %ld = %s",
157                 (long) (ep - events),
158                 _tracemouse(ep));
159 }
160 #endif
161
162 #if USE_EMX_MOUSE
163
164 #  define TOP_ROW          0
165 #  define LEFT_COL         0
166
167 static int mouse_wfd;
168 static int mouse_thread;
169 static int mouse_activated;
170 static char mouse_buttons[] =
171 {0, 1, 3, 2};
172
173 #  define M_FD(sp) sp->_mouse_fd
174
175 static void
176 write_event(int down, int button, int x, int y)
177 {
178     char buf[6];
179     unsigned long ignore;
180
181     strncpy(buf, key_mouse, 3); /* should be "\033[M" */
182     buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40);
183     buf[4] = ' ' + x - LEFT_COL + 1;
184     buf[5] = ' ' + y - TOP_ROW + 1;
185     DosWrite(mouse_wfd, buf, 6, &ignore);
186 }
187
188 static void
189 mouse_server(unsigned long ignored GCC_UNUSED)
190 {
191     unsigned short fWait = MOU_WAIT;
192     /* NOPTRRECT mourt = { 0,0,24,79 }; */
193     MOUEVENTINFO mouev;
194     HMOU hmou;
195     unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN;
196     int nbuttons = 3;
197     int oldstate = 0;
198     char err[80];
199     unsigned long rc;
200
201     /* open the handle for the mouse */
202     if (MouOpen(NULL, &hmou) == 0) {
203         rc = MouSetEventMask(&mask, hmou);
204         if (rc) {               /* retry with 2 buttons */
205             mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
206             rc = MouSetEventMask(&mask, hmou);
207             nbuttons = 2;
208         }
209         if (rc == 0 && MouDrawPtr(hmou) == 0) {
210             for (;;) {
211                 /* sit and wait on the event queue */
212                 rc = MouReadEventQue(&mouev, &fWait, hmou);
213                 if (rc) {
214                     sprintf(err, "Error reading mouse queue, rc=%lu.\r\n", rc);
215                     break;
216                 }
217                 if (!mouse_activated)
218                     goto finish;
219
220                 /*
221                  * OS/2 numbers a 3-button mouse inconsistently from other
222                  * platforms:
223                  *      1 = left
224                  *      2 = right
225                  *      3 = middle.
226                  */
227                 if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN)
228                     write_event(mouev.fs & MOUSE_BN1_DOWN,
229                                 mouse_buttons[1], mouev.col, mouev.row);
230                 if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN)
231                     write_event(mouev.fs & MOUSE_BN2_DOWN,
232                                 mouse_buttons[3], mouev.col, mouev.row);
233                 if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN)
234                     write_event(mouev.fs & MOUSE_BN3_DOWN,
235                                 mouse_buttons[2], mouev.col, mouev.row);
236
237               finish:
238                 oldstate = mouev.fs;
239             }
240         } else
241             sprintf(err, "Error setting event mask, buttons=%d, rc=%lu.\r\n",
242                     nbuttons, rc);
243
244         DosWrite(2, err, strlen(err), &rc);
245         MouClose(hmou);
246     }
247     DosExit(EXIT_THREAD, 0L);
248 }
249
250 static void
251 server_state(const int state)
252 {                               /* It would be nice to implement pointer-off and stop looping... */
253     mouse_activated = state;
254 }
255
256 #endif /* USE_EMX_MOUSE */
257
258 #if USE_SYSMOUSE
259 static void
260 handle_sysmouse(int sig GCC_UNUSED)
261 {
262     struct mouse_info the_mouse;
263     MEVENT *work;
264
265     the_mouse.operation = MOUSE_GETINFO;
266     if (SP != 0
267         && SP->_mouse_fd >= 0
268         && SP->_sysmouse_tail < FIFO_SIZE
269         && ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
270
271         if (SP->_sysmouse_head > SP->_sysmouse_tail) {
272             SP->_sysmouse_tail = 0;
273             SP->_sysmouse_head = 0;
274         }
275         work = &(SP->_sysmouse_fifo[SP->_sysmouse_tail]);
276         memset(work, 0, sizeof(*work));
277         work->id = NORMAL_EVENT;        /* there's only one mouse... */
278
279         SP->_sysmouse_old_buttons = SP->_sysmouse_new_buttons;
280         SP->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7;
281
282         if (SP->_sysmouse_new_buttons) {
283             if (SP->_sysmouse_new_buttons & 1)
284                 work->bstate |= BUTTON1_PRESSED;
285             if (SP->_sysmouse_new_buttons & 2)
286                 work->bstate |= BUTTON2_PRESSED;
287             if (SP->_sysmouse_new_buttons & 4)
288                 work->bstate |= BUTTON3_PRESSED;
289         } else {
290             if (SP->_sysmouse_old_buttons & 1)
291                 work->bstate |= BUTTON1_RELEASED;
292             if (SP->_sysmouse_old_buttons & 2)
293                 work->bstate |= BUTTON2_RELEASED;
294             if (SP->_sysmouse_old_buttons & 4)
295                 work->bstate |= BUTTON3_RELEASED;
296         }
297
298         /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */
299         the_mouse.operation = MOUSE_HIDE;
300         ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse);
301         the_mouse.operation = MOUSE_SHOW;
302         ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse);
303
304         /*
305          * We're only interested if the button is pressed or released.
306          * FIXME: implement continuous event-tracking.
307          */
308         if (SP->_sysmouse_new_buttons != SP->_sysmouse_old_buttons) {
309             SP->_sysmouse_tail += 1;
310         }
311         work->x = the_mouse.u.data.x / SP->_sysmouse_char_width;
312         work->y = the_mouse.u.data.y / SP->_sysmouse_char_height;
313     }
314 }
315 #endif
316
317 static int initialized;
318
319 static void
320 init_xterm_mouse(void)
321 {
322     SP->_mouse_type = M_XTERM;
323     SP->_mouse_xtermcap = tigetstr("XM");
324     if (!VALID_STRING(SP->_mouse_xtermcap))
325         SP->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
326 }
327
328 #if !USE_EMX_MOUSE
329 static void
330 enable_xterm_mouse(int enable)
331 {
332     putp(tparm(SP->_mouse_xtermcap, enable));
333 }
334 #endif /* !USE_EMX_MOUSE */
335
336 static void
337 initialize_mousetype(void)
338 {
339     static const char *xterm_kmous = "\033[M";
340
341     /* Try gpm first, because gpm may be configured to run in xterm */
342 #if USE_GPM_SUPPORT
343     /* GPM does printf's without checking if stdout is a terminal */
344     if (isatty(fileno(stdout))) {
345         /* GPM: initialize connection to gpm server */
346         gpm_connect.eventMask = GPM_DOWN | GPM_UP;
347         gpm_connect.defaultMask = ~(gpm_connect.eventMask | GPM_HARD);
348         gpm_connect.minMod = 0;
349         gpm_connect.maxMod = ~((1 << KG_SHIFT) | (1 << KG_SHIFTL) | (1 << KG_SHIFTR));
350         if (Gpm_Open(&gpm_connect, 0) >= 0) {   /* returns the file-descriptor */
351             SP->_mouse_type = M_GPM;
352             SP->_mouse_fd = gpm_fd;
353             return;
354         }
355     }
356 #endif
357
358     /* OS/2 VIO */
359 #if USE_EMX_MOUSE
360     if (!mouse_thread
361         && strstr(cur_term->type.term_names, "xterm") == 0
362         && key_mouse) {
363         int handles[2];
364
365         if (pipe(handles) < 0) {
366             perror("mouse pipe error");
367             return;
368         } else {
369             int rc;
370
371             if (!mouse_buttons[0]) {
372                 char *s = getenv("MOUSE_BUTTONS_123");
373
374                 mouse_buttons[0] = 1;
375                 if (s && strlen(s) >= 3) {
376                     mouse_buttons[1] = s[0] - '0';
377                     mouse_buttons[2] = s[1] - '0';
378                     mouse_buttons[3] = s[2] - '0';
379                 }
380             }
381             mouse_wfd = handles[1];
382             M_FD(SP) = handles[0];
383             /* Needed? */
384             setmode(handles[0], O_BINARY);
385             setmode(handles[1], O_BINARY);
386             /* Do not use CRT functions, we may single-threaded. */
387             rc = DosCreateThread((unsigned long *) &mouse_thread,
388                                  mouse_server, 0, 0, 8192);
389             if (rc) {
390                 printf("mouse thread error %d=%#x", rc, rc);
391                 return;
392             } else {
393                 SP->_mouse_type = M_XTERM;
394                 return;
395             }
396         }
397     }
398 #endif
399
400 #if USE_SYSMOUSE
401     {
402         struct mouse_info the_mouse;
403         char *the_device = 0;
404
405         if (isatty(SP->_ifd))
406             the_device = ttyname(SP->_ifd);
407         if (the_device == 0)
408             the_device = "/dev/tty";
409
410         SP->_mouse_fd = open(the_device, O_RDWR);
411
412         if (SP->_mouse_fd >= 0) {
413             /*
414              * sysmouse does not have a usable user interface for obtaining
415              * mouse events.  The logical way to proceed (reading data on a
416              * stream) only works if one opens the device as root.  Even in
417              * that mode, careful examination shows we lose events
418              * occasionally.  The interface provided for user programs is to
419              * establish a signal handler.  really.
420              *
421              * Take over SIGUSR2 for this purpose since SIGUSR1 is more
422              * likely to be used by an application.  getch() will have to
423              * handle the misleading EINTR's.
424              */
425             signal(SIGUSR2, SIG_IGN);
426             the_mouse.operation = MOUSE_MODE;
427             the_mouse.u.mode.mode = 0;
428             the_mouse.u.mode.signal = SIGUSR2;
429             if (ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) {
430                 signal(SIGUSR2, handle_sysmouse);
431                 the_mouse.operation = MOUSE_SHOW;
432                 ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse);
433
434 #if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO)    /* FreeBSD > 2.x */
435                 {
436 #ifndef FBIO_GETMODE            /* FreeBSD 3.x */
437 #define FBIO_GETMODE    CONS_GET
438 #define FBIO_MODEINFO   CONS_MODEINFO
439 #endif /* FBIO_GETMODE */
440                     video_info_t the_video;
441
442                     if (ioctl(SP->_mouse_fd,
443                               FBIO_GETMODE,
444                               &the_video.vi_mode) != -1
445                         && ioctl(SP->_mouse_fd,
446                                  FBIO_MODEINFO,
447                                  &the_video) != -1) {
448                         SP->_sysmouse_char_width = the_video.vi_cwidth;
449                         SP->_sysmouse_char_height = the_video.vi_cheight;
450                     }
451                 }
452 #endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */
453
454                 if (SP->_sysmouse_char_width <= 0)
455                     SP->_sysmouse_char_width = 8;
456                 if (SP->_sysmouse_char_height <= 0)
457                     SP->_sysmouse_char_height = 16;
458                 SP->_mouse_type = M_SYSMOUSE;
459                 return;
460             }
461         }
462     }
463 #endif /* USE_SYSMOUSE */
464
465     /* we know how to recognize mouse events under "xterm" */
466     if (key_mouse != 0) {
467         if (!strcmp(key_mouse, xterm_kmous)) {
468             init_xterm_mouse();
469             return;
470         }
471     } else if (strstr(cur_term->type.term_names, "xterm") != 0) {
472         (void) _nc_add_to_try(&(SP->_keytry), xterm_kmous, KEY_MOUSE);
473         init_xterm_mouse();
474         return;
475     }
476 }
477
478 static void
479 _nc_mouse_init(void)
480 /* initialize the mouse */
481 {
482     int i;
483
484     if (!initialized) {
485         initialized = TRUE;
486
487         TR(MY_TRACE, ("_nc_mouse_init() called"));
488
489         for (i = 0; i < EV_MAX; i++)
490             events[i].id = INVALID_EVENT;
491
492         initialize_mousetype();
493
494         T(("_nc_mouse_init() set mousetype to %d", SP->_mouse_type));
495     }
496 }
497
498 /*
499  * Query to see if there is a pending mouse event.  This is called from
500  * fifo_push() in lib_getch.c
501  */
502 static bool
503 _nc_mouse_event(SCREEN * sp GCC_UNUSED)
504 {
505     bool result = FALSE;
506
507     switch (SP->_mouse_type) {
508     case M_XTERM:
509         /* xterm: never have to query, mouse events are in the keyboard stream */
510 #if USE_EMX_MOUSE
511         {
512             char kbuf[3];
513
514             int i, res = read(M_FD(sp), &kbuf, 3);      /* Eat the prefix */
515             if (res != 3)
516                 printf("Got %d chars instead of 3 for prefix.\n", res);
517             for (i = 0; i < res; i++) {
518                 if (kbuf[i] != key_mouse[i])
519                     printf("Got char %d instead of %d for prefix.\n",
520                            (int) kbuf[i], (int) key_mouse[i]);
521             }
522             result = TRUE;
523         }
524 #endif /* USE_EMX_MOUSE */
525         break;
526
527 #if USE_GPM_SUPPORT
528     case M_GPM:
529         {
530             /* query server for event, return TRUE if we find one */
531             Gpm_Event ev;
532
533             if (Gpm_GetEvent(&ev) == 1) {
534                 /* there's only one mouse... */
535                 eventp->id = NORMAL_EVENT;
536
537                 eventp->bstate = 0;
538                 switch (ev.type & 0x0f) {
539                 case (GPM_DOWN):
540                     if (ev.buttons & GPM_B_LEFT)
541                         eventp->bstate |= BUTTON1_PRESSED;
542                     if (ev.buttons & GPM_B_MIDDLE)
543                         eventp->bstate |= BUTTON2_PRESSED;
544                     if (ev.buttons & GPM_B_RIGHT)
545                         eventp->bstate |= BUTTON3_PRESSED;
546                     break;
547                 case (GPM_UP):
548                     if (ev.buttons & GPM_B_LEFT)
549                         eventp->bstate |= BUTTON1_RELEASED;
550                     if (ev.buttons & GPM_B_MIDDLE)
551                         eventp->bstate |= BUTTON2_RELEASED;
552                     if (ev.buttons & GPM_B_RIGHT)
553                         eventp->bstate |= BUTTON3_RELEASED;
554                     break;
555                 default:
556                     break;
557                 }
558
559                 eventp->x = ev.x - 1;
560                 eventp->y = ev.y - 1;
561                 eventp->z = 0;
562
563                 /* bump the next-free pointer into the circular list */
564                 eventp = NEXT(eventp);
565                 result = TRUE;
566             }
567         }
568         break;
569 #endif
570
571 #if USE_SYSMOUSE
572     case M_SYSMOUSE:
573         if (SP->_sysmouse_head < SP->_sysmouse_tail) {
574             *eventp = SP->_sysmouse_fifo[SP->_sysmouse_head];
575
576             /*
577              * Point the fifo-head to the next possible location.  If there
578              * are none, reset the indices.  This may be interrupted by the
579              * signal handler, doing essentially the same reset.
580              */
581             SP->_sysmouse_head += 1;
582             if (SP->_sysmouse_head == SP->_sysmouse_tail) {
583                 SP->_sysmouse_tail = 0;
584                 SP->_sysmouse_head = 0;
585             }
586
587             /* bump the next-free pointer into the circular list */
588             eventp = NEXT(eventp);
589             result = TRUE;
590         }
591         break;
592 #endif /* USE_SYSMOUSE */
593
594     case M_NONE:
595         break;
596     }
597
598     return result;              /* true if we found an event */
599 }
600
601 static bool
602 _nc_mouse_inline(SCREEN * sp)
603 /* mouse report received in the keyboard stream -- parse its info */
604 {
605     bool result = FALSE;
606
607     TR(MY_TRACE, ("_nc_mouse_inline() called"));
608
609     if (SP->_mouse_type == M_XTERM) {
610         unsigned char kbuf[4];
611         mmask_t prev;
612         size_t grabbed;
613         int res;
614
615         /* This code requires that your xterm entry contain the kmous
616          * capability and that it be set to the \E[M documented in the
617          * Xterm Control Sequences reference.  This is how we
618          * arrange for mouse events to be reported via a KEY_MOUSE
619          * return value from wgetch().  After this value is received,
620          * _nc_mouse_inline() gets called and is immediately
621          * responsible for parsing the mouse status information
622          * following the prefix.
623          *
624          * The following quotes from the ctrlseqs.ms document in the
625          * X distribution, describing the X mouse tracking feature:
626          *
627          * Parameters for all mouse tracking escape sequences
628          * generated by xterm encode numeric parameters in a single
629          * character as value+040.  For example, !  is 1.
630          *
631          * On button press or release, xterm sends ESC [ M CbCxCy.
632          * The low two bits of Cb encode button information: 0=MB1
633          * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release.  The
634          * upper bits encode what modifiers were down when the
635          * button was pressed and are added together.  4=Shift,
636          * 8=Meta, 16=Control.  Cx and Cy are the x and y coordinates
637          * of the mouse event.  The upper left corner is (1,1).
638          *
639          * (End quote)  By the time we get here, we've eaten the
640          * key prefix.  FYI, the loop below is necessary because
641          * mouse click info isn't guaranteed to present as a
642          * single clist item.  It always does under Linux but often
643          * fails to under Solaris.
644          */
645         for (grabbed = 0; grabbed < 3; grabbed += res) {
646
647             /* For VIO mouse we add extra bit 64 to disambiguate button-up. */
648 #if USE_EMX_MOUSE
649             res = read(M_FD(sp) >= 0 ? M_FD(sp) : sp->_ifd, &kbuf, 3);
650 #else
651             res = read(sp->_ifd, kbuf + grabbed, 3 - grabbed);
652 #endif
653             if (res == -1)
654                 break;
655         }
656         kbuf[3] = '\0';
657
658         TR(TRACE_IEVENT,
659            ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
660
661         /* there's only one mouse... */
662         eventp->id = NORMAL_EVENT;
663
664         /* processing code goes here */
665         eventp->bstate = 0;
666         prev = PREV(eventp)->bstate;
667
668 #if USE_EMX_MOUSE
669 #define PRESS_POSITION(n) \
670         eventp->bstate = MASK_PRESS(n); \
671         if (kbuf[0] & 0x40) \
672             eventp->bstate = MASK_RELEASE(n)
673 #else
674 #define PRESS_POSITION(n) \
675         eventp->bstate = (prev & MASK_PRESS(n) \
676                         ? REPORT_MOUSE_POSITION \
677                         : MASK_PRESS(n))
678 #endif
679
680         switch (kbuf[0] & 0x3) {
681         case 0x0:
682             PRESS_POSITION(1);
683             break;
684
685         case 0x1:
686             PRESS_POSITION(2);
687             break;
688
689         case 0x2:
690             PRESS_POSITION(3);
691             break;
692
693         case 0x3:
694             /*
695              * Release events aren't reported for individual buttons, just for
696              * the button set as a whole.  However, because there are normally
697              * no mouse events under xterm that intervene between press and
698              * release, we can infer the button actually released by looking at
699              * the previous event.
700              */
701             if (prev & (BUTTON_PRESSED | BUTTON_RELEASED)) {
702                 eventp->bstate = BUTTON_RELEASED;
703                 if (!(prev & BUTTON1_PRESSED))
704                     eventp->bstate &= ~BUTTON1_RELEASED;
705                 if (!(prev & BUTTON2_PRESSED))
706                     eventp->bstate &= ~BUTTON2_RELEASED;
707                 if (!(prev & BUTTON3_PRESSED))
708                     eventp->bstate &= ~BUTTON3_RELEASED;
709             } else {
710                 /*
711                  * XFree86 xterm will return a stream of release-events to
712                  * let the application know where the mouse is going, if the
713                  * private mode 1002 or 1003 is enabled.
714                  */
715                 eventp->bstate = REPORT_MOUSE_POSITION;
716             }
717             break;
718         }
719         result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
720
721         if (kbuf[0] & 4) {
722             eventp->bstate |= BUTTON_SHIFT;
723         }
724         if (kbuf[0] & 8) {
725             eventp->bstate |= BUTTON_ALT;
726         }
727         if (kbuf[0] & 16) {
728             eventp->bstate |= BUTTON_CTRL;
729         }
730
731         eventp->x = (kbuf[1] - ' ') - 1;
732         eventp->y = (kbuf[2] - ' ') - 1;
733         TR(MY_TRACE,
734            ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
735             _tracemouse(eventp),
736             (long) (eventp - events)));
737
738         /* bump the next-free pointer into the circular list */
739         eventp = NEXT(eventp);
740 #if 0                           /* this return would be needed for QNX's mods to lib_getch.c */
741         return (TRUE);
742 #endif
743     }
744
745     return (result);
746 }
747
748 static void
749 mouse_activate(bool on)
750 {
751     if (!on && !initialized)
752         return;
753
754     _nc_mouse_init();
755
756     if (on) {
757
758         switch (SP->_mouse_type) {
759         case M_XTERM:
760 #if NCURSES_EXT_FUNCS
761             keyok(KEY_MOUSE, on);
762 #endif
763             TPUTS_TRACE("xterm mouse initialization");
764 #if USE_EMX_MOUSE
765             server_state(1);
766 #else
767             enable_xterm_mouse(1);
768 #endif
769             break;
770 #if USE_GPM_SUPPORT
771         case M_GPM:
772             SP->_mouse_fd = gpm_fd;
773             break;
774 #endif
775 #if USE_SYSMOUSE
776         case M_SYSMOUSE:
777             signal(SIGUSR2, handle_sysmouse);
778             break;
779 #endif
780         case M_NONE:
781             return;
782         }
783         /* Make runtime binding to cut down on object size of applications that
784          * do not use the mouse (e.g., 'clear').
785          */
786         SP->_mouse_event = _nc_mouse_event;
787         SP->_mouse_inline = _nc_mouse_inline;
788         SP->_mouse_parse = _nc_mouse_parse;
789         SP->_mouse_resume = _nc_mouse_resume;
790         SP->_mouse_wrap = _nc_mouse_wrap;
791
792     } else {
793
794         switch (SP->_mouse_type) {
795         case M_XTERM:
796             TPUTS_TRACE("xterm mouse deinitialization");
797 #if USE_EMX_MOUSE
798             server_state(0);
799 #else
800             enable_xterm_mouse(0);
801 #endif
802             break;
803 #if USE_GPM_SUPPORT
804         case M_GPM:
805             break;
806 #endif
807 #if USE_SYSMOUSE
808         case M_SYSMOUSE:
809             signal(SIGUSR2, SIG_IGN);
810             break;
811 #endif
812         case M_NONE:
813             return;
814         }
815     }
816     _nc_flush();
817 }
818
819 /**************************************************************************
820  *
821  * Device-independent code
822  *
823  **************************************************************************/
824
825 static bool
826 _nc_mouse_parse(int runcount)
827 /* parse a run of atomic mouse events into a gesture */
828 {
829     MEVENT *ep, *runp, *next, *prev = PREV(eventp);
830     int n;
831     bool merge;
832
833     TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));
834
835     /*
836      * When we enter this routine, the event list next-free pointer
837      * points just past a run of mouse events that we know were separated
838      * in time by less than the critical click interval. The job of this
839      * routine is to collapse this run into a single higher-level event
840      * or gesture.
841      *
842      * We accomplish this in two passes.  The first pass merges press/release
843      * pairs into click events.  The second merges runs of click events into
844      * double or triple-click events.
845      *
846      * It's possible that the run may not resolve to a single event (for
847      * example, if the user quadruple-clicks).  If so, leading events
848      * in the run are ignored.
849      *
850      * Note that this routine is independent of the format of the specific
851      * format of the pointing-device's reports.  We can use it to parse
852      * gestures on anything that reports press/release events on a per-
853      * button basis, as long as the device-dependent mouse code puts stuff
854      * on the queue in MEVENT format.
855      */
856     if (runcount == 1) {
857         TR(MY_TRACE,
858            ("_nc_mouse_parse: returning simple mouse event %s at slot %ld",
859             _tracemouse(prev),
860             (long) (prev - events)));
861         return (prev->id >= NORMAL_EVENT)
862             ? ((prev->bstate & eventmask) ? TRUE : FALSE)
863             : FALSE;
864     }
865
866     /* find the start of the run */
867     runp = eventp;
868     for (n = runcount; n > 0; n--) {
869         runp = PREV(runp);
870     }
871
872 #ifdef TRACE
873     if (_nc_tracing & TRACE_IEVENT) {
874         _trace_slot("before mouse press/release merge:");
875         _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
876                 (long) (runp - events),
877                 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
878                 runcount);
879     }
880 #endif /* TRACE */
881
882     /* first pass; merge press/release pairs */
883     do {
884         merge = FALSE;
885         for (ep = runp; (next = NEXT(ep)) != eventp; ep = next) {
886             if (ep->x == next->x && ep->y == next->y
887                 && (ep->bstate & BUTTON_PRESSED)
888                 && (!(ep->bstate & BUTTON1_PRESSED)
889                     == !(next->bstate & BUTTON1_RELEASED))
890                 && (!(ep->bstate & BUTTON2_PRESSED)
891                     == !(next->bstate & BUTTON2_RELEASED))
892                 && (!(ep->bstate & BUTTON3_PRESSED)
893                     == !(next->bstate & BUTTON3_RELEASED))
894                 ) {
895                 if ((eventmask & BUTTON1_CLICKED)
896                     && (ep->bstate & BUTTON1_PRESSED)) {
897                     ep->bstate &= ~BUTTON1_PRESSED;
898                     ep->bstate |= BUTTON1_CLICKED;
899                     merge = TRUE;
900                 }
901                 if ((eventmask & BUTTON2_CLICKED)
902                     && (ep->bstate & BUTTON2_PRESSED)) {
903                     ep->bstate &= ~BUTTON2_PRESSED;
904                     ep->bstate |= BUTTON2_CLICKED;
905                     merge = TRUE;
906                 }
907                 if ((eventmask & BUTTON3_CLICKED)
908                     && (ep->bstate & BUTTON3_PRESSED)) {
909                     ep->bstate &= ~BUTTON3_PRESSED;
910                     ep->bstate |= BUTTON3_CLICKED;
911                     merge = TRUE;
912                 }
913                 if (merge)
914                     next->id = INVALID_EVENT;
915             }
916         }
917     } while
918         (merge);
919
920 #ifdef TRACE
921     if (_nc_tracing & TRACE_IEVENT) {
922         _trace_slot("before mouse click merge:");
923         _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
924                 (long) (runp - events),
925                 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
926                 runcount);
927     }
928 #endif /* TRACE */
929
930     /*
931      * Second pass; merge click runs.  At this point, click events are
932      * each followed by one invalid event. We merge click events
933      * forward in the queue.
934      *
935      * NOTE: There is a problem with this design!  If the application
936      * allows enough click events to pile up in the circular queue so
937      * they wrap around, it will cheerfully merge the newest forward
938      * into the oldest, creating a bogus doubleclick and confusing
939      * the queue-traversal logic rather badly.  Generally this won't
940      * happen, because calling getmouse() marks old events invalid and
941      * ineligible for merges.  The true solution to this problem would
942      * be to timestamp each MEVENT and perform the obvious sanity check,
943      * but the timer element would have to have sub-second resolution,
944      * which would get us into portability trouble.
945      */
946     do {
947         MEVENT *follower;
948
949         merge = FALSE;
950         for (ep = runp; (next = NEXT(ep)) != eventp; ep = next)
951             if (ep->id != INVALID_EVENT) {
952                 if (next->id != INVALID_EVENT)
953                     continue;
954                 follower = NEXT(next);
955                 if (follower->id == INVALID_EVENT)
956                     continue;
957
958                 /* merge click events forward */
959                 if ((ep->bstate & BUTTON_CLICKED)
960                     && (follower->bstate & BUTTON_CLICKED)) {
961                     if ((eventmask & BUTTON1_DOUBLE_CLICKED)
962                         && (follower->bstate & BUTTON1_CLICKED)) {
963                         follower->bstate &= ~BUTTON1_CLICKED;
964                         follower->bstate |= BUTTON1_DOUBLE_CLICKED;
965                         merge = TRUE;
966                     }
967                     if ((eventmask & BUTTON2_DOUBLE_CLICKED)
968                         && (follower->bstate & BUTTON2_CLICKED)) {
969                         follower->bstate &= ~BUTTON2_CLICKED;
970                         follower->bstate |= BUTTON2_DOUBLE_CLICKED;
971                         merge = TRUE;
972                     }
973                     if ((eventmask & BUTTON3_DOUBLE_CLICKED)
974                         && (follower->bstate & BUTTON3_CLICKED)) {
975                         follower->bstate &= ~BUTTON3_CLICKED;
976                         follower->bstate |= BUTTON3_DOUBLE_CLICKED;
977                         merge = TRUE;
978                     }
979                     if (merge)
980                         ep->id = INVALID_EVENT;
981                 }
982
983                 /* merge double-click events forward */
984                 if ((ep->bstate &
985                      (BUTTON1_DOUBLE_CLICKED
986                       | BUTTON2_DOUBLE_CLICKED
987                       | BUTTON3_DOUBLE_CLICKED))
988                     && (follower->bstate & BUTTON_CLICKED)) {
989                     if ((eventmask & BUTTON1_TRIPLE_CLICKED)
990                         && (follower->bstate & BUTTON1_CLICKED)) {
991                         follower->bstate &= ~BUTTON1_CLICKED;
992                         follower->bstate |= BUTTON1_TRIPLE_CLICKED;
993                         merge = TRUE;
994                     }
995                     if ((eventmask & BUTTON2_TRIPLE_CLICKED)
996                         && (follower->bstate & BUTTON2_CLICKED)) {
997                         follower->bstate &= ~BUTTON2_CLICKED;
998                         follower->bstate |= BUTTON2_TRIPLE_CLICKED;
999                         merge = TRUE;
1000                     }
1001                     if ((eventmask & BUTTON3_TRIPLE_CLICKED)
1002                         && (follower->bstate & BUTTON3_CLICKED)) {
1003                         follower->bstate &= ~BUTTON3_CLICKED;
1004                         follower->bstate |= BUTTON3_TRIPLE_CLICKED;
1005                         merge = TRUE;
1006                     }
1007                     if (merge)
1008                         ep->id = INVALID_EVENT;
1009                 }
1010             }
1011     } while
1012         (merge);
1013
1014 #ifdef TRACE
1015     if (_nc_tracing & TRACE_IEVENT) {
1016         _trace_slot("before mouse event queue compaction:");
1017         _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1018                 (long) (runp - events),
1019                 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
1020                 runcount);
1021     }
1022 #endif /* TRACE */
1023
1024     /*
1025      * Now try to throw away trailing events flagged invalid, or that
1026      * don't match the current event mask.
1027      */
1028     for (; runcount; prev = PREV(eventp), runcount--)
1029         if (prev->id == INVALID_EVENT || !(prev->bstate & eventmask)) {
1030             eventp = prev;
1031         }
1032 #ifdef TRACE
1033     if (_nc_tracing & TRACE_IEVENT) {
1034         _trace_slot("after mouse event queue compaction:");
1035         _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1036                 (long) (runp - events),
1037                 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
1038                 runcount);
1039     }
1040     for (ep = runp; ep != eventp; ep = NEXT(ep))
1041         if (ep->id != INVALID_EVENT)
1042             TR(MY_TRACE,
1043                ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
1044                 _tracemouse(ep),
1045                 (long) (ep - events)));
1046 #endif /* TRACE */
1047
1048     /* after all this, do we have a valid event? */
1049     return (PREV(eventp)->id != INVALID_EVENT);
1050 }
1051
1052 static void
1053 _nc_mouse_wrap(SCREEN * sp GCC_UNUSED)
1054 /* release mouse -- called by endwin() before shellout/exit */
1055 {
1056     TR(MY_TRACE, ("_nc_mouse_wrap() called"));
1057
1058     switch (SP->_mouse_type) {
1059     case M_XTERM:
1060         if (eventmask)
1061             mouse_activate(FALSE);
1062         break;
1063 #if USE_GPM_SUPPORT
1064         /* GPM: pass all mouse events to next client */
1065     case M_GPM:
1066         break;
1067 #endif
1068 #if USE_SYSMOUSE
1069     case M_SYSMOUSE:
1070         mouse_activate(FALSE);
1071         break;
1072 #endif
1073     case M_NONE:
1074         break;
1075     }
1076 }
1077
1078 static void
1079 _nc_mouse_resume(SCREEN * sp GCC_UNUSED)
1080 /* re-connect to mouse -- called by doupdate() after shellout */
1081 {
1082     TR(MY_TRACE, ("_nc_mouse_resume() called"));
1083
1084     switch (SP->_mouse_type) {
1085     case M_XTERM:
1086         /* xterm: re-enable reporting */
1087         if (eventmask)
1088             mouse_activate(TRUE);
1089         break;
1090
1091 #if USE_GPM_SUPPORT
1092     case M_GPM:
1093         /* GPM: reclaim our event set */
1094         break;
1095 #endif
1096
1097 #if USE_SYSMOUSE
1098     case M_SYSMOUSE:
1099         mouse_activate(TRUE);
1100         break;
1101 #endif
1102     case M_NONE:
1103         break;
1104     }
1105 }
1106
1107 /**************************************************************************
1108  *
1109  * Mouse interface entry points for the API
1110  *
1111  **************************************************************************/
1112
1113 NCURSES_EXPORT(int)
1114 getmouse(MEVENT * aevent)
1115 /* grab a copy of the current mouse event */
1116 {
1117     T((T_CALLED("getmouse(%p)"), aevent));
1118
1119     if (aevent && (SP->_mouse_type != M_NONE)) {
1120         /* compute the current-event pointer */
1121         MEVENT *prev = PREV(eventp);
1122
1123         /* copy the event we find there */
1124         *aevent = *prev;
1125
1126         TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
1127                           _tracemouse(prev),
1128                           (long) (prev - events)));
1129
1130         prev->id = INVALID_EVENT;       /* so the queue slot becomes free */
1131         returnCode(OK);
1132     }
1133     returnCode(ERR);
1134 }
1135
1136 NCURSES_EXPORT(int)
1137 ungetmouse(MEVENT * aevent)
1138 /* enqueue a synthesized mouse event to be seen by the next wgetch() */
1139 {
1140     T((T_CALLED("ungetmouse(%p)"), aevent));
1141
1142     /* stick the given event in the next-free slot */
1143     *eventp = *aevent;
1144
1145     /* bump the next-free pointer into the circular list */
1146     eventp = NEXT(eventp);
1147
1148     /* push back the notification event on the keyboard queue */
1149     returnCode(ungetch(KEY_MOUSE));
1150 }
1151
1152 NCURSES_EXPORT(mmask_t)
1153 mousemask(mmask_t newmask, mmask_t * oldmask)
1154 /* set the mouse event mask */
1155 {
1156     mmask_t result = 0;
1157
1158     T((T_CALLED("mousemask(%#lx,%p)"), newmask, oldmask));
1159
1160     if (oldmask)
1161         *oldmask = eventmask;
1162
1163     if (!newmask && !initialized)
1164         returnBits(0);
1165
1166     _nc_mouse_init();
1167     if (SP->_mouse_type != M_NONE) {
1168         eventmask = newmask &
1169             (REPORT_MOUSE_POSITION | BUTTON_ALT | BUTTON_CTRL | BUTTON_SHIFT
1170              | BUTTON_PRESSED
1171              | BUTTON_RELEASED
1172              | BUTTON_CLICKED
1173              | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
1174              | BUTTON2_DOUBLE_CLICKED | BUTTON2_TRIPLE_CLICKED
1175              | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED);
1176
1177         mouse_activate(eventmask != 0);
1178
1179         result = eventmask;
1180     }
1181
1182     returnBits(result);
1183 }
1184
1185 NCURSES_EXPORT(bool)
1186 wenclose(const WINDOW *win, int y, int x)
1187 /* check to see if given window encloses given screen location */
1188 {
1189     bool result = FALSE;
1190
1191     T((T_CALLED("wenclose(%p,%d,%d)"), win, y, x));
1192
1193     if (win != 0) {
1194         y -= win->_yoffset;
1195         result = ((win->_begy <= y &&
1196                    win->_begx <= x &&
1197                    (win->_begx + win->_maxx) >= x &&
1198                    (win->_begy + win->_maxy) >= y) ? TRUE : FALSE);
1199     }
1200     returnBool(result);
1201 }
1202
1203 NCURSES_EXPORT(int)
1204 mouseinterval(int maxclick)
1205 /* set the maximum mouse interval within which to recognize a click */
1206 {
1207     int oldval;
1208
1209     T((T_CALLED("mouseinterval(%d)"), maxclick));
1210
1211     if (SP != 0) {
1212         oldval = SP->_maxclick;
1213         if (maxclick >= 0)
1214             SP->_maxclick = maxclick;
1215     } else {
1216         oldval = DEFAULT_MAXCLICK;
1217     }
1218
1219     returnCode(oldval);
1220 }
1221
1222 /* This may be used by other routines to ask for the existence of mouse
1223    support */
1224 NCURSES_EXPORT(int)
1225 _nc_has_mouse(void)
1226 {
1227     return (SP->_mouse_type == M_NONE ? 0 : 1);
1228 }
1229
1230 NCURSES_EXPORT(bool)
1231 wmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen)
1232 {
1233     bool result = FALSE;
1234
1235     T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"), win, pY, pX, to_screen));
1236
1237     if (win && pY && pX) {
1238         int y = *pY;
1239         int x = *pX;
1240
1241         if (to_screen) {
1242             y += win->_begy + win->_yoffset;
1243             x += win->_begx;
1244             if (wenclose(win, y, x))
1245                 result = TRUE;
1246         } else {
1247             if (wenclose(win, y, x)) {
1248                 y -= (win->_begy + win->_yoffset);
1249                 x -= win->_begx;
1250                 result = TRUE;
1251             }
1252         }
1253         if (result) {
1254             *pX = x;
1255             *pY = y;
1256         }
1257     }
1258     returnBool(result);
1259 }