Programación del joystick

Introducción

Una aplicación escrita en el lenguaje de programación C puede acceder al joystick a través de la API de GNU/Linux. En el fichero <linux/joystick.h> están disponibles todas las constantes y tipos de datos específicos para poder leer el estado de esta clase de dispositivos.

En el fichero joystick-api.txt se encuentra la documentación de la API del joystick. Ahora vamos a describir las operaciones más importantes, pero de todas formas conviene leerse dicho documento. En la sección de Descargas hay disponible un programa de ejemplo en el que puede verse todos los pasos necesarios para manejar el joystick.

Para que un programa controle los efectos de FF del joystick debe importar el fichero <linux/input.h>, aunque no lo hemos probado porque el driver que disponemos para el Logitech Wingman Force 3D no soporta esta característica. El fichero ff.txt contiene toda la documentación relativa a este tema.

Abrir el fichero de dispositivo

Lo primero que hay que hacer para acceder al joystick es abrir su fichero de dispositivo mediante la función open (el tratamiento de errores es muy importante, tanto en esta función como en cualquiera de las otras):


int error;
int fd;

error = open ("/dev/js0", O_RDONLY);

if (error == -1) {
	// error handling
} else {
	fd = error;
}

Esta función devuelve o bien un número negativo si ha ocurrido un error (normalmente indicará que no existe dicho dispositivo) o bien el descriptor de fichero del dispositivo. Este descriptor de fichero será el utilizado para realizar cualquier operación con él, operaciones que serán bloqueantes, no retornando la función hasta que no ocurra un evento (o se produzca un error).

Lo normal es que deseemos que las operaciones sean bloqueantes, pero en determinados sistemas de tiempo real al hacer cualquier operación sobre un dispositivo la función tiene que retornar inmediatamente, como por ejemplo en los ejecutivos cíclicos. En estos casos habrá que abrir el dispositivo en modo no bloqueante. Véase el fichero joystick-api.txt para toda la información relativa a cómo realizar operaciones no bloqueantes con el joystick.

Comprobar las características del joystick

Como con cualquier otro dispositivo, puede leerse las propiedades del joystick usando la función ioctl. Por ejemplo, el siguiente código permite comprobar la versión del driver del joystick (para no oscurecer el código no se ha incluido el tratamiento de errores):


typedef byte  unsigned char;
         int  driver_version;
         int  error;

error = ioctl (fd, JSIOCGVERSION, &driver_version);

printf("Driver Version: %d.%d.%d\n",
		version >> 16,
		(version >> 8) & 0xff,
		version & 0xff);

Nótese que el número de versión del driver está codificada, debiendo hacer varias transformaciones al valor devuelto para obtenerla. Con la función ioctl también puede comprobarse de forma análoga el número de ejes del dispositivo o su número de botones, entre otros parámetros. Véase el fichero joystick-api.txt para más detalles.

Lectura del joystick

Para leer el estado del joystick se utiliza la función read, la cual sólo retornará o bien cuando se produzca un evento en el dispositivo (si se ha abierto en modo no bloqueante) o bien cuando ocurra un error:


struct js_event  js_ev;
            int  error;

error = read(js_fd, &js_ev, sizeof(struct js_event));
if (error == -1) {
	perror("close");
	exit(EXIT_FAILURE);
} /* if */

Si no se ha producido ningún error la función read devuelve en la variable de tipo struct js_event el estado del joystick. Esta estructura tiene los siguientes campos:


struct js_event {
	__u32  time;      /* event timestamp in milliseconds */
	__s16  value;     /* value */
	 __u8  type;      /* event type */
	 __u8  number;    /* axis/button number */
};

El campo time indica en qué instante de tiempo se produjo el evento. El campo type permite distinguir si el evento ha consistido en un cambio en el estado o bien de un eje o bien de un botón, indicándose en number que ha sido el eje o el botón n-ésimo (tanto los ejes como los botones se numeran a partir de cero). Por último, en el campo value se indica el valor que ha tomado: entre -32767 a +32767 para un eje (tanto si es digital como analógico), y 0 ó 1 para un botón. Parece que el driver no admite botones analógicos, pero no se ha podido comprobar porque todos los botones del Logitech Wingman Force 3D son digitales.

El campo type lleva codificado entre sus bits el tipo de evento a través de las siguientes máscaras de bits:

Cada vez que se abra el descriptor de fichero del joystick el driver generará unos eventos artificiales para saber el estado inicial de todos los ejes y botones. Así pues, las primeras veces que se consulte el estado leeremos este tipo de eventos artificiales, que nos permitirán conocer el estado completo del dispositivo. Se puede distinguir estos estados artificiales utilizando la máscara de bits JS_EVENT_INIT de la siguiente forma:


if ( (js_ev.type & JS_EVENT_INIT) == JS_EVENT_INIT) {
	// Sinthetic event
}/* if */

Es importante comparar el campo type sólo con estas tres máscaras de bits y no con ninguna otra constante definida por nosotros (aunque sea cero), ya que podría haber problemas debido a los diferentes tamaños de los tipos.

Cerrar el descriptor de fichero

Cuando se termine de utilizar el joystick hay que cerrar su descriptor de fichero con la función close:


int  error;

error = close(fd)
if (error == -1) {
	perror("close");
	exit(EXIT_FAILURE);
} /* if */

Como siempre, es una buen práctica de programación comprobar que no se ha producido ningún error en la llamada a una función.