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