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