xref: /openbsd/usr.sbin/wsmoused/wsmoused.c (revision 2bfe0399)
1*2bfe0399Sderaadt /* $OpenBSD: wsmoused.c,v 1.29 2013/11/24 01:06:19 deraadt Exp $ */
2673a75b7Saaron 
3673a75b7Saaron /*
4673a75b7Saaron  * Copyright (c) 2001 Jean-Baptiste Marchand, Julien Montagne and Jerome Verdon
5673a75b7Saaron  *
6673a75b7Saaron  * Copyright (c) 1998 by Kazutaka Yokota
7673a75b7Saaron  *
8673a75b7Saaron  * Copyright (c) 1995 Michael Smith
9673a75b7Saaron  *
10673a75b7Saaron  * Copyright (c) 1993 by David Dawes <dawes@xfree86.org>
11673a75b7Saaron  *
12673a75b7Saaron  * Copyright (c) 1990,91 by Thomas Roell, Dinkelscherben, Germany.
13673a75b7Saaron  *
14673a75b7Saaron  * All rights reserved.
15673a75b7Saaron  *
16673a75b7Saaron  * Most of this code was taken from the FreeBSD moused daemon, written by
17673a75b7Saaron  * Michael Smith. The FreeBSD moused daemon already contained code from the
18673a75b7Saaron  * Xfree Project, written by David Dawes and Thomas Roell and Kazutaka Yokota.
19673a75b7Saaron  *
20673a75b7Saaron  * Adaptation to OpenBSD was done by Jean-Baptiste Marchand, Julien Montagne
21673a75b7Saaron  * and Jerome Verdon.
22673a75b7Saaron  *
23673a75b7Saaron  * Redistribution and use in source and binary forms, with or without
24673a75b7Saaron  * modification, are permitted provided that the following conditions
25673a75b7Saaron  * are met:
26673a75b7Saaron  * 1. Redistributions of source code must retain the above copyright
27673a75b7Saaron  *    notice, this list of conditions and the following disclaimer.
28673a75b7Saaron  * 2. Redistributions in binary form must reproduce the above copyright
29673a75b7Saaron  *    notice, this list of conditions and the following disclaimer in the
30673a75b7Saaron  *    documentation and/or other materials provided with the distribution.
31673a75b7Saaron  * 3. All advertising materials mentioning features or use of this software
32673a75b7Saaron  *    must display the following acknowledgement:
33673a75b7Saaron  *	This product includes software developed by
34673a75b7Saaron  *      David Dawes, Jean-Baptiste Marchand, Julien Montagne, Thomas Roell,
35673a75b7Saaron  *      Michael Smith, Jerome Verdon and Kazutaka Yokota.
36673a75b7Saaron  * 4. The name authors may not be used to endorse or promote products
37673a75b7Saaron  *    derived from this software without specific prior written permission.
38673a75b7Saaron  *
39673a75b7Saaron  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
40673a75b7Saaron  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41673a75b7Saaron  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
42673a75b7Saaron  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
43673a75b7Saaron  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44673a75b7Saaron  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45673a75b7Saaron  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46673a75b7Saaron  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47673a75b7Saaron  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48673a75b7Saaron  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49673a75b7Saaron  *
50673a75b7Saaron  *
51673a75b7Saaron  */
52673a75b7Saaron 
53673a75b7Saaron #include <sys/ioctl.h>
54f3afb3efSjbm #include <sys/stat.h>
55673a75b7Saaron #include <sys/types.h>
56673a75b7Saaron #include <sys/time.h>
57673a75b7Saaron #include <sys/tty.h>
58673a75b7Saaron #include <dev/wscons/wsconsio.h>
59673a75b7Saaron 
60673a75b7Saaron #include <ctype.h>
61673a75b7Saaron #include <err.h>
62673a75b7Saaron #include <errno.h>
63673a75b7Saaron #include <fcntl.h>
64673a75b7Saaron #include <unistd.h>
65673a75b7Saaron #include <signal.h>
66673a75b7Saaron #include <poll.h>
67673a75b7Saaron #include <stdio.h>
68673a75b7Saaron #include <string.h>
69673a75b7Saaron #include <stdlib.h>
70673a75b7Saaron #include <syslog.h>
71673a75b7Saaron 
72673a75b7Saaron #include "mouse_protocols.h"
73673a75b7Saaron #include "wsmoused.h"
74673a75b7Saaron 
75be45c8afSmiod #define	DEFAULT_TTY	"/dev/ttyCcfg"
76be45c8afSmiod #define	DEFAULT_PIDFILE	"/var/run/wsmoused.pid"
77be45c8afSmiod 
78673a75b7Saaron extern char *__progname;
79673a75b7Saaron extern char *mouse_names[];
80673a75b7Saaron 
81673a75b7Saaron int debug = 0;
82673a75b7Saaron int background = FALSE;
83be45c8afSmiod int nodaemon = FALSE;
84673a75b7Saaron int identify = FALSE;
85be45c8afSmiod char *pidfile = NULL;
86673a75b7Saaron 
87673a75b7Saaron mouse_t mouse = {
88b729828dSotto 	.flags = 0,
89b729828dSotto 	.portname = NULL,
90be45c8afSmiod 	.ttyname = NULL,
91b729828dSotto 	.proto = P_UNKNOWN,
92b729828dSotto 	.baudrate = 1200,
93b729828dSotto 	.old_baudrate = 1200,
94b729828dSotto 	.rate = MOUSE_RATE_UNKNOWN,
95b729828dSotto 	.resolution = MOUSE_RES_UNKNOWN,
96b729828dSotto 	.zmap = 0,
97b729828dSotto 	.wmode = 0,
98b729828dSotto 	.mfd = -1,
99b729828dSotto 	.clickthreshold = 500,	/* 0.5 sec */
100673a75b7Saaron };
101673a75b7Saaron 
102673a75b7Saaron /* identify the type of a wsmouse supported mouse */
103673a75b7Saaron void
104673a75b7Saaron wsmouse_identify(void)
105673a75b7Saaron {
106673a75b7Saaron 	unsigned int type;
107673a75b7Saaron 
108b47703b0Smiod 	if (mouse.mfd != -1) {
109b47703b0Smiod 		if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) == -1)
110b47703b0Smiod 			err(1, "can't detect mouse type");
111be45c8afSmiod 
112673a75b7Saaron 		printf("wsmouse supported mouse: ");
113673a75b7Saaron 		switch (type) {
114673a75b7Saaron 		case WSMOUSE_TYPE_VSXXX:
115673a75b7Saaron 			printf("DEC serial\n");
116673a75b7Saaron 			break;
117673a75b7Saaron 		case WSMOUSE_TYPE_PS2:
118673a75b7Saaron 			printf("PS/2 compatible\n");
119673a75b7Saaron 			break;
120673a75b7Saaron 		case WSMOUSE_TYPE_USB:
121673a75b7Saaron 			printf("USB\n");
122673a75b7Saaron 			break;
123673a75b7Saaron 		case WSMOUSE_TYPE_LMS:
124673a75b7Saaron 			printf("Logitech busmouse\n");
125673a75b7Saaron 			break;
126673a75b7Saaron 		case WSMOUSE_TYPE_MMS:
127673a75b7Saaron 			printf("Microsoft InPort mouse\n");
128673a75b7Saaron 			break;
129673a75b7Saaron 		case WSMOUSE_TYPE_TPANEL:
130673a75b7Saaron 			printf("Generic Touch Panel\n");
131673a75b7Saaron 			break;
132673a75b7Saaron 		case WSMOUSE_TYPE_NEXT:
133673a75b7Saaron 			printf("NeXT\n");
134673a75b7Saaron 			break;
135673a75b7Saaron 		case WSMOUSE_TYPE_ARCHIMEDES:
136673a75b7Saaron 			printf("Archimedes\n");
137673a75b7Saaron 			break;
138be45c8afSmiod 		case WSMOUSE_TYPE_ADB:
139be45c8afSmiod 			printf("ADB\n");
140be45c8afSmiod 			break;
141be45c8afSmiod 		case WSMOUSE_TYPE_HIL:
142be45c8afSmiod 			printf("HP-HIL\n");
143be45c8afSmiod 			break;
144be45c8afSmiod 		case WSMOUSE_TYPE_LUNA:
145be45c8afSmiod 			printf("Omron Luna\n");
146be45c8afSmiod 			break;
147be45c8afSmiod 		case WSMOUSE_TYPE_DOMAIN:
148be45c8afSmiod 			printf("Apollo Domain\n");
149be45c8afSmiod 			break;
150be45c8afSmiod 		case WSMOUSE_TYPE_SUN:
151be45c8afSmiod 			printf("Sun\n");
152be45c8afSmiod 			break;
153673a75b7Saaron 		default:
154673a75b7Saaron 			printf("Unknown\n");
155673a75b7Saaron 			break;
156673a75b7Saaron 		}
157b47703b0Smiod 	} else
15846347781Smpech 		warnx("unable to open %s", mouse.portname);
159673a75b7Saaron }
160673a75b7Saaron 
161673a75b7Saaron /* wsmouse_init : init a wsmouse compatible mouse */
162673a75b7Saaron void
163673a75b7Saaron wsmouse_init(void)
164673a75b7Saaron {
165673a75b7Saaron 	unsigned int res = WSMOUSE_RES_MIN;
166673a75b7Saaron 	unsigned int rate = WSMOUSE_RATE_DEFAULT;
167673a75b7Saaron 
168673a75b7Saaron 	ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
169673a75b7Saaron 	ioctl(mouse.mfd, WSMOUSEIO_SRATE, &rate);
170673a75b7Saaron }
171673a75b7Saaron 
172673a75b7Saaron /*
173673a75b7Saaron  * Buttons remapping
174673a75b7Saaron  */
175673a75b7Saaron 
176673a75b7Saaron /* physical to logical button mapping */
177673a75b7Saaron static int p2l[MOUSE_MAXBUTTON] = {
178673a75b7Saaron 	MOUSE_BUTTON1,	MOUSE_BUTTON2,	MOUSE_BUTTON3,	MOUSE_BUTTON4,
179673a75b7Saaron 	MOUSE_BUTTON5,	MOUSE_BUTTON6,	MOUSE_BUTTON7,	MOUSE_BUTTON8,
180673a75b7Saaron };
181673a75b7Saaron 
182673a75b7Saaron static char *
183673a75b7Saaron skipspace(char *s)
184673a75b7Saaron {
185*2bfe0399Sderaadt 	while (isspace((unsigned char)*s))
186673a75b7Saaron 		++s;
187673a75b7Saaron 	return s;
188673a75b7Saaron }
189673a75b7Saaron 
190673a75b7Saaron /* mouse_installmap : install a map between physical and logical buttons */
191673a75b7Saaron static int
192673a75b7Saaron mouse_installmap(char *arg)
193673a75b7Saaron {
194673a75b7Saaron 	int pbutton;
195673a75b7Saaron 	int lbutton;
196673a75b7Saaron 	char *s;
197673a75b7Saaron 
198673a75b7Saaron 	while (*arg) {
199673a75b7Saaron 		arg = skipspace(arg);
200673a75b7Saaron 		s = arg;
201*2bfe0399Sderaadt 		while (isdigit((unsigned char)*arg))
202673a75b7Saaron 			++arg;
203673a75b7Saaron 		arg = skipspace(arg);
204673a75b7Saaron 		if ((arg <= s) || (*arg != '='))
205673a75b7Saaron 			return FALSE;
206673a75b7Saaron 		lbutton = atoi(s);
207673a75b7Saaron 
208673a75b7Saaron 		arg = skipspace(++arg);
209673a75b7Saaron 		s = arg;
210*2bfe0399Sderaadt 		while (isdigit((unsigned char)*arg))
211673a75b7Saaron 			++arg;
212*2bfe0399Sderaadt 		if (arg <= s || (!isspace((unsigned char)*arg) && *arg != '\0'))
213673a75b7Saaron 			return FALSE;
214673a75b7Saaron 		pbutton = atoi(s);
215673a75b7Saaron 
216b47703b0Smiod 		if (lbutton <= 0 || lbutton > MOUSE_MAXBUTTON)
217673a75b7Saaron 			return FALSE;
218b47703b0Smiod 		if (pbutton <= 0 || pbutton > MOUSE_MAXBUTTON)
219673a75b7Saaron 			return FALSE;
220673a75b7Saaron 		p2l[pbutton - 1] = lbutton - 1;
221673a75b7Saaron 	}
222673a75b7Saaron 	return TRUE;
223673a75b7Saaron }
224673a75b7Saaron 
225673a75b7Saaron /* mouse_map : converts physical buttons to logical buttons */
226673a75b7Saaron static void
227673a75b7Saaron mouse_map(struct wscons_event *orig, struct wscons_event *mapped)
228673a75b7Saaron {
229673a75b7Saaron 	mapped->type = orig->type;
230673a75b7Saaron 	mapped->value = p2l[orig->value];
231673a75b7Saaron }
232673a75b7Saaron 
233673a75b7Saaron /* terminate signals handler */
234673a75b7Saaron static void
235673a75b7Saaron terminate(int sig)
236673a75b7Saaron {
237673a75b7Saaron 	struct wscons_event event;
238e02f5e47Sjbm 	unsigned int res;
239673a75b7Saaron 
240673a75b7Saaron 	if (mouse.mfd != -1) {
241673a75b7Saaron 		event.type = WSCONS_EVENT_WSMOUSED_OFF;
242f3afb3efSjbm 		ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event);
243e02f5e47Sjbm 		res = WSMOUSE_RES_DEFAULT;
244e02f5e47Sjbm 		ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
245673a75b7Saaron 		close(mouse.mfd);
246673a75b7Saaron 		mouse.mfd = -1;
247673a75b7Saaron 	}
248be45c8afSmiod 	if (pidfile != NULL)
249673a75b7Saaron 		unlink(pidfile);
250ded45aa8Sderaadt 	_exit(0);
251673a75b7Saaron }
252673a75b7Saaron 
253673a75b7Saaron /* buttons status (for multiple click detection) */
254673a75b7Saaron static struct {
255673a75b7Saaron 	int count;		/* 0: up, 1: single click, 2: double click,... */
256673a75b7Saaron 	struct timeval tv;	/* timestamp on the last `up' event */
257673a75b7Saaron } buttonstate[MOUSE_MAXBUTTON];
258673a75b7Saaron 
259673a75b7Saaron /*
260673a75b7Saaron  * handle button click
261673a75b7Saaron  * Note that an ioctl is sent for each button
262673a75b7Saaron  */
263673a75b7Saaron static void
264673a75b7Saaron mouse_click(struct wscons_event *event)
265673a75b7Saaron {
266673a75b7Saaron 	struct timeval max_date;
267673a75b7Saaron 	struct timeval now;
268673a75b7Saaron 	struct timeval delay;
269673a75b7Saaron 	int i = event->value; /* button number */
270673a75b7Saaron 
271a9fa58fdSderaadt 	gettimeofday(&now, NULL);
272673a75b7Saaron 	delay.tv_sec = mouse.clickthreshold / 1000;
273673a75b7Saaron 	delay.tv_usec = (mouse.clickthreshold % 1000) * 1000;
274673a75b7Saaron 	timersub(&now, &delay, &max_date);
275673a75b7Saaron 
276673a75b7Saaron 	if (event->type == WSCONS_EVENT_MOUSE_DOWN) {
277673a75b7Saaron 		if (timercmp(&max_date, &buttonstate[i].tv, >)) {
2786c2da34cSokan 			timerclear(&buttonstate[i].tv);
279673a75b7Saaron 			buttonstate[i].count = 1;
280b47703b0Smiod 		} else {
281673a75b7Saaron 			buttonstate[i].count++;
282673a75b7Saaron 		}
283673a75b7Saaron 	} else {
284673a75b7Saaron 		/* button is up */
285673a75b7Saaron 		buttonstate[i].tv.tv_sec = now.tv_sec;
286673a75b7Saaron 		buttonstate[i].tv.tv_usec = now.tv_usec;
287673a75b7Saaron 	}
288673a75b7Saaron 
289673a75b7Saaron 	/*
290673a75b7Saaron 	 * we use the time field of wscons_event structure to put the number
291673a75b7Saaron 	 * of multiple clicks
292673a75b7Saaron 	 */
293673a75b7Saaron 	if (event->type == WSCONS_EVENT_MOUSE_DOWN) {
294673a75b7Saaron 		event->time.tv_sec = buttonstate[i].count;
295673a75b7Saaron 		event->time.tv_nsec = 0;
296b47703b0Smiod 	} else {
297673a75b7Saaron 		/* button is up */
298673a75b7Saaron 		event->time.tv_sec = 0;
299673a75b7Saaron 		event->time.tv_nsec = 0;
300673a75b7Saaron 	}
301673a75b7Saaron 	ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event);
302673a75b7Saaron }
303673a75b7Saaron 
304673a75b7Saaron /* workaround for cursor speed on serial mice */
305673a75b7Saaron static void
306673a75b7Saaron normalize_event(struct wscons_event *event)
307673a75b7Saaron {
308673a75b7Saaron 	int dx, dy;
309673a75b7Saaron 	int two_power = 1;
310673a75b7Saaron 
311673a75b7Saaron /* 2: normal speed, 3: slower cursor, 1: faster cursor */
312673a75b7Saaron #define NORMALIZE_DIVISOR 3
313673a75b7Saaron 
314b47703b0Smiod 	switch (event->type) {
315b47703b0Smiod 	case WSCONS_EVENT_MOUSE_DELTA_X:
316673a75b7Saaron 		dx = abs(event->value);
317673a75b7Saaron 		while (dx > 2) {
318673a75b7Saaron 			two_power++;
319673a75b7Saaron 			dx = dx / 2;
320673a75b7Saaron 		}
321673a75b7Saaron 		event->value = event->value / (NORMALIZE_DIVISOR * two_power);
322b47703b0Smiod 		break;
323b47703b0Smiod 	case WSCONS_EVENT_MOUSE_DELTA_Y:
324673a75b7Saaron 		two_power = 1;
325673a75b7Saaron 		dy = abs(event->value);
326673a75b7Saaron 		while (dy > 2) {
327673a75b7Saaron 			two_power++;
328673a75b7Saaron 			dy = dy / 2;
329673a75b7Saaron 		}
330673a75b7Saaron 		event->value = event->value / (NORMALIZE_DIVISOR * two_power);
331b47703b0Smiod 		break;
332673a75b7Saaron 	}
333673a75b7Saaron }
334673a75b7Saaron 
335673a75b7Saaron /* send a wscons_event to the kernel */
336f3afb3efSjbm static int
337673a75b7Saaron treat_event(struct wscons_event *event)
338673a75b7Saaron {
339673a75b7Saaron 	struct wscons_event mapped_event;
340673a75b7Saaron 
341673a75b7Saaron 	if (IS_MOTION_EVENT(event->type)) {
342673a75b7Saaron 		ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event);
343f3afb3efSjbm 		return 1;
3445015c596Sjacekm 	} else if (IS_BUTTON_EVENT(event->type) &&
3455015c596Sjacekm 	    (uint)event->value < MOUSE_MAXBUTTON) {
346673a75b7Saaron 		mouse_map(event, &mapped_event);
347673a75b7Saaron 		mouse_click(&mapped_event);
348f3afb3efSjbm 		return 1;
349673a75b7Saaron 	}
350f3afb3efSjbm 	if (event->type == WSCONS_EVENT_WSMOUSED_CLOSE)
351f3afb3efSjbm 		/* we have to close mouse fd */
352f3afb3efSjbm 		return 0;
353f3afb3efSjbm 	return 1;
354673a75b7Saaron }
355673a75b7Saaron 
356673a75b7Saaron /* split a full mouse event into multiples wscons events */
357673a75b7Saaron static void
358673a75b7Saaron split_event(mousestatus_t *act)
359673a75b7Saaron {
360673a75b7Saaron 	struct wscons_event event;
361673a75b7Saaron 	int button, i, mask;
362673a75b7Saaron 
363673a75b7Saaron 	if (act->dx != 0) {
364673a75b7Saaron 		event.type = WSCONS_EVENT_MOUSE_DELTA_X;
365673a75b7Saaron 		event.value = act->dx;
366673a75b7Saaron 		normalize_event(&event);
367673a75b7Saaron 		treat_event(&event);
368673a75b7Saaron 	}
369673a75b7Saaron 	if (act->dy != 0) {
370673a75b7Saaron 		event.type = WSCONS_EVENT_MOUSE_DELTA_Y;
371673a75b7Saaron 		event.value = 0 - act->dy;
372673a75b7Saaron 		normalize_event(&event);
373673a75b7Saaron 		treat_event(&event);
374673a75b7Saaron 	}
375673a75b7Saaron 	if (act->dz != 0) {
376673a75b7Saaron 		event.type = WSCONS_EVENT_MOUSE_DELTA_Z;
377673a75b7Saaron 		event.value = act->dz;
378673a75b7Saaron 		treat_event(&event);
379673a75b7Saaron 	}
3802fc38322Smiod 	if (act->dw != 0) {
3812fc38322Smiod 		event.type = WSCONS_EVENT_MOUSE_DELTA_W;
3822fc38322Smiod 		event.value = act->dw;
3832fc38322Smiod 		treat_event(&event);
3842fc38322Smiod 	}
385673a75b7Saaron 
386673a75b7Saaron 	/* buttons state */
387673a75b7Saaron 	mask = act->flags & MOUSE_BUTTONS;
388673a75b7Saaron 	if (mask == 0)
389673a75b7Saaron 		/* no button modified */
390673a75b7Saaron 		return;
391673a75b7Saaron 
392673a75b7Saaron 	button = MOUSE_BUTTON1DOWN;
393673a75b7Saaron 	for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); i++) {
394673a75b7Saaron 		if (mask & 1) {
395b47703b0Smiod 			event.type = (act->button & button) ?
396b47703b0Smiod 			    WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
397673a75b7Saaron 			event.value = i;
398673a75b7Saaron 			treat_event(&event);
399673a75b7Saaron 		}
400673a75b7Saaron 		button <<= 1;
401673a75b7Saaron 		mask >>= 1;
402673a75b7Saaron 	}
403673a75b7Saaron }
404673a75b7Saaron 
405673a75b7Saaron /* main function */
406673a75b7Saaron static void
407673a75b7Saaron wsmoused(void)
408673a75b7Saaron {
409673a75b7Saaron 	mousestatus_t action;
410673a75b7Saaron 	struct wscons_event event; /* original wscons_event */
411673a75b7Saaron 	struct pollfd pfd[1];
412673a75b7Saaron 	int res;
413673a75b7Saaron 	u_char b;
414f3afb3efSjbm 	struct stat mdev_stat;
415673a75b7Saaron 
416673a75b7Saaron 	/* initialization */
417673a75b7Saaron 
418673a75b7Saaron 	event.type = WSCONS_EVENT_WSMOUSED_ON;
419be45c8afSmiod 	if (mouse.proto == P_WSCONS) {
420f3afb3efSjbm 		/* get major and minor of mouse device */
421f3afb3efSjbm 		res = stat(mouse.portname, &mdev_stat);
422f3afb3efSjbm 		if (res != -1)
423f3afb3efSjbm 			event.value = mdev_stat.st_rdev;
424f3afb3efSjbm 		else
425f3afb3efSjbm 			event.value = 0;
426be45c8afSmiod 	} else {
427be45c8afSmiod 		/* X11 won't start when using wsmoused(8) with a serial mouse */
428f3afb3efSjbm 		event.value = 0;
429be45c8afSmiod 	}
430f3afb3efSjbm 
431be45c8afSmiod 	/* notify kernel the start of wsmoused */
432673a75b7Saaron 	res = ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event);
433673a75b7Saaron 	if (res != 0) {
434673a75b7Saaron 		/* the display driver has no getchar() method */
435f3afb3efSjbm 		logerr(1, "this display driver has no support for wsmoused(8)");
436673a75b7Saaron 	}
437673a75b7Saaron 
438673a75b7Saaron 	bzero(&action, sizeof(action));
439673a75b7Saaron 	bzero(&event, sizeof(event));
440673a75b7Saaron 	bzero(&buttonstate, sizeof(buttonstate));
441673a75b7Saaron 
442673a75b7Saaron 	pfd[0].fd = mouse.mfd;
443673a75b7Saaron 	pfd[0].events = POLLIN;
444673a75b7Saaron 
445673a75b7Saaron 	/* process mouse data */
446673a75b7Saaron 	for (;;) {
44729f11fc7Sfgsch 		if (poll(pfd, 1, INFTIM) <= 0)
448673a75b7Saaron 			logwarn("failed to read from mouse");
449be45c8afSmiod 
450be45c8afSmiod 		if (mouse.proto == P_WSCONS) {
451673a75b7Saaron 			/* wsmouse supported mouse */
452673a75b7Saaron 			read(mouse.mfd, &event, sizeof(event));
453f3afb3efSjbm 			res = treat_event(&event);
454f3afb3efSjbm 			if (!res) {
455be45c8afSmiod 				/*
456be45c8afSmiod 				 * close mouse device and sleep until
457be45c8afSmiod 				 * the X server releases it
458be45c8afSmiod 				 */
459f3afb3efSjbm 
460f3afb3efSjbm 				struct wscons_event sleeping;
4610329dfd0Smiod 				unsigned int tries;
462f3afb3efSjbm 
463f3afb3efSjbm 				/* restore mouse resolution to default value */
464f3afb3efSjbm 				res = WSMOUSE_RES_DEFAULT;
465f3afb3efSjbm 				ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
466f3afb3efSjbm 
467f3afb3efSjbm 				close(mouse.mfd);
468f3afb3efSjbm 				mouse.mfd = -1;
469f3afb3efSjbm 
470f3afb3efSjbm 				/* sleep until X server releases mouse device */
471f3afb3efSjbm 				sleeping.type = WSCONS_EVENT_WSMOUSED_SLEEP;
472f3afb3efSjbm 				sleeping.value = 0;
473f3afb3efSjbm 				ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED,
474f3afb3efSjbm 				    &sleeping);
475f3afb3efSjbm 
4760329dfd0Smiod 				/*
4770329dfd0Smiod 				 * Since the X server could still be running
4780329dfd0Smiod 				 * (e.g. when switching from the graphics
4790329dfd0Smiod 				 * screen to a virtual text console), it might
4800329dfd0Smiod 				 * not have freed the device yet.
4810329dfd0Smiod 				 *
4820329dfd0Smiod 				 * Try to open the device until it succeeds.
4830329dfd0Smiod 				 */
4840329dfd0Smiod 				tries = 0;
4850329dfd0Smiod 				for (;;) {
486f3afb3efSjbm 					if ((mouse.mfd = open(mouse.portname,
4870329dfd0Smiod 					    O_RDONLY | O_NONBLOCK, 0)) != -1)
4880329dfd0Smiod 						break;
4890329dfd0Smiod 
4900329dfd0Smiod 					if (tries < 10) {
4910329dfd0Smiod 						tries++;
4920329dfd0Smiod 						sleep(1);
4930329dfd0Smiod 					} else {
494be45c8afSmiod 						logwarn("unable to open %s, "
4950329dfd0Smiod 						    "will retry in 10 seconds",
496f3afb3efSjbm 						    mouse.portname);
4970329dfd0Smiod 						sleep(10);
4980329dfd0Smiod 					}
4990329dfd0Smiod 				}
5000329dfd0Smiod 
501be45c8afSmiod 				wsmouse_init();
502f3afb3efSjbm 			}
503b47703b0Smiod 		} else {
504f3afb3efSjbm 			/* serial mouse (not supported by wsmouse) */
505673a75b7Saaron 			res = read(mouse.mfd, &b, 1);
506673a75b7Saaron 
507673a75b7Saaron 			/* if we have a full mouse event */
508673a75b7Saaron 			if (mouse_protocol(b, &action))
509673a75b7Saaron 				/* split it as multiple wscons_event */
510673a75b7Saaron 				split_event(&action);
511673a75b7Saaron 		}
512673a75b7Saaron 	}
513673a75b7Saaron }
514673a75b7Saaron 
515673a75b7Saaron 
516673a75b7Saaron static void
517673a75b7Saaron usage(void)
518673a75b7Saaron {
519be45c8afSmiod 	fprintf(stderr, "usage: %s [-2dfi] [-C thresh] [-D device] [-I file]"
520be45c8afSmiod 	    " [-M N=M]\n\t[-p device] [-t type]\n", __progname);
521673a75b7Saaron 	exit(1);
522673a75b7Saaron }
523673a75b7Saaron 
524673a75b7Saaron int
525673a75b7Saaron main(int argc, char **argv)
526673a75b7Saaron {
527be45c8afSmiod 	FILE *fp;
528be45c8afSmiod 	unsigned int type;
529673a75b7Saaron 	int opt;
530673a75b7Saaron 	int i;
531673a75b7Saaron 
532be45c8afSmiod #define GETOPT_STRING "2dfhip:t:C:D:I:M:"
533673a75b7Saaron 	while ((opt = (getopt(argc, argv, GETOPT_STRING))) != -1) {
534673a75b7Saaron 		switch (opt) {
535673a75b7Saaron 		case '2':
536673a75b7Saaron 			/* on two button mice, right button pastes */
537673a75b7Saaron 			p2l[MOUSE_BUTTON3] = MOUSE_BUTTON2;
538673a75b7Saaron 			break;
539673a75b7Saaron 		case 'd':
540673a75b7Saaron 			++debug;
541673a75b7Saaron 			break;
542673a75b7Saaron 		case 'f':
543673a75b7Saaron 			nodaemon = TRUE;
544673a75b7Saaron 			break;
545673a75b7Saaron 		case 'h':
546673a75b7Saaron 			usage();
547673a75b7Saaron 			break;
548673a75b7Saaron 		case 'i':
549673a75b7Saaron 			identify = TRUE;
550be45c8afSmiod 			nodaemon = TRUE;
551673a75b7Saaron 			break;
552673a75b7Saaron 		case 'p':
553ca198d9fSderaadt 			if ((mouse.portname = strdup(optarg)) == NULL)
554ca198d9fSderaadt 				logerr(1, "out of memory");
555673a75b7Saaron 			break;
556673a75b7Saaron 		case 't':
557673a75b7Saaron 			if (strcmp(optarg, "auto") == 0) {
558673a75b7Saaron 				mouse.proto = P_UNKNOWN;
559673a75b7Saaron 				mouse.flags &= ~NoPnP;
560673a75b7Saaron 				break;
561673a75b7Saaron 			}
562b47703b0Smiod 			for (i = 0; mouse_names[i] != NULL; i++)
563673a75b7Saaron 				if (strcmp(optarg,mouse_names[i]) == 0) {
564673a75b7Saaron 					mouse.proto = i;
565673a75b7Saaron 					mouse.flags |= NoPnP;
566673a75b7Saaron 					break;
567673a75b7Saaron 				}
568b47703b0Smiod 			if (mouse_names[i] != NULL)
569673a75b7Saaron 				break;
57046347781Smpech 			warnx("no such mouse protocol `%s'", optarg);
571673a75b7Saaron 			usage();
572673a75b7Saaron 			break;
573673a75b7Saaron 		case 'C':
574673a75b7Saaron #define MAX_CLICKTHRESHOLD 2000 /* max delay for double click */
575673a75b7Saaron 			mouse.clickthreshold = atoi(optarg);
576b47703b0Smiod 			if (mouse.clickthreshold < 0 ||
577b47703b0Smiod 			    mouse.clickthreshold > MAX_CLICKTHRESHOLD) {
57846347781Smpech 				warnx("invalid threshold `%s': max value is %d",
579b47703b0Smiod 				    optarg, MAX_CLICKTHRESHOLD);
580673a75b7Saaron 				usage();
581673a75b7Saaron 			}
582673a75b7Saaron 			break;
583be45c8afSmiod 		case 'D':
584be45c8afSmiod 			if ((mouse.ttyname = strdup(optarg)) == NULL)
585be45c8afSmiod 				logerr(1, "out of memory");
586be45c8afSmiod 			break;
587673a75b7Saaron 		case 'I':
588673a75b7Saaron 			pidfile = optarg;
589673a75b7Saaron 			break;
590673a75b7Saaron 		case 'M':
591673a75b7Saaron 			if (!mouse_installmap(optarg)) {
59246347781Smpech 				warnx("invalid mapping `%s'", optarg);
593673a75b7Saaron 				usage();
594673a75b7Saaron 			}
595673a75b7Saaron 			break;
596673a75b7Saaron 		default:
597673a75b7Saaron 			usage();
598673a75b7Saaron 		}
599673a75b7Saaron 	}
600be45c8afSmiod 
601be45c8afSmiod 	/*
602be45c8afSmiod 	 * Use defaults if unspecified
603be45c8afSmiod 	 */
604b47703b0Smiod 	if (mouse.portname == NULL)
605b47703b0Smiod 		mouse.portname = WSMOUSE_DEV;
606be45c8afSmiod 	if (mouse.ttyname == NULL)
607be45c8afSmiod 		mouse.ttyname = DEFAULT_TTY;
608673a75b7Saaron 
609be45c8afSmiod 	if (identify == FALSE) {
610be45c8afSmiod 		if ((mouse.cfd = open(mouse.ttyname, O_RDWR, 0)) == -1)
611be45c8afSmiod 			logerr(1, "cannot open %s", mouse.ttyname);
612be45c8afSmiod 	}
613be45c8afSmiod 
614673a75b7Saaron 	if ((mouse.mfd = open(mouse.portname,
615673a75b7Saaron 	    O_RDONLY | O_NONBLOCK, 0)) == -1)
616673a75b7Saaron 		logerr(1, "unable to open %s", mouse.portname);
617be45c8afSmiod 
618be45c8afSmiod 	/*
619be45c8afSmiod 	 * Find out whether the mouse device is a wsmouse device
620be45c8afSmiod 	 * or a serial device.
621be45c8afSmiod 	 */
622be45c8afSmiod 	if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) != -1)
623be45c8afSmiod 		mouse.proto = P_WSCONS;
624be45c8afSmiod 	else {
625673a75b7Saaron 		if (mouse_identify() == P_UNKNOWN) {
626673a75b7Saaron 			close(mouse.mfd);
627b47703b0Smiod 			logerr(1, "cannot determine mouse type on %s",
628b47703b0Smiod 			    mouse.portname);
629673a75b7Saaron 		}
630673a75b7Saaron 	}
631673a75b7Saaron 
632673a75b7Saaron 	if (identify == TRUE) {
633be45c8afSmiod 		if (mouse.proto == P_WSCONS)
634673a75b7Saaron 			wsmouse_identify();
635673a75b7Saaron 		else
636673a75b7Saaron 			printf("serial mouse: %s type\n",
637be45c8afSmiod 			    mouse_name(mouse.proto));
638b47703b0Smiod 		exit(0);
639673a75b7Saaron 	}
640673a75b7Saaron 
641be45c8afSmiod 	signal(SIGINT, terminate);
642be45c8afSmiod 	signal(SIGQUIT, terminate);
643be45c8afSmiod 	signal(SIGTERM, terminate);
644be45c8afSmiod 
645be45c8afSmiod 	if (mouse.proto == P_WSCONS)
646be45c8afSmiod 		wsmouse_init();
647be45c8afSmiod 	else
648673a75b7Saaron 		mouse_init();
649be45c8afSmiod 
650fca39c33Sdjm 	if (!nodaemon) {
651fca39c33Sdjm 		openlog(__progname, LOG_PID, LOG_DAEMON);
652fca39c33Sdjm 		if (daemon(0, 0)) {
653fca39c33Sdjm 			logerr(1, "failed to become a daemon");
654fca39c33Sdjm 		} else {
655fca39c33Sdjm 			background = TRUE;
656fca39c33Sdjm 			if (pidfile != NULL) {
657fca39c33Sdjm 				fp = fopen(pidfile, "w");
658fca39c33Sdjm 				if (fp != NULL) {
659fca39c33Sdjm 					fprintf(fp, "%ld\n", (long)getpid());
660fca39c33Sdjm 					fclose(fp);
661fca39c33Sdjm 				}
662fca39c33Sdjm 			}
663fca39c33Sdjm 		}
664fca39c33Sdjm 	}
665fca39c33Sdjm 
666673a75b7Saaron 	wsmoused();
667be45c8afSmiod 	exit(0);
668673a75b7Saaron }
669