filename | src/drivers/joy_linux.c |
changeset | 621:225d147d2b43 |
prev | 620:d9b28f78b952 |
next | 637:6a284eff5311 |
author | nkeynes |
date | Thu Feb 14 13:54:11 2008 +0000 (16 years ago) |
branch | lxdream-render |
permissions | -rw-r--r-- |
last change | Commit render work in progress. Main changes: * Preliminary OSMesa support * Move the generic gl code out to pvr2/ * Implement scene data structure + reader * Remove the 1/z adjustments |
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 #define _GNU_SOURCE
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <dirent.h>
28 #include <ctype.h>
30 #include <linux/joystick.h>
31 #include <glib/giochannel.h>
32 #include <glib.h>
34 #include "lxdream.h"
35 #include "display.h"
37 #define INPUT_PATH "/dev/input"
39 typedef struct linux_joystick {
40 struct input_driver driver;
41 const gchar *filename;
42 char name[128];
43 int fd;
44 int button_count, axis_count;
45 GIOChannel *channel;
47 } *linux_joystick_t;
49 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
50 gpointer data );
51 static linux_joystick_t linux_joystick_add( const gchar *filename, int fd );
52 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str );
53 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode );
54 static void linux_joystick_destroy( input_driver_t joy );
55 static gboolean linux_joystick_install_watch( const gchar *dir );
56 static void linux_joystick_uninstall_watch( void );
58 /**
59 * Convert keysym to keycode. Keysyms are either Button%d or Axis%d[+-], with buttons
60 * numbered 1 .. button_count, then axes from button_count+1 .. button_count + axis_count*2.
61 * The first button is Button1. (no Button0)
62 * The first axis is Axis1+, then Axis1-, Axis2+ and so forth.
63 */
64 static uint16_t linux_joystick_resolve_keysym( input_driver_t dev, const gchar *str )
65 {
66 linux_joystick_t joy = (linux_joystick_t)dev;
67 if( strncasecmp( str, "Button", 6 ) == 0 ){
68 unsigned long button = strtoul( str+6, NULL, 10 );
69 if( button > joy->button_count ) {
70 return 0;
71 }
72 return (uint16_t)button;
73 } else if( strncasecmp( str, "Axis", 4 ) == 0 ) {
74 char *endptr;
75 unsigned long axis = strtoul( str+4, &endptr, 10 );
76 if( axis > joy->axis_count || axis == 0 ) {
77 return 0;
78 }
79 int keycode = ((axis - 1) << 1) + joy->button_count + 1;
80 if( *endptr == '-' ) {
81 return keycode + 1;
82 } else {
83 return keycode;
84 }
85 } else {
86 return 0;
87 }
88 }
90 static gchar *linux_joystick_keysym_for_keycode( input_driver_t dev, uint16_t keycode )
91 {
92 linux_joystick_t joy = (linux_joystick_t)dev;
93 if( keycode == 0 ) {
94 return NULL;
95 }
96 if( keycode <= joy->button_count ) {
97 return g_strdup_printf( "Button%d", keycode );
98 }
99 if( keycode <= joy->button_count + joy->axis_count*2 ) {
100 int axis = keycode - joy->button_count - 1;
101 if( (axis & 1) == 0 ) {
102 return g_strdup_printf( "Axis%d+", (axis >> 1)+1 );
103 } else {
104 return g_strdup_printf( "Axis%d-", (axis >> 1)+1 );
105 }
106 }
107 return NULL;
108 }
110 static void linux_joystick_destroy( input_driver_t dev )
111 {
112 linux_joystick_t joy = (linux_joystick_t)dev;
113 g_free( (gchar *)joy->filename );
114 g_io_channel_shutdown(joy->channel, FALSE, NULL );
115 g_io_channel_unref(joy->channel);
116 g_free( joy );
117 }
119 /**
120 * Callback from the GIOChannel whenever data is available, or an error/disconnect
121 * occurs.
122 *
123 * On data, process all pending events and direct them to the input system.
124 * On error, close the channel and delete the device.
125 */
126 static gboolean linux_joystick_callback( GIOChannel *source, GIOCondition condition,
127 gpointer data )
128 {
129 linux_joystick_t joy = (linux_joystick_t)data;
131 if( condition & G_IO_HUP ) {
132 INFO( "Joystick '%s' disconnected\n", joy->name );
133 input_unregister_device((input_driver_t)joy);
134 return FALSE;
135 }
136 if( condition & G_IO_IN ) {
137 struct js_event event;
138 while( read( joy->fd, &event, sizeof(event) ) == sizeof(event) ) {
139 if( event.type == JS_EVENT_BUTTON ) {
140 int keycode = event.number+1;
141 if( event.value == 0 ) {
142 input_event_keyup( (input_driver_t)joy, keycode, 0 );
143 } else {
144 input_event_keydown( (input_driver_t)joy, keycode, event.value );
145 }
146 } else if( event.type == JS_EVENT_AXIS ) {
147 int keycode = (event.number*2) + joy->button_count + 1;
148 if( event.value == 0 ) {
149 input_event_keyup( (input_driver_t)joy, keycode, 0 );
150 input_event_keyup( (input_driver_t)joy, keycode+1, 0 );
151 } else if( event.value < 0 ) {
152 input_event_keydown( (input_driver_t)joy, keycode+1, -event.value );
153 input_event_keyup( (input_driver_t)joy, keycode, 0 );
154 } else {
155 input_event_keydown( (input_driver_t)joy, keycode, event.value );
156 input_event_keyup( (input_driver_t)joy, keycode+1, 0 );
157 }
158 }
159 }
160 return TRUE;
161 }
162 }
164 /**
165 * Create a new joystick device structure given filename and (open) file
166 * descriptor. The joystick is automatically added to the watch list.
167 * @return The new joystick, or NULL if an error occurred.
168 */
169 linux_joystick_t linux_joystick_new( const gchar *filename, int fd )
170 {
171 linux_joystick_t joy = g_malloc0(sizeof(struct linux_joystick));
172 joy->filename = filename;
173 joy->fd = fd;
174 joy->name[0] = '\0';
175 joy->driver.resolve_keysym = linux_joystick_resolve_keysym;
176 joy->driver.get_keysym_for_keycode = linux_joystick_keysym_for_keycode;
177 joy->driver.destroy = linux_joystick_destroy;
179 char *p = strrchr(filename, '/');
180 if( p == NULL ) {
181 joy->driver.id = filename;
182 } else {
183 joy->driver.id = p+1;
184 }
186 if( ioctl( fd, JSIOCGNAME(128), joy->name ) == -1 ||
187 ioctl( fd, JSIOCGAXES, &joy->axis_count ) == -1 ||
188 ioctl( fd, JSIOCGBUTTONS, &joy->button_count ) == -1 ) {
189 ERROR( "Error reading joystick data from %s (%s)\n", filename, strerror(errno) );
190 g_free(joy);
191 return NULL;
192 }
194 joy->channel = g_io_channel_unix_new(fd);
195 g_io_add_watch( joy->channel, G_IO_IN|G_IO_ERR|G_IO_HUP, linux_joystick_callback, joy );
196 return joy;
197 }
199 int linux_joystick_init()
200 {
201 linux_joystick_install_watch(INPUT_PATH);
202 linux_joystick_scan();
203 }
205 int linux_joystick_scan()
206 {
207 int joysticks = 0;
208 struct dirent *ent;
209 DIR *dir = opendir(INPUT_PATH);
211 if( dir == NULL ) {
212 return 0;
213 }
215 while( (ent = readdir(dir)) != NULL ) {
216 if( ent->d_name[0] == 'j' && ent->d_name[1] == 's' &&
217 isdigit(ent->d_name[2]) && !input_has_device(ent->d_name) ) {
218 gchar *name = g_strdup_printf( "%s/%s", INPUT_PATH, ent->d_name );
219 int fd = open(name, O_RDONLY|O_NONBLOCK);
220 if( fd == -1 ) {
221 g_free( name );
222 } else {
223 linux_joystick_t joy = linux_joystick_new( name, fd );
224 input_register_device( (input_driver_t)joy, (joy->axis_count*2) + joy->button_count );
225 INFO( "Attached joystick %s named '%s', (%d buttons, %d axes)",
226 joy->driver.id, joy->name, joy->button_count, joy->axis_count );
227 joysticks++;
228 }
229 }
230 }
232 closedir(dir);
233 return joysticks;
234 }
236 void linux_joystick_shutdown(void)
237 {
238 linux_joystick_uninstall_watch();
239 }
241 /*************************** dnotify support **********************/
243 static volatile int need_input_rescan = 0;
244 static int watch_dir_fd;
246 static gboolean gtk_loop_check_input(gpointer data)
247 {
248 if( need_input_rescan == 1 ) {
249 need_input_rescan = 0;
250 int js = linux_joystick_scan();
251 if( js > 0 ) {
252 maple_reattach_all();
253 }
254 }
255 return need_input_rescan != 2;
256 }
258 static void dnotify_handler(int sig )
259 {
260 need_input_rescan = 1;
261 }
263 static gboolean linux_joystick_install_watch( const gchar *dir )
264 {
265 int fd = open( dir, O_RDONLY|O_NONBLOCK );
266 if( fd == -1 ) {
267 return FALSE;
268 }
270 signal( SIGRTMIN+1, dnotify_handler );
271 fcntl(fd, F_SETSIG, SIGRTMIN + 1);
272 if( fcntl(fd, F_NOTIFY, DN_CREATE|DN_MULTISHOT) == -1 ) {
273 close(fd);
274 return FALSE;
275 }
276 watch_dir_fd = fd;
277 g_timeout_add( 500, gtk_loop_check_input, NULL );
278 }
280 static void linux_joystick_uninstall_watch(void)
281 {
282 signal( SIGRTMIN+1, SIG_IGN );
283 close( watch_dir_fd );
284 need_input_rescan = 2;
285 }
.