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