150276Speter/**************************************************************************** 2184989Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32166124Srafan * and: Thomas E. Dickey 1996-on * 3350276Speter ****************************************************************************/ 3450276Speter 3550276Speter/* 3650276Speter * This module is intended to encapsulate ncurses's interface to pointing 3750276Speter * devices. 3850276Speter * 39166124Srafan * The primary method used is xterm's internal mouse-tracking facility. 40166124Srafan * Additional methods depend on the platform: 41166124Srafan * Alessandro Rubini's GPM server (Linux) 42166124Srafan * sysmouse (FreeBSD) 43166124Srafan * special-purpose mouse interface for OS/2 EMX. 4450276Speter * 4550276Speter * Notes for implementors of new mouse-interface methods: 4650276Speter * 4750276Speter * The code is logically split into a lower level that accepts event reports 4850276Speter * in a device-dependent format and an upper level that parses mouse gestures 4950276Speter * and filters events. The mediating data structure is a circular queue of 5050276Speter * MEVENT structures. 5150276Speter * 5250276Speter * Functionally, the lower level's job is to pick up primitive events and 5350276Speter * put them on the circular queue. This can happen in one of two ways: 5450276Speter * either (a) _nc_mouse_event() detects a series of incoming mouse reports 5550276Speter * and queues them, or (b) code in lib_getch.c detects the kmous prefix in 5650276Speter * the keyboard input stream and calls _nc_mouse_inline to queue up a series 5750276Speter * of adjacent mouse reports. 5850276Speter * 5950276Speter * In either case, _nc_mouse_parse() should be called after the series is 6050276Speter * accepted to parse the digested mouse reports (low-level MEVENTs) into 6150276Speter * a gesture (a high-level or composite MEVENT). 6250276Speter * 6350276Speter * Don't be too shy about adding new event types or modifiers, if you can find 6450276Speter * room for them in the 32-bit mask. The API is written so that users get 6550276Speter * feedback on which theoretical event types they won't see when they call 6650276Speter * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being 6750276Speter * used yet, and a couple of bits open at the high end. 6850276Speter */ 6950276Speter 7050276Speter#ifdef __EMX__ 7162449Speter# include <io.h> 7250276Speter# define INCL_DOS 7350276Speter# define INCL_VIO 7450276Speter# define INCL_KBD 7550276Speter# define INCL_MOU 7650276Speter# define INCL_DOSPROCESS 7750276Speter# include <os2.h> /* Need to include before the others */ 7850276Speter#endif 7950276Speter 8050276Speter#include <curses.priv.h> 81166124Srafan 82184989SrafanMODULE_ID("$Id: lib_mouse.c,v 1.102 2008/10/18 21:48:55 tom Exp $") 83166124Srafan 8450276Speter#include <term.h> 85166124Srafan#include <tic.h> 8650276Speter 8750276Speter#if USE_GPM_SUPPORT 8850276Speter#include <linux/keyboard.h> /* defines KG_* macros */ 89166124Srafan 90166124Srafan#ifdef HAVE_LIBDL 91166124Srafan/* use dynamic loader to avoid linkage dependency */ 92166124Srafan#include <dlfcn.h> 93166124Srafan 94166124Srafan#ifdef RTLD_NOW 95166124Srafan#define my_RTLD RTLD_NOW 96166124Srafan#else 97166124Srafan#ifdef RTLD_LAZY 98166124Srafan#define my_RTLD RTLD_LAZY 99166124Srafan#else 100166124Srafanmake an error 10150276Speter#endif 102166124Srafan#endif /* RTLD_NOW */ 103166124Srafan#endif /* HAVE_LIBDL */ 104166124Srafan 105166124Srafan#endif /* USE_GPM_SUPPORT */ 106166124Srafan 107166124Srafan#if USE_SYSMOUSE 108166124Srafan#undef buttons /* symbol conflict in consio.h */ 109166124Srafan#undef mouse_info /* symbol conflict in consio.h */ 110166124Srafan#include <osreldate.h> 111166124Srafan#if (__FreeBSD_version >= 400017) 112166124Srafan#include <sys/consio.h> 113166124Srafan#include <sys/fbio.h> 114166124Srafan#else 115166124Srafan#include <machine/console.h> 11650276Speter#endif 117166124Srafan#endif /* use_SYSMOUSE */ 11850276Speter 11950276Speter#define MY_TRACE TRACE_ICALLS|TRACE_IEVENT 12050276Speter 121166124Srafan#define MASK_RELEASE(x) NCURSES_MOUSE_MASK(x, 001) 122166124Srafan#define MASK_PRESS(x) NCURSES_MOUSE_MASK(x, 002) 123166124Srafan#define MASK_CLICK(x) NCURSES_MOUSE_MASK(x, 004) 124166124Srafan#define MASK_DOUBLE_CLICK(x) NCURSES_MOUSE_MASK(x, 010) 125166124Srafan#define MASK_TRIPLE_CLICK(x) NCURSES_MOUSE_MASK(x, 020) 126166124Srafan#define MASK_RESERVED_EVENT(x) NCURSES_MOUSE_MASK(x, 040) 127166124Srafan 128166124Srafan#if NCURSES_MOUSE_VERSION == 1 129166124Srafan#define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED) 130166124Srafan#define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED) 131166124Srafan#define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED) 132166124Srafan#define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED) 133166124Srafan#define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED) 134166124Srafan#define MAX_BUTTONS 4 135166124Srafan#else 136166124Srafan#define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED | BUTTON5_CLICKED) 137166124Srafan#define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED | BUTTON5_PRESSED) 138166124Srafan#define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED | BUTTON5_RELEASED) 139166124Srafan#define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED) 140166124Srafan#define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED) 141166124Srafan#define MAX_BUTTONS 5 142166124Srafan#endif 143166124Srafan 14450276Speter#define INVALID_EVENT -1 145166124Srafan#define NORMAL_EVENT 0 14650276Speter 147166124Srafan#if USE_GPM_SUPPORT 14850276Speter 149166124Srafan#ifndef LIBGPM_SONAME 150166124Srafan#define LIBGPM_SONAME "libgpm.so" 15150276Speter#endif 15250276Speter 153184989Srafan#define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(SP->_dlopen_gpm, #name)) 15450276Speter 155166124Srafan#endif /* USE_GPM_SUPPORT */ 156166124Srafan 157184989Srafanstatic bool _nc_mouse_parse(SCREEN *, int); 15850276Speterstatic void _nc_mouse_resume(SCREEN *); 15950276Speterstatic void _nc_mouse_wrap(SCREEN *); 16050276Speter 16150276Speter/* maintain a circular list of mouse events */ 16250276Speter 163184989Srafan#define FirstEV(sp) ((sp)->_mouse_events) 164184989Srafan#define LastEV(sp) ((sp)->_mouse_events + EV_MAX - 1) 165184989Srafan 166166124Srafan#undef NEXT 167184989Srafan#define NEXT(ep) ((ep >= LastEV(sp)) \ 168184989Srafan ? FirstEV(sp) \ 169166124Srafan : ep + 1) 17050276Speter 171166124Srafan#undef PREV 172184989Srafan#define PREV(ep) ((ep <= FirstEV(sp)) \ 173184989Srafan ? LastEV(sp) \ 174166124Srafan : ep - 1) 175166124Srafan 176184989Srafan#define IndexEV(sp, ep) (ep - FirstEV(sp)) 177184989Srafan 178184989Srafan#define RunParams(sp, eventp, runp) \ 179184989Srafan (long) IndexEV(sp, runp), \ 180184989Srafan (long) (IndexEV(sp, eventp) + (EV_MAX - 1)) % EV_MAX 181184989Srafan 18250276Speter#ifdef TRACE 18362449Speterstatic void 184184989Srafan_trace_slot(SCREEN *sp, const char *tag) 18550276Speter{ 18662449Speter MEVENT *ep; 18750276Speter 18862449Speter _tracef(tag); 18950276Speter 190184989Srafan for (ep = FirstEV(sp); ep <= LastEV(sp); ep++) 19162449Speter _tracef("mouse event queue slot %ld = %s", 192184989Srafan (long) IndexEV(sp, ep), 193184989Srafan _nc_tracemouse(sp, ep)); 19450276Speter} 19550276Speter#endif 19650276Speter 197166124Srafan#if USE_EMX_MOUSE 19850276Speter 19950276Speter# define TOP_ROW 0 20050276Speter# define LEFT_COL 0 20150276Speter 20250276Speter# define M_FD(sp) sp->_mouse_fd 20350276Speter 20450276Speterstatic void 205184989Srafanwrite_event(SCREEN *sp, int down, int button, int x, int y) 20650276Speter{ 20750276Speter char buf[6]; 20850276Speter unsigned long ignore; 20950276Speter 21066963Speter strncpy(buf, key_mouse, 3); /* should be "\033[M" */ 21150276Speter buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40); 21250276Speter buf[4] = ' ' + x - LEFT_COL + 1; 21350276Speter buf[5] = ' ' + y - TOP_ROW + 1; 214184989Srafan DosWrite(sp->_emxmouse_wfd, buf, 6, &ignore); 21550276Speter} 21650276Speter 21750276Speterstatic void 218184989Srafanmouse_server(unsigned long param) 21950276Speter{ 220184989Srafan SCREEN *sp = (SCREEN *) param; 22150276Speter unsigned short fWait = MOU_WAIT; 22250276Speter /* NOPTRRECT mourt = { 0,0,24,79 }; */ 22350276Speter MOUEVENTINFO mouev; 22450276Speter HMOU hmou; 22550276Speter unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN; 22666963Speter int nbuttons = 3; 22750276Speter int oldstate = 0; 22866963Speter char err[80]; 22966963Speter unsigned long rc; 23050276Speter 23150276Speter /* open the handle for the mouse */ 23262449Speter if (MouOpen(NULL, &hmou) == 0) { 23366963Speter rc = MouSetEventMask(&mask, hmou); 23466963Speter if (rc) { /* retry with 2 buttons */ 23566963Speter mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN; 23666963Speter rc = MouSetEventMask(&mask, hmou); 23766963Speter nbuttons = 2; 23866963Speter } 23966963Speter if (rc == 0 && MouDrawPtr(hmou) == 0) { 24050276Speter for (;;) { 24150276Speter /* sit and wait on the event queue */ 24266963Speter rc = MouReadEventQue(&mouev, &fWait, hmou); 24366963Speter if (rc) { 24466963Speter sprintf(err, "Error reading mouse queue, rc=%lu.\r\n", rc); 24562449Speter break; 24666963Speter } 247184989Srafan if (!sp->_emxmouse_activated) 24850276Speter goto finish; 24950276Speter 25050276Speter /* 25150276Speter * OS/2 numbers a 3-button mouse inconsistently from other 25250276Speter * platforms: 25362449Speter * 1 = left 25462449Speter * 2 = right 25562449Speter * 3 = middle. 25650276Speter */ 25750276Speter if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN) 258184989Srafan write_event(sp, mouev.fs & MOUSE_BN1_DOWN, 259184989Srafan sp->_emxmouse_buttons[1], mouev.col, mouev.row); 26050276Speter if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN) 261184989Srafan write_event(sp, mouev.fs & MOUSE_BN2_DOWN, 262184989Srafan sp->_emxmouse_buttons[3], mouev.col, mouev.row); 26350276Speter if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN) 264184989Srafan write_event(sp, mouev.fs & MOUSE_BN3_DOWN, 265184989Srafan sp->_emxmouse_buttons[2], mouev.col, mouev.row); 26650276Speter 26750276Speter finish: 26850276Speter oldstate = mouev.fs; 26950276Speter } 27066963Speter } else 27166963Speter sprintf(err, "Error setting event mask, buttons=%d, rc=%lu.\r\n", 27266963Speter nbuttons, rc); 27350276Speter 27466963Speter DosWrite(2, err, strlen(err), &rc); 27550276Speter MouClose(hmou); 27650276Speter } 27762449Speter DosExit(EXIT_THREAD, 0L); 27850276Speter} 27966963Speter 280166124Srafan#endif /* USE_EMX_MOUSE */ 281166124Srafan 282166124Srafan#if USE_SYSMOUSE 28350276Speterstatic void 284184989Srafansysmouse_server(SCREEN *sp) 285166124Srafan{ 286166124Srafan struct mouse_info the_mouse; 287166124Srafan MEVENT *work; 288166124Srafan 289166124Srafan the_mouse.operation = MOUSE_GETINFO; 290184989Srafan if (sp != 0 291184989Srafan && sp->_mouse_fd >= 0 292184989Srafan && sp->_sysmouse_tail < FIFO_SIZE 293184989Srafan && ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { 294166124Srafan 295184989Srafan if (sp->_sysmouse_head > sp->_sysmouse_tail) { 296184989Srafan sp->_sysmouse_tail = 0; 297184989Srafan sp->_sysmouse_head = 0; 298166124Srafan } 299184989Srafan work = &(sp->_sysmouse_fifo[sp->_sysmouse_tail]); 300166124Srafan memset(work, 0, sizeof(*work)); 301166124Srafan work->id = NORMAL_EVENT; /* there's only one mouse... */ 302166124Srafan 303184989Srafan sp->_sysmouse_old_buttons = sp->_sysmouse_new_buttons; 304184989Srafan sp->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7; 305166124Srafan 306184989Srafan if (sp->_sysmouse_new_buttons) { 307184989Srafan if (sp->_sysmouse_new_buttons & 1) 308166124Srafan work->bstate |= BUTTON1_PRESSED; 309184989Srafan if (sp->_sysmouse_new_buttons & 2) 310166124Srafan work->bstate |= BUTTON2_PRESSED; 311184989Srafan if (sp->_sysmouse_new_buttons & 4) 312166124Srafan work->bstate |= BUTTON3_PRESSED; 313166124Srafan } else { 314184989Srafan if (sp->_sysmouse_old_buttons & 1) 315166124Srafan work->bstate |= BUTTON1_RELEASED; 316184989Srafan if (sp->_sysmouse_old_buttons & 2) 317166124Srafan work->bstate |= BUTTON2_RELEASED; 318184989Srafan if (sp->_sysmouse_old_buttons & 4) 319166124Srafan work->bstate |= BUTTON3_RELEASED; 320166124Srafan } 321166124Srafan 322166124Srafan /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */ 323166124Srafan the_mouse.operation = MOUSE_HIDE; 324184989Srafan ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); 325166124Srafan the_mouse.operation = MOUSE_SHOW; 326184989Srafan ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); 327166124Srafan 328166124Srafan /* 329166124Srafan * We're only interested if the button is pressed or released. 330166124Srafan * FIXME: implement continuous event-tracking. 331166124Srafan */ 332184989Srafan if (sp->_sysmouse_new_buttons != sp->_sysmouse_old_buttons) { 333184989Srafan sp->_sysmouse_tail += 1; 334166124Srafan } 335184989Srafan work->x = the_mouse.u.data.x / sp->_sysmouse_char_width; 336184989Srafan work->y = the_mouse.u.data.y / sp->_sysmouse_char_height; 337166124Srafan } 33850276Speter} 339184989Srafan 340184989Srafanstatic void 341184989Srafanhandle_sysmouse(int sig GCC_UNUSED) 342184989Srafan{ 343184989Srafan sysmouse_server(SP); 344184989Srafan} 345166124Srafan#endif /* USE_SYSMOUSE */ 34650276Speter 347166124Srafanstatic void 348184989Srafaninit_xterm_mouse(SCREEN *sp) 349166124Srafan{ 350184989Srafan sp->_mouse_type = M_XTERM; 351184989Srafan sp->_mouse_xtermcap = tigetstr("XM"); 352184989Srafan if (!VALID_STRING(sp->_mouse_xtermcap)) 353184989Srafan sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;"; 354166124Srafan} 355166124Srafan 356166124Srafanstatic void 357184989Srafanenable_xterm_mouse(SCREEN *sp, int enable) 358166124Srafan{ 359166124Srafan#if USE_EMX_MOUSE 360184989Srafan sp->_emxmouse_activated = enable; 361166124Srafan#else 362184989Srafan putp(TPARM_1(sp->_mouse_xtermcap, enable)); 36350276Speter#endif 364184989Srafan sp->_mouse_active = enable; 365166124Srafan} 36650276Speter 367166124Srafan#if USE_GPM_SUPPORT 368184989Srafanstatic bool 369166124Srafanallow_gpm_mouse(void) 370166124Srafan{ 371184989Srafan bool result = FALSE; 372184989Srafan 373166124Srafan /* GPM does printf's without checking if stdout is a terminal */ 374166124Srafan if (isatty(fileno(stdout))) { 375184989Srafan char *list = getenv("NCURSES_GPM_TERMS"); 376166124Srafan char *env = getenv("TERM"); 377184989Srafan if (list != 0) { 378184989Srafan if (env != 0) { 379184989Srafan result = _nc_name_match(list, env, "|:"); 380184989Srafan } 381184989Srafan } else { 382184989Srafan /* GPM checks the beginning of the $TERM variable to decide if it 383184989Srafan * should pass xterm events through. There is no real advantage in 384184989Srafan * allowing GPM to do this. Recent versions relax that check, and 385184989Srafan * pretend that GPM can work with any terminal having the kmous 386184989Srafan * capability. Perhaps that works for someone. If so, they can 387184989Srafan * set the environment variable (above). 388184989Srafan */ 389184989Srafan if (env != 0 && strstr(env, "linux") != 0) { 390184989Srafan result = TRUE; 391184989Srafan } 392184989Srafan } 393166124Srafan } 394184989Srafan return result; 395166124Srafan} 39650276Speter 397184989Srafan#ifdef HAVE_LIBDL 398184989Srafanstatic void 399184989Srafanunload_gpm_library(SCREEN *sp) 400184989Srafan{ 401184989Srafan if (SP->_dlopen_gpm != 0) { 402184989Srafan T(("unload GPM library")); 403184989Srafan sp->_mouse_gpm_loaded = FALSE; 404184989Srafan sp->_mouse_fd = -1; 405184989Srafan dlclose(sp->_dlopen_gpm); 406184989Srafan sp->_dlopen_gpm = 0; 407184989Srafan } 408184989Srafan} 409184989Srafan 410184989Srafanstatic void 411184989Srafanload_gpm_library(SCREEN *sp) 412184989Srafan{ 413184989Srafan sp->_mouse_gpm_found = FALSE; 414184989Srafan if ((sp->_dlopen_gpm = dlopen(LIBGPM_SONAME, my_RTLD)) != 0) { 415184989Srafan if (GET_DLSYM(gpm_fd) == 0 || 416184989Srafan GET_DLSYM(Gpm_Open) == 0 || 417184989Srafan GET_DLSYM(Gpm_Close) == 0 || 418184989Srafan GET_DLSYM(Gpm_GetEvent) == 0) { 419184989Srafan T(("GPM initialization failed: %s", dlerror())); 420184989Srafan unload_gpm_library(sp); 421184989Srafan } else { 422184989Srafan sp->_mouse_gpm_found = TRUE; 423184989Srafan sp->_mouse_gpm_loaded = TRUE; 424184989Srafan } 425184989Srafan } 426184989Srafan} 427184989Srafan#endif 428184989Srafan 429166124Srafanstatic bool 430184989Srafanenable_gpm_mouse(SCREEN *sp, bool enable) 431166124Srafan{ 432166124Srafan bool result; 433166124Srafan 434166124Srafan T((T_CALLED("enable_gpm_mouse(%d)"), enable)); 435166124Srafan 436184989Srafan if (enable && !sp->_mouse_active) { 437184989Srafan#ifdef HAVE_LIBDL 438184989Srafan if (sp->_mouse_gpm_found && !sp->_mouse_gpm_loaded) { 439184989Srafan load_gpm_library(sp); 440184989Srafan } 441184989Srafan#endif 442184989Srafan if (sp->_mouse_gpm_loaded) { 443184989Srafan /* GPM: initialize connection to gpm server */ 444184989Srafan sp->_mouse_gpm_connect.eventMask = GPM_DOWN | GPM_UP; 445184989Srafan sp->_mouse_gpm_connect.defaultMask = 446184989Srafan (unsigned short) (~(sp->_mouse_gpm_connect.eventMask | GPM_HARD)); 447184989Srafan sp->_mouse_gpm_connect.minMod = 0; 448184989Srafan sp->_mouse_gpm_connect.maxMod = 449184989Srafan (unsigned short) (~((1 << KG_SHIFT) | 450184989Srafan (1 << KG_SHIFTL) | 451184989Srafan (1 << KG_SHIFTR))); 452184989Srafan /* 453184989Srafan * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open. 454184989Srafan * The former is recognized by wscons (SunOS), and the latter by 455184989Srafan * xterm. Those will not show up in ncurses' traces. 456184989Srafan */ 457184989Srafan result = (my_Gpm_Open(&sp->_mouse_gpm_connect, 0) >= 0); 458184989Srafan } else { 459184989Srafan result = FALSE; 460184989Srafan } 461184989Srafan sp->_mouse_active = result; 462166124Srafan T(("GPM open %s", result ? "succeeded" : "failed")); 463166124Srafan } else { 464184989Srafan if (!enable && sp->_mouse_active) { 465166124Srafan /* GPM: close connection to gpm server */ 466166124Srafan my_Gpm_Close(); 467184989Srafan sp->_mouse_active = FALSE; 468166124Srafan T(("GPM closed")); 469166124Srafan } 470184989Srafan result = enable; 471166124Srafan } 472184989Srafan#ifdef HAVE_LIBDL 473184989Srafan if (!result) { 474184989Srafan unload_gpm_library(sp); 475184989Srafan } 476184989Srafan#endif 477166124Srafan returnBool(result); 478166124Srafan} 479166124Srafan#endif /* USE_GPM_SUPPORT */ 480166124Srafan 481174993Srafan#define xterm_kmous "\033[M" 482174993Srafan 48362449Speterstatic void 484184989Srafaninitialize_mousetype(SCREEN *sp) 48550276Speter{ 486166124Srafan T((T_CALLED("initialize_mousetype()"))); 487166124Srafan 48862449Speter /* Try gpm first, because gpm may be configured to run in xterm */ 48962449Speter#if USE_GPM_SUPPORT 490166124Srafan if (allow_gpm_mouse()) { 491184989Srafan if (!sp->_mouse_gpm_loaded) { 492166124Srafan#ifdef HAVE_LIBDL 493184989Srafan load_gpm_library(sp); 494166124Srafan#else /* !HAVE_LIBDL */ 495184989Srafan sp->_mouse_gpm_found = TRUE; 496184989Srafan sp->_mouse_gpm_loaded = TRUE; 49750276Speter#endif 498166124Srafan } 49950276Speter 500166124Srafan /* 501166124Srafan * The gpm_fd file-descriptor may be negative (xterm). So we have to 502166124Srafan * maintain our notion of whether the mouse connection is active 503166124Srafan * without testing the file-descriptor. 504166124Srafan */ 505184989Srafan if (sp->_mouse_gpm_found && enable_gpm_mouse(sp, TRUE)) { 506184989Srafan sp->_mouse_type = M_GPM; 507184989Srafan sp->_mouse_fd = *(my_gpm_fd); 508184989Srafan T(("GPM mouse_fd %d", sp->_mouse_fd)); 509166124Srafan returnVoid; 510166124Srafan } 511166124Srafan } 512166124Srafan#endif /* USE_GPM_SUPPORT */ 513166124Srafan 51450276Speter /* OS/2 VIO */ 515166124Srafan#if USE_EMX_MOUSE 516184989Srafan if (!sp->_emxmouse_thread 51762449Speter && strstr(cur_term->type.term_names, "xterm") == 0 51862449Speter && key_mouse) { 51950276Speter int handles[2]; 52066963Speter 52150276Speter if (pipe(handles) < 0) { 52250276Speter perror("mouse pipe error"); 523166124Srafan returnVoid; 52450276Speter } else { 52550276Speter int rc; 52650276Speter 527184989Srafan if (!sp->_emxmouse_buttons[0]) { 52850276Speter char *s = getenv("MOUSE_BUTTONS_123"); 52950276Speter 530184989Srafan sp->_emxmouse_buttons[0] = 1; 53150276Speter if (s && strlen(s) >= 3) { 532184989Srafan sp->_emxmouse_buttons[1] = s[0] - '0'; 533184989Srafan sp->_emxmouse_buttons[2] = s[1] - '0'; 534184989Srafan sp->_emxmouse_buttons[3] = s[2] - '0'; 535166124Srafan } else { 536184989Srafan sp->_emxmouse_buttons[1] = 1; 537184989Srafan sp->_emxmouse_buttons[2] = 3; 538184989Srafan sp->_emxmouse_buttons[3] = 2; 53950276Speter } 54050276Speter } 541184989Srafan sp->_emxmouse_wfd = handles[1]; 542184989Srafan M_FD(sp) = handles[0]; 54350276Speter /* Needed? */ 54450276Speter setmode(handles[0], O_BINARY); 54550276Speter setmode(handles[1], O_BINARY); 54650276Speter /* Do not use CRT functions, we may single-threaded. */ 547184989Srafan rc = DosCreateThread((unsigned long *) &sp->_emxmouse_thread, 548184989Srafan mouse_server, (long) sp, 0, 8192); 54962449Speter if (rc) { 55050276Speter printf("mouse thread error %d=%#x", rc, rc); 55162449Speter } else { 552184989Srafan sp->_mouse_type = M_XTERM; 55362449Speter } 554166124Srafan returnVoid; 55550276Speter } 55650276Speter } 557166124Srafan#endif /* USE_EMX_MOUSE */ 55850276Speter 559166124Srafan#if USE_SYSMOUSE 560166124Srafan { 561166124Srafan struct mouse_info the_mouse; 562166124Srafan char *the_device = 0; 563166124Srafan 564184989Srafan if (isatty(sp->_ifd)) 565184989Srafan the_device = ttyname(sp->_ifd); 566166124Srafan if (the_device == 0) 567166124Srafan the_device = "/dev/tty"; 568166124Srafan 569184989Srafan sp->_mouse_fd = open(the_device, O_RDWR); 570166124Srafan 571184989Srafan if (sp->_mouse_fd >= 0) { 572166124Srafan /* 573166124Srafan * sysmouse does not have a usable user interface for obtaining 574166124Srafan * mouse events. The logical way to proceed (reading data on a 575166124Srafan * stream) only works if one opens the device as root. Even in 576166124Srafan * that mode, careful examination shows we lose events 577166124Srafan * occasionally. The interface provided for user programs is to 578166124Srafan * establish a signal handler. really. 579166124Srafan * 580166124Srafan * Take over SIGUSR2 for this purpose since SIGUSR1 is more 581166124Srafan * likely to be used by an application. getch() will have to 582166124Srafan * handle the misleading EINTR's. 583166124Srafan */ 584166124Srafan signal(SIGUSR2, SIG_IGN); 585166124Srafan the_mouse.operation = MOUSE_MODE; 586166124Srafan the_mouse.u.mode.mode = 0; 587166124Srafan the_mouse.u.mode.signal = SIGUSR2; 588184989Srafan if (ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { 589166124Srafan signal(SIGUSR2, handle_sysmouse); 590166124Srafan the_mouse.operation = MOUSE_SHOW; 591184989Srafan ioctl(sp->_mouse_fd, CONS_MOUSECTL, &the_mouse); 592166124Srafan 593166124Srafan#if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) /* FreeBSD > 2.x */ 594166124Srafan { 595166124Srafan#ifndef FBIO_GETMODE /* FreeBSD 3.x */ 596166124Srafan#define FBIO_GETMODE CONS_GET 597166124Srafan#define FBIO_MODEINFO CONS_MODEINFO 598166124Srafan#endif /* FBIO_GETMODE */ 599166124Srafan video_info_t the_video; 600166124Srafan 601184989Srafan if (ioctl(sp->_mouse_fd, 602166124Srafan FBIO_GETMODE, 603166124Srafan &the_video.vi_mode) != -1 604184989Srafan && ioctl(sp->_mouse_fd, 605166124Srafan FBIO_MODEINFO, 606166124Srafan &the_video) != -1) { 607184989Srafan sp->_sysmouse_char_width = the_video.vi_cwidth; 608184989Srafan sp->_sysmouse_char_height = the_video.vi_cheight; 609166124Srafan } 610166124Srafan } 611166124Srafan#endif /* defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) */ 612166124Srafan 613184989Srafan if (sp->_sysmouse_char_width <= 0) 614184989Srafan sp->_sysmouse_char_width = 8; 615184989Srafan if (sp->_sysmouse_char_height <= 0) 616184989Srafan sp->_sysmouse_char_height = 16; 617184989Srafan sp->_mouse_type = M_SYSMOUSE; 618166124Srafan returnVoid; 619166124Srafan } 620166124Srafan } 621166124Srafan } 622166124Srafan#endif /* USE_SYSMOUSE */ 623166124Srafan 62462449Speter /* we know how to recognize mouse events under "xterm" */ 62562449Speter if (key_mouse != 0) { 626166124Srafan if (!strcmp(key_mouse, xterm_kmous) 627166124Srafan || strstr(cur_term->type.term_names, "xterm") != 0) { 628184989Srafan init_xterm_mouse(sp); 62962449Speter } 63062449Speter } else if (strstr(cur_term->type.term_names, "xterm") != 0) { 631184989Srafan if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK) 632184989Srafan init_xterm_mouse(sp); 63362449Speter } 634166124Srafan returnVoid; 63550276Speter} 63650276Speter 637166124Srafanstatic bool 638184989Srafan_nc_mouse_init(SCREEN *sp) 63962449Speter/* initialize the mouse */ 64062449Speter{ 641166124Srafan bool result = FALSE; 64262449Speter int i; 64362449Speter 644184989Srafan if (sp != 0) { 645184989Srafan if (!sp->_mouse_initialized) { 646184989Srafan sp->_mouse_initialized = TRUE; 64762449Speter 648166124Srafan TR(MY_TRACE, ("_nc_mouse_init() called")); 64962449Speter 650184989Srafan sp->_mouse_eventp = FirstEV(sp); 651166124Srafan for (i = 0; i < EV_MAX; i++) 652184989Srafan sp->_mouse_events[i].id = INVALID_EVENT; 65362449Speter 654184989Srafan initialize_mousetype(sp); 65562449Speter 656184989Srafan T(("_nc_mouse_init() set mousetype to %d", sp->_mouse_type)); 657166124Srafan } 658184989Srafan result = sp->_mouse_initialized; 65962449Speter } 660166124Srafan return result; 66162449Speter} 66262449Speter 663166124Srafan/* 664166124Srafan * Query to see if there is a pending mouse event. This is called from 665166124Srafan * fifo_push() in lib_getch.c 666166124Srafan */ 66762449Speterstatic bool 668166124Srafan_nc_mouse_event(SCREEN *sp GCC_UNUSED) 66950276Speter{ 670184989Srafan MEVENT *eventp = sp->_mouse_eventp; 671166124Srafan bool result = FALSE; 67250276Speter 673166124Srafan (void) eventp; 67450276Speter 675184989Srafan switch (sp->_mouse_type) { 676166124Srafan case M_XTERM: 677166124Srafan /* xterm: never have to query, mouse events are in the keyboard stream */ 678166124Srafan#if USE_EMX_MOUSE 679166124Srafan { 680166124Srafan char kbuf[3]; 681166124Srafan 682166124Srafan int i, res = read(M_FD(sp), &kbuf, 3); /* Eat the prefix */ 683166124Srafan if (res != 3) 684166124Srafan printf("Got %d chars instead of 3 for prefix.\n", res); 685166124Srafan for (i = 0; i < res; i++) { 686166124Srafan if (kbuf[i] != key_mouse[i]) 687166124Srafan printf("Got char %d instead of %d for prefix.\n", 688166124Srafan (int) kbuf[i], (int) key_mouse[i]); 689166124Srafan } 690166124Srafan result = TRUE; 69150276Speter } 692166124Srafan#endif /* USE_EMX_MOUSE */ 693166124Srafan break; 69450276Speter 695166124Srafan#if USE_GPM_SUPPORT 696166124Srafan case M_GPM: 697166124Srafan { 698166124Srafan /* query server for event, return TRUE if we find one */ 699166124Srafan Gpm_Event ev; 70050276Speter 701166124Srafan if (my_Gpm_GetEvent(&ev) == 1) { 702166124Srafan /* there's only one mouse... */ 703166124Srafan eventp->id = NORMAL_EVENT; 704166124Srafan 705166124Srafan eventp->bstate = 0; 706166124Srafan switch (ev.type & 0x0f) { 707166124Srafan case (GPM_DOWN): 708166124Srafan if (ev.buttons & GPM_B_LEFT) 709166124Srafan eventp->bstate |= BUTTON1_PRESSED; 710166124Srafan if (ev.buttons & GPM_B_MIDDLE) 711166124Srafan eventp->bstate |= BUTTON2_PRESSED; 712166124Srafan if (ev.buttons & GPM_B_RIGHT) 713166124Srafan eventp->bstate |= BUTTON3_PRESSED; 714166124Srafan break; 715166124Srafan case (GPM_UP): 716166124Srafan if (ev.buttons & GPM_B_LEFT) 717166124Srafan eventp->bstate |= BUTTON1_RELEASED; 718166124Srafan if (ev.buttons & GPM_B_MIDDLE) 719166124Srafan eventp->bstate |= BUTTON2_RELEASED; 720166124Srafan if (ev.buttons & GPM_B_RIGHT) 721166124Srafan eventp->bstate |= BUTTON3_RELEASED; 722166124Srafan break; 723166124Srafan default: 724166124Srafan break; 725166124Srafan } 726166124Srafan 727166124Srafan eventp->x = ev.x - 1; 728166124Srafan eventp->y = ev.y - 1; 729166124Srafan eventp->z = 0; 730166124Srafan 731166124Srafan /* bump the next-free pointer into the circular list */ 732184989Srafan sp->_mouse_eventp = eventp = NEXT(eventp); 733166124Srafan result = TRUE; 734166124Srafan } 735166124Srafan } 736166124Srafan break; 73750276Speter#endif 73850276Speter 739166124Srafan#if USE_SYSMOUSE 740166124Srafan case M_SYSMOUSE: 741184989Srafan if (sp->_sysmouse_head < sp->_sysmouse_tail) { 742184989Srafan *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head]; 74366963Speter 744166124Srafan /* 745166124Srafan * Point the fifo-head to the next possible location. If there 746166124Srafan * are none, reset the indices. This may be interrupted by the 747166124Srafan * signal handler, doing essentially the same reset. 748166124Srafan */ 749184989Srafan sp->_sysmouse_head += 1; 750184989Srafan if (sp->_sysmouse_head == sp->_sysmouse_tail) { 751184989Srafan sp->_sysmouse_tail = 0; 752184989Srafan sp->_sysmouse_head = 0; 753166124Srafan } 754166124Srafan 755166124Srafan /* bump the next-free pointer into the circular list */ 756184989Srafan sp->_mouse_eventp = eventp = NEXT(eventp); 757166124Srafan result = TRUE; 75866963Speter } 759166124Srafan break; 760166124Srafan#endif /* USE_SYSMOUSE */ 761166124Srafan 762166124Srafan case M_NONE: 763166124Srafan break; 76466963Speter } 76566963Speter 766166124Srafan return result; /* true if we found an event */ 76750276Speter} 76850276Speter 76962449Speterstatic bool 770166124Srafan_nc_mouse_inline(SCREEN *sp) 77150276Speter/* mouse report received in the keyboard stream -- parse its info */ 77250276Speter{ 773166124Srafan int b; 774166124Srafan bool result = FALSE; 775184989Srafan MEVENT *eventp = sp->_mouse_eventp; 776166124Srafan 77750276Speter TR(MY_TRACE, ("_nc_mouse_inline() called")); 77850276Speter 779184989Srafan if (sp->_mouse_type == M_XTERM) { 78062449Speter unsigned char kbuf[4]; 781166124Srafan mmask_t prev; 78262449Speter size_t grabbed; 78362449Speter int res; 78450276Speter 78550276Speter /* This code requires that your xterm entry contain the kmous 78650276Speter * capability and that it be set to the \E[M documented in the 78750276Speter * Xterm Control Sequences reference. This is how we 78850276Speter * arrange for mouse events to be reported via a KEY_MOUSE 78950276Speter * return value from wgetch(). After this value is received, 79050276Speter * _nc_mouse_inline() gets called and is immediately 79150276Speter * responsible for parsing the mouse status information 79250276Speter * following the prefix. 79350276Speter * 79450276Speter * The following quotes from the ctrlseqs.ms document in the 79550276Speter * X distribution, describing the X mouse tracking feature: 79650276Speter * 79750276Speter * Parameters for all mouse tracking escape sequences 79850276Speter * generated by xterm encode numeric parameters in a single 79950276Speter * character as value+040. For example, ! is 1. 80050276Speter * 80150276Speter * On button press or release, xterm sends ESC [ M CbCxCy. 80250276Speter * The low two bits of Cb encode button information: 0=MB1 80350276Speter * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release. The 80450276Speter * upper bits encode what modifiers were down when the 80550276Speter * button was pressed and are added together. 4=Shift, 80650276Speter * 8=Meta, 16=Control. Cx and Cy are the x and y coordinates 80750276Speter * of the mouse event. The upper left corner is (1,1). 80850276Speter * 80950276Speter * (End quote) By the time we get here, we've eaten the 81050276Speter * key prefix. FYI, the loop below is necessary because 81150276Speter * mouse click info isn't guaranteed to present as a 812166124Srafan * single clist item. 813166124Srafan * 814166124Srafan * Wheel mice may return buttons 4 and 5 when the wheel is turned. 815166124Srafan * We encode those as button presses. 81650276Speter */ 817184989Srafan for (grabbed = 0; grabbed < 3; grabbed += (size_t) res) { 81850276Speter 81962449Speter /* For VIO mouse we add extra bit 64 to disambiguate button-up. */ 820166124Srafan#if USE_EMX_MOUSE 82162449Speter res = read(M_FD(sp) >= 0 ? M_FD(sp) : sp->_ifd, &kbuf, 3); 82250276Speter#else 82362449Speter res = read(sp->_ifd, kbuf + grabbed, 3 - grabbed); 82450276Speter#endif 82562449Speter if (res == -1) 82662449Speter break; 82750276Speter } 82850276Speter kbuf[3] = '\0'; 82950276Speter 83062449Speter TR(TRACE_IEVENT, 83166963Speter ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf)); 83250276Speter 833166124Srafan /* there's only one mouse... */ 834166124Srafan eventp->id = NORMAL_EVENT; 83550276Speter 83650276Speter /* processing code goes here */ 83750276Speter eventp->bstate = 0; 838166124Srafan prev = PREV(eventp)->bstate; 839166124Srafan 840166124Srafan#if USE_EMX_MOUSE 841166124Srafan#define PRESS_POSITION(n) \ 842166124Srafan eventp->bstate = MASK_PRESS(n); \ 843166124Srafan if (kbuf[0] & 0x40) \ 844166124Srafan eventp->bstate = MASK_RELEASE(n) 845166124Srafan#else 846166124Srafan#define PRESS_POSITION(n) \ 847184989Srafan eventp->bstate = (mmask_t) (prev & MASK_PRESS(n) \ 848184989Srafan ? REPORT_MOUSE_POSITION \ 849184989Srafan : MASK_PRESS(n)) 850166124Srafan#endif 851166124Srafan 85262449Speter switch (kbuf[0] & 0x3) { 85350276Speter case 0x0: 854166124Srafan if (kbuf[0] & 64) 855166124Srafan eventp->bstate = MASK_PRESS(4); 856166124Srafan else 857166124Srafan PRESS_POSITION(1); 85850276Speter break; 85950276Speter 86050276Speter case 0x1: 861166124Srafan#if NCURSES_MOUSE_VERSION == 2 862166124Srafan if (kbuf[0] & 64) 863166124Srafan eventp->bstate = MASK_PRESS(5); 864166124Srafan else 86550276Speter#endif 866166124Srafan PRESS_POSITION(2); 86750276Speter break; 86850276Speter 86950276Speter case 0x2: 870166124Srafan PRESS_POSITION(3); 87150276Speter break; 87250276Speter 87350276Speter case 0x3: 87450276Speter /* 875166124Srafan * Release events aren't reported for individual buttons, just for 876166124Srafan * the button set as a whole. However, because there are normally 877166124Srafan * no mouse events under xterm that intervene between press and 878166124Srafan * release, we can infer the button actually released by looking at 879166124Srafan * the previous event. 88050276Speter */ 881166124Srafan if (prev & (BUTTON_PRESSED | BUTTON_RELEASED)) { 882166124Srafan eventp->bstate = BUTTON_RELEASED; 883166124Srafan for (b = 1; b <= MAX_BUTTONS; ++b) { 884166124Srafan if (!(prev & MASK_PRESS(b))) 885166124Srafan eventp->bstate &= ~MASK_RELEASE(b); 886166124Srafan } 887166124Srafan } else { 888166124Srafan /* 889166124Srafan * XFree86 xterm will return a stream of release-events to 890166124Srafan * let the application know where the mouse is going, if the 891166124Srafan * private mode 1002 or 1003 is enabled. 892166124Srafan */ 893166124Srafan eventp->bstate = REPORT_MOUSE_POSITION; 894166124Srafan } 89550276Speter break; 89650276Speter } 897166124Srafan result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE; 89850276Speter 89950276Speter if (kbuf[0] & 4) { 90050276Speter eventp->bstate |= BUTTON_SHIFT; 90150276Speter } 90250276Speter if (kbuf[0] & 8) { 90350276Speter eventp->bstate |= BUTTON_ALT; 90450276Speter } 90550276Speter if (kbuf[0] & 16) { 90650276Speter eventp->bstate |= BUTTON_CTRL; 90750276Speter } 90850276Speter 90950276Speter eventp->x = (kbuf[1] - ' ') - 1; 91050276Speter eventp->y = (kbuf[2] - ' ') - 1; 91162449Speter TR(MY_TRACE, 91266963Speter ("_nc_mouse_inline: primitive mouse-event %s has slot %ld", 913184989Srafan _nc_tracemouse(sp, eventp), 914184989Srafan (long) IndexEV(sp, eventp))); 91550276Speter 91650276Speter /* bump the next-free pointer into the circular list */ 917184989Srafan sp->_mouse_eventp = NEXT(eventp); 91862449Speter#if 0 /* this return would be needed for QNX's mods to lib_getch.c */ 91962449Speter return (TRUE); 92050276Speter#endif 92150276Speter } 92250276Speter 923166124Srafan return (result); 92450276Speter} 92550276Speter 92662449Speterstatic void 927184989Srafanmouse_activate(SCREEN *sp, bool on) 92850276Speter{ 929184989Srafan if (!on && !sp->_mouse_initialized) 93050276Speter return; 93150276Speter 932184989Srafan if (!_nc_mouse_init(sp)) 933166124Srafan return; 93450276Speter 93550276Speter if (on) { 93650276Speter 937184989Srafan switch (sp->_mouse_type) { 93850276Speter case M_XTERM: 93966963Speter#if NCURSES_EXT_FUNCS 94050276Speter keyok(KEY_MOUSE, on); 94150276Speter#endif 94250276Speter TPUTS_TRACE("xterm mouse initialization"); 943184989Srafan enable_xterm_mouse(sp, 1); 94450276Speter break; 94550276Speter#if USE_GPM_SUPPORT 94650276Speter case M_GPM: 947184989Srafan if (enable_gpm_mouse(sp, TRUE)) { 948184989Srafan sp->_mouse_fd = *(my_gpm_fd); 949184989Srafan T(("GPM mouse_fd %d", sp->_mouse_fd)); 950166124Srafan } 95150276Speter break; 95250276Speter#endif 953166124Srafan#if USE_SYSMOUSE 954166124Srafan case M_SYSMOUSE: 955166124Srafan signal(SIGUSR2, handle_sysmouse); 956184989Srafan sp->_mouse_active = TRUE; 957166124Srafan break; 958166124Srafan#endif 959166124Srafan case M_NONE: 960166124Srafan return; 96150276Speter } 96250276Speter /* Make runtime binding to cut down on object size of applications that 96350276Speter * do not use the mouse (e.g., 'clear'). 96450276Speter */ 965184989Srafan sp->_mouse_event = _nc_mouse_event; 966184989Srafan sp->_mouse_inline = _nc_mouse_inline; 967184989Srafan sp->_mouse_parse = _nc_mouse_parse; 968184989Srafan sp->_mouse_resume = _nc_mouse_resume; 969184989Srafan sp->_mouse_wrap = _nc_mouse_wrap; 97050276Speter } else { 97150276Speter 972184989Srafan switch (sp->_mouse_type) { 97350276Speter case M_XTERM: 97450276Speter TPUTS_TRACE("xterm mouse deinitialization"); 975184989Srafan enable_xterm_mouse(sp, 0); 97650276Speter break; 97750276Speter#if USE_GPM_SUPPORT 97850276Speter case M_GPM: 979184989Srafan enable_gpm_mouse(sp, FALSE); 98050276Speter break; 98150276Speter#endif 982166124Srafan#if USE_SYSMOUSE 983166124Srafan case M_SYSMOUSE: 984166124Srafan signal(SIGUSR2, SIG_IGN); 985184989Srafan sp->_mouse_active = FALSE; 986166124Srafan break; 987166124Srafan#endif 988166124Srafan case M_NONE: 989166124Srafan return; 99050276Speter } 99150276Speter } 99256639Speter _nc_flush(); 99350276Speter} 99450276Speter 99550276Speter/************************************************************************** 99650276Speter * 99750276Speter * Device-independent code 99850276Speter * 99950276Speter **************************************************************************/ 100050276Speter 100162449Speterstatic bool 1002184989Srafan_nc_mouse_parse(SCREEN *sp, int runcount) 100350276Speter/* parse a run of atomic mouse events into a gesture */ 100450276Speter{ 1005184989Srafan MEVENT *eventp = sp->_mouse_eventp; 100662449Speter MEVENT *ep, *runp, *next, *prev = PREV(eventp); 100762449Speter int n; 1008166124Srafan int b; 100962449Speter bool merge; 101050276Speter 101150276Speter TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount)); 101250276Speter 101350276Speter /* 101450276Speter * When we enter this routine, the event list next-free pointer 101550276Speter * points just past a run of mouse events that we know were separated 101650276Speter * in time by less than the critical click interval. The job of this 1017166124Srafan * routine is to collapse this run into a single higher-level event 101850276Speter * or gesture. 101950276Speter * 102050276Speter * We accomplish this in two passes. The first pass merges press/release 102150276Speter * pairs into click events. The second merges runs of click events into 102250276Speter * double or triple-click events. 102350276Speter * 102450276Speter * It's possible that the run may not resolve to a single event (for 102550276Speter * example, if the user quadruple-clicks). If so, leading events 102650276Speter * in the run are ignored. 102750276Speter * 102850276Speter * Note that this routine is independent of the format of the specific 102950276Speter * format of the pointing-device's reports. We can use it to parse 103050276Speter * gestures on anything that reports press/release events on a per- 103150276Speter * button basis, as long as the device-dependent mouse code puts stuff 103250276Speter * on the queue in MEVENT format. 103350276Speter */ 103462449Speter if (runcount == 1) { 103562449Speter TR(MY_TRACE, 103666963Speter ("_nc_mouse_parse: returning simple mouse event %s at slot %ld", 1037184989Srafan _nc_tracemouse(sp, prev), 1038184989Srafan (long) IndexEV(sp, prev))); 1039166124Srafan return (prev->id >= NORMAL_EVENT) 1040184989Srafan ? ((prev->bstate & sp->_mouse_mask) ? TRUE : FALSE) 104162449Speter : FALSE; 104250276Speter } 104350276Speter 104450276Speter /* find the start of the run */ 104550276Speter runp = eventp; 104650276Speter for (n = runcount; n > 0; n--) { 104750276Speter runp = PREV(runp); 104850276Speter } 104950276Speter 105050276Speter#ifdef TRACE 1051174993Srafan if (USE_TRACEF(TRACE_IEVENT)) { 1052184989Srafan _trace_slot(sp, "before mouse press/release merge:"); 105350276Speter _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1054184989Srafan RunParams(sp, eventp, runp), 105566963Speter runcount); 1056174993Srafan _nc_unlock_global(tracef); 105750276Speter } 105850276Speter#endif /* TRACE */ 105950276Speter 106050276Speter /* first pass; merge press/release pairs */ 106150276Speter do { 106250276Speter merge = FALSE; 106397049Speter for (ep = runp; (next = NEXT(ep)) != eventp; ep = next) { 1064166124Srafan 1065166124Srafan#define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \ 1066166124Srafan == !(next->bstate & MASK_RELEASE(x))) 1067166124Srafan 106850276Speter if (ep->x == next->x && ep->y == next->y 1069166124Srafan && (ep->bstate & BUTTON_PRESSED) 1070166124Srafan && MASK_CHANGED(1) 1071166124Srafan && MASK_CHANGED(2) 1072166124Srafan && MASK_CHANGED(3) 1073166124Srafan && MASK_CHANGED(4) 1074166124Srafan#if NCURSES_MOUSE_VERSION == 2 1075166124Srafan && MASK_CHANGED(5) 1076166124Srafan#endif 107762449Speter ) { 1078166124Srafan for (b = 1; b <= MAX_BUTTONS; ++b) { 1079184989Srafan if ((sp->_mouse_mask & MASK_CLICK(b)) 1080166124Srafan && (ep->bstate & MASK_PRESS(b))) { 1081166124Srafan ep->bstate &= ~MASK_PRESS(b); 1082166124Srafan ep->bstate |= MASK_CLICK(b); 1083166124Srafan merge = TRUE; 1084166124Srafan } 108550276Speter } 108650276Speter if (merge) 108750276Speter next->id = INVALID_EVENT; 108850276Speter } 108950276Speter } 109050276Speter } while 109150276Speter (merge); 109250276Speter 109350276Speter#ifdef TRACE 1094174993Srafan if (USE_TRACEF(TRACE_IEVENT)) { 1095184989Srafan _trace_slot(sp, "before mouse click merge:"); 109650276Speter _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1097184989Srafan RunParams(sp, eventp, runp), 109866963Speter runcount); 1099174993Srafan _nc_unlock_global(tracef); 110050276Speter } 110150276Speter#endif /* TRACE */ 110250276Speter 110350276Speter /* 110450276Speter * Second pass; merge click runs. At this point, click events are 110550276Speter * each followed by one invalid event. We merge click events 110650276Speter * forward in the queue. 110750276Speter * 110850276Speter * NOTE: There is a problem with this design! If the application 110950276Speter * allows enough click events to pile up in the circular queue so 111050276Speter * they wrap around, it will cheerfully merge the newest forward 111150276Speter * into the oldest, creating a bogus doubleclick and confusing 111250276Speter * the queue-traversal logic rather badly. Generally this won't 111350276Speter * happen, because calling getmouse() marks old events invalid and 111450276Speter * ineligible for merges. The true solution to this problem would 111550276Speter * be to timestamp each MEVENT and perform the obvious sanity check, 111650276Speter * but the timer element would have to have sub-second resolution, 111750276Speter * which would get us into portability trouble. 111850276Speter */ 111950276Speter do { 112062449Speter MEVENT *follower; 112150276Speter 112250276Speter merge = FALSE; 112397049Speter for (ep = runp; (next = NEXT(ep)) != eventp; ep = next) 112462449Speter if (ep->id != INVALID_EVENT) { 112550276Speter if (next->id != INVALID_EVENT) 112650276Speter continue; 112750276Speter follower = NEXT(next); 112850276Speter if (follower->id == INVALID_EVENT) 112950276Speter continue; 113050276Speter 113150276Speter /* merge click events forward */ 1132166124Srafan if ((ep->bstate & BUTTON_CLICKED) 1133166124Srafan && (follower->bstate & BUTTON_CLICKED)) { 1134166124Srafan for (b = 1; b <= MAX_BUTTONS; ++b) { 1135184989Srafan if ((sp->_mouse_mask & MASK_DOUBLE_CLICK(b)) 1136166124Srafan && (follower->bstate & MASK_CLICK(b))) { 1137166124Srafan follower->bstate &= ~MASK_CLICK(b); 1138166124Srafan follower->bstate |= MASK_DOUBLE_CLICK(b); 1139166124Srafan merge = TRUE; 1140166124Srafan } 114150276Speter } 114250276Speter if (merge) 114350276Speter ep->id = INVALID_EVENT; 114450276Speter } 114550276Speter 114650276Speter /* merge double-click events forward */ 1147166124Srafan if ((ep->bstate & BUTTON_DOUBLE_CLICKED) 1148166124Srafan && (follower->bstate & BUTTON_CLICKED)) { 1149166124Srafan for (b = 1; b <= MAX_BUTTONS; ++b) { 1150184989Srafan if ((sp->_mouse_mask & MASK_TRIPLE_CLICK(b)) 1151166124Srafan && (follower->bstate & MASK_CLICK(b))) { 1152166124Srafan follower->bstate &= ~MASK_CLICK(b); 1153166124Srafan follower->bstate |= MASK_TRIPLE_CLICK(b); 1154166124Srafan merge = TRUE; 1155166124Srafan } 115650276Speter } 115750276Speter if (merge) 115850276Speter ep->id = INVALID_EVENT; 115950276Speter } 116050276Speter } 116150276Speter } while 116250276Speter (merge); 116350276Speter 116450276Speter#ifdef TRACE 1165174993Srafan if (USE_TRACEF(TRACE_IEVENT)) { 1166184989Srafan _trace_slot(sp, "before mouse event queue compaction:"); 116750276Speter _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1168184989Srafan RunParams(sp, eventp, runp), 116966963Speter runcount); 1170174993Srafan _nc_unlock_global(tracef); 117150276Speter } 117250276Speter#endif /* TRACE */ 117350276Speter 117450276Speter /* 117550276Speter * Now try to throw away trailing events flagged invalid, or that 117650276Speter * don't match the current event mask. 117750276Speter */ 117850276Speter for (; runcount; prev = PREV(eventp), runcount--) 1179184989Srafan if (prev->id == INVALID_EVENT || !(prev->bstate & sp->_mouse_mask)) { 1180184989Srafan sp->_mouse_eventp = eventp = prev; 118150276Speter } 118250276Speter#ifdef TRACE 1183174993Srafan if (USE_TRACEF(TRACE_IEVENT)) { 1184184989Srafan _trace_slot(sp, "after mouse event queue compaction:"); 118550276Speter _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d", 1186184989Srafan RunParams(sp, eventp, runp), 118766963Speter runcount); 1188174993Srafan _nc_unlock_global(tracef); 118950276Speter } 119050276Speter for (ep = runp; ep != eventp; ep = NEXT(ep)) 119150276Speter if (ep->id != INVALID_EVENT) 119262449Speter TR(MY_TRACE, 119366963Speter ("_nc_mouse_parse: returning composite mouse event %s at slot %ld", 1194184989Srafan _nc_tracemouse(sp, ep), 1195184989Srafan (long) IndexEV(sp, ep))); 119650276Speter#endif /* TRACE */ 119750276Speter 119850276Speter /* after all this, do we have a valid event? */ 119962449Speter return (PREV(eventp)->id != INVALID_EVENT); 120050276Speter} 120150276Speter 120262449Speterstatic void 1203184989Srafan_nc_mouse_wrap(SCREEN *sp) 120450276Speter/* release mouse -- called by endwin() before shellout/exit */ 120550276Speter{ 120650276Speter TR(MY_TRACE, ("_nc_mouse_wrap() called")); 120750276Speter 1208184989Srafan switch (sp->_mouse_type) { 120950276Speter case M_XTERM: 1210184989Srafan if (sp->_mouse_mask) 1211184989Srafan mouse_activate(sp, FALSE); 121250276Speter break; 121350276Speter#if USE_GPM_SUPPORT 121450276Speter /* GPM: pass all mouse events to next client */ 121562449Speter case M_GPM: 1216184989Srafan if (sp->_mouse_mask) 1217184989Srafan mouse_activate(sp, FALSE); 121862449Speter break; 121950276Speter#endif 1220166124Srafan#if USE_SYSMOUSE 1221166124Srafan case M_SYSMOUSE: 1222184989Srafan mouse_activate(sp, FALSE); 1223166124Srafan break; 1224166124Srafan#endif 1225166124Srafan case M_NONE: 1226166124Srafan break; 122750276Speter } 122850276Speter} 122950276Speter 123062449Speterstatic void 1231184989Srafan_nc_mouse_resume(SCREEN *sp) 123250276Speter/* re-connect to mouse -- called by doupdate() after shellout */ 123350276Speter{ 123450276Speter TR(MY_TRACE, ("_nc_mouse_resume() called")); 123550276Speter 1236184989Srafan switch (sp->_mouse_type) { 1237166124Srafan case M_XTERM: 1238166124Srafan /* xterm: re-enable reporting */ 1239184989Srafan if (sp->_mouse_mask) 1240184989Srafan mouse_activate(sp, TRUE); 1241166124Srafan break; 1242166124Srafan 1243166124Srafan#if USE_GPM_SUPPORT 1244166124Srafan case M_GPM: 1245166124Srafan /* GPM: reclaim our event set */ 1246184989Srafan if (sp->_mouse_mask) 1247184989Srafan mouse_activate(sp, TRUE); 1248166124Srafan break; 1249166124Srafan#endif 1250166124Srafan 1251166124Srafan#if USE_SYSMOUSE 1252166124Srafan case M_SYSMOUSE: 1253184989Srafan mouse_activate(sp, TRUE); 1254166124Srafan break; 1255166124Srafan#endif 1256166124Srafan case M_NONE: 1257166124Srafan break; 1258166124Srafan } 125950276Speter} 126050276Speter 126150276Speter/************************************************************************** 126250276Speter * 126350276Speter * Mouse interface entry points for the API 126450276Speter * 126550276Speter **************************************************************************/ 126650276Speter 1267184989Srafanstatic int 1268184989Srafan_nc_getmouse(SCREEN *sp, MEVENT * aevent) 126950276Speter{ 127050276Speter T((T_CALLED("getmouse(%p)"), aevent)); 127150276Speter 1272184989Srafan if ((aevent != 0) && (sp != 0) && (sp->_mouse_type != M_NONE)) { 1273184989Srafan MEVENT *eventp = sp->_mouse_eventp; 127450276Speter /* compute the current-event pointer */ 127562449Speter MEVENT *prev = PREV(eventp); 127650276Speter 127750276Speter /* copy the event we find there */ 127850276Speter *aevent = *prev; 127950276Speter 128050276Speter TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld", 1281184989Srafan _nc_tracemouse(sp, prev), 1282184989Srafan (long) IndexEV(sp, prev))); 128350276Speter 128450276Speter prev->id = INVALID_EVENT; /* so the queue slot becomes free */ 128550276Speter returnCode(OK); 128650276Speter } 128750276Speter returnCode(ERR); 128850276Speter} 128950276Speter 1290184989Srafan/* grab a copy of the current mouse event */ 129176726SpeterNCURSES_EXPORT(int) 1292184989Srafangetmouse(MEVENT * aevent) 129350276Speter{ 1294184989Srafan return _nc_getmouse(SP, aevent); 1295184989Srafan} 1296184989Srafan 1297184989Srafanstatic int 1298184989Srafan_nc_ungetmouse(SCREEN *sp, MEVENT * aevent) 1299184989Srafan{ 1300166124Srafan int result = ERR; 130150276Speter 1302166124Srafan T((T_CALLED("ungetmouse(%p)"), aevent)); 130350276Speter 1304184989Srafan if (aevent != 0 && sp != 0) { 1305184989Srafan MEVENT *eventp = sp->_mouse_eventp; 1306166124Srafan 1307166124Srafan /* stick the given event in the next-free slot */ 1308166124Srafan *eventp = *aevent; 1309166124Srafan 1310166124Srafan /* bump the next-free pointer into the circular list */ 1311184989Srafan sp->_mouse_eventp = NEXT(eventp); 1312166124Srafan 1313166124Srafan /* push back the notification event on the keyboard queue */ 1314184989Srafan result = _nc_ungetch(sp, KEY_MOUSE); 1315166124Srafan } 1316166124Srafan returnCode(result); 131750276Speter} 131850276Speter 1319184989Srafan/* enqueue a synthesized mouse event to be seen by the next wgetch() */ 1320184989SrafanNCURSES_EXPORT(int) 1321184989Srafanungetmouse(MEVENT * aevent) 1322184989Srafan{ 1323184989Srafan return _nc_ungetmouse(SP, aevent); 1324184989Srafan} 1325184989Srafan 132676726SpeterNCURSES_EXPORT(mmask_t) 132762449Spetermousemask(mmask_t newmask, mmask_t * oldmask) 132850276Speter/* set the mouse event mask */ 132950276Speter{ 133050276Speter mmask_t result = 0; 133150276Speter 1332166124Srafan T((T_CALLED("mousemask(%#lx,%p)"), (unsigned long) newmask, oldmask)); 133350276Speter 1334166124Srafan if (SP != 0) { 1335166124Srafan if (oldmask) 1336166124Srafan *oldmask = SP->_mouse_mask; 133750276Speter 1338166124Srafan if (newmask || SP->_mouse_initialized) { 1339184989Srafan _nc_mouse_init(SP); 1340166124Srafan if (SP->_mouse_type != M_NONE) { 1341166124Srafan result = newmask & 1342166124Srafan (REPORT_MOUSE_POSITION 1343166124Srafan | BUTTON_ALT 1344166124Srafan | BUTTON_CTRL 1345166124Srafan | BUTTON_SHIFT 1346166124Srafan | BUTTON_PRESSED 1347166124Srafan | BUTTON_RELEASED 1348166124Srafan | BUTTON_CLICKED 1349166124Srafan | BUTTON_DOUBLE_CLICKED 1350166124Srafan | BUTTON_TRIPLE_CLICKED); 135150276Speter 1352184989Srafan mouse_activate(SP, (bool) (result != 0)); 135350276Speter 1354166124Srafan SP->_mouse_mask = result; 1355166124Srafan } 1356166124Srafan } 135750276Speter } 1358166124Srafan returnBits(result); 135950276Speter} 136050276Speter 136176726SpeterNCURSES_EXPORT(bool) 136262449Speterwenclose(const WINDOW *win, int y, int x) 136350276Speter/* check to see if given window encloses given screen location */ 136450276Speter{ 1365166124Srafan bool result = FALSE; 1366166124Srafan 1367166124Srafan T((T_CALLED("wenclose(%p,%d,%d)"), win, y, x)); 1368166124Srafan 1369166124Srafan if (win != 0) { 137050276Speter y -= win->_yoffset; 1371166124Srafan result = ((win->_begy <= y && 1372166124Srafan win->_begx <= x && 1373166124Srafan (win->_begx + win->_maxx) >= x && 1374166124Srafan (win->_begy + win->_maxy) >= y) ? TRUE : FALSE); 137550276Speter } 1376166124Srafan returnBool(result); 137750276Speter} 137850276Speter 137976726SpeterNCURSES_EXPORT(int) 138062449Spetermouseinterval(int maxclick) 138150276Speter/* set the maximum mouse interval within which to recognize a click */ 138250276Speter{ 138350276Speter int oldval; 138450276Speter 1385166124Srafan T((T_CALLED("mouseinterval(%d)"), maxclick)); 1386166124Srafan 138750276Speter if (SP != 0) { 138850276Speter oldval = SP->_maxclick; 138950276Speter if (maxclick >= 0) 139050276Speter SP->_maxclick = maxclick; 139150276Speter } else { 139250276Speter oldval = DEFAULT_MAXCLICK; 139350276Speter } 139450276Speter 1395166124Srafan returnCode(oldval); 139650276Speter} 139750276Speter 139850276Speter/* This may be used by other routines to ask for the existence of mouse 139950276Speter support */ 140076726SpeterNCURSES_EXPORT(int) 140162449Speter_nc_has_mouse(void) 140262449Speter{ 1403166124Srafan return (SP->_mouse_type == M_NONE ? 0 : 1); 140450276Speter} 140550276Speter 140676726SpeterNCURSES_EXPORT(bool) 1407166124Srafanwmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen) 140850276Speter{ 140962449Speter bool result = FALSE; 141050276Speter 1411166124Srafan T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"), win, pY, pX, to_screen)); 1412166124Srafan 141362449Speter if (win && pY && pX) { 141462449Speter int y = *pY; 141562449Speter int x = *pX; 141650276Speter 141762449Speter if (to_screen) { 141862449Speter y += win->_begy + win->_yoffset; 141962449Speter x += win->_begx; 142062449Speter if (wenclose(win, y, x)) 142162449Speter result = TRUE; 142262449Speter } else { 142362449Speter if (wenclose(win, y, x)) { 142462449Speter y -= (win->_begy + win->_yoffset); 142562449Speter x -= win->_begx; 142662449Speter result = TRUE; 142750276Speter } 142850276Speter } 142962449Speter if (result) { 143062449Speter *pX = x; 143162449Speter *pY = y; 143250276Speter } 143350276Speter } 1434166124Srafan returnBool(result); 143550276Speter} 1436