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