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