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