1*a9fa58fdSderaadt /* $OpenBSD: wsmoused.c,v 1.28 2013/04/18 02:29:59 deraadt Exp $ */ 2673a75b7Saaron 3673a75b7Saaron /* 4673a75b7Saaron * Copyright (c) 2001 Jean-Baptiste Marchand, Julien Montagne and Jerome Verdon 5673a75b7Saaron * 6673a75b7Saaron * Copyright (c) 1998 by Kazutaka Yokota 7673a75b7Saaron * 8673a75b7Saaron * Copyright (c) 1995 Michael Smith 9673a75b7Saaron * 10673a75b7Saaron * Copyright (c) 1993 by David Dawes <dawes@xfree86.org> 11673a75b7Saaron * 12673a75b7Saaron * Copyright (c) 1990,91 by Thomas Roell, Dinkelscherben, Germany. 13673a75b7Saaron * 14673a75b7Saaron * All rights reserved. 15673a75b7Saaron * 16673a75b7Saaron * Most of this code was taken from the FreeBSD moused daemon, written by 17673a75b7Saaron * Michael Smith. The FreeBSD moused daemon already contained code from the 18673a75b7Saaron * Xfree Project, written by David Dawes and Thomas Roell and Kazutaka Yokota. 19673a75b7Saaron * 20673a75b7Saaron * Adaptation to OpenBSD was done by Jean-Baptiste Marchand, Julien Montagne 21673a75b7Saaron * and Jerome Verdon. 22673a75b7Saaron * 23673a75b7Saaron * Redistribution and use in source and binary forms, with or without 24673a75b7Saaron * modification, are permitted provided that the following conditions 25673a75b7Saaron * are met: 26673a75b7Saaron * 1. Redistributions of source code must retain the above copyright 27673a75b7Saaron * notice, this list of conditions and the following disclaimer. 28673a75b7Saaron * 2. Redistributions in binary form must reproduce the above copyright 29673a75b7Saaron * notice, this list of conditions and the following disclaimer in the 30673a75b7Saaron * documentation and/or other materials provided with the distribution. 31673a75b7Saaron * 3. All advertising materials mentioning features or use of this software 32673a75b7Saaron * must display the following acknowledgement: 33673a75b7Saaron * This product includes software developed by 34673a75b7Saaron * David Dawes, Jean-Baptiste Marchand, Julien Montagne, Thomas Roell, 35673a75b7Saaron * Michael Smith, Jerome Verdon and Kazutaka Yokota. 36673a75b7Saaron * 4. The name authors may not be used to endorse or promote products 37673a75b7Saaron * derived from this software without specific prior written permission. 38673a75b7Saaron * 39673a75b7Saaron * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 40673a75b7Saaron * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 41673a75b7Saaron * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 42673a75b7Saaron * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 43673a75b7Saaron * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44673a75b7Saaron * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 45673a75b7Saaron * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 46673a75b7Saaron * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 47673a75b7Saaron * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 48673a75b7Saaron * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 49673a75b7Saaron * 50673a75b7Saaron * 51673a75b7Saaron */ 52673a75b7Saaron 53673a75b7Saaron #include <sys/ioctl.h> 54f3afb3efSjbm #include <sys/stat.h> 55673a75b7Saaron #include <sys/types.h> 56673a75b7Saaron #include <sys/time.h> 57673a75b7Saaron #include <sys/tty.h> 58673a75b7Saaron #include <dev/wscons/wsconsio.h> 59673a75b7Saaron 60673a75b7Saaron #include <ctype.h> 61673a75b7Saaron #include <err.h> 62673a75b7Saaron #include <errno.h> 63673a75b7Saaron #include <fcntl.h> 64673a75b7Saaron #include <unistd.h> 65673a75b7Saaron #include <signal.h> 66673a75b7Saaron #include <poll.h> 67673a75b7Saaron #include <stdio.h> 68673a75b7Saaron #include <string.h> 69673a75b7Saaron #include <stdlib.h> 70673a75b7Saaron #include <syslog.h> 71673a75b7Saaron 72673a75b7Saaron #include "mouse_protocols.h" 73673a75b7Saaron #include "wsmoused.h" 74673a75b7Saaron 75be45c8afSmiod #define DEFAULT_TTY "/dev/ttyCcfg" 76be45c8afSmiod #define DEFAULT_PIDFILE "/var/run/wsmoused.pid" 77be45c8afSmiod 78673a75b7Saaron extern char *__progname; 79673a75b7Saaron extern char *mouse_names[]; 80673a75b7Saaron 81673a75b7Saaron int debug = 0; 82673a75b7Saaron int background = FALSE; 83be45c8afSmiod int nodaemon = FALSE; 84673a75b7Saaron int identify = FALSE; 85be45c8afSmiod char *pidfile = NULL; 86673a75b7Saaron 87673a75b7Saaron mouse_t mouse = { 88b729828dSotto .flags = 0, 89b729828dSotto .portname = NULL, 90be45c8afSmiod .ttyname = NULL, 91b729828dSotto .proto = P_UNKNOWN, 92b729828dSotto .baudrate = 1200, 93b729828dSotto .old_baudrate = 1200, 94b729828dSotto .rate = MOUSE_RATE_UNKNOWN, 95b729828dSotto .resolution = MOUSE_RES_UNKNOWN, 96b729828dSotto .zmap = 0, 97b729828dSotto .wmode = 0, 98b729828dSotto .mfd = -1, 99b729828dSotto .clickthreshold = 500, /* 0.5 sec */ 100673a75b7Saaron }; 101673a75b7Saaron 102673a75b7Saaron /* identify the type of a wsmouse supported mouse */ 103673a75b7Saaron void 104673a75b7Saaron wsmouse_identify(void) 105673a75b7Saaron { 106673a75b7Saaron unsigned int type; 107673a75b7Saaron 108b47703b0Smiod if (mouse.mfd != -1) { 109b47703b0Smiod if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) == -1) 110b47703b0Smiod err(1, "can't detect mouse type"); 111be45c8afSmiod 112673a75b7Saaron printf("wsmouse supported mouse: "); 113673a75b7Saaron switch (type) { 114673a75b7Saaron case WSMOUSE_TYPE_VSXXX: 115673a75b7Saaron printf("DEC serial\n"); 116673a75b7Saaron break; 117673a75b7Saaron case WSMOUSE_TYPE_PS2: 118673a75b7Saaron printf("PS/2 compatible\n"); 119673a75b7Saaron break; 120673a75b7Saaron case WSMOUSE_TYPE_USB: 121673a75b7Saaron printf("USB\n"); 122673a75b7Saaron break; 123673a75b7Saaron case WSMOUSE_TYPE_LMS: 124673a75b7Saaron printf("Logitech busmouse\n"); 125673a75b7Saaron break; 126673a75b7Saaron case WSMOUSE_TYPE_MMS: 127673a75b7Saaron printf("Microsoft InPort mouse\n"); 128673a75b7Saaron break; 129673a75b7Saaron case WSMOUSE_TYPE_TPANEL: 130673a75b7Saaron printf("Generic Touch Panel\n"); 131673a75b7Saaron break; 132673a75b7Saaron case WSMOUSE_TYPE_NEXT: 133673a75b7Saaron printf("NeXT\n"); 134673a75b7Saaron break; 135673a75b7Saaron case WSMOUSE_TYPE_ARCHIMEDES: 136673a75b7Saaron printf("Archimedes\n"); 137673a75b7Saaron break; 138be45c8afSmiod case WSMOUSE_TYPE_ADB: 139be45c8afSmiod printf("ADB\n"); 140be45c8afSmiod break; 141be45c8afSmiod case WSMOUSE_TYPE_HIL: 142be45c8afSmiod printf("HP-HIL\n"); 143be45c8afSmiod break; 144be45c8afSmiod case WSMOUSE_TYPE_LUNA: 145be45c8afSmiod printf("Omron Luna\n"); 146be45c8afSmiod break; 147be45c8afSmiod case WSMOUSE_TYPE_DOMAIN: 148be45c8afSmiod printf("Apollo Domain\n"); 149be45c8afSmiod break; 150be45c8afSmiod case WSMOUSE_TYPE_SUN: 151be45c8afSmiod printf("Sun\n"); 152be45c8afSmiod break; 153673a75b7Saaron default: 154673a75b7Saaron printf("Unknown\n"); 155673a75b7Saaron break; 156673a75b7Saaron } 157b47703b0Smiod } else 15846347781Smpech warnx("unable to open %s", mouse.portname); 159673a75b7Saaron } 160673a75b7Saaron 161673a75b7Saaron /* wsmouse_init : init a wsmouse compatible mouse */ 162673a75b7Saaron void 163673a75b7Saaron wsmouse_init(void) 164673a75b7Saaron { 165673a75b7Saaron unsigned int res = WSMOUSE_RES_MIN; 166673a75b7Saaron unsigned int rate = WSMOUSE_RATE_DEFAULT; 167673a75b7Saaron 168673a75b7Saaron ioctl(mouse.mfd, WSMOUSEIO_SRES, &res); 169673a75b7Saaron ioctl(mouse.mfd, WSMOUSEIO_SRATE, &rate); 170673a75b7Saaron } 171673a75b7Saaron 172673a75b7Saaron /* 173673a75b7Saaron * Buttons remapping 174673a75b7Saaron */ 175673a75b7Saaron 176673a75b7Saaron /* physical to logical button mapping */ 177673a75b7Saaron static int p2l[MOUSE_MAXBUTTON] = { 178673a75b7Saaron MOUSE_BUTTON1, MOUSE_BUTTON2, MOUSE_BUTTON3, MOUSE_BUTTON4, 179673a75b7Saaron MOUSE_BUTTON5, MOUSE_BUTTON6, MOUSE_BUTTON7, MOUSE_BUTTON8, 180673a75b7Saaron }; 181673a75b7Saaron 182673a75b7Saaron static char * 183673a75b7Saaron skipspace(char *s) 184673a75b7Saaron { 185673a75b7Saaron while (isspace(*s)) 186673a75b7Saaron ++s; 187673a75b7Saaron return s; 188673a75b7Saaron } 189673a75b7Saaron 190673a75b7Saaron /* mouse_installmap : install a map between physical and logical buttons */ 191673a75b7Saaron static int 192673a75b7Saaron mouse_installmap(char *arg) 193673a75b7Saaron { 194673a75b7Saaron int pbutton; 195673a75b7Saaron int lbutton; 196673a75b7Saaron char *s; 197673a75b7Saaron 198673a75b7Saaron while (*arg) { 199673a75b7Saaron arg = skipspace(arg); 200673a75b7Saaron s = arg; 201673a75b7Saaron while (isdigit(*arg)) 202673a75b7Saaron ++arg; 203673a75b7Saaron arg = skipspace(arg); 204673a75b7Saaron if ((arg <= s) || (*arg != '=')) 205673a75b7Saaron return FALSE; 206673a75b7Saaron lbutton = atoi(s); 207673a75b7Saaron 208673a75b7Saaron arg = skipspace(++arg); 209673a75b7Saaron s = arg; 210673a75b7Saaron while (isdigit(*arg)) 211673a75b7Saaron ++arg; 212b47703b0Smiod if (arg <= s || (!isspace(*arg) && *arg != '\0')) 213673a75b7Saaron return FALSE; 214673a75b7Saaron pbutton = atoi(s); 215673a75b7Saaron 216b47703b0Smiod if (lbutton <= 0 || lbutton > MOUSE_MAXBUTTON) 217673a75b7Saaron return FALSE; 218b47703b0Smiod if (pbutton <= 0 || pbutton > MOUSE_MAXBUTTON) 219673a75b7Saaron return FALSE; 220673a75b7Saaron p2l[pbutton - 1] = lbutton - 1; 221673a75b7Saaron } 222673a75b7Saaron return TRUE; 223673a75b7Saaron } 224673a75b7Saaron 225673a75b7Saaron /* mouse_map : converts physical buttons to logical buttons */ 226673a75b7Saaron static void 227673a75b7Saaron mouse_map(struct wscons_event *orig, struct wscons_event *mapped) 228673a75b7Saaron { 229673a75b7Saaron mapped->type = orig->type; 230673a75b7Saaron mapped->value = p2l[orig->value]; 231673a75b7Saaron } 232673a75b7Saaron 233673a75b7Saaron /* terminate signals handler */ 234673a75b7Saaron static void 235673a75b7Saaron terminate(int sig) 236673a75b7Saaron { 237673a75b7Saaron struct wscons_event event; 238e02f5e47Sjbm unsigned int res; 239673a75b7Saaron 240673a75b7Saaron if (mouse.mfd != -1) { 241673a75b7Saaron event.type = WSCONS_EVENT_WSMOUSED_OFF; 242f3afb3efSjbm ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event); 243e02f5e47Sjbm res = WSMOUSE_RES_DEFAULT; 244e02f5e47Sjbm ioctl(mouse.mfd, WSMOUSEIO_SRES, &res); 245673a75b7Saaron close(mouse.mfd); 246673a75b7Saaron mouse.mfd = -1; 247673a75b7Saaron } 248be45c8afSmiod if (pidfile != NULL) 249673a75b7Saaron unlink(pidfile); 250ded45aa8Sderaadt _exit(0); 251673a75b7Saaron } 252673a75b7Saaron 253673a75b7Saaron /* buttons status (for multiple click detection) */ 254673a75b7Saaron static struct { 255673a75b7Saaron int count; /* 0: up, 1: single click, 2: double click,... */ 256673a75b7Saaron struct timeval tv; /* timestamp on the last `up' event */ 257673a75b7Saaron } buttonstate[MOUSE_MAXBUTTON]; 258673a75b7Saaron 259673a75b7Saaron /* 260673a75b7Saaron * handle button click 261673a75b7Saaron * Note that an ioctl is sent for each button 262673a75b7Saaron */ 263673a75b7Saaron static void 264673a75b7Saaron mouse_click(struct wscons_event *event) 265673a75b7Saaron { 266673a75b7Saaron struct timeval max_date; 267673a75b7Saaron struct timeval now; 268673a75b7Saaron struct timeval delay; 269673a75b7Saaron int i = event->value; /* button number */ 270673a75b7Saaron 271*a9fa58fdSderaadt gettimeofday(&now, NULL); 272673a75b7Saaron delay.tv_sec = mouse.clickthreshold / 1000; 273673a75b7Saaron delay.tv_usec = (mouse.clickthreshold % 1000) * 1000; 274673a75b7Saaron timersub(&now, &delay, &max_date); 275673a75b7Saaron 276673a75b7Saaron if (event->type == WSCONS_EVENT_MOUSE_DOWN) { 277673a75b7Saaron if (timercmp(&max_date, &buttonstate[i].tv, >)) { 2786c2da34cSokan timerclear(&buttonstate[i].tv); 279673a75b7Saaron buttonstate[i].count = 1; 280b47703b0Smiod } else { 281673a75b7Saaron buttonstate[i].count++; 282673a75b7Saaron } 283673a75b7Saaron } else { 284673a75b7Saaron /* button is up */ 285673a75b7Saaron buttonstate[i].tv.tv_sec = now.tv_sec; 286673a75b7Saaron buttonstate[i].tv.tv_usec = now.tv_usec; 287673a75b7Saaron } 288673a75b7Saaron 289673a75b7Saaron /* 290673a75b7Saaron * we use the time field of wscons_event structure to put the number 291673a75b7Saaron * of multiple clicks 292673a75b7Saaron */ 293673a75b7Saaron if (event->type == WSCONS_EVENT_MOUSE_DOWN) { 294673a75b7Saaron event->time.tv_sec = buttonstate[i].count; 295673a75b7Saaron event->time.tv_nsec = 0; 296b47703b0Smiod } else { 297673a75b7Saaron /* button is up */ 298673a75b7Saaron event->time.tv_sec = 0; 299673a75b7Saaron event->time.tv_nsec = 0; 300673a75b7Saaron } 301673a75b7Saaron ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event); 302673a75b7Saaron } 303673a75b7Saaron 304673a75b7Saaron /* workaround for cursor speed on serial mice */ 305673a75b7Saaron static void 306673a75b7Saaron normalize_event(struct wscons_event *event) 307673a75b7Saaron { 308673a75b7Saaron int dx, dy; 309673a75b7Saaron int two_power = 1; 310673a75b7Saaron 311673a75b7Saaron /* 2: normal speed, 3: slower cursor, 1: faster cursor */ 312673a75b7Saaron #define NORMALIZE_DIVISOR 3 313673a75b7Saaron 314b47703b0Smiod switch (event->type) { 315b47703b0Smiod case WSCONS_EVENT_MOUSE_DELTA_X: 316673a75b7Saaron dx = abs(event->value); 317673a75b7Saaron while (dx > 2) { 318673a75b7Saaron two_power++; 319673a75b7Saaron dx = dx / 2; 320673a75b7Saaron } 321673a75b7Saaron event->value = event->value / (NORMALIZE_DIVISOR * two_power); 322b47703b0Smiod break; 323b47703b0Smiod case WSCONS_EVENT_MOUSE_DELTA_Y: 324673a75b7Saaron two_power = 1; 325673a75b7Saaron dy = abs(event->value); 326673a75b7Saaron while (dy > 2) { 327673a75b7Saaron two_power++; 328673a75b7Saaron dy = dy / 2; 329673a75b7Saaron } 330673a75b7Saaron event->value = event->value / (NORMALIZE_DIVISOR * two_power); 331b47703b0Smiod break; 332673a75b7Saaron } 333673a75b7Saaron } 334673a75b7Saaron 335673a75b7Saaron /* send a wscons_event to the kernel */ 336f3afb3efSjbm static int 337673a75b7Saaron treat_event(struct wscons_event *event) 338673a75b7Saaron { 339673a75b7Saaron struct wscons_event mapped_event; 340673a75b7Saaron 341673a75b7Saaron if (IS_MOTION_EVENT(event->type)) { 342673a75b7Saaron ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event); 343f3afb3efSjbm return 1; 3445015c596Sjacekm } else if (IS_BUTTON_EVENT(event->type) && 3455015c596Sjacekm (uint)event->value < MOUSE_MAXBUTTON) { 346673a75b7Saaron mouse_map(event, &mapped_event); 347673a75b7Saaron mouse_click(&mapped_event); 348f3afb3efSjbm return 1; 349673a75b7Saaron } 350f3afb3efSjbm if (event->type == WSCONS_EVENT_WSMOUSED_CLOSE) 351f3afb3efSjbm /* we have to close mouse fd */ 352f3afb3efSjbm return 0; 353f3afb3efSjbm return 1; 354673a75b7Saaron } 355673a75b7Saaron 356673a75b7Saaron /* split a full mouse event into multiples wscons events */ 357673a75b7Saaron static void 358673a75b7Saaron split_event(mousestatus_t *act) 359673a75b7Saaron { 360673a75b7Saaron struct wscons_event event; 361673a75b7Saaron int button, i, mask; 362673a75b7Saaron 363673a75b7Saaron if (act->dx != 0) { 364673a75b7Saaron event.type = WSCONS_EVENT_MOUSE_DELTA_X; 365673a75b7Saaron event.value = act->dx; 366673a75b7Saaron normalize_event(&event); 367673a75b7Saaron treat_event(&event); 368673a75b7Saaron } 369673a75b7Saaron if (act->dy != 0) { 370673a75b7Saaron event.type = WSCONS_EVENT_MOUSE_DELTA_Y; 371673a75b7Saaron event.value = 0 - act->dy; 372673a75b7Saaron normalize_event(&event); 373673a75b7Saaron treat_event(&event); 374673a75b7Saaron } 375673a75b7Saaron if (act->dz != 0) { 376673a75b7Saaron event.type = WSCONS_EVENT_MOUSE_DELTA_Z; 377673a75b7Saaron event.value = act->dz; 378673a75b7Saaron treat_event(&event); 379673a75b7Saaron } 3802fc38322Smiod if (act->dw != 0) { 3812fc38322Smiod event.type = WSCONS_EVENT_MOUSE_DELTA_W; 3822fc38322Smiod event.value = act->dw; 3832fc38322Smiod treat_event(&event); 3842fc38322Smiod } 385673a75b7Saaron 386673a75b7Saaron /* buttons state */ 387673a75b7Saaron mask = act->flags & MOUSE_BUTTONS; 388673a75b7Saaron if (mask == 0) 389673a75b7Saaron /* no button modified */ 390673a75b7Saaron return; 391673a75b7Saaron 392673a75b7Saaron button = MOUSE_BUTTON1DOWN; 393673a75b7Saaron for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); i++) { 394673a75b7Saaron if (mask & 1) { 395b47703b0Smiod event.type = (act->button & button) ? 396b47703b0Smiod WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP; 397673a75b7Saaron event.value = i; 398673a75b7Saaron treat_event(&event); 399673a75b7Saaron } 400673a75b7Saaron button <<= 1; 401673a75b7Saaron mask >>= 1; 402673a75b7Saaron } 403673a75b7Saaron } 404673a75b7Saaron 405673a75b7Saaron /* main function */ 406673a75b7Saaron static void 407673a75b7Saaron wsmoused(void) 408673a75b7Saaron { 409673a75b7Saaron mousestatus_t action; 410673a75b7Saaron struct wscons_event event; /* original wscons_event */ 411673a75b7Saaron struct pollfd pfd[1]; 412673a75b7Saaron int res; 413673a75b7Saaron u_char b; 414f3afb3efSjbm struct stat mdev_stat; 415673a75b7Saaron 416673a75b7Saaron /* initialization */ 417673a75b7Saaron 418673a75b7Saaron event.type = WSCONS_EVENT_WSMOUSED_ON; 419be45c8afSmiod if (mouse.proto == P_WSCONS) { 420f3afb3efSjbm /* get major and minor of mouse device */ 421f3afb3efSjbm res = stat(mouse.portname, &mdev_stat); 422f3afb3efSjbm if (res != -1) 423f3afb3efSjbm event.value = mdev_stat.st_rdev; 424f3afb3efSjbm else 425f3afb3efSjbm event.value = 0; 426be45c8afSmiod } else { 427be45c8afSmiod /* X11 won't start when using wsmoused(8) with a serial mouse */ 428f3afb3efSjbm event.value = 0; 429be45c8afSmiod } 430f3afb3efSjbm 431be45c8afSmiod /* notify kernel the start of wsmoused */ 432673a75b7Saaron res = ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, &event); 433673a75b7Saaron if (res != 0) { 434673a75b7Saaron /* the display driver has no getchar() method */ 435f3afb3efSjbm logerr(1, "this display driver has no support for wsmoused(8)"); 436673a75b7Saaron } 437673a75b7Saaron 438673a75b7Saaron bzero(&action, sizeof(action)); 439673a75b7Saaron bzero(&event, sizeof(event)); 440673a75b7Saaron bzero(&buttonstate, sizeof(buttonstate)); 441673a75b7Saaron 442673a75b7Saaron pfd[0].fd = mouse.mfd; 443673a75b7Saaron pfd[0].events = POLLIN; 444673a75b7Saaron 445673a75b7Saaron /* process mouse data */ 446673a75b7Saaron for (;;) { 44729f11fc7Sfgsch if (poll(pfd, 1, INFTIM) <= 0) 448673a75b7Saaron logwarn("failed to read from mouse"); 449be45c8afSmiod 450be45c8afSmiod if (mouse.proto == P_WSCONS) { 451673a75b7Saaron /* wsmouse supported mouse */ 452673a75b7Saaron read(mouse.mfd, &event, sizeof(event)); 453f3afb3efSjbm res = treat_event(&event); 454f3afb3efSjbm if (!res) { 455be45c8afSmiod /* 456be45c8afSmiod * close mouse device and sleep until 457be45c8afSmiod * the X server releases it 458be45c8afSmiod */ 459f3afb3efSjbm 460f3afb3efSjbm struct wscons_event sleeping; 4610329dfd0Smiod unsigned int tries; 462f3afb3efSjbm 463f3afb3efSjbm /* restore mouse resolution to default value */ 464f3afb3efSjbm res = WSMOUSE_RES_DEFAULT; 465f3afb3efSjbm ioctl(mouse.mfd, WSMOUSEIO_SRES, &res); 466f3afb3efSjbm 467f3afb3efSjbm close(mouse.mfd); 468f3afb3efSjbm mouse.mfd = -1; 469f3afb3efSjbm 470f3afb3efSjbm /* sleep until X server releases mouse device */ 471f3afb3efSjbm sleeping.type = WSCONS_EVENT_WSMOUSED_SLEEP; 472f3afb3efSjbm sleeping.value = 0; 473f3afb3efSjbm ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, 474f3afb3efSjbm &sleeping); 475f3afb3efSjbm 4760329dfd0Smiod /* 4770329dfd0Smiod * Since the X server could still be running 4780329dfd0Smiod * (e.g. when switching from the graphics 4790329dfd0Smiod * screen to a virtual text console), it might 4800329dfd0Smiod * not have freed the device yet. 4810329dfd0Smiod * 4820329dfd0Smiod * Try to open the device until it succeeds. 4830329dfd0Smiod */ 4840329dfd0Smiod tries = 0; 4850329dfd0Smiod for (;;) { 486f3afb3efSjbm if ((mouse.mfd = open(mouse.portname, 4870329dfd0Smiod O_RDONLY | O_NONBLOCK, 0)) != -1) 4880329dfd0Smiod break; 4890329dfd0Smiod 4900329dfd0Smiod if (tries < 10) { 4910329dfd0Smiod tries++; 4920329dfd0Smiod sleep(1); 4930329dfd0Smiod } else { 494be45c8afSmiod logwarn("unable to open %s, " 4950329dfd0Smiod "will retry in 10 seconds", 496f3afb3efSjbm mouse.portname); 4970329dfd0Smiod sleep(10); 4980329dfd0Smiod } 4990329dfd0Smiod } 5000329dfd0Smiod 501be45c8afSmiod wsmouse_init(); 502f3afb3efSjbm } 503b47703b0Smiod } else { 504f3afb3efSjbm /* serial mouse (not supported by wsmouse) */ 505673a75b7Saaron res = read(mouse.mfd, &b, 1); 506673a75b7Saaron 507673a75b7Saaron /* if we have a full mouse event */ 508673a75b7Saaron if (mouse_protocol(b, &action)) 509673a75b7Saaron /* split it as multiple wscons_event */ 510673a75b7Saaron split_event(&action); 511673a75b7Saaron } 512673a75b7Saaron } 513673a75b7Saaron } 514673a75b7Saaron 515673a75b7Saaron 516673a75b7Saaron static void 517673a75b7Saaron usage(void) 518673a75b7Saaron { 519be45c8afSmiod fprintf(stderr, "usage: %s [-2dfi] [-C thresh] [-D device] [-I file]" 520be45c8afSmiod " [-M N=M]\n\t[-p device] [-t type]\n", __progname); 521673a75b7Saaron exit(1); 522673a75b7Saaron } 523673a75b7Saaron 524673a75b7Saaron int 525673a75b7Saaron main(int argc, char **argv) 526673a75b7Saaron { 527be45c8afSmiod FILE *fp; 528be45c8afSmiod unsigned int type; 529673a75b7Saaron int opt; 530673a75b7Saaron int i; 531673a75b7Saaron 532be45c8afSmiod #define GETOPT_STRING "2dfhip:t:C:D:I:M:" 533673a75b7Saaron while ((opt = (getopt(argc, argv, GETOPT_STRING))) != -1) { 534673a75b7Saaron switch (opt) { 535673a75b7Saaron case '2': 536673a75b7Saaron /* on two button mice, right button pastes */ 537673a75b7Saaron p2l[MOUSE_BUTTON3] = MOUSE_BUTTON2; 538673a75b7Saaron break; 539673a75b7Saaron case 'd': 540673a75b7Saaron ++debug; 541673a75b7Saaron break; 542673a75b7Saaron case 'f': 543673a75b7Saaron nodaemon = TRUE; 544673a75b7Saaron break; 545673a75b7Saaron case 'h': 546673a75b7Saaron usage(); 547673a75b7Saaron break; 548673a75b7Saaron case 'i': 549673a75b7Saaron identify = TRUE; 550be45c8afSmiod nodaemon = TRUE; 551673a75b7Saaron break; 552673a75b7Saaron case 'p': 553ca198d9fSderaadt if ((mouse.portname = strdup(optarg)) == NULL) 554ca198d9fSderaadt logerr(1, "out of memory"); 555673a75b7Saaron break; 556673a75b7Saaron case 't': 557673a75b7Saaron if (strcmp(optarg, "auto") == 0) { 558673a75b7Saaron mouse.proto = P_UNKNOWN; 559673a75b7Saaron mouse.flags &= ~NoPnP; 560673a75b7Saaron break; 561673a75b7Saaron } 562b47703b0Smiod for (i = 0; mouse_names[i] != NULL; i++) 563673a75b7Saaron if (strcmp(optarg,mouse_names[i]) == 0) { 564673a75b7Saaron mouse.proto = i; 565673a75b7Saaron mouse.flags |= NoPnP; 566673a75b7Saaron break; 567673a75b7Saaron } 568b47703b0Smiod if (mouse_names[i] != NULL) 569673a75b7Saaron break; 57046347781Smpech warnx("no such mouse protocol `%s'", optarg); 571673a75b7Saaron usage(); 572673a75b7Saaron break; 573673a75b7Saaron case 'C': 574673a75b7Saaron #define MAX_CLICKTHRESHOLD 2000 /* max delay for double click */ 575673a75b7Saaron mouse.clickthreshold = atoi(optarg); 576b47703b0Smiod if (mouse.clickthreshold < 0 || 577b47703b0Smiod mouse.clickthreshold > MAX_CLICKTHRESHOLD) { 57846347781Smpech warnx("invalid threshold `%s': max value is %d", 579b47703b0Smiod optarg, MAX_CLICKTHRESHOLD); 580673a75b7Saaron usage(); 581673a75b7Saaron } 582673a75b7Saaron break; 583be45c8afSmiod case 'D': 584be45c8afSmiod if ((mouse.ttyname = strdup(optarg)) == NULL) 585be45c8afSmiod logerr(1, "out of memory"); 586be45c8afSmiod break; 587673a75b7Saaron case 'I': 588673a75b7Saaron pidfile = optarg; 589673a75b7Saaron break; 590673a75b7Saaron case 'M': 591673a75b7Saaron if (!mouse_installmap(optarg)) { 59246347781Smpech warnx("invalid mapping `%s'", optarg); 593673a75b7Saaron usage(); 594673a75b7Saaron } 595673a75b7Saaron break; 596673a75b7Saaron default: 597673a75b7Saaron usage(); 598673a75b7Saaron } 599673a75b7Saaron } 600be45c8afSmiod 601be45c8afSmiod /* 602be45c8afSmiod * Use defaults if unspecified 603be45c8afSmiod */ 604b47703b0Smiod if (mouse.portname == NULL) 605b47703b0Smiod mouse.portname = WSMOUSE_DEV; 606be45c8afSmiod if (mouse.ttyname == NULL) 607be45c8afSmiod mouse.ttyname = DEFAULT_TTY; 608673a75b7Saaron 609be45c8afSmiod if (identify == FALSE) { 610be45c8afSmiod if ((mouse.cfd = open(mouse.ttyname, O_RDWR, 0)) == -1) 611be45c8afSmiod logerr(1, "cannot open %s", mouse.ttyname); 612be45c8afSmiod } 613be45c8afSmiod 614673a75b7Saaron if ((mouse.mfd = open(mouse.portname, 615673a75b7Saaron O_RDONLY | O_NONBLOCK, 0)) == -1) 616673a75b7Saaron logerr(1, "unable to open %s", mouse.portname); 617be45c8afSmiod 618be45c8afSmiod /* 619be45c8afSmiod * Find out whether the mouse device is a wsmouse device 620be45c8afSmiod * or a serial device. 621be45c8afSmiod */ 622be45c8afSmiod if (ioctl(mouse.mfd, WSMOUSEIO_GTYPE, &type) != -1) 623be45c8afSmiod mouse.proto = P_WSCONS; 624be45c8afSmiod else { 625673a75b7Saaron if (mouse_identify() == P_UNKNOWN) { 626673a75b7Saaron close(mouse.mfd); 627b47703b0Smiod logerr(1, "cannot determine mouse type on %s", 628b47703b0Smiod mouse.portname); 629673a75b7Saaron } 630673a75b7Saaron } 631673a75b7Saaron 632673a75b7Saaron if (identify == TRUE) { 633be45c8afSmiod if (mouse.proto == P_WSCONS) 634673a75b7Saaron wsmouse_identify(); 635673a75b7Saaron else 636673a75b7Saaron printf("serial mouse: %s type\n", 637be45c8afSmiod mouse_name(mouse.proto)); 638b47703b0Smiod exit(0); 639673a75b7Saaron } 640673a75b7Saaron 641be45c8afSmiod signal(SIGINT, terminate); 642be45c8afSmiod signal(SIGQUIT, terminate); 643be45c8afSmiod signal(SIGTERM, terminate); 644be45c8afSmiod 645be45c8afSmiod if (mouse.proto == P_WSCONS) 646be45c8afSmiod wsmouse_init(); 647be45c8afSmiod else 648673a75b7Saaron mouse_init(); 649be45c8afSmiod 650fca39c33Sdjm if (!nodaemon) { 651fca39c33Sdjm openlog(__progname, LOG_PID, LOG_DAEMON); 652fca39c33Sdjm if (daemon(0, 0)) { 653fca39c33Sdjm logerr(1, "failed to become a daemon"); 654fca39c33Sdjm } else { 655fca39c33Sdjm background = TRUE; 656fca39c33Sdjm if (pidfile != NULL) { 657fca39c33Sdjm fp = fopen(pidfile, "w"); 658fca39c33Sdjm if (fp != NULL) { 659fca39c33Sdjm fprintf(fp, "%ld\n", (long)getpid()); 660fca39c33Sdjm fclose(fp); 661fca39c33Sdjm } 662fca39c33Sdjm } 663fca39c33Sdjm } 664fca39c33Sdjm } 665fca39c33Sdjm 666673a75b7Saaron wsmoused(); 667be45c8afSmiod exit(0); 668673a75b7Saaron } 669