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