wsmoused.c revision 1.4 1 /* $NetBSD: wsmoused.c,v 1.4 2002/07/04 20:50:29 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.4 2002/07/04 20:50:29 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]\n",
76 getprogname());
77 exit(EXIT_FAILURE);
78 }
79
80 /*
81 * Handler for close signals
82 */
83 static void
84 signal_terminate(int sig)
85 {
86 mouse_cursor_hide(&mouse);
87 exit(EXIT_SUCCESS);
88 }
89
90 /*
91 * Open the mouse device (i.e. /dev/wsmouse). The argument secs
92 * specifies the number of seconds we must wait before opening the
93 * device. This is used when returning from X. See mouse_open_tty().
94 */
95 static void
96 mouse_open_device(struct mouse *m, int secs)
97 {
98 if (m->fd != -1) return;
99
100 sleep(secs);
101
102 /* Open mouse file descriptor */
103 m->fd = open(m->device_name,
104 O_RDONLY | O_NONBLOCK, 0);
105 if (m->fd == -1) {
106 err(EXIT_FAILURE, "cannot open '%s'",
107 m->device_name);
108 }
109 }
110
111 /*
112 * Prepare mouse file descriptors
113 */
114 static void
115 open_files(void)
116 {
117 /* Open wsdisplay status device */
118 mouse.stat_fd = open(_PATH_TTYSTAT, O_RDONLY | O_NONBLOCK, 0);
119 if (mouse.stat_fd == -1)
120 err(EXIT_FAILURE, "Cannot open `%s'", _PATH_TTYSTAT);
121
122 mouse.fd = -1;
123 mouse_open_device(&mouse, 0);
124
125 /* Open FIFO, if wanted */
126 if (mouse.fifo_name != NULL) {
127 mouse.fifo_fd = open(mouse.fifo_name, O_RDWR | O_NONBLOCK, 0);
128 if (mouse.fifo_fd == -1)
129 err(EXIT_FAILURE, "Cannot open `%s'", mouse.fifo_name);
130 }
131 }
132
133 /*
134 * Mouse event loop
135 */
136 static void
137 event_loop(void)
138 {
139 int res;
140 struct pollfd fds[2];
141 struct wscons_event event;
142
143 fds[0].fd = mouse.stat_fd;
144 fds[0].events = POLLIN;
145
146 for (;;) {
147 fds[1].fd = mouse.fd;
148 fds[1].events = POLLIN;
149 if (mouse.disabled)
150 res = poll(fds, 1, INFTIM);
151 else
152 res = poll(fds, 2, 300);
153
154 if (res < 0)
155 warn("failed to read from devices");
156
157 if (fds[0].revents & POLLIN) {
158 res = read(mouse.stat_fd, &event, sizeof(event));
159 if (res != sizeof(event))
160 warn("failed to read from mouse stat");
161 screen_event(&mouse, &event);
162 } else if (fds[1].revents & POLLIN) {
163 res = read(mouse.fd, &event, sizeof(event));
164 if (res != sizeof(event))
165 warn("failed to read from mouse");
166
167 if (mouse.fifo_fd >= 0) {
168 res = write(mouse.fifo_fd, &event,
169 sizeof(event));
170 if (res != sizeof(event))
171 warn("failed to write to fifo");
172 }
173
174 if (IS_MOTION_EVENT(event.type)) {
175 mouse_motion_event(&mouse, &event);
176 } else if (IS_BUTTON_EVENT(event.type)) {
177 mouse_button_event(&mouse, &event);
178 } else {
179 warn("unknown wsmouse event");
180 }
181 } else
182 if (!mouse.selecting) mouse_cursor_hide(&mouse);
183 }
184 }
185
186 /*
187 * Initializes mouse structure coordinate information. The mouse will
188 * be displayed at the center of the screen at start.
189 */
190 static void
191 mouse_init(void)
192 {
193 struct winsize ws;
194 struct wsdisplay_char ch;
195 int i;
196
197 /* Get terminal size */
198 if (ioctl(0, TIOCGWINSZ, &ws) < 0)
199 err(EXIT_FAILURE, "Cannot get terminal size");
200
201 /* Open current tty */
202 ioctl(mouse.stat_fd, WSDISPLAYIO_GETACTIVESCREEN, &i);
203 mouse.tty_fd = -1;
204 mouse_open_tty(&mouse, i);
205
206 /* Check if the kernel has character functions */
207 ch.row = ch.col = 0;
208 if (ioctl(mouse.tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) < 0)
209 err(EXIT_FAILURE, "ioctl(WSDISPLAYIO_GETWSCHAR) failed");
210
211 mouse.max_row = ws.ws_row - 1;
212 mouse.max_col = ws.ws_col - 1;
213 mouse.row = mouse.max_row / 2;
214 mouse.col = mouse.max_col / 2;
215 mouse.count_row = 0;
216 mouse.count_col = 0;
217 mouse.slowdown_x = 0;
218 mouse.slowdown_y = 2;
219 mouse.cursor = 0;
220 mouse.selecting = 0;
221 }
222
223 /*
224 * Hides the mouse cursor
225 */
226 void
227 mouse_cursor_hide(struct mouse *m)
228 {
229 if (!m->cursor) return;
230 char_invert(m, m->row, m->col);
231 m->cursor = 0;
232 }
233
234 /*
235 * Shows the mouse cursor
236 */
237 void
238 mouse_cursor_show(struct mouse *m)
239 {
240 if (m->cursor) return;
241 char_invert(m, m->row, m->col);
242 m->cursor = 1;
243 }
244
245 /*
246 * Opens the specified tty (we want it for ioctl's). If tty_fd in
247 * mouse structure is -1, no close is performed. Otherwise, the old
248 * file descriptor is closed and the new one opened.
249 */
250 void
251 mouse_open_tty(struct mouse *m, int ttyno)
252 {
253 char buf[20];
254
255 if (m->tty_fd >= 0) close(m->tty_fd);
256 if (ttyno == XConsole) {
257 m->disabled = 1;
258 (void)close(m->fd);
259 m->fd = -1;
260 return;
261 }
262 /* Open with delay. When returning from X, wsmoused keeps busy
263 some seconds so we have to wait. */
264 mouse_open_device(m, 5);
265 (void)snprintf(buf, sizeof(buf), _PATH_TTYPREFIX "%d", ttyno);
266 m->tty_fd = open(buf, O_RDONLY | O_NONBLOCK);
267 if (m->tty_fd < 0)
268 errx(EXIT_FAILURE, "Cannot open `%s'", buf);
269 m->disabled = 0;
270 }
271
272 /*
273 * Flip the foreground and background colors on char at coordinates
274 */
275 void
276 char_invert(struct mouse *m, size_t row, size_t col)
277 {
278 struct wsdisplay_char ch;
279 int t;
280
281 ch.row = row;
282 ch.col = col;
283
284 if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) {
285 warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
286 return;
287 }
288
289 t = ch.foreground;
290 ch.foreground = ch.background;
291 ch.background = t;
292
293 if (ioctl(m->tty_fd, WSDISPLAYIO_PUTWSCHAR, &ch) == -1)
294 warn("ioctl(WSDISPLAYIO_PUTWSCHAR) failed");
295 }
296
297 /*
298 * Main function
299 */
300 int
301 main(int argc, char **argv)
302 {
303 int opt;
304
305 setprogname(argv[0]);
306
307 /* Setup mouse default data */
308 memset(&mouse, 0, sizeof(struct mouse));
309 mouse.fifo_fd = -1;
310 mouse.device_name = _PATH_DEFAULT_MOUSE;
311 mouse.fifo_name = NULL;
312
313 /* Parse command line options */
314 while ((opt = getopt(argc, argv, "nf:d:X:")) != -1) {
315 switch (opt) {
316 case 'd': /* Mouse device name */
317 mouse.device_name = strdup(optarg);
318 break;
319 case 'f': /* FIFO file name */
320 mouse.fifo_name = strdup(optarg);
321 break;
322 case 'n': /* No daemon */
323 NoDaemon = 1;
324 break;
325 case 'X': /* X console number */
326 XConsole = atoi(optarg);
327 break;
328 default:
329 usage();
330 /* NOTREACHED */
331 }
332 }
333
334 open_files();
335 mouse_init();
336 mouse_sel_init();
337
338 /* Setup signal handlers */
339 (void)signal(SIGINT, signal_terminate);
340 (void)signal(SIGKILL, signal_terminate);
341 (void)signal(SIGQUIT, signal_terminate);
342 (void)signal(SIGTERM, signal_terminate);
343
344 if (!NoDaemon) daemon(0, 0);
345 event_loop();
346
347 return EXIT_SUCCESS;
348 }
349