1 /**************************************************************************** 2 * Copyright 2018-2019,2020 Thomas E. Dickey * 3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /**************************************************************************** 31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 32 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 33 * and: Thomas E. Dickey 1996-on * 34 * and: Juergen Pfeifer 2008 * 35 ****************************************************************************/ 36 37 /* 38 * This module is intended to encapsulate ncurses's interface to pointing 39 * devices. 40 * 41 * The primary method used is xterm's internal mouse-tracking facility. 42 * Additional methods depend on the platform: 43 * Alessandro Rubini's GPM server (Linux) 44 * sysmouse (FreeBSD) 45 * special-purpose mouse interface for OS/2 EMX. 46 * 47 * Notes for implementors of new mouse-interface methods: 48 * 49 * The code is logically split into a lower level that accepts event reports 50 * in a device-dependent format and an upper level that parses mouse gestures 51 * and filters events. The mediating data structure is a circular queue of 52 * MEVENT structures. 53 * 54 * Functionally, the lower level's job is to pick up primitive events and 55 * put them on the circular queue. This can happen in one of two ways: 56 * either (a) _nc_mouse_event() detects a series of incoming mouse reports 57 * and queues them, or (b) code in lib_getch.c detects the kmous prefix in 58 * the keyboard input stream and calls _nc_mouse_inline to queue up a series 59 * of adjacent mouse reports. 60 * 61 * In either case, _nc_mouse_parse() should be called after the series is 62 * accepted to parse the digested mouse reports (low-level MEVENTs) into 63 * a gesture (a high-level or composite MEVENT). 64 * 65 * Don't be too shy about adding new event types or modifiers, if you can find 66 * room for them in the 32-bit mask. The API is written so that users get 67 * feedback on which theoretical event types they won't see when they call 68 * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being 69 * used yet, and a couple of bits open at the high end. 70 */ 71 72 #ifdef __EMX__ 73 # include <io.h> 74 # define INCL_DOS 75 # define INCL_VIO 76 # define INCL_KBD 77 # define INCL_MOU 78 # define INCL_DOSPROCESS 79 # include <os2.h> /* Need to include before the others */ 80 #endif 81 82 #include <curses.priv.h> 83 84 #ifndef CUR 85 #define CUR SP_TERMTYPE 86 #endif 87 88 MODULE_ID("$Id: lib_mouse.c,v 1.184 2020/02/02 23:34:34 tom Exp $") 89 90 #include <tic.h> 91 92 #if USE_GPM_SUPPORT 93 #include <linux/keyboard.h> /* defines KG_* macros */ 94 95 #ifdef HAVE_LIBDL 96 /* use dynamic loader to avoid linkage dependency */ 97 #include <dlfcn.h> 98 99 #ifdef RTLD_NOW 100 #define my_RTLD RTLD_NOW 101 #else 102 #ifdef RTLD_LAZY 103 #define my_RTLD RTLD_LAZY 104 #else 105 make an error 106 #endif 107 #endif /* RTLD_NOW */ 108 #endif /* HAVE_LIBDL */ 109 110 #endif /* USE_GPM_SUPPORT */ 111 112 #if USE_SYSMOUSE 113 #undef buttons /* symbol conflict in consio.h */ 114 #undef mouse_info /* symbol conflict in consio.h */ 115 #include <osreldate.h> 116 #if defined(__DragonFly_version) || (defined(__FreeBSD__) && (__FreeBSD_version >= 400017)) 117 #include <sys/consio.h> 118 #include <sys/fbio.h> 119 #else 120 #include <machine/console.h> 121 #endif 122 #endif /* use_SYSMOUSE */ 123 124 #if USE_KLIBC_MOUSE 125 #include <sys/socket.h> 126 #define pipe(handles) socketpair(AF_LOCAL, SOCK_STREAM, 0, handles) 127 #define DosWrite(hfile, pbuffer, cbwrite, pcbactual) \ 128 write(hfile, pbuffer, cbwrite) 129 #define DosExit(action, result ) /* do nothing */ 130 #define DosCreateThread(ptid, pfn, param, flag, cbStack) \ 131 (*(ptid) = _beginthread(pfn, NULL, cbStack, \ 132 (void *)param), (*(ptid) == -1)) 133 #endif 134 135 #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT 136 137 #define MASK_RELEASE(x) (mmask_t) NCURSES_MOUSE_MASK(x, 001) 138 #define MASK_PRESS(x) (mmask_t) NCURSES_MOUSE_MASK(x, 002) 139 #define MASK_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 004) 140 #define MASK_DOUBLE_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 010) 141 #define MASK_TRIPLE_CLICK(x) (mmask_t) NCURSES_MOUSE_MASK(x, 020) 142 #define MASK_RESERVED_EVENT(x) (mmask_t) NCURSES_MOUSE_MASK(x, 040) 143 144 #if NCURSES_MOUSE_VERSION == 1 145 #define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED) 146 #define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED) 147 #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED) 148 #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED) 149 #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED) 150 #define MAX_BUTTONS 4 151 #else 152 #define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED | BUTTON5_CLICKED) 153 #define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED | BUTTON5_PRESSED) 154 #define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED | BUTTON5_RELEASED) 155 #define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED) 156 #define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED) 157 #define MAX_BUTTONS 5 158 #endif 159 160 #define INVALID_EVENT -1 161 #define NORMAL_EVENT 0 162 163 #define ValidEvent(ep) ((ep)->id != INVALID_EVENT) 164 #define Invalidate(ep) (ep)->id = INVALID_EVENT 165 166 #if USE_GPM_SUPPORT 167 168 #ifndef LIBGPM_SONAME 169 #define LIBGPM_SONAME "libgpm.so" 170 #endif 171 172 #define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(sp->_dlopen_gpm, #name)) 173 174 #endif /* USE_GPM_SUPPORT */ 175 176 static bool _nc_mouse_parse(SCREEN *, int); 177 static void _nc_mouse_resume(SCREEN *); 178 static void _nc_mouse_wrap(SCREEN *); 179 180 /* maintain a circular list of mouse events */ 181 182 #define FirstEV(sp) ((sp)->_mouse_events) 183 #define LastEV(sp) ((sp)->_mouse_events + EV_MAX - 1) 184 185 #undef NEXT 186 #define NEXT(ep) ((ep >= LastEV(SP_PARM)) \ 187 ? FirstEV(SP_PARM) \ 188 : ep + 1) 189 190 #undef PREV 191 #define PREV(ep) ((ep <= FirstEV(SP_PARM)) \ 192 ? LastEV(SP_PARM) \ 193 : ep - 1) 194 195 #define IndexEV(sp, ep) (ep - FirstEV(sp)) 196 197 #define RunParams(sp, eventp, runp) \ 198 (long) IndexEV(sp, runp), \ 199 (long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX 200 201 #ifdef TRACE 202 static void 203 _trace_slot(SCREEN *sp, const char *tag) 204 { 205 MEVENT *ep; 206 207 _tracef("%s", tag); 208 209 for (ep = FirstEV(sp); ep <= LastEV(sp); ep++) 210 _tracef("mouse event queue slot %ld = %s", 211 (long) IndexEV(sp, ep), 212 _nc_tracemouse(sp, ep)); 213 } 214 #endif 215 216 #if USE_EMX_MOUSE 217 218 # define TOP_ROW 0 219 # define LEFT_COL 0 220 221 # define M_FD(sp) sp->_mouse_fd 222 223 static void 224 write_event(SCREEN *sp, int down, int button, int x, int y) 225 { 226 char buf[6]; 227 unsigned long ignore; 228 229 _nc_STRCPY(buf, "\033[M", sizeof(buf)); /* should be the same as key_mouse */ 230 buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40); 231 buf[4] = ' ' + x - LEFT_COL + 1; 232 buf[5] = ' ' + y - TOP_ROW + 1; 233 DosWrite(sp->_emxmouse_wfd, buf, 6, &ignore); 234 } 235 236 static void 237 #if USE_KLIBC_MOUSE 238 mouse_server(void *param) 239 #else 240 mouse_server(unsigned long param) 241 #endif 242 { 243 SCREEN *sp = (SCREEN *) param; 244 unsigned short fWait = MOU_WAIT; 245 /* NOPTRRECT mourt = { 0,0,24,79 }; */ 246 MOUEVENTINFO mouev; 247 HMOU hmou; 248 unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN; 249 int nbuttons = 3; 250 int oldstate = 0; 251 char err[80]; 252 unsigned long rc; 253 254 /* open the handle for the mouse */ 255 if (MouOpen(NULL, &hmou) == 0) { 256 rc = MouSetEventMask(&mask, hmou); 257 if (rc) { /* retry with 2 buttons */ 258 mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN; 259 rc = MouSetEventMask(&mask, hmou); 260 nbuttons = 2; 261 } 262 if (rc == 0 && MouDrawPtr(hmou) == 0) { 263 for (;;) { 264 /* sit and wait on the event queue */ 265 rc = MouReadEventQue(&mouev, &fWait, hmou); 266 if (rc) { 267 _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err)) 268 "Error reading mouse queue, rc=%lu.\r\n", rc); 269 break; 270 } 271 if (!sp->_emxmouse_activated) 272 goto finish; 273 274 /* 275 * OS/2 numbers a 3-button mouse inconsistently from other 276 * platforms: 277 * 1 = left 278 * 2 = right 279 * 3 = middle. 280 */ 281 if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN) 282 write_event(sp, mouev.fs & MOUSE_BN1_DOWN, 283 sp->_emxmouse_buttons[1], mouev.col, mouev.row); 284 if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN) 285 write_event(sp, mouev.fs & MOUSE_BN2_DOWN, 286 sp->_emxmouse_buttons[3], mouev.col, mouev.row); 287 if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN) 288 write_event(sp, mouev.fs & MOUSE_BN3_DOWN, 289 sp->_emxmouse_buttons[2], mouev.col, mouev.row); 290 291 finish: 292 oldstate = mouev.fs; 293 } 294 } else { 295 _nc_SPRINTF(err, _nc_SLIMIT(sizeof(err)) 296 "Error setting event mask, buttons=%d, rc=%lu.\r\n", 297 nbuttons, rc); 298 } 299 300 DosWrite(2, err, strlen(err), &rc); 301 MouClose(hmou); 302 } 303 DosExit(EXIT_THREAD, 0L); 304 } 305 306 #endif /* USE_EMX_MOUSE */ 307 308 #if USE_SYSMOUSE 309 static void 310 sysmouse_server(SCREEN *sp) 311 { 312 struct mouse_info the_mouse; 313 MEVENT *work; 314 315 the_mouse.operation = MOUSE_GETINFO; 316 if (sp != 0 317 && sp->_mouse_fd >= 0 318 && sp->_sysmouse_tail < FIFO_SIZE 319 && ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { 320 321 if (sp->_sysmouse_head > sp->_sysmouse_tail) { 322 sp->_sysmouse_tail = 0; 323 sp->_sysmouse_head = 0; 324 } 325 work = &(sp->_sysmouse_fifo[sp->_sysmouse_tail]); 326 memset(work, 0, sizeof(*work)); 327 work->id = NORMAL_EVENT; /* there's only one mouse... */ 328 329 sp->_sysmouse_old_buttons = sp->_sysmouse_new_buttons; 330 sp->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7; 331 332 if (sp->_sysmouse_new_buttons) { 333 if (sp->_sysmouse_new_buttons & 1) 334 work->bstate |= BUTTON1_PRESSED; 335 if (sp->_sysmouse_new_buttons & 2) 336 work->bstate |= BUTTON2_PRESSED; 337 if (sp->_sysmouse_new_buttons & 4) 338 work->bstate |= BUTTON3_PRESSED; 339 } else { 340 if (sp->_sysmouse_old_buttons & 1) 341 work->bstate |= BUTTON1_RELEASED; 342 if (sp->_sysmouse_old_buttons & 2) 343 work->bstate |= BUTTON2_RELEASED; 344 if (sp->_sysmouse_old_buttons & 4) 345 work->bstate |= BUTTON3_RELEASED; 346 } 347 348 /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */ 349 the_mouse.operation = MOUSE_HIDE; 350 ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); 351 the_mouse.operation = MOUSE_SHOW; 352 ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); 353 354 /* 355 * We're only interested if the button is pressed or released. 356 * FIXME: implement continuous event-tracking. 357 */ 358 if (sp->_sysmouse_new_buttons != sp->_sysmouse_old_buttons) { 359 sp->_sysmouse_tail += 1; 360 } 361 work->x = the_mouse.u.data.x / sp->_sysmouse_char_width; 362 work->y = the_mouse.u.data.y / sp->_sysmouse_char_height; 363 } 364 } 365 366 static void 367 handle_sysmouse(int sig GCC_UNUSED) 368 { 369 sysmouse_server(CURRENT_SCREEN); 370 } 371 #endif /* USE_SYSMOUSE */ 372 373 #ifndef USE_TERM_DRIVER 374 #define xterm_kmous "\033[M" 375 376 static void 377 init_xterm_mouse(SCREEN *sp) 378 { 379 sp->_mouse_type = M_XTERM; 380 sp->_mouse_format = MF_X10; 381 sp->_mouse_xtermcap = tigetstr("XM"); 382 if (VALID_STRING(sp->_mouse_xtermcap)) { 383 char *code = strstr(sp->_mouse_xtermcap, "[?"); 384 if (code != 0) { 385 code += 2; 386 while ((*code >= '0') && (*code <= '9')) { 387 char *next = code; 388 while ((*next >= '0') && (*next <= '9')) { 389 ++next; 390 } 391 if (!strncmp(code, "1006", (size_t) (next - code))) { 392 sp->_mouse_format = MF_SGR1006; 393 } 394 #ifdef EXP_XTERM_1005 395 if (!strncmp(code, "1005", (size_t) (next - code))) { 396 sp->_mouse_format = MF_XTERM_1005; 397 } 398 #endif 399 if (*next == ';') { 400 while (*next == ';') { 401 ++next; 402 } 403 code = next; 404 } else { 405 break; 406 } 407 } 408 } 409 } else { 410 int code = tigetnum("XM"); 411 switch (code) { 412 case 1006: 413 break; 414 default: 415 code = 1000; 416 break; 417 } 418 sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;"; 419 } 420 } 421 #endif 422 423 static void 424 enable_xterm_mouse(SCREEN *sp, int enable) 425 { 426 #if USE_EMX_MOUSE 427 sp->_emxmouse_activated = enable; 428 #else 429 NCURSES_PUTP2("xterm-mouse", TPARM_1(sp->_mouse_xtermcap, enable)); 430 #endif 431 sp->_mouse_active = enable; 432 } 433 434 #if USE_GPM_SUPPORT 435 static bool 436 allow_gpm_mouse(SCREEN *sp GCC_UNUSED) 437 { 438 bool result = FALSE; 439 440 #if USE_WEAK_SYMBOLS 441 /* Danger Robinson: do not use dlopen for libgpm if already loaded */ 442 if ((Gpm_Wgetch) != 0) { 443 if (!sp->_mouse_gpm_loaded) { 444 T(("GPM library was already dlopen'd, not by us")); 445 } 446 } else 447 #endif 448 /* GPM does printf's without checking if stdout is a terminal */ 449 if (NC_ISATTY(fileno(stdout))) { 450 const char *list = getenv("NCURSES_GPM_TERMS"); 451 const char *env = getenv("TERM"); 452 if (list != 0) { 453 if (env != 0) { 454 result = _nc_name_match(list, env, "|:"); 455 } 456 } else { 457 /* GPM checks the beginning of the $TERM variable to decide if it 458 * should pass xterm events through. There is no real advantage in 459 * allowing GPM to do this. Recent versions relax that check, and 460 * pretend that GPM can work with any terminal having the kmous 461 * capability. Perhaps that works for someone. If so, they can 462 * set the environment variable (above). 463 */ 464 if (env != 0 && strstr(env, "linux") != 0) { 465 result = TRUE; 466 } 467 } 468 } 469 return result; 470 } 471 472 #ifdef HAVE_LIBDL 473 static void 474 unload_gpm_library(SCREEN *sp) 475 { 476 if (sp->_dlopen_gpm != 0) { 477 T(("unload GPM library")); 478 sp->_mouse_gpm_loaded = FALSE; 479 sp->_mouse_fd = -1; 480 dlclose(sp->_dlopen_gpm); 481 sp->_dlopen_gpm = 0; 482 } 483 } 484 485 static void 486 load_gpm_library(SCREEN *sp) 487 { 488 sp->_mouse_gpm_found = FALSE; 489 if ((sp->_dlopen_gpm = dlopen(LIBGPM_SONAME, my_RTLD)) != 0) { 490 #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__) 491 #pragma GCC diagnostic push 492 #pragma GCC diagnostic ignored "-Wpedantic" 493 #endif 494 if (GET_DLSYM(gpm_fd) == 0 || 495 GET_DLSYM(Gpm_Open) == 0 || 496 GET_DLSYM(Gpm_Close) == 0 || 497 GET_DLSYM(Gpm_GetEvent) == 0) { 498 #if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__) 499 #pragma GCC diagnostic pop 500 #endif 501 T(("GPM initialization failed: %s", dlerror())); 502 unload_gpm_library(sp); 503 } else { 504 sp->_mouse_gpm_found = TRUE; 505 sp->_mouse_gpm_loaded = TRUE; 506 } 507 } 508 } 509 #endif 510 511 static bool 512 enable_gpm_mouse(SCREEN *sp, bool enable) 513 { 514 bool result; 515 516 T((T_CALLED("enable_gpm_mouse(%d)"), enable)); 517 518 if (enable && !sp->_mouse_active) { 519 #ifdef HAVE_LIBDL 520 if (sp->_mouse_gpm_found && !sp->_mouse_gpm_loaded) { 521 load_gpm_library(sp); 522 } 523 #endif 524 if (sp->_mouse_gpm_loaded) { 525 int code; 526 527 /* GPM: initialize connection to gpm server */ 528 sp->_mouse_gpm_connect.eventMask = GPM_DOWN | GPM_UP; 529 sp->_mouse_gpm_connect.defaultMask = 530 (unsigned short) (~(sp->_mouse_gpm_connect.eventMask | GPM_HARD)); 531 sp->_mouse_gpm_connect.minMod = 0; 532 sp->_mouse_gpm_connect.maxMod = 533 (unsigned short) (~((1 << KG_SHIFT) | 534 (1 << KG_SHIFTL) | 535 (1 << KG_SHIFTR))); 536 /* 537 * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open. 538 * The former is recognized by wscons (SunOS), and the latter by 539 * xterm. Those will not show up in ncurses' traces. 540 */ 541 code = my_Gpm_Open(&sp->_mouse_gpm_connect, 0); 542 result = (code >= 0); 543 544 /* 545 * GPM can return a -2 if it is trying to do something with xterm. 546 * Ignore that, since it conflicts with our use of stdin. 547 */ 548 if (code == -2) { 549 my_Gpm_Close(); 550 } 551 } else { 552 result = FALSE; 553 } 554 sp->_mouse_active = result; 555 T(("GPM open %s", result ? "succeeded" : "failed")); 556 } else { 557 if (!enable && sp->_mouse_active) { 558 /* GPM: close connection to gpm server */ 559 my_Gpm_Close(); 560 sp->_mouse_active = FALSE; 561 T(("GPM closed")); 562 } 563 result = enable; 564 } 565 #ifdef HAVE_LIBDL 566 if (!result) { 567 unload_gpm_library(sp); 568 } 569 #endif 570 returnBool(result); 571 } 572 #endif /* USE_GPM_SUPPORT */ 573 574 static void 575 initialize_mousetype(SCREEN *sp) 576 { 577 T((T_CALLED("initialize_mousetype()"))); 578 579 /* Try gpm first, because gpm may be configured to run in xterm */ 580 #if USE_GPM_SUPPORT 581 if (allow_gpm_mouse(sp)) { 582 if (!sp->_mouse_gpm_loaded) { 583 #ifdef HAVE_LIBDL 584 load_gpm_library(sp); 585 #else /* !HAVE_LIBDL */ 586 sp->_mouse_gpm_found = TRUE; 587 sp->_mouse_gpm_loaded = TRUE; 588 #endif 589 } 590 591 /* 592 * The gpm_fd file-descriptor may be negative (xterm). So we have to 593 * maintain our notion of whether the mouse connection is active 594 * without testing the file-descriptor. 595 */ 596 if (sp->_mouse_gpm_found && enable_gpm_mouse(sp, TRUE)) { 597 sp->_mouse_type = M_GPM; 598 sp->_mouse_fd = *(my_gpm_fd); 599 T(("GPM mouse_fd %d", sp->_mouse_fd)); 600 returnVoid; 601 } 602 } 603 #endif /* USE_GPM_SUPPORT */ 604 605 /* OS/2 VIO */ 606 #if USE_EMX_MOUSE 607 if (!sp->_emxmouse_thread 608 && strstr(SP_TERMTYPE term_names, "xterm") == 0 609 && NonEmpty(key_mouse)) { 610 int handles[2]; 611 612 if (pipe(handles) < 0) { 613 perror("mouse pipe error"); 614 returnVoid; 615 } else { 616 int rc; 617 618 if (!sp->_emxmouse_buttons[0]) { 619 const char *s = getenv("MOUSE_BUTTONS_123"); 620 621 sp->_emxmouse_buttons[0] = 1; 622 if (s && strlen(s) >= 3) { 623 sp->_emxmouse_buttons[1] = s[0] - '0'; 624 sp->_emxmouse_buttons[2] = s[1] - '0'; 625 sp->_emxmouse_buttons[3] = s[2] - '0'; 626 } else { 627 sp->_emxmouse_buttons[1] = 1; 628 sp->_emxmouse_buttons[2] = 3; 629 sp->_emxmouse_buttons[3] = 2; 630 } 631 } 632 sp->_emxmouse_wfd = handles[1]; 633 M_FD(sp) = handles[0]; 634 /* Needed? */ 635 setmode(handles[0], O_BINARY); 636 setmode(handles[1], O_BINARY); 637 /* Do not use CRT functions, we may single-threaded. */ 638 rc = DosCreateThread((unsigned long *) &sp->_emxmouse_thread, 639 mouse_server, (long) sp, 0, 8192); 640 if (rc) { 641 printf("mouse thread error %d=%#x", rc, rc); 642 } else { 643 sp->_mouse_type = M_XTERM; 644 } 645 returnVoid; 646 } 647 } 648 #endif /* USE_EMX_MOUSE */ 649 650 #if USE_SYSMOUSE 651 { 652 static char dev_tty[] = "/dev/tty"; 653 struct mouse_info the_mouse; 654 char *the_device = 0; 655 656 if (NC_ISATTY(sp->_ifd)) 657 the_device = ttyname(sp->_ifd); 658 if (the_device == 0) 659 the_device = dev_tty; 660 661 sp->_mouse_fd = open(the_device, O_RDWR); 662 663 if (sp->_mouse_fd >= 0) { 664 /* 665 * sysmouse does not have a usable user interface for obtaining 666 * mouse events. The logical way to proceed (reading data on a 667 * stream) only works if one opens the device as root. Even in 668 * that mode, careful examination shows we lose events 669 * occasionally. The interface provided for user programs is to 670 * establish a signal handler. really. 671 * 672 * Take over SIGUSR2 for this purpose since SIGUSR1 is more 673 * likely to be used by an application. getch() will have to 674 * handle the misleading EINTR's. 675 */ 676 signal(SIGUSR2, SIG_IGN); 677 the_mouse.operation = MOUSE_MODE; 678 the_mouse.u.mode.mode = 0; 679 the_mouse.u.mode.signal = SIGUSR2; 680 if (ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { 681 signal(SIGUSR2, handle_sysmouse); 682 the_mouse.operation = MOUSE_SHOW; 683 ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); 684 685 #if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) /* FreeBSD > 2.x */ 686 { 687 #ifndef FBIO_GETMODE /* FreeBSD 3.x */ 688 #define FBIO_GETMODE CONS_GET 689 #define FBIO_MODEINFO CONS_MODEINFO 690 #endif /* FBIO_GETMODE */ 691 video_info_t the_video; 692 693 if (ioctl(sp->_mouse_fd, 694 FBIO_GETMODE, 695 &the_video.vi_mode) != -1 696 && ioctl(sp->_mouse_fd, 697 FBIO_MODEINFO, 698 &the_video) != -1) { 699 sp->_sysmouse_char_width = the_video.vi_cwidth; 700 sp->_sysmouse_char_height = the_video.vi_cheight; 701 } 702 } 703 #endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */ 704 705 if (sp->_sysmouse_char_width <= 0) 706 sp->_sysmouse_char_width = 8; 707 if (sp->_sysmouse_char_height <= 0) 708 sp->_sysmouse_char_height = 16; 709 sp->_mouse_type = M_SYSMOUSE; 710 returnVoid; 711 } 712 } 713 } 714 #endif /* USE_SYSMOUSE */ 715 716 #ifdef USE_TERM_DRIVER 717 CallDriver(sp, td_initmouse); 718 #else 719 /* we know how to recognize mouse events under "xterm" */ 720 if (NonEmpty(key_mouse)) { 721 init_xterm_mouse(sp); 722 } else if (strstr(SP_TERMTYPE term_names, "xterm") != 0) { 723 if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK) 724 init_xterm_mouse(sp); 725 } 726 #endif 727 728 returnVoid; 729 } 730 731 static bool 732 _nc_mouse_init(SCREEN *sp) 733 /* initialize the mouse */ 734 { 735 bool result = FALSE; 736 737 if (sp != 0) { 738 if (!sp->_mouse_initialized) { 739 int i; 740 741 sp->_mouse_initialized = TRUE; 742 743 TR(MY_TRACE, ("_nc_mouse_init() called")); 744 745 sp->_mouse_eventp = FirstEV(sp); 746 for (i = 0; i < EV_MAX; i++) 747 Invalidate(sp->_mouse_events + i); 748 749 initialize_mousetype(sp); 750 751 T(("_nc_mouse_init() set mousetype to %d", sp->_mouse_type)); 752 } 753 result = sp->_mouse_initialized; 754 } 755 return result; 756 } 757 758 /* 759 * Query to see if there is a pending mouse event. This is called from 760 * fifo_push() in lib_getch.c 761 */ 762 static bool 763 _nc_mouse_event(SCREEN *sp) 764 { 765 MEVENT *eventp = sp->_mouse_eventp; 766 bool result = FALSE; 767 768 (void) eventp; 769 770 switch (sp->_mouse_type) { 771 case M_XTERM: 772 /* xterm: never have to query, mouse events are in the keyboard stream */ 773 #if USE_EMX_MOUSE 774 { 775 char kbuf[3]; 776 777 int i, res = read(M_FD(sp), &kbuf, 3); /* Eat the prefix */ 778 if (res != 3) 779 printf("Got %d chars instead of 3 for prefix.\n", res); 780 for (i = 0; i < res; i++) { 781 if (kbuf[i] != key_mouse[i]) 782 printf("Got char %d instead of %d for prefix.\n", 783 (int) kbuf[i], (int) key_mouse[i]); 784 } 785 result = TRUE; 786 } 787 #endif /* USE_EMX_MOUSE */ 788 break; 789 790 #if USE_GPM_SUPPORT 791 case M_GPM: 792 if (sp->_mouse_fd >= 0) { 793 /* query server for event, return TRUE if we find one */ 794 Gpm_Event ev; 795 796 switch (my_Gpm_GetEvent(&ev)) { 797 case 0: 798 /* Connection closed, drop the mouse. */ 799 sp->_mouse_fd = -1; 800 break; 801 case 1: 802 /* there's only one mouse... */ 803 eventp->id = NORMAL_EVENT; 804 805 eventp->bstate = 0; 806 switch (ev.type & 0x0f) { 807 case (GPM_DOWN): 808 if (ev.buttons & GPM_B_LEFT) 809 eventp->bstate |= BUTTON1_PRESSED; 810 if (ev.buttons & GPM_B_MIDDLE) 811 eventp->bstate |= BUTTON2_PRESSED; 812 if (ev.buttons & GPM_B_RIGHT) 813 eventp->bstate |= BUTTON3_PRESSED; 814 break; 815 case (GPM_UP): 816 if (ev.buttons & GPM_B_LEFT) 817 eventp->bstate |= BUTTON1_RELEASED; 818 if (ev.buttons & GPM_B_MIDDLE) 819 eventp->bstate |= BUTTON2_RELEASED; 820 if (ev.buttons & GPM_B_RIGHT) 821 eventp->bstate |= BUTTON3_RELEASED; 822 break; 823 default: 824 eventp->bstate |= REPORT_MOUSE_POSITION; 825 break; 826 } 827 828 eventp->x = ev.x - 1; 829 eventp->y = ev.y - 1; 830 eventp->z = 0; 831 832 /* bump the next-free pointer into the circular list */ 833 sp->_mouse_eventp = NEXT(eventp); 834 result = TRUE; 835 break; 836 } 837 } 838 break; 839 #endif 840 841 #if USE_SYSMOUSE 842 case M_SYSMOUSE: 843 if (sp->_sysmouse_head < sp->_sysmouse_tail) { 844 *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head]; 845 846 /* 847 * Point the fifo-head to the next possible location. If there 848 * are none, reset the indices. This may be interrupted by the 849 * signal handler, doing essentially the same reset. 850 */ 851 sp->_sysmouse_head += 1; 852 if (sp->_sysmouse_head == sp->_sysmouse_tail) { 853 sp->_sysmouse_tail = 0; 854 sp->_sysmouse_head = 0; 855 } 856 857 /* bump the next-free pointer into the circular list */ 858 sp->_mouse_eventp = eventp = NEXT(eventp); 859 result = TRUE; 860 } 861 break; 862 #endif /* USE_SYSMOUSE */ 863 864 #ifdef USE_TERM_DRIVER 865 case M_TERM_DRIVER: 866 while (sp->_drv_mouse_head < sp->_drv_mouse_tail) { 867 *eventp = sp->_drv_mouse_fifo[sp->_drv_mouse_head]; 868 869 /* 870 * Point the fifo-head to the next possible location. If there 871 * are none, reset the indices. 872 */ 873 sp->_drv_mouse_head += 1; 874 if (sp->_drv_mouse_head == sp->_drv_mouse_tail) { 875 sp->_drv_mouse_tail = 0; 876 sp->_drv_mouse_head = 0; 877 } 878 879 /* bump the next-free pointer into the circular list */ 880 sp->_mouse_eventp = eventp = NEXT(eventp); 881 result = TRUE; 882 } 883 break; 884 #endif 885 886 case M_NONE: 887 break; 888 } 889 890 return result; /* true if we found an event */ 891 } 892 893 #if USE_EMX_MOUSE 894 #define PRESS_POSITION(n) \ 895 do { \ 896 eventp->bstate = MASK_PRESS(n); \ 897 sp->_mouse_bstate |= MASK_PRESS(n); \ 898 if (button & 0x40) { \ 899 eventp->bstate = MASK_RELEASE(n); \ 900 sp->_mouse_bstate &= ~MASK_PRESS(n); \ 901 } \ 902 } while (0) 903 #else 904 #define PRESS_POSITION(n) \ 905 do { \ 906 eventp->bstate = (mmask_t) ((sp->_mouse_bstate & MASK_PRESS(n)) \ 907 ? REPORT_MOUSE_POSITION \ 908 : MASK_PRESS(n)); \ 909 sp->_mouse_bstate |= MASK_PRESS(n); \ 910 } while (0) 911 #endif 912 913 static bool 914 handle_wheel(SCREEN *sp, MEVENT * eventp, int button, int wheel) 915 { 916 bool result = TRUE; 917 918 switch (button & 3) { 919 case 0: 920 if (wheel) { 921 eventp->bstate = MASK_PRESS(4); 922 /* Do not record in sp->_mouse_bstate; there will be no 923 * corresponding release event. 924 */ 925 } else { 926 PRESS_POSITION(1); 927 } 928 break; 929 case 1: 930 if (wheel) { 931 #if NCURSES_MOUSE_VERSION == 2 932 eventp->bstate = MASK_PRESS(5); 933 /* See comment above for button 4 */ 934 #else 935 /* Ignore this event as it is not a true press of the button */ 936 eventp->bstate = REPORT_MOUSE_POSITION; 937 #endif 938 } else { 939 PRESS_POSITION(2); 940 } 941 break; 942 case 2: 943 PRESS_POSITION(3); 944 break; 945 default: 946 result = FALSE; 947 break; 948 } 949 return result; 950 } 951 952 static bool 953 decode_X10_bstate(SCREEN *sp, MEVENT * eventp, unsigned intro) 954 { 955 bool result; 956 957 eventp->bstate = 0; 958 959 if (!handle_wheel(sp, eventp, (int) intro, (intro & 96) == 96)) { 960 961 /* 962 * Release events aren't reported for individual buttons, just for 963 * the button set as a whole. However, because there are normally 964 * no mouse events under xterm that intervene between press and 965 * release, we can infer the button actually released by looking at 966 * the previous event. 967 */ 968 if (sp->_mouse_bstate & BUTTON_PRESSED) { 969 int b; 970 971 eventp->bstate = BUTTON_RELEASED; 972 for (b = 1; b <= MAX_BUTTONS; ++b) { 973 if (!(sp->_mouse_bstate & MASK_PRESS(b))) 974 eventp->bstate &= ~MASK_RELEASE(b); 975 } 976 sp->_mouse_bstate = 0; 977 } else { 978 /* 979 * xterm will return a stream of release-events to let the 980 * application know where the mouse is going, if private mode 981 * 1002 or 1003 is enabled. 982 */ 983 eventp->bstate = REPORT_MOUSE_POSITION; 984 } 985 } 986 987 if (intro & 4) { 988 eventp->bstate |= BUTTON_SHIFT; 989 } 990 if (intro & 8) { 991 eventp->bstate |= BUTTON_ALT; 992 } 993 if (intro & 16) { 994 eventp->bstate |= BUTTON_CTRL; 995 } 996 result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE; 997 return result; 998 } 999 1000 /* This code requires that your xterm entry contain the kmous capability and 1001 * that it be set to the \E[M documented in the Xterm Control Sequences 1002 * reference. This is how we arrange for mouse events to be reported via a 1003 * KEY_MOUSE return value from wgetch(). After this value is received, 1004 * _nc_mouse_inline() gets called and is immediately responsible for parsing 1005 * the mouse status information following the prefix. 1006 * 1007 * The following quotes from the ctlseqs.ms document in the XTerm distribution, 1008 * describing the mouse tracking feature: 1009 * 1010 * Parameters for all mouse tracking escape sequences generated by xterm encode 1011 * numeric parameters in a single character as value+040. For example, ! is 1012 * 1. 1013 * 1014 * On button press or release, xterm sends ESC [ M CbCxCy. The low two bits of 1015 * Cb encode button information: 0=MB1 pressed, 1=MB2 pressed, 2=MB3 pressed, 1016 * 3=release. The upper bits encode what modifiers were down when the button 1017 * was pressed and are added together. 4=Shift, 8=Meta, 16=Control. Cx and Cy 1018 * are the x and y coordinates of the mouse event. The upper left corner is 1019 * (1,1). 1020 * 1021 * (End quote) By the time we get here, we've eaten the key prefix. FYI, the 1022 * loop below is necessary because mouse click info isn't guaranteed to present 1023 * as a single clist item. 1024 * 1025 * Wheel mice may return buttons 4 and 5 when the wheel is turned. We encode 1026 * those as button presses. 1027 */ 1028 static bool 1029 decode_xterm_X10(SCREEN *sp, MEVENT * eventp) 1030 { 1031 #define MAX_KBUF 3 1032 unsigned char kbuf[MAX_KBUF + 1]; 1033 size_t grabbed; 1034 int res; 1035 bool result; 1036 1037 # if USE_PTHREADS_EINTR 1038 # if USE_WEAK_SYMBOLS 1039 if ((pthread_self) && (pthread_kill) && (pthread_equal)) 1040 # endif 1041 _nc_globals.read_thread = pthread_self(); 1042 # endif 1043 for (grabbed = 0; grabbed < MAX_KBUF; grabbed += (size_t) res) { 1044 1045 /* For VIO mouse we add extra bit 64 to disambiguate button-up. */ 1046 res = (int) read( 1047 #if USE_EMX_MOUSE 1048 (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd, 1049 #else 1050 sp->_ifd, 1051 #endif 1052 kbuf + grabbed, (size_t) (MAX_KBUF - (int) grabbed)); 1053 if (res == -1) 1054 break; 1055 } 1056 #if USE_PTHREADS_EINTR 1057 _nc_globals.read_thread = 0; 1058 #endif 1059 kbuf[MAX_KBUF] = '\0'; 1060 1061 TR(TRACE_IEVENT, 1062 ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf)); 1063 1064 /* there's only one mouse... */ 1065 eventp->id = NORMAL_EVENT; 1066 1067 result = decode_X10_bstate(sp, eventp, kbuf[0]); 1068 1069 eventp->x = (kbuf[1] - ' ') - 1; 1070 eventp->y = (kbuf[2] - ' ') - 1; 1071 1072 return result; 1073 } 1074 1075 #ifdef EXP_XTERM_1005 1076 /* 1077 * This is identical to X10/X11 responses except that there are two UTF-8 1078 * characters storing the ordinates instead of two bytes. 1079 */ 1080 static bool 1081 decode_xterm_1005(SCREEN *sp, MEVENT * eventp) 1082 { 1083 char kbuf[80]; 1084 size_t grabbed; 1085 size_t limit = (sizeof(kbuf) - 1); 1086 unsigned coords[2]; 1087 bool result; 1088 1089 coords[0] = 0; 1090 coords[1] = 0; 1091 1092 # if USE_PTHREADS_EINTR 1093 # if USE_WEAK_SYMBOLS 1094 if ((pthread_self) && (pthread_kill) && (pthread_equal)) 1095 # endif 1096 _nc_globals.read_thread = pthread_self(); 1097 # endif 1098 for (grabbed = 0; grabbed < limit;) { 1099 int res; 1100 1101 res = (int) read( 1102 #if USE_EMX_MOUSE 1103 (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd, 1104 #else 1105 sp->_ifd, 1106 #endif 1107 (kbuf + grabbed), (size_t) 1); 1108 if (res == -1) 1109 break; 1110 grabbed += (size_t) res; 1111 if (grabbed > 1) { 1112 size_t check = 1; 1113 int n; 1114 1115 for (n = 0; n < 2; ++n) { 1116 int rc; 1117 1118 if (check >= grabbed) 1119 break; 1120 rc = _nc_conv_to_utf32(&coords[n], kbuf + check, (unsigned) 1121 (grabbed - check)); 1122 if (!rc) 1123 break; 1124 check += (size_t) rc; 1125 } 1126 if (n >= 2) 1127 break; 1128 } 1129 } 1130 #if USE_PTHREADS_EINTR 1131 _nc_globals.read_thread = 0; 1132 #endif 1133 1134 TR(TRACE_IEVENT, 1135 ("_nc_mouse_inline sees the following xterm data: %s", 1136 _nc_visbufn(kbuf, (int) grabbed))); 1137 1138 /* there's only one mouse... */ 1139 eventp->id = NORMAL_EVENT; 1140 1141 result = decode_X10_bstate(sp, eventp, UChar(kbuf[0])); 1142 1143 eventp->x = (int) (coords[0] - ' ') - 1; 1144 eventp->y = (int) (coords[1] - ' ') - 1; 1145 1146 return result; 1147 } 1148 #endif /* EXP_XTERM_1005 */ 1149 1150 /* 1151 * ECMA-48 section 5.4 1152 */ 1153 #define isInter(c) ((c) >= 0x20 && (c) <= 0x2f) 1154 #define isParam(c) ((c) >= 0x30 && (c) <= 0x3f) 1155 #define isFinal(c) ((c) >= 0x40 && (c) <= 0x7e) 1156 1157 #define MAX_PARAMS 9 1158 1159 typedef struct { 1160 int nerror; /* nonzero if there are unexpected chars */ 1161 int nparam; /* number of numeric parameters */ 1162 int params[MAX_PARAMS]; 1163 int final; /* the final-character */ 1164 } SGR_DATA; 1165 1166 static bool 1167 read_SGR(SCREEN *sp, SGR_DATA * result) 1168 { 1169 char kbuf[80]; /* bigger than any possible mouse response */ 1170 int grabbed = 0; 1171 int ch = 0; 1172 int now = -1; 1173 int marker = 1; 1174 1175 memset(result, 0, sizeof(*result)); 1176 # if USE_PTHREADS_EINTR 1177 # if USE_WEAK_SYMBOLS 1178 if ((pthread_self) && (pthread_kill) && (pthread_equal)) 1179 # endif 1180 _nc_globals.read_thread = pthread_self(); 1181 # endif 1182 1183 do { 1184 int res; 1185 1186 res = (int) read( 1187 #if USE_EMX_MOUSE 1188 (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd, 1189 #else 1190 sp->_ifd, 1191 #endif 1192 (kbuf + grabbed), (size_t) 1); 1193 if (res == -1) 1194 break; 1195 if ((grabbed + MAX_KBUF) >= (int) sizeof(kbuf)) { 1196 result->nerror++; 1197 break; 1198 } 1199 ch = UChar(kbuf[grabbed]); 1200 kbuf[grabbed + 1] = 0; 1201 switch (ch) { 1202 case '0': 1203 case '1': 1204 case '2': 1205 case '3': 1206 case '4': 1207 case '5': 1208 case '6': 1209 case '7': 1210 case '8': 1211 case '9': 1212 if (marker) { 1213 ++now; 1214 result->nparam = (now + 1); 1215 } 1216 marker = 0; 1217 result->params[now] = (result->params[now] * 10) + (ch - '0'); 1218 break; 1219 case ';': 1220 if (marker) { 1221 ++now; 1222 result->nparam = (now + 1); 1223 } 1224 marker = 1; 1225 break; 1226 default: 1227 if (ch < 32 || ch > 126) { 1228 /* 1229 * Technically other characters could be interspersed in the 1230 * response. Ignore those for now. 1231 */ 1232 result->nerror++; 1233 continue; 1234 } else if (isFinal(ch)) { 1235 if (marker) { 1236 result->nparam++; 1237 } 1238 result->final = ch; 1239 } else { 1240 result->nerror++; 1241 } 1242 break; 1243 } 1244 ++grabbed; 1245 } while (!isFinal(ch)); 1246 #if USE_PTHREADS_EINTR 1247 _nc_globals.read_thread = 0; 1248 #endif 1249 1250 kbuf[++grabbed] = 0; 1251 TR(TRACE_IEVENT, 1252 ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf)); 1253 return (grabbed > 0) && (result->nerror == 0); 1254 } 1255 1256 static bool 1257 decode_xterm_SGR1006(SCREEN *sp, MEVENT * eventp) 1258 { 1259 SGR_DATA data; 1260 bool result = FALSE; 1261 if (read_SGR(sp, &data)) { 1262 int b = data.params[0]; 1263 int b3 = 1 + (b & 3); 1264 1265 eventp->id = NORMAL_EVENT; 1266 if (data.final == 'M') { 1267 (void) handle_wheel(sp, eventp, b, (b & 64) == 64); 1268 } else { 1269 mmask_t pressed = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_PRESSED); 1270 mmask_t release = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_RELEASED); 1271 if (sp->_mouse_bstate & pressed) { 1272 eventp->bstate = release; 1273 sp->_mouse_bstate &= ~pressed; 1274 } else { 1275 eventp->bstate = REPORT_MOUSE_POSITION; 1276 } 1277 } 1278 if (b & 4) { 1279 eventp->bstate |= BUTTON_SHIFT; 1280 } 1281 if (b & 8) { 1282 eventp->bstate |= BUTTON_ALT; 1283 } 1284 if (b & 16) { 1285 eventp->bstate |= BUTTON_CTRL; 1286 } 1287 result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE; 1288 eventp->x = (data.params[1] ? (data.params[1] - 1) : 0); 1289 eventp->y = (data.params[2] ? (data.params[2] - 1) : 0); 1290 } 1291 return result; 1292 } 1293 1294 static bool 1295 _nc_mouse_inline(SCREEN *sp) 1296 /* mouse report received in the keyboard stream -- parse its info */ 1297 { 1298 bool result = FALSE; 1299 MEVENT *eventp = sp->_mouse_eventp; 1300 1301 TR(MY_TRACE, ("_nc_mouse_inline() called")); 1302 1303 if (sp->_mouse_type == M_XTERM) { 1304 switch (sp->_mouse_format) { 1305 case MF_X10: 1306 result = decode_xterm_X10(sp, eventp); 1307 break; 1308 case MF_SGR1006: 1309 result = decode_xterm_SGR1006(sp, eventp); 1310 break; 1311 #ifdef EXP_XTERM_1005 1312 case MF_XTERM_1005: 1313 result = decode_xterm_1005(sp, eventp); 1314 break; 1315 #endif 1316 } 1317 1318 TR(MY_TRACE, 1319 ("_nc_mouse_inline: primitive mouse-event %s has slot %ld", 1320 _nc_tracemouse(sp, eventp), 1321 (long) IndexEV(sp, eventp))); 1322 1323 /* bump the next-free pointer into the circular list */ 1324 sp->_mouse_eventp = NEXT(eventp); 1325 1326 if (!result) { 1327 /* If this event is from a wheel-mouse, treat it like position 1328 * reports and avoid waiting for the release-events which will 1329 * never come. 1330 */ 1331 if (eventp->bstate & BUTTON_PRESSED) { 1332 int b; 1333 1334 for (b = 4; b <= MAX_BUTTONS; ++b) { 1335 if ((eventp->bstate & MASK_PRESS(b))) { 1336 result = TRUE; 1337 break; 1338 } 1339 } 1340 } 1341 } 1342 } 1343 1344 return (result); 1345 } 1346 1347 static void 1348 mouse_activate(SCREEN *sp, int on) 1349 { 1350 if (!on && !sp->_mouse_initialized) 1351 return; 1352 1353 if (!_nc_mouse_init(sp)) 1354 return; 1355 1356 if (on) { 1357 sp->_mouse_bstate = 0; 1358 switch (sp->_mouse_type) { 1359 case M_XTERM: 1360 #if NCURSES_EXT_FUNCS 1361 NCURSES_SP_NAME(keyok) (NCURSES_SP_ARGx KEY_MOUSE, on); 1362 #endif 1363 TPUTS_TRACE("xterm mouse initialization"); 1364 enable_xterm_mouse(sp, 1); 1365 break; 1366 #if USE_GPM_SUPPORT 1367 case M_GPM: 1368 if (enable_gpm_mouse(sp, TRUE)) { 1369 sp->_mouse_fd = *(my_gpm_fd); 1370 T(("GPM mouse_fd %d", sp->_mouse_fd)); 1371 } 1372 break; 1373 #endif 1374 #if USE_SYSMOUSE 1375 case M_SYSMOUSE: 1376 signal(SIGUSR2, handle_sysmouse); 1377 sp->_mouse_active = TRUE; 1378 break; 1379 #endif 1380 #ifdef USE_TERM_DRIVER 1381 case M_TERM_DRIVER: 1382 sp->_mouse_active = TRUE; 1383 break; 1384 #endif 1385 case M_NONE: 1386 return; 1387 } 1388 /* Make runtime binding to cut down on object size of applications that 1389 * do not use the mouse (e.g., 'clear'). 1390 */ 1391 sp->_mouse_event = _nc_mouse_event; 1392 sp->_mouse_inline = _nc_mouse_inline; 1393 sp->_mouse_parse = _nc_mouse_parse; 1394 sp->_mouse_resume = _nc_mouse_resume; 1395 sp->_mouse_wrap = _nc_mouse_wrap; 1396 } else { 1397 1398 switch (sp->_mouse_type) { 1399 case M_XTERM: 1400 TPUTS_TRACE("xterm mouse deinitialization"); 1401 enable_xterm_mouse(sp, 0); 1402 break; 1403 #if USE_GPM_SUPPORT 1404 case M_GPM: 1405 enable_gpm_mouse(sp, FALSE); 1406 break; 1407 #endif 1408 #if USE_SYSMOUSE 1409 case M_SYSMOUSE: 1410 signal(SIGUSR2, SIG_IGN); 1411 sp->_mouse_active = FALSE; 1412 break; 1413 #endif 1414 #ifdef USE_TERM_DRIVER 1415 case M_TERM_DRIVER: 1416 sp->_mouse_active = FALSE; 1417 break; 1418 #endif 1419 case M_NONE: 1420 return; 1421 } 1422 } 1423 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG); 1424 } 1425 1426 /************************************************************************** 1427 * 1428 * Device-independent code 1429 * 1430 **************************************************************************/ 1431 1432 static bool 1433 _nc_mouse_parse(SCREEN *sp, int runcount) 1434 /* parse a run of atomic mouse events into a gesture */ 1435 { 1436 MEVENT *eventp = sp->_mouse_eventp; 1437 MEVENT *next, *ep; 1438 MEVENT *first_valid = NULL; 1439 MEVENT *first_invalid = NULL; 1440 int n; 1441 int b; 1442 bool merge; 1443 bool endLoop; 1444 1445 TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount)); 1446 1447 /* 1448 * When we enter this routine, the event list next-free pointer 1449 * points just past a run of mouse events that we know were separated 1450 * in time by less than the critical click interval. The job of this 1451 * routine is to collapse this run into a single higher-level event 1452 * or gesture. 1453 * 1454 * We accomplish this in two passes. The first pass merges press/release 1455 * pairs into click events. The second merges runs of click events into 1456 * double or triple-click events. 1457 * 1458 * It's possible that the run may not resolve to a single event (for 1459 * example, if the user quadruple-clicks). If so, leading events 1460 * in the run are ignored if user does not call getmouse in a loop (getting 1461 * them from newest to older). 1462 * 1463 * Note that this routine is independent of the format of the specific 1464 * format of the pointing-device's reports. We can use it to parse 1465 * gestures on anything that reports press/release events on a per- 1466 * button basis, as long as the device-dependent mouse code puts stuff 1467 * on the queue in MEVENT format. 1468 */ 1469 1470 /* 1471 * Reset all events that were not set, in case the user sometimes calls 1472 * getmouse only once and other times until there are no more events in 1473 * queue. 1474 * 1475 * This also allows reaching the beginning of the run. 1476 */ 1477 ep = eventp; 1478 for (n = runcount; n < EV_MAX; n++) { 1479 Invalidate(ep); 1480 ep = NEXT(ep); 1481 } 1482 1483 #ifdef TRACE 1484 if (USE_TRACEF(TRACE_IEVENT)) { 1485 _trace_slot(sp, "before mouse press/release merge:"); 1486 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1487 RunParams(sp, eventp, ep), 1488 runcount); 1489 _nc_unlock_global(tracef); 1490 } 1491 #endif /* TRACE */ 1492 1493 /* first pass; merge press/release pairs */ 1494 endLoop = FALSE; 1495 while (!endLoop) { 1496 next = NEXT(ep); 1497 if (next == eventp) { 1498 /* Will end the loop, but compact before */ 1499 endLoop = TRUE; 1500 } else { 1501 1502 #define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \ 1503 == !(next->bstate & MASK_RELEASE(x))) 1504 1505 if (ValidEvent(ep) && ValidEvent(next) 1506 && ep->x == next->x && ep->y == next->y 1507 && (ep->bstate & BUTTON_PRESSED) 1508 && (!(next->bstate & BUTTON_PRESSED))) { 1509 bool changed = TRUE; 1510 1511 for (b = 1; b <= MAX_BUTTONS; ++b) { 1512 if (!MASK_CHANGED(b)) { 1513 changed = FALSE; 1514 break; 1515 } 1516 } 1517 1518 if (changed) { 1519 merge = FALSE; 1520 for (b = 1; b <= MAX_BUTTONS; ++b) { 1521 if ((sp->_mouse_mask & MASK_CLICK(b)) 1522 && (ep->bstate & MASK_PRESS(b))) { 1523 next->bstate &= ~MASK_RELEASE(b); 1524 next->bstate |= MASK_CLICK(b); 1525 merge = TRUE; 1526 } 1527 } 1528 if (merge) { 1529 Invalidate(ep); 1530 } 1531 } 1532 } 1533 } 1534 1535 /* Compact valid events */ 1536 if (!ValidEvent(ep)) { 1537 if ((first_valid != NULL) && (first_invalid == NULL)) { 1538 first_invalid = ep; 1539 } 1540 } else { 1541 if (first_valid == NULL) { 1542 first_valid = ep; 1543 } else if (first_invalid != NULL) { 1544 *first_invalid = *ep; 1545 Invalidate(ep); 1546 first_invalid = NEXT(first_invalid); 1547 } 1548 } 1549 1550 ep = next; 1551 } 1552 1553 if (first_invalid != NULL) { 1554 eventp = first_invalid; 1555 } 1556 #ifdef TRACE 1557 if (USE_TRACEF(TRACE_IEVENT)) { 1558 _trace_slot(sp, "before mouse click merge:"); 1559 if (first_valid == NULL) { 1560 _tracef("_nc_mouse_parse: no valid event"); 1561 } else { 1562 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1563 RunParams(sp, eventp, first_valid), 1564 runcount); 1565 _nc_unlock_global(tracef); 1566 } 1567 } 1568 #endif /* TRACE */ 1569 1570 /* 1571 * Second pass; merge click runs. We merge click events forward in the 1572 * queue. For example, double click can be changed to triple click. 1573 * 1574 * NOTE: There is a problem with this design! If the application 1575 * allows enough click events to pile up in the circular queue so 1576 * they wrap around, it will cheerfully merge the newest forward 1577 * into the oldest, creating a bogus doubleclick and confusing 1578 * the queue-traversal logic rather badly. Generally this won't 1579 * happen, because calling getmouse() marks old events invalid and 1580 * ineligible for merges. The true solution to this problem would 1581 * be to timestamp each MEVENT and perform the obvious sanity check, 1582 * but the timer element would have to have sub-second resolution, 1583 * which would get us into portability trouble. 1584 */ 1585 first_invalid = NULL; 1586 endLoop = (first_valid == NULL); 1587 ep = first_valid; 1588 while (!endLoop) { 1589 next = NEXT(ep); 1590 1591 if (next == eventp) { 1592 /* Will end the loop, but check event type and compact before */ 1593 endLoop = TRUE; 1594 } else if (!ValidEvent(next)) { 1595 continue; 1596 } else { 1597 /* merge click events forward */ 1598 if ((ep->bstate & BUTTON_CLICKED) 1599 && (next->bstate & BUTTON_CLICKED)) { 1600 merge = FALSE; 1601 for (b = 1; b <= MAX_BUTTONS; ++b) { 1602 if ((sp->_mouse_mask & MASK_DOUBLE_CLICK(b)) 1603 && (ep->bstate & MASK_CLICK(b)) 1604 && (next->bstate & MASK_CLICK(b))) { 1605 next->bstate &= ~MASK_CLICK(b); 1606 next->bstate |= MASK_DOUBLE_CLICK(b); 1607 merge = TRUE; 1608 } 1609 } 1610 if (merge) { 1611 Invalidate(ep); 1612 } 1613 } 1614 1615 /* merge double-click events forward */ 1616 if ((ep->bstate & BUTTON_DOUBLE_CLICKED) 1617 && (next->bstate & BUTTON_CLICKED)) { 1618 merge = FALSE; 1619 for (b = 1; b <= MAX_BUTTONS; ++b) { 1620 if ((sp->_mouse_mask & MASK_TRIPLE_CLICK(b)) 1621 && (ep->bstate & MASK_DOUBLE_CLICK(b)) 1622 && (next->bstate & MASK_CLICK(b))) { 1623 next->bstate &= ~MASK_CLICK(b); 1624 next->bstate |= MASK_TRIPLE_CLICK(b); 1625 merge = TRUE; 1626 } 1627 } 1628 if (merge) { 1629 Invalidate(ep); 1630 } 1631 } 1632 } 1633 1634 /* Discard event if it does not match event mask */ 1635 if (!(ep->bstate & sp->_mouse_mask2)) { 1636 Invalidate(ep); 1637 } 1638 1639 /* Compact valid events */ 1640 if (!ValidEvent(ep)) { 1641 if (ep == first_valid) { 1642 first_valid = next; 1643 } else if (first_invalid == NULL) { 1644 first_invalid = ep; 1645 } 1646 } else if (first_invalid != NULL) { 1647 *first_invalid = *ep; 1648 Invalidate(ep); 1649 first_invalid = NEXT(first_invalid); 1650 } 1651 1652 ep = next; 1653 } 1654 1655 if (first_invalid == NULL) { 1656 first_invalid = eventp; 1657 } 1658 sp->_mouse_eventp = first_invalid; 1659 1660 #ifdef TRACE 1661 if (first_valid != NULL) { 1662 if (USE_TRACEF(TRACE_IEVENT)) { 1663 _trace_slot(sp, "after mouse event queue compaction:"); 1664 _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1665 RunParams(sp, first_invalid, first_valid), 1666 runcount); 1667 _nc_unlock_global(tracef); 1668 } 1669 for (ep = first_valid; ep != first_invalid; ep = NEXT(ep)) { 1670 if (ValidEvent(ep)) 1671 TR(MY_TRACE, 1672 ("_nc_mouse_parse: returning composite mouse event %s at slot %ld", 1673 _nc_tracemouse(sp, ep), 1674 (long) IndexEV(sp, ep))); 1675 } 1676 } 1677 #endif /* TRACE */ 1678 1679 /* after all this, do we have a valid event? */ 1680 return ValidEvent(PREV(first_invalid)); 1681 } 1682 1683 static void 1684 _nc_mouse_wrap(SCREEN *sp) 1685 /* release mouse -- called by endwin() before shellout/exit */ 1686 { 1687 TR(MY_TRACE, ("_nc_mouse_wrap() called")); 1688 1689 switch (sp->_mouse_type) { 1690 case M_XTERM: 1691 if (sp->_mouse_mask) 1692 mouse_activate(sp, FALSE); 1693 break; 1694 #if USE_GPM_SUPPORT 1695 /* GPM: pass all mouse events to next client */ 1696 case M_GPM: 1697 if (sp->_mouse_mask) 1698 mouse_activate(sp, FALSE); 1699 break; 1700 #endif 1701 #if USE_SYSMOUSE 1702 case M_SYSMOUSE: 1703 mouse_activate(sp, FALSE); 1704 break; 1705 #endif 1706 #ifdef USE_TERM_DRIVER 1707 case M_TERM_DRIVER: 1708 mouse_activate(sp, FALSE); 1709 break; 1710 #endif 1711 case M_NONE: 1712 break; 1713 } 1714 } 1715 1716 static void 1717 _nc_mouse_resume(SCREEN *sp) 1718 /* re-connect to mouse -- called by doupdate() after shellout */ 1719 { 1720 TR(MY_TRACE, ("_nc_mouse_resume() called")); 1721 1722 switch (sp->_mouse_type) { 1723 case M_XTERM: 1724 /* xterm: re-enable reporting */ 1725 if (sp->_mouse_mask) 1726 mouse_activate(sp, TRUE); 1727 break; 1728 1729 #if USE_GPM_SUPPORT 1730 case M_GPM: 1731 /* GPM: reclaim our event set */ 1732 if (sp->_mouse_mask) 1733 mouse_activate(sp, TRUE); 1734 break; 1735 #endif 1736 1737 #if USE_SYSMOUSE 1738 case M_SYSMOUSE: 1739 mouse_activate(sp, TRUE); 1740 break; 1741 #endif 1742 1743 #ifdef USE_TERM_DRIVER 1744 case M_TERM_DRIVER: 1745 mouse_activate(sp, TRUE); 1746 break; 1747 #endif 1748 1749 case M_NONE: 1750 break; 1751 } 1752 } 1753 1754 /************************************************************************** 1755 * 1756 * Mouse interface entry points for the API 1757 * 1758 **************************************************************************/ 1759 1760 NCURSES_EXPORT(int) 1761 NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent) 1762 { 1763 int result = ERR; 1764 MEVENT *eventp; 1765 1766 T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent)); 1767 1768 if ((aevent != 0) && 1769 (SP_PARM != 0) && 1770 (SP_PARM->_mouse_type != M_NONE) && 1771 (eventp = SP_PARM->_mouse_eventp) != 0) { 1772 /* compute the current-event pointer */ 1773 MEVENT *prev = PREV(eventp); 1774 1775 /* 1776 * Discard events not matching mask (there could be still some if 1777 * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns 1778 * false). 1779 */ 1780 while (ValidEvent(prev) && (!(prev->bstate & SP_PARM->_mouse_mask2))) { 1781 Invalidate(prev); 1782 prev = PREV(prev); 1783 } 1784 if (ValidEvent(prev)) { 1785 /* copy the event we find there */ 1786 *aevent = *prev; 1787 1788 TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld", 1789 _nc_tracemouse(SP_PARM, prev), 1790 (long) IndexEV(SP_PARM, prev))); 1791 1792 Invalidate(prev); /* so the queue slot becomes free */ 1793 SP_PARM->_mouse_eventp = prev; 1794 result = OK; 1795 } else { 1796 /* Reset the provided event */ 1797 aevent->bstate = 0; 1798 Invalidate(aevent); 1799 aevent->x = 0; 1800 aevent->y = 0; 1801 aevent->z = 0; 1802 } 1803 } 1804 returnCode(result); 1805 } 1806 1807 #if NCURSES_SP_FUNCS 1808 /* grab a copy of the current mouse event */ 1809 NCURSES_EXPORT(int) 1810 getmouse(MEVENT * aevent) 1811 { 1812 return NCURSES_SP_NAME(getmouse) (CURRENT_SCREEN, aevent); 1813 } 1814 #endif 1815 1816 NCURSES_EXPORT(int) 1817 NCURSES_SP_NAME(ungetmouse) (NCURSES_SP_DCLx MEVENT * aevent) 1818 { 1819 int result = ERR; 1820 MEVENT *eventp; 1821 1822 T((T_CALLED("ungetmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent)); 1823 1824 if (aevent != 0 && 1825 SP_PARM != 0 && 1826 (eventp = SP_PARM->_mouse_eventp) != 0) { 1827 1828 /* stick the given event in the next-free slot */ 1829 *eventp = *aevent; 1830 1831 /* bump the next-free pointer into the circular list */ 1832 SP_PARM->_mouse_eventp = NEXT(eventp); 1833 1834 /* push back the notification event on the keyboard queue */ 1835 result = NCURSES_SP_NAME(ungetch) (NCURSES_SP_ARGx KEY_MOUSE); 1836 } 1837 returnCode(result); 1838 } 1839 1840 #if NCURSES_SP_FUNCS 1841 /* enqueue a synthesized mouse event to be seen by the next wgetch() */ 1842 NCURSES_EXPORT(int) 1843 ungetmouse(MEVENT * aevent) 1844 { 1845 return NCURSES_SP_NAME(ungetmouse) (CURRENT_SCREEN, aevent); 1846 } 1847 #endif 1848 1849 NCURSES_EXPORT(mmask_t) 1850 NCURSES_SP_NAME(mousemask) (NCURSES_SP_DCLx mmask_t newmask, mmask_t * oldmask) 1851 /* set the mouse event mask */ 1852 { 1853 mmask_t result = 0; 1854 1855 T((T_CALLED("mousemask(%p,%#lx,%p)"), 1856 (void *) SP_PARM, 1857 (unsigned long) newmask, 1858 (void *) oldmask)); 1859 1860 if (SP_PARM != 0) { 1861 if (oldmask) 1862 *oldmask = SP_PARM->_mouse_mask; 1863 1864 if (newmask || SP_PARM->_mouse_initialized) { 1865 _nc_mouse_init(SP_PARM); 1866 1867 if (SP_PARM->_mouse_type != M_NONE) { 1868 int b; 1869 1870 result = newmask & 1871 (REPORT_MOUSE_POSITION 1872 | BUTTON_ALT 1873 | BUTTON_CTRL 1874 | BUTTON_SHIFT 1875 | BUTTON_PRESSED 1876 | BUTTON_RELEASED 1877 | BUTTON_CLICKED 1878 | BUTTON_DOUBLE_CLICKED 1879 | BUTTON_TRIPLE_CLICKED); 1880 1881 mouse_activate(SP_PARM, (bool) (result != 0)); 1882 1883 SP_PARM->_mouse_mask = result; 1884 SP_PARM->_mouse_mask2 = result; 1885 1886 /* 1887 * Make a mask corresponding to the states we will need to 1888 * retain (temporarily) while building up the state that the 1889 * user asked for. 1890 */ 1891 for (b = 1; b <= MAX_BUTTONS; ++b) { 1892 if (SP_PARM->_mouse_mask2 & MASK_TRIPLE_CLICK(b)) 1893 SP_PARM->_mouse_mask2 |= MASK_DOUBLE_CLICK(b); 1894 if (SP_PARM->_mouse_mask2 & MASK_DOUBLE_CLICK(b)) 1895 SP_PARM->_mouse_mask2 |= MASK_CLICK(b); 1896 if (SP_PARM->_mouse_mask2 & MASK_CLICK(b)) 1897 SP_PARM->_mouse_mask2 |= (MASK_PRESS(b) | 1898 MASK_RELEASE(b)); 1899 } 1900 } 1901 } 1902 } 1903 returnMMask(result); 1904 } 1905 1906 #if NCURSES_SP_FUNCS 1907 NCURSES_EXPORT(mmask_t) 1908 mousemask(mmask_t newmask, mmask_t * oldmask) 1909 { 1910 return NCURSES_SP_NAME(mousemask) (CURRENT_SCREEN, newmask, oldmask); 1911 } 1912 #endif 1913 1914 NCURSES_EXPORT(bool) 1915 wenclose(const WINDOW *win, int y, int x) 1916 /* check to see if given window encloses given screen location */ 1917 { 1918 bool result = FALSE; 1919 1920 T((T_CALLED("wenclose(%p,%d,%d)"), (const void *) win, y, x)); 1921 1922 if (win != 0) { 1923 y -= win->_yoffset; 1924 result = ((win->_begy <= y && 1925 win->_begx <= x && 1926 (win->_begx + win->_maxx) >= x && 1927 (win->_begy + win->_maxy) >= y) ? TRUE : FALSE); 1928 } 1929 returnBool(result); 1930 } 1931 1932 NCURSES_EXPORT(int) 1933 NCURSES_SP_NAME(mouseinterval) (NCURSES_SP_DCLx int maxclick) 1934 /* set the maximum mouse interval within which to recognize a click */ 1935 { 1936 int oldval; 1937 1938 T((T_CALLED("mouseinterval(%p,%d)"), (void *) SP_PARM, maxclick)); 1939 1940 if (SP_PARM != 0) { 1941 oldval = SP_PARM->_maxclick; 1942 if (maxclick >= 0) 1943 SP_PARM->_maxclick = maxclick; 1944 } else { 1945 oldval = DEFAULT_MAXCLICK; 1946 } 1947 1948 returnCode(oldval); 1949 } 1950 1951 #if NCURSES_SP_FUNCS 1952 NCURSES_EXPORT(int) 1953 mouseinterval(int maxclick) 1954 { 1955 return NCURSES_SP_NAME(mouseinterval) (CURRENT_SCREEN, maxclick); 1956 } 1957 #endif 1958 1959 /* This may be used by other routines to ask for the existence of mouse 1960 support */ 1961 NCURSES_EXPORT(bool) 1962 _nc_has_mouse(SCREEN *sp) 1963 { 1964 return (((0 == sp) || (sp->_mouse_type == M_NONE)) ? FALSE : TRUE); 1965 } 1966 1967 NCURSES_EXPORT(bool) 1968 NCURSES_SP_NAME(has_mouse) (NCURSES_SP_DCL0) 1969 { 1970 return _nc_has_mouse(SP_PARM); 1971 } 1972 1973 #if NCURSES_SP_FUNCS 1974 NCURSES_EXPORT(bool) 1975 has_mouse(void) 1976 { 1977 return _nc_has_mouse(CURRENT_SCREEN); 1978 } 1979 #endif 1980 1981 NCURSES_EXPORT(bool) 1982 wmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen) 1983 { 1984 bool result = FALSE; 1985 1986 T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"), 1987 (const void *) win, 1988 (void *) pY, 1989 (void *) pX, 1990 to_screen)); 1991 1992 if (win && pY && pX) { 1993 int y = *pY; 1994 int x = *pX; 1995 1996 if (to_screen) { 1997 y += win->_begy + win->_yoffset; 1998 x += win->_begx; 1999 if (wenclose(win, y, x)) 2000 result = TRUE; 2001 } else { 2002 if (wenclose(win, y, x)) { 2003 y -= (win->_begy + win->_yoffset); 2004 x -= win->_begx; 2005 result = TRUE; 2006 } 2007 } 2008 if (result) { 2009 *pX = x; 2010 *pY = y; 2011 } 2012 } 2013 returnBool(result); 2014 } 2015