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