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 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);
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");
}
}
}
udev_unref(pState->udev);
udev_monitor_unref(pState->p_device_monitor);