Search
lxdream.org :: lxdream/src/drivers/joy_linux.c :: diff
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 (13 years ago)
permissions -rw-r--r--
last change Bug #49: Joystick support work in progress
file annotate diff log raw
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.3 @@ -0,0 +1,206 @@
1.4 +/**
1.5 + * $Id: joy_linux.c,v 1.12 2007-11-08 11:54:16 nkeynes Exp $
1.6 + *
1.7 + * Linux joystick input device support
1.8 + *
1.9 + * Copyright (c) 2008 Nathan Keynes.
1.10 + *
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.15 + *
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.20 + */
1.21 +
1.22 +#include <sys/types.h>
1.23 +#include <sys/ioctl.h>
1.24 +#include <errno.h>
1.25 +#include <stdio.h>
1.26 +#include <string.h>
1.27 +#include <fcntl.h>
1.28 +#include <dirent.h>
1.29 +#include <ctype.h>
1.30 +
1.31 +#include <linux/joystick.h>
1.32 +#include <glib/giochannel.h>
1.33 +#include <glib.h>
1.34 +
1.35 +#include "lxdream.h"
1.36 +#include "display.h"
1.37 +
1.38 +#define INPUT_PATH "/dev/input"
1.39 +
1.40 +typedef struct linux_joystick {
1.41 + struct input_driver driver;
1.42 + const gchar *filename;
1.43 + char name[128];
1.44 + int fd;
1.45 + int button_count, axis_count;
1.46 + GIOChannel *channel;
1.47 +
1.48 +} *linux_joystick_t;
1.49 +
1.50 +static GList *linux_joysticks = NULL;
1.51 +
1.52 +
1.53 +static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
1.54 + gpointer data );
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.59 +
1.60 +/**
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.64 + */
1.65 +static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str )
1.66 +{
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.71 + return 0;
1.72 + }
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.77 + return 0;
1.78 + }
1.79 + return (uint16_t)(axis+joy->button_count);
1.80 + } else {
1.81 + return 0;
1.82 + }
1.83 +}
1.84 +
1.85 +static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode )
1.86 +{
1.87 + linux_joystick_t joy = (linux_joystick_t)dev;
1.88 + if( keycode == 0 ) {
1.89 + return NULL;
1.90 + }
1.91 + if( keycode <= joy->button_count ) {
1.92 + return g_strdup_printf( "Button%d", keycode );
1.93 + }
1.94 + if( keycode <= joy->button_count + joy->axis_count ) {
1.95 + return g_strdup_printf( "Axis%d", keycode - joy->button_count );
1.96 + }
1.97 + return NULL;
1.98 +}
1.99 +
1.100 +static void linux_joystick_destroy( input_driver_t dev )
1.101 +{
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.106 + g_free( joy );
1.107 +}
1.108 +
1.109 +/**
1.110 + * Callback from the GIOChannel whenever data is available, or an error/disconnect
1.111 + * occurs.
1.112 + *
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.115 + */
1.116 +static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
1.117 + gpointer data )
1.118 +{
1.119 + linux_joystick_t joy = (linux_joystick_t)data;
1.120 +
1.121 + if( condition & G_IO_HUP ) {
1.122 + close(joy->fd);
1.123 + INFO( "Joystick '%s' disconnected\n", joy->name );
1.124 + input_unregister_device((input_driver_t)joy);
1.125 + return FALSE;
1.126 + }
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.130 + int keycode = 0;
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.135 + }
1.136 + if( keycode != 0 ) {
1.137 + if( event.value == 0 ) {
1.138 + input_event_keyup( (input_driver_t)joy, keycode, event.value );
1.139 + } else {
1.140 + input_event_keydown( (input_driver_t)joy, keycode, event.value );
1.141 + }
1.142 + }
1.143 + }
1.144 + return TRUE;
1.145 + }
1.146 +}
1.147 +
1.148 +/**
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.152 + */
1.153 +linux_joystick_t linux_joystick_add( const gchar *filename, int fd )
1.154 +{
1.155 + linux_joystick_t joy = g_malloc0(sizeof(struct linux_joystick));
1.156 + joy->filename = filename;
1.157 + joy->fd = fd;
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.162 +
1.163 + char *p = strrchr(filename, '/');
1.164 + if( p == NULL ) {
1.165 + joy->driver.id = filename;
1.166 + } else {
1.167 + joy->driver.id = p+1;
1.168 + }
1.169 +
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.174 + g_free(joy);
1.175 + return NULL;
1.176 + }
1.177 +
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.183 + return joy;
1.184 +}
1.185 +
1.186 +int linux_joystick_init()
1.187 +{
1.188 + struct dirent *ent;
1.189 + DIR *dir = opendir(INPUT_PATH);
1.190 +
1.191 + if( dir == NULL ) {
1.192 + return 0;
1.193 + }
1.194 +
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.200 + if( fd == -1 ) {
1.201 + g_free( name );
1.202 + } else {
1.203 + linux_joystick_t js = linux_joystick_add( name, fd );
1.204 + }
1.205 + }
1.206 + }
1.207 +
1.208 + closedir(dir);
1.209 +}
.