1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/drivers/joy_linux.c Mon Jan 28 02:38:09 2008 +0000
1.5 + * $Id: joy_linux.c,v 1.12 2007-11-08 11:54:16 nkeynes Exp $
1.7 + * Linux joystick input device support
1.9 + * Copyright (c) 2008 Nathan Keynes.
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.14 + * (at your option) any later version.
1.16 + * This program is distributed in the hope that it will be useful,
1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.22 +#include <sys/types.h>
1.23 +#include <sys/ioctl.h>
1.26 +#include <string.h>
1.28 +#include <dirent.h>
1.31 +#include <linux/joystick.h>
1.32 +#include <glib/giochannel.h>
1.35 +#include "lxdream.h"
1.36 +#include "display.h"
1.38 +#define INPUT_PATH "/dev/input"
1.40 +typedef struct linux_joystick {
1.41 + struct input_driver driver;
1.42 + const gchar *filename;
1.45 + int button_count, axis_count;
1.46 + GIOChannel *channel;
1.48 +} *linux_joystick_t;
1.50 +static GList *linux_joysticks = NULL;
1.53 +static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
1.55 +static linux_joystick_t linux_joystick_add( const gchar *filename, int fd );
1.56 +static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str );
1.57 +static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode );
1.58 +static void linux_joystick_destroy( input_driver_t joy );
1.61 + * Convert keysym to keycode. Keysyms are either Button%d or Axis%d, with buttons
1.62 + * numbered 1 .. button_count, then axes from button_count+1 .. button_count + axis_count.
1.63 + * The first button is Button1. (no Button0)
1.65 +static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str )
1.67 + linux_joystick_t joy = (linux_joystick_t)dev;
1.68 + if( strncasecmp( str, "Button", 6 ) == 0 ){
1.69 + unsigned long button = strtoul( str+6, NULL, 10 );
1.70 + if( button > joy->button_count ) {
1.73 + return (uint16_t)button;
1.74 + } else if( strncasecmp( str, "Axis", 4 ) == 0 ) {
1.75 + unsigned long axis = strtoul( str+4, NULL, 10 );
1.76 + if( axis > joy->axis_count || axis == 0 ) {
1.79 + return (uint16_t)(axis+joy->button_count);
1.85 +static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode )
1.87 + linux_joystick_t joy = (linux_joystick_t)dev;
1.88 + if( keycode == 0 ) {
1.91 + if( keycode <= joy->button_count ) {
1.92 + return g_strdup_printf( "Button%d", keycode );
1.94 + if( keycode <= joy->button_count + joy->axis_count ) {
1.95 + return g_strdup_printf( "Axis%d", keycode - joy->button_count );
1.100 +static void linux_joystick_destroy( input_driver_t dev )
1.102 + linux_joystick_t joy = (linux_joystick_t)dev;
1.103 + g_free( (gchar *)joy->filename );
1.104 + g_io_channel_shutdown(joy->channel, FALSE, NULL );
1.105 + g_io_channel_unref(joy->channel);
1.110 + * Callback from the GIOChannel whenever data is available, or an error/disconnect
1.113 + * On data, process all pending events and direct them to the input system.
1.114 + * On error, close the channel and delete the device.
1.116 +static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
1.119 + linux_joystick_t joy = (linux_joystick_t)data;
1.121 + if( condition & G_IO_HUP ) {
1.123 + INFO( "Joystick '%s' disconnected\n", joy->name );
1.124 + input_unregister_device((input_driver_t)joy);
1.127 + if( condition & G_IO_IN ) {
1.128 + struct js_event event;
1.129 + while( read( joy->fd, &event, sizeof(event) ) == sizeof(event) ) {
1.131 + if( event.type == JS_EVENT_BUTTON ) {
1.132 + keycode = event.number+1;
1.133 + } else if( event.type == JS_EVENT_AXIS ) {
1.134 + keycode = event.number+1 + joy->button_count;
1.136 + if( keycode != 0 ) {
1.137 + if( event.value == 0 ) {
1.138 + input_event_keyup( (input_driver_t)joy, keycode, event.value );
1.140 + input_event_keydown( (input_driver_t)joy, keycode, event.value );
1.149 + * Create a new joystick device structure given filename and (open) file
1.150 + * descriptor. The joystick is automatically added to the watch list.
1.151 + * @return The new joystick, or NULL if an error occurred.
1.153 +linux_joystick_t linux_joystick_add( const gchar *filename, int fd )
1.155 + linux_joystick_t joy = g_malloc0(sizeof(struct linux_joystick));
1.156 + joy->filename = filename;
1.158 + joy->name[0] = '\0';
1.159 + joy->driver.resolve_keysym = linux_joystick_resolve_keysym;
1.160 + joy->driver.get_keysym_for_keycode = linux_joystick_keysym_for_keycode;
1.161 + joy->driver.destroy = linux_joystick_destroy;
1.163 + char *p = strrchr(filename, '/');
1.164 + if( p == NULL ) {
1.165 + joy->driver.id = filename;
1.167 + joy->driver.id = p+1;
1.170 + if( ioctl( fd, JSIOCGNAME(128), joy->name ) == -1 ||
1.171 + ioctl( fd, JSIOCGAXES, &joy->axis_count ) == -1 ||
1.172 + ioctl( fd, JSIOCGBUTTONS, &joy->button_count ) == -1 ) {
1.173 + ERROR( "Error reading joystick data from %s (%s)\n", filename, strerror(errno) );
1.178 + joy->channel = g_io_channel_unix_new(fd);
1.179 + g_io_add_watch( joy->channel, G_IO_IN|G_IO_ERR|G_IO_HUP, linux_joystick_callback, joy );
1.180 + input_register_device( (input_driver_t)joy, joy->axis_count + joy->button_count );
1.181 + INFO( "Attached joystick '%s' at %s (%d buttons, %d axes)", joy->name, joy->filename,
1.182 + joy->button_count, joy->axis_count );
1.186 +int linux_joystick_init()
1.188 + struct dirent *ent;
1.189 + DIR *dir = opendir(INPUT_PATH);
1.191 + if( dir == NULL ) {
1.195 + while( (ent = readdir(dir)) != NULL ) {
1.196 + if( ent->d_name[0] == 'j' && ent->d_name[1] == 's' &&
1.197 + isdigit(ent->d_name[2]) ) {
1.198 + gchar *name = g_strdup_printf( "%s/%s", INPUT_PATH, ent->d_name );
1.199 + int fd = open(name, O_RDONLY|O_NONBLOCK);
1.203 + linux_joystick_t js = linux_joystick_add( name, fd );