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