1 /* $OpenBSD: lib_mouse.c,v 1.13 2001/01/22 18:01:41 millert Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 ****************************************************************************/ 35 36 /* 37 * This module is intended to encapsulate ncurses's interface to pointing 38 * devices. 39 * 40 * The first method used is xterm's internal mouse-tracking facility. 41 * The second is Alessandro Rubini's GPM server. 42 * 43 * Notes for implementors of new mouse-interface methods: 44 * 45 * The code is logically split into a lower level that accepts event reports 46 * in a device-dependent format and an upper level that parses mouse gestures 47 * and filters events. The mediating data structure is a circular queue of 48 * MEVENT structures. 49 * 50 * Functionally, the lower level's job is to pick up primitive events and 51 * put them on the circular queue. This can happen in one of two ways: 52 * either (a) _nc_mouse_event() detects a series of incoming mouse reports 53 * and queues them, or (b) code in lib_getch.c detects the kmous prefix in 54 * the keyboard input stream and calls _nc_mouse_inline to queue up a series 55 * of adjacent mouse reports. 56 * 57 * In either case, _nc_mouse_parse() should be called after the series is 58 * accepted to parse the digested mouse reports (low-level MEVENTs) into 59 * a gesture (a high-level or composite MEVENT). 60 * 61 * Don't be too shy about adding new event types or modifiers, if you can find 62 * room for them in the 32-bit mask. The API is written so that users get 63 * feedback on which theoretical event types they won't see when they call 64 * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being 65 * used yet, and a couple of bits open at the high end. 66 */ 67 68 #ifdef __EMX__ 69 # include <io.h> 70 # define INCL_DOS 71 # define INCL_VIO 72 # define INCL_KBD 73 # define INCL_MOU 74 # define INCL_DOSPROCESS 75 # include <os2.h> /* Need to include before the others */ 76 #endif 77 78 #include <curses.priv.h> 79 #include <term.h> 80 81 #if USE_GPM_SUPPORT 82 #ifndef LINT /* don't need this for llib-lncurses */ 83 #undef buttons /* term.h defines this, and gpm uses it! */ 84 #include <gpm.h> 85 #include <linux/keyboard.h> /* defines KG_* macros */ 86 #endif 87 #endif 88 89 MODULE_ID("$From: lib_mouse.c,v 1.57 2000/12/10 02:43:27 tom Exp $") 90 91 #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT 92 93 #define INVALID_EVENT -1 94 95 static int mousetype; 96 #define M_XTERM -1 /* use xterm's mouse tracking? */ 97 #define M_NONE 0 /* no mouse device */ 98 #define M_GPM 1 /* use GPM */ 99 #define M_QNX 2 /* QNX mouse on console */ 100 #define M_QNX_TERM 3 /* QNX mouse on pterm/xterm (using qansi-m) */ 101 102 #if USE_GPM_SUPPORT 103 #ifndef LINT 104 static Gpm_Connect gpm_connect; 105 #endif 106 #endif 107 108 static mmask_t eventmask; /* current event mask */ 109 110 static bool _nc_mouse_parse(int); 111 static void _nc_mouse_resume(SCREEN *); 112 static void _nc_mouse_wrap(SCREEN *); 113 114 /* maintain a circular list of mouse events */ 115 116 /* The definition of the circular list size (EV_MAX), is in curses.priv.h, so 117 * wgetch() may refer to the size and call _nc_mouse_parse() before circular 118 * list overflow. 119 */ 120 static MEVENT events[EV_MAX]; /* hold the last mouse event seen */ 121 static MEVENT *eventp = events; /* next free slot in event queue */ 122 #define NEXT(ep) ((ep == events + EV_MAX - 1) ? events : ep + 1) 123 #define PREV(ep) ((ep == events) ? events + EV_MAX - 1 : ep - 1) 124 125 #ifdef TRACE 126 static void 127 _trace_slot(const char *tag) 128 { 129 MEVENT *ep; 130 131 _tracef(tag); 132 133 for (ep = events; ep < events + EV_MAX; ep++) 134 _tracef("mouse event queue slot %ld = %s", 135 (long) (ep - events), 136 _tracemouse(ep)); 137 } 138 #endif 139 140 #ifdef USE_EMX_MOUSE 141 142 # define TOP_ROW 0 143 # define LEFT_COL 0 144 145 static int mouse_wfd; 146 static int mouse_thread; 147 static int mouse_activated; 148 static char mouse_buttons[] = 149 {0, 1, 3, 2}; 150 151 # define M_FD(sp) sp->_mouse_fd 152 153 static void 154 write_event(int down, int button, int x, int y) 155 { 156 char buf[6]; 157 unsigned long ignore; 158 159 strncpy(buf, key_mouse, 3); /* should be "\033[M" */ 160 buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40); 161 buf[4] = ' ' + x - LEFT_COL + 1; 162 buf[5] = ' ' + y - TOP_ROW + 1; 163 DosWrite(mouse_wfd, buf, 6, &ignore); 164 } 165 166 static void 167 mouse_server(unsigned long ignored GCC_UNUSED) 168 { 169 unsigned short fWait = MOU_WAIT; 170 /* NOPTRRECT mourt = { 0,0,24,79 }; */ 171 MOUEVENTINFO mouev; 172 HMOU hmou; 173 unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN; 174 int nbuttons = 3; 175 int oldstate = 0; 176 char err[80]; 177 unsigned long rc; 178 179 /* open the handle for the mouse */ 180 if (MouOpen(NULL, &hmou) == 0) { 181 rc = MouSetEventMask(&mask, hmou); 182 if (rc) { /* retry with 2 buttons */ 183 mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN; 184 rc = MouSetEventMask(&mask, hmou); 185 nbuttons = 2; 186 } 187 if (rc == 0 && MouDrawPtr(hmou) == 0) { 188 for (;;) { 189 /* sit and wait on the event queue */ 190 rc = MouReadEventQue(&mouev, &fWait, hmou); 191 if (rc) { 192 snprintf(err, sizeof(err), 193 "Error reading mouse queue, rc=%lu.\r\n", rc); 194 break; 195 } 196 if (!mouse_activated) 197 goto finish; 198 199 /* 200 * OS/2 numbers a 3-button mouse inconsistently from other 201 * platforms: 202 * 1 = left 203 * 2 = right 204 * 3 = middle. 205 */ 206 if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN) 207 write_event(mouev.fs & MOUSE_BN1_DOWN, 208 mouse_buttons[1], mouev.col, mouev.row); 209 if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN) 210 write_event(mouev.fs & MOUSE_BN2_DOWN, 211 mouse_buttons[3], mouev.col, mouev.row); 212 if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN) 213 write_event(mouev.fs & MOUSE_BN3_DOWN, 214 mouse_buttons[2], mouev.col, mouev.row); 215 216 finish: 217 oldstate = mouev.fs; 218 } 219 } else 220 snprintf(err, sizeof(err), 221 "Error setting event mask, buttons=%d, rc=%lu.\r\n", 222 nbuttons, rc); 223 224 DosWrite(2, err, strlen(err), &rc); 225 MouClose(hmou); 226 } 227 DosExit(EXIT_THREAD, 0L); 228 } 229 230 static void 231 server_state(const int state) 232 { /* It would be nice to implement pointer-off and stop looping... */ 233 mouse_activated = state; 234 } 235 236 #endif 237 238 static int initialized; 239 240 static void 241 initialize_mousetype(void) 242 { 243 static const char *xterm_kmous = "\033[M"; 244 245 /* Try gpm first, because gpm may be configured to run in xterm */ 246 #if USE_GPM_SUPPORT 247 /* GPM: initialize connection to gpm server */ 248 gpm_connect.eventMask = GPM_DOWN | GPM_UP; 249 gpm_connect.defaultMask = ~(gpm_connect.eventMask | GPM_HARD); 250 gpm_connect.minMod = 0; 251 gpm_connect.maxMod = ~((1 << KG_SHIFT) | (1 << KG_SHIFTL) | (1 << KG_SHIFTR)); 252 if (Gpm_Open(&gpm_connect, 0) >= 0) { /* returns the file-descriptor */ 253 mousetype = M_GPM; 254 SP->_mouse_fd = gpm_fd; 255 return; 256 } 257 #endif 258 259 /* OS/2 VIO */ 260 #ifdef USE_EMX_MOUSE 261 if (!mouse_thread 262 && strstr(cur_term->type.term_names, "xterm") == 0 263 && key_mouse) { 264 int handles[2]; 265 266 if (pipe(handles) < 0) { 267 perror("mouse pipe error"); 268 return; 269 } else { 270 int rc; 271 272 if (!mouse_buttons[0]) { 273 char *s = getenv("MOUSE_BUTTONS_123"); 274 275 mouse_buttons[0] = 1; 276 if (s && strlen(s) >= 3) { 277 mouse_buttons[1] = s[0] - '0'; 278 mouse_buttons[2] = s[1] - '0'; 279 mouse_buttons[3] = s[2] - '0'; 280 } 281 } 282 mouse_wfd = handles[1]; 283 M_FD(SP) = handles[0]; 284 /* Needed? */ 285 setmode(handles[0], O_BINARY); 286 setmode(handles[1], O_BINARY); 287 /* Do not use CRT functions, we may single-threaded. */ 288 rc = DosCreateThread((unsigned long *) &mouse_thread, 289 mouse_server, 0, 0, 8192); 290 if (rc) { 291 printf("mouse thread error %d=%#x", rc, rc); 292 return; 293 } else { 294 mousetype = M_XTERM; 295 return; 296 } 297 } 298 } 299 #endif 300 301 /* we know how to recognize mouse events under "xterm" */ 302 if (key_mouse != 0) { 303 if (!strcmp(key_mouse, xterm_kmous)) { 304 mousetype = M_XTERM; 305 return; 306 } 307 } else if (strstr(cur_term->type.term_names, "xterm") != 0) { 308 (void) _nc_add_to_try(&(SP->_keytry), xterm_kmous, KEY_MOUSE); 309 mousetype = M_XTERM; 310 return; 311 } 312 } 313 314 static void 315 _nc_mouse_init(void) 316 /* initialize the mouse */ 317 { 318 int i; 319 320 if (!initialized) { 321 initialized = TRUE; 322 323 TR(MY_TRACE, ("_nc_mouse_init() called")); 324 325 for (i = 0; i < EV_MAX; i++) 326 events[i].id = INVALID_EVENT; 327 328 initialize_mousetype(); 329 330 T(("_nc_mouse_init() set mousetype to %d", mousetype)); 331 } 332 } 333 334 static bool 335 _nc_mouse_event(SCREEN * sp GCC_UNUSED) 336 /* query to see if there is a pending mouse event */ 337 { 338 #if USE_GPM_SUPPORT 339 /* GPM: query server for event, return TRUE if we find one */ 340 Gpm_Event ev; 341 342 if (gpm_fd >= 0 343 && (_nc_timed_wait(3, 0, (int *) 0) & 2) != 0 344 && Gpm_GetEvent(&ev) == 1) { 345 eventp->id = 0; /* there's only one mouse... */ 346 347 eventp->bstate = 0; 348 switch (ev.type & 0x0f) { 349 case (GPM_DOWN): 350 if (ev.buttons & GPM_B_LEFT) 351 eventp->bstate |= BUTTON1_PRESSED; 352 if (ev.buttons & GPM_B_MIDDLE) 353 eventp->bstate |= BUTTON2_PRESSED; 354 if (ev.buttons & GPM_B_RIGHT) 355 eventp->bstate |= BUTTON3_PRESSED; 356 break; 357 case (GPM_UP): 358 if (ev.buttons & GPM_B_LEFT) 359 eventp->bstate |= BUTTON1_RELEASED; 360 if (ev.buttons & GPM_B_MIDDLE) 361 eventp->bstate |= BUTTON2_RELEASED; 362 if (ev.buttons & GPM_B_RIGHT) 363 eventp->bstate |= BUTTON3_RELEASED; 364 break; 365 default: 366 break; 367 } 368 369 eventp->x = ev.x - 1; 370 eventp->y = ev.y - 1; 371 eventp->z = 0; 372 373 /* bump the next-free pointer into the circular list */ 374 eventp = NEXT(eventp); 375 return (TRUE); 376 } 377 #endif 378 379 #ifdef USE_EMX_MOUSE 380 if (SP->_mouse_fd >= 0 381 && (_nc_timed_wait(3, 0, (int *) 0) & 2) != 0) { 382 char kbuf[3]; 383 384 int i, res = read(M_FD(sp), &kbuf, 3); /* Eat the prefix */ 385 if (res != 3) 386 printf("Got %d chars instead of 3 for prefix.\n", res); 387 for (i = 0; i < res; i++) { 388 if (kbuf[i] != key_mouse[i]) 389 printf("Got char %d instead of %d for prefix.\n", 390 (int) kbuf[i], (int) key_mouse[i]); 391 } 392 return TRUE; 393 } 394 #endif /* USE_EMX_MOUSE */ 395 396 /* xterm: never have to query, mouse events are in the keyboard stream */ 397 return (FALSE); /* no event waiting */ 398 } 399 400 static bool 401 _nc_mouse_inline(SCREEN * sp) 402 /* mouse report received in the keyboard stream -- parse its info */ 403 { 404 TR(MY_TRACE, ("_nc_mouse_inline() called")); 405 406 if (mousetype == M_XTERM) { 407 unsigned char kbuf[4]; 408 MEVENT *prev; 409 size_t grabbed; 410 int res; 411 412 /* This code requires that your xterm entry contain the kmous 413 * capability and that it be set to the \E[M documented in the 414 * Xterm Control Sequences reference. This is how we 415 * arrange for mouse events to be reported via a KEY_MOUSE 416 * return value from wgetch(). After this value is received, 417 * _nc_mouse_inline() gets called and is immediately 418 * responsible for parsing the mouse status information 419 * following the prefix. 420 * 421 * The following quotes from the ctrlseqs.ms document in the 422 * X distribution, describing the X mouse tracking feature: 423 * 424 * Parameters for all mouse tracking escape sequences 425 * generated by xterm encode numeric parameters in a single 426 * character as value+040. For example, ! is 1. 427 * 428 * On button press or release, xterm sends ESC [ M CbCxCy. 429 * The low two bits of Cb encode button information: 0=MB1 430 * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release. The 431 * upper bits encode what modifiers were down when the 432 * button was pressed and are added together. 4=Shift, 433 * 8=Meta, 16=Control. Cx and Cy are the x and y coordinates 434 * of the mouse event. The upper left corner is (1,1). 435 * 436 * (End quote) By the time we get here, we've eaten the 437 * key prefix. FYI, the loop below is necessary because 438 * mouse click info isn't guaranteed to present as a 439 * single clist item. It always does under Linux but often 440 * fails to under Solaris. 441 */ 442 for (grabbed = 0; grabbed < 3; grabbed += res) { 443 444 /* For VIO mouse we add extra bit 64 to disambiguate button-up. */ 445 #ifdef USE_EMX_MOUSE 446 res = read(M_FD(sp) >= 0 ? M_FD(sp) : sp->_ifd, &kbuf, 3); 447 #else 448 res = read(sp->_ifd, kbuf + grabbed, 3 - grabbed); 449 #endif 450 if (res == -1) 451 break; 452 } 453 kbuf[3] = '\0'; 454 455 TR(TRACE_IEVENT, 456 ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf)); 457 458 eventp->id = 0; /* there's only one mouse... */ 459 460 /* processing code goes here */ 461 eventp->bstate = 0; 462 switch (kbuf[0] & 0x3) { 463 case 0x0: 464 eventp->bstate = BUTTON1_PRESSED; 465 #ifdef USE_EMX_MOUSE 466 if (kbuf[0] & 0x40) 467 eventp->bstate = BUTTON1_RELEASED; 468 #endif 469 break; 470 471 case 0x1: 472 eventp->bstate = BUTTON2_PRESSED; 473 #ifdef USE_EMX_MOUSE 474 if (kbuf[0] & 0x40) 475 eventp->bstate = BUTTON2_RELEASED; 476 #endif 477 break; 478 479 case 0x2: 480 eventp->bstate = BUTTON3_PRESSED; 481 #ifdef USE_EMX_MOUSE 482 if (kbuf[0] & 0x40) 483 eventp->bstate = BUTTON3_RELEASED; 484 #endif 485 break; 486 487 case 0x3: 488 /* 489 * Release events aren't reported for individual buttons, 490 * just for the button set as a whole... 491 */ 492 eventp->bstate = 493 (BUTTON1_RELEASED | 494 BUTTON2_RELEASED | 495 BUTTON3_RELEASED); 496 /* 497 * ...however, because there are no kinds of mouse events under 498 * xterm that can intervene between press and release, we can 499 * deduce which buttons were actually released by looking at the 500 * previous event. 501 */ 502 prev = PREV(eventp); 503 if (!(prev->bstate & BUTTON1_PRESSED)) 504 eventp->bstate &= ~BUTTON1_RELEASED; 505 if (!(prev->bstate & BUTTON2_PRESSED)) 506 eventp->bstate &= ~BUTTON2_RELEASED; 507 if (!(prev->bstate & BUTTON3_PRESSED)) 508 eventp->bstate &= ~BUTTON3_RELEASED; 509 break; 510 } 511 512 if (kbuf[0] & 4) { 513 eventp->bstate |= BUTTON_SHIFT; 514 } 515 if (kbuf[0] & 8) { 516 eventp->bstate |= BUTTON_ALT; 517 } 518 if (kbuf[0] & 16) { 519 eventp->bstate |= BUTTON_CTRL; 520 } 521 522 eventp->x = (kbuf[1] - ' ') - 1; 523 eventp->y = (kbuf[2] - ' ') - 1; 524 TR(MY_TRACE, 525 ("_nc_mouse_inline: primitive mouse-event %s has slot %ld", 526 _tracemouse(eventp), 527 (long) (eventp - events))); 528 529 /* bump the next-free pointer into the circular list */ 530 eventp = NEXT(eventp); 531 #if 0 /* this return would be needed for QNX's mods to lib_getch.c */ 532 return (TRUE); 533 #endif 534 } 535 536 return (FALSE); 537 } 538 539 static void 540 mouse_activate(bool on) 541 { 542 if (!on && !initialized) 543 return; 544 545 _nc_mouse_init(); 546 547 if (on) { 548 549 switch (mousetype) { 550 case M_XTERM: 551 #if NCURSES_EXT_FUNCS 552 keyok(KEY_MOUSE, on); 553 #endif 554 TPUTS_TRACE("xterm mouse initialization"); 555 #ifdef USE_EMX_MOUSE 556 server_state(1); 557 #else 558 putp("\033[?1000h"); 559 #endif 560 break; 561 #if USE_GPM_SUPPORT 562 case M_GPM: 563 SP->_mouse_fd = gpm_fd; 564 break; 565 #endif 566 } 567 /* Make runtime binding to cut down on object size of applications that 568 * do not use the mouse (e.g., 'clear'). 569 */ 570 SP->_mouse_event = _nc_mouse_event; 571 SP->_mouse_inline = _nc_mouse_inline; 572 SP->_mouse_parse = _nc_mouse_parse; 573 SP->_mouse_resume = _nc_mouse_resume; 574 SP->_mouse_wrap = _nc_mouse_wrap; 575 576 } else { 577 578 switch (mousetype) { 579 case M_XTERM: 580 TPUTS_TRACE("xterm mouse deinitialization"); 581 #ifdef USE_EMX_MOUSE 582 server_state(0); 583 #else 584 putp("\033[?1000l"); 585 #endif 586 break; 587 #if USE_GPM_SUPPORT 588 case M_GPM: 589 break; 590 #endif 591 } 592 } 593 _nc_flush(); 594 } 595 596 /************************************************************************** 597 * 598 * Device-independent code 599 * 600 **************************************************************************/ 601 602 static bool 603 _nc_mouse_parse(int runcount) 604 /* parse a run of atomic mouse events into a gesture */ 605 { 606 MEVENT *ep, *runp, *next, *prev = PREV(eventp); 607 int n; 608 bool merge; 609 610 TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount)); 611 612 /* 613 * When we enter this routine, the event list next-free pointer 614 * points just past a run of mouse events that we know were separated 615 * in time by less than the critical click interval. The job of this 616 * routine is to collaps this run into a single higher-level event 617 * or gesture. 618 * 619 * We accomplish this in two passes. The first pass merges press/release 620 * pairs into click events. The second merges runs of click events into 621 * double or triple-click events. 622 * 623 * It's possible that the run may not resolve to a single event (for 624 * example, if the user quadruple-clicks). If so, leading events 625 * in the run are ignored. 626 * 627 * Note that this routine is independent of the format of the specific 628 * format of the pointing-device's reports. We can use it to parse 629 * gestures on anything that reports press/release events on a per- 630 * button basis, as long as the device-dependent mouse code puts stuff 631 * on the queue in MEVENT format. 632 */ 633 if (runcount == 1) { 634 TR(MY_TRACE, 635 ("_nc_mouse_parse: returning simple mouse event %s at slot %ld", 636 _tracemouse(prev), 637 (long) (prev - events))); 638 return (prev->id >= 0) 639 ? ((prev->bstate & eventmask) ? TRUE : FALSE) 640 : FALSE; 641 } 642 643 /* find the start of the run */ 644 runp = eventp; 645 for (n = runcount; n > 0; n--) { 646 runp = PREV(runp); 647 } 648 649 #ifdef TRACE 650 if (_nc_tracing & TRACE_IEVENT) { 651 _trace_slot("before mouse press/release merge:"); 652 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 653 (long) (runp - events), 654 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX, 655 runcount); 656 } 657 #endif /* TRACE */ 658 659 /* first pass; merge press/release pairs */ 660 do { 661 merge = FALSE; 662 for (ep = runp; next = NEXT(ep), next != eventp; ep = next) { 663 if (ep->x == next->x && ep->y == next->y 664 && (ep->bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED)) 665 && (!(ep->bstate & BUTTON1_PRESSED) 666 == !(next->bstate & BUTTON1_RELEASED)) 667 && (!(ep->bstate & BUTTON2_PRESSED) 668 == !(next->bstate & BUTTON2_RELEASED)) 669 && (!(ep->bstate & BUTTON3_PRESSED) 670 == !(next->bstate & BUTTON3_RELEASED)) 671 ) { 672 if ((eventmask & BUTTON1_CLICKED) 673 && (ep->bstate & BUTTON1_PRESSED)) { 674 ep->bstate &= ~BUTTON1_PRESSED; 675 ep->bstate |= BUTTON1_CLICKED; 676 merge = TRUE; 677 } 678 if ((eventmask & BUTTON2_CLICKED) 679 && (ep->bstate & BUTTON2_PRESSED)) { 680 ep->bstate &= ~BUTTON2_PRESSED; 681 ep->bstate |= BUTTON2_CLICKED; 682 merge = TRUE; 683 } 684 if ((eventmask & BUTTON3_CLICKED) 685 && (ep->bstate & BUTTON3_PRESSED)) { 686 ep->bstate &= ~BUTTON3_PRESSED; 687 ep->bstate |= BUTTON3_CLICKED; 688 merge = TRUE; 689 } 690 if (merge) 691 next->id = INVALID_EVENT; 692 } 693 } 694 } while 695 (merge); 696 697 #ifdef TRACE 698 if (_nc_tracing & TRACE_IEVENT) { 699 _trace_slot("before mouse click merge:"); 700 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 701 (long) (runp - events), 702 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX, 703 runcount); 704 } 705 #endif /* TRACE */ 706 707 /* 708 * Second pass; merge click runs. At this point, click events are 709 * each followed by one invalid event. We merge click events 710 * forward in the queue. 711 * 712 * NOTE: There is a problem with this design! If the application 713 * allows enough click events to pile up in the circular queue so 714 * they wrap around, it will cheerfully merge the newest forward 715 * into the oldest, creating a bogus doubleclick and confusing 716 * the queue-traversal logic rather badly. Generally this won't 717 * happen, because calling getmouse() marks old events invalid and 718 * ineligible for merges. The true solution to this problem would 719 * be to timestamp each MEVENT and perform the obvious sanity check, 720 * but the timer element would have to have sub-second resolution, 721 * which would get us into portability trouble. 722 */ 723 do { 724 MEVENT *follower; 725 726 merge = FALSE; 727 for (ep = runp; next = NEXT(ep), next != eventp; ep = next) 728 if (ep->id != INVALID_EVENT) { 729 if (next->id != INVALID_EVENT) 730 continue; 731 follower = NEXT(next); 732 if (follower->id == INVALID_EVENT) 733 continue; 734 735 /* merge click events forward */ 736 if ((ep->bstate & 737 (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) 738 && (follower->bstate & 739 (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED))) { 740 if ((eventmask & BUTTON1_DOUBLE_CLICKED) 741 && (follower->bstate & BUTTON1_CLICKED)) { 742 follower->bstate &= ~BUTTON1_CLICKED; 743 follower->bstate |= BUTTON1_DOUBLE_CLICKED; 744 merge = TRUE; 745 } 746 if ((eventmask & BUTTON2_DOUBLE_CLICKED) 747 && (follower->bstate & BUTTON2_CLICKED)) { 748 follower->bstate &= ~BUTTON2_CLICKED; 749 follower->bstate |= BUTTON2_DOUBLE_CLICKED; 750 merge = TRUE; 751 } 752 if ((eventmask & BUTTON3_DOUBLE_CLICKED) 753 && (follower->bstate & BUTTON3_CLICKED)) { 754 follower->bstate &= ~BUTTON3_CLICKED; 755 follower->bstate |= BUTTON3_DOUBLE_CLICKED; 756 merge = TRUE; 757 } 758 if (merge) 759 ep->id = INVALID_EVENT; 760 } 761 762 /* merge double-click events forward */ 763 if ((ep->bstate & 764 (BUTTON1_DOUBLE_CLICKED 765 | BUTTON2_DOUBLE_CLICKED 766 | BUTTON3_DOUBLE_CLICKED)) 767 && (follower->bstate & 768 (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED))) { 769 if ((eventmask & BUTTON1_TRIPLE_CLICKED) 770 && (follower->bstate & BUTTON1_CLICKED)) { 771 follower->bstate &= ~BUTTON1_CLICKED; 772 follower->bstate |= BUTTON1_TRIPLE_CLICKED; 773 merge = TRUE; 774 } 775 if ((eventmask & BUTTON2_TRIPLE_CLICKED) 776 && (follower->bstate & BUTTON2_CLICKED)) { 777 follower->bstate &= ~BUTTON2_CLICKED; 778 follower->bstate |= BUTTON2_TRIPLE_CLICKED; 779 merge = TRUE; 780 } 781 if ((eventmask & BUTTON3_TRIPLE_CLICKED) 782 && (follower->bstate & BUTTON3_CLICKED)) { 783 follower->bstate &= ~BUTTON3_CLICKED; 784 follower->bstate |= BUTTON3_TRIPLE_CLICKED; 785 merge = TRUE; 786 } 787 if (merge) 788 ep->id = INVALID_EVENT; 789 } 790 } 791 } while 792 (merge); 793 794 #ifdef TRACE 795 if (_nc_tracing & TRACE_IEVENT) { 796 _trace_slot("before mouse event queue compaction:"); 797 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 798 (long) (runp - events), 799 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX, 800 runcount); 801 } 802 #endif /* TRACE */ 803 804 /* 805 * Now try to throw away trailing events flagged invalid, or that 806 * don't match the current event mask. 807 */ 808 for (; runcount; prev = PREV(eventp), runcount--) 809 if (prev->id == INVALID_EVENT || !(prev->bstate & eventmask)) { 810 eventp = prev; 811 } 812 #ifdef TRACE 813 if (_nc_tracing & TRACE_IEVENT) { 814 _trace_slot("after mouse event queue compaction:"); 815 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 816 (long) (runp - events), 817 (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX, 818 runcount); 819 } 820 for (ep = runp; ep != eventp; ep = NEXT(ep)) 821 if (ep->id != INVALID_EVENT) 822 TR(MY_TRACE, 823 ("_nc_mouse_parse: returning composite mouse event %s at slot %ld", 824 _tracemouse(ep), 825 (long) (ep - events))); 826 #endif /* TRACE */ 827 828 /* after all this, do we have a valid event? */ 829 return (PREV(eventp)->id != INVALID_EVENT); 830 } 831 832 static void 833 _nc_mouse_wrap(SCREEN * sp GCC_UNUSED) 834 /* release mouse -- called by endwin() before shellout/exit */ 835 { 836 TR(MY_TRACE, ("_nc_mouse_wrap() called")); 837 838 switch (mousetype) { 839 case M_XTERM: 840 if (eventmask) 841 mouse_activate(FALSE); 842 break; 843 #if USE_GPM_SUPPORT 844 /* GPM: pass all mouse events to next client */ 845 case M_GPM: 846 break; 847 #endif 848 } 849 } 850 851 static void 852 _nc_mouse_resume(SCREEN * sp GCC_UNUSED) 853 /* re-connect to mouse -- called by doupdate() after shellout */ 854 { 855 TR(MY_TRACE, ("_nc_mouse_resume() called")); 856 857 /* xterm: re-enable reporting */ 858 if (mousetype == M_XTERM && eventmask) 859 mouse_activate(TRUE); 860 861 /* GPM: reclaim our event set */ 862 } 863 864 /************************************************************************** 865 * 866 * Mouse interface entry points for the API 867 * 868 **************************************************************************/ 869 870 NCURSES_EXPORT(int) 871 getmouse(MEVENT * aevent) 872 /* grab a copy of the current mouse event */ 873 { 874 T((T_CALLED("getmouse(%p)"), aevent)); 875 876 if (aevent && (mousetype != M_NONE)) { 877 /* compute the current-event pointer */ 878 MEVENT *prev = PREV(eventp); 879 880 /* copy the event we find there */ 881 *aevent = *prev; 882 883 TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld", 884 _tracemouse(prev), 885 (long) (prev - events))); 886 887 prev->id = INVALID_EVENT; /* so the queue slot becomes free */ 888 returnCode(OK); 889 } 890 returnCode(ERR); 891 } 892 893 NCURSES_EXPORT(int) 894 ungetmouse(MEVENT * aevent) 895 /* enqueue a synthesized mouse event to be seen by the next wgetch() */ 896 { 897 /* stick the given event in the next-free slot */ 898 *eventp = *aevent; 899 900 /* bump the next-free pointer into the circular list */ 901 eventp = NEXT(eventp); 902 903 /* push back the notification event on the keyboard queue */ 904 return ungetch(KEY_MOUSE); 905 } 906 907 NCURSES_EXPORT(mmask_t) 908 mousemask(mmask_t newmask, mmask_t * oldmask) 909 /* set the mouse event mask */ 910 { 911 mmask_t result = 0; 912 913 T((T_CALLED("mousemask(%#lx,%p)"), newmask, oldmask)); 914 915 if (oldmask) 916 *oldmask = eventmask; 917 918 if (!newmask && !initialized) 919 returnCode(0); 920 921 _nc_mouse_init(); 922 if (mousetype != M_NONE) { 923 eventmask = newmask & 924 (BUTTON_ALT | BUTTON_CTRL | BUTTON_SHIFT 925 | BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED 926 | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED 927 | BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_CLICKED 928 | BUTTON2_DOUBLE_CLICKED | BUTTON2_TRIPLE_CLICKED 929 | BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_CLICKED 930 | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED); 931 932 mouse_activate(eventmask != 0); 933 934 result = eventmask; 935 } 936 937 returnCode(result); 938 } 939 940 NCURSES_EXPORT(bool) 941 wenclose(const WINDOW *win, int y, int x) 942 /* check to see if given window encloses given screen location */ 943 { 944 if (win) { 945 y -= win->_yoffset; 946 return ((win->_begy <= y && 947 win->_begx <= x && 948 (win->_begx + win->_maxx) >= x && 949 (win->_begy + win->_maxy) >= y) ? TRUE : FALSE); 950 } 951 return FALSE; 952 } 953 954 NCURSES_EXPORT(int) 955 mouseinterval(int maxclick) 956 /* set the maximum mouse interval within which to recognize a click */ 957 { 958 int oldval; 959 960 if (SP != 0) { 961 oldval = SP->_maxclick; 962 if (maxclick >= 0) 963 SP->_maxclick = maxclick; 964 } else { 965 oldval = DEFAULT_MAXCLICK; 966 } 967 968 return (oldval); 969 } 970 971 /* This may be used by other routines to ask for the existence of mouse 972 support */ 973 NCURSES_EXPORT(int) 974 _nc_has_mouse(void) 975 { 976 return (mousetype == M_NONE ? 0 : 1); 977 } 978 979 NCURSES_EXPORT(bool) 980 wmouse_trafo 981 (const WINDOW *win, int *pY, int *pX, bool to_screen) 982 { 983 bool result = FALSE; 984 985 if (win && pY && pX) { 986 int y = *pY; 987 int x = *pX; 988 989 if (to_screen) { 990 y += win->_begy + win->_yoffset; 991 x += win->_begx; 992 if (wenclose(win, y, x)) 993 result = TRUE; 994 } else { 995 if (wenclose(win, y, x)) { 996 y -= (win->_begy + win->_yoffset); 997 x -= win->_begx; 998 result = TRUE; 999 } 1000 } 1001 if (result) { 1002 *pX = x; 1003 *pY = y; 1004 } 1005 } 1006 return (result); 1007 } 1008 1009 /* lib_mouse.c ends here */ 1010