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