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