📄 lib_mouse.c
字号:
/**************************************************************************** * Copyright (c) 1998-2004,2005 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, distribute with modifications, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is * * furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included * * in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * * Except as contained in this notice, the name(s) of the above copyright * * holders shall not be used in advertising or otherwise to promote the * * sale, use or other dealings in this Software without prior written * * authorization. * ****************************************************************************//**************************************************************************** * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * * and: Eric S. Raymond <esr@snark.thyrsus.com> * * and: Thomas E. Dickey 1996-on * ****************************************************************************//* * This module is intended to encapsulate ncurses's interface to pointing * devices. * * The primary method used is xterm's internal mouse-tracking facility. * Additional methods depend on the platform: * Alessandro Rubini's GPM server (Linux) * sysmouse (FreeBSD) * special-purpose mouse interface for OS/2 EMX. * * Notes for implementors of new mouse-interface methods: * * The code is logically split into a lower level that accepts event reports * in a device-dependent format and an upper level that parses mouse gestures * and filters events. The mediating data structure is a circular queue of * MEVENT structures. * * Functionally, the lower level's job is to pick up primitive events and * put them on the circular queue. This can happen in one of two ways: * either (a) _nc_mouse_event() detects a series of incoming mouse reports * and queues them, or (b) code in lib_getch.c detects the kmous prefix in * the keyboard input stream and calls _nc_mouse_inline to queue up a series * of adjacent mouse reports. * * In either case, _nc_mouse_parse() should be called after the series is * accepted to parse the digested mouse reports (low-level MEVENTs) into * a gesture (a high-level or composite MEVENT). * * Don't be too shy about adding new event types or modifiers, if you can find * room for them in the 32-bit mask. The API is written so that users get * feedback on which theoretical event types they won't see when they call * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being * used yet, and a couple of bits open at the high end. */#ifdef __EMX__# include <io.h># define INCL_DOS# define INCL_VIO# define INCL_KBD# define INCL_MOU# define INCL_DOSPROCESS# include <os2.h> /* Need to include before the others */#endif#include <curses.priv.h>MODULE_ID("$Id: lib_mouse.c,v 1.77 2005/09/10 22:58:57 tom Exp $")#include <term.h>#include <tic.h>#if USE_GPM_SUPPORT#ifndef LINT /* don't need this for llib-lncurses */#undef buttons /* term.h defines this, and gpm uses it! */#include <gpm.h>#include <linux/keyboard.h> /* defines KG_* macros *//* use dynamic loader to avoid linkage dependency */#include <dlfcn.h>#ifdef RTLD_NOW#define my_RTLD RTLD_NOW#else#ifdef RTLD_LAZY#define my_RTLD RTLD_LAZY#elsemake an error#endif#endif#endif#endif /* USE_GPM_SUPPORT */#if USE_SYSMOUSE#undef buttons /* symbol conflict in consio.h */#undef mouse_info /* symbol conflict in consio.h */#include <osreldate.h>#if (__FreeBSD_version >= 400017)#include <sys/consio.h>#include <sys/fbio.h>#else#include <machine/console.h>#endif#endif /* use_SYSMOUSE */#define MY_TRACE TRACE_ICALLS|TRACE_IEVENT#define MASK_RELEASE(x) NCURSES_MOUSE_MASK(x, 001)#define MASK_PRESS(x) NCURSES_MOUSE_MASK(x, 002)#define MASK_CLICK(x) NCURSES_MOUSE_MASK(x, 004)#define MASK_DOUBLE_CLICK(x) NCURSES_MOUSE_MASK(x, 010)#define MASK_TRIPLE_CLICK(x) NCURSES_MOUSE_MASK(x, 020)#define MASK_RESERVED_EVENT(x) NCURSES_MOUSE_MASK(x, 040)#if NCURSES_MOUSE_VERSION == 1#define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED)#define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED)#define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED)#define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED)#define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED)#define MAX_BUTTONS 4#else#define BUTTON_CLICKED (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED | BUTTON4_CLICKED | BUTTON5_CLICKED)#define BUTTON_PRESSED (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED | BUTTON5_PRESSED)#define BUTTON_RELEASED (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED | BUTTON4_RELEASED | BUTTON5_RELEASED)#define BUTTON_DOUBLE_CLICKED (BUTTON1_DOUBLE_CLICKED | BUTTON2_DOUBLE_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON4_DOUBLE_CLICKED | BUTTON5_DOUBLE_CLICKED)#define BUTTON_TRIPLE_CLICKED (BUTTON1_TRIPLE_CLICKED | BUTTON2_TRIPLE_CLICKED | BUTTON3_TRIPLE_CLICKED | BUTTON4_TRIPLE_CLICKED | BUTTON5_TRIPLE_CLICKED)#define MAX_BUTTONS 5#endif#define INVALID_EVENT -1#define NORMAL_EVENT 0#if USE_GPM_SUPPORT#ifndef LINT#ifndef LIBGPM_SONAME#define LIBGPM_SONAME "libgpm.so"#endif#define GET_DLSYM(name) (my_##name = (TYPE_##name) dlsym(obj, #name))static Gpm_Connect gpm_connect;typedef int *TYPE_gpm_fd;typedef int (*TYPE_Gpm_Open) (Gpm_Connect *, int);typedef int (*TYPE_Gpm_Close) (void);typedef int (*TYPE_Gpm_GetEvent) (Gpm_Event *);static TYPE_gpm_fd my_gpm_fd;static TYPE_Gpm_Open my_Gpm_Open;static TYPE_Gpm_Close my_Gpm_Close;static TYPE_Gpm_GetEvent my_Gpm_GetEvent;#endif /* LINT */#endif /* USE_GPM_SUPPORT */static mmask_t eventmask; /* current event mask */static bool _nc_mouse_parse(int);static void _nc_mouse_resume(SCREEN *);static void _nc_mouse_wrap(SCREEN *);/* maintain a circular list of mouse events *//* The definition of the circular list size (EV_MAX), is in curses.priv.h, so * wgetch() may refer to the size and call _nc_mouse_parse() before circular * list overflow. */static MEVENT events[EV_MAX]; /* hold the last mouse event seen */static MEVENT *eventp = events; /* next free slot in event queue */#undef NEXT#define NEXT(ep) ((ep == events + EV_MAX - 1) ? events : ep + 1)#undef PREV#define PREV(ep) ((ep == events) ? events + EV_MAX - 1 : ep - 1)#ifdef TRACEstatic void_trace_slot(const char *tag){ MEVENT *ep; _tracef(tag); for (ep = events; ep < events + EV_MAX; ep++) _tracef("mouse event queue slot %ld = %s", (long) (ep - events), _tracemouse(ep));}#endif#if USE_EMX_MOUSE# define TOP_ROW 0# define LEFT_COL 0static int mouse_wfd;static int mouse_thread;static int mouse_activated;static char mouse_buttons[] ={0, 1, 3, 2};# define M_FD(sp) sp->_mouse_fdstatic voidwrite_event(int down, int button, int x, int y){ char buf[6]; unsigned long ignore; strncpy(buf, key_mouse, 3); /* should be "\033[M" */ buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40); buf[4] = ' ' + x - LEFT_COL + 1; buf[5] = ' ' + y - TOP_ROW + 1; DosWrite(mouse_wfd, buf, 6, &ignore);}static voidmouse_server(unsigned long ignored GCC_UNUSED){ unsigned short fWait = MOU_WAIT; /* NOPTRRECT mourt = { 0,0,24,79 }; */ MOUEVENTINFO mouev; HMOU hmou; unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN; int nbuttons = 3; int oldstate = 0; char err[80]; unsigned long rc; /* open the handle for the mouse */ if (MouOpen(NULL, &hmou) == 0) { rc = MouSetEventMask(&mask, hmou); if (rc) { /* retry with 2 buttons */ mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN; rc = MouSetEventMask(&mask, hmou); nbuttons = 2; } if (rc == 0 && MouDrawPtr(hmou) == 0) { for (;;) { /* sit and wait on the event queue */ rc = MouReadEventQue(&mouev, &fWait, hmou); if (rc) { sprintf(err, "Error reading mouse queue, rc=%lu.\r\n", rc); break; } if (!mouse_activated) goto finish; /* * OS/2 numbers a 3-button mouse inconsistently from other * platforms: * 1 = left * 2 = right * 3 = middle. */ if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN) write_event(mouev.fs & MOUSE_BN1_DOWN, mouse_buttons[1], mouev.col, mouev.row); if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN) write_event(mouev.fs & MOUSE_BN2_DOWN, mouse_buttons[3], mouev.col, mouev.row); if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN) write_event(mouev.fs & MOUSE_BN3_DOWN, mouse_buttons[2], mouev.col, mouev.row); finish: oldstate = mouev.fs; } } else sprintf(err, "Error setting event mask, buttons=%d, rc=%lu.\r\n", nbuttons, rc); DosWrite(2, err, strlen(err), &rc); MouClose(hmou); } DosExit(EXIT_THREAD, 0L);}#endif /* USE_EMX_MOUSE */#if USE_SYSMOUSEstatic voidhandle_sysmouse(int sig GCC_UNUSED){ struct mouse_info the_mouse; MEVENT *work; the_mouse.operation = MOUSE_GETINFO; if (SP != 0 && SP->_mouse_fd >= 0 && SP->_sysmouse_tail < FIFO_SIZE && ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse) != -1) { if (SP->_sysmouse_head > SP->_sysmouse_tail) { SP->_sysmouse_tail = 0; SP->_sysmouse_head = 0; } work = &(SP->_sysmouse_fifo[SP->_sysmouse_tail]); memset(work, 0, sizeof(*work)); work->id = NORMAL_EVENT; /* there's only one mouse... */ SP->_sysmouse_old_buttons = SP->_sysmouse_new_buttons; SP->_sysmouse_new_buttons = the_mouse.u.data.buttons & 0x7; if (SP->_sysmouse_new_buttons) { if (SP->_sysmouse_new_buttons & 1) work->bstate |= BUTTON1_PRESSED; if (SP->_sysmouse_new_buttons & 2) work->bstate |= BUTTON2_PRESSED; if (SP->_sysmouse_new_buttons & 4) work->bstate |= BUTTON3_PRESSED; } else { if (SP->_sysmouse_old_buttons & 1) work->bstate |= BUTTON1_RELEASED; if (SP->_sysmouse_old_buttons & 2) work->bstate |= BUTTON2_RELEASED; if (SP->_sysmouse_old_buttons & 4) work->bstate |= BUTTON3_RELEASED; } /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */ the_mouse.operation = MOUSE_HIDE; ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse); the_mouse.operation = MOUSE_SHOW; ioctl(SP->_mouse_fd, CONS_MOUSECTL, &the_mouse); /* * We're only interested if the button is pressed or released. * FIXME: implement continuous event-tracking. */ if (SP->_sysmouse_new_buttons != SP->_sysmouse_old_buttons) { SP->_sysmouse_tail += 1; } work->x = the_mouse.u.data.x / SP->_sysmouse_char_width; work->y = the_mouse.u.data.y / SP->_sysmouse_char_height; }}#endif /* USE_SYSMOUSE */static int initialized;static voidinit_xterm_mouse(void){ SP->_mouse_type = M_XTERM; SP->_mouse_xtermcap = tigetstr("XM"); if (!VALID_STRING(SP->_mouse_xtermcap)) SP->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";}static voidenable_xterm_mouse(int enable){#if USE_EMX_MOUSE mouse_activated = enable;#else putp(tparm(SP->_mouse_xtermcap, enable));#endif SP->_mouse_active = enable;}#if USE_GPM_SUPPORTstatic intallow_gpm_mouse(void){ /* GPM does printf's without checking if stdout is a terminal */ if (isatty(fileno(stdout))) { char *env = getenv("TERM"); /* GPM checks the beginning of the $TERM variable to decide if * it should pass xterm events through. There is no real advantage * in allowing GPM to do this. */ if (env == 0 || strncmp(env, "xterm", 5)) return TRUE; } return FALSE;}static boolenable_gpm_mouse(int enable){ bool result; T((T_CALLED("enable_gpm_mouse(%d)"), enable)); if (enable && !SP->_mouse_active) { /* GPM: initialize connection to gpm server */ gpm_connect.eventMask = GPM_DOWN | GPM_UP; gpm_connect.defaultMask = ~(gpm_connect.eventMask | GPM_HARD); gpm_connect.minMod = 0; gpm_connect.maxMod = (unsigned short) (~((1 << KG_SHIFT) | (1 << KG_SHIFTL) | (1 << KG_SHIFTR))); /* * Note: GPM hardcodes \E[?1001s and \E[?1000h during its open. * The former is recognized by wscons (SunOS), and the latter by * xterm. Those will not show up in ncurses' traces. */ result = (my_Gpm_Open(&gpm_connect, 0) >= 0); SP->_mouse_active = result; T(("GPM open %s", result ? "succeeded" : "failed")); } else { if (!enable && SP->_mouse_active) { /* GPM: close connection to gpm server */ my_Gpm_Close(); SP->_mouse_active = FALSE; T(("GPM closed")); } result = FALSE; } returnBool(result);}#endif /* USE_GPM_SUPPORT */static voidinitialize_mousetype(void){ static const char *xterm_kmous = "\033[M"; T((T_CALLED("initialize_mousetype()"))); /* Try gpm first, because gpm may be configured to run in xterm */#if USE_GPM_SUPPORT if (allow_gpm_mouse()) { static bool first = TRUE; static bool found = FALSE; if (first) { void *obj; first = FALSE; if ((obj = dlopen(LIBGPM_SONAME, my_RTLD)) != 0) { if (GET_DLSYM(gpm_fd) == 0 || GET_DLSYM(Gpm_Open) == 0 || GET_DLSYM(Gpm_Close) == 0 || GET_DLSYM(Gpm_GetEvent) == 0) { T(("GPM initialization failed: %s", dlerror())); dlclose(obj);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -