Search
lxdream.org :: lxdream/src/drivers/joy_linux.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/joy_linux.c
changeset 615:38b69ec2f4c8
prev614:a2d239d4438a
next620:d9b28f78b952
author nkeynes
date Mon Jan 28 11:17:15 2008 +0000 (16 years ago)
permissions -rw-r--r--
last change Split axes into positive and negative sides. Works better now
view annotate diff log raw
     1 /**
     2  * $Id: joy_linux.c,v 1.12 2007-11-08 11:54:16 nkeynes Exp $
     3  *
     4  * Linux joystick input device support
     5  *
     6  * Copyright (c) 2008 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    19 #include <sys/types.h>
    20 #include <sys/ioctl.h>
    21 #include <errno.h>
    22 #include <stdio.h>
    23 #include <string.h>
    24 #include <fcntl.h>
    25 #include <dirent.h>
    26 #include <ctype.h>
    28 #include <linux/joystick.h>
    29 #include <glib/giochannel.h>
    30 #include <glib.h>
    32 #include "lxdream.h"
    33 #include "display.h"
    35 #define INPUT_PATH "/dev/input"
    37 typedef struct linux_joystick {
    38     struct input_driver driver;
    39     const gchar *filename;
    40     char name[128];
    41     int fd;
    42     int button_count, axis_count;
    43     GIOChannel *channel;
    45 } *linux_joystick_t;
    47 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition, 
    48 					 gpointer data );
    49 static linux_joystick_t linux_joystick_add( const gchar *filename, int fd );
    50 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str );
    51 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode );
    52 static void linux_joystick_destroy( input_driver_t joy );
    54 /**
    55  * Convert keysym to keycode. Keysyms are either Button%d or Axis%d[+-], with buttons
    56  * numbered 1 .. button_count, then axes from button_count+1 .. button_count + axis_count*2.
    57  * The first button is Button1. (no Button0)
    58  * The first axis is Axis1+, then Axis1-, Axis2+ and so forth.
    59  */
    60 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str )
    61 {
    62     linux_joystick_t joy = (linux_joystick_t)dev;
    63     if( strncasecmp( str, "Button", 6 ) == 0 ){
    64 	unsigned long button = strtoul( str+6, NULL, 10 );
    65 	if( button > joy->button_count ) {
    66 	    return 0;
    67 	}
    68 	return (uint16_t)button;
    69     } else if( strncasecmp( str, "Axis", 4 ) == 0 ) {
    70 	char *endptr;
    71 	unsigned long axis = strtoul( str+4, &endptr, 10 );
    72 	if( axis > joy->axis_count || axis == 0 ) {
    73 	    return 0;
    74 	}
    75 	int keycode = ((axis - 1) << 1) + joy->button_count + 1;
    76 	if( *endptr == '-' ) {
    77 	    return keycode + 1;
    78 	} else {
    79 	    return keycode;
    80 	}
    81     } else {
    82 	return 0;
    83     }
    84 }
    86 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode )
    87 {
    88     linux_joystick_t joy = (linux_joystick_t)dev;
    89     if( keycode == 0 ) {
    90 	return NULL;
    91     }
    92     if( keycode <= joy->button_count ) {
    93 	return g_strdup_printf( "Button%d", keycode );
    94     }
    95     if( keycode <= joy->button_count + joy->axis_count*2 ) {
    96 	int axis = keycode - joy->button_count - 1;
    97 	if( (axis & 1) == 0 ) {
    98 	    return g_strdup_printf( "Axis%d+", (axis >> 1)+1 );
    99 	} else {
   100 	    return g_strdup_printf( "Axis%d-", (axis >> 1)+1 );
   101 	}
   102     }
   103     return NULL;
   104 }
   106 static void linux_joystick_destroy( input_driver_t dev )
   107 {
   108     linux_joystick_t joy = (linux_joystick_t)dev;
   109     g_free( (gchar *)joy->filename );
   110     g_io_channel_shutdown(joy->channel, FALSE, NULL );
   111     g_io_channel_unref(joy->channel);
   112     g_free( joy );
   113 }
   115 /**
   116  * Callback from the GIOChannel whenever data is available, or an error/disconnect
   117  * occurs.
   118  *
   119  * On data, process all pending events and direct them to the input system.
   120  * On error, close the channel and delete the device.
   121  */
   122 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition, 
   123 					 gpointer data )
   124 {
   125     linux_joystick_t joy = (linux_joystick_t)data;
   127     if( condition & G_IO_HUP ) {
   128 	close(joy->fd);
   129 	INFO( "Joystick '%s' disconnected\n", joy->name );
   130 	input_unregister_device((input_driver_t)joy);
   131 	return FALSE;
   132     }
   133     if( condition & G_IO_IN ) {
   134 	struct js_event event;
   135 	while( read( joy->fd, &event, sizeof(event) ) == sizeof(event) ) {
   136 	    if( event.type == JS_EVENT_BUTTON ) {
   137 		int keycode = event.number+1;
   138 		if( event.value == 0 ) {
   139 		    input_event_keyup( (input_driver_t)joy, keycode, 0 );
   140 		} else {
   141 		    input_event_keydown( (input_driver_t)joy, keycode, event.value );
   142 		}
   143 	    } else if( event.type == JS_EVENT_AXIS ) {
   144 		int keycode = (event.number*2) + joy->button_count + 1;
   145 		if( event.value == 0 ) {
   146 		    input_event_keyup( (input_driver_t)joy, keycode, 0 );
   147 		    input_event_keyup( (input_driver_t)joy, keycode+1, 0 );
   148 		} else if( event.value < 0 ) {
   149 		    input_event_keydown( (input_driver_t)joy, keycode+1, -event.value );
   150 		    input_event_keyup( (input_driver_t)joy, keycode, 0 );
   151 		} else {
   152 		    input_event_keydown( (input_driver_t)joy, keycode, event.value );
   153 		    input_event_keyup( (input_driver_t)joy, keycode+1, 0 );
   154 		}
   155 	    }
   156 	}
   157 	return TRUE;
   158     }
   159 }
   161 /**
   162  * Create a new joystick device structure given filename and (open) file
   163  * descriptor. The joystick is automatically added to the watch list.
   164  * @return The new joystick, or NULL if an error occurred.
   165  */
   166 linux_joystick_t linux_joystick_new( const gchar *filename, int fd )
   167 {
   168     linux_joystick_t joy = g_malloc0(sizeof(struct linux_joystick));
   169     joy->filename = filename;
   170     joy->fd = fd;
   171     joy->name[0] = '\0';
   172     joy->driver.resolve_keysym = linux_joystick_resolve_keysym;
   173     joy->driver.get_keysym_for_keycode = linux_joystick_keysym_for_keycode;
   174     joy->driver.destroy = linux_joystick_destroy;
   176     char *p = strrchr(filename, '/');
   177     if( p == NULL ) {
   178 	joy->driver.id = filename;
   179     } else {
   180 	joy->driver.id = p+1;
   181     }
   183     if( ioctl( fd, JSIOCGNAME(128), joy->name ) == -1 ||
   184 	ioctl( fd, JSIOCGAXES, &joy->axis_count ) == -1 || 
   185 	ioctl( fd, JSIOCGBUTTONS, &joy->button_count ) == -1 ) {
   186 	ERROR( "Error reading joystick data from %s (%s)\n", filename, strerror(errno) );
   187 	g_free(joy);
   188 	return NULL;
   189     }
   191     joy->channel = g_io_channel_unix_new(fd);
   192     g_io_add_watch( joy->channel, G_IO_IN|G_IO_ERR|G_IO_HUP, linux_joystick_callback, joy );
   193     return joy;
   194 }
   196 int linux_joystick_init()
   197 {
   198     int joysticks = 0;
   199     struct dirent *ent;
   200     DIR *dir = opendir(INPUT_PATH);
   202     if( dir == NULL ) {
   203 	return 0;
   204     }
   206     while( (ent = readdir(dir)) != NULL ) {
   207 	if( ent->d_name[0] == 'j' && ent->d_name[1] == 's' &&
   208 	    isdigit(ent->d_name[2]) && !input_has_device(ent->d_name) ) {
   209 	    gchar *name = g_strdup_printf( "%s/%s", INPUT_PATH, ent->d_name );
   210 	    int fd = open(name, O_RDONLY|O_NONBLOCK);
   211 	    if( fd == -1 ) {
   212 		g_free( name );
   213 	    } else {
   214 		linux_joystick_t joy = linux_joystick_new( name, fd );
   215 		input_register_device( (input_driver_t)joy, (joy->axis_count*2) + joy->button_count );
   216 		INFO( "Attached joystick %s named '%s', (%d buttons, %d axes)", 
   217 		      joy->driver.id, joy->name, joy->button_count, joy->axis_count );
   218 		joysticks++;
   219 	    }
   220 	}
   221     }
   223     closedir(dir);
   224     return joysticks;
   225 }
.