gyruby 0.0.1

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 +68 -0
  2. data/ext/gyruby_ext.c +409 -0
  3. metadata +55 -0
data/ext/extconf.rb ADDED
@@ -0,0 +1,68 @@
1
+ # gyruby - a ruby binding for the Gyration FiireChief remote control for LinuxMCE
2
+ # Copyright (C) 2007 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ require 'mkmf'
19
+
20
+ def crash(s)
21
+ puts "--------------------------------------------------"
22
+ puts " extconf failure: #{s}"
23
+ puts "--------------------------------------------------"
24
+ exit 1
25
+ end
26
+
27
+ unless find_executable("pkg-config")
28
+ crash("pkg-config needed")
29
+ end
30
+
31
+ $CFLAGS += " -std=c99 -Wall -I. " + `pkg-config --cflags glib-2.0`.strip
32
+ $LIBS += " " + `pkg-config --libs glib-2.0`
33
+
34
+ unless have_library('glib-2.0')
35
+ crash "libglib-2.0 needed"
36
+ end
37
+
38
+ unless have_library("X11")
39
+ crash "libX11 needed"
40
+ end
41
+
42
+ unless find_header('X11/Xlib.h', "/usr/include", "/usr/local/include")
43
+ crash "X11/Xlib.h needed"
44
+ end
45
+
46
+ unless have_library('usb')
47
+ crash "libusb needed"
48
+ end
49
+
50
+ unless find_header('usb.h', "/usr/include", "/usr/local/include")
51
+ crash "usb.h needed"
52
+ end
53
+
54
+ if ARGV.include?("-d")
55
+ $CFLAGS += " -D GYRUBY_DEBUG"
56
+ end
57
+
58
+ if ARGV.include?("-O0")
59
+ $CFLAGS.gsub!(/-O./, "-O0")
60
+ else
61
+ $CFLAGS.gsub!(/-O./, "-O3")
62
+ end
63
+
64
+ if ARGV.include?("-gdb")
65
+ $CFLAGS += " -g -gdwarf-2 -g3"
66
+ end
67
+
68
+ create_makefile("gyruby_ext")
data/ext/gyruby_ext.c ADDED
@@ -0,0 +1,409 @@
1
+ // gyruby - a ruby binding for the Gyration FiireChief remote control for LinuxMCE
2
+ // Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ //
4
+ // This program is free software; you can redistribute it and/or
5
+ // modify it under the terms of the GNU General Public License
6
+ // as published by the Free Software Foundation; either version 2
7
+ // of the License, or (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program; if not, write to the Free Software
16
+ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ #include <usb.h>
19
+ #include <stdio.h>
20
+ #include "ruby.h"
21
+ #include <glib.h>
22
+ #include <X11/Xlib.h>
23
+
24
+ #define RB_REMOTE(c_remote_pointer,klass) (c_remote_pointer->rb_remote = (Data_Wrap_Struct(klass, c_remote_mark, c_remote_free, (c_remote_pointer))))
25
+ #define C_REMOTE(rb_remote,c_remote_pointer) Data_Get_Struct((rb_remote), c_remote, (c_remote_pointer))
26
+
27
+ #define PACKET_SIZE 6
28
+ #define BUFFER_UNIT 1024
29
+
30
+ typedef struct {
31
+ usb_dev_handle *handle;
32
+ GHashTable *subscriptions;
33
+ VALUE rb_remote;
34
+ Display *display;
35
+ VALUE display_name;
36
+ Window root;
37
+ ID grab_callback;
38
+ } c_remote;
39
+
40
+ typedef gpointer
41
+ (*gyration_data_handler)(gint result, gchar *data, gpointer user_data);
42
+
43
+ typedef gpointer
44
+ (*grab_data_handler)(gint x, gint y, gpointer user_data);
45
+
46
+ static VALUE rb_remote;
47
+
48
+ static gchar*
49
+ c_remote_inspect(c_remote *remote) {
50
+ gchar *buffer = g_new(gchar, BUFFER_UNIT);
51
+ snprintf(buffer, BUFFER_UNIT, "<%s:%p>",
52
+ rb_obj_classname(remote->rb_remote),
53
+ remote);
54
+ return buffer;
55
+ }
56
+
57
+ static void
58
+ c_remote_free(c_remote *remote) {
59
+ g_hash_table_destroy(remote->subscriptions);
60
+ if (remote->handle != NULL) {
61
+ if (usb_close(remote->handle) < 0) {
62
+ gchar *buffer = c_remote_inspect(remote);
63
+ fprintf(stderr, "Unable to close usb handle! Freeing %s anyway.", buffer);
64
+ free(buffer);
65
+ }
66
+ }
67
+ free(remote);
68
+ }
69
+
70
+ static void
71
+ g_hash_table_mark_value_pointers(gpointer key, gpointer value, gpointer user_data) {
72
+ VALUE *callback = (VALUE *) value;
73
+ rb_gc_mark(*callback);
74
+ }
75
+
76
+ static void
77
+ c_remote_mark(c_remote *remote) {
78
+ g_hash_table_foreach(remote->subscriptions, g_hash_table_mark_value_pointers, NULL);
79
+ rb_gc_mark(remote->grab_callback);
80
+ rb_gc_mark(remote->display_name);
81
+ }
82
+
83
+ static VALUE
84
+ rb_remote_inspect(VALUE self) {
85
+ c_remote *this;
86
+ gchar *buffer;
87
+ VALUE rval;
88
+ C_REMOTE(self, this);
89
+ buffer = c_remote_inspect(this);
90
+ rval = rb_str_new2(buffer);
91
+ free(buffer);
92
+ return rval;
93
+ }
94
+
95
+ static guint
96
+ subscription_hash(gconstpointer key) {
97
+ gchar *subscription = (gchar *) key;
98
+ guint hash = 5381;
99
+
100
+ for (int i = 0; i < (sizeof(gint) / sizeof(gchar)) + PACKET_SIZE; i++)
101
+ hash = ((hash << 5) + hash) + subscription[i]; /* hash * 33 + subscription[i] */
102
+
103
+ return hash;
104
+ }
105
+
106
+ static gboolean
107
+ subscription_equal(gconstpointer a, gconstpointer b) {
108
+ return memcmp(a, b, sizeof(gchar) * ((sizeof(gint) / sizeof(gchar)) + PACKET_SIZE)) == 0;
109
+ }
110
+
111
+ static VALUE
112
+ rb_remote_alloc(VALUE class) {
113
+ c_remote *remote = ALLOC(c_remote);
114
+ remote->handle = NULL;
115
+ remote->subscriptions = g_hash_table_new_full(subscription_hash, subscription_equal, free, free);
116
+ remote->display = NULL;
117
+ remote->display_name = Qnil;
118
+ return RB_REMOTE(remote, class);
119
+ }
120
+
121
+ static VALUE
122
+ rb_gyruby_set_debug(VALUE self, VALUE arg) {
123
+ Check_Type(arg, T_FIXNUM);
124
+ usb_set_debug(NUM2INT(arg));
125
+ return arg;
126
+ }
127
+
128
+ static void
129
+ c_remote_fetch_handle(c_remote *remote) {
130
+ if (remote->handle == NULL) {
131
+ struct usb_bus *busses = usb_get_busses();
132
+ for (struct usb_bus *bus = busses; bus; bus = bus->next) {
133
+ for (struct usb_device *dev = bus->devices; dev; dev = dev->next) {
134
+ if (dev->descriptor.idVendor == 0x0c16 &&
135
+ dev->descriptor.idProduct == 0x0006) {
136
+ remote->handle = usb_open(dev);
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ static void
144
+ c_remote_claim_device(c_remote *remote) {
145
+ c_remote_fetch_handle(remote);
146
+ if (remote->handle) {
147
+ if (usb_claim_interface(remote->handle, 1) < 0) {
148
+ char dname[32];
149
+ if (usb_get_driver_np(remote->handle, 1, dname, 31) == 0) {
150
+ if (usb_detach_kernel_driver_np(remote->handle, 1) < 0) {
151
+ rb_raise(rb_eRuntimeError, "Failed claiming device. Also failed detaching kernel driver for device.");
152
+ } else {
153
+ if (usb_claim_interface(remote->handle, 1) < 0) {
154
+ rb_raise(rb_eRuntimeError, "Failed claiming device. Succeded in detaching kernel driver for device. But still failed claiming device.");
155
+ }
156
+ }
157
+ } else {
158
+ rb_raise(rb_eRuntimeError, "Failed claiming device. Also failed getting kernel driver for device.");
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ static gpointer
165
+ c_remote_handle_usb_data(c_remote *remote, gyration_data_handler handler, gpointer user_data) {
166
+ char inpacket[PACKET_SIZE];
167
+ for (gint i = 0; i < PACKET_SIZE; i++)
168
+ inpacket[i] = 0;
169
+ gint result = usb_interrupt_read(remote->handle,
170
+ 0x82,
171
+ inpacket,
172
+ PACKET_SIZE,
173
+ 50);
174
+ if (result < 0 && result != -110) {
175
+ rb_raise(rb_eRuntimeError, "Got result %i.", result);
176
+ }
177
+ return handler(result, inpacket, user_data);
178
+ }
179
+
180
+ static Bool
181
+ true_predicate(Display *display, XEvent *event, XPointer arg) {
182
+ return True;
183
+ }
184
+
185
+ static gpointer
186
+ c_remote_handle_grab_data(c_remote *remote, grab_data_handler handler, gpointer user_data) {
187
+ XEvent event;
188
+ if (XCheckIfEvent(remote->display, &event, true_predicate, NULL) == True)
189
+ return handler(event.xmotion.x_root, event.xmotion.y_root, user_data);
190
+ else
191
+ return NULL;
192
+ }
193
+
194
+ static gpointer
195
+ c_remote_handle_data_until(c_remote *remote, gyration_data_handler gyration_handler, grab_data_handler grab_handler, gpointer user_data) {
196
+ gpointer rval = NULL;
197
+ struct timeval wait_time = { 0, 1 };
198
+ c_remote_claim_device(remote);
199
+ while (1) {
200
+ if (gyration_handler != NULL &&
201
+ remote->handle != NULL &&
202
+ (rval = c_remote_handle_usb_data(remote, gyration_handler, user_data)) != NULL)
203
+ return rval;
204
+ if (grab_handler != NULL &&
205
+ remote->display != NULL &&
206
+ (rval = c_remote_handle_grab_data(remote, grab_handler, user_data)) != NULL)
207
+ return rval;
208
+ rb_thread_wait_for(wait_time);
209
+ }
210
+ return rval;
211
+ }
212
+
213
+ static gchar*
214
+ subscription_from_result_and_packet(gint result, gchar *packet) {
215
+ gchar *subscription = malloc(sizeof(gint) + (sizeof(gchar) * PACKET_SIZE));
216
+ memcpy(subscription,
217
+ &result,
218
+ sizeof(gint));
219
+ memcpy(subscription + (sizeof(gint) / sizeof(gchar)),
220
+ packet,
221
+ sizeof(gchar) * 6);
222
+ return subscription;
223
+ }
224
+
225
+ static gpointer
226
+ c_remote_yield_gyration_data(gint result, gchar *packet, gpointer user_data) {
227
+ VALUE result_value = INT2NUM(result);
228
+ VALUE result_string = rb_str_new(packet, PACKET_SIZE);
229
+ VALUE rval;
230
+ if ((rval = rb_yield_values(2, result_value, result_string)) == Qnil)
231
+ return NULL;
232
+ else
233
+ return GINT_TO_POINTER(rval);
234
+ }
235
+
236
+ static gpointer
237
+ c_remote_call_grab_handler(gint x, gint y, gpointer user_data) {
238
+ c_remote *remote = (c_remote *) user_data;
239
+ VALUE rval;
240
+ if ((rval = rb_funcall(remote->rb_remote,
241
+ remote->grab_callback,
242
+ 2,
243
+ INT2NUM(x),
244
+ INT2NUM(y))) == Qnil)
245
+ return NULL;
246
+ else
247
+ return GINT_TO_POINTER(rval);
248
+ }
249
+
250
+ static gpointer
251
+ c_remote_call_subscription(gint result, gchar *packet, gpointer user_data) {
252
+ gchar *subscription = subscription_from_result_and_packet(result, packet);
253
+ c_remote *remote = (c_remote *) user_data;
254
+ ID *callback;
255
+ if ((callback = g_hash_table_lookup(remote->subscriptions, subscription)) != NULL) {
256
+ VALUE rval;
257
+ if ((rval = rb_funcall(remote->rb_remote,
258
+ *callback,
259
+ 2,
260
+ INT2NUM(result),
261
+ rb_str_new(packet, PACKET_SIZE))) == Qnil)
262
+ return NULL;
263
+ else
264
+ return GINT_TO_POINTER(rval);
265
+ } else {
266
+ return NULL;
267
+ }
268
+ }
269
+
270
+ static VALUE
271
+ rb_remote_listen(VALUE self) {
272
+ c_remote *this;
273
+ gpointer rval;
274
+ C_REMOTE(self, this);
275
+ if ((rval = c_remote_handle_data_until(this, c_remote_call_subscription, c_remote_call_grab_handler, this)) == NULL)
276
+ return Qnil;
277
+ else
278
+ return (VALUE) GPOINTER_TO_INT(rval);
279
+ }
280
+
281
+ static VALUE
282
+ rb_remote_record(VALUE self) {
283
+ c_remote *this;
284
+ gpointer rval;
285
+ C_REMOTE(self, this);
286
+ if ((rval = c_remote_handle_data_until(this, c_remote_yield_gyration_data, NULL, NULL)) == NULL)
287
+ return Qnil;
288
+ else
289
+ return (VALUE) GPOINTER_TO_INT(rval);
290
+ }
291
+
292
+ static VALUE
293
+ rb_remote_subscribe(VALUE self, VALUE result_value, VALUE result_string, VALUE callback) {
294
+ c_remote *this;
295
+ gchar *subscription;
296
+ ID *callback_id;
297
+ Check_Type(result_value, T_FIXNUM);
298
+ Check_Type(result_string, T_STRING);
299
+ Check_Type(callback, T_SYMBOL);
300
+ C_REMOTE(self, this);
301
+ callback_id = ALLOC(ID);
302
+ *callback_id = rb_to_id(callback);
303
+ subscription = subscription_from_result_and_packet(NUM2INT(result_value),
304
+ RSTRING(result_string)->ptr);
305
+ g_hash_table_insert(this->subscriptions, subscription, callback_id);
306
+ return self;
307
+ }
308
+
309
+ static void
310
+ g_hash_table_copy_to_rb_hash(gpointer key, gpointer value, gpointer user_data) {
311
+ VALUE *hash = (VALUE *) user_data;
312
+ gchar *subscription = (gchar *) key;
313
+ ID *callback_id = (ID *) value;
314
+ VALUE ary = rb_ary_new();
315
+ gint result;
316
+ gchar packet[PACKET_SIZE];
317
+ memcpy(&result, subscription, sizeof(gint));
318
+ memcpy(packet, subscription + (sizeof(gint) / sizeof(gchar)), sizeof(gchar) * PACKET_SIZE);
319
+ rb_ary_push(ary, INT2NUM(result));
320
+ rb_ary_push(ary, rb_str_new(packet, PACKET_SIZE));
321
+ rb_hash_aset(*hash, ID2SYM(*callback_id), ary);
322
+ }
323
+
324
+ static VALUE
325
+ rb_remote_subscriptions(VALUE self) {
326
+ c_remote *this;
327
+ VALUE rval;
328
+ C_REMOTE(self, this);
329
+ rval = rb_hash_new();
330
+ g_hash_table_foreach(this->subscriptions, g_hash_table_copy_to_rb_hash, &rval);
331
+ return rval;
332
+ }
333
+
334
+ static VALUE
335
+ rb_remote_get_grab(VALUE self) {
336
+ c_remote *this;
337
+ VALUE rval;
338
+ C_REMOTE(self, this);
339
+ if (this->display == NULL)
340
+ return Qnil;
341
+ rval = rb_ary_new();
342
+ rb_ary_push(rval, this->display_name);
343
+ rb_ary_push(rval, ID2SYM(this->grab_callback));
344
+ return rval;
345
+ }
346
+
347
+ static VALUE
348
+ rb_remote_grab(VALUE self, VALUE display_name_value, VALUE callback) {
349
+ c_remote *this;
350
+ Check_Type(display_name_value, T_STRING);
351
+ C_REMOTE(self, this);
352
+ this->display = XOpenDisplay(RSTRING(display_name_value)->ptr);
353
+ this->root = XDefaultRootWindow(this->display);
354
+ if (XGrabPointer(this->display,
355
+ this->root,
356
+ False,
357
+ PointerMotionMask,
358
+ GrabModeAsync,
359
+ GrabModeAsync,
360
+ None,
361
+ None,
362
+ CurrentTime) != GrabSuccess) {
363
+ this->display = NULL;
364
+ rb_raise(rb_eRuntimeError, "Failed grabbing the pointer for the root window at %s.", RSTRING(display_name_value)->ptr);
365
+ }
366
+ this->grab_callback = rb_to_id(callback);
367
+ this->display_name = display_name_value;
368
+ return display_name_value;
369
+ }
370
+
371
+ static VALUE
372
+ rb_remote_release(VALUE self) {
373
+ c_remote *this;
374
+ C_REMOTE(self, this);
375
+ if (this->display == NULL)
376
+ rb_raise(rb_eRuntimeError, "You can't release a %s that isn't grabbed.", rb_obj_classname(self));
377
+ XUngrabPointer(this->display, CurrentTime);
378
+ XCloseDisplay(this->display);
379
+ this->display = NULL;
380
+ return self;
381
+ }
382
+
383
+ #ifdef __cplusplus
384
+ extern "C" {
385
+ #endif
386
+ void Init_gyruby_ext() {
387
+ usb_init();
388
+ usb_find_busses();
389
+ usb_find_devices();
390
+
391
+ VALUE rb_gyruby = rb_define_module("Gyruby");
392
+ rb_define_singleton_method(rb_gyruby, "debug=", rb_gyruby_set_debug, 1);
393
+ rb_remote = rb_define_class_under(rb_gyruby,
394
+ "Remote",
395
+ rb_cObject);
396
+ rb_define_alloc_func(rb_remote, rb_remote_alloc);
397
+ rb_define_method(rb_remote, "record", rb_remote_record, 0);
398
+ rb_define_method(rb_remote, "subscribe", rb_remote_subscribe, 3);
399
+ rb_define_method(rb_remote, "inspect", rb_remote_inspect, 0);
400
+ rb_define_method(rb_remote, "subscriptions", rb_remote_subscriptions, 0);
401
+ rb_define_method(rb_remote, "listen", rb_remote_listen, 0);
402
+ rb_define_method(rb_remote, "grab", rb_remote_grab, 2);
403
+ rb_define_method(rb_remote, "grabbed?", rb_remote_get_grab, 0);
404
+ rb_define_method(rb_remote, "release", rb_remote_release, 0);
405
+ }
406
+ #ifdef __cplusplus
407
+ }
408
+ #endif
409
+
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gyruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Martin Kihlgren
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-05-23 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: gyruby at troja dot ath dot cx
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - ext/extconf.rb
26
+ - ext/gyruby_ext.c
27
+ has_rdoc: true
28
+ homepage:
29
+ post_install_message:
30
+ rdoc_options:
31
+ - --line-numbers
32
+ - --inline-source
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: "0"
40
+ version:
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ requirements: []
48
+
49
+ rubyforge_project:
50
+ rubygems_version: 1.0.1
51
+ signing_key:
52
+ specification_version: 2
53
+ summary: A ruby binding for the Gyration FiireChief remote control for LinuxMCE.
54
+ test_files: []
55
+