@ -21,49 +21,61 @@
# include <linux/input.h>
# include <linux/input.h>
# include <sys/inotify.h>
# include <sys/inotify.h>
/*
/*
* Abstract away evdev and inotify .
* Abstract away evdev and inotify .
*
*
* We could make this cleaner by creating a single file descriptor via epoll
* We could make this cleaner by creating a single file descriptor via epoll
* but this would break FreeBSD compatibility without a dedicated kqueue
* but this would break FreeBSD compatibility without a dedicated kqueue
* implementation . A thread based approach was also considered ,
* implementation . A thread based approach was also considered , but
* but inter - thread communication adds too much overhead ( ~ 100u s ) .
* inter - thread communication adds too much overhead ( ~ 100u s ) .
*
*
* Overview :
* Overview :
*
*
* A ' devmon ' is a file descriptor which can be created with devmon_create ( )
* A ' devmon ' is a file descriptor which can be created with devmon_create ( )
* and subsequently monitored for new devices read with devmon_read_device ( ) .
* and subsequently monitored for new devices read with devmon_read_device ( ) .
*
*
* A ' device ' always corresponds to a keyboard from which activity can be
* A ' device ' always corresponds to a keyboard or mouse from which activity can
* monitored with device - > fd and events subsequently read using
* be monitored with device - > fd and events subsequently read using
* device_read_event ( ) .
* device_read_event ( ) .
*
*
* If the event returned by device_read_event ( ) is of type DEV_REMOVED then the
* If the event returned by device_read_event ( ) is of type DEV_REMOVED then the
* corresponding device should be considered invalid by the caller .
* corresponding device should be considered invalid by the caller .
*/
*/
static int is_keyboard ( int fd )
/*
* returns 1 if keyboard , 2 if mouse , and 0 if neither .
*/
static int device_type ( int fd )
{
{
uint32_t keymask ;
uint32_t mask [ BTN_LEFT / 32 + 1 ] = { 0 } ;
if ( ioctl ( fd , EVIOCGBIT ( EV_KEY , sizeof keymask ) , & key mask) < 0 ) {
if ( ioctl ( fd , EVIOCGBIT ( EV_KEY , ( BTN_LEFT / 32 + 1 ) * 4 ) , mask ) < 0 ) {
perror ( " ioctl " ) ;
perror ( " ioctl " ) ;
return 0 ;
return 0 ;
}
}
/* The first 31 bits correspond to [KEY_ESC-KEY_S] */
/* The first 31 bits correspond to [KEY_ESC-KEY_S] */
return keymask = = 0xFFFFFFFE ;
if ( mask [ 0 ] = = 0xFFFFFFFE )
return 1 ;
else if ( mask [ BTN_LEFT / 32 ] > > BTN_LEFT % 32 )
return 2 ;
else
return 0 ;
}
}
static int device_init ( const char * path , struct device * dev )
static int device_init ( const char * path , struct device * dev )
{
{
int fd ;
int fd ;
int type ;
if ( ( fd = open ( path , O_RDONLY | O_NONBLOCK , 0600 ) ) < 0 ) {
if ( ( fd = open ( path , O_RDONLY | O_NONBLOCK , 0600 ) ) < 0 ) {
fprintf ( stderr , " failed to open %s \n " , path ) ;
fprintf ( stderr , " failed to open %s \n " , path ) ;
return - 1 ;
return - 1 ;
}
}
if ( is_keyboard ( fd ) ) {
type = device_type ( fd ) ;
if ( type ) {
struct input_id info ;
struct input_id info ;
if ( ioctl ( fd , EVIOCGNAME ( sizeof ( dev - > name ) ) , dev - > name ) = = - 1 ) {
if ( ioctl ( fd , EVIOCGNAME ( sizeof ( dev - > name ) ) , dev - > name ) = = - 1 ) {
@ -80,6 +92,7 @@ static int device_init(const char *path, struct device *dev)
dev - > path [ sizeof ( dev - > path ) - 1 ] = 0 ;
dev - > path [ sizeof ( dev - > path ) - 1 ] = 0 ;
dev - > fd = fd ;
dev - > fd = fd ;
dev - > is_keyboard = type = = 1 ;
dev - > vendor_id = info . vendor ;
dev - > vendor_id = info . vendor ;
dev - > product_id = info . product ;
dev - > product_id = info . product ;
@ -143,7 +156,7 @@ int device_scan(struct device devices[MAX_DEVICES])
return ndevs ;
return ndevs ;
}
}
/*
/*
* NOTE : Only a single devmon fd may exist . Implementing this properly
* NOTE : Only a single devmon fd may exist . Implementing this properly
* would involve bookkeeping state for each fd , but this is
* would involve bookkeeping state for each fd , but this is
* unnecessary for our use .
* unnecessary for our use .
@ -169,7 +182,7 @@ int devmon_create()
return fd ;
return fd ;
}
}
/*
/*
* A non blocking call which returns any devices available on the provided
* A non blocking call which returns any devices available on the provided
* monitor descriptor . The return value should not be freed or modified by the calling
* monitor descriptor . The return value should not be freed or modified by the calling
* code . Returns NULL if no devices are available .
* code . Returns NULL if no devices are available .
@ -215,7 +228,7 @@ int device_ungrab(struct device *dev)
return ioctl ( dev - > fd , EVIOCGRAB , ( void * ) 0 ) ;
return ioctl ( dev - > fd , EVIOCGRAB , ( void * ) 0 ) ;
}
}
/*
/*
* Read a device event from the given device or return
* Read a device event from the given device or return
* NULL if none are available ( may happen in the
* NULL if none are available ( may happen in the
* case of a spurious wakeup ) .
* case of a spurious wakeup ) .
@ -254,6 +267,8 @@ struct device_event *device_read_event(struct device *dev)
ev . code = KEYD_MOUSE_2 ;
ev . code = KEYD_MOUSE_2 ;
else if ( ev . code = = KEY_FN )
else if ( ev . code = = KEY_FN )
ev . code = KEYD_FN ;
ev . code = KEYD_FN ;
else if ( ev . code > = BTN_DIGI & & ev . code < = BTN_TOOL_QUADTAP )
;
else {
else {
fprintf ( stderr , " ERROR: unsupported evdev code: 0x%x \n " , ev . code ) ;
fprintf ( stderr , " ERROR: unsupported evdev code: 0x%x \n " , ev . code ) ;
return NULL ;
return NULL ;