GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/wsmoused/wsmoused.c Lines: 0 239 0.0 %
Date: 2017-11-07 Branches: 0 166 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: wsmoused.c,v 1.36 2015/10/26 09:58:18 deraadt Exp $ */
2
3
/*
4
 * Copyright (c) 2001 Jean-Baptiste Marchand, Julien Montagne and Jerome Verdon
5
 *
6
 * Copyright (c) 1998 by Kazutaka Yokota
7
 *
8
 * Copyright (c) 1995 Michael Smith
9
 *
10
 * Copyright (c) 1993 by David Dawes <dawes@xfree86.org>
11
 *
12
 * Copyright (c) 1990,91 by Thomas Roell, Dinkelscherben, Germany.
13
 *
14
 * All rights reserved.
15
 *
16
 * Most of this code was taken from the FreeBSD moused daemon, written by
17
 * Michael Smith. The FreeBSD moused daemon already contained code from the
18
 * Xfree Project, written by David Dawes and Thomas Roell and Kazutaka Yokota.
19
 *
20
 * Adaptation to OpenBSD was done by Jean-Baptiste Marchand, Julien Montagne
21
 * and Jerome Verdon.
22
 *
23
 * Redistribution and use in source and binary forms, with or without
24
 * modification, are permitted provided that the following conditions
25
 * are met:
26
 * 1. Redistributions of source code must retain the above copyright
27
 *    notice, this list of conditions and the following disclaimer.
28
 * 2. Redistributions in binary form must reproduce the above copyright
29
 *    notice, this list of conditions and the following disclaimer in the
30
 *    documentation and/or other materials provided with the distribution.
31
 * 3. All advertising materials mentioning features or use of this software
32
 *    must display the following acknowledgement:
33
 *	This product includes software developed by
34
 *      David Dawes, Jean-Baptiste Marchand, Julien Montagne, Thomas Roell,
35
 *      Michael Smith, Jerome Verdon and Kazutaka Yokota.
36
 * 4. The name authors may not be used to endorse or promote products
37
 *    derived from this software without specific prior written permission.
38
 *
39
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
40
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
42
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
43
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
 *
50
 *
51
 */
52
53
#include <sys/ioctl.h>
54
#include <sys/stat.h>
55
#include <sys/types.h>
56
#include <sys/time.h>
57
#include <sys/tty.h>
58
#include <dev/wscons/wsconsio.h>
59
60
#include <ctype.h>
61
#include <err.h>
62
#include <errno.h>
63
#include <fcntl.h>
64
#include <unistd.h>
65
#include <signal.h>
66
#include <poll.h>
67
#include <stdio.h>
68
#include <string.h>
69
#include <stdlib.h>
70
#include <syslog.h>
71
72
#include "mouse_protocols.h"
73
#include "wsmoused.h"
74
75
#define	DEFAULT_TTY	"/dev/ttyCcfg"
76
77
extern char *__progname;
78
extern char *mouse_names[];
79
80
int debug = 0;
81
int background = FALSE;
82
int nodaemon = FALSE;
83
int identify = FALSE;
84
85
mouse_t mouse = {
86
	.flags = 0,
87
	.portname = NULL,
88
	.ttyname = NULL,
89
	.proto = P_UNKNOWN,
90
	.rate = MOUSE_RATE_UNKNOWN,
91
	.resolution = MOUSE_RES_UNKNOWN,
92
	.mfd = -1,
93
	.clickthreshold = 500,	/* 0.5 sec */
94
};
95
96
/* identify the type of a wsmouse supported mouse */
97
void
98
wsmouse_identify(void)
99
{
100
	unsigned int type;
101
102
	if (mouse.mfd != -1) {
103
		if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) == -1)
104
			err(1, "can't detect mouse type");
105
106
		printf("wsmouse supported mouse: ");
107
		switch (type) {
108
		case WSMOUSE_TYPE_VSXXX:
109
			printf("DEC serial\n");
110
			break;
111
		case WSMOUSE_TYPE_PS2:
112
			printf("PS/2 compatible\n");
113
			break;
114
		case WSMOUSE_TYPE_USB:
115
			printf("USB\n");
116
			break;
117
		case WSMOUSE_TYPE_LMS:
118
			printf("Logitech busmouse\n");
119
			break;
120
		case WSMOUSE_TYPE_MMS:
121
			printf("Microsoft InPort mouse\n");
122
			break;
123
		case WSMOUSE_TYPE_TPANEL:
124
			printf("Generic Touch Panel\n");
125
			break;
126
		case WSMOUSE_TYPE_NEXT:
127
			printf("NeXT\n");
128
			break;
129
		case WSMOUSE_TYPE_ARCHIMEDES:
130
			printf("Archimedes\n");
131
			break;
132
		case WSMOUSE_TYPE_ADB:
133
			printf("ADB\n");
134
			break;
135
		case WSMOUSE_TYPE_HIL:
136
			printf("HP-HIL\n");
137
			break;
138
		case WSMOUSE_TYPE_LUNA:
139
			printf("Omron Luna\n");
140
			break;
141
		case WSMOUSE_TYPE_DOMAIN:
142
			printf("Apollo Domain\n");
143
			break;
144
		case WSMOUSE_TYPE_SUN:
145
			printf("Sun\n");
146
			break;
147
		default:
148
			printf("Unknown\n");
149
			break;
150
		}
151
	} else
152
		warnx("unable to open %s", mouse.portname);
153
}
154
155
/* wsmouse_init : init a wsmouse compatible mouse */
156
void
157
wsmouse_init(void)
158
{
159
	unsigned int res = WSMOUSE_RES_MIN;
160
	unsigned int rate = WSMOUSE_RATE_DEFAULT;
161
162
	ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
163
	ioctl(mouse.mfd, WSMOUSEIO_SRATE, &rate);
164
}
165
166
/*
167
 * Buttons remapping
168
 */
169
170
/* physical to logical button mapping */
171
static int p2l[MOUSE_MAXBUTTON] = {
172
	MOUSE_BUTTON1,	MOUSE_BUTTON2,	MOUSE_BUTTON3,	MOUSE_BUTTON4,
173
	MOUSE_BUTTON5,	MOUSE_BUTTON6,	MOUSE_BUTTON7,	MOUSE_BUTTON8,
174
};
175
176
static char *
177
skipspace(char *s)
178
{
179
	while (isspace((unsigned char)*s))
180
		++s;
181
	return s;
182
}
183
184
/* mouse_installmap : install a map between physical and logical buttons */
185
static int
186
mouse_installmap(char *arg)
187
{
188
	int pbutton;
189
	int lbutton;
190
	char *s;
191
192
	while (*arg) {
193
		arg = skipspace(arg);
194
		s = arg;
195
		while (isdigit((unsigned char)*arg))
196
			++arg;
197
		arg = skipspace(arg);
198
		if ((arg <= s) || (*arg != '='))
199
			return FALSE;
200
		lbutton = atoi(s);
201
202
		arg = skipspace(++arg);
203
		s = arg;
204
		while (isdigit((unsigned char)*arg))
205
			++arg;
206
		if (arg <= s || (!isspace((unsigned char)*arg) && *arg != '\0'))
207
			return FALSE;
208
		pbutton = atoi(s);
209
210
		if (lbutton <= 0 || lbutton > MOUSE_MAXBUTTON)
211
			return FALSE;
212
		if (pbutton <= 0 || pbutton > MOUSE_MAXBUTTON)
213
			return FALSE;
214
		p2l[pbutton - 1] = lbutton - 1;
215
	}
216
	return TRUE;
217
}
218
219
/* terminate signals handler */
220
static void
221
terminate(int sig)
222
{
223
	struct wscons_event event;
224
	unsigned int res;
225
226
	if (mouse.mfd != -1) {
227
		event.type = WSCONS_EVENT_WSMOUSED_OFF;
228
		ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event);
229
		res = WSMOUSE_RES_DEFAULT;
230
		ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
231
		close(mouse.mfd);
232
		mouse.mfd = -1;
233
	}
234
	_exit(0);
235
}
236
237
/* buttons status (for multiple click detection) */
238
static struct {
239
	int count;		/* 0: up, 1: single click, 2: double click,... */
240
	struct timeval tv;	/* timestamp on the last `up' event */
241
} buttonstate[MOUSE_MAXBUTTON];
242
243
/*
244
 * handle button click
245
 * Note that an ioctl is sent for each button
246
 */
247
static void
248
mouse_click(struct wscons_event *event)
249
{
250
	struct timeval max_date;
251
	struct timeval now;
252
	struct timeval delay;
253
	int i; /* button number */
254
255
	i = event->value = p2l[event->value];
256
257
	gettimeofday(&now, NULL);
258
	delay.tv_sec = mouse.clickthreshold / 1000;
259
	delay.tv_usec = (mouse.clickthreshold % 1000) * 1000;
260
	timersub(&now, &delay, &max_date);
261
262
	if (event->type == WSCONS_EVENT_MOUSE_DOWN) {
263
		if (timercmp(&max_date, &buttonstate[i].tv, >)) {
264
			timerclear(&buttonstate[i].tv);
265
			buttonstate[i].count = 1;
266
		} else {
267
			buttonstate[i].count++;
268
		}
269
	} else {
270
		/* button is up */
271
		buttonstate[i].tv.tv_sec = now.tv_sec;
272
		buttonstate[i].tv.tv_usec = now.tv_usec;
273
	}
274
275
	/*
276
	 * we use the time field of wscons_event structure to put the number
277
	 * of multiple clicks
278
	 */
279
	if (event->type == WSCONS_EVENT_MOUSE_DOWN) {
280
		event->time.tv_sec = buttonstate[i].count;
281
		event->time.tv_nsec = 0;
282
	} else {
283
		/* button is up */
284
		event->time.tv_sec = 0;
285
		event->time.tv_nsec = 0;
286
	}
287
	ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event);
288
}
289
290
/* workaround for cursor speed on serial mice */
291
static void
292
normalize_event(struct wscons_event *event)
293
{
294
	int dx, dy;
295
	int two_power = 1;
296
297
/* 2: normal speed, 3: slower cursor, 1: faster cursor */
298
#define NORMALIZE_DIVISOR 3
299
300
	switch (event->type) {
301
	case WSCONS_EVENT_MOUSE_DELTA_X:
302
		dx = abs(event->value);
303
		while (dx > 2) {
304
			two_power++;
305
			dx = dx / 2;
306
		}
307
		event->value = event->value / (NORMALIZE_DIVISOR * two_power);
308
		break;
309
	case WSCONS_EVENT_MOUSE_DELTA_Y:
310
		two_power = 1;
311
		dy = abs(event->value);
312
		while (dy > 2) {
313
			two_power++;
314
			dy = dy / 2;
315
		}
316
		event->value = event->value / (NORMALIZE_DIVISOR * two_power);
317
		break;
318
	}
319
}
320
321
/* send a wscons_event to the kernel */
322
static void
323
treat_event(struct wscons_event *event)
324
{
325
	if (IS_MOTION_EVENT(event->type)) {
326
		ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event);
327
	} else if (IS_BUTTON_EVENT(event->type) &&
328
	    (uint)event->value < MOUSE_MAXBUTTON) {
329
		mouse_click(event);
330
	}
331
}
332
333
/* split a full mouse event into multiples wscons events */
334
static void
335
split_event(mousestatus_t *act)
336
{
337
	struct wscons_event event;
338
	int button, i, mask;
339
340
	if (act->dx != 0) {
341
		event.type = WSCONS_EVENT_MOUSE_DELTA_X;
342
		event.value = act->dx;
343
		normalize_event(&event);
344
		treat_event(&event);
345
	}
346
	if (act->dy != 0) {
347
		event.type = WSCONS_EVENT_MOUSE_DELTA_Y;
348
		event.value = 0 - act->dy;
349
		normalize_event(&event);
350
		treat_event(&event);
351
	}
352
	if (act->dz != 0) {
353
		event.type = WSCONS_EVENT_MOUSE_DELTA_Z;
354
		event.value = act->dz;
355
		treat_event(&event);
356
	}
357
	if (act->dw != 0) {
358
		event.type = WSCONS_EVENT_MOUSE_DELTA_W;
359
		event.value = act->dw;
360
		treat_event(&event);
361
	}
362
363
	/* buttons state */
364
	mask = act->flags & MOUSE_BUTTONS;
365
	if (mask == 0)
366
		/* no button modified */
367
		return;
368
369
	button = MOUSE_BUTTON1DOWN;
370
	for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); i++) {
371
		if (mask & 1) {
372
			event.type = (act->button & button) ?
373
			    WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
374
			event.value = i;
375
			treat_event(&event);
376
		}
377
		button <<= 1;
378
		mask >>= 1;
379
	}
380
}
381
382
/* main function */
383
static void
384
wsmoused(void)
385
{
386
	mousestatus_t action;
387
	struct wscons_event event; /* original wscons_event */
388
	struct pollfd pfd[1];
389
	int res;
390
	u_char b;
391
392
	/* notify kernel the start of wsmoused */
393
	event.type = WSCONS_EVENT_WSMOUSED_ON;
394
	res = ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event);
395
	if (res != 0) {
396
		/* the display driver has no getchar() method */
397
		logerr(1, "this display driver has no support for wsmoused(8)");
398
	}
399
400
	bzero(&action, sizeof(action));
401
	bzero(&event, sizeof(event));
402
	bzero(&buttonstate, sizeof(buttonstate));
403
404
	pfd[0].fd = mouse.mfd;
405
	pfd[0].events = POLLIN;
406
407
	/* process mouse data */
408
	for (;;) {
409
		if (poll(pfd, 1, INFTIM) <= 0)
410
			logwarn("failed to read from mouse");
411
412
		if (mouse.proto == P_WSCONS) {
413
			/* wsmouse supported mouse */
414
			read(mouse.mfd, &event, sizeof(event));
415
			treat_event(&event);
416
		} else {
417
			/* serial mouse (not supported by wsmouse) */
418
			res = read(mouse.mfd, &b, 1);
419
420
			/* if we have a full mouse event */
421
			if (mouse_protocol(b, &action))
422
				/* split it as multiple wscons_event */
423
				split_event(&action);
424
		}
425
	}
426
}
427
428
429
static void
430
usage(void)
431
{
432
	fprintf(stderr, "usage: %s [-2dfi] [-C thresh] [-D device]"
433
	    " [-M N=M]\n\t[-p device] [-t type]\n", __progname);
434
	exit(1);
435
}
436
437
int
438
main(int argc, char **argv)
439
{
440
	unsigned int type;
441
	int opt;
442
	int i;
443
444
#define GETOPT_STRING "2dfhip:t:C:D:M:"
445
	while ((opt = (getopt(argc, argv, GETOPT_STRING))) != -1) {
446
		switch (opt) {
447
		case '2':
448
			/* on two button mice, right button pastes */
449
			p2l[MOUSE_BUTTON3] = MOUSE_BUTTON2;
450
			break;
451
		case 'd':
452
			++debug;
453
			break;
454
		case 'f':
455
			nodaemon = TRUE;
456
			break;
457
		case 'h':
458
			usage();
459
			break;
460
		case 'i':
461
			identify = TRUE;
462
			nodaemon = TRUE;
463
			break;
464
		case 'p':
465
			if ((mouse.portname = strdup(optarg)) == NULL)
466
				logerr(1, "out of memory");
467
			break;
468
		case 't':
469
			if (strcmp(optarg, "auto") == 0) {
470
				mouse.proto = P_UNKNOWN;
471
				mouse.flags &= ~NoPnP;
472
				break;
473
			}
474
			for (i = 0; mouse_names[i] != NULL; i++)
475
				if (strcmp(optarg,mouse_names[i]) == 0) {
476
					mouse.proto = i;
477
					mouse.flags |= NoPnP;
478
					break;
479
				}
480
			if (mouse_names[i] != NULL)
481
				break;
482
			warnx("no such mouse protocol `%s'", optarg);
483
			usage();
484
			break;
485
		case 'C':
486
#define MAX_CLICKTHRESHOLD 2000 /* max delay for double click */
487
			mouse.clickthreshold = atoi(optarg);
488
			if (mouse.clickthreshold < 0 ||
489
			    mouse.clickthreshold > MAX_CLICKTHRESHOLD) {
490
				warnx("invalid threshold `%s': max value is %d",
491
				    optarg, MAX_CLICKTHRESHOLD);
492
				usage();
493
			}
494
			break;
495
		case 'D':
496
			if ((mouse.ttyname = strdup(optarg)) == NULL)
497
				logerr(1, "out of memory");
498
			break;
499
		case 'M':
500
			if (!mouse_installmap(optarg)) {
501
				warnx("invalid mapping `%s'", optarg);
502
				usage();
503
			}
504
			break;
505
		default:
506
			usage();
507
		}
508
	}
509
510
	/*
511
	 * Use defaults if unspecified
512
	 */
513
	if (mouse.portname == NULL)
514
		mouse.portname = WSMOUSE_DEV;
515
	if (mouse.ttyname == NULL)
516
		mouse.ttyname = DEFAULT_TTY;
517
518
	if (identify == FALSE) {
519
		if ((mouse.cfd = open(mouse.ttyname, O_RDWR, 0)) == -1)
520
			logerr(1, "cannot open %s", mouse.ttyname);
521
	}
522
523
	if ((mouse.mfd = open(mouse.portname,
524
	    O_RDONLY | O_NONBLOCK, 0)) == -1)
525
		logerr(1, "unable to open %s", mouse.portname);
526
527
	/*
528
	 * Find out whether the mouse device is a wsmouse device
529
	 * or a serial device.
530
	 */
531
	if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) != -1)
532
		mouse.proto = P_WSCONS;
533
	else {
534
		if (mouse_identify() == P_UNKNOWN) {
535
			close(mouse.mfd);
536
			logerr(1, "cannot determine mouse type on %s",
537
			    mouse.portname);
538
		}
539
	}
540
541
	if (identify == TRUE) {
542
		if (mouse.proto == P_WSCONS)
543
			wsmouse_identify();
544
		else
545
			printf("serial mouse: %s type\n",
546
			    mouse_name(mouse.proto));
547
		exit(0);
548
	}
549
550
	signal(SIGINT, terminate);
551
	signal(SIGQUIT, terminate);
552
	signal(SIGTERM, terminate);
553
554
	if (mouse.proto == P_WSCONS)
555
		wsmouse_init();
556
	else
557
		mouse_init();
558
559
	if (!nodaemon) {
560
		openlog(__progname, LOG_PID, LOG_DAEMON);
561
		if (daemon(0, 0)) {
562
			logerr(1, "failed to become a daemon");
563
		} else {
564
			background = TRUE;
565
		}
566
	}
567
568
	wsmoused();
569
	exit(0);
570
}