gyruby 0.0.1

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 +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
+