ncurses 4.2
[ncurses.git] / ncurses / lib_mouse.c
1 /****************************************************************************
2  * Copyright (c) 1998 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  ****************************************************************************/
33
34 /*
35  * This module is intended to encapsulate ncurses's interface to pointing
36  * devices.
37  *
38  * The first method used is xterm's internal mouse-tracking facility.
39  * The second (not yet implemented) will be Alessandro Rubini's GPM server.
40  *
41  * Notes for implementors of new mouse-interface methods:
42  *
43  * The code is logically split into a lower level that accepts event reports
44  * in a device-dependent format and an upper level that parses mouse gestures
45  * and filters events.  The mediating data structure is a circular queue of
46  * MEVENT structures.
47  *
48  * Functionally, the lower level's job is to pick up primitive events and
49  * put them on the circular queue.  This can happen in one of two ways:
50  * either (a) _nc_mouse_event() detects a series of incoming mouse reports
51  * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
52  * the keyboard input stream and calls _nc_mouse_inline to queue up a series
53  * of adjacent mouse reports.
54  *
55  * In either case, _nc_mouse_parse() should be called after the series is
56  * accepted to parse the digested mouse reports (low-level MEVENTs) into
57  * a gesture (a high-level or composite MEVENT).
58  *
59  * Don't be too shy about adding new event types or modifiers, if you can find
60  * room for them in the 32-bit mask.  The API is written so that users get
61  * feedback on which theoretical event types they won't see when they call
62  * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
63  * used yet, and a couple of bits open at the high end.
64  */
65
66 #include <curses.priv.h>
67 #include <term.h>
68
69 #if USE_GPM_SUPPORT
70 #ifndef LINT            /* don't need this for llib-lncurses */
71 #undef buttons          /* term.h defines this, and gpm uses it! */
72 #include <gpm.h>
73 #endif
74 #endif
75
76 MODULE_ID("$Id: lib_mouse.c,v 1.35 1998/02/11 12:13:55 tom Exp $")
77
78 #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
79
80 #define INVALID_EVENT   -1
81
82 static int              mousetype;
83 #define M_XTERM         -1      /* use xterm's mouse tracking? */
84 #define M_NONE          0       /* no mouse device */
85 #define M_GPM           1       /* use GPM */
86 #define M_QNX           2       /* QNX mouse on console */
87 #define M_QNX_TERM      3       /* QNX mouse on pterm/xterm (using qansi-m) */
88
89 #if USE_GPM_SUPPORT
90 #ifndef LINT
91 static Gpm_Connect gpm_connect;
92 #endif
93 #endif
94
95 static mmask_t  eventmask;              /* current event mask */
96
97 static bool _nc_mouse_parse(int);
98 static void _nc_mouse_resume(SCREEN *);
99 static void _nc_mouse_wrap(SCREEN *);
100
101 /* maintain a circular list of mouse events */
102
103 /* The definition of the circular list size (EV_MAX), is in curses.priv.h, so
104  * wgetch() may refer to the size and call _nc_mouse_parse() before circular
105  * list overflow.
106  */
107 static MEVENT   events[EV_MAX];         /* hold the last mouse event seen */
108 static MEVENT   *eventp = events;       /* next free slot in event queue */
109 #define NEXT(ep)        ((ep == events + EV_MAX - 1) ? events : ep + 1)
110 #define PREV(ep)        ((ep == events) ? events + EV_MAX - 1 : ep - 1)
111
112 #ifdef TRACE
113 static void _trace_slot(const char *tag)
114 {
115         MEVENT *ep;
116
117         _tracef(tag);
118
119         for (ep = events; ep < events + EV_MAX; ep++)
120                 _tracef("mouse event queue slot %d = %s", ep-events, _tracemouse(ep));
121 }
122 #endif
123
124 /* FIXME: The list of names should be configurable */
125 static int is_xterm(const char *name)
126 {
127     return (!strncmp(name, "xterm", 5)
128       ||    !strncmp(name, "rxvt",  4)
129       ||    !strncmp(name, "kterm", 5)
130       ||    !strncmp(name, "color_xterm", 11));
131 }
132
133 static void _nc_mouse_init(void)
134 /* initialize the mouse */
135 {
136     int i;
137     static int initialized;
138
139     if (initialized) {
140         return;
141     }
142     initialized = TRUE;
143
144     TR(MY_TRACE, ("_nc_mouse_init() called"));
145
146     for (i = 0; i < EV_MAX; i++)
147         events[i].id = INVALID_EVENT;
148
149     /* we know how to recognize mouse events under xterm */
150     if (key_mouse != 0
151      && is_xterm(cur_term->type.term_names))
152         mousetype = M_XTERM;
153
154 #if USE_GPM_SUPPORT
155     else if (!strncmp(cur_term->type.term_names, "linux", 5))
156     {
157         /* GPM: initialize connection to gpm server */
158         gpm_connect.eventMask = GPM_DOWN|GPM_UP;
159         gpm_connect.defaultMask = ~gpm_connect.eventMask;
160         gpm_connect.minMod = 0;
161         gpm_connect.maxMod = ~0;
162         if (Gpm_Open (&gpm_connect, 0) >= 0) { /* returns the file-descriptor */
163             mousetype = M_GPM;
164             SP->_mouse_fd = gpm_fd;
165         }
166     }
167 #endif
168
169     T(("_nc_mouse_init() set mousetype to %d", mousetype));
170 }
171
172 static bool _nc_mouse_event(SCREEN *sp GCC_UNUSED)
173 /* query to see if there is a pending mouse event */
174 {
175 #if USE_GPM_SUPPORT
176     /* GPM: query server for event, return TRUE if we find one */
177     Gpm_Event ev;
178
179     if (gpm_fd >= 0
180      && _nc_timed_wait(2, 0, (int *)0)
181      && Gpm_GetEvent(&ev) == 1)
182     {
183         eventp->id = 0;         /* there's only one mouse... */
184
185         eventp->bstate = 0;
186         switch (ev.type & 0x0f)
187         {
188         case(GPM_DOWN):
189             if (ev.buttons & GPM_B_LEFT)   eventp->bstate |= BUTTON1_PRESSED;
190             if (ev.buttons & GPM_B_MIDDLE) eventp->bstate |= BUTTON2_PRESSED;
191             if (ev.buttons & GPM_B_RIGHT)  eventp->bstate |= BUTTON3_PRESSED;
192             break;
193         case(GPM_UP):
194             if (ev.buttons & GPM_B_LEFT)   eventp->bstate |= BUTTON1_RELEASED;
195             if (ev.buttons & GPM_B_MIDDLE) eventp->bstate |= BUTTON2_RELEASED;
196             if (ev.buttons & GPM_B_RIGHT)  eventp->bstate |= BUTTON3_RELEASED;
197             break;
198         default:
199             break;
200         }
201
202         eventp->x = ev.x - 1;
203         eventp->y = ev.y - 1;
204         eventp->z = 0;
205
206         /* bump the next-free pointer into the circular list */
207         eventp = NEXT(eventp);
208         return (TRUE);
209     }
210 #endif
211
212     /* xterm: never have to query, mouse events are in the keyboard stream */
213     return(FALSE);      /* no event waiting */
214 }
215
216 static bool _nc_mouse_inline(SCREEN *sp)
217 /* mouse report received in the keyboard stream -- parse its info */
218 {
219     TR(MY_TRACE, ("_nc_mouse_inline() called"));
220
221     if (mousetype == M_XTERM)
222     {
223         unsigned char   kbuf[4];
224         MEVENT  *prev;
225         size_t  grabbed;
226         int     res;
227
228         /* This code requires that your xterm entry contain the kmous
229          * capability and that it be set to the \E[M documented in the
230          * Xterm Control Sequences reference.  This is how we
231          * arrange for mouse events to be reported via a KEY_MOUSE
232          * return value from wgetch().  After this value is received,
233          * _nc_mouse_inline() gets called and is immediately
234          * responsible for parsing the mouse status information
235          * following the prefix.
236          *
237          * The following quotes from the ctrlseqs.ms document in the
238          * X distribution, describing the X mouse tracking feature:
239          *
240          * Parameters for all mouse tracking escape sequences
241          * generated by xterm encode numeric parameters in a single
242          * character as value+040.  For example, !  is 1.
243          *
244          * On button press or release, xterm sends ESC [ M CbCxCy.
245          * The low two bits of Cb encode button information: 0=MB1
246          * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release.  The
247          * upper bits encode what modifiers were down when the
248          * button was pressed and are added together.  4=Shift,
249          * 8=Meta, 16=Control.  Cx and Cy are the x and y coordinates
250          * of the mouse event.  The upper left corner is (1,1).
251          *
252          * (End quote)  By the time we get here, we've eaten the
253          * key prefix.  FYI, the loop below is necessary because
254          * mouse click info isn't guaranteed to present as a
255          * single clist item.  It always does under Linux but often
256          * fails to under Solaris.
257          */
258         for (grabbed = 0; grabbed < 3; grabbed += res)
259         {
260              res = read(sp->_ifd, kbuf + grabbed, 3-grabbed);
261              if (res == -1)
262                  break;
263         }
264         kbuf[3] = '\0';
265
266         TR(TRACE_IEVENT, ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
267
268         eventp->id = 0;         /* there's only one mouse... */
269
270         /* processing code goes here */
271         eventp->bstate = 0;
272         switch (kbuf[0] & 0x3)
273         {
274         case 0x0:
275             eventp->bstate = BUTTON1_PRESSED;
276             break;
277
278         case 0x1:
279             eventp->bstate = BUTTON2_PRESSED;
280             break;
281
282         case 0x2:
283             eventp->bstate = BUTTON3_PRESSED;
284             break;
285
286         case 0x3:
287             /*
288              * Release events aren't reported for individual buttons,
289              * just for the button set as a whole...
290              */
291             eventp->bstate =
292                 (BUTTON1_RELEASED |
293                  BUTTON2_RELEASED |
294                  BUTTON3_RELEASED);
295             /*
296              * ...however, because there are no kinds of mouse events under
297              * xterm that can intervene between press and release, we can
298              * deduce which buttons were actually released by looking at the
299              * previous event.
300              */
301             prev = PREV(eventp);
302             if (!(prev->bstate & BUTTON1_PRESSED))
303                 eventp->bstate &=~ BUTTON1_RELEASED;
304             if (!(prev->bstate & BUTTON2_PRESSED))
305                 eventp->bstate &=~ BUTTON2_RELEASED;
306             if (!(prev->bstate & BUTTON3_PRESSED))
307                 eventp->bstate &=~ BUTTON3_RELEASED;
308             break;
309         }
310
311         if (kbuf[0] & 4) {
312             eventp->bstate |= BUTTON_SHIFT;
313         }
314         if (kbuf[0] & 8) {
315             eventp->bstate |= BUTTON_ALT;
316         }
317         if (kbuf[0] & 16) {
318             eventp->bstate |= BUTTON_CTRL;
319         }
320
321         eventp->x = (kbuf[1] - ' ') - 1;
322         eventp->y = (kbuf[2] - ' ') - 1;
323         TR(MY_TRACE, ("_nc_mouse_inline: primitive mouse-event %s has slot %d", _tracemouse(eventp), eventp - events));
324
325         /* bump the next-free pointer into the circular list */
326         eventp = NEXT(eventp);
327 #if 0   /* this return would be needed for QNX's mods to lib_getch.c */
328         return(TRUE);
329 #endif
330     }
331
332     return(FALSE);
333 }
334
335 static void mouse_activate(bool on)
336 {
337     _nc_mouse_init();
338
339     if (on) {
340
341         switch (mousetype) {
342         case M_XTERM:
343 #ifdef NCURSES_EXT_FUNCS
344             keyok(KEY_MOUSE, on);
345 #endif
346             TPUTS_TRACE("xterm mouse initialization");
347             putp("\033[?1000h");
348             break;
349 #if USE_GPM_SUPPORT
350         case M_GPM:
351             SP->_mouse_fd = gpm_fd;
352             break;
353 #endif
354         }
355         /* Make runtime binding to cut down on object size of applications that
356          * do not use the mouse (e.g., 'clear').
357          */
358         SP->_mouse_event  = _nc_mouse_event;
359         SP->_mouse_inline = _nc_mouse_inline;
360         SP->_mouse_parse  = _nc_mouse_parse;
361         SP->_mouse_resume = _nc_mouse_resume;
362         SP->_mouse_wrap   = _nc_mouse_wrap;
363
364     } else {
365
366         switch (mousetype) {
367         case M_XTERM:
368             TPUTS_TRACE("xterm mouse deinitialization");
369             putp("\033[?1000l");
370             break;
371 #if USE_GPM_SUPPORT
372         case M_GPM:
373             break;
374 #endif
375         }
376     }
377     (void) fflush(SP->_ofp);
378 }
379
380 /**************************************************************************
381  *
382  * Device-independent code
383  *
384  **************************************************************************/
385
386 static bool _nc_mouse_parse(int runcount)
387 /* parse a run of atomic mouse events into a gesture */
388 {
389     MEVENT      *ep, *runp, *next, *prev = PREV(eventp);
390     int         n;
391     bool        merge;
392
393     TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));
394
395     /*
396      * When we enter this routine, the event list next-free pointer
397      * points just past a run of mouse events that we know were separated
398      * in time by less than the critical click interval. The job of this
399      * routine is to collaps this run into a single higher-level event
400      * or gesture.
401      *
402      * We accomplish this in two passes.  The first pass merges press/release
403      * pairs into click events.  The second merges runs of click events into
404      * double or triple-click events.
405      *
406      * It's possible that the run may not resolve to a single event (for
407      * example, if the user quadruple-clicks).  If so, leading events
408      * in the run are ignored.
409      *
410      * Note that this routine is independent of the format of the specific
411      * format of the pointing-device's reports.  We can use it to parse
412      * gestures on anything that reports press/release events on a per-
413      * button basis, as long as the device-dependent mouse code puts stuff
414      * on the queue in MEVENT format.
415      */
416     if (runcount == 1)
417     {
418         TR(MY_TRACE, ("_nc_mouse_parse: returning simple mouse event %s at slot %d",
419            _tracemouse(prev), prev-events));
420         return (prev->id >= 0)
421                 ? ((prev->bstate & eventmask) ? TRUE : FALSE)
422                 : FALSE;
423     }
424
425     /* find the start of the run */
426     runp = eventp;
427     for (n = runcount; n > 0; n--) {
428         runp = PREV(runp);
429     }
430
431 #ifdef TRACE
432     if (_nc_tracing & TRACE_IEVENT)
433     {
434         _trace_slot("before mouse press/release merge:");
435         _tracef("_nc_mouse_parse: run starts at %d, ends at %d, count %d",
436             runp-events, ((eventp - events) + (EV_MAX-1)) % EV_MAX, runcount);
437     }
438 #endif /* TRACE */
439
440     /* first pass; merge press/release pairs */
441     do {
442         merge = FALSE;
443         for (ep = runp; next = NEXT(ep), next != eventp; ep = next)
444         {
445             if (ep->x == next->x && ep->y == next->y
446                 && (ep->bstate & (BUTTON1_PRESSED|BUTTON2_PRESSED|BUTTON3_PRESSED))
447                 && (!(ep->bstate & BUTTON1_PRESSED)
448                     == !(next->bstate & BUTTON1_RELEASED))
449                 && (!(ep->bstate & BUTTON2_PRESSED)
450                     == !(next->bstate & BUTTON2_RELEASED))
451                 && (!(ep->bstate & BUTTON3_PRESSED)
452                     == !(next->bstate & BUTTON3_RELEASED))
453                 )
454             {
455                 if ((eventmask & BUTTON1_CLICKED)
456                         && (ep->bstate & BUTTON1_PRESSED))
457                 {
458                     ep->bstate &=~ BUTTON1_PRESSED;
459                     ep->bstate |= BUTTON1_CLICKED;
460                     merge = TRUE;
461                 }
462                 if ((eventmask & BUTTON2_CLICKED)
463                         && (ep->bstate & BUTTON2_PRESSED))
464                 {
465                     ep->bstate &=~ BUTTON2_PRESSED;
466                     ep->bstate |= BUTTON2_CLICKED;
467                     merge = TRUE;
468                 }
469                 if ((eventmask & BUTTON3_CLICKED)
470                         && (ep->bstate & BUTTON3_PRESSED))
471                 {
472                     ep->bstate &=~ BUTTON3_PRESSED;
473                     ep->bstate |= BUTTON3_CLICKED;
474                     merge = TRUE;
475                 }
476                 if (merge)
477                     next->id = INVALID_EVENT;
478             }
479         }
480     } while
481         (merge);
482
483 #ifdef TRACE
484     if (_nc_tracing & TRACE_IEVENT)
485     {
486         _trace_slot("before mouse click merge:");
487         _tracef("_nc_mouse_parse: run starts at %d, ends at %d, count %d",
488             runp-events, ((eventp - events) + (EV_MAX-1)) % EV_MAX, runcount);
489     }
490 #endif /* TRACE */
491
492     /*
493      * Second pass; merge click runs.  At this point, click events are
494      * each followed by one invalid event. We merge click events
495      * forward in the queue.
496      *
497      * NOTE: There is a problem with this design!  If the application
498      * allows enough click events to pile up in the circular queue so
499      * they wrap around, it will cheerfully merge the newest forward
500      * into the oldest, creating a bogus doubleclick and confusing
501      * the queue-traversal logic rather badly.  Generally this won't
502      * happen, because calling getmouse() marks old events invalid and
503      * ineligible for merges.  The true solution to this problem would
504      * be to timestamp each MEVENT and perform the obvious sanity check,
505      * but the timer element would have to have sub-second resolution,
506      * which would get us into portability trouble.
507      */
508     do {
509         MEVENT  *follower;
510
511         merge = FALSE;
512         for (ep = runp; next = NEXT(ep), next != eventp; ep = next)
513             if (ep->id != INVALID_EVENT)
514             {
515                 if (next->id != INVALID_EVENT)
516                     continue;
517                 follower = NEXT(next);
518                 if (follower->id == INVALID_EVENT)
519                     continue;
520
521                 /* merge click events forward */
522                 if ((ep->bstate &
523                         (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED))
524                     && (follower->bstate &
525                         (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)))
526                 {
527                     if ((eventmask & BUTTON1_DOUBLE_CLICKED)
528                         && (follower->bstate & BUTTON1_CLICKED))
529                     {
530                         follower->bstate &=~ BUTTON1_CLICKED;
531                         follower->bstate |= BUTTON1_DOUBLE_CLICKED;
532                         merge = TRUE;
533                     }
534                     if ((eventmask & BUTTON2_DOUBLE_CLICKED)
535                         && (follower->bstate & BUTTON2_CLICKED))
536                     {
537                         follower->bstate &=~ BUTTON2_CLICKED;
538                         follower->bstate |= BUTTON2_DOUBLE_CLICKED;
539                         merge = TRUE;
540                     }
541                     if ((eventmask & BUTTON3_DOUBLE_CLICKED)
542                         && (follower->bstate & BUTTON3_CLICKED))
543                     {
544                         follower->bstate &=~ BUTTON3_CLICKED;
545                         follower->bstate |= BUTTON3_DOUBLE_CLICKED;
546                         merge = TRUE;
547                     }
548                     if (merge)
549                         ep->id = INVALID_EVENT;
550                 }
551
552                 /* merge double-click events forward */
553                 if ((ep->bstate &
554                         (BUTTON1_DOUBLE_CLICKED
555                          | BUTTON2_DOUBLE_CLICKED
556                          | BUTTON3_DOUBLE_CLICKED))
557                     && (follower->bstate &
558                         (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)))
559                 {
560                     if ((eventmask & BUTTON1_TRIPLE_CLICKED)
561                         && (follower->bstate & BUTTON1_CLICKED))
562                     {
563                         follower->bstate &=~ BUTTON1_CLICKED;
564                         follower->bstate |= BUTTON1_TRIPLE_CLICKED;
565                         merge = TRUE;
566                     }
567                     if ((eventmask & BUTTON2_TRIPLE_CLICKED)
568                         && (follower->bstate & BUTTON2_CLICKED))
569                     {
570                         follower->bstate &=~ BUTTON2_CLICKED;
571                         follower->bstate |= BUTTON2_TRIPLE_CLICKED;
572                         merge = TRUE;
573                     }
574                     if ((eventmask & BUTTON3_TRIPLE_CLICKED)
575                         && (follower->bstate & BUTTON3_CLICKED))
576                     {
577                         follower->bstate &=~ BUTTON3_CLICKED;
578                         follower->bstate |= BUTTON3_TRIPLE_CLICKED;
579                         merge = TRUE;
580                     }
581                     if (merge)
582                         ep->id = INVALID_EVENT;
583                 }
584             }
585     } while
586         (merge);
587
588 #ifdef TRACE
589     if (_nc_tracing & TRACE_IEVENT)
590     {
591         _trace_slot("before mouse event queue compaction:");
592         _tracef("_nc_mouse_parse: run starts at %d, ends at %d, count %d",
593             runp-events, ((eventp - events) + (EV_MAX-1)) % EV_MAX, runcount);
594     }
595 #endif /* TRACE */
596
597     /*
598      * Now try to throw away trailing events flagged invalid, or that
599      * don't match the current event mask.
600      */
601     for (; runcount; prev = PREV(eventp), runcount--)
602         if (prev->id == INVALID_EVENT || !(prev->bstate & eventmask)) {
603             eventp = prev;
604         }
605
606 #ifdef TRACE
607     if (_nc_tracing & TRACE_IEVENT)
608     {
609         _trace_slot("after mouse event queue compaction:");
610         _tracef("_nc_mouse_parse: run starts at %d, ends at %d, count %d",
611             runp-events, ((eventp - events) + (EV_MAX-1)) % EV_MAX, runcount);
612     }
613     for (ep = runp; ep != eventp; ep = NEXT(ep))
614         if (ep->id != INVALID_EVENT)
615             TR(MY_TRACE, ("_nc_mouse_parse: returning composite mouse event %s at slot %d",
616                 _tracemouse(ep), ep-events));
617 #endif /* TRACE */
618
619     /* after all this, do we have a valid event? */
620     return(PREV(eventp)->id != INVALID_EVENT);
621 }
622
623 static void _nc_mouse_wrap(SCREEN *sp GCC_UNUSED)
624 /* release mouse -- called by endwin() before shellout/exit */
625 {
626     TR(MY_TRACE, ("_nc_mouse_wrap() called"));
627
628     switch (mousetype) {
629     case M_XTERM:
630         if (eventmask)
631             mouse_activate(FALSE);
632         break;
633 #if USE_GPM_SUPPORT
634         /* GPM: pass all mouse events to next client */
635         case M_GPM:
636             break;
637 #endif
638     }
639 }
640
641 static void _nc_mouse_resume(SCREEN *sp GCC_UNUSED)
642 /* re-connect to mouse -- called by doupdate() after shellout */
643 {
644     TR(MY_TRACE, ("_nc_mouse_resume() called"));
645
646     /* xterm: re-enable reporting */
647     if (mousetype == M_XTERM && eventmask)
648         mouse_activate(TRUE);
649
650     /* GPM: reclaim our event set */
651 }
652
653 /**************************************************************************
654  *
655  * Mouse interface entry points for the API
656  *
657  **************************************************************************/
658
659 int getmouse(MEVENT *aevent)
660 /* grab a copy of the current mouse event */
661 {
662     T((T_CALLED("getmouse(%p)"), aevent));
663
664     if (aevent && (mousetype != M_NONE))
665     {
666         /* compute the current-event pointer */
667         MEVENT  *prev = PREV(eventp);
668
669         /* copy the event we find there */
670         *aevent = *prev;
671
672         TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %d",
673             _tracemouse(prev), prev-events));
674
675         prev->id = INVALID_EVENT;       /* so the queue slot becomes free */
676         returnCode(OK);
677     }
678     returnCode(ERR);
679 }
680
681 int ungetmouse(MEVENT *aevent)
682 /* enqueue a synthesized mouse event to be seen by the next wgetch() */
683 {
684     /* stick the given event in the next-free slot */
685     *eventp = *aevent;
686
687     /* bump the next-free pointer into the circular list */
688     eventp = NEXT(eventp);
689
690     /* push back the notification event on the keyboard queue */
691     return ungetch(KEY_MOUSE);
692 }
693
694 mmask_t mousemask(mmask_t newmask, mmask_t *oldmask)
695 /* set the mouse event mask */
696 {
697     mmask_t result = 0;
698
699     T((T_CALLED("mousemask(%#lx,%p)"), newmask, oldmask));
700
701     if (oldmask)
702         *oldmask = eventmask;
703
704     _nc_mouse_init();
705     if ( mousetype != M_NONE )
706     {
707         eventmask = newmask &
708             (BUTTON_ALT | BUTTON_CTRL | BUTTON_SHIFT
709              | BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED
710              | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
711              | BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_CLICKED
712              | BUTTON2_DOUBLE_CLICKED | BUTTON2_TRIPLE_CLICKED
713              | BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_CLICKED
714              | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED);
715
716         mouse_activate(eventmask != 0);
717
718         result = eventmask;
719     }
720
721     returnCode(result);
722 }
723
724 bool wenclose(const WINDOW *win, int y, int x)
725 /* check to see if given window encloses given screen location */
726 {
727     if (win)
728     {
729         y -= win->_yoffset;
730         return ((win->_begy <= y &&
731                  win->_begx <= x &&
732                  (win->_begx + win->_maxx) >= x &&
733                  (win->_begy + win->_maxy) >= y) ? TRUE : FALSE);
734     }
735     return FALSE;
736 }
737
738 int mouseinterval(int maxclick)
739 /* set the maximum mouse interval within which to recognize a click */
740 {
741     int oldval;
742
743     if (SP != 0) {
744         oldval = SP->_maxclick;
745         if (maxclick >= 0)
746             SP->_maxclick = maxclick;
747     } else {
748         oldval = DEFAULT_MAXCLICK;
749     }
750
751     return(oldval);
752 }
753
754 /* This may be used by other routines to ask for the existence of mouse
755    support */
756 int _nc_has_mouse(void) {
757   return (mousetype==M_NONE ? 0:1);
758 }
759
760 /* lib_mouse.c ends here */