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