gyruby 0.0.2 → 0.1.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.
data/ext/extconf.rb CHANGED
@@ -29,7 +29,7 @@ unless find_executable("pkg-config")
29
29
  end
30
30
 
31
31
  $CFLAGS += " -std=c99 -Wall -I. " + `pkg-config --cflags glib-2.0`.strip
32
- $LIBS += " " + `pkg-config --libs glib-2.0`
32
+ $LIBS += " " + `pkg-config --libs gthread-2.0`
33
33
 
34
34
  unless have_library('glib-2.0')
35
35
  crash "libglib-2.0 needed"
data/ext/gyruby_ext.c CHANGED
@@ -19,7 +19,9 @@
19
19
  #include <stdio.h>
20
20
  #include "ruby.h"
21
21
  #include <glib.h>
22
+ #include <stdlib.h>
22
23
  #include <X11/Xlib.h>
24
+ #include <errno.h>
23
25
 
24
26
  #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
27
  #define C_REMOTE(rb_remote,c_remote_pointer) Data_Get_Struct((rb_remote), c_remote, (c_remote_pointer))
@@ -27,15 +29,37 @@
27
29
  #define PACKET_SIZE 6
28
30
  #define BUFFER_UNIT 1024
29
31
 
32
+ extern gint errno;
33
+ extern gint sys_nerr;
34
+ extern const char *sys_errlist[];
35
+
30
36
  typedef struct {
31
- usb_dev_handle *usb_handle; // is NULL if we didnt find the device. will be automatically looked up. will not be looked up again after having been set once
32
- GHashTable *subscriptions; // is initialized as a GHashTable, and destroyed on c_remote_free
33
- VALUE rb_remote; // the ruby VALUE for this struct
34
- Display *display; // is NULL if we havent connected to a display yet. will be looked up on grab and reset to NULL on release
35
- VALUE display_name; // is Qnil if we havent connected to a display yet. will be set on grab and reset to Qnil on release
36
- ID grab_callback; // is whatever until connect to a display, can be anything, except when display is set - in which case it is set to the ID of the callback for pointer movements
37
+ /*
38
+ * The ruby representation of this object.
39
+ */
40
+ VALUE rb_remote;
41
+
42
+ /*
43
+ * Mouse capture runtime data.
44
+ */
45
+ Display *display;
46
+
47
+ /*
48
+ * USB capture runtime data.
49
+ */
50
+ usb_dev_handle *usb_handle;
51
+ GAsyncQueue *usb_event_queue;
52
+ gboolean run_usb_thread;
53
+ GThread *usb_thread;
37
54
  } c_remote;
38
55
 
56
+ typedef struct {
57
+ gint x;
58
+ gint y;
59
+ gint xmax;
60
+ gint ymax;
61
+ } mouse_event;
62
+
39
63
  typedef VALUE
40
64
  (*gyration_data_handler)(gint result, gchar *data, gpointer user_data);
41
65
 
@@ -44,6 +68,15 @@ typedef VALUE
44
68
 
45
69
  static VALUE rb_remote;
46
70
 
71
+ static const gchar*
72
+ errmsg() {
73
+ if (errno < sys_nerr) {
74
+ return sys_errlist[errno];
75
+ } else {
76
+ return "UNKNOWN ERROR";
77
+ }
78
+ }
79
+
47
80
  static gchar*
48
81
  c_remote_inspect(c_remote *remote) {
49
82
  gchar *buffer = g_new(gchar, BUFFER_UNIT);
@@ -54,29 +87,37 @@ c_remote_inspect(c_remote *remote) {
54
87
  }
55
88
 
56
89
  static void
57
- c_remote_free(c_remote *remote) {
58
- g_hash_table_destroy(remote->subscriptions);
59
- if (remote->usb_handle != NULL) {
60
- if (usb_close(remote->usb_handle) < 0) {
61
- gchar *buffer = c_remote_inspect(remote);
62
- fprintf(stderr, "Unable to close usb handle! Freeing %s anyway.", buffer);
63
- free(buffer);
64
- }
65
- }
66
- free(remote);
90
+ unclaim_device_handle(usb_dev_handle *handle) {
91
+ if (usb_release_interface(handle, 1) < 0)
92
+ rb_raise(rb_eRuntimeError, "Failed releasing device: %s", errmsg());
93
+ if (usb_close(handle) < 0)
94
+ rb_raise(rb_eRuntimeError, "Failed closing device: %s", errmsg());
67
95
  }
68
96
 
69
97
  static void
70
- g_hash_table_mark_value_pointers(gpointer key, gpointer value, gpointer user_data) {
71
- VALUE *callback = (VALUE *) value;
72
- rb_gc_mark(*callback);
98
+ c_remote_unclaim_device(c_remote *remote) {
99
+ remote->run_usb_thread = FALSE;
100
+ if (g_thread_self() != remote->usb_thread)
101
+ g_thread_join(remote->usb_thread);
102
+ unclaim_device_handle(remote->usb_handle);
103
+ remote->usb_handle = NULL;
104
+ remote->usb_thread = NULL;
105
+ g_async_queue_unref(remote->usb_event_queue);
73
106
  }
74
107
 
75
108
  static void
76
- c_remote_mark(c_remote *remote) {
77
- g_hash_table_foreach(remote->subscriptions, g_hash_table_mark_value_pointers, NULL);
78
- rb_gc_mark(remote->grab_callback);
79
- rb_gc_mark(remote->display_name);
109
+ ungrab_mouse(Display *display) {
110
+ XUngrabPointer(display, CurrentTime);
111
+ XCloseDisplay(display);
112
+ }
113
+
114
+ static void
115
+ c_remote_free(c_remote *remote) {
116
+ if (remote->display != NULL)
117
+ ungrab_mouse(remote->display);
118
+ if (remote->usb_handle != NULL)
119
+ c_remote_unclaim_device(remote);
120
+ free(remote);
80
121
  }
81
122
 
82
123
  static VALUE
@@ -91,29 +132,19 @@ rb_remote_inspect(VALUE self) {
91
132
  return rval;
92
133
  }
93
134
 
94
- static guint
95
- subscription_hash(gconstpointer key) {
96
- gchar *subscription = (gchar *) key;
97
- guint hash = 5381;
98
-
99
- for (int i = 0; i < (sizeof(gint) / sizeof(gchar)) + PACKET_SIZE; i++)
100
- hash = ((hash << 5) + hash) + subscription[i]; /* hash * 33 + subscription[i] */
101
-
102
- return hash;
103
- }
104
-
105
- static gboolean
106
- subscription_equal(gconstpointer a, gconstpointer b) {
107
- return memcmp(a, b, sizeof(gchar) * ((sizeof(gint) / sizeof(gchar)) + PACKET_SIZE)) == 0;
135
+ static void
136
+ c_remote_mark(c_remote *remote) {
108
137
  }
109
138
 
110
139
  static VALUE
111
140
  rb_remote_alloc(VALUE class) {
112
141
  c_remote *remote = ALLOC(c_remote);
113
- remote->usb_handle = NULL;
114
- remote->subscriptions = g_hash_table_new_full(subscription_hash, subscription_equal, free, free);
115
142
  remote->display = NULL;
116
- remote->display_name = Qnil;
143
+
144
+ remote->usb_handle = NULL;
145
+ remote->usb_event_queue = NULL;
146
+ remote->run_usb_thread = FALSE;
147
+ remote->usb_thread = NULL;
117
148
  return RB_REMOTE(remote, class);
118
149
  }
119
150
 
@@ -124,56 +155,96 @@ rb_gyruby_set_debug(VALUE self, VALUE arg) {
124
155
  return arg;
125
156
  }
126
157
 
127
- static void
128
- c_remote_fetch_handle(c_remote *remote) {
129
- if (remote->usb_handle == NULL) {
130
- struct usb_bus *busses = usb_get_busses();
131
- for (struct usb_bus *bus = busses; bus; bus = bus->next) {
132
- for (struct usb_device *dev = bus->devices; dev; dev = dev->next) {
133
- if (dev->descriptor.idVendor == 0x0c16 &&
134
- dev->descriptor.idProduct == 0x0006) {
135
- remote->usb_handle = usb_open(dev);
136
- }
158
+ static VALUE
159
+ rb_remote_unclaim_device(VALUE self) {
160
+ c_remote *this;
161
+ C_REMOTE(self, this);
162
+ if (this->usb_handle == NULL) {
163
+ return Qfalse;
164
+ } else {
165
+ c_remote_unclaim_device(this);
166
+ return Qtrue;
167
+ }
168
+ }
169
+
170
+ static usb_dev_handle*
171
+ fetch_device_handle() {
172
+ struct usb_bus *busses = usb_get_busses();
173
+ for (struct usb_bus *bus = busses; bus; bus = bus->next) {
174
+ for (struct usb_device *dev = bus->devices; dev; dev = dev->next) {
175
+ if (dev->descriptor.idVendor == 0x0c16 &&
176
+ dev->descriptor.idProduct == 0x0006) {
177
+ return usb_open(dev);
137
178
  }
138
179
  }
139
180
  }
181
+ return NULL;
140
182
  }
141
183
 
142
- static void
143
- c_remote_claim_device(c_remote *remote) {
144
- c_remote_fetch_handle(remote);
145
- if (remote->usb_handle) {
146
- if (usb_claim_interface(remote->usb_handle, 1) < 0) {
184
+ static usb_dev_handle*
185
+ claim_device_handle() {
186
+ usb_dev_handle *handle = fetch_device_handle();
187
+ if (handle == NULL) {
188
+ rb_raise(rb_eRuntimeError, "Failed finding device. Do you have permission to list it? Is it plugged in?");
189
+ } else {
190
+ if (usb_claim_interface(handle, 1) < 0) {
147
191
  char dname[32];
148
- if (usb_get_driver_np(remote->usb_handle, 1, dname, 31) == 0) {
149
- if (usb_detach_kernel_driver_np(remote->usb_handle, 1) < 0) {
150
- rb_raise(rb_eRuntimeError, "Failed claiming device. Also failed detaching kernel driver for device.");
192
+ if (usb_get_driver_np(handle, 1, dname, 31) == 0) {
193
+ if (usb_detach_kernel_driver_np(handle, 1) < 0) {
194
+ rb_raise(rb_eRuntimeError,
195
+ "Failed claiming device. Also failed detaching kernel driver for device: %s", errmsg());
151
196
  } else {
152
- if (usb_claim_interface(remote->usb_handle, 1) < 0) {
153
- rb_raise(rb_eRuntimeError, "Failed claiming device. Succeded in detaching kernel driver for device. But still failed claiming device.");
197
+ if (usb_claim_interface(handle, 1) < 0) {
198
+ rb_raise(rb_eRuntimeError,
199
+ "Failed claiming device. Succeded in detaching kernel driver for device. But still failed claiming device: %s", errmsg());
154
200
  }
155
201
  }
156
202
  } else {
157
- rb_raise(rb_eRuntimeError, "Failed claiming device. Also failed getting kernel driver for device.");
203
+ rb_raise(rb_eRuntimeError,
204
+ "Failed claiming device. Also failed getting kernel driver for device: %s", errmsg());
158
205
  }
159
206
  }
160
207
  }
208
+ return handle;
209
+ }
210
+
211
+ static gpointer
212
+ read_usb(gpointer arg) {
213
+ c_remote *remote = (c_remote *) arg;
214
+ gchar inpacket[PACKET_SIZE];
215
+ while (remote->run_usb_thread) {
216
+ gint result = usb_interrupt_read(remote->usb_handle,
217
+ 0x82,
218
+ inpacket,
219
+ PACKET_SIZE,
220
+ 50);
221
+ if (result < 0 && result != -110) {
222
+ fprintf(stderr, "Weird error: The result code from usb_interrupt_read was: %i\n", result);
223
+ c_remote_unclaim_device(remote);
224
+ } else if (result != -110) {
225
+ gchar *packet_copy = g_new(gchar, PACKET_SIZE);
226
+ memcpy(packet_copy, inpacket, sizeof(gchar) * PACKET_SIZE);
227
+ g_async_queue_push(remote->usb_event_queue, packet_copy);
228
+ }
229
+ }
230
+ return NULL;
161
231
  }
162
232
 
233
+
163
234
  static VALUE
164
- c_remote_handle_usb_data(c_remote *remote, gyration_data_handler handler, gpointer user_data) {
165
- char inpacket[PACKET_SIZE];
166
- for (gint i = 0; i < PACKET_SIZE; i++)
167
- inpacket[i] = 0;
168
- gint result = usb_interrupt_read(remote->usb_handle,
169
- 0x82,
170
- inpacket,
171
- PACKET_SIZE,
172
- 50);
173
- if (result < 0 && result != -110) {
174
- rb_raise(rb_eRuntimeError, "Got result %i.", result);
235
+ rb_remote_claim_device(VALUE self) {
236
+ c_remote *this;
237
+ C_REMOTE(self, this);
238
+ if (this->usb_handle == NULL) {
239
+ this->usb_handle = claim_device_handle();
240
+
241
+ this->usb_event_queue = g_async_queue_new_full(free);
242
+ this->run_usb_thread = TRUE;
243
+ this->usb_thread = g_thread_create(read_usb, this, TRUE, NULL);
244
+ return Qtrue;
245
+ } else {
246
+ return Qfalse;
175
247
  }
176
- return handler(result, inpacket, user_data);
177
248
  }
178
249
 
179
250
  static Bool
@@ -181,222 +252,135 @@ true_predicate(Display *display, XEvent *event, XPointer arg) {
181
252
  return True;
182
253
  }
183
254
 
184
- static VALUE
185
- c_remote_handle_grab_data(c_remote *remote, grab_data_handler handler, gpointer user_data) {
255
+ static mouse_event*
256
+ fetch_mouse_event(Display *display) {
186
257
  XEvent event;
187
- if (XCheckIfEvent(remote->display, &event, true_predicate, NULL) == True &&
258
+ if (XCheckIfEvent(display, &event, true_predicate, NULL) == True &&
188
259
  event.xmotion.same_screen == True) {
189
- Screen *screen = XDefaultScreenOfDisplay(remote->display);
190
- return handler(event.xmotion.x_root, event.xmotion.y_root, WidthOfScreen(screen), HeightOfScreen(screen), user_data);
260
+ mouse_event *rval = ALLOC(mouse_event);
261
+ Screen *screen = XDefaultScreenOfDisplay(display);
262
+ rval->x = event.xmotion.x_root;
263
+ rval->y = event.xmotion.y_root;
264
+ rval->xmax = WidthOfScreen(screen);
265
+ rval->ymax = HeightOfScreen(screen);
266
+ return rval;
191
267
  } else {
192
- return Qnil;
268
+ return NULL;
193
269
  }
194
270
  }
195
271
 
196
- static void
197
- c_remote_grab_pointer(c_remote *remote) {
198
- if (remote->display_name != Qnil) {
199
- remote->display = XOpenDisplay(RSTRING(remote->display_name)->ptr);
200
- if (XGrabPointer(remote->display,
201
- XDefaultRootWindow(remote->display),
202
- False,
203
- PointerMotionMask,
204
- GrabModeAsync,
205
- GrabModeAsync,
206
- None,
207
- None,
208
- CurrentTime) != GrabSuccess) {
209
- remote->display = NULL;
210
- rb_raise(rb_eRuntimeError, "Failed grabbing the pointer for the root window at %s.", RSTRING(remote->display_name)->ptr);
272
+ static VALUE
273
+ rb_remote_fetch_mouse_event(VALUE self) {
274
+ c_remote *this;
275
+ C_REMOTE(self, this);
276
+ if (this->display == NULL) {
277
+ rb_raise(rb_eRuntimeError,
278
+ "You can't fetch mouse events from a %s that hasn't grabbed the pointer.",
279
+ rb_obj_classname(self));
280
+ } else {
281
+ mouse_event *event = fetch_mouse_event(this->display);
282
+ if (event == NULL) {
283
+ return Qnil;
284
+ } else {
285
+ VALUE rval = rb_hash_new();
286
+ rb_hash_aset(rval, ID2SYM(rb_intern("x")), INT2NUM(event->x));
287
+ rb_hash_aset(rval, ID2SYM(rb_intern("y")), INT2NUM(event->y));
288
+ rb_hash_aset(rval, ID2SYM(rb_intern("xmax")), INT2NUM(event->xmax));
289
+ rb_hash_aset(rval, ID2SYM(rb_intern("ymax")), INT2NUM(event->ymax));
290
+ free(event);
291
+ return rval;
211
292
  }
212
293
  }
213
294
  }
214
295
 
215
296
  static VALUE
216
- c_remote_handle_data_loop(VALUE arg) {
217
- gpointer *args = (gpointer *) GINT_TO_POINTER(arg);
218
- c_remote *remote = (c_remote *) args[0];
219
- gyration_data_handler gyration_handler = (gyration_data_handler) args[1];
220
- grab_data_handler grab_handler = (grab_data_handler) args[2];
221
- gpointer user_data = args[3];
222
- VALUE rval = Qnil;
223
- struct timeval wait_time = { 0, 1 };
224
- while (1) {
225
- if (gyration_handler != NULL &&
226
- remote->usb_handle != NULL &&
227
- (rval = c_remote_handle_usb_data(remote, gyration_handler, user_data)) != Qnil)
228
- return rval;
229
- if (grab_handler != NULL &&
230
- remote->display_name != Qnil &&
231
- (rval = c_remote_handle_grab_data(remote, grab_handler, user_data)) != Qnil)
297
+ rb_remote_fetch_usb_event(VALUE self) {
298
+ c_remote *this;
299
+ C_REMOTE(self, this);
300
+ if (this->usb_event_queue == NULL) {
301
+ rb_raise(rb_eRuntimeError,
302
+ "You can't fetch usb events from a %s that hasn't claimed the device.",
303
+ rb_obj_classname(self));
304
+ } else {
305
+ gpointer next_event = g_async_queue_try_pop(this->usb_event_queue);
306
+ if (next_event == NULL) {
307
+ return Qnil;
308
+ } else {
309
+ VALUE rval = rb_str_new((gchar *) next_event, 6);
310
+ free(next_event);
232
311
  return rval;
233
- rb_thread_wait_for(wait_time);
312
+ }
234
313
  }
235
- return rval;
236
314
  }
237
315
 
238
- static VALUE
239
- c_remote_release_pointer(VALUE arg) {
240
- c_remote *remote = (c_remote *) GINT_TO_POINTER(arg);
241
- if (remote->display != NULL) {
242
- XUngrabPointer(remote->display, CurrentTime);
243
- XCloseDisplay(remote->display);
244
- remote->display = NULL;
316
+ static Display*
317
+ grab_mouse() {
318
+ Display *display = XOpenDisplay(NULL);
319
+ if (XGrabPointer(display,
320
+ XDefaultRootWindow(display),
321
+ False,
322
+ PointerMotionMask,
323
+ GrabModeAsync,
324
+ GrabModeAsync,
325
+ None,
326
+ None,
327
+ CurrentTime) != GrabSuccess) {
328
+ rb_raise(rb_eRuntimeError, "Failed grabbing the pointer for the root window!");
245
329
  }
246
- return Qnil;
247
- }
248
-
249
- static VALUE
250
- c_remote_handle_data_until(c_remote *remote, gyration_data_handler gyration_handler, grab_data_handler grab_handler, gpointer user_data) {
251
- VALUE rval;
252
- gpointer loop_args[4] = { remote, gyration_handler, grab_handler, user_data };
253
- c_remote_claim_device(remote);
254
- c_remote_grab_pointer(remote);
255
- rval = rb_ensure(c_remote_handle_data_loop, GPOINTER_TO_INT(loop_args), c_remote_release_pointer, GPOINTER_TO_INT(remote));
256
- return rval;
257
- }
258
-
259
- static gchar*
260
- subscription_from_result_and_packet(gint result, gchar *packet) {
261
- gchar *subscription = malloc(sizeof(gint) + (sizeof(gchar) * PACKET_SIZE));
262
- memcpy(subscription,
263
- &result,
264
- sizeof(gint));
265
- memcpy(subscription + (sizeof(gint) / sizeof(gchar)),
266
- packet,
267
- sizeof(gchar) * 6);
268
- return subscription;
269
- }
270
-
271
- static VALUE
272
- c_remote_yield_gyration_data(gint result, gchar *packet, gpointer user_data) {
273
- VALUE result_value = INT2NUM(result);
274
- VALUE result_string = rb_str_new(packet, PACKET_SIZE);
275
- return rb_yield_values(2, result_value, result_string);
276
- }
277
-
278
- static VALUE
279
- c_remote_call_grab_handler(gint x, gint y, gint maxx, gint maxy, gpointer user_data) {
280
- c_remote *remote = (c_remote *) user_data;
281
- return rb_funcall(remote->rb_remote,
282
- remote->grab_callback,
283
- 4,
284
- INT2NUM(x),
285
- INT2NUM(y),
286
- INT2NUM(maxx),
287
- INT2NUM(maxy));
288
- }
289
-
290
- static VALUE
291
- c_remote_call_subscription(gint result, gchar *packet, gpointer user_data) {
292
- gchar *subscription = subscription_from_result_and_packet(result, packet);
293
- c_remote *remote = (c_remote *) user_data;
294
- ID *callback;
295
- if ((callback = g_hash_table_lookup(remote->subscriptions, subscription)) != NULL) {
296
- return rb_funcall(remote->rb_remote,
297
- *callback,
298
- 2,
299
- INT2NUM(result),
300
- rb_str_new(packet, PACKET_SIZE));
301
- } else {
302
- return Qnil;
303
- }
304
- }
305
-
306
- static VALUE
307
- rb_remote_listen(VALUE self) {
308
- c_remote *this;
309
- C_REMOTE(self, this);
310
- return c_remote_handle_data_until(this, c_remote_call_subscription, c_remote_call_grab_handler, this);
330
+ return display;
311
331
  }
312
332
 
313
333
  static VALUE
314
- rb_remote_record(VALUE self) {
334
+ rb_remote_ungrab_mouse(VALUE self) {
315
335
  c_remote *this;
316
336
  C_REMOTE(self, this);
317
- return c_remote_handle_data_until(this, c_remote_yield_gyration_data, NULL, NULL);
337
+ if (this->display == NULL) {
338
+ return Qfalse;
339
+ } else {
340
+ ungrab_mouse(this->display);
341
+ this->display = NULL;
342
+ return Qtrue;
343
+ }
318
344
  }
319
345
 
320
346
  static VALUE
321
- rb_remote_subscribe(VALUE self, VALUE result_value, VALUE result_string, VALUE callback) {
347
+ rb_remote_grab_mouse(VALUE self) {
322
348
  c_remote *this;
323
- gchar *subscription;
324
- ID *callback_id;
325
- Check_Type(result_value, T_FIXNUM);
326
- Check_Type(result_string, T_STRING);
327
- Check_Type(callback, T_SYMBOL);
328
349
  C_REMOTE(self, this);
329
- callback_id = ALLOC(ID);
330
- *callback_id = rb_to_id(callback);
331
- subscription = subscription_from_result_and_packet(NUM2INT(result_value),
332
- RSTRING(result_string)->ptr);
333
- g_hash_table_insert(this->subscriptions, subscription, callback_id);
334
- return self;
335
- }
336
-
337
- static void
338
- g_hash_table_copy_to_rb_hash(gpointer key, gpointer value, gpointer user_data) {
339
- VALUE *hash = (VALUE *) user_data;
340
- gchar *subscription = (gchar *) key;
341
- ID *callback_id = (ID *) value;
342
- VALUE ary = rb_ary_new();
343
- gint result;
344
- gchar packet[PACKET_SIZE];
345
- memcpy(&result, subscription, sizeof(gint));
346
- memcpy(packet, subscription + (sizeof(gint) / sizeof(gchar)), sizeof(gchar) * PACKET_SIZE);
347
- rb_ary_push(ary, INT2NUM(result));
348
- rb_ary_push(ary, rb_str_new(packet, PACKET_SIZE));
349
- rb_hash_aset(*hash, ID2SYM(*callback_id), ary);
350
+ if (this->display == NULL) {
351
+ this->display = grab_mouse();
352
+ return Qtrue;
353
+ } else {
354
+ return Qfalse;
355
+ }
350
356
  }
351
357
 
352
358
  static VALUE
353
- rb_remote_subscriptions(VALUE self) {
359
+ rb_remote_get_claimed_device(VALUE self) {
354
360
  c_remote *this;
355
- VALUE rval;
356
361
  C_REMOTE(self, this);
357
- rval = rb_hash_new();
358
- g_hash_table_foreach(this->subscriptions, g_hash_table_copy_to_rb_hash, &rval);
359
- return rval;
362
+ if (this->usb_handle == NULL)
363
+ return Qfalse;
364
+ else
365
+ return Qtrue;
360
366
  }
361
367
 
362
368
  static VALUE
363
- rb_remote_get_grab(VALUE self) {
369
+ rb_remote_get_grabbed_mouse(VALUE self) {
364
370
  c_remote *this;
365
- VALUE rval;
366
371
  C_REMOTE(self, this);
367
372
  if (this->display == NULL)
368
- return Qnil;
369
- rval = rb_ary_new();
370
- rb_ary_push(rval, this->display_name);
371
- rb_ary_push(rval, ID2SYM(this->grab_callback));
372
- return rval;
373
- }
374
-
375
- static VALUE
376
- rb_remote_grab(VALUE self, VALUE display_name_value, VALUE callback) {
377
- c_remote *this;
378
- Check_Type(display_name_value, T_STRING);
379
- Check_Type(callback, T_SYMBOL);
380
- C_REMOTE(self, this);
381
- this->grab_callback = rb_to_id(callback);
382
- this->display_name = display_name_value;
383
- return display_name_value;
384
- }
385
-
386
- static VALUE
387
- rb_remote_release(VALUE self) {
388
- c_remote *this;
389
- C_REMOTE(self, this);
390
- if (this->display_name == Qnil)
391
- rb_raise(rb_eRuntimeError, "You can't release a %s that isn't grabbed.", rb_obj_classname(self));
392
- this->display_name = Qnil;
393
- return self;
373
+ return Qfalse;
374
+ else
375
+ return Qtrue;
394
376
  }
395
377
 
396
378
  #ifdef __cplusplus
397
379
  extern "C" {
398
380
  #endif
399
381
  void Init_gyruby_ext() {
382
+ g_thread_init(NULL);
383
+
400
384
  usb_init();
401
385
  usb_find_busses();
402
386
  usb_find_devices();
@@ -406,15 +390,17 @@ extern "C" {
406
390
  rb_remote = rb_define_class_under(rb_gyruby,
407
391
  "Remote",
408
392
  rb_cObject);
393
+
409
394
  rb_define_alloc_func(rb_remote, rb_remote_alloc);
410
- rb_define_method(rb_remote, "record", rb_remote_record, 0);
411
- rb_define_method(rb_remote, "subscribe", rb_remote_subscribe, 3);
412
395
  rb_define_method(rb_remote, "inspect", rb_remote_inspect, 0);
413
- rb_define_method(rb_remote, "subscriptions", rb_remote_subscriptions, 0);
414
- rb_define_method(rb_remote, "listen", rb_remote_listen, 0);
415
- rb_define_method(rb_remote, "grab", rb_remote_grab, 2);
416
- rb_define_method(rb_remote, "grabbed?", rb_remote_get_grab, 0);
417
- rb_define_method(rb_remote, "release", rb_remote_release, 0);
396
+ rb_define_private_method(rb_remote, "grab_mouse", rb_remote_grab_mouse, 0);
397
+ rb_define_private_method(rb_remote, "ungrab_mouse", rb_remote_ungrab_mouse, 0);
398
+ rb_define_private_method(rb_remote, "claim_device", rb_remote_claim_device, 0);
399
+ rb_define_private_method(rb_remote, "unclaim_device", rb_remote_unclaim_device, 0);
400
+ rb_define_private_method(rb_remote, "fetch_usb_event", rb_remote_fetch_usb_event, 0);
401
+ rb_define_private_method(rb_remote, "fetch_mouse_event", rb_remote_fetch_mouse_event, 0);
402
+ rb_define_private_method(rb_remote, "claimed_device?", rb_remote_get_claimed_device, 0);
403
+ rb_define_private_method(rb_remote, "grabbed_mouse?", rb_remote_get_grabbed_mouse, 0);
418
404
  }
419
405
  #ifdef __cplusplus
420
406
  }
@@ -0,0 +1,88 @@
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
+ require 'pathname'
19
+
20
+ class MplayerController < Gyruby::Remote
21
+
22
+ def initialize(user)
23
+ super()
24
+ subscribe("\b%\377\351\000\000") do |s|
25
+ volume_up
26
+ end
27
+ subscribe("\b%\377\352\000\000") do |s|
28
+ volume_down
29
+ end
30
+ subscribe("\b%\377\234\000\000") do |s|
31
+ osd_up
32
+ end
33
+ subscribe("\b%\377\235\000\000") do |s|
34
+ osd_down
35
+ end
36
+ subscribe("\b%\377\313\000\000", "\b%\377\000\000\000") do |key, options|
37
+ seek(options[:x].to_f/ options[:xmax].to_f)
38
+ end
39
+ @osd = 0
40
+ @user = user
41
+ end
42
+
43
+ private
44
+
45
+ def send_command(cmd)
46
+ ctrl_path = Pathname.new(File.join("/home", @user, ".mplayer", "fifo"))
47
+ ctrl_path.open("a") do |ctrl|
48
+ ctrl.puts(cmd)
49
+ end if ctrl_path.exist? && mplayer_running?
50
+ end
51
+
52
+ def seek(f)
53
+ puts f.inspect
54
+ send_command("seek #{f * 100} 1")
55
+ end
56
+
57
+ def osd_up
58
+ @osd += 1 if @osd < 3
59
+ send_command("osd #{@osd}")
60
+ end
61
+
62
+ def osd_down
63
+ @osd -= 1 if @osd > 0
64
+ send_command("osd #{@osd}")
65
+ end
66
+
67
+ def volume_up
68
+ send_command("volume 10")
69
+ end
70
+
71
+ def volume_down
72
+ send_command("volume -10")
73
+ end
74
+
75
+ def mplayer_running?
76
+ pgrep_pid = fork do
77
+ STDOUT.reopen(open("/dev/null", "w"))
78
+ args = ["/usr/bin/env",
79
+ "pgrep",
80
+ "-U", @user,
81
+ "mplayer"]
82
+ exec(*args)
83
+ end
84
+ pid, status = Process.wait2(pgrep_pid)
85
+ status.exitstatus == 0
86
+ end
87
+
88
+ end
@@ -0,0 +1,92 @@
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
+ module Gyruby
19
+
20
+ class Remote
21
+
22
+ def initialize
23
+ @subscribed_buttons = {}
24
+ @pressed_buttons = {}
25
+ @mouse_buttons = {}
26
+ @listening = false
27
+ end
28
+
29
+ def subscribe(button, release = false, frequency = 10, &block)
30
+ if release
31
+ @mouse_buttons[button] = {
32
+ :action => :press,
33
+ :block => block,
34
+ :frequency => frequency,
35
+ :last => nil
36
+ }
37
+ @mouse_buttons[release] = {:action => :release, :press => button}
38
+ else
39
+ @subscribed_buttons[button] = block
40
+ end
41
+ end
42
+
43
+ def record(&block)
44
+ claim_device unless claimed_device?
45
+ loop do
46
+ event = fetch_usb_event
47
+ yield event unless event.nil?
48
+ sleep(0.001)
49
+ end
50
+ end
51
+
52
+ def listen
53
+ claim_device unless claimed_device?
54
+ loop do
55
+ handle_usb_event if claimed_device?
56
+ handle_mouse_event if grabbed_mouse?
57
+ sleep(0.001)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def handle_usb_event
64
+ event = fetch_usb_event
65
+ if block = @subscribed_buttons[event]
66
+ block.call(event)
67
+ end
68
+ if button_status = @mouse_buttons[event]
69
+ if button_status[:action] == :press
70
+ @pressed_buttons[event] = button_status
71
+ grab_mouse unless grabbed_mouse?
72
+ elsif button_status[:action] == :release
73
+ @pressed_buttons.delete(button_status[:press])
74
+ ungrab_mouse if grabbed_mouse? && @pressed_buttons.empty?
75
+ end
76
+ end
77
+ end
78
+
79
+ def handle_mouse_event
80
+ mouse_event = fetch_mouse_event
81
+ @pressed_buttons.each do |key_event, button_status|
82
+ button_status[:last] ||= Time.now
83
+ if (Time.now - button_status[:last]) > 1.0 / button_status[:frequency]
84
+ @mouse_buttons[key_event][:block].call(key_event, mouse_event)
85
+ button_status[:last] = Time.now
86
+ end
87
+ end unless mouse_event.nil?
88
+ end
89
+
90
+ end
91
+
92
+ end
data/lib/gyruby.rb CHANGED
@@ -1,2 +1,7 @@
1
1
 
2
- require 'gyruby_ext'
2
+ $: << File.join(File.dirname(__FILE__))
3
+
4
+ require 'gyruby_ext'
5
+ require 'gyruby/remote'
6
+ require 'gyruby/mplayer_controller'
7
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gyruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Kihlgren
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-23 00:00:00 +02:00
12
+ date: 2008-05-26 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -23,6 +23,8 @@ extra_rdoc_files: []
23
23
 
24
24
  files:
25
25
  - lib/gyruby.rb
26
+ - lib/gyruby/remote.rb
27
+ - lib/gyruby/mplayer_controller.rb
26
28
  - ext/extconf.rb
27
29
  - ext/gyruby_ext.c
28
30
  has_rdoc: true
@@ -48,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
48
50
  requirements: []
49
51
 
50
52
  rubyforge_project:
51
- rubygems_version: 1.0.1
53
+ rubygems_version: 1.1.1
52
54
  signing_key:
53
55
  specification_version: 2
54
56
  summary: A ruby binding for the Gyration FiireChief remote control for LinuxMCE.