wsmoused.c revision 1.6 1 /* $NetBSD: wsmoused.c,v 1.6 2002/12/25 19:13:53 jmmv 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.6 2002/12/25 19:13:53 jmmv 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] [-ln] [-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 /* Right handed by default */
318 mouse.but_select = 0;
319 mouse.but_paste = 2;
320
321 /* Parse command line options */
322 while ((opt = getopt(argc, argv, "d:f:lny:X:x:")) != -1) {
323 switch (opt) {
324 case 'd': /* Mouse device name */
325 mouse.device_name = strdup(optarg);
326 break;
327 case 'f': /* FIFO file name */
328 mouse.fifo_name = strdup(optarg);
329 break;
330 case 'l': /* Left handed */
331 mouse.but_select = 2;
332 mouse.but_paste = 0;
333 break;
334 case 'n': /* No daemon */
335 NoDaemon = 1;
336 break;
337 case 'X': /* X console number */
338 XConsole = atoi(optarg);
339 break;
340 case 'x': /* x slowdown */
341 mouse.slowdown_x = atoi(optarg);
342 break;
343 case 'y': /* y slowdown */
344 mouse.slowdown_y = atoi(optarg);
345 break;
346 default:
347 usage();
348 /* NOTREACHED */
349 }
350 }
351
352 open_files();
353 mouse_init();
354 mouse_sel_init();
355
356 /* Setup signal handlers */
357 (void)signal(SIGINT, signal_terminate);
358 (void)signal(SIGKILL, signal_terminate);
359 (void)signal(SIGQUIT, signal_terminate);
360 (void)signal(SIGTERM, signal_terminate);
361
362 if (!NoDaemon) daemon(0, 0);
363 event_loop();
364
365 return EXIT_SUCCESS;
366 }
367