moused.c revision 58344
1/**
2 ** Copyright (c) 1995 Michael Smith, All rights reserved.
3 **
4 ** Redistribution and use in source and binary forms, with or without
5 ** modification, are permitted provided that the following conditions
6 ** are met:
7 ** 1. Redistributions of source code must retain the above copyright
8 **    notice, this list of conditions and the following disclaimer as
9 **    the first lines of this file unmodified.
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 **    notice, this list of conditions and the following disclaimer in the
12 **    documentation and/or other materials provided with the distribution.
13 ** 3. All advertising materials mentioning features or use of this software
14 **    must display the following acknowledgment:
15 **      This product includes software developed by Michael Smith.
16 ** 4. The name of the author may not be used to endorse or promote products
17 **    derived from this software without specific prior written permission.
18 **
19 **
20 ** THIS SOFTWARE IS PROVIDED BY Michael Smith ``AS IS'' AND ANY
21 ** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith BE LIABLE FOR
24 ** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29 ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 ** EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **
32 **/
33
34/**
35 ** MOUSED.C
36 **
37 ** Mouse daemon : listens to a serial port, the bus mouse interface, or
38 ** the PS/2 mouse port for mouse data stream, interprets data and passes
39 ** ioctls off to the console driver.
40 **
41 ** The mouse interface functions are derived closely from the mouse
42 ** handler in the XFree86 X server.  Many thanks to the XFree86 people
43 ** for their great work!
44 **
45 **/
46
47#ifndef lint
48static const char rcsid[] =
49  "$FreeBSD: head/usr.sbin/moused/moused.c 58344 2000-03-20 10:40:44Z yokota $";
50#endif /* not lint */
51
52#include <ctype.h>
53#include <err.h>
54#include <errno.h>
55#include <fcntl.h>
56#include <limits.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <stdarg.h>
60#include <string.h>
61#include <ctype.h>
62#include <signal.h>
63#include <setjmp.h>
64#include <termios.h>
65#include <syslog.h>
66
67#include <machine/console.h>
68#include <machine/mouse.h>
69
70#include <sys/types.h>
71#include <sys/time.h>
72#include <sys/socket.h>
73#include <sys/un.h>
74#include <unistd.h>
75
76#define MAX_CLICKTHRESHOLD	2000	/* 2 seconds */
77#define MAX_BUTTON2TIMEOUT	2000	/* 2 seconds */
78#define DFLT_CLICKTHRESHOLD	 500	/* 0.5 second */
79#define DFLT_BUTTON2TIMEOUT	 500	/* 0.5 second */
80
81#define TRUE		1
82#define FALSE		0
83
84#define MOUSE_XAXIS	(-1)
85#define MOUSE_YAXIS	(-2)
86
87/* Logitech PS2++ protocol */
88#define MOUSE_PS2PLUS_CHECKBITS(b)	\
89			((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
90#define MOUSE_PS2PLUS_PACKET_TYPE(b)	\
91			(((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
92
93#define	ChordMiddle	0x0001
94#define Emulate3Button	0x0002
95#define ClearDTR	0x0004
96#define ClearRTS	0x0008
97#define NoPnP		0x0010
98
99#define ID_NONE		0
100#define ID_PORT		1
101#define ID_IF		2
102#define ID_TYPE 	4
103#define ID_MODEL	8
104#define ID_ALL		(ID_PORT | ID_IF | ID_TYPE | ID_MODEL)
105
106#define debug(fmt,args...) \
107	if (debug&&nodaemon) warnx(fmt, ##args)
108
109#define logerr(e, fmt, args...) {				\
110	if (background) {					\
111	    syslog(LOG_DAEMON | LOG_ERR, fmt ": %m", ##args);	\
112	    exit(e);						\
113	} else							\
114	    err(e, fmt, ##args);				\
115}
116
117#define logerrx(e, fmt, args...) {				\
118	if (background) {					\
119	    syslog(LOG_DAEMON | LOG_ERR, fmt, ##args);		\
120	    exit(e);						\
121	} else							\
122	    errx(e, fmt, ##args);				\
123}
124
125#define logwarn(fmt, args...) {					\
126	if (background)						\
127	    syslog(LOG_DAEMON | LOG_WARNING, fmt ": %m", ##args); \
128	else							\
129	    warn(fmt, ##args);					\
130}
131
132#define logwarnx(fmt, args...) {				\
133	if (background)						\
134	    syslog(LOG_DAEMON | LOG_WARNING, fmt, ##args);	\
135	else							\
136	    warnx(fmt, ##args);					\
137}
138
139/* structures */
140
141/* symbol table entry */
142typedef struct {
143    char *name;
144    int val;
145    int val2;
146} symtab_t;
147
148/* serial PnP ID string */
149typedef struct {
150    int revision;	/* PnP revision, 100 for 1.00 */
151    char *eisaid;	/* EISA ID including mfr ID and product ID */
152    char *serial;	/* serial No, optional */
153    char *class;	/* device class, optional */
154    char *compat;	/* list of compatible drivers, optional */
155    char *description;	/* product description, optional */
156    int neisaid;	/* length of the above fields... */
157    int nserial;
158    int nclass;
159    int ncompat;
160    int ndescription;
161} pnpid_t;
162
163/* global variables */
164
165int	debug = 0;
166int	nodaemon = FALSE;
167int	background = FALSE;
168int	identify = ID_NONE;
169int	extioctl = FALSE;
170char	*pidfile = "/var/run/moused.pid";
171
172/* local variables */
173
174/* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
175static symtab_t rifs[] = {
176    { "serial",		MOUSE_IF_SERIAL },
177    { "bus",		MOUSE_IF_BUS },
178    { "inport",		MOUSE_IF_INPORT },
179    { "ps/2",		MOUSE_IF_PS2 },
180    { "sysmouse",	MOUSE_IF_SYSMOUSE },
181    { "usb",		MOUSE_IF_USB },
182    { NULL,		MOUSE_IF_UNKNOWN },
183};
184
185/* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
186static char *rnames[] = {
187    "microsoft",
188    "mousesystems",
189    "logitech",
190    "mmseries",
191    "mouseman",
192    "busmouse",
193    "inportmouse",
194    "ps/2",
195    "mmhitab",
196    "glidepoint",
197    "intellimouse",
198    "thinkingmouse",
199    "sysmouse",
200    "x10mouseremote",
201    "kidspad",
202#if notyet
203    "mariqua",
204#endif
205    NULL
206};
207
208/* models */
209static symtab_t	rmodels[] = {
210    { "NetScroll",		MOUSE_MODEL_NETSCROLL },
211    { "NetMouse/NetScroll Optical", MOUSE_MODEL_NET },
212    { "GlidePoint",		MOUSE_MODEL_GLIDEPOINT },
213    { "ThinkingMouse",		MOUSE_MODEL_THINK },
214    { "IntelliMouse",		MOUSE_MODEL_INTELLI },
215    { "EasyScroll/SmartScroll",	MOUSE_MODEL_EASYSCROLL },
216    { "MouseMan+",		MOUSE_MODEL_MOUSEMANPLUS },
217    { "Kidspad",		MOUSE_MODEL_KIDSPAD },
218    { "VersaPad",		MOUSE_MODEL_VERSAPAD },
219    { "IntelliMouse Explorer",	MOUSE_MODEL_EXPLORER },
220    { "4D Mouse",		MOUSE_MODEL_4D },
221    { "4D+ Mouse",		MOUSE_MODEL_4DPLUS },
222    { "generic",		MOUSE_MODEL_GENERIC },
223    { NULL, 			MOUSE_MODEL_UNKNOWN },
224};
225
226/* PnP EISA/product IDs */
227static symtab_t pnpprod[] = {
228    /* Kensignton ThinkingMouse */
229    { "KML0001",	MOUSE_PROTO_THINK,	MOUSE_MODEL_THINK },
230    /* MS IntelliMouse */
231    { "MSH0001",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
232    /* MS IntelliMouse TrackBall */
233    { "MSH0004",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
234    /* Tremon Wheel Mouse MUSD */
235    { "HTK0001",        MOUSE_PROTO_INTELLI,    MOUSE_MODEL_INTELLI },
236    /* Genius PnP Mouse */
237    { "KYE0001",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
238    /* MouseSystems SmartScroll Mouse (OEM from Genius?) */
239    { "KYE0002",	MOUSE_PROTO_MS,		MOUSE_MODEL_EASYSCROLL },
240    /* Genius NetMouse */
241    { "KYE0003",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_NET },
242    /* Genius Kidspad, Easypad and other tablets */
243    { "KYE0005",	MOUSE_PROTO_KIDSPAD,	MOUSE_MODEL_KIDSPAD },
244    /* Genius EZScroll */
245    { "KYEEZ00",	MOUSE_PROTO_MS,		MOUSE_MODEL_EASYSCROLL },
246    /* Logitech Cordless MouseMan Wheel */
247    { "LGI8033",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
248    /* Logitech MouseMan (new 4 button model) */
249    { "LGI800C",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
250    /* Logitech MouseMan+ */
251    { "LGI8050",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
252    /* Logitech FirstMouse+ */
253    { "LGI8051",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
254    /* Logitech serial */
255    { "LGI8001",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
256    /* A4 Tech 4D/4D+ Mouse */
257    { "A4W0005",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_4D },
258    /* 8D Scroll Mouse */
259    { "PEC9802",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
260    /* Mitsumi Wireless Scroll Mouse */
261    { "MTM6401",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
262
263    /* MS bus */
264    { "PNP0F00",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
265    /* MS serial */
266    { "PNP0F01",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
267    /* MS InPort */
268    { "PNP0F02",	MOUSE_PROTO_INPORT,	MOUSE_MODEL_GENERIC },
269    /* MS PS/2 */
270    { "PNP0F03",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
271    /*
272     * EzScroll returns PNP0F04 in the compatible device field; but it
273     * doesn't look compatible... XXX
274     */
275    /* MouseSystems */
276    { "PNP0F04",	MOUSE_PROTO_MSC,	MOUSE_MODEL_GENERIC },
277    /* MouseSystems */
278    { "PNP0F05",	MOUSE_PROTO_MSC,	MOUSE_MODEL_GENERIC },
279#if notyet
280    /* Genius Mouse */
281    { "PNP0F06",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
282    /* Genius Mouse */
283    { "PNP0F07",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
284#endif
285    /* Logitech serial */
286    { "PNP0F08",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
287    /* MS BallPoint serial */
288    { "PNP0F09",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
289    /* MS PnP serial */
290    { "PNP0F0A",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
291    /* MS PnP BallPoint serial */
292    { "PNP0F0B",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
293    /* MS serial comatible */
294    { "PNP0F0C",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
295    /* MS InPort comatible */
296    { "PNP0F0D",	MOUSE_PROTO_INPORT,	MOUSE_MODEL_GENERIC },
297    /* MS PS/2 comatible */
298    { "PNP0F0E",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
299    /* MS BallPoint comatible */
300    { "PNP0F0F",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
301#if notyet
302    /* TI QuickPort */
303    { "PNP0F10",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
304#endif
305    /* MS bus comatible */
306    { "PNP0F11",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
307    /* Logitech PS/2 */
308    { "PNP0F12",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
309    /* PS/2 */
310    { "PNP0F13",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
311#if notyet
312    /* MS Kids Mouse */
313    { "PNP0F14",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
314#endif
315    /* Logitech bus */
316    { "PNP0F15",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
317#if notyet
318    /* Logitech SWIFT */
319    { "PNP0F16",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
320#endif
321    /* Logitech serial compat */
322    { "PNP0F17",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
323    /* Logitech bus compatible */
324    { "PNP0F18",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
325    /* Logitech PS/2 compatible */
326    { "PNP0F19",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
327#if notyet
328    /* Logitech SWIFT compatible */
329    { "PNP0F1A",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
330    /* HP Omnibook */
331    { "PNP0F1B",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
332    /* Compaq LTE TrackBall PS/2 */
333    { "PNP0F1C",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
334    /* Compaq LTE TrackBall serial */
335    { "PNP0F1D",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
336    /* MS Kidts Trackball */
337    { "PNP0F1E",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
338#endif
339    /* Interlink VersaPad */
340    { "LNK0001",	MOUSE_PROTO_VERSAPAD,	MOUSE_MODEL_VERSAPAD },
341
342    { NULL,		MOUSE_PROTO_UNKNOWN,	MOUSE_MODEL_GENERIC },
343};
344
345/* the table must be ordered by MOUSE_PROTO_XXX in mouse.h */
346static unsigned short rodentcflags[] =
347{
348    (CS7	           | CREAD | CLOCAL | HUPCL ),	/* MicroSoft */
349    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* MouseSystems */
350    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* Logitech */
351    (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ),	/* MMSeries */
352    (CS7		   | CREAD | CLOCAL | HUPCL ),	/* MouseMan */
353    0,							/* Bus */
354    0,							/* InPort */
355    0,							/* PS/2 */
356    (CS8		   | CREAD | CLOCAL | HUPCL ),	/* MM HitTablet */
357    (CS7	           | CREAD | CLOCAL | HUPCL ),	/* GlidePoint */
358    (CS7                   | CREAD | CLOCAL | HUPCL ),	/* IntelliMouse */
359    (CS7                   | CREAD | CLOCAL | HUPCL ),	/* Thinking Mouse */
360    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* sysmouse */
361    (CS7	           | CREAD | CLOCAL | HUPCL ),	/* X10 MouseRemote */
362    (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ),	/* kidspad etc. */
363    (CS8		   | CREAD | CLOCAL | HUPCL ),	/* VersaPad */
364#if notyet
365    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* Mariqua */
366#endif
367};
368
369static struct rodentparam {
370    int flags;
371    char *portname;		/* /dev/XXX */
372    int rtype;			/* MOUSE_PROTO_XXX */
373    int level;			/* operation level: 0 or greater */
374    int baudrate;
375    int rate;			/* report rate */
376    int resolution;		/* MOUSE_RES_XXX or a positive number */
377    int zmap[4];		/* MOUSE_{X|Y}AXIS or a button number */
378    int wmode;			/* wheel mode button number */
379    int mfd;			/* mouse file descriptor */
380    int cfd;			/* /dev/consolectl file descriptor */
381    int mremsfd;		/* mouse remote server file descriptor */
382    int mremcfd;		/* mouse remote client file descriptor */
383    long clickthreshold;	/* double click speed in msec */
384    long button2timeout;	/* 3 button emulation timeout */
385    mousehw_t hw;		/* mouse device hardware information */
386    mousemode_t mode;		/* protocol information */
387} rodent = {
388    flags : 0,
389    portname : NULL,
390    rtype : MOUSE_PROTO_UNKNOWN,
391    level : -1,
392    baudrate : 1200,
393    rate : 0,
394    resolution : MOUSE_RES_UNKNOWN,
395    zmap: { 0, 0, 0, 0 },
396    wmode: 0,
397    mfd : -1,
398    cfd : -1,
399    mremsfd : -1,
400    mremcfd : -1,
401    clickthreshold : DFLT_CLICKTHRESHOLD,
402    button2timeout : DFLT_BUTTON2TIMEOUT,
403};
404
405/* button status */
406struct button_state {
407    int count;		/* 0: up, 1: single click, 2: double click,... */
408    struct timeval tv;	/* timestamp on the last button event */
409};
410static struct button_state	bstate[MOUSE_MAXBUTTON]; /* button state */
411static struct button_state	*mstate[MOUSE_MAXBUTTON];/* mapped button st.*/
412static struct button_state	zstate[4];		 /* Z/W axis state */
413
414/* state machine for 3 button emulation */
415
416#define S0	0	/* start */
417#define S1	1	/* button 1 delayed down */
418#define S2	2	/* button 3 delayed down */
419#define S3	3	/* both buttons down -> button 2 down */
420#define S4	4	/* button 1 delayed up */
421#define S5	5	/* button 1 down */
422#define S6	6	/* button 3 down */
423#define S7	7	/* both buttons down */
424#define S8	8	/* button 3 delayed up */
425#define S9	9	/* button 1 or 3 up after S3 */
426
427#define A(b1, b3)	(((b1) ? 2 : 0) | ((b3) ? 1 : 0))
428#define A_TIMEOUT	4
429
430static struct {
431    int s[A_TIMEOUT + 1];
432    int buttons;
433    int mask;
434} states[10] = {
435    /* S0 */
436    { { S0, S2, S1, S3, S0 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN) },
437    /* S1 */
438    { { S4, S2, S1, S3, S5 }, 0, ~MOUSE_BUTTON1DOWN },
439    /* S2 */
440    { { S8, S2, S1, S3, S6 }, 0, ~MOUSE_BUTTON3DOWN },
441    /* S3 */
442    { { S0, S9, S9, S3, S3 }, MOUSE_BUTTON2DOWN, ~0 },
443    /* S4 */
444    { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON1DOWN, ~0 },
445    /* S5 */
446    { { S0, S2, S5, S7, S5 }, MOUSE_BUTTON1DOWN, ~0 },
447    /* S6 */
448    { { S0, S6, S1, S7, S6 }, MOUSE_BUTTON3DOWN, ~0 },
449    /* S7 */
450    { { S0, S6, S5, S7, S7 }, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, ~0 },
451    /* S8 */
452    { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON3DOWN, ~0 },
453    /* S9 */
454    { { S0, S9, S9, S3, S9 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN) },
455};
456static int		mouse_button_state;
457static struct timeval	mouse_button_state_tv;
458
459static jmp_buf env;
460
461/* function prototypes */
462
463static void	moused(void);
464static void	hup(int sig);
465static void	cleanup(int sig);
466static void	usage(void);
467
468static int	r_identify(void);
469static char	*r_if(int type);
470static char	*r_name(int type);
471static char	*r_model(int model);
472static void	r_init(void);
473static int	r_protocol(u_char b, mousestatus_t *act);
474static int	r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans);
475static int	r_installmap(char *arg);
476static void	r_map(mousestatus_t *act1, mousestatus_t *act2);
477static void	r_timestamp(mousestatus_t *act);
478static int	r_timeout(void);
479static void	r_click(mousestatus_t *act);
480static void	setmousespeed(int old, int new, unsigned cflag);
481
482static int	pnpwakeup1(void);
483static int	pnpwakeup2(void);
484static int	pnpgets(char *buf);
485static int	pnpparse(pnpid_t *id, char *buf, int len);
486static symtab_t	*pnpproto(pnpid_t *id);
487
488static symtab_t	*gettoken(symtab_t *tab, char *s, int len);
489static char	*gettokenname(symtab_t *tab, int val);
490
491static void	mremote_serversetup();
492static void	mremote_clientchg(int add);
493
494static int kidspad(u_char rxc, mousestatus_t *act);
495
496int
497main(int argc, char *argv[])
498{
499    int c;
500    int	i;
501    int	j;
502
503    for (i = 0; i < MOUSE_MAXBUTTON; ++i)
504	mstate[i] = &bstate[i];
505
506    while((c = getopt(argc,argv,"3C:DE:F:I:PRS:cdfhi:l:m:p:r:st:w:z:")) != -1)
507	switch(c) {
508
509	case '3':
510	    rodent.flags |= Emulate3Button;
511	    break;
512
513	case 'E':
514	    rodent.button2timeout = atoi(optarg);
515	    if ((rodent.button2timeout < 0) ||
516	        (rodent.button2timeout > MAX_BUTTON2TIMEOUT)) {
517	        warnx("invalid argument `%s'", optarg);
518	        usage();
519	    }
520	    break;
521
522	case 'c':
523	    rodent.flags |= ChordMiddle;
524	    break;
525
526	case 'd':
527	    ++debug;
528	    break;
529
530	case 'f':
531	    nodaemon = TRUE;
532	    break;
533
534	case 'i':
535	    if (strcmp(optarg, "all") == 0)
536	        identify = ID_ALL;
537	    else if (strcmp(optarg, "port") == 0)
538	        identify = ID_PORT;
539	    else if (strcmp(optarg, "if") == 0)
540	        identify = ID_IF;
541	    else if (strcmp(optarg, "type") == 0)
542	        identify = ID_TYPE;
543	    else if (strcmp(optarg, "model") == 0)
544	        identify = ID_MODEL;
545	    else {
546	        warnx("invalid argument `%s'", optarg);
547	        usage();
548	    }
549	    nodaemon = TRUE;
550	    break;
551
552	case 'l':
553	    rodent.level = atoi(optarg);
554	    if ((rodent.level < 0) || (rodent.level > 4)) {
555	        warnx("invalid argument `%s'", optarg);
556	        usage();
557	    }
558	    break;
559
560	case 'm':
561	    if (!r_installmap(optarg)) {
562	        warnx("invalid argument `%s'", optarg);
563	        usage();
564	    }
565	    break;
566
567	case 'p':
568	    rodent.portname = optarg;
569	    break;
570
571	case 'r':
572	    if (strcmp(optarg, "high") == 0)
573	        rodent.resolution = MOUSE_RES_HIGH;
574	    else if (strcmp(optarg, "medium-high") == 0)
575	        rodent.resolution = MOUSE_RES_HIGH;
576	    else if (strcmp(optarg, "medium-low") == 0)
577	        rodent.resolution = MOUSE_RES_MEDIUMLOW;
578	    else if (strcmp(optarg, "low") == 0)
579	        rodent.resolution = MOUSE_RES_LOW;
580	    else if (strcmp(optarg, "default") == 0)
581	        rodent.resolution = MOUSE_RES_DEFAULT;
582	    else {
583	        rodent.resolution = atoi(optarg);
584	        if (rodent.resolution <= 0) {
585	            warnx("invalid argument `%s'", optarg);
586	            usage();
587	        }
588	    }
589	    break;
590
591	case 's':
592	    rodent.baudrate = 9600;
593	    break;
594
595	case 'w':
596	    i = atoi(optarg);
597	    if ((i <= 0) || (i > MOUSE_MAXBUTTON)) {
598		warnx("invalid argument `%s'", optarg);
599		usage();
600	    }
601	    rodent.wmode = 1 << (i - 1);
602	    break;
603
604	case 'z':
605	    if (strcmp(optarg, "x") == 0)
606		rodent.zmap[0] = MOUSE_XAXIS;
607	    else if (strcmp(optarg, "y") == 0)
608		rodent.zmap[0] = MOUSE_YAXIS;
609            else {
610		i = atoi(optarg);
611		/*
612		 * Use button i for negative Z axis movement and
613		 * button (i + 1) for positive Z axis movement.
614		 */
615		if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
616	            warnx("invalid argument `%s'", optarg);
617	            usage();
618		}
619		rodent.zmap[0] = i;
620		rodent.zmap[1] = i + 1;
621		mstate[i - 1] = &zstate[0];
622		mstate[i] = &zstate[1];
623		printf("optind: %d, optarg: '%s'\n", optind, optarg);
624		for (j = 1; j < 4; ++j) {
625		    if ((optind >= argc) || !isdigit(*argv[optind]))
626			break;
627		    i = atoi(argv[optind]);
628		    if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
629			warnx("invalid argument `%s'", argv[optind]);
630			usage();
631		    }
632		    rodent.zmap[j] = i;
633		    mstate[i - 1] = &zstate[j];
634		    ++optind;
635		}
636		if ((rodent.zmap[2] != 0) && (rodent.zmap[3] == 0)) {
637		    rodent.zmap[3] = rodent.zmap[2] + 1;
638		    mstate[rodent.zmap[3] - 1] = &zstate[3];
639		}
640		for (i = 0; i < 4; ++i) {
641		    if (rodent.zmap[i] != 0)
642			rodent.zmap[i] = 1 << (rodent.zmap[i] - 1);
643		}
644	    }
645	    break;
646
647	case 'C':
648	    rodent.clickthreshold = atoi(optarg);
649	    if ((rodent.clickthreshold < 0) ||
650	        (rodent.clickthreshold > MAX_CLICKTHRESHOLD)) {
651	        warnx("invalid argument `%s'", optarg);
652	        usage();
653	    }
654	    break;
655
656	case 'D':
657	    rodent.flags |= ClearDTR;
658	    break;
659
660	case 'F':
661	    rodent.rate = atoi(optarg);
662	    if (rodent.rate <= 0) {
663	        warnx("invalid argument `%s'", optarg);
664	        usage();
665	    }
666	    break;
667
668	case 'I':
669	    pidfile = optarg;
670	    break;
671
672	case 'P':
673	    rodent.flags |= NoPnP;
674	    break;
675
676	case 'R':
677	    rodent.flags |= ClearRTS;
678	    break;
679
680	case 'S':
681	    rodent.baudrate = atoi(optarg);
682	    if (rodent.baudrate <= 0) {
683	        warnx("invalid argument `%s'", optarg);
684	        usage();
685	    }
686	    debug("rodent baudrate %d", rodent.baudrate);
687	    break;
688
689	case 't':
690	    if (strcmp(optarg, "auto") == 0) {
691		rodent.rtype = MOUSE_PROTO_UNKNOWN;
692		rodent.flags &= ~NoPnP;
693		rodent.level = -1;
694		break;
695	    }
696	    for (i = 0; rnames[i]; i++)
697		if (strcmp(optarg, rnames[i]) == 0) {
698		    rodent.rtype = i;
699		    rodent.flags |= NoPnP;
700		    rodent.level = (i == MOUSE_PROTO_SYSMOUSE) ? 1 : 0;
701		    break;
702		}
703	    if (rnames[i])
704		break;
705	    warnx("no such mouse type `%s'", optarg);
706	    usage();
707
708	case 'h':
709	case '?':
710	default:
711	    usage();
712	}
713
714    /* the default port name */
715    switch(rodent.rtype) {
716
717    case MOUSE_PROTO_INPORT:
718        /* INPORT and BUS are the same... */
719	rodent.rtype = MOUSE_PROTO_BUS;
720	/* FALL THROUGH */
721    case MOUSE_PROTO_BUS:
722	if (!rodent.portname)
723	    rodent.portname = "/dev/mse0";
724	break;
725
726    case MOUSE_PROTO_PS2:
727	if (!rodent.portname)
728	    rodent.portname = "/dev/psm0";
729	break;
730
731    default:
732	if (rodent.portname)
733	    break;
734	warnx("no port name specified");
735	usage();
736    }
737
738    for (;;) {
739	if (setjmp(env) == 0) {
740	    signal(SIGHUP, hup);
741	    signal(SIGINT , cleanup);
742	    signal(SIGQUIT, cleanup);
743	    signal(SIGTERM, cleanup);
744            if ((rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK, 0))
745		== -1)
746	        logerr(1, "unable to open %s", rodent.portname);
747            if (r_identify() == MOUSE_PROTO_UNKNOWN) {
748	        logwarnx("cannot determine mouse type on %s", rodent.portname);
749	        close(rodent.mfd);
750	        rodent.mfd = -1;
751            }
752
753	    /* print some information */
754            if (identify != ID_NONE) {
755		if (identify == ID_ALL)
756                    printf("%s %s %s %s\n",
757		        rodent.portname, r_if(rodent.hw.iftype),
758		        r_name(rodent.rtype), r_model(rodent.hw.model));
759		else if (identify & ID_PORT)
760		    printf("%s\n", rodent.portname);
761		else if (identify & ID_IF)
762		    printf("%s\n", r_if(rodent.hw.iftype));
763		else if (identify & ID_TYPE)
764		    printf("%s\n", r_name(rodent.rtype));
765		else if (identify & ID_MODEL)
766		    printf("%s\n", r_model(rodent.hw.model));
767		exit(0);
768	    } else {
769                debug("port: %s  interface: %s  type: %s  model: %s",
770		    rodent.portname, r_if(rodent.hw.iftype),
771		    r_name(rodent.rtype), r_model(rodent.hw.model));
772	    }
773
774	    if (rodent.mfd == -1) {
775	        /*
776	         * We cannot continue because of error.  Exit if the
777		 * program has not become a daemon.  Otherwise, block
778		 * until the the user corrects the problem and issues SIGHUP.
779	         */
780	        if (!background)
781		    exit(1);
782	        sigpause(0);
783	    }
784
785            r_init();			/* call init function */
786	    moused();
787	}
788
789	if (rodent.mfd != -1)
790	    close(rodent.mfd);
791	if (rodent.cfd != -1)
792	    close(rodent.cfd);
793	rodent.mfd = rodent.cfd = -1;
794    }
795    /* NOT REACHED */
796
797    exit(0);
798}
799
800static void
801moused(void)
802{
803    struct mouse_info mouse;
804    mousestatus_t action0;		/* original mouse action */
805    mousestatus_t action;		/* interrim buffer */
806    mousestatus_t action2;		/* mapped action */
807    struct timeval timeout;
808    fd_set fds;
809    u_char b;
810    FILE *fp;
811    int flags;
812    int c;
813    int i;
814
815    if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
816	logerr(1, "cannot open /dev/consolectl", 0);
817
818    if (!nodaemon && !background)
819	if (daemon(0, 0)) {
820	    logerr(1, "failed to become a daemon", 0);
821	} else {
822	    background = TRUE;
823	    fp = fopen(pidfile, "w");
824	    if (fp != NULL) {
825		fprintf(fp, "%d\n", getpid());
826		fclose(fp);
827	    }
828	}
829
830    /* clear mouse data */
831    bzero(&action0, sizeof(action0));
832    bzero(&action, sizeof(action));
833    bzero(&action2, sizeof(action2));
834    bzero(&mouse, sizeof(mouse));
835    mouse_button_state = S0;
836    gettimeofday(&mouse_button_state_tv, NULL);
837    for (i = 0; i < MOUSE_MAXBUTTON; ++i) {
838	bstate[i].count = 0;
839	bstate[i].tv = mouse_button_state_tv;
840    }
841    for (i = 0; i < sizeof(zstate)/sizeof(zstate[0]); ++i) {
842	zstate[i].count = 0;
843	zstate[i].tv = mouse_button_state_tv;
844    }
845
846    /* choose which ioctl command to use */
847    mouse.operation = MOUSE_MOTION_EVENT;
848    extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
849
850    /* process mouse data */
851    timeout.tv_sec = 0;
852    timeout.tv_usec = 20000;		/* 20 msec */
853    for (;;) {
854
855	FD_ZERO(&fds);
856	FD_SET(rodent.mfd, &fds);
857	if (rodent.mremsfd >= 0)
858	    FD_SET(rodent.mremsfd, &fds);
859	if (rodent.mremcfd >= 0)
860	    FD_SET(rodent.mremcfd, &fds);
861
862	c = select(FD_SETSIZE, &fds, NULL, NULL,
863		   (rodent.flags & Emulate3Button) ? &timeout : NULL);
864	if (c < 0) {                    /* error */
865	    logwarn("failed to read from mouse", 0);
866	    continue;
867	} else if (c == 0) {            /* timeout */
868	    /* assert(rodent.flags & Emulate3Button) */
869	    action0.button = action0.obutton;
870	    action0.dx = action0.dy = action0.dz = 0;
871	    action0.flags = flags = 0;
872	    if (r_timeout() && r_statetrans(&action0, &action, A_TIMEOUT)) {
873		if (debug > 2)
874		    debug("flags:%08x buttons:%08x obuttons:%08x",
875			  action.flags, action.button, action.obutton);
876	    } else {
877		action0.obutton = action0.button;
878		continue;
879	    }
880	} else {
881	    /*  MouseRemote client connect/disconnect  */
882	    if ((rodent.mremsfd >= 0) && FD_ISSET(rodent.mremsfd, &fds)) {
883		mremote_clientchg(TRUE);
884		continue;
885	    }
886	    if ((rodent.mremcfd >= 0) && FD_ISSET(rodent.mremcfd, &fds)) {
887		mremote_clientchg(FALSE);
888		continue;
889	    }
890	    /* mouse movement */
891	    if (read(rodent.mfd, &b, 1) == -1) {
892		if (errno == EWOULDBLOCK)
893		    continue;
894		else
895		    return;
896	    }
897	    if ((flags = r_protocol(b, &action0)) == 0)
898		continue;
899	    r_timestamp(&action0);
900	    r_statetrans(&action0, &action,
901	    		 A(action0.button & MOUSE_BUTTON1DOWN,
902	    		   action0.button & MOUSE_BUTTON3DOWN));
903	    debug("flags:%08x buttons:%08x obuttons:%08x", action.flags,
904		  action.button, action.obutton);
905	}
906	action0.obutton = action0.button;
907	flags &= MOUSE_POSCHANGED;
908	flags |= action.obutton ^ action.button;
909	action.flags = flags;
910
911	if (flags) {			/* handler detected action */
912	    r_map(&action, &action2);
913	    debug("activity : buttons 0x%08x  dx %d  dy %d  dz %d",
914		action2.button, action2.dx, action2.dy, action2.dz);
915
916	    if (extioctl) {
917	        r_click(&action2);
918	        if (action2.flags & MOUSE_POSCHANGED) {
919    		    mouse.operation = MOUSE_MOTION_EVENT;
920	            mouse.u.data.buttons = action2.button;
921	            mouse.u.data.x = action2.dx;
922	            mouse.u.data.y = action2.dy;
923	            mouse.u.data.z = action2.dz;
924		    if (debug < 2)
925	                ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
926	        }
927	    } else {
928	        mouse.operation = MOUSE_ACTION;
929	        mouse.u.data.buttons = action2.button;
930	        mouse.u.data.x = action2.dx;
931	        mouse.u.data.y = action2.dy;
932	        mouse.u.data.z = action2.dz;
933		if (debug < 2)
934	            ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
935	    }
936
937            /*
938	     * If the Z axis movement is mapped to a imaginary physical
939	     * button, we need to cook up a corresponding button `up' event
940	     * after sending a button `down' event.
941	     */
942            if ((rodent.zmap[0] > 0) && (action.dz != 0)) {
943		action.obutton = action.button;
944		action.dx = action.dy = action.dz = 0;
945	        r_map(&action, &action2);
946	        debug("activity : buttons 0x%08x  dx %d  dy %d  dz %d",
947		    action2.button, action2.dx, action2.dy, action2.dz);
948
949	        if (extioctl) {
950	            r_click(&action2);
951	        } else {
952	            mouse.operation = MOUSE_ACTION;
953	            mouse.u.data.buttons = action2.button;
954		    mouse.u.data.x = mouse.u.data.y = mouse.u.data.z = 0;
955		    if (debug < 2)
956	                ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
957	        }
958	    }
959	}
960    }
961    /* NOT REACHED */
962}
963
964static void
965hup(int sig)
966{
967    longjmp(env, 1);
968}
969
970static void
971cleanup(int sig)
972{
973    if (rodent.rtype == MOUSE_PROTO_X10MOUSEREM)
974	unlink(_PATH_MOUSEREMOTE);
975    exit(0);
976}
977
978/**
979 ** usage
980 **
981 ** Complain, and free the CPU for more worthy tasks
982 **/
983static void
984usage(void)
985{
986    fprintf(stderr, "%s\n%s\n%s\n",
987	"usage: moused [-DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
988	"              [-C threshold] [-m N=M] [-w N] [-z N] [-t <mousetype>]",
989	"              [-3 [-E timeout]] -p <port>",
990	"       moused [-d] -i <info> -p <port>");
991    exit(1);
992}
993
994/**
995 ** Mouse interface code, courtesy of XFree86 3.1.2.
996 **
997 ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
998 ** to clean, reformat and rationalise naming, it's quite possible that
999 ** some things in here have been broken.
1000 **
1001 ** I hope not 8)
1002 **
1003 ** The following code is derived from a module marked :
1004 **/
1005
1006/* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
1007/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
1008 17:03:40 dawes Exp $ */
1009/*
1010 *
1011 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
1012 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
1013 *
1014 * Permission to use, copy, modify, distribute, and sell this software and its
1015 * documentation for any purpose is hereby granted without fee, provided that
1016 * the above copyright notice appear in all copies and that both that
1017 * copyright notice and this permission notice appear in supporting
1018 * documentation, and that the names of Thomas Roell and David Dawes not be
1019 * used in advertising or publicity pertaining to distribution of the
1020 * software without specific, written prior permission.  Thomas Roell
1021 * and David Dawes makes no representations about the suitability of this
1022 * software for any purpose.  It is provided "as is" without express or
1023 * implied warranty.
1024 *
1025 * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
1026 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
1027 * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
1028 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
1029 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
1030 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1031 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1032 *
1033 */
1034
1035/**
1036 ** GlidePoint support from XFree86 3.2.
1037 ** Derived from the module:
1038 **/
1039
1040/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.19 1996/10/16 14:40:51 dawes Exp $ */
1041/* $XConsortium: xf86_Mouse.c /main/10 1996/01/30 15:16:12 kaleb $ */
1042
1043/* the following table must be ordered by MOUSE_PROTO_XXX in mouse.h */
1044static unsigned char proto[][7] = {
1045    /*  hd_mask hd_id   dp_mask dp_id   bytes b4_mask b4_id */
1046    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x23,  0x00 }, /* MicroSoft */
1047    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* MouseSystems */
1048    {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* Logitech */
1049    {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* MMSeries */
1050    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* MouseMan */
1051    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* Bus */
1052    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* InPort */
1053    {	0xc0,	0x00,	0x00,	0x00,	3,    0x00,  0xff }, /* PS/2 mouse */
1054    {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* MM HitTablet */
1055    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* GlidePoint */
1056    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x3f,  0x00 }, /* IntelliMouse */
1057    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* ThinkingMouse */
1058    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* sysmouse */
1059    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x23,  0x00 }, /* X10 MouseRem */
1060    {	0x80,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* KIDSPAD */
1061    {	0xc3,	0xc0,	0x00,	0x00,	6,    0x00,  0xff }, /* VersaPad */
1062#if notyet
1063    {	0xf8,	0x80,	0x00,	0x00,	5,   ~0x2f,  0x10 }, /* Mariqua */
1064#endif
1065};
1066static unsigned char cur_proto[7];
1067
1068static int
1069r_identify(void)
1070{
1071    char pnpbuf[256];	/* PnP identifier string may be up to 256 bytes long */
1072    pnpid_t pnpid;
1073    symtab_t *t;
1074    int level;
1075    int len;
1076
1077    /* set the driver operation level, if applicable */
1078    if (rodent.level < 0)
1079	rodent.level = 1;
1080    ioctl(rodent.mfd, MOUSE_SETLEVEL, &rodent.level);
1081    rodent.level = (ioctl(rodent.mfd, MOUSE_GETLEVEL, &level) == 0) ? level : 0;
1082
1083    /*
1084     * Interrogate the driver and get some intelligence on the device...
1085     * The following ioctl functions are not always supported by device
1086     * drivers.  When the driver doesn't support them, we just trust the
1087     * user to supply valid information.
1088     */
1089    rodent.hw.iftype = MOUSE_IF_UNKNOWN;
1090    rodent.hw.model = MOUSE_MODEL_GENERIC;
1091    ioctl(rodent.mfd, MOUSE_GETHWINFO, &rodent.hw);
1092
1093    if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1094        bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
1095    rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
1096    rodent.mode.rate = -1;
1097    rodent.mode.resolution = MOUSE_RES_UNKNOWN;
1098    rodent.mode.accelfactor = 0;
1099    rodent.mode.level = 0;
1100    if (ioctl(rodent.mfd, MOUSE_GETMODE, &rodent.mode) == 0) {
1101        if ((rodent.mode.protocol == MOUSE_PROTO_UNKNOWN)
1102	    || (rodent.mode.protocol >= sizeof(proto)/sizeof(proto[0]))) {
1103	    logwarnx("unknown mouse protocol (%d)", rodent.mode.protocol);
1104	    return MOUSE_PROTO_UNKNOWN;
1105        } else {
1106	    /* INPORT and BUS are the same... */
1107	    if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
1108	        rodent.mode.protocol = MOUSE_PROTO_BUS;
1109	    if (rodent.mode.protocol != rodent.rtype) {
1110		/* Hmm, the driver doesn't agree with the user... */
1111                if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1112	            logwarnx("mouse type mismatch (%s != %s), %s is assumed",
1113		        r_name(rodent.mode.protocol), r_name(rodent.rtype),
1114		        r_name(rodent.mode.protocol));
1115	        rodent.rtype = rodent.mode.protocol;
1116                bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
1117	    }
1118        }
1119        cur_proto[4] = rodent.mode.packetsize;
1120        cur_proto[0] = rodent.mode.syncmask[0];	/* header byte bit mask */
1121        cur_proto[1] = rodent.mode.syncmask[1];	/* header bit pattern */
1122    }
1123
1124    /* maybe this is an PnP mouse... */
1125    if (rodent.mode.protocol == MOUSE_PROTO_UNKNOWN) {
1126
1127        if (rodent.flags & NoPnP)
1128            return rodent.rtype;
1129	if (((len = pnpgets(pnpbuf)) <= 0) || !pnpparse(&pnpid, pnpbuf, len))
1130            return rodent.rtype;
1131
1132        debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
1133	    pnpid.neisaid, pnpid.neisaid, pnpid.eisaid,
1134	    pnpid.ncompat, pnpid.ncompat, pnpid.compat,
1135	    pnpid.ndescription, pnpid.ndescription, pnpid.description);
1136
1137	/* we have a valid PnP serial device ID */
1138        rodent.hw.iftype = MOUSE_IF_SERIAL;
1139	t = pnpproto(&pnpid);
1140	if (t != NULL) {
1141            rodent.mode.protocol = t->val;
1142            rodent.hw.model = t->val2;
1143	} else {
1144            rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
1145	}
1146	if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
1147	    rodent.mode.protocol = MOUSE_PROTO_BUS;
1148
1149        /* make final adjustment */
1150	if (rodent.mode.protocol != MOUSE_PROTO_UNKNOWN) {
1151	    if (rodent.mode.protocol != rodent.rtype) {
1152		/* Hmm, the device doesn't agree with the user... */
1153                if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1154	            logwarnx("mouse type mismatch (%s != %s), %s is assumed",
1155		        r_name(rodent.mode.protocol), r_name(rodent.rtype),
1156		        r_name(rodent.mode.protocol));
1157	        rodent.rtype = rodent.mode.protocol;
1158                bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
1159	    }
1160	}
1161    }
1162
1163    debug("proto params: %02x %02x %02x %02x %d %02x %02x",
1164	cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
1165	cur_proto[4], cur_proto[5], cur_proto[6]);
1166
1167    return rodent.rtype;
1168}
1169
1170static char *
1171r_if(int iftype)
1172{
1173    char *s;
1174
1175    s = gettokenname(rifs, iftype);
1176    return (s == NULL) ? "unknown" : s;
1177}
1178
1179static char *
1180r_name(int type)
1181{
1182    return ((type == MOUSE_PROTO_UNKNOWN)
1183	|| (type > sizeof(rnames)/sizeof(rnames[0]) - 1))
1184	? "unknown" : rnames[type];
1185}
1186
1187static char *
1188r_model(int model)
1189{
1190    char *s;
1191
1192    s = gettokenname(rmodels, model);
1193    return (s == NULL) ? "unknown" : s;
1194}
1195
1196static void
1197r_init(void)
1198{
1199    unsigned char buf[16];	/* scrach buffer */
1200    fd_set fds;
1201    char *s;
1202    char c;
1203    int i;
1204
1205    /**
1206     ** This comment is a little out of context here, but it contains
1207     ** some useful information...
1208     ********************************************************************
1209     **
1210     ** The following lines take care of the Logitech MouseMan protocols.
1211     **
1212     ** NOTE: There are different versions of both MouseMan and TrackMan!
1213     **       Hence I add another protocol P_LOGIMAN, which the user can
1214     **       specify as MouseMan in his XF86Config file. This entry was
1215     **       formerly handled as a special case of P_MS. However, people
1216     **       who don't have the middle button problem, can still specify
1217     **       Microsoft and use P_MS.
1218     **
1219     ** By default, these mice should use a 3 byte Microsoft protocol
1220     ** plus a 4th byte for the middle button. However, the mouse might
1221     ** have switched to a different protocol before we use it, so I send
1222     ** the proper sequence just in case.
1223     **
1224     ** NOTE: - all commands to (at least the European) MouseMan have to
1225     **         be sent at 1200 Baud.
1226     **       - each command starts with a '*'.
1227     **       - whenever the MouseMan receives a '*', it will switch back
1228     **	 to 1200 Baud. Hence I have to select the desired protocol
1229     **	 first, then select the baud rate.
1230     **
1231     ** The protocols supported by the (European) MouseMan are:
1232     **   -  5 byte packed binary protocol, as with the Mouse Systems
1233     **      mouse. Selected by sequence "*U".
1234     **   -  2 button 3 byte MicroSoft compatible protocol. Selected
1235     **      by sequence "*V".
1236     **   -  3 button 3+1 byte MicroSoft compatible protocol (default).
1237     **      Selected by sequence "*X".
1238     **
1239     ** The following baud rates are supported:
1240     **   -  1200 Baud (default). Selected by sequence "*n".
1241     **   -  9600 Baud. Selected by sequence "*q".
1242     **
1243     ** Selecting a sample rate is no longer supported with the MouseMan!
1244     ** Some additional lines in xf86Config.c take care of ill configured
1245     ** baud rates and sample rates. (The user will get an error.)
1246     */
1247
1248    switch (rodent.rtype) {
1249
1250    case MOUSE_PROTO_LOGI:
1251	/*
1252	 * The baud rate selection command must be sent at the current
1253	 * baud rate; try all likely settings
1254	 */
1255	setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
1256	setmousespeed(4800, rodent.baudrate, rodentcflags[rodent.rtype]);
1257	setmousespeed(2400, rodent.baudrate, rodentcflags[rodent.rtype]);
1258	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1259	/* select MM series data format */
1260	write(rodent.mfd, "S", 1);
1261	setmousespeed(rodent.baudrate, rodent.baudrate,
1262		      rodentcflags[MOUSE_PROTO_MM]);
1263	/* select report rate/frequency */
1264	if      (rodent.rate <= 0)   write(rodent.mfd, "O", 1);
1265	else if (rodent.rate <= 15)  write(rodent.mfd, "J", 1);
1266	else if (rodent.rate <= 27)  write(rodent.mfd, "K", 1);
1267	else if (rodent.rate <= 42)  write(rodent.mfd, "L", 1);
1268	else if (rodent.rate <= 60)  write(rodent.mfd, "R", 1);
1269	else if (rodent.rate <= 85)  write(rodent.mfd, "M", 1);
1270	else if (rodent.rate <= 125) write(rodent.mfd, "Q", 1);
1271	else			     write(rodent.mfd, "N", 1);
1272	break;
1273
1274    case MOUSE_PROTO_LOGIMOUSEMAN:
1275	/* The command must always be sent at 1200 baud */
1276	setmousespeed(1200, 1200, rodentcflags[rodent.rtype]);
1277	write(rodent.mfd, "*X", 2);
1278	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1279	break;
1280
1281    case MOUSE_PROTO_HITTAB:
1282	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1283
1284	/*
1285	 * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
1286	 * The tablet must be configured to be in MM mode, NO parity,
1287	 * Binary Format.  xf86Info.sampleRate controls the sensativity
1288	 * of the tablet.  We only use this tablet for it's 4-button puck
1289	 * so we don't run in "Absolute Mode"
1290	 */
1291	write(rodent.mfd, "z8", 2);	/* Set Parity = "NONE" */
1292	usleep(50000);
1293	write(rodent.mfd, "zb", 2);	/* Set Format = "Binary" */
1294	usleep(50000);
1295	write(rodent.mfd, "@", 1);	/* Set Report Mode = "Stream" */
1296	usleep(50000);
1297	write(rodent.mfd, "R", 1);	/* Set Output Rate = "45 rps" */
1298	usleep(50000);
1299	write(rodent.mfd, "I\x20", 2);	/* Set Incrememtal Mode "20" */
1300	usleep(50000);
1301	write(rodent.mfd, "E", 1);	/* Set Data Type = "Relative */
1302	usleep(50000);
1303
1304	/* Resolution is in 'lines per inch' on the Hitachi tablet */
1305	if      (rodent.resolution == MOUSE_RES_LOW) 		c = 'g';
1306	else if (rodent.resolution == MOUSE_RES_MEDIUMLOW)	c = 'e';
1307	else if (rodent.resolution == MOUSE_RES_MEDIUMHIGH)	c = 'h';
1308	else if (rodent.resolution == MOUSE_RES_HIGH)		c = 'd';
1309	else if (rodent.resolution <=   40) 			c = 'g';
1310	else if (rodent.resolution <=  100) 			c = 'd';
1311	else if (rodent.resolution <=  200) 			c = 'e';
1312	else if (rodent.resolution <=  500) 			c = 'h';
1313	else if (rodent.resolution <= 1000) 			c = 'j';
1314	else                                			c = 'd';
1315	write(rodent.mfd, &c, 1);
1316	usleep(50000);
1317
1318	write(rodent.mfd, "\021", 1);	/* Resume DATA output */
1319	break;
1320
1321    case MOUSE_PROTO_THINK:
1322	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1323	/* the PnP ID string may be sent again, discard it */
1324	usleep(200000);
1325	i = FREAD;
1326	ioctl(rodent.mfd, TIOCFLUSH, &i);
1327	/* send the command to initialize the beast */
1328	for (s = "E5E5"; *s; ++s) {
1329	    write(rodent.mfd, s, 1);
1330	    FD_ZERO(&fds);
1331	    FD_SET(rodent.mfd, &fds);
1332	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1333		break;
1334	    read(rodent.mfd, &c, 1);
1335	    debug("%c", c);
1336	    if (c != *s)
1337	        break;
1338	}
1339	break;
1340
1341    case MOUSE_PROTO_MSC:
1342	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1343	if (rodent.flags & ClearDTR) {
1344	   i = TIOCM_DTR;
1345	   ioctl(rodent.mfd, TIOCMBIC, &i);
1346        }
1347        if (rodent.flags & ClearRTS) {
1348	   i = TIOCM_RTS;
1349	   ioctl(rodent.mfd, TIOCMBIC, &i);
1350        }
1351	break;
1352
1353    case MOUSE_PROTO_SYSMOUSE:
1354	if (rodent.hw.iftype == MOUSE_IF_SYSMOUSE)
1355	    setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1356	/* fall through */
1357
1358    case MOUSE_PROTO_BUS:
1359    case MOUSE_PROTO_INPORT:
1360    case MOUSE_PROTO_PS2:
1361	if (rodent.rate >= 0)
1362	    rodent.mode.rate = rodent.rate;
1363	if (rodent.resolution != MOUSE_RES_UNKNOWN)
1364	    rodent.mode.resolution = rodent.resolution;
1365	ioctl(rodent.mfd, MOUSE_SETMODE, &rodent.mode);
1366	break;
1367
1368    case MOUSE_PROTO_X10MOUSEREM:
1369	mremote_serversetup();
1370	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1371	break;
1372
1373
1374    case MOUSE_PROTO_VERSAPAD:
1375	tcsendbreak(rodent.mfd, 0);	/* send break for 400 msec */
1376	i = FREAD;
1377	ioctl(rodent.mfd, TIOCFLUSH, &i);
1378	for (i = 0; i < 7; ++i) {
1379	    FD_ZERO(&fds);
1380	    FD_SET(rodent.mfd, &fds);
1381	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1382		break;
1383	    read(rodent.mfd, &c, 1);
1384	    buf[i] = c;
1385	}
1386	debug("%s\n", buf);
1387	if ((buf[0] != 'V') || (buf[1] != 'P')|| (buf[7] != '\r'))
1388	    break;
1389	setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
1390	tcsendbreak(rodent.mfd, 0);	/* send break for 400 msec again */
1391	for (i = 0; i < 7; ++i) {
1392	    FD_ZERO(&fds);
1393	    FD_SET(rodent.mfd, &fds);
1394	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1395		break;
1396	    read(rodent.mfd, &c, 1);
1397	    debug("%c", c);
1398	    if (c != buf[i])
1399		break;
1400	}
1401	i = FREAD;
1402	ioctl(rodent.mfd, TIOCFLUSH, &i);
1403	break;
1404
1405    default:
1406	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1407	break;
1408    }
1409}
1410
1411static int
1412r_protocol(u_char rBuf, mousestatus_t *act)
1413{
1414    /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1415    static int butmapmss[4] = {	/* Microsoft, MouseMan, GlidePoint,
1416				   IntelliMouse, Thinking Mouse */
1417	0,
1418	MOUSE_BUTTON3DOWN,
1419	MOUSE_BUTTON1DOWN,
1420	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1421    };
1422    static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
1423				    Thinking Mouse */
1424	0,
1425	MOUSE_BUTTON4DOWN,
1426	MOUSE_BUTTON2DOWN,
1427	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1428    };
1429    /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1430    static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
1431				       MouseMan+ */
1432	0,
1433	MOUSE_BUTTON2DOWN,
1434	MOUSE_BUTTON4DOWN,
1435	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1436    };
1437    /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
1438    static int butmapmsc[8] = {	/* MouseSystems, MMSeries, Logitech,
1439				   Bus, sysmouse */
1440	0,
1441	MOUSE_BUTTON3DOWN,
1442	MOUSE_BUTTON2DOWN,
1443	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1444	MOUSE_BUTTON1DOWN,
1445	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1446	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1447	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1448    };
1449    /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1450    static int butmapps2[8] = {	/* PS/2 */
1451	0,
1452	MOUSE_BUTTON1DOWN,
1453	MOUSE_BUTTON3DOWN,
1454	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1455	MOUSE_BUTTON2DOWN,
1456	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1457	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1458	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1459    };
1460    /* for Hitachi tablet */
1461    static int butmaphit[8] = {	/* MM HitTablet */
1462	0,
1463	MOUSE_BUTTON3DOWN,
1464	MOUSE_BUTTON2DOWN,
1465	MOUSE_BUTTON1DOWN,
1466	MOUSE_BUTTON4DOWN,
1467	MOUSE_BUTTON5DOWN,
1468	MOUSE_BUTTON6DOWN,
1469	MOUSE_BUTTON7DOWN,
1470    };
1471    /* for serial VersaPad */
1472    static int butmapversa[8] = { /* VersaPad */
1473	0,
1474	0,
1475	MOUSE_BUTTON3DOWN,
1476	MOUSE_BUTTON3DOWN,
1477	MOUSE_BUTTON1DOWN,
1478	MOUSE_BUTTON1DOWN,
1479	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1480	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1481    };
1482    /* for PS/2 VersaPad */
1483    static int butmapversaps2[8] = { /* VersaPad */
1484	0,
1485	MOUSE_BUTTON3DOWN,
1486	0,
1487	MOUSE_BUTTON3DOWN,
1488	MOUSE_BUTTON1DOWN,
1489	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1490	MOUSE_BUTTON1DOWN,
1491	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1492    };
1493    static int           pBufP = 0;
1494    static unsigned char pBuf[8];
1495    static int		 prev_x, prev_y;
1496    static int		 on = FALSE;
1497    int			 x, y;
1498
1499    debug("received char 0x%x",(int)rBuf);
1500    if (rodent.rtype == MOUSE_PROTO_KIDSPAD)
1501	return kidspad(rBuf, act) ;
1502
1503    /*
1504     * Hack for resyncing: We check here for a package that is:
1505     *  a) illegal (detected by wrong data-package header)
1506     *  b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
1507     *  c) bad header-package
1508     *
1509     * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
1510     *       -128 are allowed, but since they are very seldom we can easily
1511     *       use them as package-header with no button pressed.
1512     * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
1513     *         0x80 is not valid as a header byte. For a PS/2 mouse we skip
1514     *         checking data bytes.
1515     *         For resyncing a PS/2 mouse we require the two most significant
1516     *         bits in the header byte to be 0. These are the overflow bits,
1517     *         and in case of an overflow we actually lose sync. Overflows
1518     *         are very rare, however, and we quickly gain sync again after
1519     *         an overflow condition. This is the best we can do. (Actually,
1520     *         we could use bit 0x08 in the header byte for resyncing, since
1521     *         that bit is supposed to be always on, but nobody told
1522     *         Microsoft...)
1523     */
1524
1525    if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
1526	((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
1527    {
1528	pBufP = 0;		/* skip package */
1529    }
1530
1531    if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
1532	return 0;
1533
1534    /* is there an extra data byte? */
1535    if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
1536    {
1537	/*
1538	 * Hack for Logitech MouseMan Mouse - Middle button
1539	 *
1540	 * Unfortunately this mouse has variable length packets: the standard
1541	 * Microsoft 3 byte packet plus an optional 4th byte whenever the
1542	 * middle button status changes.
1543	 *
1544	 * We have already processed the standard packet with the movement
1545	 * and button info.  Now post an event message with the old status
1546	 * of the left and right buttons and the updated middle button.
1547	 */
1548
1549	/*
1550	 * Even worse, different MouseMen and TrackMen differ in the 4th
1551	 * byte: some will send 0x00/0x20, others 0x01/0x21, or even
1552	 * 0x02/0x22, so I have to strip off the lower bits.
1553         *
1554         * [JCH-96/01/21]
1555         * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
1556         * and it is activated by tapping the glidepad with the finger! 8^)
1557         * We map it to bit bit3, and the reverse map in xf86Events just has
1558         * to be extended so that it is identified as Button 4. The lower
1559         * half of the reverse-map may remain unchanged.
1560	 */
1561
1562        /*
1563	 * [KY-97/08/03]
1564	 * Receive the fourth byte only when preceeding three bytes have
1565	 * been detected (pBufP >= cur_proto[4]).  In the previous
1566	 * versions, the test was pBufP == 0; thus, we may have mistakingly
1567	 * received a byte even if we didn't see anything preceeding
1568	 * the byte.
1569	 */
1570
1571	if ((rBuf & cur_proto[5]) != cur_proto[6]) {
1572            pBufP = 0;
1573	    return 0;
1574	}
1575
1576	switch (rodent.rtype) {
1577#if notyet
1578	case MOUSE_PROTO_MARIQUA:
1579	    /*
1580	     * This mouse has 16! buttons in addition to the standard
1581	     * three of them.  They return 0x10 though 0x1f in the
1582	     * so-called `ten key' mode and 0x30 though 0x3f in the
1583	     * `function key' mode.  As there are only 31 bits for
1584	     * button state (including the standard three), we ignore
1585	     * the bit 0x20 and don't distinguish the two modes.
1586	     */
1587	    act->dx = act->dy = act->dz = 0;
1588	    act->obutton = act->button;
1589	    rBuf &= 0x1f;
1590	    act->button = (1 << (rBuf - 13))
1591                | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1592	    /*
1593	     * FIXME: this is a button "down" event. There needs to be
1594	     * a corresponding button "up" event... XXX
1595	     */
1596	    break;
1597#endif /* notyet */
1598
1599	/*
1600	 * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
1601	 * always send the fourth byte, whereas the fourth byte is
1602	 * optional for GlidePoint and ThinkingMouse. The fourth byte
1603	 * is also optional for MouseMan+ and FirstMouse+ in their
1604	 * native mode. It is always sent if they are in the IntelliMouse
1605	 * compatible mode.
1606	 */
1607	case MOUSE_PROTO_INTELLI:	/* IntelliMouse, NetMouse, Mie Mouse,
1608					   MouseMan+ */
1609	    act->dx = act->dy = 0;
1610	    act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
1611	    if ((act->dz >= 7) || (act->dz <= -7))
1612		act->dz = 0;
1613	    act->obutton = act->button;
1614	    act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1615		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1616	    break;
1617
1618	default:
1619	    act->dx = act->dy = act->dz = 0;
1620	    act->obutton = act->button;
1621	    act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1622		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1623	    break;
1624	}
1625
1626	act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1627	    | (act->obutton ^ act->button);
1628        pBufP = 0;
1629	return act->flags;
1630    }
1631
1632    if (pBufP >= cur_proto[4])
1633	pBufP = 0;
1634    pBuf[pBufP++] = rBuf;
1635    if (pBufP != cur_proto[4])
1636	return 0;
1637
1638    /*
1639     * assembly full package
1640     */
1641
1642    debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
1643	cur_proto[4],
1644	pBuf[0], pBuf[1], pBuf[2], pBuf[3],
1645	pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
1646
1647    act->dz = 0;
1648    act->obutton = act->button;
1649    switch (rodent.rtype)
1650    {
1651    case MOUSE_PROTO_MS:		/* Microsoft */
1652    case MOUSE_PROTO_LOGIMOUSEMAN:	/* MouseMan/TrackMan */
1653    case MOUSE_PROTO_X10MOUSEREM:	/* X10 MouseRemote */
1654	act->button = act->obutton & MOUSE_BUTTON4DOWN;
1655	if (rodent.flags & ChordMiddle)
1656	    act->button |= ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
1657		? MOUSE_BUTTON2DOWN
1658		: butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1659	else
1660	    act->button |= (act->obutton & MOUSE_BUTTON2DOWN)
1661		| butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1662
1663	/* Send X10 btn events to remote client (ensure -128-+127 range) */
1664	if ((rodent.rtype == MOUSE_PROTO_X10MOUSEREM) &&
1665	    ((pBuf[0] & 0xFC) == 0x44) && (pBuf[2] == 0x3F)) {
1666	    if (rodent.mremcfd >= 0) {
1667		unsigned char key = (signed char)(((pBuf[0] & 0x03) << 6) |
1668						  (pBuf[1] & 0x3F));
1669		write( rodent.mremcfd, &key, 1 );
1670	    }
1671	    return 0;
1672	}
1673
1674	act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1675	act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1676	break;
1677
1678    case MOUSE_PROTO_GLIDEPOINT:	/* GlidePoint */
1679    case MOUSE_PROTO_THINK:		/* ThinkingMouse */
1680    case MOUSE_PROTO_INTELLI:		/* IntelliMouse, NetMouse, Mie Mouse,
1681					   MouseMan+ */
1682	act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
1683            | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1684	act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1685	act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1686	break;
1687
1688    case MOUSE_PROTO_MSC:		/* MouseSystems Corp */
1689#if notyet
1690    case MOUSE_PROTO_MARIQUA:		/* Mariqua */
1691#endif
1692	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1693	act->dx =    (char)(pBuf[1]) + (char)(pBuf[3]);
1694	act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
1695	break;
1696
1697    case MOUSE_PROTO_HITTAB:		/* MM HitTablet */
1698	act->button = butmaphit[pBuf[0] & 0x07];
1699	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1700	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1701	break;
1702
1703    case MOUSE_PROTO_MM:		/* MM Series */
1704    case MOUSE_PROTO_LOGI:		/* Logitech Mice */
1705	act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
1706	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1707	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1708	break;
1709
1710    case MOUSE_PROTO_VERSAPAD:		/* VersaPad */
1711	act->button = butmapversa[(pBuf[0] & MOUSE_VERSA_BUTTONS) >> 3];
1712	act->button |= (pBuf[0] & MOUSE_VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
1713	act->dx = act->dy = 0;
1714	if (!(pBuf[0] & MOUSE_VERSA_IN_USE)) {
1715	    on = FALSE;
1716	    break;
1717	}
1718	x = (pBuf[2] << 6) | pBuf[1];
1719	if (x & 0x800)
1720	    x -= 0x1000;
1721	y = (pBuf[4] << 6) | pBuf[3];
1722	if (y & 0x800)
1723	    y -= 0x1000;
1724	if (on) {
1725	    act->dx = prev_x - x;
1726	    act->dy = prev_y - y;
1727	} else {
1728	    on = TRUE;
1729	}
1730	prev_x = x;
1731	prev_y = y;
1732	break;
1733
1734    case MOUSE_PROTO_BUS:		/* Bus */
1735    case MOUSE_PROTO_INPORT:		/* InPort */
1736	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1737	act->dx =   (char)pBuf[1];
1738	act->dy = - (char)pBuf[2];
1739	break;
1740
1741    case MOUSE_PROTO_PS2:		/* PS/2 */
1742	act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
1743	act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ?    pBuf[1] - 256  :  pBuf[1];
1744	act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ?  -(pBuf[2] - 256) : -pBuf[2];
1745	/*
1746	 * Moused usually operates the psm driver at the operation level 1
1747	 * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
1748	 * The following code takes effect only when the user explicitly
1749	 * requets the level 2 at which wheel movement and additional button
1750	 * actions are encoded in model-dependent formats. At the level 0
1751	 * the following code is no-op because the psm driver says the model
1752	 * is MOUSE_MODEL_GENERIC.
1753	 */
1754	switch (rodent.hw.model) {
1755	case MOUSE_MODEL_EXPLORER:
1756	    /* wheel and additional button data is in the fourth byte */
1757	    act->dz = (pBuf[3] & MOUSE_EXPLORER_ZNEG)
1758		? (pBuf[3] & 0x0f) - 16 : (pBuf[3] & 0x0f);
1759	    act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON4DOWN)
1760		? MOUSE_BUTTON4DOWN : 0;
1761	    act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON5DOWN)
1762		? MOUSE_BUTTON5DOWN : 0;
1763	    break;
1764	case MOUSE_MODEL_INTELLI:
1765	case MOUSE_MODEL_NET:
1766	    /* wheel data is in the fourth byte */
1767	    act->dz = (char)pBuf[3];
1768	    if ((act->dz >= 7) || (act->dz <= -7))
1769		act->dz = 0;
1770	    /* some compatible mice may have additional buttons */
1771	    act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON4DOWN)
1772		? MOUSE_BUTTON4DOWN : 0;
1773	    act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON5DOWN)
1774		? MOUSE_BUTTON5DOWN : 0;
1775	    break;
1776	case MOUSE_MODEL_MOUSEMANPLUS:
1777	    if (((pBuf[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
1778		    && (abs(act->dx) > 191)
1779		    && MOUSE_PS2PLUS_CHECKBITS(pBuf)) {
1780		/* the extended data packet encodes button and wheel events */
1781		switch (MOUSE_PS2PLUS_PACKET_TYPE(pBuf)) {
1782		case 1:
1783		    /* wheel data packet */
1784		    act->dx = act->dy = 0;
1785		    if (pBuf[2] & 0x80) {
1786			/* horizontal roller count - ignore it XXX*/
1787		    } else {
1788			/* vertical roller count */
1789			act->dz = (pBuf[2] & MOUSE_PS2PLUS_ZNEG)
1790			    ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
1791		    }
1792		    act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
1793			? MOUSE_BUTTON4DOWN : 0;
1794		    act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
1795			? MOUSE_BUTTON5DOWN : 0;
1796		    break;
1797		case 2:
1798		    /* this packet type is reserved by Logitech */
1799		    /*
1800		     * IBM ScrollPoint Mouse uses this packet type to
1801		     * encode both vertical and horizontal scroll movement.
1802		     */
1803		    act->dx = act->dy = 0;
1804		    /* horizontal roller count */
1805		    if (pBuf[2] & 0x0f)
1806			act->dz = (pBuf[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
1807		    /* vertical roller count */
1808		    if (pBuf[2] & 0xf0)
1809			act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
1810#if 0
1811		    /* vertical roller count */
1812		    act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG)
1813			? ((pBuf[2] >> 4) & 0x0f) - 16
1814			: ((pBuf[2] >> 4) & 0x0f);
1815		    /* horizontal roller count */
1816		    act->dw = (pBuf[2] & MOUSE_SPOINT_WNEG)
1817			? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
1818#endif
1819		    break;
1820		case 0:
1821		    /* device type packet - shouldn't happen */
1822		    /* FALL THROUGH */
1823		default:
1824		    act->dx = act->dy = 0;
1825		    act->button = act->obutton;
1826            	    debug("unknown PS2++ packet type %d: 0x%02x 0x%02x 0x%02x\n",
1827			  MOUSE_PS2PLUS_PACKET_TYPE(pBuf),
1828			  pBuf[0], pBuf[1], pBuf[2]);
1829		    break;
1830		}
1831	    } else {
1832		/* preserve button states */
1833		act->button |= act->obutton & MOUSE_EXTBUTTONS;
1834	    }
1835	    break;
1836	case MOUSE_MODEL_GLIDEPOINT:
1837	    /* `tapping' action */
1838	    act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
1839	    break;
1840	case MOUSE_MODEL_NETSCROLL:
1841	    /* three addtional bytes encode buttons and wheel events */
1842	    act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
1843		? MOUSE_BUTTON4DOWN : 0;
1844	    act->button |= (pBuf[3] & MOUSE_PS2_BUTTON1DOWN)
1845		? MOUSE_BUTTON5DOWN : 0;
1846	    act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
1847	    break;
1848	case MOUSE_MODEL_THINK:
1849	    /* the fourth button state in the first byte */
1850	    act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
1851	    break;
1852	case MOUSE_MODEL_VERSAPAD:
1853	    act->button = butmapversaps2[pBuf[0] & MOUSE_PS2VERSA_BUTTONS];
1854	    act->button |=
1855		(pBuf[0] & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
1856	    act->dx = act->dy = 0;
1857	    if (!(pBuf[0] & MOUSE_PS2VERSA_IN_USE)) {
1858		on = FALSE;
1859		break;
1860	    }
1861	    x = ((pBuf[4] << 8) & 0xf00) | pBuf[1];
1862	    if (x & 0x800)
1863		x -= 0x1000;
1864	    y = ((pBuf[4] << 4) & 0xf00) | pBuf[2];
1865	    if (y & 0x800)
1866		y -= 0x1000;
1867	    if (on) {
1868		act->dx = prev_x - x;
1869		act->dy = prev_y - y;
1870	    } else {
1871		on = TRUE;
1872	    }
1873	    prev_x = x;
1874	    prev_y = y;
1875	    break;
1876	case MOUSE_MODEL_4D:
1877	    act->dx = (pBuf[1] & 0x80) ?    pBuf[1] - 256  :  pBuf[1];
1878	    act->dy = (pBuf[2] & 0x80) ?  -(pBuf[2] - 256) : -pBuf[2];
1879	    switch (pBuf[0] & MOUSE_4D_WHEELBITS) {
1880	    case 0x10:
1881		act->dz = 1;
1882		break;
1883	    case 0x30:
1884		act->dz = -1;
1885		break;
1886	    case 0x40:	/* 2nd wheel rolling right XXX */
1887		act->dz = 2;
1888		break;
1889	    case 0xc0:	/* 2nd wheel rolling left XXX */
1890		act->dz = -2;
1891		break;
1892	    }
1893	    break;
1894	case MOUSE_MODEL_4DPLUS:
1895	    if ((act->dx < 16 - 256) && (act->dy > 256 - 16)) {
1896		act->dx = act->dy = 0;
1897		if (pBuf[2] & MOUSE_4DPLUS_BUTTON4DOWN)
1898		    act->button |= MOUSE_BUTTON4DOWN;
1899		act->dz = (pBuf[2] & MOUSE_4DPLUS_ZNEG)
1900			      ? ((pBuf[2] & 0x07) - 8) : (pBuf[2] & 0x07);
1901	    } else {
1902		/* preserve previous button states */
1903		act->button |= act->obutton & MOUSE_EXTBUTTONS;
1904	    }
1905	    break;
1906	case MOUSE_MODEL_GENERIC:
1907	default:
1908	    break;
1909	}
1910	break;
1911
1912    case MOUSE_PROTO_SYSMOUSE:		/* sysmouse */
1913	act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
1914	act->dx =    (char)(pBuf[1]) + (char)(pBuf[3]);
1915	act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
1916	if (rodent.level == 1) {
1917	    act->dz = ((char)(pBuf[5] << 1) + (char)(pBuf[6] << 1))/2;
1918	    act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
1919	}
1920	break;
1921
1922    default:
1923	return 0;
1924    }
1925    /*
1926     * We don't reset pBufP here yet, as there may be an additional data
1927     * byte in some protocols. See above.
1928     */
1929
1930    /* has something changed? */
1931    act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1932	| (act->obutton ^ act->button);
1933
1934    return act->flags;
1935}
1936
1937static int
1938r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans)
1939{
1940    int changed;
1941    int flags;
1942
1943    a2->dx = a1->dx;
1944    a2->dy = a1->dy;
1945    a2->dz = a1->dz;
1946    a2->obutton = a2->button;
1947    a2->button = a1->button;
1948    a2->flags = a1->flags;
1949    changed = FALSE;
1950
1951    if (rodent.flags & Emulate3Button) {
1952	if (debug > 2)
1953	    debug("state:%d, trans:%d -> state:%d",
1954		  mouse_button_state, trans,
1955		  states[mouse_button_state].s[trans]);
1956	if (mouse_button_state != states[mouse_button_state].s[trans]) {
1957	    gettimeofday(&mouse_button_state_tv, NULL);
1958	    changed = TRUE;
1959	}
1960	mouse_button_state = states[mouse_button_state].s[trans];
1961	a2->button &=
1962	    ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN);
1963	a2->button &= states[mouse_button_state].mask;
1964	a2->button |= states[mouse_button_state].buttons;
1965	flags = a2->flags & MOUSE_POSCHANGED;
1966	flags |= a2->obutton ^ a2->button;
1967	if (flags & MOUSE_BUTTON2DOWN) {
1968	    a2->flags = flags & MOUSE_BUTTON2DOWN;
1969	    r_timestamp(a2);
1970	}
1971	a2->flags = flags;
1972    }
1973    return changed;
1974}
1975
1976/* phisical to logical button mapping */
1977static int p2l[MOUSE_MAXBUTTON] = {
1978    MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
1979    MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
1980    0x00000100,        0x00000200,        0x00000400,        0x00000800,
1981    0x00001000,        0x00002000,        0x00004000,        0x00008000,
1982    0x00010000,        0x00020000,        0x00040000,        0x00080000,
1983    0x00100000,        0x00200000,        0x00400000,        0x00800000,
1984    0x01000000,        0x02000000,        0x04000000,        0x08000000,
1985    0x10000000,        0x20000000,        0x40000000,
1986};
1987
1988static char *
1989skipspace(char *s)
1990{
1991    while(isspace(*s))
1992	++s;
1993    return s;
1994}
1995
1996static int
1997r_installmap(char *arg)
1998{
1999    int pbutton;
2000    int lbutton;
2001    char *s;
2002
2003    while (*arg) {
2004	arg = skipspace(arg);
2005	s = arg;
2006	while (isdigit(*arg))
2007	    ++arg;
2008	arg = skipspace(arg);
2009	if ((arg <= s) || (*arg != '='))
2010	    return FALSE;
2011	lbutton = atoi(s);
2012
2013	arg = skipspace(++arg);
2014	s = arg;
2015	while (isdigit(*arg))
2016	    ++arg;
2017	if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
2018	    return FALSE;
2019	pbutton = atoi(s);
2020
2021	if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
2022	    return FALSE;
2023	if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
2024	    return FALSE;
2025	p2l[pbutton - 1] = 1 << (lbutton - 1);
2026	mstate[lbutton - 1] = &bstate[pbutton - 1];
2027    }
2028
2029    return TRUE;
2030}
2031
2032static void
2033r_map(mousestatus_t *act1, mousestatus_t *act2)
2034{
2035    register int pb;
2036    register int pbuttons;
2037    int lbuttons;
2038
2039    pbuttons = act1->button;
2040    lbuttons = 0;
2041
2042    act2->obutton = act2->button;
2043    if (pbuttons & rodent.wmode) {
2044	pbuttons &= ~rodent.wmode;
2045	act1->dz = act1->dy;
2046	act1->dx = 0;
2047	act1->dy = 0;
2048    }
2049    act2->dx = act1->dx;
2050    act2->dy = act1->dy;
2051    act2->dz = act1->dz;
2052
2053    switch (rodent.zmap[0]) {
2054    case 0:	/* do nothing */
2055	break;
2056    case MOUSE_XAXIS:
2057	if (act1->dz != 0) {
2058	    act2->dx = act1->dz;
2059	    act2->dz = 0;
2060	}
2061	break;
2062    case MOUSE_YAXIS:
2063	if (act1->dz != 0) {
2064	    act2->dy = act1->dz;
2065	    act2->dz = 0;
2066	}
2067	break;
2068    default:	/* buttons */
2069	pbuttons &= ~(rodent.zmap[0] | rodent.zmap[1]
2070		    | rodent.zmap[2] | rodent.zmap[3]);
2071	if ((act1->dz < -1) && rodent.zmap[2]) {
2072	    pbuttons |= rodent.zmap[2];
2073	    zstate[2].count = 1;
2074	} else if (act1->dz < 0) {
2075	    pbuttons |= rodent.zmap[0];
2076	    zstate[0].count = 1;
2077	} else if ((act1->dz > 1) && rodent.zmap[3]) {
2078	    pbuttons |= rodent.zmap[3];
2079	    zstate[3].count = 1;
2080	} else if (act1->dz > 0) {
2081	    pbuttons |= rodent.zmap[1];
2082	    zstate[1].count = 1;
2083	}
2084	act2->dz = 0;
2085	break;
2086    }
2087
2088    for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
2089	lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
2090	pbuttons >>= 1;
2091    }
2092    act2->button = lbuttons;
2093
2094    act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
2095	| (act2->obutton ^ act2->button);
2096}
2097
2098static void
2099r_timestamp(mousestatus_t *act)
2100{
2101    struct timeval tv;
2102    struct timeval tv1;
2103    struct timeval tv2;
2104    struct timeval tv3;
2105    int button;
2106    int mask;
2107    int i;
2108
2109    mask = act->flags & MOUSE_BUTTONS;
2110#if 0
2111    if (mask == 0)
2112	return;
2113#endif
2114
2115    gettimeofday(&tv1, NULL);
2116
2117    /* double click threshold */
2118    tv2.tv_sec = rodent.clickthreshold/1000;
2119    tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
2120    timersub(&tv1, &tv2, &tv);
2121    debug("tv:  %ld %ld", tv.tv_sec, tv.tv_usec);
2122
2123    /* 3 button emulation timeout */
2124    tv2.tv_sec = rodent.button2timeout/1000;
2125    tv2.tv_usec = (rodent.button2timeout%1000)*1000;
2126    timersub(&tv1, &tv2, &tv3);
2127
2128    button = MOUSE_BUTTON1DOWN;
2129    for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
2130        if (mask & 1) {
2131            if (act->button & button) {
2132                /* the button is down */
2133    		debug("  :  %ld %ld",
2134		    bstate[i].tv.tv_sec, bstate[i].tv.tv_usec);
2135		if (timercmp(&tv, &bstate[i].tv, >)) {
2136                    bstate[i].count = 1;
2137                } else {
2138                    ++bstate[i].count;
2139                }
2140		bstate[i].tv = tv1;
2141            } else {
2142                /* the button is up */
2143                bstate[i].tv = tv1;
2144            }
2145        } else {
2146	    if (act->button & button) {
2147		/* the button has been down */
2148		if (timercmp(&tv3, &bstate[i].tv, >)) {
2149		    bstate[i].count = 1;
2150		    bstate[i].tv = tv1;
2151		    act->flags |= button;
2152		    debug("button %d timeout", i + 1);
2153		}
2154	    } else {
2155		/* the button has been up */
2156	    }
2157	}
2158	button <<= 1;
2159	mask >>= 1;
2160    }
2161}
2162
2163static int
2164r_timeout(void)
2165{
2166    struct timeval tv;
2167    struct timeval tv1;
2168    struct timeval tv2;
2169
2170    gettimeofday(&tv1, NULL);
2171    tv2.tv_sec = rodent.button2timeout/1000;
2172    tv2.tv_usec = (rodent.button2timeout%1000)*1000;
2173    timersub(&tv1, &tv2, &tv);
2174    return timercmp(&tv, &mouse_button_state_tv, >);
2175}
2176
2177static void
2178r_click(mousestatus_t *act)
2179{
2180    struct mouse_info mouse;
2181    int button;
2182    int mask;
2183    int i;
2184
2185    mask = act->flags & MOUSE_BUTTONS;
2186    if (mask == 0)
2187	return;
2188
2189    button = MOUSE_BUTTON1DOWN;
2190    for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
2191        if (mask & 1) {
2192	    debug("mstate[%d]->count:%d", i, mstate[i]->count);
2193            if (act->button & button) {
2194                /* the button is down */
2195	        mouse.u.event.value = mstate[i]->count;
2196            } else {
2197                /* the button is up */
2198	        mouse.u.event.value = 0;
2199            }
2200	    mouse.operation = MOUSE_BUTTON_EVENT;
2201	    mouse.u.event.id = button;
2202	    if (debug < 2)
2203	        ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
2204	    debug("button %d  count %d", i + 1, mouse.u.event.value);
2205        }
2206	button <<= 1;
2207	mask >>= 1;
2208    }
2209}
2210
2211/* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
2212/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
2213/*
2214 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
2215 *
2216 * Permission to use, copy, modify, distribute, and sell this software and its
2217 * documentation for any purpose is hereby granted without fee, provided that
2218 * the above copyright notice appear in all copies and that both that
2219 * copyright notice and this permission notice appear in supporting
2220 * documentation, and that the name of David Dawes
2221 * not be used in advertising or publicity pertaining to distribution of
2222 * the software without specific, written prior permission.
2223 * David Dawes makes no representations about the suitability of this
2224 * software for any purpose.  It is provided "as is" without express or
2225 * implied warranty.
2226 *
2227 * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
2228 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2229 * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
2230 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
2231 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
2232 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
2233 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2234 *
2235 */
2236
2237
2238static void
2239setmousespeed(int old, int new, unsigned cflag)
2240{
2241	struct termios tty;
2242	char *c;
2243
2244	if (tcgetattr(rodent.mfd, &tty) < 0)
2245	{
2246		logwarn("unable to get status of mouse fd", 0);
2247		return;
2248	}
2249
2250	tty.c_iflag = IGNBRK | IGNPAR;
2251	tty.c_oflag = 0;
2252	tty.c_lflag = 0;
2253	tty.c_cflag = (tcflag_t)cflag;
2254	tty.c_cc[VTIME] = 0;
2255	tty.c_cc[VMIN] = 1;
2256
2257	switch (old)
2258	{
2259	case 9600:
2260		cfsetispeed(&tty, B9600);
2261		cfsetospeed(&tty, B9600);
2262		break;
2263	case 4800:
2264		cfsetispeed(&tty, B4800);
2265		cfsetospeed(&tty, B4800);
2266		break;
2267	case 2400:
2268		cfsetispeed(&tty, B2400);
2269		cfsetospeed(&tty, B2400);
2270		break;
2271	case 1200:
2272	default:
2273		cfsetispeed(&tty, B1200);
2274		cfsetospeed(&tty, B1200);
2275	}
2276
2277	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
2278	{
2279		logwarn("unable to set status of mouse fd", 0);
2280		return;
2281	}
2282
2283	switch (new)
2284	{
2285	case 9600:
2286		c = "*q";
2287		cfsetispeed(&tty, B9600);
2288		cfsetospeed(&tty, B9600);
2289		break;
2290	case 4800:
2291		c = "*p";
2292		cfsetispeed(&tty, B4800);
2293		cfsetospeed(&tty, B4800);
2294		break;
2295	case 2400:
2296		c = "*o";
2297		cfsetispeed(&tty, B2400);
2298		cfsetospeed(&tty, B2400);
2299		break;
2300	case 1200:
2301	default:
2302		c = "*n";
2303		cfsetispeed(&tty, B1200);
2304		cfsetospeed(&tty, B1200);
2305	}
2306
2307	if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
2308	    || rodent.rtype == MOUSE_PROTO_LOGI)
2309	{
2310		if (write(rodent.mfd, c, 2) != 2)
2311		{
2312			logwarn("unable to write to mouse fd", 0);
2313			return;
2314		}
2315	}
2316	usleep(100000);
2317
2318	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
2319		logwarn("unable to set status of mouse fd", 0);
2320}
2321
2322/*
2323 * PnP COM device support
2324 *
2325 * It's a simplistic implementation, but it works :-)
2326 * KY, 31/7/97.
2327 */
2328
2329/*
2330 * Try to elicit a PnP ID as described in
2331 * Microsoft, Hayes: "Plug and Play External COM Device Specification,
2332 * rev 1.00", 1995.
2333 *
2334 * The routine does not fully implement the COM Enumerator as par Section
2335 * 2.1 of the document.  In particular, we don't have idle state in which
2336 * the driver software monitors the com port for dynamic connection or
2337 * removal of a device at the port, because `moused' simply quits if no
2338 * device is found.
2339 *
2340 * In addition, as PnP COM device enumeration procedure slightly has
2341 * changed since its first publication, devices which follow earlier
2342 * revisions of the above spec. may fail to respond if the rev 1.0
2343 * procedure is used. XXX
2344 */
2345static int
2346pnpwakeup1(void)
2347{
2348    struct timeval timeout;
2349    fd_set fds;
2350    int i;
2351
2352    /*
2353     * This is the procedure described in rev 1.0 of PnP COM device spec.
2354     * Unfortunately, some devices which comform to earlier revisions of
2355     * the spec gets confused and do not return the ID string...
2356     */
2357    debug("PnP COM device rev 1.0 probe...");
2358
2359    /* port initialization (2.1.2) */
2360    ioctl(rodent.mfd, TIOCMGET, &i);
2361    i |= TIOCM_DTR;		/* DTR = 1 */
2362    i &= ~TIOCM_RTS;		/* RTS = 0 */
2363    ioctl(rodent.mfd, TIOCMSET, &i);
2364    usleep(240000);
2365
2366    /*
2367     * The PnP COM device spec. dictates that the mouse must set DSR
2368     * in response to DTR (by hardware or by software) and that if DSR is
2369     * not asserted, the host computer should think that there is no device
2370     * at this serial port.  But some mice just don't do that...
2371     */
2372    ioctl(rodent.mfd, TIOCMGET, &i);
2373    debug("modem status 0%o", i);
2374    if ((i & TIOCM_DSR) == 0)
2375	return FALSE;
2376
2377    /* port setup, 1st phase (2.1.3) */
2378    setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
2379    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
2380    ioctl(rodent.mfd, TIOCMBIC, &i);
2381    usleep(240000);
2382    i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
2383    ioctl(rodent.mfd, TIOCMBIS, &i);
2384    usleep(240000);
2385
2386    /* wait for response, 1st phase (2.1.4) */
2387    i = FREAD;
2388    ioctl(rodent.mfd, TIOCFLUSH, &i);
2389    i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
2390    ioctl(rodent.mfd, TIOCMBIS, &i);
2391
2392    /* try to read something */
2393    FD_ZERO(&fds);
2394    FD_SET(rodent.mfd, &fds);
2395    timeout.tv_sec = 0;
2396    timeout.tv_usec = 240000;
2397    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2398	debug("pnpwakeup1(): valid response in first phase.");
2399	return TRUE;
2400    }
2401
2402    /* port setup, 2nd phase (2.1.5) */
2403    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
2404    ioctl(rodent.mfd, TIOCMBIC, &i);
2405    usleep(240000);
2406
2407    /* wait for respose, 2nd phase (2.1.6) */
2408    i = FREAD;
2409    ioctl(rodent.mfd, TIOCFLUSH, &i);
2410    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
2411    ioctl(rodent.mfd, TIOCMBIS, &i);
2412
2413    /* try to read something */
2414    FD_ZERO(&fds);
2415    FD_SET(rodent.mfd, &fds);
2416    timeout.tv_sec = 0;
2417    timeout.tv_usec = 240000;
2418    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2419	debug("pnpwakeup1(): valid response in second phase.");
2420	return TRUE;
2421    }
2422
2423    return FALSE;
2424}
2425
2426static int
2427pnpwakeup2(void)
2428{
2429    struct timeval timeout;
2430    fd_set fds;
2431    int i;
2432
2433    /*
2434     * This is a simplified procedure; it simply toggles RTS.
2435     */
2436    debug("alternate probe...");
2437
2438    ioctl(rodent.mfd, TIOCMGET, &i);
2439    i |= TIOCM_DTR;		/* DTR = 1 */
2440    i &= ~TIOCM_RTS;		/* RTS = 0 */
2441    ioctl(rodent.mfd, TIOCMSET, &i);
2442    usleep(240000);
2443
2444    setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
2445
2446    /* wait for respose */
2447    i = FREAD;
2448    ioctl(rodent.mfd, TIOCFLUSH, &i);
2449    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
2450    ioctl(rodent.mfd, TIOCMBIS, &i);
2451
2452    /* try to read something */
2453    FD_ZERO(&fds);
2454    FD_SET(rodent.mfd, &fds);
2455    timeout.tv_sec = 0;
2456    timeout.tv_usec = 240000;
2457    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2458	debug("pnpwakeup2(): valid response.");
2459	return TRUE;
2460    }
2461
2462    return FALSE;
2463}
2464
2465static int
2466pnpgets(char *buf)
2467{
2468    struct timeval timeout;
2469    fd_set fds;
2470    int begin;
2471    int i;
2472    char c;
2473
2474    if (!pnpwakeup1() && !pnpwakeup2()) {
2475	/*
2476	 * According to PnP spec, we should set DTR = 1 and RTS = 0 while
2477	 * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
2478	 * assuming there is something at the port even if it didn't
2479	 * respond to the PnP enumeration procedure.
2480	 */
2481disconnect_idle:
2482	i = TIOCM_DTR | TIOCM_RTS;		/* DTR = 1, RTS = 1 */
2483	ioctl(rodent.mfd, TIOCMBIS, &i);
2484	return 0;
2485    }
2486
2487    /* collect PnP COM device ID (2.1.7) */
2488    begin = -1;
2489    i = 0;
2490    usleep(240000);	/* the mouse must send `Begin ID' within 200msec */
2491    while (read(rodent.mfd, &c, 1) == 1) {
2492	/* we may see "M", or "M3..." before `Begin ID' */
2493	buf[i++] = c;
2494        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
2495	    debug("begin-id %02x", c);
2496	    begin = i - 1;
2497	    break;
2498        }
2499        debug("%c %02x", c, c);
2500	if (i >= 256)
2501	    break;
2502    }
2503    if (begin < 0) {
2504	/* we haven't seen `Begin ID' in time... */
2505	goto connect_idle;
2506    }
2507
2508    ++c;			/* make it `End ID' */
2509    for (;;) {
2510        FD_ZERO(&fds);
2511        FD_SET(rodent.mfd, &fds);
2512        timeout.tv_sec = 0;
2513        timeout.tv_usec = 240000;
2514        if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
2515	    break;
2516
2517	read(rodent.mfd, &buf[i], 1);
2518        if (buf[i++] == c)	/* End ID */
2519	    break;
2520	if (i >= 256)
2521	    break;
2522    }
2523    if (begin > 0) {
2524	i -= begin;
2525	bcopy(&buf[begin], &buf[0], i);
2526    }
2527    /* string may not be human readable... */
2528    debug("len:%d, '%-*.*s'", i, i, i, buf);
2529
2530    if (buf[i - 1] == c)
2531	return i;		/* a valid PnP string */
2532
2533    /*
2534     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
2535     * in idle state.  But, `moused' shall leave the modem control lines
2536     * as they are. See above.
2537     */
2538connect_idle:
2539
2540    /* we may still have something in the buffer */
2541    return ((i > 0) ? i : 0);
2542}
2543
2544static int
2545pnpparse(pnpid_t *id, char *buf, int len)
2546{
2547    char s[3];
2548    int offset;
2549    int sum = 0;
2550    int i, j;
2551
2552    id->revision = 0;
2553    id->eisaid = NULL;
2554    id->serial = NULL;
2555    id->class = NULL;
2556    id->compat = NULL;
2557    id->description = NULL;
2558    id->neisaid = 0;
2559    id->nserial = 0;
2560    id->nclass = 0;
2561    id->ncompat = 0;
2562    id->ndescription = 0;
2563
2564    if ((buf[0] != 0x28) && (buf[0] != 0x08)) {
2565	/* non-PnP mice */
2566	switch(buf[0]) {
2567	default:
2568	    return FALSE;
2569	case 'M': /* Microsoft */
2570	    id->eisaid = "PNP0F01";
2571	    break;
2572	case 'H': /* MouseSystems */
2573	    id->eisaid = "PNP0F04";
2574	    break;
2575	}
2576	id->neisaid = strlen(id->eisaid);
2577	id->class = "MOUSE";
2578	id->nclass = strlen(id->class);
2579	debug("non-PnP mouse '%c'", buf[0]);
2580	return TRUE;
2581    }
2582
2583    /* PnP mice */
2584    offset = 0x28 - buf[0];
2585
2586    /* calculate checksum */
2587    for (i = 0; i < len - 3; ++i) {
2588	sum += buf[i];
2589	buf[i] += offset;
2590    }
2591    sum += buf[len - 1];
2592    for (; i < len; ++i)
2593	buf[i] += offset;
2594    debug("PnP ID string: '%*.*s'", len, len, buf);
2595
2596    /* revision */
2597    buf[1] -= offset;
2598    buf[2] -= offset;
2599    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
2600    debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
2601
2602    /* EISA vender and product ID */
2603    id->eisaid = &buf[3];
2604    id->neisaid = 7;
2605
2606    /* option strings */
2607    i = 10;
2608    if (buf[i] == '\\') {
2609        /* device serial # */
2610        for (j = ++i; i < len; ++i) {
2611            if (buf[i] == '\\')
2612		break;
2613        }
2614	if (i >= len)
2615	    i -= 3;
2616	if (i - j == 8) {
2617            id->serial = &buf[j];
2618            id->nserial = 8;
2619	}
2620    }
2621    if (buf[i] == '\\') {
2622        /* PnP class */
2623        for (j = ++i; i < len; ++i) {
2624            if (buf[i] == '\\')
2625		break;
2626        }
2627	if (i >= len)
2628	    i -= 3;
2629	if (i > j + 1) {
2630            id->class = &buf[j];
2631            id->nclass = i - j;
2632        }
2633    }
2634    if (buf[i] == '\\') {
2635	/* compatible driver */
2636        for (j = ++i; i < len; ++i) {
2637            if (buf[i] == '\\')
2638		break;
2639        }
2640	/*
2641	 * PnP COM spec prior to v0.96 allowed '*' in this field,
2642	 * it's not allowed now; just igore it.
2643	 */
2644	if (buf[j] == '*')
2645	    ++j;
2646	if (i >= len)
2647	    i -= 3;
2648	if (i > j + 1) {
2649            id->compat = &buf[j];
2650            id->ncompat = i - j;
2651        }
2652    }
2653    if (buf[i] == '\\') {
2654	/* product description */
2655        for (j = ++i; i < len; ++i) {
2656            if (buf[i] == ';')
2657		break;
2658        }
2659	if (i >= len)
2660	    i -= 3;
2661	if (i > j + 1) {
2662            id->description = &buf[j];
2663            id->ndescription = i - j;
2664        }
2665    }
2666
2667    /* checksum exists if there are any optional fields */
2668    if ((id->nserial > 0) || (id->nclass > 0)
2669	|| (id->ncompat > 0) || (id->ndescription > 0)) {
2670        debug("PnP checksum: 0x%X", sum);
2671        sprintf(s, "%02X", sum & 0x0ff);
2672        if (strncmp(s, &buf[len - 3], 2) != 0) {
2673#if 0
2674            /*
2675	     * I found some mice do not comply with the PnP COM device
2676	     * spec regarding checksum... XXX
2677	     */
2678            logwarnx("PnP checksum error", 0);
2679	    return FALSE;
2680#endif
2681        }
2682    }
2683
2684    return TRUE;
2685}
2686
2687static symtab_t *
2688pnpproto(pnpid_t *id)
2689{
2690    symtab_t *t;
2691    int i, j;
2692
2693    if (id->nclass > 0)
2694	if ( strncmp(id->class, "MOUSE", id->nclass) != 0 &&
2695	     strncmp(id->class, "TABLET", id->nclass) != 0)
2696	    /* this is not a mouse! */
2697	    return NULL;
2698
2699    if (id->neisaid > 0) {
2700        t = gettoken(pnpprod, id->eisaid, id->neisaid);
2701	if (t->val != MOUSE_PROTO_UNKNOWN)
2702            return t;
2703    }
2704
2705    /*
2706     * The 'Compatible drivers' field may contain more than one
2707     * ID separated by ','.
2708     */
2709    if (id->ncompat <= 0)
2710	return NULL;
2711    for (i = 0; i < id->ncompat; ++i) {
2712        for (j = i; id->compat[i] != ','; ++i)
2713            if (i >= id->ncompat)
2714		break;
2715        if (i > j) {
2716            t = gettoken(pnpprod, id->compat + j, i - j);
2717	    if (t->val != MOUSE_PROTO_UNKNOWN)
2718                return t;
2719	}
2720    }
2721
2722    return NULL;
2723}
2724
2725/* name/val mapping */
2726
2727static symtab_t *
2728gettoken(symtab_t *tab, char *s, int len)
2729{
2730    int i;
2731
2732    for (i = 0; tab[i].name != NULL; ++i) {
2733	if (strncmp(tab[i].name, s, len) == 0)
2734	    break;
2735    }
2736    return &tab[i];
2737}
2738
2739static char *
2740gettokenname(symtab_t *tab, int val)
2741{
2742    int i;
2743
2744    for (i = 0; tab[i].name != NULL; ++i) {
2745	if (tab[i].val == val)
2746	    return tab[i].name;
2747    }
2748    return NULL;
2749}
2750
2751
2752/*
2753 * code to read from the Genius Kidspad tablet.
2754
2755The tablet responds to the COM PnP protocol 1.0 with EISA-ID KYE0005,
2756and to pre-pnp probes (RTS toggle) with 'T' (tablet ?)
27579600, 8 bit, parity odd.
2758
2759The tablet puts out 5 bytes. b0 (mask 0xb8, value 0xb8) contains
2760the proximity, tip and button info:
2761   (byte0 & 0x1)	true = tip pressed
2762   (byte0 & 0x2)	true = button pressed
2763   (byte0 & 0x40)	false = pen in proximity of tablet.
2764
2765The next 4 bytes are used for coordinates xl, xh, yl, yh (7 bits valid).
2766
2767Only absolute coordinates are returned, so we use the following approach:
2768we store the last coordinates sent when the pen went out of the tablet,
2769
2770
2771 *
2772 */
2773
2774typedef enum {
2775    S_IDLE, S_PROXY, S_FIRST, S_DOWN, S_UP
2776} k_status ;
2777
2778static int
2779kidspad(u_char rxc, mousestatus_t *act)
2780{
2781    static buf[5];
2782    static int buflen = 0, b_prev = 0 , x_prev = -1, y_prev = -1 ;
2783    static k_status status = S_IDLE ;
2784    static struct timeval old, now ;
2785    static int x_idle = -1, y_idle = -1 ;
2786
2787    int deltat, x, y ;
2788
2789    if (buflen > 0 && (rxc & 0x80) ) {
2790	fprintf(stderr, "invalid code %d 0x%x\n", buflen, rxc);
2791	buflen = 0 ;
2792    }
2793    if (buflen == 0 && (rxc & 0xb8) != 0xb8 ) {
2794	fprintf(stderr, "invalid code 0 0x%x\n", rxc);
2795	return 0 ; /* invalid code, no action */
2796    }
2797    buf[buflen++] = rxc ;
2798    if (buflen < 5)
2799	return 0 ;
2800
2801    buflen = 0 ; /* for next time... */
2802
2803    x = buf[1]+128*(buf[2] - 7) ;
2804    if (x < 0) x = 0 ;
2805    y = 28*128 - (buf[3] + 128* (buf[4] - 7)) ;
2806    if (y < 0) y = 0 ;
2807
2808    x /= 8 ;
2809    y /= 8 ;
2810
2811    act->flags = 0 ;
2812    act->obutton = act->button ;
2813    act->dx = act->dy = act->dz = 0 ;
2814    gettimeofday(&now, NULL);
2815    if ( buf[0] & 0x40 ) /* pen went out of reach */
2816	status = S_IDLE ;
2817    else if (status == S_IDLE) { /* pen is newly near the tablet */
2818	act->flags |= MOUSE_POSCHANGED ; /* force update */
2819	status = S_PROXY ;
2820	x_prev = x ;
2821	y_prev = y ;
2822    }
2823    old = now ;
2824    act->dx = x - x_prev ;
2825    act->dy = y - y_prev ;
2826    if (act->dx || act->dy)
2827	act->flags |= MOUSE_POSCHANGED ;
2828    x_prev = x ;
2829    y_prev = y ;
2830    if (b_prev != 0 && b_prev != buf[0]) { /* possibly record button change */
2831	act->button = 0 ;
2832	if ( buf[0] & 0x01 ) /* tip pressed */
2833	    act->button |= MOUSE_BUTTON1DOWN ;
2834	if ( buf[0] & 0x02 ) /* button pressed */
2835	    act->button |= MOUSE_BUTTON2DOWN ;
2836	act->flags |= MOUSE_BUTTONSCHANGED ;
2837    }
2838    b_prev = buf[0] ;
2839    return act->flags ;
2840}
2841
2842static void
2843mremote_serversetup()
2844{
2845    struct sockaddr_un ad;
2846
2847    /* Open a UNIX domain stream socket to listen for mouse remote clients */
2848    unlink(_PATH_MOUSEREMOTE);
2849
2850    if ( (rodent.mremsfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
2851	logerrx(1, "unable to create unix domain socket %s",_PATH_MOUSEREMOTE);
2852
2853    umask(0111);
2854
2855    bzero(&ad, sizeof(ad));
2856    ad.sun_family = AF_UNIX;
2857    strcpy(ad.sun_path, _PATH_MOUSEREMOTE);
2858#ifndef SUN_LEN
2859#define SUN_LEN(unp) ( ((char *)(unp)->sun_path - (char *)(unp)) + \
2860                       strlen((unp)->path) )
2861#endif
2862    if (bind(rodent.mremsfd, (struct sockaddr *) &ad, SUN_LEN(&ad)) < 0)
2863	logerrx(1, "unable to bind unix domain socket %s", _PATH_MOUSEREMOTE);
2864
2865    listen(rodent.mremsfd, 1);
2866}
2867
2868static void
2869mremote_clientchg(int add)
2870{
2871    struct sockaddr_un ad;
2872    int ad_len, fd;
2873
2874    if (rodent.rtype != MOUSE_PROTO_X10MOUSEREM)
2875	return;
2876
2877    if ( add ) {
2878	/*  Accept client connection, if we don't already have one  */
2879	ad_len = sizeof(ad);
2880	fd = accept(rodent.mremsfd, (struct sockaddr *) &ad, &ad_len);
2881	if (fd < 0)
2882	    logwarnx("failed accept on mouse remote socket");
2883
2884	if ( rodent.mremcfd < 0 ) {
2885	    rodent.mremcfd = fd;
2886	    debug("remote client connect...accepted");
2887	}
2888	else {
2889	    close(fd);
2890	    debug("another remote client connect...disconnected");
2891	}
2892    }
2893    else {
2894	/* Client disconnected */
2895	debug("remote client disconnected");
2896	close( rodent.mremcfd );
2897	rodent.mremcfd = -1;
2898    }
2899}
2900
2901
2902