Qurp Updates

2015/4/18 22:36

Over on github Kramble Has very kindly added sound to qurp and improved the woeful input support which I had added some time ago. Kramble also sent me links to these two articles which cover the basics on linux input quite nicely. I've subsequently made a few changes myself to the input so that connecting and disconnecting the device is handled properly in game:

Initially I tried just dropping and reinitialising the file descriptors to the dev/input/event* files every second or so, which is dumb but works. Unfortunatley the calls to close() were just too slow and caused noticable juddering in the game. So after a bit of googling it seems that the udev (http://www.signal11.us/oss/udev/) library can be used to monitor the plugging in and removal of devices.

Most of what follows is a slight refactoring of the udev sample code which can be found here http://www.signal11.us/oss/udev/udev_example.c . There is also the Reference Manual The full source code can be found in the platform.pi.c file on the git repo.

Setup

// setup udev stuff so we can enumerate and monitor 
// devices being connected or disconnected.
// Create the udev object
pState->udev = udev_new();
if (!pState->udev) {
    printf("Can't create udev! \n");
    exit(1);
}

// Initial enumeration and registering of input devices.
// we only do this for "event" inputs
{
    struct udev_enumerate *enumerate;
    struct udev_list_entry *devices, *dev_list_entry;
    struct udev_device *dev;

    enumerate = udev_enumerate_new(pState->udev);
    udev_enumerate_add_match_subsystem(enumerate, "input"); //only enumerate for kb/mouse etc
    udev_enumerate_scan_devices(enumerate);
    devices = udev_enumerate_get_list_entry(enumerate);

    udev_list_entry_foreach(dev_list_entry, devices) {
        const char *path, *devnode;
        path = udev_list_entry_get_name(dev_list_entry);
        dev = udev_device_new_from_syspath(pState->udev, path);
        devnode = udev_device_get_devnode(dev);

        if (devnode != NULL){
            // This opens the "dev/input/event*" file
            // which we can monitor as before for input events
            RegisterInputDeviceByName(pState, devnode);				
        }
    }
    udev_enumerate_unref(enumerate);
}

// Create a monitor for input devices being added or removed
// The monitor is polled on the Tick Function
pState->p_device_monitor = udev_monitor_new_from_netlink(pState->udev, "udev");
// Again only monitor for kb/mouse events  etc
udev_monitor_filter_add_match_subsystem_devtype(pState->p_device_monitor, "input", NULL);
udev_monitor_enable_receiving(pState->p_device_monitor);
// I only get this once after initialising it, i think this is correct
pState->device_monitor_fd = udev_monitor_get_fd(pState->p_device_monitor);	

Monitoring For changes

Called once per frame during the plaform Tick update.

// Poll the udev stuff for device changes
{
    struct udev_device *dev;
    fd_set fds;
    struct timeval tv;
    int ret;

    FD_ZERO(&fds);
    FD_SET(pState->device_monitor_fd, &fds);
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    ret = select(pState->device_monitor_fd + 1, &fds, NULL, NULL, &tv);

    // Check if our file descriptor has received data.
    if (ret > 0 && FD_ISSET(pState->device_monitor_fd, &fds)) {
        // Make the call to receive the device.
        // select() ensured that this will not block.
        dev = udev_monitor_receive_device(pState->p_device_monitor);
        if (dev) {
            const char *p_devnode = udev_device_get_devnode(dev);
            if (p_devnode){
                if (!strcmp(udev_device_get_action(dev), "remove"))
                {
                    // call close on this device's file handle
                    ResetInputDeviceByName(pState, p_devnode);
                }
                if (!strcmp(udev_device_get_action(dev), "add"))
                {
                    // open a connection to this device
                    RegisterInputDeviceByName(pState, p_devnode);
                }
            }
        }
        else 
        {
            printf("No Device from receive_device(). An error occured.\n");
        }
    }
}

Cleanup

udev_unref(pState->udev);	
udev_monitor_unref(pState->p_device_monitor);