wsmoused.c revision 1.9 1 /* $NetBSD: wsmoused.c,v 1.9 2003/03/04 22:31:15 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.9 2003/03/04 22:31:15 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 #include <util.h>
55
56 #include "pathnames.h"
57 #include "wsmoused.h"
58
59 #define IS_MOTION_EVENT(type) (((type) == WSCONS_EVENT_MOUSE_DELTA_X) || \
60 ((type) == WSCONS_EVENT_MOUSE_DELTA_Y) || \
61 ((type) == WSCONS_EVENT_MOUSE_DELTA_Z))
62 #define IS_BUTTON_EVENT(type) (((type) == WSCONS_EVENT_MOUSE_UP) || \
63 ((type) == WSCONS_EVENT_MOUSE_DOWN))
64
65 static struct mouse mouse;
66 static char *PidFile = NULL;
67 int XConsole = -1;
68
69 /*
70 * Show program usage information
71 */
72 static void
73 usage(void)
74 {
75 (void)fprintf(stderr,
76 "Usage: %s [-d device] [-f config_file] [-n]\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 config_free();
89 exit(EXIT_SUCCESS);
90 }
91
92 /*
93 * Open the mouse device (i.e. /dev/wsmouse). The argument secs
94 * specifies the number of seconds we must wait before opening the
95 * device. This is used when returning from X. See mouse_open_tty().
96 */
97 static void
98 mouse_open_device(struct mouse *m, int secs)
99 {
100 if (m->fd != -1) return;
101
102 sleep(secs);
103
104 /* Open mouse file descriptor */
105 m->fd = open(m->device_name,
106 O_RDONLY | O_NONBLOCK, 0);
107 if (m->fd == -1) {
108 err(EXIT_FAILURE, "cannot open %s", 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", mouse.tstat_name);
122
123 mouse.fd = -1;
124 mouse_open_device(&mouse, 0);
125
126 /* Open FIFO, if wanted */
127 mouse.fifo_fd = -1;
128 if (mouse.fifo_name != NULL) {
129 mouse.fifo_fd = open(mouse.fifo_name, O_RDWR | O_NONBLOCK, 0);
130 if (mouse.fifo_fd == -1)
131 err(EXIT_FAILURE, "cannot open %s", mouse.fifo_name);
132 }
133 }
134
135 /*
136 * Mouse event loop
137 */
138 static void
139 event_loop(void)
140 {
141 int res;
142 struct pollfd fds[2];
143 struct wscons_event event;
144
145 fds[0].fd = mouse.stat_fd;
146 fds[0].events = POLLIN;
147
148 for (;;) {
149 fds[1].fd = mouse.fd;
150 fds[1].events = POLLIN;
151 if (mouse.disabled)
152 res = poll(fds, 1, INFTIM);
153 else
154 res = poll(fds, 2, 300);
155
156 if (res < 0)
157 warn("failed to read from devices");
158
159 if (fds[0].revents & POLLIN) {
160 res = read(mouse.stat_fd, &event, sizeof(event));
161 if (res != sizeof(event))
162 warn("failed to read from mouse stat");
163 screen_event(&mouse, &event);
164 } else if (fds[1].revents & POLLIN) {
165 res = read(mouse.fd, &event, sizeof(event));
166 if (res != sizeof(event))
167 warn("failed to read from mouse");
168
169 if (mouse.fifo_fd >= 0) {
170 res = write(mouse.fifo_fd, &event,
171 sizeof(event));
172 if (res != sizeof(event))
173 warn("failed to write to fifo");
174 }
175
176 if (IS_MOTION_EVENT(event.type)) {
177 mouse_motion_event(&mouse, &event);
178 } else if (IS_BUTTON_EVENT(event.type)) {
179 mouse_button_event(&mouse, &event);
180 } else {
181 warn("unknown wsmouse event");
182 }
183 } else
184 if (!mouse.selecting) mouse_cursor_hide(&mouse);
185 }
186 }
187
188 /*
189 * Initializes mouse structure coordinate information. The mouse will
190 * be displayed at the center of the screen at start.
191 */
192 static void
193 mouse_init(void)
194 {
195 struct winsize ws;
196 struct wsdisplay_char ch;
197 int i;
198
199 /* Get terminal size */
200 if (ioctl(0, TIOCGWINSZ, &ws) < 0)
201 err(EXIT_FAILURE, "cannot get terminal size");
202
203 /* Open current tty */
204 ioctl(mouse.stat_fd, WSDISPLAYIO_GETACTIVESCREEN, &i);
205 mouse.tty_fd = -1;
206 mouse_open_tty(&mouse, i);
207
208 /* Check if the kernel has character functions */
209 ch.row = ch.col = 0;
210 if (ioctl(mouse.tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) < 0)
211 err(EXIT_FAILURE, "ioctl(WSDISPLAYIO_GETWSCHAR) failed");
212
213 mouse.max_row = ws.ws_row - 1;
214 mouse.max_col = ws.ws_col - 1;
215 mouse.row = mouse.max_row / 2;
216 mouse.col = mouse.max_col / 2;
217 mouse.count_row = 0;
218 mouse.count_col = 0;
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, nodaemon = -1;
304 int needconf = 0;
305 char *conffile;
306 struct block *mode;
307
308 setprogname(argv[0]);
309
310 memset(&mouse, 0, sizeof(struct mouse));
311 conffile = _PATH_CONF;
312
313 /* Parse command line options */
314 while ((opt = getopt(argc, argv, "d:f:n")) != -1) {
315 switch (opt) {
316 case 'd': /* Mouse device name */
317 mouse.device_name = optarg;
318 break;
319 case 'f': /* Configuration file name */
320 needconf = 1;
321 conffile = optarg;
322 break;
323 case 'n': /* No daemon */
324 nodaemon = 1;
325 break;
326 default:
327 usage();
328 /* NOTREACHED */
329 }
330 }
331
332 /* Read the configuration file and get our mode configuration */
333 config_read(conffile, needconf);
334 mode = config_get_mode("sel");
335
336 /* Set values according to the configuration file */
337 if (mouse.device_name == NULL)
338 mouse.device_name = block_get_propval(mode, "device",
339 _PATH_DEFAULT_MOUSE);
340
341 if (nodaemon == -1)
342 nodaemon = block_get_propval_int(mode, "nodaemon", 0);
343
344 mouse.slowdown_x = block_get_propval_int(mode, "slowdown_x", 0);
345 mouse.slowdown_y = block_get_propval_int(mode, "slowdown_y", 3);
346 mouse.tstat_name = block_get_propval(mode, "ttystat", _PATH_TTYSTAT);
347 XConsole = block_get_propval_int(mode, "xconsole", -1);
348
349 if (block_get_propval_int(mode, "lefthanded", 0)) {
350 mouse.but_select = 2;
351 mouse.but_paste = 0;
352 } else {
353 mouse.but_select = 0;
354 mouse.but_paste = 2;
355 }
356
357 open_files();
358 mouse_init();
359 mouse_sel_init();
360
361 /* Setup signal handlers */
362 (void)signal(SIGINT, signal_terminate);
363 (void)signal(SIGKILL, signal_terminate);
364 (void)signal(SIGQUIT, signal_terminate);
365 (void)signal(SIGTERM, signal_terminate);
366
367 if (!nodaemon) {
368 /* Become a daemon */
369 if (daemon(0, 0) == -1)
370 err(EXIT_FAILURE, "failed to become a daemon");
371
372 /* Create the pidfile, if wanted */
373 PidFile = block_get_propval(mode, "pidfile", NULL);
374 if (pidfile(PidFile) == -1)
375 warn("pidfile %s", PidFile);
376 }
377 event_loop();
378
379 /* NOTREACHED */
380 return EXIT_SUCCESS;
381 }
382