1 /****************************************************************************
2 * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
31 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
32 * and: Thomas E. Dickey 1996-on *
33 * and: Juergen Pfeifer 2008 *
34 ****************************************************************************/
37 * This module is intended to encapsulate ncurses's interface to pointing
40 * The primary method used is xterm's internal mouse-tracking facility.
41 * Additional methods depend on the platform:
42 * Alessandro Rubini's GPM server (Linux)
44 * special-purpose mouse interface for OS/2 EMX.
46 * Notes for implementors of new mouse-interface methods:
48 * The code is logically split into a lower level that accepts event reports
49 * in a device-dependent format and an upper level that parses mouse gestures
50 * and filters events. The mediating data structure is a circular queue of
53 * Functionally, the lower level's job is to pick up primitive events and
54 * put them on the circular queue. This can happen in one of two ways:
55 * either (a) _nc_mouse_event() detects a series of incoming mouse reports
56 * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
57 * the keyboard input stream and calls _nc_mouse_inline to queue up a series
58 * of adjacent mouse reports.
60 * In either case, _nc_mouse_parse() should be called after the series is
61 * accepted to parse the digested mouse reports (low-level MEVENTs) into
62 * a gesture (a high-level or composite MEVENT).
64 * Don't be too shy about adding new event types or modifiers, if you can find
65 * room for them in the 32-bit mask. The API is written so that users get
66 * feedback on which theoretical event types they won't see when they call
67 * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
68 * used yet, and a couple of bits open at the high end.
77 # define INCL_DOSPROCESS
78 # include <os2.h> /* Need to include before the others */
81 #include <curses.priv.h>
84 #define CUR SP_TERMTYPE
87 MODULE_ID("$Id: lib_mouse.c,v 1.166 2015/07/05 00:28:27 tom Exp $")
92 #include <linux/keyboard.h> /* defines KG_* macros */
95 /* use dynamic loader to avoid linkage dependency */
99 #define my_RTLD RTLD_NOW
102 #define my_RTLD RTLD_LAZY
106 #endif /* RTLD_NOW */
107 #endif /* HAVE_LIBDL */
109 #endif /* USE_GPM_SUPPORT */
112 #undef buttons /* symbol conflict in consio.h */
113 #undef mouse_info /* symbol conflict in consio.h */
114 #include <osreldate.h>
115 #if defined(__DragonFly_version) || (defined(__FreeBSD__) && (__FreeBSD_version >= 400017))
116 #include <sys/consio.h>
117 #include <sys/fbio.h>
119 #include <machine/console.h>
121 #endif /* use_SYSMOUSE */
124 #include <sys/socket.h>
125 #define pipe(handles) socketpair(AF_LOCAL, SOCK_STREAM, 0, handles)
126 #define DosWrite(hfile, pbuffer, cbwrite, pcbactual) \
127 write(hfile, pbuffer, cbwrite)
128 #define DosExit(action, result ) /* do nothing */
129 #define DosCreateThread(ptid, pfn, param, flag, cbStack) \
130 (*(ptid) = _beginthread(pfn, NULL, cbStack, \
131 (void *)param), (*(ptid) == -1))
134 #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
136 #define MASK_RELEASE(x) (mmask_t) NCURSES_MOUSE_MASK(x, 001)
137 #define MASK_PRESS(x) (mmask_t) NCURSES_MOUSE_MASK(x, 002)
138 #define MASK_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 004)
139 #define MASK_DOUBLE_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 010)
140 #define MASK_TRIPLE_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 020)
141 #define MASK_RESERVED_EVENT(x) (mmask_t) NCURSES_MOUSE_MASK(x, 040)
143 #if NCURSES_MOUSE_VERSION == 1
144 #define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED)
145 #define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED)
146 #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED)
147 #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED)
148 #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED)
149 #define MAX_BUTTONS 4
151 #define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED | BUTTON5_CLICKED)
152 #define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED | BUTTON5_PRESSED)
153 #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED | BUTTON5_RELEASED)
154 #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED)
155 #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED)
156 #define MAX_BUTTONS 5
159 #define INVALID_EVENT -1
160 #define NORMAL_EVENT 0
162 #define ValidEvent(ep) ((ep)->id != INVALID_EVENT)
163 #define Invalidate(ep) (ep)->id = INVALID_EVENT
167 #ifndef LIBGPM_SONAME
168 #define LIBGPM_SONAME "libgpm.so"
171 #define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(sp->_dlopen_gpm, #name))
173 #endif /* USE_GPM_SUPPORT */
175 static bool _nc_mouse_parse(SCREEN
*, int);
176 static void _nc_mouse_resume(SCREEN
*);
177 static void _nc_mouse_wrap(SCREEN
*);
179 /* maintain a circular list of mouse events */
181 #define FirstEV(sp) ((sp)->_mouse_events)
182 #define LastEV(sp) ((sp)->_mouse_events + EV_MAX - 1)
185 #define NEXT(ep) ((ep >= LastEV(SP_PARM)) \
190 #define PREV(ep) ((ep <= FirstEV(SP_PARM)) \
194 #define IndexEV(sp, ep) (ep - FirstEV(sp))
196 #define RunParams(sp, eventp, runp) \
197 (long) IndexEV(sp, runp), \
198 (long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX
202 _trace_slot(SCREEN
*sp
, const char *tag
)
208 for (ep
= FirstEV(sp
); ep
<= LastEV(sp
); ep
++)
209 _tracef("mouse event queue slot %ld = %s",
210 (long) IndexEV(sp
, ep
),
211 _nc_tracemouse(sp
, ep
));
220 # define M_FD(sp) sp->_mouse_fd
223 write_event(SCREEN
*sp
, int down
, int button
, int x
, int y
)
226 unsigned long ignore
;
228 strcpy(buf
, "\033[M"); /* should be the same as key_mouse */
229 buf
[3] = ' ' + (button
- 1) + (down
? 0 : 0x40);
230 buf
[4] = ' ' + x
- LEFT_COL
+ 1;
231 buf
[5] = ' ' + y
- TOP_ROW
+ 1;
232 DosWrite(sp
->_emxmouse_wfd
, buf
, 6, &ignore
);
237 mouse_server(void *param
)
239 mouse_server(unsigned long param
)
242 SCREEN
*sp
= (SCREEN
*) param
;
243 unsigned short fWait
= MOU_WAIT
;
244 /* NOPTRRECT mourt = { 0,0,24,79 }; */
247 unsigned short mask
= MOUSE_BN1_DOWN
| MOUSE_BN2_DOWN
| MOUSE_BN3_DOWN
;
253 /* open the handle for the mouse */
254 if (MouOpen(NULL
, &hmou
) == 0) {
255 rc
= MouSetEventMask(&mask
, hmou
);
256 if (rc
) { /* retry with 2 buttons */
257 mask
= MOUSE_BN1_DOWN
| MOUSE_BN2_DOWN
;
258 rc
= MouSetEventMask(&mask
, hmou
);
261 if (rc
== 0 && MouDrawPtr(hmou
) == 0) {
263 /* sit and wait on the event queue */
264 rc
= MouReadEventQue(&mouev
, &fWait
, hmou
);
266 _nc_SPRINTF(err
, _nc_SLIMIT(sizeof(err
))
267 "Error reading mouse queue, rc=%lu.\r\n", rc
);
270 if (!sp
->_emxmouse_activated
)
274 * OS/2 numbers a 3-button mouse inconsistently from other
280 if ((mouev
.fs
^ oldstate
) & MOUSE_BN1_DOWN
)
281 write_event(sp
, mouev
.fs
& MOUSE_BN1_DOWN
,
282 sp
->_emxmouse_buttons
[1], mouev
.col
, mouev
.row
);
283 if ((mouev
.fs
^ oldstate
) & MOUSE_BN2_DOWN
)
284 write_event(sp
, mouev
.fs
& MOUSE_BN2_DOWN
,
285 sp
->_emxmouse_buttons
[3], mouev
.col
, mouev
.row
);
286 if ((mouev
.fs
^ oldstate
) & MOUSE_BN3_DOWN
)
287 write_event(sp
, mouev
.fs
& MOUSE_BN3_DOWN
,
288 sp
->_emxmouse_buttons
[2], mouev
.col
, mouev
.row
);
294 _nc_SPRINTF(err
, _nc_SLIMIT(sizeof(err
))
295 "Error setting event mask, buttons=%d, rc=%lu.\r\n",
299 DosWrite(2, err
, strlen(err
), &rc
);
302 DosExit(EXIT_THREAD
, 0L);
305 #endif /* USE_EMX_MOUSE */
309 sysmouse_server(SCREEN
*sp
)
311 struct mouse_info the_mouse
;
314 the_mouse
.operation
= MOUSE_GETINFO
;
316 && sp
->_mouse_fd
>= 0
317 && sp
->_sysmouse_tail
< FIFO_SIZE
318 && ioctl(sp
->_mouse_fd
, CONS_MOUSECTL
, &the_mouse
) != -1) {
320 if (sp
->_sysmouse_head
> sp
->_sysmouse_tail
) {
321 sp
->_sysmouse_tail
= 0;
322 sp
->_sysmouse_head
= 0;
324 work
= &(sp
->_sysmouse_fifo
[sp
->_sysmouse_tail
]);
325 memset(work
, 0, sizeof(*work
));
326 work
->id
= NORMAL_EVENT
; /* there's only one mouse... */
328 sp
->_sysmouse_old_buttons
= sp
->_sysmouse_new_buttons
;
329 sp
->_sysmouse_new_buttons
= the_mouse
.u
.data
.buttons
& 0x7;
331 if (sp
->_sysmouse_new_buttons
) {
332 if (sp
->_sysmouse_new_buttons
& 1)
333 work
->bstate
|= BUTTON1_PRESSED
;
334 if (sp
->_sysmouse_new_buttons
& 2)
335 work
->bstate
|= BUTTON2_PRESSED
;
336 if (sp
->_sysmouse_new_buttons
& 4)
337 work
->bstate
|= BUTTON3_PRESSED
;
339 if (sp
->_sysmouse_old_buttons
& 1)
340 work
->bstate
|= BUTTON1_RELEASED
;
341 if (sp
->_sysmouse_old_buttons
& 2)
342 work
->bstate
|= BUTTON2_RELEASED
;
343 if (sp
->_sysmouse_old_buttons
& 4)
344 work
->bstate
|= BUTTON3_RELEASED
;
347 /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */
348 the_mouse
.operation
= MOUSE_HIDE
;
349 ioctl(sp
->_mouse_fd
, CONS_MOUSECTL
, &the_mouse
);
350 the_mouse
.operation
= MOUSE_SHOW
;
351 ioctl(sp
->_mouse_fd
, CONS_MOUSECTL
, &the_mouse
);
354 * We're only interested if the button is pressed or released.
355 * FIXME: implement continuous event-tracking.
357 if (sp
->_sysmouse_new_buttons
!= sp
->_sysmouse_old_buttons
) {
358 sp
->_sysmouse_tail
+= 1;
360 work
->x
= the_mouse
.u
.data
.x
/ sp
->_sysmouse_char_width
;
361 work
->y
= the_mouse
.u
.data
.y
/ sp
->_sysmouse_char_height
;
366 handle_sysmouse(int sig GCC_UNUSED
)
368 sysmouse_server(CURRENT_SCREEN
);
370 #endif /* USE_SYSMOUSE */
372 #ifndef USE_TERM_DRIVER
373 #define xterm_kmous "\033[M"
376 init_xterm_mouse(SCREEN
*sp
)
378 sp
->_mouse_type
= M_XTERM
;
379 sp
->_mouse_format
= MF_X10
;
380 sp
->_mouse_xtermcap
= tigetstr("XM");
381 if (VALID_STRING(sp
->_mouse_xtermcap
)) {
382 char *code
= strstr(sp
->_mouse_xtermcap
, "[?");
385 while ((*code
>= '0') && (*code
<= '9')) {
387 while ((*next
>= '0') && (*next
<= '9')) {
390 if (!strncmp(code
, "1006", (size_t) (next
- code
))) {
391 sp
->_mouse_format
= MF_SGR1006
;
393 #ifdef EXP_XTERM_1005
394 if (!strncmp(code
, "1005", (size_t) (next
- code
))) {
395 sp
->_mouse_format
= MF_XTERM_1005
;
399 while (*next
== ';') {
409 int code
= tigetnum("XM");
417 sp
->_mouse_xtermcap
= "\033[?1000%?%p1%{1}%=%th%el%;";
423 enable_xterm_mouse(SCREEN
*sp
, int enable
)
426 sp
->_emxmouse_activated
= enable
;
428 NCURSES_PUTP2("xterm-mouse", TPARM_1(sp
->_mouse_xtermcap
, enable
));
430 sp
->_mouse_active
= enable
;
435 allow_gpm_mouse(SCREEN
*sp GCC_UNUSED
)
440 /* Danger Robinson: do not use dlopen for libgpm if already loaded */
442 if (!sp
->_mouse_gpm_loaded
) {
443 T(("GPM library was already dlopen'd, not by us"));
447 /* GPM does printf's without checking if stdout is a terminal */
448 if (NC_ISATTY(fileno(stdout
))) {
449 const char *list
= getenv("NCURSES_GPM_TERMS");
450 const char *env
= getenv("TERM");
453 result
= _nc_name_match(list
, env
, "|:");
456 /* GPM checks the beginning of the $TERM variable to decide if it
457 * should pass xterm events through. There is no real advantage in
458 * allowing GPM to do this. Recent versions relax that check, and
459 * pretend that GPM can work with any terminal having the kmous
460 * capability. Perhaps that works for someone. If so, they can
461 * set the environment variable (above).
463 if (env
!= 0 && strstr(env
, "linux") != 0) {
473 unload_gpm_library(SCREEN
*sp
)
475 if (sp
->_dlopen_gpm
!= 0) {
476 T(("unload GPM library"));
477 sp
->_mouse_gpm_loaded
= FALSE
;
479 dlclose(sp
->_dlopen_gpm
);
485 load_gpm_library(SCREEN
*sp
)
487 sp
->_mouse_gpm_found
= FALSE
;
488 if ((sp
->_dlopen_gpm
= dlopen(LIBGPM_SONAME
, my_RTLD
)) != 0) {
489 if (GET_DLSYM(gpm_fd
) == 0 ||
490 GET_DLSYM(Gpm_Open
) == 0 ||
491 GET_DLSYM(Gpm_Close
) == 0 ||
492 GET_DLSYM(Gpm_GetEvent
) == 0) {
493 T(("GPM initialization failed: %s", dlerror()));
494 unload_gpm_library(sp
);
496 sp
->_mouse_gpm_found
= TRUE
;
497 sp
->_mouse_gpm_loaded
= TRUE
;
504 enable_gpm_mouse(SCREEN
*sp
, bool enable
)
508 T((T_CALLED("enable_gpm_mouse(%d)"), enable
));
510 if (enable
&& !sp
->_mouse_active
) {
512 if (sp
->_mouse_gpm_found
&& !sp
->_mouse_gpm_loaded
) {
513 load_gpm_library(sp
);
516 if (sp
->_mouse_gpm_loaded
) {
519 /* GPM: initialize connection to gpm server */
520 sp
->_mouse_gpm_connect
.eventMask
= GPM_DOWN
| GPM_UP
;
521 sp
->_mouse_gpm_connect
.defaultMask
=
522 (unsigned short) (~(sp
->_mouse_gpm_connect
.eventMask
| GPM_HARD
));
523 sp
->_mouse_gpm_connect
.minMod
= 0;
524 sp
->_mouse_gpm_connect
.maxMod
=
525 (unsigned short) (~((1 << KG_SHIFT
) |
529 * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open.
530 * The former is recognized by wscons (SunOS), and the latter by
531 * xterm. Those will not show up in ncurses' traces.
533 code
= my_Gpm_Open(&sp
->_mouse_gpm_connect
, 0);
534 result
= (code
>= 0);
537 * GPM can return a -2 if it is trying to do something with xterm.
538 * Ignore that, since it conflicts with our use of stdin.
546 sp
->_mouse_active
= result
;
547 T(("GPM open %s", result
? "succeeded" : "failed"));
549 if (!enable
&& sp
->_mouse_active
) {
550 /* GPM: close connection to gpm server */
552 sp
->_mouse_active
= FALSE
;
559 unload_gpm_library(sp
);
564 #endif /* USE_GPM_SUPPORT */
567 initialize_mousetype(SCREEN
*sp
)
569 T((T_CALLED("initialize_mousetype()")));
571 /* Try gpm first, because gpm may be configured to run in xterm */
573 if (allow_gpm_mouse(sp
)) {
574 if (!sp
->_mouse_gpm_loaded
) {
576 load_gpm_library(sp
);
577 #else /* !HAVE_LIBDL */
578 sp
->_mouse_gpm_found
= TRUE
;
579 sp
->_mouse_gpm_loaded
= TRUE
;
584 * The gpm_fd file-descriptor may be negative (xterm). So we have to
585 * maintain our notion of whether the mouse connection is active
586 * without testing the file-descriptor.
588 if (sp
->_mouse_gpm_found
&& enable_gpm_mouse(sp
, TRUE
)) {
589 sp
->_mouse_type
= M_GPM
;
590 sp
->_mouse_fd
= *(my_gpm_fd
);
591 T(("GPM mouse_fd %d", sp
->_mouse_fd
));
595 #endif /* USE_GPM_SUPPORT */
599 if (!sp
->_emxmouse_thread
600 && strstr(TerminalOf(sp
)->type
.term_names
, "xterm") == 0
604 if (pipe(handles
) < 0) {
605 perror("mouse pipe error");
610 if (!sp
->_emxmouse_buttons
[0]) {
611 const char *s
= getenv("MOUSE_BUTTONS_123");
613 sp
->_emxmouse_buttons
[0] = 1;
614 if (s
&& strlen(s
) >= 3) {
615 sp
->_emxmouse_buttons
[1] = s
[0] - '0';
616 sp
->_emxmouse_buttons
[2] = s
[1] - '0';
617 sp
->_emxmouse_buttons
[3] = s
[2] - '0';
619 sp
->_emxmouse_buttons
[1] = 1;
620 sp
->_emxmouse_buttons
[2] = 3;
621 sp
->_emxmouse_buttons
[3] = 2;
624 sp
->_emxmouse_wfd
= handles
[1];
625 M_FD(sp
) = handles
[0];
627 setmode(handles
[0], O_BINARY
);
628 setmode(handles
[1], O_BINARY
);
629 /* Do not use CRT functions, we may single-threaded. */
630 rc
= DosCreateThread((unsigned long *) &sp
->_emxmouse_thread
,
631 mouse_server
, (long) sp
, 0, 8192);
633 printf("mouse thread error %d=%#x", rc
, rc
);
635 sp
->_mouse_type
= M_XTERM
;
640 #endif /* USE_EMX_MOUSE */
644 struct mouse_info the_mouse
;
645 char *the_device
= 0;
647 if (NC_ISATTY(sp
->_ifd
))
648 the_device
= ttyname(sp
->_ifd
);
650 the_device
= "/dev/tty";
652 sp
->_mouse_fd
= open(the_device
, O_RDWR
);
654 if (sp
->_mouse_fd
>= 0) {
656 * sysmouse does not have a usable user interface for obtaining
657 * mouse events. The logical way to proceed (reading data on a
658 * stream) only works if one opens the device as root. Even in
659 * that mode, careful examination shows we lose events
660 * occasionally. The interface provided for user programs is to
661 * establish a signal handler. really.
663 * Take over SIGUSR2 for this purpose since SIGUSR1 is more
664 * likely to be used by an application. getch() will have to
665 * handle the misleading EINTR's.
667 signal(SIGUSR2
, SIG_IGN
);
668 the_mouse
.operation
= MOUSE_MODE
;
669 the_mouse
.u
.mode
.mode
= 0;
670 the_mouse
.u
.mode
.signal
= SIGUSR2
;
671 if (ioctl(sp
->_mouse_fd
, CONS_MOUSECTL
, &the_mouse
) != -1) {
672 signal(SIGUSR2
, handle_sysmouse
);
673 the_mouse
.operation
= MOUSE_SHOW
;
674 ioctl(sp
->_mouse_fd
, CONS_MOUSECTL
, &the_mouse
);
676 #if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) /* FreeBSD > 2.x */
678 #ifndef FBIO_GETMODE /* FreeBSD 3.x */
679 #define FBIO_GETMODE CONS_GET
680 #define FBIO_MODEINFO CONS_MODEINFO
681 #endif /* FBIO_GETMODE */
682 video_info_t the_video
;
684 if (ioctl(sp
->_mouse_fd
,
686 &the_video
.vi_mode
) != -1
687 && ioctl(sp
->_mouse_fd
,
690 sp
->_sysmouse_char_width
= the_video
.vi_cwidth
;
691 sp
->_sysmouse_char_height
= the_video
.vi_cheight
;
694 #endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */
696 if (sp
->_sysmouse_char_width
<= 0)
697 sp
->_sysmouse_char_width
= 8;
698 if (sp
->_sysmouse_char_height
<= 0)
699 sp
->_sysmouse_char_height
= 16;
700 sp
->_mouse_type
= M_SYSMOUSE
;
705 #endif /* USE_SYSMOUSE */
707 #ifdef USE_TERM_DRIVER
708 CallDriver(sp
, td_initmouse
);
710 /* we know how to recognize mouse events under "xterm" */
711 if (key_mouse
!= 0) {
712 if (!strcmp(key_mouse
, xterm_kmous
)
713 || strstr(TerminalOf(sp
)->type
.term_names
, "xterm") != 0) {
714 init_xterm_mouse(sp
);
716 } else if (strstr(TerminalOf(sp
)->type
.term_names
, "xterm") != 0) {
717 if (_nc_add_to_try(&(sp
->_keytry
), xterm_kmous
, KEY_MOUSE
) == OK
)
718 init_xterm_mouse(sp
);
726 _nc_mouse_init(SCREEN
*sp
)
727 /* initialize the mouse */
733 if (!sp
->_mouse_initialized
) {
734 sp
->_mouse_initialized
= TRUE
;
736 TR(MY_TRACE
, ("_nc_mouse_init() called"));
738 sp
->_mouse_eventp
= FirstEV(sp
);
739 for (i
= 0; i
< EV_MAX
; i
++)
740 Invalidate(sp
->_mouse_events
+ i
);
742 initialize_mousetype(sp
);
744 T(("_nc_mouse_init() set mousetype to %d", sp
->_mouse_type
));
746 result
= sp
->_mouse_initialized
;
752 * Query to see if there is a pending mouse event. This is called from
753 * fifo_push() in lib_getch.c
756 _nc_mouse_event(SCREEN
*sp
)
758 MEVENT
*eventp
= sp
->_mouse_eventp
;
763 switch (sp
->_mouse_type
) {
765 /* xterm: never have to query, mouse events are in the keyboard stream */
770 int i
, res
= read(M_FD(sp
), &kbuf
, 3); /* Eat the prefix */
772 printf("Got %d chars instead of 3 for prefix.\n", res
);
773 for (i
= 0; i
< res
; i
++) {
774 if (kbuf
[i
] != key_mouse
[i
])
775 printf("Got char %d instead of %d for prefix.\n",
776 (int) kbuf
[i
], (int) key_mouse
[i
]);
780 #endif /* USE_EMX_MOUSE */
785 if (sp
->_mouse_fd
>= 0) {
786 /* query server for event, return TRUE if we find one */
789 switch (my_Gpm_GetEvent(&ev
)) {
791 /* Connection closed, drop the mouse. */
795 /* there's only one mouse... */
796 eventp
->id
= NORMAL_EVENT
;
799 switch (ev
.type
& 0x0f) {
801 if (ev
.buttons
& GPM_B_LEFT
)
802 eventp
->bstate
|= BUTTON1_PRESSED
;
803 if (ev
.buttons
& GPM_B_MIDDLE
)
804 eventp
->bstate
|= BUTTON2_PRESSED
;
805 if (ev
.buttons
& GPM_B_RIGHT
)
806 eventp
->bstate
|= BUTTON3_PRESSED
;
809 if (ev
.buttons
& GPM_B_LEFT
)
810 eventp
->bstate
|= BUTTON1_RELEASED
;
811 if (ev
.buttons
& GPM_B_MIDDLE
)
812 eventp
->bstate
|= BUTTON2_RELEASED
;
813 if (ev
.buttons
& GPM_B_RIGHT
)
814 eventp
->bstate
|= BUTTON3_RELEASED
;
817 eventp
->bstate
|= REPORT_MOUSE_POSITION
;
821 eventp
->x
= ev
.x
- 1;
822 eventp
->y
= ev
.y
- 1;
825 /* bump the next-free pointer into the circular list */
826 sp
->_mouse_eventp
= NEXT(eventp
);
836 if (sp
->_sysmouse_head
< sp
->_sysmouse_tail
) {
837 *eventp
= sp
->_sysmouse_fifo
[sp
->_sysmouse_head
];
840 * Point the fifo-head to the next possible location. If there
841 * are none, reset the indices. This may be interrupted by the
842 * signal handler, doing essentially the same reset.
844 sp
->_sysmouse_head
+= 1;
845 if (sp
->_sysmouse_head
== sp
->_sysmouse_tail
) {
846 sp
->_sysmouse_tail
= 0;
847 sp
->_sysmouse_head
= 0;
850 /* bump the next-free pointer into the circular list */
851 sp
->_mouse_eventp
= eventp
= NEXT(eventp
);
855 #endif /* USE_SYSMOUSE */
857 #ifdef USE_TERM_DRIVER
859 while (sp
->_drv_mouse_head
< sp
->_drv_mouse_tail
) {
860 *eventp
= sp
->_drv_mouse_fifo
[sp
->_drv_mouse_head
];
863 * Point the fifo-head to the next possible location. If there
864 * are none, reset the indices.
866 sp
->_drv_mouse_head
+= 1;
867 if (sp
->_drv_mouse_head
== sp
->_drv_mouse_tail
) {
868 sp
->_drv_mouse_tail
= 0;
869 sp
->_drv_mouse_head
= 0;
872 /* bump the next-free pointer into the circular list */
873 sp
->_mouse_eventp
= eventp
= NEXT(eventp
);
883 return result
; /* true if we found an event */
887 #define PRESS_POSITION(n) \
889 eventp->bstate = MASK_PRESS(n); \
890 sp->_mouse_bstate |= MASK_PRESS(n); \
891 if (kbuf[0] & 0x40) { \
892 eventp->bstate = MASK_RELEASE(n); \
893 sp->_mouse_bstate &= ~MASK_PRESS(n); \
897 #define PRESS_POSITION(n) \
899 eventp->bstate = (mmask_t) (sp->_mouse_bstate & MASK_PRESS(n) \
900 ? REPORT_MOUSE_POSITION \
902 sp->_mouse_bstate |= MASK_PRESS(n); \
907 handle_wheel(SCREEN
*sp
, MEVENT
* eventp
, int button
, int wheel
)
911 switch (button
& 3) {
914 eventp
->bstate
= MASK_PRESS(4);
915 /* Do not record in sp->_mouse_bstate; there will be no
916 * corresponding release event.
924 #if NCURSES_MOUSE_VERSION == 2
925 eventp
->bstate
= MASK_PRESS(5);
926 /* See comment above for button 4 */
928 /* Ignore this event as it is not a true press of the button */
929 eventp
->bstate
= REPORT_MOUSE_POSITION
;
946 decode_X10_bstate(SCREEN
*sp
, MEVENT
* eventp
, unsigned intro
)
953 if (!handle_wheel(sp
, eventp
, (int) intro
, (intro
& 96) == 96)) {
955 * Release events aren't reported for individual buttons, just for
956 * the button set as a whole. However, because there are normally
957 * no mouse events under xterm that intervene between press and
958 * release, we can infer the button actually released by looking at
959 * the previous event.
961 if (sp
->_mouse_bstate
& BUTTON_PRESSED
) {
962 eventp
->bstate
= BUTTON_RELEASED
;
963 for (b
= 1; b
<= MAX_BUTTONS
; ++b
) {
964 if (!(sp
->_mouse_bstate
& MASK_PRESS(b
)))
965 eventp
->bstate
&= ~MASK_RELEASE(b
);
967 sp
->_mouse_bstate
= 0;
970 * xterm will return a stream of release-events to let the
971 * application know where the mouse is going, if private mode
972 * 1002 or 1003 is enabled.
974 eventp
->bstate
= REPORT_MOUSE_POSITION
;
979 eventp
->bstate
|= BUTTON_SHIFT
;
982 eventp
->bstate
|= BUTTON_ALT
;
985 eventp
->bstate
|= BUTTON_CTRL
;
987 result
= (eventp
->bstate
& REPORT_MOUSE_POSITION
) ? TRUE
: FALSE
;
991 /* This code requires that your xterm entry contain the kmous capability and
992 * that it be set to the \E[M documented in the Xterm Control Sequences
993 * reference. This is how we arrange for mouse events to be reported via a
994 * KEY_MOUSE return value from wgetch(). After this value is received,
995 * _nc_mouse_inline() gets called and is immediately responsible for parsing
996 * the mouse status information following the prefix.
998 * The following quotes from the ctlseqs.ms document in the XTerm distribution,
999 * describing the mouse tracking feature:
1001 * Parameters for all mouse tracking escape sequences generated by xterm encode
1002 * numeric parameters in a single character as value+040. For example, ! is
1005 * On button press or release, xterm sends ESC [ M CbCxCy. The low two bits of
1006 * Cb encode button information: 0=MB1 pressed, 1=MB2 pressed, 2=MB3 pressed,
1007 * 3=release. The upper bits encode what modifiers were down when the button
1008 * was pressed and are added together. 4=Shift, 8=Meta, 16=Control. Cx and Cy
1009 * are the x and y coordinates of the mouse event. The upper left corner is
1012 * (End quote) By the time we get here, we've eaten the key prefix. FYI, the
1013 * loop below is necessary because mouse click info isn't guaranteed to present
1014 * as a single clist item.
1016 * Wheel mice may return buttons 4 and 5 when the wheel is turned. We encode
1017 * those as button presses.
1020 decode_xterm_X10(SCREEN
*sp
, MEVENT
* eventp
)
1022 unsigned char kbuf
[4];
1027 # if USE_PTHREADS_EINTR
1028 # if USE_WEAK_SYMBOLS
1029 if ((pthread_self
) && (pthread_kill
) && (pthread_equal
))
1031 _nc_globals
.read_thread
= pthread_self();
1033 for (grabbed
= 0; grabbed
< 3; grabbed
+= (size_t) res
) {
1035 /* For VIO mouse we add extra bit 64 to disambiguate button-up. */
1038 (M_FD(sp
) >= 0) ? M_FD(sp
) : sp
->_ifd
,
1042 kbuf
+ grabbed
, 3 - grabbed
);
1046 #if USE_PTHREADS_EINTR
1047 _nc_globals
.read_thread
= 0;
1052 ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf
));
1054 /* there's only one mouse... */
1055 eventp
->id
= NORMAL_EVENT
;
1057 result
= decode_X10_bstate(sp
, eventp
, kbuf
[0]);
1059 eventp
->x
= (kbuf
[1] - ' ') - 1;
1060 eventp
->y
= (kbuf
[2] - ' ') - 1;
1065 #ifdef EXP_XTERM_1005
1067 * This is identical to X10/X11 responses except that there are two UTF-8
1068 * characters storing the ordinates instead of two bytes.
1071 decode_xterm_1005(SCREEN
*sp
, MEVENT
* eventp
)
1075 size_t limit
= (sizeof(kbuf
) - 1);
1083 # if USE_PTHREADS_EINTR
1084 # if USE_WEAK_SYMBOLS
1085 if ((pthread_self
) && (pthread_kill
) && (pthread_equal
))
1087 _nc_globals
.read_thread
= pthread_self();
1089 for (grabbed
= 0; grabbed
< limit
;) {
1093 (M_FD(sp
) >= 0) ? M_FD(sp
) : sp
->_ifd
,
1100 grabbed
+= (size_t) res
;
1105 for (n
= 0; n
< 2; ++n
) {
1106 if (check
>= grabbed
)
1108 rc
= _nc_conv_to_utf32(&coords
[n
], kbuf
+ check
, (unsigned)
1112 check
+= (size_t) rc
;
1118 #if USE_PTHREADS_EINTR
1119 _nc_globals
.read_thread
= 0;
1123 ("_nc_mouse_inline sees the following xterm data: %s",
1124 _nc_visbufn(kbuf
, (int) grabbed
)));
1126 /* there's only one mouse... */
1127 eventp
->id
= NORMAL_EVENT
;
1129 result
= decode_X10_bstate(sp
, eventp
, UChar(kbuf
[0]));
1131 eventp
->x
= (int) (coords
[0] - ' ') - 1;
1132 eventp
->y
= (int) (coords
[1] - ' ') - 1;
1136 #endif /* EXP_XTERM_1005 */
1139 * ECMA-48 section 5.4
1141 #define isInter(c) ((c) >= 0x20 && (c) <= 0x2f)
1142 #define isParam(c) ((c) >= 0x30 && (c) <= 0x3f)
1143 #define isFinal(c) ((c) >= 0x40 && (c) <= 0x7e)
1145 #define MAX_PARAMS 9
1148 int nerror
; /* nonzero if there are unexpected chars */
1149 int nparam
; /* number of numeric parameters */
1150 int params
[MAX_PARAMS
];
1151 int final
; /* the final-character */
1155 read_SGR(SCREEN
*sp
, SGR_DATA
* result
)
1157 char kbuf
[80]; /* bigger than any possible mouse response */
1164 memset(result
, 0, sizeof(*result
));
1165 # if USE_PTHREADS_EINTR
1166 # if USE_WEAK_SYMBOLS
1167 if ((pthread_self
) && (pthread_kill
) && (pthread_equal
))
1169 _nc_globals
.read_thread
= pthread_self();
1174 (M_FD(sp
) >= 0) ? M_FD(sp
) : sp
->_ifd
,
1181 if ((grabbed
+ 3) >= (int) sizeof(kbuf
)) {
1185 ch
= UChar(kbuf
[grabbed
]);
1186 kbuf
[grabbed
+ 1] = 0;
1200 result
->nparam
= (now
+ 1);
1203 result
->params
[now
] = (result
->params
[now
] * 10) + (ch
- '0');
1208 result
->nparam
= (now
+ 1);
1213 if (ch
< 32 || ch
> 126) {
1215 * Technically other characters could be interspersed in the
1216 * response. Ignore those for now.
1220 } else if (isFinal(ch
)) {
1231 } while (!isFinal(ch
));
1232 #if USE_PTHREADS_EINTR
1233 _nc_globals
.read_thread
= 0;
1236 kbuf
[++grabbed
] = 0;
1238 ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf
));
1239 return (grabbed
> 0) && (result
->nerror
== 0);
1243 decode_xterm_SGR1006(SCREEN
*sp
, MEVENT
* eventp
)
1246 bool result
= FALSE
;
1247 if (read_SGR(sp
, &data
)) {
1248 int b
= data
.params
[0];
1249 int b3
= 1 + (b
& 3);
1251 eventp
->id
= NORMAL_EVENT
;
1252 if (data
.final
== 'M') {
1253 (void) handle_wheel(sp
, eventp
, b
, (b
& 64) == 64);
1255 mmask_t pressed
= (mmask_t
) NCURSES_MOUSE_MASK(b3
, NCURSES_BUTTON_PRESSED
);
1256 mmask_t release
= (mmask_t
) NCURSES_MOUSE_MASK(b3
, NCURSES_BUTTON_RELEASED
);
1257 if (sp
->_mouse_bstate
& pressed
) {
1258 eventp
->bstate
= release
;
1259 sp
->_mouse_bstate
&= ~pressed
;
1261 eventp
->bstate
= REPORT_MOUSE_POSITION
;
1264 result
= (eventp
->bstate
& REPORT_MOUSE_POSITION
) ? TRUE
: FALSE
;
1265 eventp
->x
= (data
.params
[1] ? (data
.params
[1] - 1) : 0);
1266 eventp
->y
= (data
.params
[2] ? (data
.params
[2] - 1) : 0);
1272 _nc_mouse_inline(SCREEN
*sp
)
1273 /* mouse report received in the keyboard stream -- parse its info */
1275 bool result
= FALSE
;
1276 MEVENT
*eventp
= sp
->_mouse_eventp
;
1278 TR(MY_TRACE
, ("_nc_mouse_inline() called"));
1280 if (sp
->_mouse_type
== M_XTERM
) {
1281 switch (sp
->_mouse_format
) {
1283 result
= decode_xterm_X10(sp
, eventp
);
1286 result
= decode_xterm_SGR1006(sp
, eventp
);
1288 #ifdef EXP_XTERM_1005
1290 result
= decode_xterm_1005(sp
, eventp
);
1296 ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
1297 _nc_tracemouse(sp
, eventp
),
1298 (long) IndexEV(sp
, eventp
)));
1300 /* bump the next-free pointer into the circular list */
1301 sp
->_mouse_eventp
= NEXT(eventp
);
1304 /* If this event is from a wheel-mouse, treat it like position
1305 * reports and avoid waiting for the release-events which will
1308 if (eventp
->bstate
& BUTTON_PRESSED
) {
1311 for (b
= 4; b
<= MAX_BUTTONS
; ++b
) {
1312 if ((eventp
->bstate
& MASK_PRESS(b
))) {
1325 mouse_activate(SCREEN
*sp
, int on
)
1327 if (!on
&& !sp
->_mouse_initialized
)
1330 if (!_nc_mouse_init(sp
))
1334 sp
->_mouse_bstate
= 0;
1335 switch (sp
->_mouse_type
) {
1337 #if NCURSES_EXT_FUNCS
1338 NCURSES_SP_NAME(keyok
) (NCURSES_SP_ARGx KEY_MOUSE
, on
);
1340 TPUTS_TRACE("xterm mouse initialization");
1341 enable_xterm_mouse(sp
, 1);
1345 if (enable_gpm_mouse(sp
, TRUE
)) {
1346 sp
->_mouse_fd
= *(my_gpm_fd
);
1347 T(("GPM mouse_fd %d", sp
->_mouse_fd
));
1353 signal(SIGUSR2
, handle_sysmouse
);
1354 sp
->_mouse_active
= TRUE
;
1357 #ifdef USE_TERM_DRIVER
1359 sp
->_mouse_active
= TRUE
;
1365 /* Make runtime binding to cut down on object size of applications that
1366 * do not use the mouse (e.g., 'clear').
1368 sp
->_mouse_event
= _nc_mouse_event
;
1369 sp
->_mouse_inline
= _nc_mouse_inline
;
1370 sp
->_mouse_parse
= _nc_mouse_parse
;
1371 sp
->_mouse_resume
= _nc_mouse_resume
;
1372 sp
->_mouse_wrap
= _nc_mouse_wrap
;
1375 switch (sp
->_mouse_type
) {
1377 TPUTS_TRACE("xterm mouse deinitialization");
1378 enable_xterm_mouse(sp
, 0);
1382 enable_gpm_mouse(sp
, FALSE
);
1387 signal(SIGUSR2
, SIG_IGN
);
1388 sp
->_mouse_active
= FALSE
;
1391 #ifdef USE_TERM_DRIVER
1393 sp
->_mouse_active
= FALSE
;
1400 NCURSES_SP_NAME(_nc_flush
) (NCURSES_SP_ARG
);
1403 /**************************************************************************
1405 * Device-independent code
1407 **************************************************************************/
1410 _nc_mouse_parse(SCREEN
*sp
, int runcount
)
1411 /* parse a run of atomic mouse events into a gesture */
1413 MEVENT
*eventp
= sp
->_mouse_eventp
;
1415 MEVENT
*first_valid
= NULL
;
1416 MEVENT
*first_invalid
= NULL
;
1422 TR(MY_TRACE
, ("_nc_mouse_parse(%d) called", runcount
));
1425 * When we enter this routine, the event list next-free pointer
1426 * points just past a run of mouse events that we know were separated
1427 * in time by less than the critical click interval. The job of this
1428 * routine is to collapse this run into a single higher-level event
1431 * We accomplish this in two passes. The first pass merges press/release
1432 * pairs into click events. The second merges runs of click events into
1433 * double or triple-click events.
1435 * It's possible that the run may not resolve to a single event (for
1436 * example, if the user quadruple-clicks). If so, leading events
1437 * in the run are ignored if user does not call getmouse in a loop (getting
1438 * them from newest to older).
1440 * Note that this routine is independent of the format of the specific
1441 * format of the pointing-device's reports. We can use it to parse
1442 * gestures on anything that reports press/release events on a per-
1443 * button basis, as long as the device-dependent mouse code puts stuff
1444 * on the queue in MEVENT format.
1448 * Reset all events that were not set, in case the user sometimes calls
1449 * getmouse only once and other times until there are no more events in
1452 * This also allows reaching the beginning of the run.
1455 for (n
= runcount
; n
< EV_MAX
; n
++) {
1461 if (USE_TRACEF(TRACE_IEVENT
)) {
1462 _trace_slot(sp
, "before mouse press/release merge:");
1463 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1464 RunParams(sp
, eventp
, ep
),
1466 _nc_unlock_global(tracef
);
1470 /* first pass; merge press/release pairs */
1474 if (next
== eventp
) {
1475 /* Will end the loop, but compact before */
1479 #define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \
1480 == !(next->bstate & MASK_RELEASE(x)))
1482 if (ValidEvent(ep
) && ValidEvent(next
)
1483 && ep
->x
== next
->x
&& ep
->y
== next
->y
1484 && (ep
->bstate
& BUTTON_PRESSED
)
1485 && (!(next
->bstate
& BUTTON_PRESSED
))) {
1486 bool changed
= TRUE
;
1488 for (b
= 1; b
<= MAX_BUTTONS
; ++b
) {
1489 if (!MASK_CHANGED(b
)) {
1497 for (b
= 1; b
<= MAX_BUTTONS
; ++b
) {
1498 if ((sp
->_mouse_mask
& MASK_CLICK(b
))
1499 && (ep
->bstate
& MASK_PRESS(b
))) {
1500 next
->bstate
&= ~MASK_RELEASE(b
);
1501 next
->bstate
|= MASK_CLICK(b
);
1512 /* Compact valid events */
1513 if (!ValidEvent(ep
)) {
1514 if ((first_valid
!= NULL
) && (first_invalid
== NULL
)) {
1518 if (first_valid
== NULL
) {
1520 } else if (first_invalid
!= NULL
) {
1521 *first_invalid
= *ep
;
1523 first_invalid
= NEXT(first_invalid
);
1530 if (first_invalid
!= NULL
) {
1531 eventp
= first_invalid
;
1534 if (USE_TRACEF(TRACE_IEVENT
)) {
1535 _trace_slot(sp
, "before mouse click merge:");
1536 if (first_valid
== NULL
) {
1537 _tracef("_nc_mouse_parse: no valid event");
1539 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1540 RunParams(sp
, eventp
, first_valid
),
1542 _nc_unlock_global(tracef
);
1548 * Second pass; merge click runs. We merge click events forward in the
1549 * queue. For example, double click can be changed to triple click.
1551 * NOTE: There is a problem with this design! If the application
1552 * allows enough click events to pile up in the circular queue so
1553 * they wrap around, it will cheerfully merge the newest forward
1554 * into the oldest, creating a bogus doubleclick and confusing
1555 * the queue-traversal logic rather badly. Generally this won't
1556 * happen, because calling getmouse() marks old events invalid and
1557 * ineligible for merges. The true solution to this problem would
1558 * be to timestamp each MEVENT and perform the obvious sanity check,
1559 * but the timer element would have to have sub-second resolution,
1560 * which would get us into portability trouble.
1562 first_invalid
= NULL
;
1563 endLoop
= (first_valid
== NULL
);
1568 if (next
== eventp
) {
1569 /* Will end the loop, but check event type and compact before */
1571 } else if (!ValidEvent(next
)) {
1574 /* merge click events forward */
1575 if ((ep
->bstate
& BUTTON_CLICKED
)
1576 && (next
->bstate
& BUTTON_CLICKED
)) {
1578 for (b
= 1; b
<= MAX_BUTTONS
; ++b
) {
1579 if ((sp
->_mouse_mask
& MASK_DOUBLE_CLICK(b
))
1580 && (ep
->bstate
& MASK_CLICK(b
))
1581 && (next
->bstate
& MASK_CLICK(b
))) {
1582 next
->bstate
&= ~MASK_CLICK(b
);
1583 next
->bstate
|= MASK_DOUBLE_CLICK(b
);
1592 /* merge double-click events forward */
1593 if ((ep
->bstate
& BUTTON_DOUBLE_CLICKED
)
1594 && (next
->bstate
& BUTTON_CLICKED
)) {
1596 for (b
= 1; b
<= MAX_BUTTONS
; ++b
) {
1597 if ((sp
->_mouse_mask
& MASK_TRIPLE_CLICK(b
))
1598 && (ep
->bstate
& MASK_DOUBLE_CLICK(b
))
1599 && (next
->bstate
& MASK_CLICK(b
))) {
1600 next
->bstate
&= ~MASK_CLICK(b
);
1601 next
->bstate
|= MASK_TRIPLE_CLICK(b
);
1611 /* Discard event if it does not match event mask */
1612 if (!(ep
->bstate
& sp
->_mouse_mask2
)) {
1616 /* Compact valid events */
1617 if (!ValidEvent(ep
)) {
1618 if (ep
== first_valid
) {
1620 } else if (first_invalid
== NULL
) {
1623 } else if (first_invalid
!= NULL
) {
1624 *first_invalid
= *ep
;
1626 first_invalid
= NEXT(first_invalid
);
1632 if (first_invalid
== NULL
) {
1633 first_invalid
= eventp
;
1635 sp
->_mouse_eventp
= first_invalid
;
1638 if (first_valid
!= NULL
) {
1639 if (USE_TRACEF(TRACE_IEVENT
)) {
1640 _trace_slot(sp
, "after mouse event queue compaction:");
1641 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
1642 RunParams(sp
, first_invalid
, first_valid
),
1644 _nc_unlock_global(tracef
);
1646 for (ep
= first_valid
; ep
!= first_invalid
; ep
= NEXT(ep
)) {
1649 ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
1650 _nc_tracemouse(sp
, ep
),
1651 (long) IndexEV(sp
, ep
)));
1656 /* after all this, do we have a valid event? */
1657 return ValidEvent(PREV(first_invalid
));
1661 _nc_mouse_wrap(SCREEN
*sp
)
1662 /* release mouse -- called by endwin() before shellout/exit */
1664 TR(MY_TRACE
, ("_nc_mouse_wrap() called"));
1666 switch (sp
->_mouse_type
) {
1668 if (sp
->_mouse_mask
)
1669 mouse_activate(sp
, FALSE
);
1672 /* GPM: pass all mouse events to next client */
1674 if (sp
->_mouse_mask
)
1675 mouse_activate(sp
, FALSE
);
1680 mouse_activate(sp
, FALSE
);
1683 #ifdef USE_TERM_DRIVER
1685 mouse_activate(sp
, FALSE
);
1694 _nc_mouse_resume(SCREEN
*sp
)
1695 /* re-connect to mouse -- called by doupdate() after shellout */
1697 TR(MY_TRACE
, ("_nc_mouse_resume() called"));
1699 switch (sp
->_mouse_type
) {
1701 /* xterm: re-enable reporting */
1702 if (sp
->_mouse_mask
)
1703 mouse_activate(sp
, TRUE
);
1708 /* GPM: reclaim our event set */
1709 if (sp
->_mouse_mask
)
1710 mouse_activate(sp
, TRUE
);
1716 mouse_activate(sp
, TRUE
);
1720 #ifdef USE_TERM_DRIVER
1722 mouse_activate(sp
, TRUE
);
1731 /**************************************************************************
1733 * Mouse interface entry points for the API
1735 **************************************************************************/
1738 NCURSES_SP_NAME(getmouse
) (NCURSES_SP_DCLx MEVENT
* aevent
)
1742 T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM
, (void *) aevent
));
1744 if ((aevent
!= 0) && (SP_PARM
!= 0) && (SP_PARM
->_mouse_type
!= M_NONE
)) {
1745 MEVENT
*eventp
= SP_PARM
->_mouse_eventp
;
1746 /* compute the current-event pointer */
1747 MEVENT
*prev
= PREV(eventp
);
1750 * Discard events not matching mask (there could be still some if
1751 * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns
1754 while (ValidEvent(prev
) && (!(prev
->bstate
& SP_PARM
->_mouse_mask2
))) {
1758 if (ValidEvent(prev
)) {
1759 /* copy the event we find there */
1762 TR(TRACE_IEVENT
, ("getmouse: returning event %s from slot %ld",
1763 _nc_tracemouse(SP_PARM
, prev
),
1764 (long) IndexEV(SP_PARM
, prev
)));
1766 Invalidate(prev
); /* so the queue slot becomes free */
1767 SP_PARM
->_mouse_eventp
= prev
;
1770 /* Reset the provided event */
1781 #if NCURSES_SP_FUNCS
1782 /* grab a copy of the current mouse event */
1784 getmouse(MEVENT
* aevent
)
1786 return NCURSES_SP_NAME(getmouse
) (CURRENT_SCREEN
, aevent
);
1791 NCURSES_SP_NAME(ungetmouse
) (NCURSES_SP_DCLx MEVENT
* aevent
)
1795 T((T_CALLED("ungetmouse(%p,%p)"), (void *) SP_PARM
, (void *) aevent
));
1797 if (aevent
!= 0 && SP_PARM
!= 0) {
1798 MEVENT
*eventp
= SP_PARM
->_mouse_eventp
;
1800 /* stick the given event in the next-free slot */
1803 /* bump the next-free pointer into the circular list */
1804 SP_PARM
->_mouse_eventp
= NEXT(eventp
);
1806 /* push back the notification event on the keyboard queue */
1807 result
= NCURSES_SP_NAME(ungetch
) (NCURSES_SP_ARGx KEY_MOUSE
);
1812 #if NCURSES_SP_FUNCS
1813 /* enqueue a synthesized mouse event to be seen by the next wgetch() */
1815 ungetmouse(MEVENT
* aevent
)
1817 return NCURSES_SP_NAME(ungetmouse
) (CURRENT_SCREEN
, aevent
);
1821 NCURSES_EXPORT(mmask_t
)
1822 NCURSES_SP_NAME(mousemask
) (NCURSES_SP_DCLx mmask_t newmask
, mmask_t
* oldmask
)
1823 /* set the mouse event mask */
1828 T((T_CALLED("mousemask(%p,%#lx,%p)"),
1830 (unsigned long) newmask
,
1835 *oldmask
= SP_PARM
->_mouse_mask
;
1837 if (newmask
|| SP_PARM
->_mouse_initialized
) {
1838 _nc_mouse_init(SP_PARM
);
1839 if (SP_PARM
->_mouse_type
!= M_NONE
) {
1841 (REPORT_MOUSE_POSITION
1848 | BUTTON_DOUBLE_CLICKED
1849 | BUTTON_TRIPLE_CLICKED
);
1851 mouse_activate(SP_PARM
, (bool) (result
!= 0));
1853 SP_PARM
->_mouse_mask
= result
;
1854 SP_PARM
->_mouse_mask2
= result
;
1857 * Make a mask corresponding to the states we will need to
1858 * retain (temporarily) while building up the state that the
1861 for (b
= 1; b
<= MAX_BUTTONS
; ++b
) {
1862 if (SP_PARM
->_mouse_mask2
& MASK_TRIPLE_CLICK(b
))
1863 SP_PARM
->_mouse_mask2
|= MASK_DOUBLE_CLICK(b
);
1864 if (SP_PARM
->_mouse_mask2
& MASK_DOUBLE_CLICK(b
))
1865 SP_PARM
->_mouse_mask2
|= MASK_CLICK(b
);
1866 if (SP_PARM
->_mouse_mask2
& MASK_CLICK(b
))
1867 SP_PARM
->_mouse_mask2
|= (MASK_PRESS(b
) |
1873 returnMMask(result
);
1876 #if NCURSES_SP_FUNCS
1877 NCURSES_EXPORT(mmask_t
)
1878 mousemask(mmask_t newmask
, mmask_t
* oldmask
)
1880 return NCURSES_SP_NAME(mousemask
) (CURRENT_SCREEN
, newmask
, oldmask
);
1884 NCURSES_EXPORT(bool)
1885 wenclose(const WINDOW
*win
, int y
, int x
)
1886 /* check to see if given window encloses given screen location */
1888 bool result
= FALSE
;
1890 T((T_CALLED("wenclose(%p,%d,%d)"), (const void *) win
, y
, x
));
1894 result
= ((win
->_begy
<= y
&&
1896 (win
->_begx
+ win
->_maxx
) >= x
&&
1897 (win
->_begy
+ win
->_maxy
) >= y
) ? TRUE
: FALSE
);
1903 NCURSES_SP_NAME(mouseinterval
) (NCURSES_SP_DCLx
int maxclick
)
1904 /* set the maximum mouse interval within which to recognize a click */
1908 T((T_CALLED("mouseinterval(%p,%d)"), (void *) SP_PARM
, maxclick
));
1911 oldval
= SP_PARM
->_maxclick
;
1913 SP_PARM
->_maxclick
= maxclick
;
1915 oldval
= DEFAULT_MAXCLICK
;
1921 #if NCURSES_SP_FUNCS
1923 mouseinterval(int maxclick
)
1925 return NCURSES_SP_NAME(mouseinterval
) (CURRENT_SCREEN
, maxclick
);
1929 /* This may be used by other routines to ask for the existence of mouse
1931 NCURSES_EXPORT(bool)
1932 _nc_has_mouse(SCREEN
*sp
)
1934 return (((0 == sp
) || (sp
->_mouse_type
== M_NONE
)) ? FALSE
: TRUE
);
1937 NCURSES_EXPORT(bool)
1938 NCURSES_SP_NAME(has_mouse
) (NCURSES_SP_DCL0
)
1940 return _nc_has_mouse(SP_PARM
);
1943 #if NCURSES_SP_FUNCS
1944 NCURSES_EXPORT(bool)
1947 return _nc_has_mouse(CURRENT_SCREEN
);
1951 NCURSES_EXPORT(bool)
1952 wmouse_trafo(const WINDOW
*win
, int *pY
, int *pX
, bool to_screen
)
1954 bool result
= FALSE
;
1956 T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"),
1962 if (win
&& pY
&& pX
) {
1967 y
+= win
->_begy
+ win
->_yoffset
;
1969 if (wenclose(win
, y
, x
))
1972 if (wenclose(win
, y
, x
)) {
1973 y
-= (win
->_begy
+ win
->_yoffset
);