2 * $Id: joy_linux.c,v 1.12 2007-11-08 11:54:16 nkeynes Exp $
4 * Linux joystick input device support
6 * Copyright (c) 2008 Nathan Keynes.
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.
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.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
28 #include <linux/joystick.h>
29 #include <glib/giochannel.h>
35 #define INPUT_PATH "/dev/input"
37 typedef struct linux_joystick {
38 struct input_driver driver;
39 const gchar *filename;
42 int button_count, axis_count;
47 static GList *linux_joysticks = NULL;
50 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
52 static linux_joystick_t linux_joystick_add( const gchar *filename, int fd );
53 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str );
54 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode );
55 static void linux_joystick_destroy( input_driver_t joy );
58 * Convert keysym to keycode. Keysyms are either Button%d or Axis%d, with buttons
59 * numbered 1 .. button_count, then axes from button_count+1 .. button_count + axis_count.
60 * The first button is Button1. (no Button0)
62 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str )
64 linux_joystick_t joy = (linux_joystick_t)dev;
65 if( strncasecmp( str, "Button", 6 ) == 0 ){
66 unsigned long button = strtoul( str+6, NULL, 10 );
67 if( button > joy->button_count ) {
70 return (uint16_t)button;
71 } else if( strncasecmp( str, "Axis", 4 ) == 0 ) {
72 unsigned long axis = strtoul( str+4, NULL, 10 );
73 if( axis > joy->axis_count || axis == 0 ) {
76 return (uint16_t)(axis+joy->button_count);
82 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode )
84 linux_joystick_t joy = (linux_joystick_t)dev;
88 if( keycode <= joy->button_count ) {
89 return g_strdup_printf( "Button%d", keycode );
91 if( keycode <= joy->button_count + joy->axis_count ) {
92 return g_strdup_printf( "Axis%d", keycode - joy->button_count );
97 static void linux_joystick_destroy( input_driver_t dev )
99 linux_joystick_t joy = (linux_joystick_t)dev;
100 g_free( (gchar *)joy->filename );
101 g_io_channel_shutdown(joy->channel, FALSE, NULL );
102 g_io_channel_unref(joy->channel);
107 * Callback from the GIOChannel whenever data is available, or an error/disconnect
110 * On data, process all pending events and direct them to the input system.
111 * On error, close the channel and delete the device.
113 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
116 linux_joystick_t joy = (linux_joystick_t)data;
118 if( condition & G_IO_HUP ) {
120 INFO( "Joystick '%s' disconnected\n", joy->name );
121 input_unregister_device((input_driver_t)joy);
124 if( condition & G_IO_IN ) {
125 struct js_event event;
126 while( read( joy->fd, &event, sizeof(event) ) == sizeof(event) ) {
128 if( event.type == JS_EVENT_BUTTON ) {
129 keycode = event.number+1;
130 } else if( event.type == JS_EVENT_AXIS ) {
131 keycode = event.number+1 + joy->button_count;
134 if( event.value == 0 ) {
135 input_event_keyup( (input_driver_t)joy, keycode, event.value );
137 input_event_keydown( (input_driver_t)joy, keycode, event.value );
146 * Create a new joystick device structure given filename and (open) file
147 * descriptor. The joystick is automatically added to the watch list.
148 * @return The new joystick, or NULL if an error occurred.
150 linux_joystick_t linux_joystick_add( const gchar *filename, int fd )
152 linux_joystick_t joy = g_malloc0(sizeof(struct linux_joystick));
153 joy->filename = filename;
156 joy->driver.resolve_keysym = linux_joystick_resolve_keysym;
157 joy->driver.get_keysym_for_keycode = linux_joystick_keysym_for_keycode;
158 joy->driver.destroy = linux_joystick_destroy;
160 char *p = strrchr(filename, '/');
162 joy->driver.id = filename;
164 joy->driver.id = p+1;
167 if( ioctl( fd, JSIOCGNAME(128), joy->name ) == -1 ||
168 ioctl( fd, JSIOCGAXES, &joy->axis_count ) == -1 ||
169 ioctl( fd, JSIOCGBUTTONS, &joy->button_count ) == -1 ) {
170 ERROR( "Error reading joystick data from %s (%s)\n", filename, strerror(errno) );
175 joy->channel = g_io_channel_unix_new(fd);
176 g_io_add_watch( joy->channel, G_IO_IN|G_IO_ERR|G_IO_HUP, linux_joystick_callback, joy );
177 input_register_device( (input_driver_t)joy, joy->axis_count + joy->button_count );
178 INFO( "Attached joystick '%s' at %s (%d buttons, %d axes)", joy->name, joy->filename,
179 joy->button_count, joy->axis_count );
183 int linux_joystick_init()
186 DIR *dir = opendir(INPUT_PATH);
192 while( (ent = readdir(dir)) != NULL ) {
193 if( ent->d_name[0] == 'j' && ent->d_name[1] == 's' &&
194 isdigit(ent->d_name[2]) ) {
195 gchar *name = g_strdup_printf( "%s/%s", INPUT_PATH, ent->d_name );
196 int fd = open(name, O_RDONLY|O_NONBLOCK);
200 linux_joystick_t js = linux_joystick_add( name, fd );
.