wsmoused.c revision 1.12 1 /* $NetBSD: wsmoused.c,v 1.12 2003/08/06 22:11:50 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 M. Merino Vidal.
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 __COPYRIGHT("@(#) Copyright (c) 2002, 2003\n"
36 "The NetBSD Foundation, Inc. All rights reserved.\n");
37 __RCSID("$NetBSD: wsmoused.c,v 1.12 2003/08/06 22:11:50 jmmv Exp $");
38 #endif /* not lint */
39
40 #include <sys/ioctl.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/tty.h>
44 #include <dev/wscons/wsconsio.h>
45
46 #include <err.h>
47 #include <fcntl.h>
48 #include <poll.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <util.h>
55
56 #include "pathnames.h"
57 #include "wsmoused.h"
58
59 /* --------------------------------------------------------------------- */
60
61 /*
62 * Global variables.
63 */
64
65 static struct mouse Mouse;
66 static char *Pid_File = NULL;
67 static int X_Console = -1;
68
69 #ifdef WSMOUSED_SELECTION_MODE
70 extern struct mode_bootstrap Action_Mode;
71 #endif
72 #ifdef WSMOUSED_SELECTION_MODE
73 extern struct mode_bootstrap Selection_Mode;
74 #endif
75
76 #define MAX_MODES 2
77 static struct mode_bootstrap *Modes[MAX_MODES];
78 static struct mode_bootstrap *Avail_Modes[] = {
79 #ifdef WSMOUSED_ACTION_MODE
80 &Action_Mode,
81 #endif
82 #ifdef WSMOUSED_SELECTION_MODE
83 &Selection_Mode,
84 #endif
85 };
86
87 /* --------------------------------------------------------------------- */
88
89 /*
90 * Prototypes for functions private to this module.
91 */
92
93 static void usage(void);
94 static void open_device(unsigned int);
95 static void init_mouse(void);
96 static void event_loop(void);
97 static void generic_wscons_event(struct wscons_event);
98 static int attach_mode(const char *);
99 static void attach_modes(char *);
100 static void detach_mode(const char *);
101 static void detach_modes(void);
102 static void signal_terminate(int);
103 int main(int, char **);
104
105 /* --------------------------------------------------------------------- */
106
107 /* Shows program usage information and exits. */
108 static void
109 usage(void)
110 {
111
112 (void)fprintf(stderr,
113 "Usage: %s [-d device] [-f config_file] [-m modes] [-n]\n",
114 getprogname());
115 exit(EXIT_FAILURE);
116 }
117
118 /* --------------------------------------------------------------------- */
119
120 /* Initializes mouse information. Basically, it opens required files
121 * for the daemon to work. */
122 static void
123 init_mouse(void)
124 {
125
126 Mouse.m_devfd = -1;
127 open_device(0);
128
129 /* Open FIFO, if wanted */
130 Mouse.m_fifofd = -1;
131 if (Mouse.m_fifoname != NULL) {
132 Mouse.m_fifofd = open(Mouse.m_fifoname,
133 O_RDWR | O_NONBLOCK, 0);
134 if (Mouse.m_fifofd == -1)
135 err(EXIT_FAILURE, "cannot open %s", Mouse.m_fifoname);
136 }
137 }
138
139 /* --------------------------------------------------------------------- */
140
141 /* Opens the mouse device (if not already opened). The argument `secs'
142 * specifies how much seconds the function will wait before trying to
143 * open the device; this is used when returning from the X console. */
144 static void
145 open_device(unsigned int secs)
146 {
147
148 if (Mouse.m_devfd != -1)
149 return;
150
151 sleep(secs);
152
153 /* Open mouse file descriptor */
154 Mouse.m_devfd = open(Mouse.m_devname, O_RDONLY | O_NONBLOCK, 0);
155 if (Mouse.m_devfd == -1)
156 err(EXIT_FAILURE, "cannot open %s", Mouse.m_devname);
157 }
158
159 /* --------------------------------------------------------------------- */
160
161 /* Main program event loop. This function polls the wscons status
162 * device and the mouse device; whenever an event is received, the
163 * appropiate callback is fired for all attached modes. If the polls
164 * times out (which only appens when the mouse is disabled), another
165 * callback is launched. */
166 static void
167 event_loop(void)
168 {
169 int i, res;
170 struct pollfd fds[2];
171 struct wscons_event event;
172
173 fds[0].fd = Mouse.m_statfd;
174 fds[0].events = POLLIN;
175
176 for (;;) {
177 fds[1].fd = Mouse.m_devfd;
178 fds[1].events = POLLIN;
179 if (Mouse.m_disabled)
180 res = poll(fds, 1, INFTIM);
181 else
182 res = poll(fds, 2, 300);
183
184 if (res < 0)
185 warn("failed to read from devices");
186
187 if (fds[0].revents & POLLIN) {
188 res = read(Mouse.m_statfd, &event, sizeof(event));
189 if (res != sizeof(event))
190 warn("failed to read from mouse stat");
191
192 generic_wscons_event(event);
193
194 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
195 if (Modes[i]->mb_wscons_event != NULL)
196 Modes[i]->mb_wscons_event(event);
197
198 } else if (fds[1].revents & POLLIN) {
199 res = read(Mouse.m_devfd, &event, sizeof(event));
200 if (res != sizeof(event))
201 warn("failed to read from mouse");
202
203 if (Mouse.m_fifofd >= 0) {
204 res = write(Mouse.m_fifofd, &event,
205 sizeof(event));
206 if (res != sizeof(event))
207 warn("failed to write to fifo");
208 }
209
210 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
211 if (Modes[i]->mb_wsmouse_event != NULL)
212 Modes[i]->mb_wsmouse_event(event);
213 } else {
214 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
215 if (Modes[i]->mb_poll_timeout != NULL)
216 Modes[i]->mb_poll_timeout();
217 }
218 }
219 }
220
221 /* --------------------------------------------------------------------- */
222
223 /* This function parses generic wscons status events. Actually, it
224 * handles the screen switch event to enable or disable the mouse,
225 * depending if we are entering or leaving the X console. */
226 static void
227 generic_wscons_event(struct wscons_event evt)
228 {
229
230 switch (evt.type) {
231 case WSCONS_EVENT_SCREEN_SWITCH:
232 if (evt.value == X_Console) {
233 Mouse.m_disabled = 1;
234 (void)close(Mouse.m_devfd);
235 Mouse.m_devfd = -1;
236 } else {
237 if (Mouse.m_disabled) {
238 open_device(5);
239 Mouse.m_disabled = 0;
240 } else {
241 (void)close(Mouse.m_devfd);
242 Mouse.m_devfd = -1;
243 open_device(0);
244 }
245 }
246 break;
247 }
248 }
249
250 /* --------------------------------------------------------------------- */
251
252 /* Attaches a mode to the list of active modes, based on its name.
253 * Returns 1 on success or 0 if the mode fails to initialize or there is
254 * any other problem. */
255 static int
256 attach_mode(const char *name)
257 {
258 int i, pos;
259 struct mode_bootstrap *mb;
260
261 for (i = 0, pos = -1; i < MAX_MODES; i++)
262 if (Modes[i] == NULL) {
263 pos = i;
264 break;
265 }
266 if (pos == -1) {
267 warnx("modes table full; cannot register `%s'", name);
268 return 0;
269 }
270
271 for (i = 0; i < MAX_MODES; i++) {
272 mb = Avail_Modes[i];
273 if (mb != NULL && strcmp(name, mb->mb_name) == 0) {
274 int res;
275
276 res = mb->mb_startup(&Mouse);
277 if (res == 0) {
278 warnx("startup failed for `%s' mode",
279 mb->mb_name);
280 return 0;
281 } else {
282 Modes[pos] = mb;
283 return 1;
284 }
285 }
286 }
287
288 warnx("unknown mode `%s' (see the `modes' directive)", name);
289 return 0;
290 }
291
292 /* --------------------------------------------------------------------- */
293
294 /* Attaches all modes given in the whitespace separated string `list'.
295 * A fatal error is produced if no active modes can be attached. */
296 static void
297 attach_modes(char *list)
298 {
299 char *last, *p;
300 int count;
301
302 /* Attach all requested modes */
303 (void)memset(&Modes, 0, sizeof(struct mode_bootstrap) * MAX_MODES);
304 for (count = 0, (p = strtok_r(list, " ", &last)); p;
305 (p = strtok_r(NULL, " ", &last))) {
306 if (attach_mode(p))
307 count++;
308 }
309
310 if (count == 0)
311 errx(EXIT_FAILURE, "no active modes found; exiting...");
312 }
313
314 /* --------------------------------------------------------------------- */
315
316 /* Detaches a mode from the active modes list based on its name. */
317 static void
318 detach_mode(const char *name)
319 {
320 int i;
321 struct mode_bootstrap *mb;
322
323 for (i = 0; i < MAX_MODES; i++) {
324 mb = Modes[i];
325 if (mb != NULL && strcmp(name, mb->mb_name) == 0) {
326 int res;
327
328 res = mb->mb_cleanup();
329 if (res == 0) {
330 warnx("cleanup failed for `%s' mode",
331 mb->mb_name);
332 return;
333 } else {
334 Modes[i] = NULL;
335 return;
336 }
337 }
338 }
339
340 warnx("unknown mode `%s' (see the `modes' directive)", name);
341 }
342
343 /* --------------------------------------------------------------------- */
344
345 /* Detaches all active modes. */
346 static void
347 detach_modes(void)
348 {
349 int i;
350
351 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
352 detach_mode(Modes[i]->mb_name);
353 }
354
355 /* --------------------------------------------------------------------- */
356
357 /* Signal handler for close signals. The program can only be exited
358 * through this function. */
359 /* ARGSUSED */
360 static void
361 signal_terminate(int sig)
362 {
363
364 detach_modes();
365 config_free();
366 exit(EXIT_SUCCESS);
367 }
368
369 /* --------------------------------------------------------------------- */
370
371 /* Main program. Parses command line options, reads the configuration
372 * file, initializes the mouse and associated files and launches the main
373 * event loop. */
374 int
375 main(int argc, char **argv)
376 {
377 char *conffile, *modelist, *tstat;
378 int needconf, nodaemon, opt;
379 struct block *conf;
380
381 setprogname(argv[0]);
382
383 (void)memset(&Mouse, 0, sizeof(struct mouse));
384 conffile = _PATH_CONF;
385 modelist = NULL;
386 needconf = 0;
387 nodaemon = -1;
388
389 /* Parse command line options */
390 while ((opt = getopt(argc, argv, "d:f:m:n")) != -1) {
391 switch (opt) {
392 case 'd': /* Mouse device name */
393 Mouse.m_devname = optarg;
394 break;
395 case 'f': /* Configuration file name */
396 needconf = 1;
397 conffile = optarg;
398 break;
399 case 'm': /* List of modes to activate */
400 modelist = optarg;
401 break;
402 case 'n': /* No daemon */
403 nodaemon = 1;
404 break;
405 default:
406 usage();
407 /* NOTREACHED */
408 }
409 }
410
411 /* Read the configuration file and get some basic properties */
412 config_read(conffile, needconf);
413 conf = config_get_mode("Global");
414 if (nodaemon == -1)
415 nodaemon = block_get_propval_int(conf, "nodaemon", 0);
416 X_Console = block_get_propval_int(conf, "xconsole", -1);
417
418 /* Open wsdisplay status device */
419 tstat = block_get_propval(conf, "ttystat", _PATH_TTYSTAT);
420 Mouse.m_statfd = open(tstat, O_RDONLY | O_NONBLOCK, 0);
421 if (Mouse.m_statfd == -1)
422 err(EXIT_FAILURE, "cannot open %s", tstat);
423
424 /* Initialize mouse information and attach modes */
425 if (Mouse.m_devname == NULL)
426 Mouse.m_devname = block_get_propval(conf, "device",
427 _PATH_DEFAULT_MOUSE);
428 init_mouse();
429 if (modelist != NULL)
430 attach_modes(modelist);
431 else
432 attach_modes(block_get_propval(conf, "modes", "selection"));
433
434 /* Setup signal handlers */
435 (void)signal(SIGINT, signal_terminate);
436 (void)signal(SIGKILL, signal_terminate);
437 (void)signal(SIGQUIT, signal_terminate);
438 (void)signal(SIGTERM, signal_terminate);
439
440 if (!nodaemon) {
441 /* Become a daemon */
442 if (daemon(0, 0) == -1)
443 err(EXIT_FAILURE, "failed to become a daemon");
444
445 /* Create the pidfile, if wanted */
446 Pid_File = block_get_propval(conf, "pidfile", NULL);
447 if (pidfile(Pid_File) == -1)
448 warn("pidfile %s", Pid_File);
449 }
450
451 event_loop();
452
453 /* NOTREACHED */
454 return EXIT_SUCCESS;
455 }
456