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