diff options
author | Andrey Nazarov <skuller@skuller.net> | 2011-03-28 17:19:31 +0400 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2011-03-28 17:19:31 +0400 |
commit | bacbae4d9d377e5262bc4167a82b46b1e47d889e (patch) | |
tree | 43c15eccbf7ad7637fbb0a84f6e103676d14fe37 | |
parent | d0823646b69482ac60afd7984341e1b257b54ff9 (diff) |
Auto add mouse devices with libudev.
Support reading from several evdev file descriptors at once. When
‘in_device’ cvar is empty, attempt to do an udev scan and add any
available mouse devices automatically.
-rw-r--r-- | build/q2pro.mk | 4 | ||||
-rwxr-xr-x | configure | 8 | ||||
-rw-r--r-- | src/in_evdev.c | 175 |
3 files changed, 158 insertions, 29 deletions
diff --git a/build/q2pro.mk b/build/q2pro.mk index 670e4e6..9d89f01 100644 --- a/build/q2pro.mk +++ b/build/q2pro.mk @@ -126,6 +126,10 @@ ifdef USE_DINPUT SRCFILES+=in_evdev.c endif +ifdef USE_UDEV +LDFLAGS+=-ludev +endif + ifdef USE_LIRC SRCFILES+=in_lirc.c LDFLAGS+=-llirc_client @@ -83,6 +83,7 @@ use_snddma="yes" use_openal="no" use_dsound="no" use_dinput="no" +use_udev="no" use_lirc="no" use_zlib="yes" use_curl="no" @@ -296,6 +297,7 @@ Linux) use_dl="yes" have_endian_h="yes" [ "$use_icmp" = "no" ] || use_icmp="yes" +[ "$use_dinput" = "yes" ] && use_udev="yes" ;; esac @@ -437,6 +439,7 @@ else use_openal="no" use_dsound="no" use_dinput="no" + use_udev="no" use_lirc="no" use_curl="no" use_tga="no" @@ -633,6 +636,11 @@ if [ "$use_dinput" = "yes" ]; then echo "#define USE_DINPUT 1" >> $config_h fi +if [ "$use_udev" = "yes" ]; then + echo "USE_UDEV=yes" >> $config_mk + echo "#define USE_UDEV 1" >> $config_h +fi + if [ "$use_lirc" = "yes" ]; then echo "USE_LIRC=yes" >> $config_mk echo "#define USE_LIRC 1" >> $config_h diff --git a/src/in_evdev.c b/src/in_evdev.c index 9c1766e..5d38503 100644 --- a/src/in_evdev.c +++ b/src/in_evdev.c @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "in_public.h" #include "cl_public.h" #include "io_sleep.h" +#include "q_list.h" #include <sys/types.h> #include <sys/stat.h> @@ -35,38 +36,57 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include <SDL.h> #endif -static cvar_t *in_device; +#if USE_UDEV +#include <libudev.h> +#endif + +#define FOR_EACH_EVDEV( dev ) \ + LIST_FOR_EACH( evdev_t, dev, &evdev.devices, entry ) +#define FOR_EACH_EVDEV_SAFE( dev, next ) \ + LIST_FOR_EACH_SAFE( evdev_t, dev, next, &evdev.devices, entry ) + +typedef struct { + list_t entry; + int fd; + ioentry_t *io; +} evdev_t; static struct { qboolean initialized; grab_t grabbed; - int fd; + list_t devices; int dx, dy; - ioentry_t *io; } evdev; #define MAX_EVENTS 64 #define EVENT_SIZE sizeof( struct input_event ) -static void ShutdownMouse( void ); +static void evdev_remove( evdev_t *dev ) { + IO_Remove( dev->fd ); + close( dev->fd ); + List_Remove( &dev->entry ); + Z_Free( dev ); +} -static void GetMouseEvents( void ) { +static void evdev_read( evdev_t *dev ) { struct input_event ev[MAX_EVENTS]; ssize_t bytes; size_t i, count; unsigned button, time; - if( !evdev.initialized || !evdev.grabbed /*|| !evdev.io->canread*/ ) { +#if 0 + if( !dev->io->canread ) { return; } +#endif - bytes = read( evdev.fd, ev, EVENT_SIZE * MAX_EVENTS ); + bytes = read( dev->fd, ev, EVENT_SIZE * MAX_EVENTS ); if( bytes == -1 ) { if( errno == EAGAIN || errno == EINTR ) { return; } Com_EPrintf( "Couldn't read event: %s\n", strerror( errno ) ); - ShutdownMouse(); + evdev_remove( dev ); return; } @@ -120,10 +140,23 @@ static void GetMouseEvents( void ) { } } +static void GetMouseEvents( void ) { + evdev_t *dev, *next; + + if( !evdev.initialized || !evdev.grabbed ) { + return; + } + + FOR_EACH_EVDEV_SAFE( dev, next ) { + evdev_read( dev ); + } +} + static qboolean GetMouseMotion( int *dx, int *dy ) { if( !evdev.initialized || !evdev.grabbed ) { return qfalse; } + *dx = evdev.dx; *dy = evdev.dy; evdev.dx = 0; @@ -132,12 +165,15 @@ static qboolean GetMouseMotion( int *dx, int *dy ) { } static void ShutdownMouse( void ) { + evdev_t *dev, *next; + if( !evdev.initialized ) { return; } - IO_Remove( evdev.fd ); - close( evdev.fd ); + FOR_EACH_EVDEV_SAFE( dev, next ) { + evdev_remove( dev ); + } #if USE_SDL SDL_ShowCursor( SDL_ENABLE ); @@ -148,30 +184,109 @@ static void ShutdownMouse( void ) { memset( &evdev, 0, sizeof( evdev ) ); } -static qboolean InitMouse( void ) { - in_device = Cvar_Get( "in_device", "", 0 ); - if( !in_device->string[0] ) { - Com_WPrintf( "No evdev input device specified.\n" ); - return qfalse; +static evdev_t *evdev_add( const char *path ) { + evdev_t *dev; + int fd; + + fd = open( path, O_RDONLY ); + if( fd == -1 ) { + Com_EPrintf( "Couldn't open %s: %s\n", path, strerror( errno ) ); + return NULL; } + + Com_DPrintf( "Adding device %s\n", path ); + + fcntl( fd, F_SETFL, fcntl( fd, F_GETFL, 0 ) | FNDELAY ); + + dev = Z_Malloc( sizeof( *dev ) ); + dev->fd = fd; + dev->io = IO_Add( fd ); + List_Append( &evdev.devices, &dev->entry ); + + return dev; +} + +#if USE_UDEV +static void auto_add_devices( void ) { + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *list, *entry; + struct udev_device *dev; + const char *path; + + udev = udev_new(); + if( !udev ) { + Com_EPrintf( "Couldn't create udev handle\n" ); + return; + } + + enumerate = udev_enumerate_new( udev ); + udev_enumerate_add_match_subsystem( enumerate, "input" ); + udev_enumerate_add_match_sysname( enumerate, "event[0-9]*" ); + udev_enumerate_add_match_property( enumerate, "ID_INPUT_MOUSE", "1" ); + udev_enumerate_scan_devices( enumerate ); + list = udev_enumerate_get_list_entry( enumerate ); + udev_list_entry_foreach( entry, list ) { + path = udev_list_entry_get_name( entry ); + dev = udev_device_new_from_syspath( udev, path ); + if( dev ) { + path = udev_device_get_devnode( dev ); + if( path ) { + evdev_add( path ); + } + udev_device_unref( dev ); + } + } + + udev_enumerate_unref( enumerate ); + udev_unref( udev ); +} +#endif + +static qboolean InitMouse( void ) { + cvar_t *var; + + List_Init( &evdev.devices ); + + var = Cvar_Get( "in_device", "", 0 ); - evdev.fd = open( in_device->string, O_RDONLY ); - if( evdev.fd == -1 ) { - Com_EPrintf( "Couldn't open %s: %s\n", in_device->string, - strerror( errno ) ); + if( var->string[0] ) { + // add user specified device + if( !evdev_add( var->string ) ) { + return qfalse; + } + } else { +#if USE_UDEV + // find devices automatically + auto_add_devices(); + + if( LIST_EMPTY( &evdev.devices ) ) { + Com_EPrintf( + "Automatic libudev scan was unable to find any " + "usable mouse device. You may need to set the " + "'in_device' variable manually before direct " + "mouse input can be used.\n" ); + return qfalse; + } +#else + Com_EPrintf( + "No input device specified, and libudev support " + "is not compiled in. Please set the 'in_device' variable " + "manually before direct mouse input can be used.\n" ); return qfalse; +#endif } - - fcntl( evdev.fd, F_SETFL, fcntl( evdev.fd, F_GETFL, 0 ) | FNDELAY ); - evdev.io = IO_Add( evdev.fd ); - Com_Printf( "Evdev interface initialized.\n" ); + Com_Printf( "Evdev mouse initialized.\n" ); evdev.initialized = qtrue; return qtrue; } static void GrabMouse( grab_t grab ) { + struct input_event ev; + evdev_t *dev; + if( !evdev.initialized ) { return; } @@ -188,7 +303,6 @@ static void GrabMouse( grab_t grab ) { SDL_WM_SetCaption( "[" PRODUCT "]", APPLICATION ); SDL_ShowCursor( SDL_DISABLE ); #endif - evdev.io->wantread = qtrue; } else { #if USE_SDL if( evdev.grabbed == IN_GRAB ) { @@ -201,14 +315,17 @@ static void GrabMouse( grab_t grab ) { SDL_ShowCursor( SDL_ENABLE ); } #endif - evdev.io->wantread = !!grab; } - // pump pending events - if( grab ) { - struct input_event ev; + FOR_EACH_EVDEV( dev ) { + if( !grab ) { + dev->io->wantread = qfalse; + continue; + } - while( read( evdev.fd, &ev, EVENT_SIZE ) == EVENT_SIZE ) + // pump pending events + dev->io->wantread = qtrue; + while( read( dev->fd, &ev, EVENT_SIZE ) == EVENT_SIZE ) ; } |