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

Line Branch Exec Source
1
/* $OpenBSD: wsmoused.c,v 1.37 2017/10/24 09:36:13 jsg 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
161
	ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
162
}
163
164
/*
165
 * Buttons remapping
166
 */
167
168
/* physical to logical button mapping */
169
static int p2l[MOUSE_MAXBUTTON] = {
170
	MOUSE_BUTTON1,	MOUSE_BUTTON2,	MOUSE_BUTTON3,	MOUSE_BUTTON4,
171
	MOUSE_BUTTON5,	MOUSE_BUTTON6,	MOUSE_BUTTON7,	MOUSE_BUTTON8,
172
};
173
174
static char *
175
skipspace(char *s)
176
{
177
	while (isspace((unsigned char)*s))
178
		++s;
179
	return s;
180
}
181
182
/* mouse_installmap : install a map between physical and logical buttons */
183
static int
184
mouse_installmap(char *arg)
185
{
186
	int pbutton;
187
	int lbutton;
188
	char *s;
189
190
	while (*arg) {
191
		arg = skipspace(arg);
192
		s = arg;
193
		while (isdigit((unsigned char)*arg))
194
			++arg;
195
		arg = skipspace(arg);
196
		if ((arg <= s) || (*arg != '='))
197
			return FALSE;
198
		lbutton = atoi(s);
199
200
		arg = skipspace(++arg);
201
		s = arg;
202
		while (isdigit((unsigned char)*arg))
203
			++arg;
204
		if (arg <= s || (!isspace((unsigned char)*arg) && *arg != '\0'))
205
			return FALSE;
206
		pbutton = atoi(s);
207
208
		if (lbutton <= 0 || lbutton > MOUSE_MAXBUTTON)
209
			return FALSE;
210
		if (pbutton <= 0 || pbutton > MOUSE_MAXBUTTON)
211
			return FALSE;
212
		p2l[pbutton - 1] = lbutton - 1;
213
	}
214
	return TRUE;
215
}
216
217
/* terminate signals handler */
218
static void
219
terminate(int sig)
220
{
221
	struct wscons_event event;
222
	unsigned int res;
223
224
	if (mouse.mfd != -1) {
225
		event.type = WSCONS_EVENT_WSMOUSED_OFF;
226
		ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event);
227
		res = WSMOUSE_RES_DEFAULT;
228
		ioctl(mouse.mfd, WSMOUSEIO_SRES, &res);
229
		close(mouse.mfd);
230
		mouse.mfd = -1;
231
	}
232
	_exit(0);
233
}
234
235
/* buttons status (for multiple click detection) */
236
static struct {
237
	int count;		/* 0: up, 1: single click, 2: double click,... */
238
	struct timeval tv;	/* timestamp on the last `up' event */
239
} buttonstate[MOUSE_MAXBUTTON];
240
241
/*
242
 * handle button click
243
 * Note that an ioctl is sent for each button
244
 */
245
static void
246
mouse_click(struct wscons_event *event)
247
{
248
	struct timeval max_date;
249
	struct timeval now;
250
	struct timeval delay;
251
	int i; /* button number */
252
253
	i = event->value = p2l[event->value];
254
255
	gettimeofday(&now, NULL);
256
	delay.tv_sec = mouse.clickthreshold / 1000;
257
	delay.tv_usec = (mouse.clickthreshold % 1000) * 1000;
258
	timersub(&now, &delay, &max_date);
259
260
	if (event->type == WSCONS_EVENT_MOUSE_DOWN) {
261
		if (timercmp(&max_date, &buttonstate[i].tv, >)) {
262
			timerclear(&buttonstate[i].tv);
263
			buttonstate[i].count = 1;
264
		} else {
265
			buttonstate[i].count++;
266
		}
267
	} else {
268
		/* button is up */
269
		buttonstate[i].tv.tv_sec = now.tv_sec;
270
		buttonstate[i].tv.tv_usec = now.tv_usec;
271
	}
272
273
	/*
274
	 * we use the time field of wscons_event structure to put the number
275
	 * of multiple clicks
276
	 */
277
	if (event->type == WSCONS_EVENT_MOUSE_DOWN) {
278
		event->time.tv_sec = buttonstate[i].count;
279
		event->time.tv_nsec = 0;
280
	} else {
281
		/* button is up */
282
		event->time.tv_sec = 0;
283
		event->time.tv_nsec = 0;
284
	}
285
	ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event);
286
}
287
288
/* workaround for cursor speed on serial mice */
289
static void
290
normalize_event(struct wscons_event *event)
291
{
292
	int dx, dy;
293
	int two_power = 1;
294
295
/* 2: normal speed, 3: slower cursor, 1: faster cursor */
296
#define NORMALIZE_DIVISOR 3
297
298
	switch (event->type) {
299
	case WSCONS_EVENT_MOUSE_DELTA_X:
300
		dx = abs(event->value);
301
		while (dx > 2) {
302
			two_power++;
303
			dx = dx / 2;
304
		}
305
		event->value = event->value / (NORMALIZE_DIVISOR * two_power);
306
		break;
307
	case WSCONS_EVENT_MOUSE_DELTA_Y:
308
		two_power = 1;
309
		dy = abs(event->value);
310
		while (dy > 2) {
311
			two_power++;
312
			dy = dy / 2;
313
		}
314
		event->value = event->value / (NORMALIZE_DIVISOR * two_power);
315
		break;
316
	}
317
}
318
319
/* send a wscons_event to the kernel */
320
static void
321
treat_event(struct wscons_event *event)
322
{
323
	if (IS_MOTION_EVENT(event->type)) {
324
		ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event);
325
	} else if (IS_BUTTON_EVENT(event->type) &&
326
	    (uint)event->value < MOUSE_MAXBUTTON) {
327
		mouse_click(event);
328
	}
329
}
330
331
/* split a full mouse event into multiples wscons events */
332
static void
333
split_event(mousestatus_t *act)
334
{
335
	struct wscons_event event;
336
	int button, i, mask;
337
338
	if (act->dx != 0) {
339
		event.type = WSCONS_EVENT_MOUSE_DELTA_X;
340
		event.value = act->dx;
341
		normalize_event(&event);
342
		treat_event(&event);
343
	}
344
	if (act->dy != 0) {
345
		event.type = WSCONS_EVENT_MOUSE_DELTA_Y;
346
		event.value = 0 - act->dy;
347
		normalize_event(&event);
348
		treat_event(&event);
349
	}
350
	if (act->dz != 0) {
351
		event.type = WSCONS_EVENT_MOUSE_DELTA_Z;
352
		event.value = act->dz;
353
		treat_event(&event);
354
	}
355
	if (act->dw != 0) {
356
		event.type = WSCONS_EVENT_MOUSE_DELTA_W;
357
		event.value = act->dw;
358
		treat_event(&event);
359
	}
360
361
	/* buttons state */
362
	mask = act->flags & MOUSE_BUTTONS;
363
	if (mask == 0)
364
		/* no button modified */
365
		return;
366
367
	button = MOUSE_BUTTON1DOWN;
368
	for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); i++) {
369
		if (mask & 1) {
370
			event.type = (act->button & button) ?
371
			    WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
372
			event.value = i;
373
			treat_event(&event);
374
		}
375
		button <<= 1;
376
		mask >>= 1;
377
	}
378
}
379
380
/* main function */
381
static void
382
wsmoused(void)
383
{
384
	mousestatus_t action;
385
	struct wscons_event event; /* original wscons_event */
386
	struct pollfd pfd[1];
387
	int res;
388
	u_char b;
389
390
	/* notify kernel the start of wsmoused */
391
	event.type = WSCONS_EVENT_WSMOUSED_ON;
392
	res = ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event);
393
	if (res != 0) {
394
		/* the display driver has no getchar() method */
395
		logerr(1, "this display driver has no support for wsmoused(8)");
396
	}
397
398
	bzero(&action, sizeof(action));
399
	bzero(&event, sizeof(event));
400
	bzero(&buttonstate, sizeof(buttonstate));
401
402
	pfd[0].fd = mouse.mfd;
403
	pfd[0].events = POLLIN;
404
405
	/* process mouse data */
406
	for (;;) {
407
		if (poll(pfd, 1, INFTIM) <= 0)
408
			logwarn("failed to read from mouse");
409
410
		if (mouse.proto == P_WSCONS) {
411
			/* wsmouse supported mouse */
412
			read(mouse.mfd, &event, sizeof(event));
413
			treat_event(&event);
414
		} else {
415
			/* serial mouse (not supported by wsmouse) */
416
			res = read(mouse.mfd, &b, 1);
417
418
			/* if we have a full mouse event */
419
			if (mouse_protocol(b, &action))
420
				/* split it as multiple wscons_event */
421
				split_event(&action);
422
		}
423
	}
424
}
425
426
427
static void
428
usage(void)
429
{
430
	fprintf(stderr, "usage: %s [-2dfi] [-C thresh] [-D device]"
431
	    " [-M N=M]\n\t[-p device] [-t type]\n", __progname);
432
	exit(1);
433
}
434
435
int
436
main(int argc, char **argv)
437
{
438
	unsigned int type;
439
	int opt;
440
	int i;
441
442
#define GETOPT_STRING "2dfhip:t:C:D:M:"
443
	while ((opt = (getopt(argc, argv, GETOPT_STRING))) != -1) {
444
		switch (opt) {
445
		case '2':
446
			/* on two button mice, right button pastes */
447
			p2l[MOUSE_BUTTON3] = MOUSE_BUTTON2;
448
			break;
449
		case 'd':
450
			++debug;
451
			break;
452
		case 'f':
453
			nodaemon = TRUE;
454
			break;
455
		case 'h':
456
			usage();
457
			break;
458
		case 'i':
459
			identify = TRUE;
460
			nodaemon = TRUE;
461
			break;
462
		case 'p':
463
			if ((mouse.portname = strdup(optarg)) == NULL)
464
				logerr(1, "out of memory");
465
			break;
466
		case 't':
467
			if (strcmp(optarg, "auto") == 0) {
468
				mouse.proto = P_UNKNOWN;
469
				mouse.flags &= ~NoPnP;
470
				break;
471
			}
472
			for (i = 0; mouse_names[i] != NULL; i++)
473
				if (strcmp(optarg,mouse_names[i]) == 0) {
474
					mouse.proto = i;
475
					mouse.flags |= NoPnP;
476
					break;
477
				}
478
			if (mouse_names[i] != NULL)
479
				break;
480
			warnx("no such mouse protocol `%s'", optarg);
481
			usage();
482
			break;
483
		case 'C':
484
#define MAX_CLICKTHRESHOLD 2000 /* max delay for double click */
485
			mouse.clickthreshold = atoi(optarg);
486
			if (mouse.clickthreshold < 0 ||
487
			    mouse.clickthreshold > MAX_CLICKTHRESHOLD) {
488
				warnx("invalid threshold `%s': max value is %d",
489
				    optarg, MAX_CLICKTHRESHOLD);
490
				usage();
491
			}
492
			break;
493
		case 'D':
494
			if ((mouse.ttyname = strdup(optarg)) == NULL)
495
				logerr(1, "out of memory");
496
			break;
497
		case 'M':
498
			if (!mouse_installmap(optarg)) {
499
				warnx("invalid mapping `%s'", optarg);
500
				usage();
501
			}
502
			break;
503
		default:
504
			usage();
505
		}
506
	}
507
508
	/*
509
	 * Use defaults if unspecified
510
	 */
511
	if (mouse.portname == NULL)
512
		mouse.portname = WSMOUSE_DEV;
513
	if (mouse.ttyname == NULL)
514
		mouse.ttyname = DEFAULT_TTY;
515
516
	if (identify == FALSE) {
517
		if ((mouse.cfd = open(mouse.ttyname, O_RDWR, 0)) == -1)
518
			logerr(1, "cannot open %s", mouse.ttyname);
519
	}
520
521
	if ((mouse.mfd = open(mouse.portname,
522
	    O_RDONLY | O_NONBLOCK, 0)) == -1)
523
		logerr(1, "unable to open %s", mouse.portname);
524
525
	/*
526
	 * Find out whether the mouse device is a wsmouse device
527
	 * or a serial device.
528
	 */
529
	if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) != -1)
530
		mouse.proto = P_WSCONS;
531
	else {
532
		if (mouse_identify() == P_UNKNOWN) {
533
			close(mouse.mfd);
534
			logerr(1, "cannot determine mouse type on %s",
535
			    mouse.portname);
536
		}
537
	}
538
539
	if (identify == TRUE) {
540
		if (mouse.proto == P_WSCONS)
541
			wsmouse_identify();
542
		else
543
			printf("serial mouse: %s type\n",
544
			    mouse_name(mouse.proto));
545
		exit(0);
546
	}
547
548
	signal(SIGINT, terminate);
549
	signal(SIGQUIT, terminate);
550
	signal(SIGTERM, terminate);
551
552
	if (mouse.proto == P_WSCONS)
553
		wsmouse_init();
554
	else
555
		mouse_init();
556
557
	if (!nodaemon) {
558
		openlog(__progname, LOG_PID, LOG_DAEMON);
559
		if (daemon(0, 0)) {
560
			logerr(1, "failed to become a daemon");
561
		} else {
562
			background = TRUE;
563
		}
564
	}
565
566
	wsmoused();
567
	exit(0);
568
}