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