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 614:a2d239d4438a
next615:38b69ec2f4c8
author nkeynes
date Mon Jan 28 02:38:09 2008 +0000 (14 years ago)
permissions -rw-r--r--
last change Bug #49: Joystick support work in progress
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 GList *linux_joysticks = NULL;
    50 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition, 
    51 					 gpointer data );
    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 );
    57 /**
    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)
    61  */
    62 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str )
    63 {
    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 ) {
    68 	    return 0;
    69 	}
    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 ) {
    74 	    return 0;
    75 	}
    76 	return (uint16_t)(axis+joy->button_count);
    77     } else {
    78 	return 0;
    79     }
    80 }
    82 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode )
    83 {
    84     linux_joystick_t joy = (linux_joystick_t)dev;
    85     if( keycode == 0 ) {
    86 	return NULL;
    87     }
    88     if( keycode <= joy->button_count ) {
    89 	return g_strdup_printf( "Button%d", keycode );
    90     }
    91     if( keycode <= joy->button_count + joy->axis_count ) {
    92 	return g_strdup_printf( "Axis%d", keycode - joy->button_count );
    93     }
    94     return NULL;
    95 }
    97 static void linux_joystick_destroy( input_driver_t dev )
    98 {
    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);
   103     g_free( joy );
   104 }
   106 /**
   107  * Callback from the GIOChannel whenever data is available, or an error/disconnect
   108  * occurs.
   109  *
   110  * On data, process all pending events and direct them to the input system.
   111  * On error, close the channel and delete the device.
   112  */
   113 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition, 
   114 					 gpointer data )
   115 {
   116     linux_joystick_t joy = (linux_joystick_t)data;
   118     if( condition & G_IO_HUP ) {
   119 	close(joy->fd);
   120 	INFO( "Joystick '%s' disconnected\n", joy->name );
   121 	input_unregister_device((input_driver_t)joy);
   122 	return FALSE;
   123     }
   124     if( condition & G_IO_IN ) {
   125 	struct js_event event;
   126 	while( read( joy->fd, &event, sizeof(event) ) == sizeof(event) ) {
   127 	    int keycode = 0;
   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;
   132 	    }
   133 	    if( keycode != 0 ) {
   134 		if( event.value == 0 ) {
   135 		    input_event_keyup( (input_driver_t)joy, keycode, event.value );
   136 		} else {
   137 		    input_event_keydown( (input_driver_t)joy, keycode, event.value );
   138 		}
   139 	    }
   140 	}
   141 	return TRUE;
   142     }
   143 }
   145 /**
   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.
   149  */
   150 linux_joystick_t linux_joystick_add( const gchar *filename, int fd )
   151 {
   152     linux_joystick_t joy = g_malloc0(sizeof(struct linux_joystick));
   153     joy->filename = filename;
   154     joy->fd = fd;
   155     joy->name[0] = '\0';
   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, '/');
   161     if( p == NULL ) {
   162 	joy->driver.id = filename;
   163     } else {
   164 	joy->driver.id = p+1;
   165     }
   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) );
   171 	g_free(joy);
   172 	return NULL;
   173     }
   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 );
   180     return joy;
   181 }
   183 int linux_joystick_init()
   184 {
   185     struct dirent *ent;
   186     DIR *dir = opendir(INPUT_PATH);
   188     if( dir == NULL ) {
   189 	return 0;
   190     }
   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);
   197 	    if( fd == -1 ) {
   198 		g_free( name );
   199 	    } else {
   200 		linux_joystick_t js = linux_joystick_add( name, fd );
   201 	    }
   202 	}
   203     }
   205     closedir(dir);
   206 }
.