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