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