/* * Support for Linux evdevs...the /dev/input/event* devices. * * Please see the file LICENSE.txt in the source's root directory. * * This file written by Ryan C. Gordon. */ #include "manymouse.h" #ifdef __linux__ #include #include #include #include #include #include #include #include #include #include #include /* evdev interface... */ #define test_bit(array, bit) (array[bit/8] & (1<<(bit%8))) /* linux allows 32 evdev nodes currently. */ #define MAX_MICE 32 typedef struct { int fd; int min_x; int min_y; int max_x; int max_y; char name[64]; } MouseStruct; static MouseStruct mice[MAX_MICE]; static unsigned int available_mice = 0; static int poll_mouse(MouseStruct *mouse, ManyMouseEvent *outevent) { int unhandled = 1; while (unhandled) /* read until failure or valid event. */ { struct input_event event; int br = read(mouse->fd, &event, sizeof (event)); if (br == -1) { if (errno == EAGAIN) return 0; /* just no new data at the moment. */ /* mouse was unplugged? */ close(mouse->fd); /* stop reading from this mouse. */ mouse->fd = -1; outevent->type = MANYMOUSE_EVENT_DISCONNECT; return 1; } /* if */ if (br != sizeof (event)) return 0; /* oh well. */ unhandled = 0; /* will reset if necessary. */ outevent->value = event.value; if (event.type == EV_REL) { outevent->type = MANYMOUSE_EVENT_RELMOTION; if ((event.code == REL_X) || (event.code == REL_DIAL)) outevent->item = 0; else if (event.code == REL_Y) outevent->item = 1; else if (event.code == REL_WHEEL) { outevent->type = MANYMOUSE_EVENT_SCROLL; outevent->item = 0; } /* else if */ else if (event.code == REL_HWHEEL) { outevent->type = MANYMOUSE_EVENT_SCROLL; outevent->item = 1; } /* else if */ else { unhandled = 1; } /* else */ } /* if */ else if (event.type == EV_ABS) { outevent->type = MANYMOUSE_EVENT_ABSMOTION; if (event.code == ABS_X) { outevent->item = 0; outevent->minval = mouse->min_x; outevent->maxval = mouse->max_x; } /* if */ else if (event.code == ABS_Y) { outevent->item = 1; outevent->minval = mouse->min_y; outevent->maxval = mouse->max_y; } /* if */ else { unhandled = 1; } /* else */ } /* else if */ else if (event.type == EV_KEY) { outevent->type = MANYMOUSE_EVENT_BUTTON; if ((event.code >= BTN_LEFT) && (event.code <= BTN_BACK)) outevent->item = event.code - BTN_MOUSE; /* just in case some device uses this block of events instead... */ else if ((event.code >= BTN_MISC) && (event.code <= BTN_LEFT)) outevent->item = (event.code - BTN_MISC); else if (event.code == BTN_TOUCH) /* tablet... */ outevent->item = 0; else if (event.code == BTN_STYLUS) /* tablet... */ outevent->item = 1; else if (event.code == BTN_STYLUS2) /* tablet... */ outevent->item = 2; else { /*printf("unhandled mouse button: 0x%X\n", event.code);*/ unhandled = 1; } /* else */ } /* else if */ else { unhandled = 1; } /* else */ } /* while */ return 1; /* got a valid event */ } /* poll_mouse */ static int init_mouse(const char *fname, int fd) { MouseStruct *mouse = &mice[available_mice]; int has_absolutes = 0; int is_mouse = 0; unsigned char relcaps[(REL_MAX / 8) + 1]; unsigned char abscaps[(ABS_MAX / 8) + 1]; unsigned char keycaps[(KEY_MAX / 8) + 1]; memset(relcaps, '\0', sizeof (relcaps)); memset(abscaps, '\0', sizeof (abscaps)); memset(keycaps, '\0', sizeof (keycaps)); if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof (keycaps)), keycaps) == -1) return 0; /* gotta have some buttons! :) */ if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof (relcaps)), relcaps) != -1) { if ( (test_bit(relcaps, REL_X)) && (test_bit(relcaps, REL_Y)) ) { if (test_bit(keycaps, BTN_MOUSE)) is_mouse = 1; } /* if */ #if ALLOW_DIALS_TO_BE_MICE if (test_bit(relcaps, REL_DIAL)) is_mouse = 1; // griffin powermate? #endif } /* if */ if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof (abscaps)), abscaps) != -1) { if ( (test_bit(abscaps, ABS_X)) && (test_bit(abscaps, ABS_Y)) ) { /* might be a touchpad... */ if (test_bit(keycaps, BTN_TOUCH)) { is_mouse = 1; /* touchpad, touchscreen, or tablet. */ has_absolutes = 1; } /* if */ } /* if */ } /* if */ if (!is_mouse) return 0; mouse->min_x = mouse->min_y = mouse->max_x = mouse->max_y = 0; if (has_absolutes) { struct input_absinfo absinfo; if (ioctl(fd, EVIOCGABS(ABS_X), &absinfo) == -1) return 0; mouse->min_x = absinfo.minimum; mouse->max_x = absinfo.maximum; if (ioctl(fd, EVIOCGABS(ABS_Y), &absinfo) == -1) return 0; mouse->min_y = absinfo.minimum; mouse->max_y = absinfo.maximum; } /* if */ if (ioctl(fd, EVIOCGNAME(sizeof (mouse->name)), mouse->name) == -1) snprintf(mouse->name, sizeof (mouse->name), "Unknown device"); mouse->fd = fd; return 1; /* we're golden. */ } /* init_mouse */ /* Return a file descriptor if this is really a mouse, -1 otherwise. */ static int open_if_mouse(const char *fname) { struct stat statbuf; int fd; int devmajor, devminor; if (stat(fname, &statbuf) == -1) return 0; if (S_ISCHR(statbuf.st_mode) == 0) return 0; /* not a character device... */ /* evdev node ids are major 13, minor 64-96. Is this safe to check? */ devmajor = (statbuf.st_rdev & 0xFF00) >> 8; devminor = (statbuf.st_rdev & 0x00FF); if ( (devmajor != 13) || (devminor < 64) || (devminor > 96) ) return 0; /* not an evdev. */ if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) == -1) return 0; if (init_mouse(fname, fd)) return 1; close(fd); return 0; } /* open_if_mouse */ static int linux_evdev_init(void) { DIR *dirp; struct dirent *dent; int i; for (i = 0; i < MAX_MICE; i++) mice[i].fd = -1; dirp = opendir("/dev/input"); if (!dirp) return -1; while ((dent = readdir(dirp)) != NULL) { char fname[128]; snprintf(fname, sizeof (fname), "/dev/input/%s", dent->d_name); if (open_if_mouse(fname)) available_mice++; } /* while */ closedir(dirp); return available_mice; } /* linux_evdev_init */ static void linux_evdev_quit(void) { while (available_mice) { int fd = mice[available_mice--].fd; if (fd != -1) close(fd); } /* while */ } /* linux_evdev_quit */ static const char *linux_evdev_name(unsigned int index) { return (index < available_mice) ? mice[index].name : NULL; } /* linux_evdev_name */ static int linux_evdev_poll(ManyMouseEvent *event) { /* * (i) is static so we iterate through all mice round-robin. This * prevents a chatty mouse from dominating the queue. */ static unsigned int i = 0; if (i >= available_mice) i = 0; /* handle reset condition. */ if (event != NULL) { while (i < available_mice) { MouseStruct *mouse = &mice[i]; if (mouse->fd != -1) { if (poll_mouse(mouse, event)) { event->device = i; return 1; } /* if */ } /* if */ i++; } /* while */ } /* if */ return 0; /* no new events */ } /* linux_evdev_poll */ static const ManyMouseDriver ManyMouseDriver_interface = { "Linux /dev/input/event* interface", linux_evdev_init, linux_evdev_quit, linux_evdev_name, linux_evdev_poll }; const ManyMouseDriver *ManyMouseDriver_evdev = &ManyMouseDriver_interface; #else const ManyMouseDriver *ManyMouseDriver_evdev = 0; #endif /* ifdef Linux blocker */ /* end of linux_evdev.c ... */