gyruby 0.0.2 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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.