joystick 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/ext/extconf.rb +5 -0
  2. data/ext/joystick.c +451 -0
  3. metadata +63 -0
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'joystick'
4
+
5
+ create_makefile(extension_name)
@@ -0,0 +1,451 @@
1
+ /**
2
+ * Joystick - Ruby binding for linux kernel joystick
3
+ * Copyright (C) 2008 Claudio Fiorini <claudio@cfiorini.it>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ **/
18
+
19
+ /* Any help and suggestions are always welcome */
20
+ /* TODO change klass to obj when it's an Object and not a Class... */
21
+
22
+
23
+ #include "ruby.h"
24
+ #include "ruby/io.h"
25
+ #include <linux/joystick.h>
26
+ #include <sys/select.h>
27
+ #include <sys/time.h>
28
+ #include <sys/types.h>
29
+ #include <fcntl.h>
30
+ #include <unistd.h>
31
+ #include <string.h>
32
+ #include <errno.h>
33
+ #include <stdint.h>
34
+
35
+ #define MAX_JS 32
36
+ #define NAME_LENGTH 128
37
+
38
+ static VALUE rb_mJoystick;
39
+ static VALUE rb_cDevice;
40
+ static VALUE rb_cEvent;
41
+ static VALUE rb_cSixaxis;
42
+
43
+ void Init_joystick();
44
+
45
+ static struct js_event jse[MAX_JS];
46
+
47
+ void jsdevice_mark(int* fd)
48
+ {
49
+ rb_gc_mark(*fd);
50
+ }
51
+
52
+ void jsdevice_free(int* fd)
53
+ {
54
+ free(fd);
55
+ }
56
+
57
+ void jssix_mark(int* fh)
58
+ {
59
+ rb_gc_mark(*fh);
60
+ }
61
+
62
+ void jssix_free(int* fh)
63
+ {
64
+ free(fh);
65
+ }
66
+
67
+ /*
68
+ * Document-method: Joystick::Device.new
69
+ * call-seq: new(path)
70
+ *
71
+ * Construct a new Joystick::Device object. +path+ is the file
72
+ * path to the joystick's input device e.g. "/dev/input/js0"
73
+ */
74
+ VALUE js_dev_init(VALUE klass, VALUE dev_path)
75
+ {
76
+ int *fd;
77
+ VALUE dev;
78
+
79
+ if((fd = malloc(sizeof(int))) != NULL) {
80
+ if((*fd = open(RSTRING_PTR(dev_path), O_RDONLY)) >= 0) {
81
+ if(*fd >= MAX_JS)
82
+ rb_raise(rb_eException, "Error");
83
+
84
+ dev = Data_Wrap_Struct(klass, jsdevice_mark, jsdevice_free, fd);
85
+ rb_ivar_set(dev, rb_intern("@axis"), rb_ary_new());
86
+ rb_ivar_set(dev, rb_intern("@button"), rb_ary_new());
87
+ return dev;
88
+ }
89
+ }
90
+ return Qnil;
91
+ }
92
+
93
+ /*
94
+ * Document-method: Joystick::Device#axes
95
+ * call-seq: axes()
96
+ *
97
+ * Returns the number of axes of the device.
98
+ */
99
+ VALUE js_dev_axes(VALUE klass)
100
+ {
101
+ int *fd;
102
+ unsigned char axes;
103
+
104
+ Data_Get_Struct(klass, int, fd);
105
+ if(ioctl(*fd, JSIOCGAXES, &axes) == -1) {
106
+ rb_raise(rb_eException, "cannot retrieve axes");
107
+ }
108
+ return INT2FIX(axes);
109
+ }
110
+
111
+ /*
112
+ * Document-method: Joystick::Device#buttons
113
+ * call-seq: buttons()
114
+ *
115
+ * Returns the number of buttons on the device.
116
+ */
117
+ VALUE js_dev_buttons(VALUE klass)
118
+ {
119
+ int *fd;
120
+ unsigned char buttons;
121
+ Data_Get_Struct(klass, int, fd);
122
+ if(ioctl(*fd, JSIOCGBUTTONS, &buttons) == -1) {
123
+ rb_raise(rb_eException, "cannot retrieve buttons");
124
+ }
125
+
126
+ return INT2FIX(buttons);
127
+ }
128
+
129
+ /*
130
+ * Document-method: Joystick::Device#axis
131
+ * call-seq: axis()
132
+ *
133
+ * Reader for @axis which stores the latest axis values.
134
+ */
135
+ VALUE js_dev_axis(VALUE klass)
136
+ {
137
+ return rb_ivar_get(klass, rb_intern("@axis"));
138
+ }
139
+
140
+ /*
141
+ * Document-method: Joystick::Device#button
142
+ * call-seq: button()
143
+ *
144
+ * Reader for @button which stores the latest button values.
145
+ */
146
+ VALUE js_dev_button(VALUE klass)
147
+ {
148
+ return rb_ivar_get(klass, rb_intern("@button"));
149
+ }
150
+
151
+ /*
152
+ * Document-method: Joystick::Device#name
153
+ * call-seq: name()
154
+ *
155
+ * Returns the name of the device.
156
+ */
157
+ VALUE js_dev_name(VALUE klass)
158
+ {
159
+ int *fd;
160
+ char name[NAME_LENGTH] = "Unknown";
161
+
162
+ Data_Get_Struct(klass, int, fd);
163
+ if(ioctl(*fd, JSIOCGNAME(NAME_LENGTH), name) == -1) {
164
+ rb_raise(rb_eException, "cannot retrieve name");
165
+ }
166
+ return rb_str_new2(name);
167
+ }
168
+
169
+ /*
170
+ * Document-method: Joystick::Device#axes_maps
171
+ * call-seq: axes_maps()
172
+ *
173
+ * TODO figure this out
174
+ */
175
+ VALUE js_dev_axes_maps(VALUE klass)
176
+ {
177
+ int *fd;
178
+
179
+ uint8_t axes_maps[ABS_MAX + 1];
180
+ Data_Get_Struct(klass, int, fd);
181
+ if(ioctl(*fd, JSIOCGAXMAP, &axes_maps) == -1) {
182
+ rb_raise(rb_eException, "cannot retrive axes");
183
+ }
184
+ return INT2FIX(axes_maps);
185
+ }
186
+
187
+ /*
188
+ * Document-method: Joystick::Device#version
189
+ * call-seq: version()
190
+ *
191
+ * Returns a string containing the version of the device.
192
+ */
193
+ VALUE js_dev_version(VALUE klass)
194
+ {
195
+ int *fd;
196
+ int version = 0x000800;
197
+ char js_version[16];
198
+ Data_Get_Struct(klass, int, fd);
199
+ if(ioctl(*fd, JSIOCGVERSION, &version) == -1) {
200
+ rb_raise(rb_eException, "version error");
201
+ }
202
+
203
+ sprintf(js_version, "%d.%d.%d\n",
204
+ version >> 16, (version >> 8) & 0xff, version & 0xff);
205
+
206
+ return rb_str_new2(js_version);
207
+ }
208
+
209
+ /* used for blocking calls to Joystick::Device#event */
210
+ struct event_arg {
211
+ int *fd;
212
+ ssize_t l;
213
+ };
214
+
215
+ /* general idea stolen from curses.c */
216
+ static VALUE
217
+ js_event_func(void *_arg)
218
+ {
219
+ struct event_arg *arg = (struct event_arg *)_arg;
220
+ arg->l = read(*(arg->fd), &jse[*(arg->fd)], sizeof(struct js_event));
221
+ return Qnil;
222
+ }
223
+
224
+ /*
225
+ * Document-method: Joystick::Device#event
226
+ * call-seq: event(+nonblocking+)
227
+ *
228
+ * Get a Joystick::Event object from the device.
229
+ *
230
+ * The optional +nonblocking+ argument determines whether or not
231
+ * this is a blocking call. It is blocking by default.
232
+ */
233
+ VALUE js_dev_event_get(int argc, VALUE *argv, VALUE klass)
234
+ {
235
+ struct event_arg arg;
236
+ int *fd;
237
+ ssize_t length;
238
+ VALUE nonblocking;
239
+
240
+ rb_scan_args(argc, argv, "01", &nonblocking);
241
+
242
+ Data_Get_Struct(klass, int, fd);
243
+
244
+ if(RTEST(nonblocking))
245
+ {
246
+ /* TODO I'm not sure how big of a performance hit this is */
247
+ fcntl(*fd, F_SETFL, O_NONBLOCK); /* non-blocking mode */
248
+ length = read(*fd, &jse[*fd], sizeof(struct js_event));
249
+ fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) & ~O_NONBLOCK); /* revert to blocking mode */
250
+ } else {
251
+ arg.fd = fd;
252
+ rb_thread_blocking_region(js_event_func, (void *)&arg, RUBY_UBF_IO, 0);
253
+ length = arg.l;
254
+ }
255
+
256
+ if(length > 0)
257
+ {
258
+ switch(jse[*fd].type & ~JS_EVENT_INIT) /* TODO I think it's safe to assume we have a valid event now */
259
+ {
260
+ case JS_EVENT_AXIS:
261
+ rb_ary_store(rb_ivar_get(klass, rb_intern("@axis")), jse[*fd].number, INT2FIX(jse[*fd].value));
262
+ break;
263
+ case JS_EVENT_BUTTON:
264
+ rb_ary_store(rb_ivar_get(klass, rb_intern("@button")), jse[*fd].number, INT2FIX(jse[*fd].value));
265
+ }
266
+ return Data_Wrap_Struct(rb_cEvent, 0, 0, fd);
267
+ }
268
+
269
+ return Qnil;
270
+ }
271
+
272
+ /*
273
+ * Document-method: Joystick::Device#close
274
+ * call-seq: close()
275
+ *
276
+ * Close the file handle for the device. This should be called
277
+ * for all Joystick::Devices before the script terminates.
278
+ */
279
+ VALUE js_dev_close(VALUE klass)
280
+ {
281
+ int *fd;
282
+
283
+ Data_Get_Struct(klass, int, fd);
284
+ close(*fd);
285
+ return Qnil;
286
+ }
287
+
288
+ /*
289
+ * Document-method: Joystick::Event#number
290
+ * call-seq: number()
291
+ *
292
+ * Returns the number of the axis or button responsible for
293
+ * the event.
294
+ */
295
+ VALUE js_event_number(VALUE klass)
296
+ {
297
+ int *fd;
298
+ Data_Get_Struct(klass, int, fd);
299
+ return INT2FIX((fd && *fd >= 0) ? jse[*fd].number : -1);
300
+ }
301
+
302
+ /*
303
+ * Document-method: Joystick::Event#type
304
+ * call-seq: type()
305
+ *
306
+ * Returns the type of the event. Normally this should either
307
+ * be either :axis or :button. If "something goes wrong", the
308
+ * numerical type is returned.
309
+ */
310
+ VALUE js_event_type(VALUE klass)
311
+ {
312
+ int *fd;
313
+ Data_Get_Struct(klass, int, fd);
314
+ switch(((fd && *fd >= 0) ? jse[*fd].type : -1) & ~JS_EVENT_INIT)
315
+ {
316
+ case JS_EVENT_AXIS:
317
+ return ID2SYM(rb_intern("axis"));
318
+ case JS_EVENT_BUTTON:
319
+ return ID2SYM(rb_intern("button"));
320
+ default:
321
+ return INT2FIX(((fd && *fd >= 0) ? jse[*fd].type : -1) & ~JS_EVENT_INIT);
322
+ }
323
+ }
324
+
325
+ /*
326
+ * Document-method: Joystick::Event#time
327
+ * call-seq: time()
328
+ *
329
+ * Returns the time, in milliseconds, that the event occurred.
330
+ * TODO what is time 0?
331
+ */
332
+ VALUE js_event_time(VALUE klass)
333
+ {
334
+ int *fd;
335
+ Data_Get_Struct(klass, int, fd);
336
+ return INT2FIX((fd && *fd >= 0) ? jse[*fd].time : -1);
337
+ }
338
+
339
+ /*
340
+ * Document-method: Joystick::Event#value
341
+ * call-seq: value()
342
+ *
343
+ * Returns the value of the event, which is internally a
344
+ * signed 16-bit integer. It can range from -32768 to 32767.
345
+ */
346
+ VALUE js_event_value(VALUE klass)
347
+ {
348
+ int *fd;
349
+ Data_Get_Struct(klass, int, fd);
350
+ return INT2FIX((fd && *fd >= 0) ? jse[*fd].value : -1);
351
+ }
352
+
353
+ /*
354
+ * Document-method: Joystick::SixAxis.new
355
+ * call-seq: new(path)
356
+ *
357
+ * TODO
358
+ */
359
+ VALUE js_six_init(VALUE klass, VALUE path)
360
+ {
361
+ int *fh;
362
+ if((fh = malloc(sizeof(int))) != NULL) {
363
+ if((*fh = open(RSTRING_PTR(path), O_RDONLY)) >= 0) {
364
+ return Data_Wrap_Struct(klass, jssix_mark, jssix_free, fh);
365
+ } else
366
+ rb_raise(rb_eException, "Error opening %s", RSTRING_PTR(path));
367
+ }
368
+ return Qnil;
369
+ }
370
+
371
+ /*
372
+ * Document-method: Joystick::SixAxis#get_sixaxis
373
+ * call-seq: get_sixaxis()
374
+ *
375
+ * TODO
376
+ */
377
+ VALUE js_six_get_six(VALUE klass)
378
+ {
379
+ int *fh;
380
+ int res;
381
+ int x = -1;
382
+ int y = -1;
383
+ int z = -1;
384
+ unsigned char buf[128];
385
+ VALUE saxis = rb_hash_new();
386
+
387
+ Data_Get_Struct(klass, int, fh);
388
+ if(res = read(*fh, buf, sizeof(buf))) {
389
+ if(res == 48) {
390
+ x = buf[40]<<8 | buf[41];
391
+ y = buf[42]<<8 | buf[43];
392
+ z = buf[44]<<8 | buf[45];
393
+ } else if(res == 49) {
394
+ x = buf[41]<<8 | buf[42];
395
+ y = buf[43]<<8 | buf[44];
396
+ z = buf[45]<<8 | buf[46];
397
+ }
398
+
399
+ rb_hash_aset(saxis, ID2SYM(rb_intern("x")), INT2FIX(x));
400
+ rb_hash_aset(saxis, ID2SYM(rb_intern("y")), INT2FIX(y));
401
+ rb_hash_aset(saxis, ID2SYM(rb_intern("z")), INT2FIX(z));
402
+
403
+ return saxis;
404
+ } else
405
+ rb_raise(rb_eException, "error");
406
+
407
+ return Qnil;
408
+ }
409
+
410
+ /*
411
+ * Document-method: Joystick::SixAxis#close
412
+ * call-seq: close(path)
413
+ *
414
+ * TODO
415
+ */
416
+ VALUE js_six_close(VALUE klass)
417
+ {
418
+ int *fh;
419
+
420
+ Data_Get_Struct(klass, int, fh);
421
+
422
+ return INT2FIX(close(*fh));
423
+ }
424
+
425
+ void Init_joystick()
426
+ {
427
+ rb_mJoystick = rb_define_module("Joystick");
428
+
429
+ rb_cDevice = rb_define_class_under(rb_mJoystick, "Device", rb_cObject);
430
+ rb_define_singleton_method(rb_cDevice, "new", js_dev_init, 1);
431
+ rb_define_method(rb_cDevice, "axes", js_dev_axes, 0);
432
+ rb_define_method(rb_cDevice, "buttons", js_dev_buttons, 0);
433
+ rb_define_method(rb_cDevice, "axis", js_dev_axis, 0);
434
+ rb_define_method(rb_cDevice, "button", js_dev_button, 0);
435
+ rb_define_method(rb_cDevice, "axes_maps", js_dev_axes_maps, 0);
436
+ rb_define_method(rb_cDevice, "name", js_dev_name, 0);
437
+ rb_define_method(rb_cDevice, "version", js_dev_version, 0);
438
+ rb_define_method(rb_cDevice, "event", js_dev_event_get, -1);
439
+ rb_define_method(rb_cDevice, "close", js_dev_close, 0);
440
+
441
+ rb_cEvent = rb_define_class_under(rb_mJoystick, "Event", rb_cObject);
442
+ rb_define_method(rb_cEvent, "time", js_event_time, 0);
443
+ rb_define_method(rb_cEvent, "value", js_event_value, 0);
444
+ rb_define_method(rb_cEvent, "number", js_event_number, 0);
445
+ rb_define_method(rb_cEvent, "type", js_event_type, 0);
446
+
447
+ rb_cSixaxis = rb_define_class_under(rb_mJoystick, "SixAxis", rb_cObject);
448
+ rb_define_singleton_method(rb_cSixaxis, "new", js_six_init, 1);
449
+ rb_define_method(rb_cSixaxis, "get_sixaxis", js_six_get_six, 0);
450
+ rb_define_method(rb_cSixaxis, "close", js_six_close, 0);
451
+ }
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: joystick
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Max Anselm
9
+ - Claudio Fiorini
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-03-18 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: ! 'This is a Ruby extension that wraps the fuctionality provided by
16
+
17
+ linux/joystick.h, allowing Ruby scripts to use input from Xbox 360
18
+
19
+ controllers, PS3 Sixaxis controllers, etc.
20
+
21
+
22
+ I originally took this code from Claudio Fiorini''s rjoystick gem, made it a
23
+
24
+ little easier to use, made it work with Ruby''s Threads, and added
25
+
26
+ documentation.
27
+
28
+ '
29
+ email: silverhammermba@gmail.com
30
+ executables: []
31
+ extensions:
32
+ - ext/extconf.rb
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ext/joystick.c
36
+ - ext/extconf.rb
37
+ homepage: http://github.com/silverhammermba/joystick
38
+ licenses:
39
+ - GPL-3
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ - ext
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.11
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: binding for Linux kernel joysticks
63
+ test_files: []