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