xref: /netbsd/usr.sbin/wsmoused/wsmoused.c (revision c4a72b64)
1 /* $NetBSD: wsmoused.c,v 1.5 2002/08/20 16:55:28 christos Exp $ */
2 
3 /*
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Julio Merino.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. The name authors may not be used to endorse or promote products
16  *    derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #ifndef lint
35 __RCSID("$NetBSD: wsmoused.c,v 1.5 2002/08/20 16:55:28 christos Exp $");
36 #endif /* not lint */
37 
38 #include <sys/ioctl.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/tty.h>
42 #include <dev/wscons/wsconsio.h>
43 
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <poll.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include <syslog.h>
53 #include <unistd.h>
54 
55 #include "pathnames.h"
56 #include "wsmoused.h"
57 
58 #define IS_MOTION_EVENT(type) (((type) == WSCONS_EVENT_MOUSE_DELTA_X) || \
59 			       ((type) == WSCONS_EVENT_MOUSE_DELTA_Y) || \
60 			       ((type) == WSCONS_EVENT_MOUSE_DELTA_Z))
61 #define IS_BUTTON_EVENT(type) (((type) == WSCONS_EVENT_MOUSE_UP) || \
62 			       ((type) == WSCONS_EVENT_MOUSE_DOWN))
63 
64 static struct mouse mouse;
65 int XConsole = -1;
66 int NoDaemon = 0;
67 
68 /*
69  * Show program usage information
70  */
71 static void
72 usage(void)
73 {
74 	(void)fprintf(stderr,
75 	    "Usage: %s [-d device] [-f fifo] [-n] [-X number] [-x number] "
76 	    "[-y number]\n",
77 	    getprogname());
78 	exit(EXIT_FAILURE);
79 }
80 
81 /*
82  * Handler for close signals
83  */
84 static void
85 signal_terminate(int sig)
86 {
87 	mouse_cursor_hide(&mouse);
88 	exit(EXIT_SUCCESS);
89 }
90 
91 /*
92  * Open the mouse device (i.e. /dev/wsmouse). The argument secs
93  * specifies the number of seconds we must wait before opening the
94  * device. This is used when returning from X. See mouse_open_tty().
95  */
96 static void
97 mouse_open_device(struct mouse *m, int secs)
98 {
99 	if (m->fd != -1) return;
100 
101 	sleep(secs);
102 
103 	/* Open mouse file descriptor */
104 	m->fd = open(m->device_name,
105 	             O_RDONLY | O_NONBLOCK, 0);
106 	if (m->fd == -1) {
107 		err(EXIT_FAILURE, "cannot open '%s'",
108 		    m->device_name);
109 	}
110 }
111 
112 /*
113  * Prepare mouse file descriptors
114  */
115 static void
116 open_files(void)
117 {
118 	/* Open wsdisplay status device */
119 	mouse.stat_fd = open(_PATH_TTYSTAT, O_RDONLY | O_NONBLOCK, 0);
120 	if (mouse.stat_fd == -1)
121 		err(EXIT_FAILURE, "Cannot open `%s'", _PATH_TTYSTAT);
122 
123 	mouse.fd = -1;
124 	mouse_open_device(&mouse, 0);
125 
126 	/* Open FIFO, if wanted */
127 	if (mouse.fifo_name != NULL) {
128 		mouse.fifo_fd = open(mouse.fifo_name, O_RDWR | O_NONBLOCK, 0);
129 		if (mouse.fifo_fd == -1)
130 			err(EXIT_FAILURE, "Cannot open `%s'", mouse.fifo_name);
131 	}
132 }
133 
134 /*
135  * Mouse event loop
136  */
137 static void
138 event_loop(void)
139 {
140 	int res;
141 	struct pollfd fds[2];
142 	struct wscons_event event;
143 
144 	fds[0].fd = mouse.stat_fd;
145 	fds[0].events = POLLIN;
146 
147 	for (;;) {
148 		fds[1].fd = mouse.fd;
149 		fds[1].events = POLLIN;
150 		if (mouse.disabled)
151 			res = poll(fds, 1, INFTIM);
152 		else
153 			res = poll(fds, 2, 300);
154 
155 		if (res < 0)
156 			warn("failed to read from devices");
157 
158 		if (fds[0].revents & POLLIN) {
159 			res = read(mouse.stat_fd, &event, sizeof(event));
160 			if (res != sizeof(event))
161 				warn("failed to read from mouse stat");
162 			screen_event(&mouse, &event);
163 		} else if (fds[1].revents & POLLIN) {
164 			res = read(mouse.fd, &event, sizeof(event));
165 			if (res != sizeof(event))
166 				warn("failed to read from mouse");
167 
168 			if (mouse.fifo_fd >= 0) {
169 				res = write(mouse.fifo_fd, &event,
170 				            sizeof(event));
171 				if (res != sizeof(event))
172 					warn("failed to write to fifo");
173 			}
174 
175 			if (IS_MOTION_EVENT(event.type)) {
176 				mouse_motion_event(&mouse, &event);
177 			} else if (IS_BUTTON_EVENT(event.type)) {
178 				mouse_button_event(&mouse, &event);
179 			} else {
180 				warn("unknown wsmouse event");
181 			}
182 		} else
183 			if (!mouse.selecting) mouse_cursor_hide(&mouse);
184 	}
185 }
186 
187 /*
188  * Initializes mouse structure coordinate information. The mouse will
189  * be displayed at the center of the screen at start.
190  */
191 static void
192 mouse_init(void)
193 {
194 	struct winsize ws;
195 	struct wsdisplay_char ch;
196 	int i;
197 
198 	/* Get terminal size */
199 	if (ioctl(0, TIOCGWINSZ, &ws) < 0)
200 		err(EXIT_FAILURE, "Cannot get terminal size");
201 
202 	/* Open current tty */
203 	ioctl(mouse.stat_fd, WSDISPLAYIO_GETACTIVESCREEN, &i);
204 	mouse.tty_fd = -1;
205 	mouse_open_tty(&mouse, i);
206 
207 	/* Check if the kernel has character functions */
208 	ch.row = ch.col = 0;
209 	if (ioctl(mouse.tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) < 0)
210 		err(EXIT_FAILURE, "ioctl(WSDISPLAYIO_GETWSCHAR) failed");
211 
212 	mouse.max_row = ws.ws_row - 1;
213 	mouse.max_col = ws.ws_col - 1;
214 	mouse.row = mouse.max_row / 2;
215 	mouse.col = mouse.max_col / 2;
216 	mouse.count_row = 0;
217 	mouse.count_col = 0;
218 	mouse.cursor = 0;
219 	mouse.selecting = 0;
220 }
221 
222 /*
223  * Hides the mouse cursor
224  */
225 void
226 mouse_cursor_hide(struct mouse *m)
227 {
228 	if (!m->cursor) return;
229 	char_invert(m, m->row, m->col);
230 	m->cursor = 0;
231 }
232 
233 /*
234  * Shows the mouse cursor
235  */
236 void
237 mouse_cursor_show(struct mouse *m)
238 {
239 	if (m->cursor) return;
240 	char_invert(m, m->row, m->col);
241 	m->cursor = 1;
242 }
243 
244 /*
245  * Opens the specified tty (we want it for ioctl's). If tty_fd in
246  * mouse structure is -1, no close is performed. Otherwise, the old
247  * file descriptor is closed and the new one opened.
248  */
249 void
250 mouse_open_tty(struct mouse *m, int ttyno)
251 {
252 	char buf[20];
253 
254 	if (m->tty_fd >= 0) close(m->tty_fd);
255 	if (ttyno == XConsole) {
256 		m->disabled = 1;
257 		(void)close(m->fd);
258 		m->fd = -1;
259 		return;
260 	}
261 	/* Open with delay. When returning from X, wsmoused keeps busy
262 	   some seconds so we have to wait. */
263 	mouse_open_device(m, 5);
264 	(void)snprintf(buf, sizeof(buf), _PATH_TTYPREFIX "%d", ttyno);
265 	m->tty_fd = open(buf, O_RDONLY | O_NONBLOCK);
266 	if (m->tty_fd < 0)
267 		errx(EXIT_FAILURE, "Cannot open `%s'", buf);
268 	m->disabled = 0;
269 }
270 
271 /*
272  * Flip the foreground and background colors on char at coordinates
273  */
274 void
275 char_invert(struct mouse *m, size_t row, size_t col)
276 {
277 	struct wsdisplay_char ch;
278 	int t;
279 
280 	ch.row = row;
281 	ch.col = col;
282 
283 	if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) {
284 		warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
285 		return;
286 	}
287 
288 	t = ch.foreground;
289 	ch.foreground = ch.background;
290 	ch.background = t;
291 
292 	if (ioctl(m->tty_fd, WSDISPLAYIO_PUTWSCHAR, &ch) == -1)
293 		warn("ioctl(WSDISPLAYIO_PUTWSCHAR) failed");
294 }
295 
296 /*
297  * Main function
298  */
299 int
300 main(int argc, char **argv)
301 {
302 	int opt;
303 
304 	setprogname(argv[0]);
305 
306 	/* Setup mouse default data */
307 	memset(&mouse, 0, sizeof(struct mouse));
308 	mouse.fifo_fd = -1;
309 	mouse.device_name = _PATH_DEFAULT_MOUSE;
310 	mouse.fifo_name = NULL;
311 
312 	/* Defaults that can be overriden by options. If you change
313 	 * these, update wsmoused.8 accordingly. */
314 	mouse.slowdown_x = 0;
315 	mouse.slowdown_y = 3;
316 
317 	/* Parse command line options */
318 	while ((opt = getopt(argc, argv, "nf:d:X:x:y:")) != -1) {
319 		switch (opt) {
320 		case 'd': /* Mouse device name */
321 			mouse.device_name = strdup(optarg);
322 			break;
323 		case 'f': /* FIFO file name */
324 			mouse.fifo_name = strdup(optarg);
325 			break;
326 		case 'n': /* No daemon */
327 			NoDaemon = 1;
328 			break;
329 		case 'X': /* X console number */
330 			XConsole = atoi(optarg);
331 			break;
332 		case 'x': /* x slowdown */
333 			mouse.slowdown_x = atoi(optarg);
334 			break;
335 		case 'y': /* y slowdown */
336 			mouse.slowdown_y = atoi(optarg);
337 			break;
338 		default:
339 			usage();
340 			/* NOTREACHED */
341 		}
342 	}
343 
344 	open_files();
345 	mouse_init();
346 	mouse_sel_init();
347 
348 	/* Setup signal handlers */
349 	(void)signal(SIGINT,  signal_terminate);
350 	(void)signal(SIGKILL, signal_terminate);
351 	(void)signal(SIGQUIT, signal_terminate);
352 	(void)signal(SIGTERM, signal_terminate);
353 
354 	if (!NoDaemon) daemon(0, 0);
355 	event_loop();
356 
357 	return EXIT_SUCCESS;
358 }
359