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