Click here to Skip to main content
15,894,825 members
Articles / Desktop Programming / MFC

Support Multiple Mouse Inputs in a Dialog Box

Rate me:
Please Sign up or sign in to vote.
4.00/5 (4 votes)
2 Apr 2009CPOL3 min read 30.9K   849   10  
Multiple mouse support in an MFC dialog project using VC++ 6.0!
/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <linux/input.h>  /* 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)
{
    if (index < available_mice)
        return(mice[index].name);
    return(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_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 ... */

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
United States United States
Always trying something new. blog tries to keep up (nwpodcast.blogspot.com)

Comments and Discussions