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