joystick 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []