diff options
author | Aaron Campbell <aaron@cvs.openbsd.org> | 2000-09-01 05:59:13 +0000 |
---|---|---|
committer | Aaron Campbell <aaron@cvs.openbsd.org> | 2000-09-01 05:59:13 +0000 |
commit | 2ce91b322e158cd700f986d2d3a54c62c454d286 (patch) | |
tree | 7ab77fec134ab2d5a5822dadd577685951805470 | |
parent | 9f611c71e03f67658657c280acac65377e8d66e5 (diff) |
Userland mouse daemon for new PCVT mouse features. The daemon must be started
for the mouse to work. A common usage for a PS/2 mouse might be:
/usr/sbin/moused -p /dev/psm0 -M2=3 -m IntelliMouse
This sets the mouse port to /dev/psm0, maps the right mouse button to paste
(by default, the right mouse button is for "extending" selections and the
middle button pastes), and the -m flag sets the specific model, in this example
for a Microsoft IntelliMouse, which enables the wheel scrollback functionality.
Note that for wheel mice you also need "option INTELLIMOUSE" compiled into the
kernel.
This daemon comes from FreeBSD and was ported to OpenBSD by Jean-Baptiste
Marchand.
-rw-r--r-- | usr.sbin/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/moused/Makefile | 18 | ||||
-rw-r--r-- | usr.sbin/moused/moused.8 | 256 | ||||
-rw-r--r-- | usr.sbin/moused/moused.c | 2035 | ||||
-rw-r--r-- | usr.sbin/moused/moused.h | 346 |
5 files changed, 2657 insertions, 2 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index ed915b09bfa..6ee30db9636 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.67 2000/07/02 02:07:57 mickey Exp $ +# $OpenBSD: Makefile,v 1.68 2000/09/01 05:59:12 aaron Exp $ # not yet done: catman @@ -8,7 +8,7 @@ SUBDIR= ac accton adduser amd arp bootpd bootpgw bootpef bootptest \ chroot config cron dev_mkdb dhcp \ edquota gspa httpd inetd iostat \ ipftest ipmon ipsend kgmon kvm_mkdb lpr \ - mailwrapper map-mbone mrinfo mopd mrouted mtrace mtree named \ + mailwrapper map-mbone mrinfo mopd moused mrouted mtrace mtree named \ netgroup_mkdb openssl pkg_install portmap ppp pppd pppoe pstat \ pwd_mkdb quot quotaon rarpd rbootd rdconfig rdate repquota rmt \ rpc.bootparamd rpc.lockd rpc.pcnfsd rwhod \ diff --git a/usr.sbin/moused/Makefile b/usr.sbin/moused/Makefile new file mode 100644 index 00000000000..8aacf575122 --- /dev/null +++ b/usr.sbin/moused/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.1 2000/09/01 05:59:12 aaron Exp $ + +.if ${MACHINE} == "i386" + +PROG= moused +SRCS= moused.c + +.else + +NOPROG=yes + +.endif + +MAN= moused.8 +MANSUBDIR= i386 + +.include <bsd.prog.mk> + diff --git a/usr.sbin/moused/moused.8 b/usr.sbin/moused/moused.8 new file mode 100644 index 00000000000..b9a81d2ddcd --- /dev/null +++ b/usr.sbin/moused/moused.8 @@ -0,0 +1,256 @@ +.Dd August 30, 2000 +.Dt MOUSED 8 +.Os + +.Sh NAME +.Nm moused +.Nd +mouse daemon +.Sh SYNOPSIS +.Nm +.Op Fl 3DPRcdfs +.Op Fl I Ar pid_file +.Op Fl F Ar rate +.Op Fl r Ar resolution +.Op Fl S Ar baudrate +.Op Fl C Ar threshold +.Op Fl t Ar protocol +.Op Fl m Ar mousetype +.Op Fl M Ar N=M +.Op Fl w Ar N +.Op Fl z Ar target +.Op Fl b Ar buttons +.Ar -p Ar device + +.Nm +.Ar -i Ar info +.Ar -p Ar device + +.Sh DESCRIPTION +.Nm +is the mouse daemon, whose purpose is to handle the mouse under the +console, providing copy and paste functions. + +The daemon listens to mouse events, read bytes from the mouse, translates with +the appropriate protocol and then transmits to the pcvt console driver the +actions to execute. + +The following options are available: +.Bl -tag -width Ds +.It Fl 3 +Emulate the third (middle) button for 2-button mice. It is emulated by pressing +the left and right physical buttons simultaneously. +.It Fl C +Set double click speed as the maximum interval in msec between button clicks. +Without this option, the default value of 500 msec will be assumed. This option +will have effect only on the cut and paste operations in the text mode console. +.It Fl D +Lower DTR on the serial port. This option is valid only if +.Pa mousesystems +is selected as the protocol type. The DTR line may need to be +dropped for a 3-button mouse to operate in the +.Pa mousesystems +mode. +.It Fl F Ar rate +Set the report rate (reports/sec) of the device if supported. +.It Fl I Ar file +Write the process id of the +.Pa moused +daemon in the specified file. Without this option, the process id will be stored +in +.Pa /var/run/moused.pid. +.It Fl P +Do not start the Plug and Play COM device enumeration procedure when identifying +the serial mouse. If this option is given together with the +.Pa i +option, the +.Pa moused +command won't be able to print useful information for the serial mouse. +.It Fl R +Lower RTS on the serial port. This option is valid only if +.Pa mousesystems +is selected as the protocol type by the +.Pa -t +option below. It is often used with the +.Pa -D option above. Both RTS and DTR lines may need to be dropped for a +3-button mouse to operate in the +.Pa mousesystems +mode. +is selected as the protocol type by the +.Pa mousesystems +mode. +.It Fl S Ar baudrate +Select the baudrate for the serial port (1200 to 9600). Not all serial mice +support this option. +.It Fl c +Some mice report middle button down events as if the left and right buttons are +pressed. This option handles this. +.It Fl d +Enable debugging messages. +.It Fl f +Do not become a daemon and instead run as a foreground process. Useful for +testing and debugging. +.It Fl i Ar info +Print specified information and quit. Available pieces of information are : +.Bl -column -indent +.It port Ta Device name, i.e. +.Pa /dev/cua0[0-3] +for a serial mouse, +.Pa /dev/lms0 +for a logitech-style bus mouse, +.Pa /dev/mms0 +for a microsoft-style bus mouse and +.Pa /dev/psm0 +for a PS/2 mouse. +.It if Ta Physical interface : serial, bus, inport or PS/2. +.It type Ta Protocol type : one of the type supported by the +.Pa -t +option. +.It model Ta Mouse model. The +.Pa moused +command may not always be able to identify the model. +.It all Ta All of the above options. Print device name, physical interface, type +and model in this order, in one line. +.El +.It Fl m Ar mousetype +Precise the model of the mouse. For example, +.Pa IntelliMouse +can be specified for a PS/2 mouse with a wheel. +.It Fl M Ar N=M +Assign the physical button +.Pa N +to the logical button +.Pa M. +You may specify as many instances of this option as you like. More than one +physical button may be assigned to a logical button at the same time. In this +case the logical button will be down, if either of the assigned physical buttons +is held down. Do not put space around `='. +.It Fl w Ar N +Make the physical button +.Ar N +act as the wheel mode button. +While this button is pressed, X and Y axis movement is reported to be zero +and the Y axis movement is mapped to Z axis. +You may further map the Z axis movement to virtual buttons by the +.Fl z +option below. +.It Fl z Ar target +Map Z axis (roller/wheel) movement to another axis or to virtual buttons. +Valid +.Ar target +maybe: +.Bl -tag -compact -width x__ +.It Ar x +.It Ar y +X or Y axis movement will be reported when the Z axis movement is detected. +.It Ar N +Report the virtual buttons +.Ar N +and +.Ar N+1 +down events respectively when negative and positive Z axis movement +is detected. +There doesn't need to be physical buttons +.Ar N +and +.Ar N+1 . +Note that mapping to logical buttons is carried out after mapping +from the Z axis movement to the virtual buttons is done. +.El +.It Fl p Ar port +Use the device +.Pa port +to communicate with the mouse. +.Pa port +must be one of +.Pa /dev/cua0[0-3] +for a serial mouse, +.Pa /dev/lms0 +for a logitech-bus style mouse, +.Pa /dev/mms0 +for a microsoft-bus style mouse or +.Pa /dev/psm0 +for a PS/2 mouse. +.It Fl r Ar resolution +Set the resolution of the device; in Dots Per Inch, or +.Pa low +, +.Pa medium-low +, +.Pa medium-high +or +.Pa high. +This option may not be supported by all the devices. +.It Fl s +Select a baudrate of 9600 for the serial line. Not all serial mice support this +option. +.It Fl t Ar type +Specify the protocol type of the mouse attached to the port. You may explicitly +specify a type listed below or use +.Pa auto +to let the +.Pa moused +command to automatically select an appropriate protocol for the given mouse. If +you entirely ommit this option in the command line, +.Pa auto +is assumed. Under normal circumstances, you need to use this option only if the +.Pa moused +command is unable to detect the protocol automatically. + +Note that if a protocol type is specified with this option, the +.Pa -P +option above is implied and Plug and Play COM device enumeration procedure will be disabled. + +Also note that if your mouse is attached to the PS/2 mouse port, +you should always choose +.Pa auto +or +.Pa ps/2 +, regardless of the brand and model of the mouse. Likewise, if your mouse is +attached to the bus mouse port, choose +.Pa auto +or +.Pa busmouse +. Serial mouse protocols will not work with these mice. + +Valid protocol type for this option are the following: + +For serial mice: +.Bl -tag -width thinkingmouse +.It microsoft Ta Microsoft serial mouse protocol. Most 2-button serial mice use this protocol. +.It intellimouse Ta Microsoft IntelliMouse protocol. Genius NetMouse, ASCII Mie +Mouse, Logitech MouseMan+ and FirstMouse+ use this protocol too. Other mice +with a roller/wheel may be compatible with this protocol. +.It mousesystems Ta MouseSystems 5-byte protocol. 3-button mice may use this +protocol. +.It mmseries Ta MM Series mouse protocol. +.It logitech Ta Logitech mouse protocol. Note that this is for old Logitech +models. +.Pa mouseman +or +.Pa intellimouse +should be specified for newer models. +.It mouseman Ta Logitech MouseMan and TrackMan protocol. Some 3-button mice +may be compatible with this protocol. Note that MouseMan+ and FirstMouse+ use +.Pa intellimouse +protocol rather than this one. +.It glidepoint Ta ALPS GlidePoint protocol. +.It thinkingmouse Ta Kensington ThinkingMouse protocol. +.It mmhitab Ta Hitachi tablet protocol. +.El + +For the Logitech-style and Microsoft-style bus mouse: +.Bl -tag -indent +.It busmouse Ta This is the only protocol type available for the bus style +mice and should be specified for any bus style mice, regardless of the brand. +.El + +For the PS/2 mouse: +.Bl -tag -indent +.It ps/2 Ta This is the only protocol type available for the PS/2 mouse and +should be specified for any PS/2 mice, regardless of the brand. + +.Sh HISTORY +This moused daemon is a slightly modified version of the moused daemon from the +FreeBSD project, written by Michael Smith <msmith@FreeBSD.org>. Both inherits of +code from the Xfree Project. diff --git a/usr.sbin/moused/moused.c b/usr.sbin/moused/moused.c new file mode 100644 index 00000000000..8974e980319 --- /dev/null +++ b/usr.sbin/moused/moused.c @@ -0,0 +1,2035 @@ +/* + * Copyright (c) 2000 Jean-Baptiste Marchand, Julien Montagne and Jerome Verdon + * + * Copyright (c) 1998 by Kazutaka Yokota + * + * Copyright (c) 1995 Michael Smith + * + * Copyright (c) 1993 by David Dawes <dawes@xfree86.org> + * + * Copyright (c) 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * + * All rights reserved. + * + * Most of this code was taken from the FreeBSD moused daemon, written by + * Michael Smith. The FreeBSD moused daemon already contained code from the + * Xfree Project, written by David Dawes and Thomas Roell and Kazutaka Yokota. + * + * Adaptation to OpenBSD was done by Jean-Baptiste Marchand, Julien Montagne + * and Jerome Verdon. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * David Dawes, Jean-Baptiste Marchand, Julien Montagne, Thomas Roell, + * Michael Smith, Jerome Verdon and Kazutaka Yokota. + * 4. The name authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/tty.h> +#include <machine/pcvt_ioctl.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <varargs.h> + +#include "moused.h" + +extern char *optarg; +extern int optind; +extern char *__progname; + +int debug = 0; +int nodaemon = FALSE; +int background = FALSE; +int identify = ID_NONE; +char *pidfile = "/var/run/moused.pid"; +static jmp_buf env; + +/* + * Most of the structures are from the Xfree Project + */ + +/* Buttons status (for multiple click detection) */ + +static struct { + int count; /* 0: up, 1: single click, 2: double click,... */ + struct timeval tv; /* timestamp on the last `up' event */ +} buttonstate[MOUSE_MAXBUTTON]; + +/* Mouse physical interfaces */ + +static symtab_t mouse_ifs[] = { + { "serial", MOUSE_IF_SERIAL }, + { "bus", MOUSE_IF_BUS }, + { "inport", MOUSE_IF_INPORT }, + { "ps/2", MOUSE_IF_PS2 }, + { "usb", MOUSE_IF_USB }, + { NULL, MOUSE_IF_UNKNOWN } +}; + +/* Mouse model names */ + +static symtab_t mouse_models[] = { + { "NetScroll", MOUSE_MODEL_NETSCROLL }, + { "NetMouse", MOUSE_MODEL_NET }, + { "GlidePoint", MOUSE_MODEL_GLIDEPOINT }, + { "ThinkingMouse", MOUSE_MODEL_THINK }, + { "IntelliMouse", MOUSE_MODEL_INTELLI }, + { "EasyScroll", MOUSE_MODEL_EASYSCROLL }, + { "MouseMan+", MOUSE_MODEL_MOUSEMANPLUS }, + { "Kidspad", MOUSE_MODEL_KIDSPAD }, + { "VersaPad", MOUSE_MODEL_VERSAPAD }, + { "generic", MOUSE_MODEL_GENERIC }, + { NULL, MOUSE_MODEL_UNKNOWN }, +}; + +/* + * Cflags of each mouse protocol, ordered by P_XXX + */ + +static unsigned short mousecflags[] = +{ + (CS7 | CREAD | CLOCAL | HUPCL ), /* MicroSoft */ + (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems */ + (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* Logitech */ + (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ), /* MMSeries */ + (CS7 | CREAD | CLOCAL | HUPCL ), /* MouseMan */ + 0, /* Bus */ + 0, /* InPort */ + 0, /* PS/2 */ + (CS8 | CREAD | CLOCAL | HUPCL ), /* MM HitTablet */ + (CS7 | CREAD | CLOCAL | HUPCL ), /* GlidePoint */ + (CS7 | CREAD | CLOCAL | HUPCL ), /* IntelliMouse */ + (CS7 | CREAD | CLOCAL | HUPCL ), /* Thinking Mouse */ +}; + +/* Array ordered by P_XXX giving protocol properties */ + +static unsigned char proto[][7] = { + /* hd_mask hd_id dp_mask dp_id bytes b4_mask b4_id */ + { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* MicroSoft */ + { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* MouseSystems */ + { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* Logitech */ + { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MMSeries */ + { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* MouseMan */ + { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* Bus */ + { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* InPort */ + { 0xc0, 0x00, 0x00, 0x00, 3, 0x00, 0xff }, /* PS/2 mouse */ + { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MM HitTablet */ + { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* GlidePoint */ + { 0x40, 0x40, 0x40, 0x00, 3, ~0x3f, 0x00 }, /* IntelliMouse */ + { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* ThinkingMouse */ +}; + +/* + * array ordered by P_XXX (mouse protocols) giving the protocol corresponding + * to the name of a mouse + */ + +static char *mouse_names[] = { + "microsoft", + "mousesystems", + "logitech", + "mmseries", + "mouseman", + "busmouse", + "inportmouse", + "ps/2", + "mmhitab", + "glidepoint", + "intellimouse", + "thinkingmouse", + NULL +}; + +/* protocol currently used */ + +static unsigned char cur_proto[7]; + +mouse_t mouse = { + flags : 0, + portname : NULL, + proto : P_UNKNOWN, + baudrate : 1200, + old_baudrate : 1200, + rate : 0, + resolution : MOUSE_RES_UNKNOWN, + zmap: 0, + wmode: 0, + mfd : -1, + clickthreshold : 500, /* 0.5 sec */ +}; + +/* PnP EISA/product IDs */ +static symtab_t pnpprod[] = { + { "KML0001",P_THINKING,MOUSE_MODEL_THINK}, /* Kensignton ThinkingMouse */ + { "MSH0001",P_IMSERIAL,MOUSE_MODEL_INTELLI},/* MS IntelliMouse */ + { "MSH0004",P_IMSERIAL,MOUSE_MODEL_INTELLI},/* MS IntelliMouse TrackBall */ + { "KYEEZ00",P_MS,MOUSE_MODEL_GENERIC}, /* Genius EZScroll */ + { "KYE0001",P_MS,MOUSE_MODEL_GENERIC}, /* Genius PnP Mouse */ + { "KYE0003",P_IMSERIAL,MOUSE_MODEL_NET},/* Genius NetMouse */ + { "LGI800C",P_IMSERIAL,MOUSE_MODEL_MOUSEMANPLUS}, /* Logitech MouseMan (4 button model) */ + { "LGI8050",P_IMSERIAL,MOUSE_MODEL_MOUSEMANPLUS}, /* Logitech MouseMan+ */ + { "LGI8051",P_IMSERIAL,MOUSE_MODEL_MOUSEMANPLUS}, /* Logitech FirstMouse+ */ + { "LGI8001",P_LOGIMAN,MOUSE_MODEL_GENERIC}, /* Logitech serial */ + + { "PNP0F00",P_BM,MOUSE_MODEL_GENERIC }, /* MS bus */ + { "PNP0F01",P_MS,MOUSE_MODEL_GENERIC }, /* MS serial */ + { "PNP0F02",P_BM,MOUSE_MODEL_GENERIC }, /* MS InPort */ + { "PNP0F03",P_PS2,MOUSE_MODEL_GENERIC }, /* MS PS/2 */ + /* + * EzScroll returns PNP0F04 in the compatible device field; but it + * doesn't look compatible... XXX + */ + { "PNP0F04",P_MSC,MOUSE_MODEL_GENERIC }, /* MouseSystems */ + { "PNP0F05",P_MSC,MOUSE_MODEL_GENERIC }, /* MouseSystems */ + { "PNP0F08",P_LOGIMAN,MOUSE_MODEL_GENERIC },/* Logitech serial */ + { "PNP0F09",P_MS ,MOUSE_MODEL_GENERIC }, /* MS BallPoint serial */ + { "PNP0F0A",P_MS ,MOUSE_MODEL_GENERIC }, /* MS PnP serial */ + { "PNP0F0B",P_MS ,MOUSE_MODEL_GENERIC }, /* MS PnP BallPoint serial */ + { "PNP0F0C",P_MS ,MOUSE_MODEL_GENERIC }, /* MS serial comatible */ + { "PNP0F0D",P_BM,MOUSE_MODEL_GENERIC }, /* MS InPort comatible */ + { "PNP0F0E",P_PS2,MOUSE_MODEL_GENERIC }, /* MS PS/2 comatible */ + { "PNP0F0F",P_MS,MOUSE_MODEL_GENERIC }, /* MS BallPoint comatible */ + { "PNP0F11",P_BM,MOUSE_MODEL_GENERIC }, /* MS bus comatible */ + { "PNP0F12",P_PS2,MOUSE_MODEL_GENERIC }, /* Logitech PS/2 */ + { "PNP0F13",P_PS2,MOUSE_MODEL_GENERIC }, /* PS/2 */ + { "PNP0F15",P_BM,MOUSE_MODEL_GENERIC }, /* Logitech bus */ + { "PNP0F17",P_LOGIMAN,MOUSE_MODEL_GENERIC },/* Logitech serial compat */ + { "PNP0F18",P_BM,MOUSE_MODEL_GENERIC }, /* Logitech bus compatible */ + { "PNP0F19",P_PS2,MOUSE_MODEL_GENERIC }, /* Logitech PS/2 compatible */ + { NULL, -1 }, +}; + +/* + * XXX Functions + */ + +static char * +skipspace(char *s) +{ + while(isspace(*s)) + ++s; + return s; +} + +static char * +gettokenname(symtab_t *tab, int val) +{ + int i; + + for (i = 0; tab[i].name != NULL; ++i) { + if (tab[i].val == val) + return tab[i].name; + } + return NULL; +} + +static symtab_t * +gettoken(symtab_t *tab, char *s, int len) +{ + int i; + + for (i = 0; tab[i].name != NULL; ++i) { + if (strncmp(tab[i].name, s, len) == 0) + break; + } + return &tab[i]; +} + +static char * +mouse_name(int type) +{ + return ((type == P_UNKNOWN) + || (type > sizeof(mouse_names)/sizeof(mouse_names[0]) - 1)) + ? "unknown" : mouse_names[type]; +} + +static char * +mouse_model(int model) +{ + char *s; + + s = gettokenname(mouse_models, model); + return (s == NULL) ? "unknown" : s; +} + +/* Fills the hardware info in the main structure */ + +static void +mouse_fill_hwinfo(void) +{ + /* default settings */ + + mouse.hw.iftype = MOUSE_IF_UNKNOWN; + mouse.hw.type = MOUSE_TYPE_MOUSE; + if (mouse.hw.model == 0) { /* If no type has been given */ + mouse.hw.model = MOUSE_MODEL_GENERIC; + } + + if (strcmp(mouse.portname,PMS_DEV) == 0) { + mouse.hw.iftype = MOUSE_IF_PS2; + } + + if (strcmp(mouse.portname,LMS_DEV) == 0) { + mouse.hw.iftype = MOUSE_IF_BUS; + } + + if (strcmp(mouse.portname,MMS_DEV) == 0) { + mouse.hw.iftype = MOUSE_IF_INPORT; + } + + /* serial device begins with /dev/cua0 (9 caracters) */ + if (strncmp(mouse.portname,SERIAL_DEV,9) == 0) { + mouse.hw.iftype = MOUSE_IF_SERIAL; + } + +} + +/* Fills the mode structure in the main structure */ + +static void +mouse_fill_mousemode(void) +{ + /* default settings */ + + mouse.mode.protocol = P_UNKNOWN; + mouse.mode.rate = -1; /* unknown */ + mouse.mode.resolution = MOUSE_RES_UNKNOWN; + mouse.mode.accelfactor = 0; /* no accel */ + + if (strcmp(mouse.portname,PMS_DEV) == 0) { + mouse.mode.protocol = P_PS2; + if (mouse.hw.model == MOUSE_MODEL_INTELLI) + mouse.mode.packetsize = MOUSE_INTELLI_PACKETSIZE; + else + mouse.mode.packetsize = MOUSE_PS2_PACKETSIZE; + mouse.mode.syncmask[0] = MOUSE_PS2_SYNCMASK; + mouse.mode.syncmask[1] = MOUSE_PS2_SYNC; + } + + if (strcmp(mouse.portname,LMS_DEV) == 0) { + mouse.mode.protocol = P_BM; + mouse.mode.packetsize = MOUSE_MSC_PACKETSIZE; + mouse.mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + mouse.mode.syncmask[1] = MOUSE_MSC_SYNC; + } + + if (strcmp(mouse.portname,MMS_DEV) == 0) { + mouse.hw.iftype = P_INPORT; + mouse.mode.packetsize = MOUSE_MSC_PACKETSIZE; + mouse.mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + mouse.mode.syncmask[1] = MOUSE_MSC_SYNC; + } + if (mouse.proto != -1) + mouse.mode.protocol = mouse.proto; +} + +static void +hup(int sig) +{ + longjmp(env, 1); +} + +static void +cleanup(int sig) +{ + exit(0); +} + +/* + * Begin of functions from the Xfree Project + */ + +/* + * Functions below come from the Xfree Project and are derived from a two files + * of Xfree86 3.3.6 with the following CVS tags : + $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.21.2.24 + 1999/12/11 19:00:42 hohndel Exp $ + and + $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_PnPMouse.c,v 1.1.2.6 + 1999/07/29 09:22:51 hohndel Exp $ + */ + +void SetMouseSpeed(int old, int new, unsigned int cflag) +{ + struct termios tty; + char *c; + + if (mouse.hw.iftype != MOUSE_IF_SERIAL) + return; + + if (tcgetattr(mouse.mfd, &tty) < 0) + { + ErrorF("Warning: %s unable to get status of mouse fd (%s)\n", + mouse.portname, strerror(errno)); + return; + } + + /* this will query the initial baudrate only once */ + if (mouse.old_baudrate < 0) { + switch (cfgetispeed(&tty)) + { + case B9600: + mouse.old_baudrate = 9600; + break; + case B4800: + mouse.old_baudrate = 4800; + break; + case B2400: + mouse.old_baudrate = 2400; + break; + case B1200: + default: + mouse.old_baudrate = 1200; + break; + } + } + + tty.c_iflag = IGNBRK | IGNPAR; + tty.c_oflag = 0; + tty.c_lflag = 0; + tty.c_cflag = (tcflag_t)cflag; + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 1; + + switch (old) + { + case 9600: + cfsetispeed(&tty, B9600); + cfsetospeed(&tty, B9600); + break; + case 4800: + cfsetispeed(&tty, B4800); + cfsetospeed(&tty, B4800); + break; + case 2400: + cfsetispeed(&tty, B2400); + cfsetospeed(&tty, B2400); + break; + case 1200: + default: + cfsetispeed(&tty, B1200); + cfsetospeed(&tty, B1200); + } + + if (tcsetattr(mouse.mfd, TCSADRAIN, &tty) < 0) { + printf("Unable to get mouse status. Exiting...\n"); + exit(1); + } + + switch (new) + { + case 9600: + c = "*q"; + cfsetispeed(&tty, B9600); + cfsetospeed(&tty, B9600); + break; + case 4800: + c = "*p"; + cfsetispeed(&tty, B4800); + cfsetospeed(&tty, B4800); + break; + case 2400: + c = "*o"; + cfsetispeed(&tty, B2400); + cfsetospeed(&tty, B2400); + break; + case 1200: + default: + c = "*n"; + cfsetispeed(&tty, B1200); + cfsetospeed(&tty, B1200); + } + + if (mouse.proto == P_LOGIMAN || mouse.proto == P_LOGI) + { + if (write(mouse.mfd, c, 2) != 2) { + printf("Unable to write to mouse. Exiting...\n"); + exit(1); + } + } + usleep(100000); + + if (tcsetattr(mouse.mfd, TCSADRAIN, &tty) < 0) { + printf("Unable to get mouse status. Exiting...\n"); + exit(1); + } +} + +int +FlushInput(int fd) +{ + fd_set fds; + struct timeval timeout; + char c[4]; + + if (tcflush(fd, TCIFLUSH) == 0) + return 0; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(fd, &fds); + while (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) { + read(fd, &c, sizeof(c)); + FD_ZERO(&fds); + FD_SET(fd, &fds); + } + return 0; +} + +/* + * Try to elicit a PnP ID as described in + * Microsoft, Hayes: "Plug and Play External COM Device Specification, + * rev 1.00", 1995. + * + * The routine does not fully implement the COM Enumerator as par Section + * 2.1 of the document. In particular, we don't have idle state in which + * the driver software monitors the com port for dynamic connection or + * removal of a device at the port, because `moused' simply quits if no + * device is found. + * + * In addition, as PnP COM device enumeration procedure slightly has + * changed since its first publication, devices which follow earlier + * revisions of the above spec. may fail to respond if the rev 1.0 + * procedure is used. XXX + */ + +static int +pnpgets(int mouse_fd, char *buf) +{ + struct timeval timeout; + fd_set fds; + int i; + char c; + +#if 0 + /* + * This is the procedure described in rev 1.0 of PnP COM device spec. + * Unfortunately, some devices which comform to earlier revisions of + * the spec gets confused and do not return the ID string... + */ + + /* port initialization (2.1.2) */ + ioctl(mouse_fd, TIOCMGET, &i); + i |= TIOCM_DTR; /* DTR = 1 */ + i &= ~TIOCM_RTS; /* RTS = 0 */ + ioctl(mouse_fd, TIOCMSET, &i); + usleep(200000); + if ((ioctl(mouse_fd, TIOCMGET, &i) == -1) || ((i & TIOCM_DSR) == 0)) + goto disconnect_idle; + + /* port setup, 1st phase (2.1.3) */ + SetMouseSpeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL)); + i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ + ioctl(mouse_fd, TIOCMBIC, &i); + usleep(200000); + i = TIOCM_DTR; /* DTR = 1, RTS = 0 */ + ioctl(mouse_fd, TIOCMBIS, &i); + usleep(200000); + + /* wait for response, 1st phase (2.1.4) */ + FlushInput(mouse_fd); + i = TIOCM_RTS; /* DTR = 1, RTS = 1 */ + ioctl(mouse_fd, TIOCMBIS, &i); + + /* try to read something */ + FD_ZERO(&fds); + FD_SET(mouse_fd, &fds); + timeout.tv_sec = 0; + timeout.tv_usec = 200000; + if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0) { + + /* port setup, 2nd phase (2.1.5) */ + i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ + ioctl(mouse_fd, TIOCMBIC, &i); + usleep(200000); + + /* wait for respose, 2nd phase (2.1.6) */ + FlushInput(mouse_fd); + i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ + ioctl(mouse_fd, TIOCMBIS, &i); + + /* try to read something */ + FD_ZERO(&fds); + FD_SET(mouse_fd, &fds); + timeout.tv_sec = 0; + timeout.tv_usec = 200000; + if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0) + goto connect_idle; + } +#else + + /* + * This is a simplified procedure; it simply toggles RTS. + */ + SetMouseSpeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL)); + + ioctl(mouse_fd, TIOCMGET, &i); + i |= TIOCM_DTR; /* DTR = 1 */ + i &= ~TIOCM_RTS; /* RTS = 0 */ + ioctl(mouse_fd, TIOCMSET, &i); + usleep(200000); + + /* wait for response */ + FlushInput(mouse_fd); + i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ + ioctl(mouse_fd, TIOCMBIS, &i); + + /* try to read something */ + FD_ZERO(&fds); + FD_SET(mouse_fd, &fds); + timeout.tv_sec = 0; + timeout.tv_usec = 200000; + if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0) + goto connect_idle; +#endif + + /* collect PnP COM device ID (2.1.7) */ + i = 0; + usleep(200000); /* the mouse must send `Begin ID' within 200msec */ + while (read(mouse_fd, &c, 1) == 1) { + /* we may see "M", or "M3..." before `Begin ID' */ + if ((c == 0x08) || (c == 0x28)) { /* Begin ID */ + buf[i++] = c; + break; + } + } + if (i <= 0) { + /* we haven't seen `Begin ID' in time... */ + goto connect_idle; + } + + ++c; /* make it `End ID' */ + for (;;) { + FD_ZERO(&fds); + FD_SET(mouse_fd, &fds); + timeout.tv_sec = 0; + timeout.tv_usec = 200000; + if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0) + break; + + read(mouse_fd, &buf[i], 1); + if (buf[i++] == c) /* End ID */ + break; + if (i >= 256) + break; + } + if (buf[i - 1] != c) + goto connect_idle; + return i; + +#if 0 + /* + * According to PnP spec, we should set DTR = 1 and RTS = 0 while + * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed, + * assuming there is something at the port even if it didn't + * respond to the PnP enumeration procedure. + */ +disconnect_idle: + i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ + ioctl(mouse_fd, TIOCMBIS, &i); +#endif + +connect_idle: + return 0; +} + +/* + * pnpparse : parse a PnP string ID + */ + +static int +pnpparse(pnpid_t *id, char *buf, int len) +{ + char s[3]; + int offset; + int sum = 0; + int i, j; + + id->revision = 0; + id->eisaid = NULL; + id->serial = NULL; + id->class = NULL; + id->compat = NULL; + id->description = NULL; + id->neisaid = 0; + id->nserial = 0; + id->nclass = 0; + id->ncompat = 0; + id->ndescription = 0; + + offset = 0x28 - buf[0]; + + /* calculate checksum */ + for (i = 0; i < len - 3; ++i) { + sum += buf[i]; + buf[i] += offset; + } + sum += buf[len - 1]; + for (; i < len; ++i) + buf[i] += offset; + ErrorF("Mouse: PnP ID string: '%*.*s'\n", len, len, buf); + + /* revision */ + buf[1] -= offset; + buf[2] -= offset; + id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f); + ErrorF("Mouse: PnP rev %d.%02d\n", id->revision / 100, id->revision % 100); + + /* EISA vender and product ID */ + id->eisaid = &buf[3]; + id->neisaid = 7; + + /* option strings */ + i = 10; + if (buf[i] == '\\') { + /* device serial # */ + for (j = ++i; i < len; ++i) { + if (buf[i] == '\\') + break; + } + if (i >= len) + i -= 3; + if (i - j == 8) { + id->serial = &buf[j]; + id->nserial = 8; + } + } + if (buf[i] == '\\') { + /* PnP class */ + for (j = ++i; i < len; ++i) { + if (buf[i] == '\\') + break; + } + if (i >= len) + i -= 3; + if (i > j + 1) { + id->class = &buf[j]; + id->nclass = i - j; + } + } + if (buf[i] == '\\') { + /* compatible driver */ + for (j = ++i; i < len; ++i) { + if (buf[i] == '\\') + break; + } + /* + * PnP COM spec prior to v0.96 allowed '*' in this field, + * it's not allowed now; just ignore it. + */ + if (buf[j] == '*') + ++j; + if (i >= len) + i -= 3; + if (i > j + 1) { + id->compat = &buf[j]; + id->ncompat = i - j; + } + } + if (buf[i] == '\\') { + /* product description */ + for (j = ++i; i < len; ++i) { + if (buf[i] == ';') + break; + } + if (i >= len) + i -= 3; + if (i > j + 1) { + id->description = &buf[j]; + id->ndescription = i - j; + } + } + + /* checksum exists if there are any optional fields */ + if ((id->nserial > 0) || (id->nclass > 0) + || (id->ncompat > 0) || (id->ndescription > 0)) { +#if 0 + ErrorF("Mouse: PnP checksum: 0x%02X\n", sum); +#endif + sprintf(s, "%02X", sum & 0x0ff); + if (strncmp(s, &buf[len - 3], 2) != 0) { +#if 0 + /* + * Checksum error!! + * I found some mice do not comply with the PnP COM device + * spec regarding checksum... XXX + */ + return FALSE; +#endif + } + } + + return 1; +} + +/* + * pnpproto : return the prototype used, based on the PnP ID string + */ + +static symtab_t * +pnpproto(pnpid_t *id) +{ + symtab_t *t; + int i, j; + + if (id->nclass > 0) + if (strncmp(id->class, "MOUSE", id->nclass) != 0) + /* this is not a mouse! */ + return NULL; + + if (id->neisaid > 0) { + t = gettoken(pnpprod, id->eisaid, id->neisaid); + if (t->val != -1) + return t; + } + + /* + * The 'Compatible drivers' field may contain more than one + * ID separated by ','. + */ + if (id->ncompat <= 0) + return NULL; + for (i = 0; i < id->ncompat; ++i) { + for (j = i; id->compat[i] != ','; ++i) + if (i >= id->ncompat) + break; + if (i > j) { + t = gettoken(pnpprod, id->compat + j, i - j); + if (t->val != -1) + return t; + } + } + + return NULL; +} + +/* mouse_if : returns a string giving the name of the physical interface */ + +static char * +mouse_if(int iftype) +{ + char *s; + + s = gettokenname(mouse_ifs, iftype); + return (s == NULL) ? "unknown" : s; +} + +/* mouse_init : inits the mouse by writing appropriate sequences */ + +static void +mouse_init(void) +{ + fd_set fds; + char *s; + char c; + int i; + + /** + ** This comment is a little out of context here, but it contains + ** some useful information... + ******************************************************************** + ** + ** The following lines take care of the Logitech MouseMan protocols. + ** + ** NOTE: There are different versions of both MouseMan and TrackMan! + ** Hence I add another protocol P_LOGIMAN, which the user can + ** specify as MouseMan in his XF86Config file. This entry was + ** formerly handled as a special case of P_MS. However, people + ** who don't have the middle button problem, can still specify + ** Microsoft and use P_MS. + ** + ** By default, these mice should use a 3 byte Microsoft protocol + ** plus a 4th byte for the middle button. However, the mouse might + ** have switched to a different protocol before we use it, so I send + ** the proper sequence just in case. + ** + ** NOTE: - all commands to (at least the European) MouseMan have to + ** be sent at 1200 Baud. + ** - each command starts with a '*'. + ** - whenever the MouseMan receives a '*', it will switch back + ** to 1200 Baud. Hence I have to select the desired protocol + ** first, then select the baud rate. + ** + ** The protocols supported by the (European) MouseMan are: + ** - 5 byte packed binary protocol, as with the Mouse Systems + ** mouse. Selected by sequence "*U". + ** - 2 button 3 byte MicroSoft compatible protocol. Selected + ** by sequence "*V". + ** - 3 button 3+1 byte MicroSoft compatible protocol (default). + ** Selected by sequence "*X". + ** + ** The following baud rates are supported: + ** - 1200 Baud (default). Selected by sequence "*n". + ** - 9600 Baud. Selected by sequence "*q". + ** + ** Selecting a sample rate is no longer supported with the MouseMan! + ** Some additional lines in xf86Config.c take care of ill configured + ** baud rates and sample rates. (The user will get an error.) + */ + + switch (mouse.proto) { + + case P_LOGI: + /* + * The baud rate selection command must be sent at the current + * baud rate; try all likely settings + */ + SetMouseSpeed(9600, mouse.baudrate, mousecflags[mouse.proto]); + SetMouseSpeed(4800, mouse.baudrate, mousecflags[mouse.proto]); + SetMouseSpeed(2400, mouse.baudrate, mousecflags[mouse.proto]); + // SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]); + /* select MM series data format */ + write(mouse.mfd, "S", 1); + SetMouseSpeed(mouse.baudrate, mouse.baudrate, + mousecflags[P_MM]); + /* select report rate/frequency */ + if (mouse.rate <= 0) write(mouse.mfd, "O", 1); + else if (mouse.rate <= 15) write(mouse.mfd, "J", 1); + else if (mouse.rate <= 27) write(mouse.mfd, "K", 1); + else if (mouse.rate <= 42) write(mouse.mfd, "L", 1); + else if (mouse.rate <= 60) write(mouse.mfd, "R", 1); + else if (mouse.rate <= 85) write(mouse.mfd, "M", 1); + else if (mouse.rate <= 125) write(mouse.mfd, "Q", 1); + else write(mouse.mfd, "N", 1); + break; + + case P_LOGIMAN: + /* The command must always be sent at 1200 baud */ + SetMouseSpeed(1200, 1200, mousecflags[mouse.proto]); + write(mouse.mfd, "*X", 2); + SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]); + break; + + case P_MMHIT: + SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]); + + /* + * Initialize Hitachi PUMA Plus - Model 1212E to desired settings. + * The tablet must be configured to be in MM mode, NO parity, + * Binary Format. xf86Info.sampleRate controls the sensativity + * of the tablet. We only use this tablet for it's 4-button puck + * so we don't run in "Absolute Mode" + */ + write(mouse.mfd, "z8", 2); /* Set Parity = "NONE" */ + usleep(50000); + write(mouse.mfd, "zb", 2); /* Set Format = "Binary" */ + usleep(50000); + write(mouse.mfd, "@", 1); /* Set Report Mode = "Stream" */ + usleep(50000); + write(mouse.mfd, "R", 1); /* Set Output Rate = "45 rps" */ + usleep(50000); + write(mouse.mfd, "I\x20", 2); /* Set Incrememtal Mode "20" */ + usleep(50000); + write(mouse.mfd, "E", 1); /* Set Data Type = "Relative */ + usleep(50000); + + /* Resolution is in 'lines per inch' on the Hitachi tablet */ + if (mouse.resolution == MOUSE_RES_LOW) c = 'g'; + else if (mouse.resolution == MOUSE_RES_MEDIUMLOW) c = 'e'; + else if (mouse.resolution == MOUSE_RES_MEDIUMHIGH) c = 'h'; + else if (mouse.resolution == MOUSE_RES_HIGH) c = 'd'; + else if (mouse.resolution <= 40) c = 'g'; + else if (mouse.resolution <= 100) c = 'd'; + else if (mouse.resolution <= 200) c = 'e'; + else if (mouse.resolution <= 500) c = 'h'; + else if (mouse.resolution <= 1000) c = 'j'; + else c = 'd'; + write(mouse.mfd, &c, 1); + usleep(50000); + + write(mouse.mfd, "\021", 1); /* Resume DATA output */ + break; + + case P_THINKING: + SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]); + /* the PnP ID string may be sent again, discard it */ + usleep(200000); + i = FREAD; + ioctl(mouse.mfd, TIOCFLUSH, &i); + /* send the command to initialize the beast */ + for (s = "E5E5"; *s; ++s) { + write(mouse.mfd, s, 1); + FD_ZERO(&fds); + FD_SET(mouse.mfd, &fds); + if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0) + break; + read(mouse.mfd, &c, 1); + debug("%c", c); + if (c != *s) + break; + } + break; + + case P_MSC: + SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]); + if (mouse.flags & ClearDTR) { + i = TIOCM_DTR; + ioctl(mouse.mfd, TIOCMBIC, &i); + } + if (mouse.flags & ClearRTS) { + i = TIOCM_RTS; + ioctl(mouse.mfd, TIOCMBIC, &i); + } + break; + + case P_BM: + case P_PS2: + if (mouse.rate >= 0) + mouse.mode.rate = mouse.rate; + if (mouse.resolution != MOUSE_RES_UNKNOWN) + mouse.mode.resolution = mouse.resolution; + /* XXX Rate and resolution are not set ! (pms driver can't) XXX */ + break; + + default: + SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]); + break; + } +} + + + + +/* mouse_identify : identify the protocol used by the mouse */ + +static int +mouse_identify(void) +{ + char pnpbuf[256]; /* PnP identifier string may be up to 256 bytes long */ + pnpid_t pnpid; + symtab_t *t; + int len; + + /* + * Note : We don't have the ioctl that exist in FreeBSD so we simulate + * them with the functions mouse_fill_hwinfo() and mouse_fill_mousemode() + */ + mouse_fill_hwinfo(); + mouse_fill_mousemode(); + + if (mouse.proto != P_UNKNOWN) + bcopy(proto[mouse.proto], cur_proto, sizeof(cur_proto)); + + if ((mouse.mode.protocol == P_UNKNOWN) + || (mouse.mode.protocol >= sizeof(proto)/sizeof(proto[0]))) { + logwarn("unknown mouse protocol (%d)", mouse.mode.protocol); + return P_UNKNOWN; + } else { + /* INPORT and BUS are the same... */ + if (mouse.mode.protocol == P_INPORT) + mouse.mode.protocol = P_BM; + if (mouse.mode.protocol != mouse.proto) { + /* Hmm, the driver doesn't agree with the user... */ + if (mouse.proto != P_UNKNOWN) + logwarn("mouse type mismatch (%s != %s), %s is assumed", + mouse_name(mouse.mode.protocol), + mouse_name(mouse.proto), + mouse_name(mouse.mode.protocol)); + mouse.proto = mouse.mode.protocol; + bcopy(proto[mouse.proto], cur_proto, sizeof(cur_proto)); + } + } + cur_proto[4] = mouse.mode.packetsize; + cur_proto[0] = mouse.mode.syncmask[0]; /* header byte bit mask */ + cur_proto[1] = mouse.mode.syncmask[1]; /* header bit pattern */ + + /* maybe this is an PnP mouse... */ + if (mouse.mode.protocol == P_UNKNOWN) { + + if (mouse.flags & NoPnP) + return mouse.proto; + if (((len = pnpgets(mouse.mfd,pnpbuf)) <= 0) + || !pnpparse(&pnpid, pnpbuf, len)) + return mouse.proto; + + debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'", + pnpid.neisaid, pnpid.neisaid, pnpid.eisaid, + pnpid.ncompat, pnpid.ncompat, pnpid.compat, + pnpid.ndescription, pnpid.ndescription, pnpid.description); + + /* we have a valid PnP serial device ID */ + mouse.hw.iftype = MOUSE_IF_SERIAL; + t = pnpproto(&pnpid); + if (t != NULL) { + mouse.mode.protocol = t->val; + mouse.hw.model = t->val2; + } else { + mouse.mode.protocol = P_UNKNOWN; + } + if (mouse.mode.protocol == P_INPORT) + mouse.mode.protocol = P_BM; + + /* make final adjustment */ + if (mouse.mode.protocol != P_UNKNOWN) { + if (mouse.mode.protocol != mouse.proto) { + /* Hmm, the device doesn't agree with the user... */ + if (mouse.proto != P_UNKNOWN) + logwarn("mouse type mismatch (%s != %s), %s is assumed", + mouse_name(mouse.mode.protocol), + mouse_name(mouse.proto), + mouse_name(mouse.mode.protocol)); + mouse.proto = mouse.mode.protocol; + bcopy(proto[mouse.proto], cur_proto, sizeof(cur_proto)); + } + } + } + + debug("proto params: %02x %02x %02x %02x %d %02x %02x", + cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3], + cur_proto[4], cur_proto[5], cur_proto[6]); + + return mouse.proto; +} + +/* mouse_protocol : decode bytes with the current mouse protocol */ + +static int +mouse_protocol(u_char rBuf, mousestatus_t *act) +{ + /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */ + static int butmapmss[4] = { /* Microsoft, MouseMan, GlidePoint, + IntelliMouse, Thinking Mouse */ + 0, + MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, + }; + static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint, + Thinking Mouse */ + 0, + MOUSE_BUTTON4DOWN, + MOUSE_BUTTON2DOWN, + MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN, + }; + /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */ + static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse, + MouseMan+ */ + 0, + MOUSE_BUTTON2DOWN, + MOUSE_BUTTON4DOWN, + MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN, + }; + /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */ + static int butmapmsc[8] = { /* MouseSystems, MMSeries, Logitech, + Bus, sysmouse */ + 0, + MOUSE_BUTTON3DOWN, + MOUSE_BUTTON2DOWN, + MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN + }; + /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */ + static int butmapps2[8] = { /* PS/2 */ + 0, + MOUSE_BUTTON1DOWN, + MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, + MOUSE_BUTTON2DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, + MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN + }; + /* for Hitachi tablet */ + static int butmaphit[8] = { /* MM HitTablet */ + 0, + MOUSE_BUTTON3DOWN, + MOUSE_BUTTON2DOWN, + MOUSE_BUTTON1DOWN, + MOUSE_BUTTON4DOWN, + MOUSE_BUTTON5DOWN, + MOUSE_BUTTON6DOWN, + MOUSE_BUTTON7DOWN, + }; + /* for PS/2 VersaPad */ + static int butmapversaps2[8] = { /* VersaPad */ + 0, + MOUSE_BUTTON3DOWN, + 0, + MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, + MOUSE_BUTTON1DOWN, + MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, + }; + static int pBufP = 0; + static unsigned char pBuf[8]; + static int prev_x, prev_y; + static int on = FALSE; + int x, y; + + debug("received char 0x%x",(int)rBuf); + + /* + * Hack for resyncing: We check here for a package that is: + * a) illegal (detected by wrong data-package header) + * b) invalid (0x80 == -128 and that might be wrong for MouseSystems) + * c) bad header-package + * + * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of + * -128 are allowed, but since they are very seldom we can easily + * use them as package-header with no button pressed. + * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore, + * 0x80 is not valid as a header byte. For a PS/2 mouse we skip + * checking data bytes. + * For resyncing a PS/2 mouse we require the two most significant + * bits in the header byte to be 0. These are the overflow bits, + * and in case of an overflow we actually lose sync. Overflows + * are very rare, however, and we quickly gain sync again after + * an overflow condition. This is the best we can do. (Actually, + * we could use bit 0x08 in the header byte for resyncing, since + * that bit is supposed to be always on, but nobody told + * Microsoft...) + */ + + if (pBufP != 0 && mouse.proto != P_PS2 && + ((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80)) + { + pBufP = 0; /* skip package */ + } + + if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1]) + return 0; + + /* is there an extra data byte? */ + if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1]) + { + /* + * Hack for Logitech MouseMan Mouse - Middle button + * + * Unfortunately this mouse has variable length packets: the standard + * Microsoft 3 byte packet plus an optional 4th byte whenever the + * middle button status changes. + * + * We have already processed the standard packet with the movement + * and button info. Now post an event message with the old status + * of the left and right buttons and the updated middle button. + */ + + /* + * Even worse, different MouseMen and TrackMen differ in the 4th + * byte: some will send 0x00/0x20, others 0x01/0x21, or even + * 0x02/0x22, so I have to strip off the lower bits. + * + * [JCH-96/01/21] + * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte" + * and it is activated by tapping the glidepad with the finger! 8^) + * We map it to bit bit3, and the reverse map in xf86Events just has + * to be extended so that it is identified as Button 4. The lower + * half of the reverse-map may remain unchanged. + */ + + /* + * [KY-97/08/03] + * Receive the fourth byte only when preceeding three bytes have + * been detected (pBufP >= cur_proto[4]). In the previous + * versions, the test was pBufP == 0; thus, we may have mistakingly + * received a byte even if we didn't see anything preceeding + * the byte. + */ + + if ((rBuf & cur_proto[5]) != cur_proto[6]) { + pBufP = 0; + return 0; + } + + switch (mouse.proto) { + + /* + * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse + * always send the fourth byte, whereas the fourth byte is + * optional for GlidePoint and ThinkingMouse. The fourth byte + * is also optional for MouseMan+ and FirstMouse+ in their + * native mode. It is always sent if they are in the IntelliMouse + * compatible mode. + */ + case P_IMSERIAL: /* IntelliMouse, NetMouse, Mie Mouse, + MouseMan+ */ + act->dx = act->dy = 0; + act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f); + act->obutton = act->button; + act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4] + | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN)); + break; + + default: + act->dx = act->dy = act->dz = 0; + act->obutton = act->button; + act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4] + | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN)); + break; + } + + act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0) + | (act->obutton ^ act->button); + pBufP = 0; + return act->flags; + } + + if (pBufP >= cur_proto[4]) + pBufP = 0; + pBuf[pBufP++] = rBuf; + if (pBufP != cur_proto[4]) + return 0; + + /* + * assembly full package + */ + + debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x", + cur_proto[4], + pBuf[0], pBuf[1], pBuf[2], pBuf[3], + pBuf[4], pBuf[5], pBuf[6], pBuf[7]); + + act->dz = 0; + act->obutton = act->button; + switch (mouse.proto) + { + case P_MS: /* Microsoft */ + case P_LOGIMAN: /* MouseMan/TrackMan */ + case P_GLIDEPOINT: /* GlidePoint */ + case P_THINKING: /* ThinkingMouse */ + case P_IMSERIAL: /* IntelliMouse, NetMouse, Mie Mouse, + MouseMan+ */ + act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN)) + | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4]; + act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F)); + act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F)); + break; + + case P_MSC: /* MouseSystems Corp */ + act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS]; + act->dx = (char)(pBuf[1]) + (char)(pBuf[3]); + act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4])); + break; + + case P_MMHIT: /* MM HitTablet */ + act->button = butmaphit[pBuf[0] & 0x07]; + act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1]; + act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2]; + break; + + case P_MM: /* MM Series */ + case P_LOGI: /* Logitech Mice */ + act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS]; + act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1]; + act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2]; + break; + + case P_BM: /* Bus */ + case P_INPORT: /* InPort */ + act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS]; + act->dx = (char)pBuf[1]; + act->dy = - (char)pBuf[2]; + break; + + case P_PS2: /* PS/2 */ + act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS]; + act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ? pBuf[1] - 256 : pBuf[1]; + act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ? -(pBuf[2] - 256) : -pBuf[2]; + /* + * Moused usually operates the psm driver at the operation level 1 + * which sends mouse data in P_SYSMOUSE protocol. + * The following code takes effect only when the user explicitly + * requets the level 2 at which wheel movement and additional button + * actions are encoded in model-dependent formats. At the level 0 + * the following code is no-op because the psm driver says the model + * is MOUSE_MODEL_GENERIC. + */ + switch (mouse.hw.model) { + case MOUSE_MODEL_INTELLI: + case MOUSE_MODEL_NET: + /* wheel data is in the fourth byte */ + act->dz = (char)pBuf[3]; + break; + case MOUSE_MODEL_MOUSEMANPLUS: + if (((pBuf[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC) + && (abs(act->dx) > 191) + && MOUSE_PS2PLUS_CHECKBITS(pBuf)) { + /* the extended data packet encodes button and wheel events */ + switch (MOUSE_PS2PLUS_PACKET_TYPE(pBuf)) { + case 1: + /* wheel data packet */ + act->dx = act->dy = 0; + if (pBuf[2] & 0x80) { + /* horizontal roller count - ignore it XXX*/ + } else { + /* vertical roller count */ + act->dz = (pBuf[2] & MOUSE_PS2PLUS_ZNEG) + ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f); + } + act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN) + ? MOUSE_BUTTON4DOWN : 0; + act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON5DOWN) + ? MOUSE_BUTTON5DOWN : 0; + break; + case 2: + /* this packet type is reserved, and currently ignored */ + /* FALL THROUGH */ + case 0: + /* device type packet - shouldn't happen */ + /* FALL THROUGH */ + default: + act->dx = act->dy = 0; + act->button = act->obutton; + debug("unknown PS2++ packet type %d: 0x%02x 0x%02x 0x%02x\n", + MOUSE_PS2PLUS_PACKET_TYPE(pBuf), + pBuf[0], pBuf[1], pBuf[2]); + break; + } + } else { + /* preserve button states */ + act->button |= act->obutton & MOUSE_EXTBUTTONS; + } + break; + case MOUSE_MODEL_GLIDEPOINT: + /* `tapping' action */ + act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; + break; + case MOUSE_MODEL_NETSCROLL: + /* three addtional bytes encode button and wheel events */ + act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN) + ? MOUSE_BUTTON4DOWN : 0; + act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4]; + break; + case MOUSE_MODEL_THINK: + /* the fourth button state in the first byte */ + act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0; + break; + case MOUSE_MODEL_VERSAPAD: + act->button = butmapversaps2[pBuf[0] & MOUSE_PS2VERSA_BUTTONS]; + act->button |= + (pBuf[0] & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0; + act->dx = act->dy = 0; + if (!(pBuf[0] & MOUSE_PS2VERSA_IN_USE)) { + on = FALSE; + break; + } + x = ((pBuf[4] << 8) & 0xf00) | pBuf[1]; + if (x & 0x800) + x -= 0x1000; + y = ((pBuf[4] << 4) & 0xf00) | pBuf[2]; + if (y & 0x800) + y -= 0x1000; + if (on) { + act->dx = prev_x - x; + act->dy = prev_y - y; + } else { + on = TRUE; + } + prev_x = x; + prev_y = y; + break; + case MOUSE_MODEL_GENERIC: + default: + break; + } + break; + + default: + return 0; + } + /* + * We don't reset pBufP here yet, as there may be an additional data + * byte in some protocols. See above. + */ + + /* has something changed? */ + act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0) + | (act->obutton ^ act->button); + + if (mouse.flags & Emulate3Button) { + if (((act->flags & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN)) + == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN)) + && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN)) + == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) { + act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN); + act->button |= MOUSE_BUTTON2DOWN; + } else if ((act->obutton & MOUSE_BUTTON2DOWN) + && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN)) + != (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) { + act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN + | MOUSE_BUTTON3DOWN); + } + act->flags &= MOUSE_POSCHANGED; + act->flags |= act->obutton ^ act->button; + } + + return act->flags; +} + +/* + * Buttons remapping + */ + +/* physical to logical button mapping */ +static int p2l[MOUSE_MAXBUTTON] = { + MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN, + MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, +}; + +/* + * End of functions from the Xfree Project + */ + +/* mouse_installmap : install a map between physical and logical buttons */ + +static int +mouse_installmap(char *arg) +{ + int pbutton; + int lbutton; + char *s; + + while (*arg) { + arg = skipspace(arg); + s = arg; + while (isdigit(*arg)) + ++arg; + arg = skipspace(arg); + if ((arg <= s) || (*arg != '=')) + return FALSE; + lbutton = atoi(s); + + arg = skipspace(++arg); + s = arg; + while (isdigit(*arg)) + ++arg; + if ((arg <= s) || (!isspace(*arg) && (*arg != '\0'))) + return FALSE; + pbutton = atoi(s); + + if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON)) + return FALSE; + if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON)) + return FALSE; + p2l[pbutton - 1] = 1 << (lbutton - 1); + } + + return TRUE; +} + +/* mouse_map : convert physical buttons to logical buttons */ + +static void +mouse_map(mousestatus_t *act1, mousestatus_t *act2) +{ + int pb; + int pbuttons; + int lbuttons; + + pbuttons = act1->button; + lbuttons = 0; + + act2->obutton = act2->button; + if (pbuttons & mouse.wmode) { + pbuttons &= ~mouse.wmode; + act1->dz = act1->dy; + act1->dx = 0; + act1->dy = 0; + } + act2->dx = act1->dx; + act2->dy = act1->dy; + act2->dz = act1->dz; + + switch (mouse.zmap) { + case 0: /* do nothing */ + break; + case MOUSE_XAXIS: + if (act1->dz != 0) { + act2->dx = act1->dz; + act2->dz = 0; + } + break; + case MOUSE_YAXIS: + if (act1->dz != 0) { + act2->dy = act1->dz; + act2->dz = 0; + } + break; + default: /* buttons */ + pbuttons &= ~(mouse.zmap | (mouse.zmap << 1)); + if (act1->dz < 0) + pbuttons |= mouse.zmap; + else if (act1->dz > 0) + pbuttons |= (mouse.zmap << 1); + act2->dz = 0; + break; + } + + for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) { + lbuttons |= (pbuttons & 1) ? p2l[pb] : 0; + pbuttons >>= 1; + } + act2->button = lbuttons; + + act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0) + | (act2->obutton ^ act2->button); +} + +/* + * Function to handle button click + * Note that an ioctl is sent for each button + */ + +static void +mouse_click(mousestatus_t *act) +{ + mouse_info_t mouse_infos; + struct timeval tv; + struct timeval tv1; + struct timeval tv2; + struct timezone tz; + int button; + int mask; + int i; + + mask = act->flags & MOUSE_BUTTONS; + if (mask == 0) + return; + + gettimeofday(&tv1, &tz); + tv2.tv_sec = mouse.clickthreshold/1000; + tv2.tv_usec = (mouse.clickthreshold%1000)*1000; + timersub(&tv1, &tv2, &tv); + debug("tv: %ld %ld", tv.tv_sec, tv.tv_usec); + button = MOUSE_BUTTON1DOWN; + for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) { + if (mask & 1) { + if (act->button & button) { + /* the button is down */ + debug(" : %ld %ld", + buttonstate[i].tv.tv_sec, buttonstate[i].tv.tv_usec); + if (timercmp(&tv, &buttonstate[i].tv, >)) { + buttonstate[i].tv.tv_sec = 0; + buttonstate[i].tv.tv_usec = 0; + buttonstate[i].count = 1; + } else { + ++buttonstate[i].count; + } + mouse_infos.u.event.value = buttonstate[i].count; + } else { + /* the button is up */ + buttonstate[i].tv = tv1; + mouse_infos.u.event.value = 0; + } + mouse_infos.operation = MOUSE_BUTTON_EVENT; + mouse_infos.u.event.id = button; + ioctl(mouse.cfd, PCVT_MOUSECTL, &mouse_infos); + debug("button %d count %d\n", i + 1, mouse_infos.u.event.value); + } + button <<= 1; + mask >>= 1; + } +} + +/* + * moused : main function + * - wait for mouse events + * - translate according to the current protocol + * - execute the actions associated to events + */ + +static void +moused(void) +{ + mouse_info_t mouse_infos; + mousestatus_t action; /* original mouse action */ + mousestatus_t action2; /* mapped action */ + fd_set fds; + u_char b; + FILE *fp; + + if ((mouse.cfd = open("/dev/pcvtctl", O_RDWR, 0)) == -1) + logerr(1, "cannot open /dev/pcvtctl"); + + if (!nodaemon && !background) { + if (daemon(0, 0)) { + logerr(1, "failed to become a daemon"); + } else { + background = TRUE; + fp = fopen(pidfile, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } + } + + /* display initial cursor */ + + mouse_infos.operation = MOUSE_INIT; + ioctl(mouse.cfd,PCVT_MOUSECTL,&mouse_infos); + + /* clear mouse data */ + bzero(&action, sizeof(action)); + bzero(&action2, sizeof(action2)); + bzero(&buttonstate, sizeof(buttonstate)); + bzero(&mouse_infos, sizeof(mouse)); + + /* process mouse data */ + for (;;) { + + FD_ZERO(&fds); + FD_SET(mouse.mfd, &fds); + + if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0) + logwarn("failed to read from mouse"); + + /* mouse event */ + read(mouse.mfd, &b, 1); + if (mouse_protocol(b, &action)) { /* handler detected action */ + mouse_map(&action, &action2); +#if 0 + printf("activity : buttons 0x%08x dx %d dy %d dz %d\n", + action2.button, action2.dx, action2.dy, action2.dz); +#endif + mouse_click(&action2); + if (action2.flags & MOUSE_POSCHANGED) { + mouse_infos.operation = MOUSE_MOTION_EVENT; + mouse_infos.u.data.buttons = action2.button; + mouse_infos.u.data.x = action2.dx; + mouse_infos.u.data.y = action2.dy; + mouse_infos.u.data.z = action2.dz; + ioctl(mouse.cfd, PCVT_MOUSECTL, &mouse_infos); + } + /* + * If the Z axis movement is mapped to a imaginary physical + * button, we need to cook up a corresponding button `up' event + * after sending a button `down' event. + */ + if ((mouse.zmap > 0) && (action.dz != 0)) { + action.obutton = action.button; + action.dx = action.dy = action.dz = 0; + mouse_map(&action, &action2); + debug("activity : buttons 0x%08x dx %d dy %d dz %d", + action2.button, action2.dx, action2.dy, action2.dz); + + mouse_click(&action2); + } + /* NOT REACHED */ + } + } +} + +static void +usage(void) +{ + printf("usage : %s [-3DPRcdfs] [-I file] [-F rate] [-r resolution ] [-S baudrate]",__progname); + printf(" [-C threshold] [-t protocol] [-m type] [-M N=M] [-b buttons]"); + printf(" [-w N] [-z target] -p port \n"); + printf(" %s -i info -p port \n",__progname); + exit(1); +} + +int +main(int argc, char **argv) +{ + int opt; + int i; + + while ((opt = (getopt(argc,argv,"3DPRb:cdfhi:m:st:I:F:r:M:S:C:p:w:z:"))) != -1) { + switch (opt) { + case '3': + mouse.flags |= Emulate3Button; + break; + case 'D': + mouse.flags |= ClearDTR; + break; + case 'P': + mouse.flags |= NoPnP; + break; + case 'R': + mouse.flags |= ClearRTS; + break; + case 'b': + mouse.hw.buttons = atoi(optarg); + break; + case 'c': + mouse.flags |= ChordMiddle; + break; + case 'd': + ++debug; + break; + case 'f': + nodaemon = TRUE; + break; + case 'M': + if (!mouse_installmap(optarg)) { + warnx("invalid mapping `%s'\n", optarg); + usage(); + } + break; + case 's': + mouse.baudrate = 9600; + break; + case 't': + if (strcmp(optarg, "auto") == 0) { + mouse.proto = P_UNKNOWN; + mouse.flags &= ~NoPnP; + break; + } + for (i = 0; mouse_names[i]; i++) + if (strcmp(optarg,mouse_names[i]) == 0){ + mouse.proto = i; + mouse.flags |= NoPnP; + break; + } + if (mouse_names[i]) + break; + printf("no such mouse protocol `%s'\n", optarg); + usage(); + break; + case 'm': + for (i = 0 ; mouse_models[i].name; i++) + if (strcmp(optarg,mouse_models[i].name) == 0){ + mouse.hw.model = + mouse_models[i].val; + break; + } + if (mouse_models[i].name) + break; + printf("no such mouse type `%s'\n", optarg); + usage(); + break; + case 'i': + if (strcmp(optarg, "all") == 0) + identify = ID_ALL; + else if (strcmp(optarg, "port") == 0) + identify = ID_PORT; + else if (strcmp(optarg, "if") == 0) + identify = ID_IF; + else if (strcmp(optarg, "type") == 0) + identify = ID_TYPE; + else if (strcmp(optarg, "model") == 0) + identify = ID_MODEL; + else { + printf("invalid argument `%s'", optarg); + usage(); + } + break; + case 'I': + pidfile = optarg; + break; + case 'F': + mouse.rate = atoi(optarg); + if (mouse.rate <= 0) { + printf("invalid rate `%s'\n", optarg); + usage(); + } + break; + case 'r': + if (strcmp(optarg, "high") == 0) + mouse.resolution = MOUSE_RES_HIGH; + else if (strcmp(optarg, "medium-high") == 0) + mouse.resolution = MOUSE_RES_HIGH; + else if (strcmp(optarg, "medium-low") == 0) + mouse.resolution = MOUSE_RES_MEDIUMLOW; + else if (strcmp(optarg, "low") == 0) + mouse.resolution = MOUSE_RES_LOW; + else if (strcmp(optarg, "default") == 0) + mouse.resolution = MOUSE_RES_DEFAULT; + else { + mouse.resolution = atoi(optarg); + if (mouse.resolution <= 0) { + printf("invalid resolution `%s'\n", optarg); + usage(); + } + } + break; + case 'S': + mouse.baudrate = atoi(optarg); + if (mouse.baudrate <= 0) { + printf("invalid baudrate `%s'\n", optarg); + usage(); + } + break; + case 'C': +#define MAX_CLICKTHRESHOLD 2000 /* max delay for double click */ + + mouse.clickthreshold = atoi(optarg); + if ((mouse.clickthreshold < 0) || + (mouse.clickthreshold > MAX_CLICKTHRESHOLD)) { + printf("invalid threshold `%s': max value is %d\n" + , optarg,MAX_CLICKTHRESHOLD); + usage(); + } + break; + case 'p': + mouse.portname = strdup(optarg); + break; + case 'h': + usage(); + break; + case 'w': + i = atoi(optarg); + if ((i <= 0) || (i > MOUSE_MAXBUTTON)) { + warnx("invalid argument `%s'", optarg); + usage(); + } + mouse.wmode = 1 << (i - 1); + break; + case 'z': + if (strcmp(optarg, "x") == 0) + mouse.zmap = MOUSE_XAXIS; + else if (strcmp(optarg, "y") == 0) + mouse.zmap = MOUSE_YAXIS; + else { + i = atoi(optarg); + /* + * Use button i for negative Z axis movement + * and button (i + 1) for positive Z axis + * movement. + */ + if ((i <= 0) || + (i > MOUSE_MAXBUTTON - 1)) { + warnx("invalid argument `%s'", + optarg); + usage(); + } + mouse.zmap = 1 << (i - 1); + } + break; + default: + usage(); + } + } + /* + * mouse device name : + * Logitech bus mouse : /dev/lms0 + * Microsoft bus mouse (also refered as inport) : /dev/mms0 + * PS/2 mouse : /dev/pms0 + * + * Note that Logitech and Microsoft bus mouse speak the same + * protocol (BusMouse). + */ + switch(mouse.proto) { + case P_INPORT: + /* Inport and Bus use the same protocol (BusMouse) */ + mouse.proto = P_BM; + mouse.portname = MMS_DEV; + break; + case P_BM: + if (!mouse.portname) + mouse.portname = LMS_DEV; + break; + case P_PS2: + if (!mouse.portname) + mouse.portname = PMS_DEV; + break; + default: + if (mouse.portname) /* serial mouse */ + break; + printf("no port name specified\n"); + usage(); + } + + for (;;) { + if (setjmp(env) == 0) { + signal(SIGHUP, hup); + signal(SIGINT , cleanup); + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + if ((mouse.mfd = open(mouse.portname, + O_RDWR | O_NONBLOCK, 0)) == -1) + logerr(1, "unable to open %s", mouse.portname); + if (mouse_identify() == P_UNKNOWN) { + logwarn("cannot determine mouse type on %s", mouse.portname); + close(mouse.mfd); + mouse.mfd = -1; + } + + /* print some information */ + if (identify != ID_NONE) { + if (identify == ID_ALL) + printf("%s %s %s %s\n", + mouse.portname, + mouse_if(mouse.hw.iftype), + mouse_name(mouse.proto), + mouse_model(mouse.hw.model)); + else if (identify & ID_PORT) + printf("%s\n", mouse.portname); + else if (identify & ID_IF) + printf("%s\n", mouse_if(mouse.hw.iftype)); + else if (identify & ID_TYPE) + printf("%s\n", mouse_name(mouse.proto)); + else if (identify & ID_MODEL) + printf("%s\n", mouse_model(mouse.hw.model)); + exit(0); + } else { + debug("port: %s interface: %s type: %s model: %s", + mouse.portname, mouse_if(mouse.hw.iftype), + mouse_name(mouse.proto), mouse_model(mouse.hw.model)); + } + + if (mouse.mfd == -1) { + /* + * We cannot continue because of error. Exit if the + * program has not become a daemon. Otherwise, block + * until the the user corrects the problem and issues + * SIGHUP. + */ + if (!background) + exit(1); + sigpause(0); + } + + mouse_init(); /* init mouse */ + moused(); + } + + if (mouse.mfd != -1) + close(mouse.mfd); + } + /* NOT REACHED */ + + exit(0); + return (0); +} + diff --git a/usr.sbin/moused/moused.h b/usr.sbin/moused/moused.h new file mode 100644 index 00000000000..1f983a1d8c3 --- /dev/null +++ b/usr.sbin/moused/moused.h @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2000 Jean-Baptiste Marchand, Julien Montagne and Jerome Verdon + * + * Copyright (c) 1998 by Kazutaka Yokota + * + * Copyright (c) 1995 Michael Smith + * + * Copyright (c) 1993 by David Dawes <dawes@xfree86.org> + * + * Copyright (c) 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * + * All rights reserved. + * + * Most of this code was taken from the FreeBSD moused daemon, written by + * Michael Smith. The FreeBSD moused daemon already contained code from the + * Xfree Project, written by David Dawes and Thomas Roell and Kazutaka Yokota. + * + * Adaptation to OpenBSD was done by Jean-Baptiste Marchand, Julien Montagne + * and Jerome Verdon. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * David Dawes, Jean-Baptiste Marchand, Julien Montagne, Thomas Roell, + * Michael Smith, Jerome Verdon and Kazutaka Yokota. + * 4. The name authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +/* Logging macros */ + +#define debug(fmt,args...) \ + if (debug&&nodaemon) printf(fmt, ##args) + +#define logerr(e, fmt, args...) { \ + if (background) { \ + syslog(LOG_DAEMON | LOG_ERR, fmt, ##args); \ + exit(e); \ + } else \ + errx(e, fmt, ##args); \ +} + +#define logwarn(fmt, args...) { \ + if (background) \ + syslog(LOG_DAEMON | LOG_WARNING, fmt, ##args); \ + else \ + warnx(fmt, ##args); \ +} + +/* Logitech PS2++ protocol */ +#define MOUSE_PS2PLUS_CHECKBITS(b) \ + ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f)) +#define MOUSE_PS2PLUS_PACKET_TYPE(b) \ + (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4)) + +/* Daemon flags */ + +#define ChordMiddle 0x0001 /* avoid bug reporting middle button as down + when left and right are pressed */ +#define Emulate3Button 0x0002 /* option to emulate a third button */ +#define ClearDTR 0x0004 /* for mousesystems protocol (3 button mouse) */ +#define ClearRTS 0x0008 /* idem as above */ +#define NoPnP 0x0010 /* disable PnP for PnP mice */ + +/* Mouse info flags (when specifying the -i option) */ + +#define ID_NONE 0 +#define ID_PORT 1 +#define ID_IF 2 +#define ID_TYPE 4 +#define ID_MODEL 8 +#define ID_ALL (ID_PORT | ID_IF | ID_TYPE | ID_MODEL) + +#define FALSE 0 +#define TRUE 1 +#define ErrorF printf + +/* Mouse Interface */ + +#define MOUSE_IF_UNKNOWN (-1) +#define MOUSE_IF_SERIAL 0 +#define MOUSE_IF_BUS 1 +#define MOUSE_IF_INPORT 2 +#define MOUSE_IF_PS2 3 +#define MOUSE_IF_USB 4 + +/* Devices corresponding to physical interfaces */ + +#define LMS_DEV "/dev/lms0" +#define MMS_DEV "/dev/mms0" +#define PMS_DEV "/dev/psm0" +#define SERIAL_DEV "/dev/cua0" /* can be /dev/cua00, /dev/cua01, ... */ + +/* array giving the names of the physical interface (ordered by MOUSE_IF_XXX) */ + +/* symbol table entry */ +typedef struct { + char *name; + int val; + int val2; +} symtab_t; + +/* Mouse type */ + +#define MOUSE_TYPE_UNKNOWN (-1) +#define MOUSE_TYPE_MOUSE 0 +#define MOUSE_TRACKBALL 1 +#define MOUSE_STICK 2 +#define MOUSE_PAD 3 + +/* Mouse models */ + +#define MOUSE_MODEL_UNKNOWN (-1) +#define MOUSE_MODEL_GENERIC 0 +#define MOUSE_MODEL_GLIDEPOINT 1 +#define MOUSE_MODEL_NETSCROLL 2 +#define MOUSE_MODEL_NET 3 +#define MOUSE_MODEL_INTELLI 4 +#define MOUSE_MODEL_THINK 5 +#define MOUSE_MODEL_EASYSCROLL 6 +#define MOUSE_MODEL_MOUSEMANPLUS 7 +#define MOUSE_MODEL_KIDSPAD 8 +#define MOUSE_MODEL_VERSAPAD 9 + +/* Mouse protocols */ + +#define P_UNKNOWN (-1) +#define P_MS 0 /* Microsoft Serial, 3 bytes */ +#define P_MSC 1 /* Mouse Systems, 5 bytes */ +#define P_LOGI 2 /* Logitech, 3 bytes */ +#define P_MM 3 /* MM series, 3 bytes */ +#define P_LOGIMAN 4 /* Logitech MouseMan 3/4 bytes */ +#define P_BM 5 /* MS/Logitech bus mouse */ +#define P_INPORT 6 /* MS/ATI InPort mouse (same protocol as above) */ +#define P_PS2 7 /* PS/2 mouse, 3 bytes */ +#define P_MMHIT 8 /* Hitachi Tablet 3 bytes */ +#define P_GLIDEPOINT 9 /* ALPS GlidePoint, 3/4 bytes */ +#define P_IMSERIAL 10 /* MS IntelliMouse, 4 bytes */ +#define P_THINKING 11 /* Kensignton Thinking Mouse, 3/4 bytes */ + +/* X and Y axis */ + +#define MOUSE_XAXIS (-1) +#define MOUSE_YAXIS (-2) + +/* flags */ + +#define MOUSE_STDBUTTONSCHANGED MOUSE_STDBUTTONS +#define MOUSE_EXTBUTTONSCHANGED MOUSE_EXTBUTTONS +#define MOUSE_BUTTONSCHANGED MOUSE_BUTTONS +#define MOUSE_POSCHANGED 0x80000000 + +/* + * List of all the protocols parameters + * The parameters are : + * - size of the packet + * - synchronization mask + * - synchronization value (must be equal to data ANDed with SYNCMASK) + * - mask of buttons + * - mask of each button separetely + */ + +/* Microsoft Serial mouse data packet */ +#define MOUSE_MSS_PACKETSIZE 3 +#define MOUSE_MSS_SYNCMASK 0x40 +#define MOUSE_MSS_SYNC 0x40 +#define MOUSE_MSS_BUTTONS 0x30 +#define MOUSE_MSS_BUTTON1DOWN 0x20 /* left */ +#define MOUSE_MSS_BUTTON2DOWN 0x00 /* no middle button */ +#define MOUSE_MSS_BUTTON3DOWN 0x10 /* right */ + +/* Logitech MouseMan data packet (M+ protocol) */ +#define MOUSE_LMAN_BUTTON2DOWN 0x20 /* middle button, the 4th byte */ + +/* ALPS GlidePoint extention (variant of M+ protocol) */ +#define MOUSE_ALPS_BUTTON2DOWN 0x20 /* middle button, the 4th byte */ +#define MOUSE_ALPS_TAP 0x10 /* `tapping' action, the 4th byte */ + +/* Kinsington Thinking Mouse extention (variant of M+ protocol) */ +#define MOUSE_THINK_BUTTON2DOWN 0x20 /* lower-left button, the 4th byte */ +#define MOUSE_THINK_BUTTON4DOWN 0x10 /* lower-right button, the 4th byte */ + +/* MS IntelliMouse (variant of MS Serial) */ +#define MOUSE_INTELLI_PACKETSIZE 4 +#define MOUSE_INTELLI_BUTTON2DOWN 0x10 /* middle button the 4th byte */ + +/* Mouse Systems Corp. mouse data packet */ +#define MOUSE_MSC_PACKETSIZE 5 +#define MOUSE_MSC_SYNCMASK 0xf8 +#define MOUSE_MSC_SYNC 0x80 +#define MOUSE_MSC_BUTTONS 0x07 +#define MOUSE_MSC_BUTTON1UP 0x04 /* left */ +#define MOUSE_MSC_BUTTON2UP 0x02 /* middle */ +#define MOUSE_MSC_BUTTON3UP 0x01 /* right */ +#define MOUSE_MSC_MAXBUTTON 3 + +/* MM series mouse data packet */ +#define MOUSE_MM_PACKETSIZE 3 +#define MOUSE_MM_SYNCMASK 0xe0 +#define MOUSE_MM_SYNC 0x80 +#define MOUSE_MM_BUTTONS 0x07 +#define MOUSE_MM_BUTTON1DOWN 0x04 /* left */ +#define MOUSE_MM_BUTTON2DOWN 0x02 /* middle */ +#define MOUSE_MM_BUTTON3DOWN 0x01 /* right */ +#define MOUSE_MM_XPOSITIVE 0x10 +#define MOUSE_MM_YPOSITIVE 0x08 + +/* PS/2 mouse data packet */ +#define MOUSE_PS2_PACKETSIZE 3 +#define MOUSE_PS2_SYNCMASK 0xc8 +#define MOUSE_PS2_SYNC 0x08 +#define MOUSE_PS2_BUTTONS 0x07 /* 0x03 for 2 button mouse */ +#define MOUSE_PS2_BUTTON1DOWN 0x01 /* left */ +#define MOUSE_PS2_BUTTON2DOWN 0x04 /* middle */ +#define MOUSE_PS2_BUTTON3DOWN 0x02 /* right */ +#define MOUSE_PS2_TAP MOUSE_PS2_SYNC /* GlidePoint (PS/2) `tapping' + * Yes! this is the same bit + * as SYNC! + */ +#define MOUSE_PS2PLUS_BUTTON4DOWN 0x10 /* 4th button on MouseMan+ */ +#define MOUSE_PS2PLUS_BUTTON5DOWN 0x20 + +#define MOUSE_PS2_XNEG 0x10 +#define MOUSE_PS2_YNEG 0x20 +#define MOUSE_PS2_XOVERFLOW 0x40 +#define MOUSE_PS2_YOVERFLOW 0x80 +#define MOUSE_PS2PLUS_ZNEG 0x08 /* MouseMan+ negative wheel movement */ +#define MOUSE_PS2PLUS_SYNCMASK 0x48 +#define MOUSE_PS2PLUS_SYNC 0x48 + +/* Interlink VersaPad (serial I/F) data packet */ +#define MOUSE_VERSA_PACKETSIZE 6 +#define MOUSE_VERSA_IN_USE 0x04 +#define MOUSE_VERSA_SYNCMASK 0xc3 +#define MOUSE_VERSA_SYNC 0xc0 +#define MOUSE_VERSA_BUTTONS 0x30 +#define MOUSE_VERSA_BUTTON1DOWN 0x20 /* left */ +#define MOUSE_VERSA_BUTTON2DOWN 0x00 /* middle */ +#define MOUSE_VERSA_BUTTON3DOWN 0x10 /* right */ +#define MOUSE_VERSA_TAP 0x08 + +/* Interlink VersaPad (PS/2 I/F) data packet */ +#define MOUSE_PS2VERSA_PACKETSIZE 6 +#define MOUSE_PS2VERSA_IN_USE 0x10 +#define MOUSE_PS2VERSA_SYNCMASK 0xe8 +#define MOUSE_PS2VERSA_SYNC 0xc8 +#define MOUSE_PS2VERSA_BUTTONS 0x05 +#define MOUSE_PS2VERSA_BUTTON1DOWN 0x04 /* left */ +#define MOUSE_PS2VERSA_BUTTON2DOWN 0x00 /* middle */ +#define MOUSE_PS2VERSA_BUTTON3DOWN 0x01 /* right */ +#define MOUSE_PS2VERSA_TAP 0x02 + +/* Mouse resolutions */ + +#define MOUSE_RES_UNKNOWN (-1) +#define MOUSE_RES_DEFAULT 0 +#define MOUSE_RES_LOW (-2) +#define MOUSE_RES_MEDIUMLOW (-3) +#define MOUSE_RES_MEDIUMHIGH (-4) +#define MOUSE_RES_HIGH (-5) + +/* serial PnP ID string */ +typedef struct { + int revision; /* PnP revision, 100 for 1.00 */ + char *eisaid; /* EISA ID including mfr ID and product ID */ + char *serial; /* serial No, optional */ + char *class; /* device class, optional */ + char *compat; /* list of compatible drivers, optional */ + char *description; /* product description, optional */ + int neisaid; /* length of the above fields... */ + int nserial; + int nclass; + int ncompat; + int ndescription; +} pnpid_t; + +/* mousehw structure : hardware infos */ + +typedef struct mousehw { + int buttons; /* -1 if unknown */ + int iftype; /* MOUSE_IF_XXX */ + int type; /* MOUSE_TYPE_XXX */ + int model; /* MOUSE_MODEL_XXX */ +} mousehw_t; + +/* mousemode structure : mouse settings */ + +typedef struct mousemode { + int protocol; /* MOUSE_PROTO_XXX */ + int rate; /* report rate (per sec), -1 if unknown */ + int resolution; /* MOUSE_RES_XXX, -1 if unknown */ + int accelfactor; /* accelation factor (must be 1 or greater) */ + int packetsize; /* the length of the data packet */ + unsigned char syncmask[2]; /* sync. data bits in the header byte */ +} mousemode_t; + +/* mouse structure : main structure */ + +typedef struct mouse_s { + int flags; + char *portname; /* /dev/XXX */ + int proto; /* MOUSE_PROTO_XXX */ + int baudrate; + int old_baudrate; + int rate; /* report rate */ + int resolution; /* MOUSE_RES_XXX or a positive number */ + int zmap; /* MOUSE_{X|Y}AXIS or a button number */ + int wmode; /* wheel mode button number */ + int mfd; /* mouse file descriptor */ + int cfd; /* console file descriptor */ + long clickthreshold; /* double click speed in msec */ + mousehw_t hw; /* mouse device hardware information */ + mousemode_t mode; /* protocol information */ +} mouse_t ; + +/* Current status of the mouse */ +typedef struct mousestatus { + int flags; /* state change flags */ + int button; /* button status */ + int obutton; /* previous button status */ + int dx; /* x movement */ + int dy; /* y movement */ + int dz; /* z movement */ +} mousestatus_t; + |