rubyosa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/rbosa.c ADDED
@@ -0,0 +1,551 @@
1
+ /*
2
+ * Copyright (c) 2006, Apple Computer, Inc. All rights reserved.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions
6
+ * are met:
7
+ * 1. Redistributions of source code must retain the above copyright
8
+ * notice, this list of conditions and the following disclaimer.
9
+ * 2. Redistributions in binary form must reproduce the above copyright
10
+ * notice, this list of conditions and the following disclaimer in the
11
+ * documentation and/or other materials provided with the distribution.
12
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
13
+ * its contributors may be used to endorse or promote products derived
14
+ * from this software without specific prior written permission.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
17
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
20
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ * POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ #include "rbosa.h"
30
+
31
+ static VALUE mOSA;
32
+ static VALUE cOSAElement;
33
+ static VALUE cOSAElementList;
34
+ static VALUE cOSAElementRecord;
35
+ static VALUE mOSAEventDispatcher;
36
+
37
+ static ID sClasses;
38
+ static ID sApp;
39
+
40
+ #define INSPECT_AEDESC 0
41
+
42
+ static void
43
+ rbosa_element_free (void *ptr)
44
+ {
45
+ AEDisposeDesc (ptr);
46
+ free (ptr);
47
+ }
48
+
49
+ static VALUE
50
+ __rbosa_class_from_desc_data (VALUE app, AEDesc res)
51
+ {
52
+ DescType data;
53
+ Size datasize;
54
+ VALUE classes, klass;
55
+
56
+ classes = rb_ivar_get (app, sClasses);
57
+ if (NIL_P (classes))
58
+ return Qnil;
59
+ klass = Qnil;
60
+
61
+ datasize = AEGetDescDataSize (&res);
62
+ /* This should always be a four-byte code. */
63
+ if (datasize != sizeof (DescType))
64
+ return Qnil;
65
+
66
+ if (AEGetDescData (&res, &data, datasize) == noErr) {
67
+ char dtStr[5];
68
+
69
+ *(DescType *)dtStr = CFSwapInt32HostToBig (data);
70
+ klass = rb_hash_aref (classes, rb_str_new (dtStr, 4));
71
+ }
72
+
73
+ return klass;
74
+ }
75
+
76
+ static VALUE
77
+ rbosa_element_make (VALUE klass, AEDesc *desc, VALUE app)
78
+ {
79
+ AEDesc * newDesc;
80
+ VALUE new_klass, obj;
81
+
82
+ newDesc = (AEDesc *)malloc (sizeof (AEDesc));
83
+ if (newDesc == NULL)
84
+ rb_fatal ("cannot allocate memory");
85
+ memcpy (newDesc, desc, sizeof (AEDesc));
86
+ new_klass = Qnil;
87
+
88
+ /* Let's replace the klass here according to the type of the descriptor,
89
+ * if the basic class OSA::Element was given.
90
+ */
91
+ if (klass == cOSAElement) {
92
+ if (newDesc->descriptorType == 'list') {
93
+ klass = cOSAElementList;
94
+ }
95
+ else if (newDesc->descriptorType == 'reco') {
96
+ klass = cOSAElementRecord;
97
+ }
98
+ else if (newDesc->descriptorType == 'type') {
99
+ new_klass = __rbosa_class_from_desc_data (app, *newDesc);
100
+ }
101
+ else if (newDesc->descriptorType == 'obj ' && !NIL_P (app)) {
102
+ AEDesc res;
103
+ OSErr err;
104
+
105
+ if ((err = AEGetParamDesc ((AppleEvent *)newDesc, 'want', '****', &res)) == noErr)
106
+ new_klass = __rbosa_class_from_desc_data (app, res);
107
+ }
108
+ }
109
+
110
+ if (!NIL_P (new_klass))
111
+ klass = new_klass;
112
+
113
+ obj = Data_Wrap_Struct (klass, NULL, rbosa_element_free, newDesc);
114
+
115
+ rb_ivar_set (obj, sApp, NIL_P (app) ? obj : app);
116
+
117
+ return obj;
118
+ }
119
+
120
+ static AEDesc *
121
+ rbosa_element_aedesc (VALUE element)
122
+ {
123
+ AEDesc * desc;
124
+
125
+ if (!rb_obj_is_kind_of (element, cOSAElement))
126
+ rb_raise (rb_eArgError, "Invalid argument of type '%s' (required: OSA::Element)", rb_class2name (rb_class_of (element)));
127
+
128
+ Data_Get_Struct (element, AEDesc, desc);
129
+
130
+ return desc;
131
+ }
132
+
133
+ static VALUE
134
+ rbosa_element_new (VALUE self, VALUE type, VALUE value)
135
+ {
136
+ FourCharCode ffc_type;
137
+ OSErr error;
138
+ const char * c_value;
139
+ unsigned c_value_size;
140
+ AEDesc desc;
141
+
142
+ ffc_type = RVAL2FOURCHAR (type);
143
+
144
+ if (NIL_P (value)) {
145
+ c_value = NULL;
146
+ c_value_size = 0;
147
+ }
148
+ else if (rb_obj_is_kind_of (value, rb_cInteger)) {
149
+ FourCharCode code;
150
+
151
+ code = NUM2INT (value);
152
+ c_value = (const char *)&code;
153
+ c_value_size = sizeof (FourCharCode);
154
+ }
155
+ else if (ffc_type == 'alis') {
156
+ AliasHandle alias;
157
+
158
+ rbobj_to_alias_handle (value, &alias);
159
+
160
+ c_value = (const char *)*alias;
161
+ c_value_size = GetHandleSize ((Handle)alias);
162
+ }
163
+ else {
164
+ Check_Type (value, T_STRING);
165
+ c_value = RSTRING (value)->ptr;
166
+ c_value_size = RSTRING (value)->len;
167
+ }
168
+
169
+ error = AECreateDesc (ffc_type, c_value, c_value_size, &desc);
170
+ if (error != noErr)
171
+ rb_raise (rb_eArgError, "Cannot create Apple Event descriptor from type '%s' value '%s' : %s (%d)",
172
+ RVAL2CSTR (type), c_value, GetMacOSStatusErrorString (error), error);
173
+
174
+ return rbosa_element_make (self, &desc, Qnil);
175
+ }
176
+
177
+ static VALUE
178
+ rbosa_element_new_os (VALUE self, VALUE desired_class, VALUE container, VALUE key_form, VALUE key_data)
179
+ {
180
+ OSErr error;
181
+ AEDesc obj_specifier;
182
+
183
+ error = CreateObjSpecifier (RVAL2FOURCHAR (desired_class),
184
+ rbosa_element_aedesc (container),
185
+ RVAL2FOURCHAR (key_form),
186
+ rbosa_element_aedesc (key_data),
187
+ false,
188
+ &obj_specifier);
189
+
190
+ if (error != noErr)
191
+ rb_raise (rb_eArgError, "Cannot create Apple Event object specifier for desired class '%s' : %s (%d)",
192
+ RVAL2CSTR (desired_class), GetMacOSStatusErrorString (error), error);
193
+
194
+ return rbosa_element_make (self, &obj_specifier, Qnil);
195
+ }
196
+
197
+ static VALUE
198
+ rbosa_app_send_event (VALUE self, VALUE event_class, VALUE event_id, VALUE params, VALUE need_retval)
199
+ {
200
+ OSErr error;
201
+ AppleEvent ae;
202
+ AppleEvent reply;
203
+ VALUE rb_timeout;
204
+ SInt32 timeout;
205
+
206
+ error = AECreateAppleEvent (RVAL2FOURCHAR (event_class),
207
+ RVAL2FOURCHAR (event_id),
208
+ rbosa_element_aedesc (self),
209
+ kAutoGenerateReturnID,
210
+ kAnyTransactionID,
211
+ &ae);
212
+ if (error != noErr)
213
+ rb_raise (rb_eArgError, "Cannot create Apple Event '%s%s' : %s (%d)",
214
+ RVAL2CSTR (event_class), RVAL2CSTR (event_id), GetMacOSStatusErrorString (error), error);
215
+
216
+ if (!NIL_P (params)) {
217
+ unsigned i;
218
+
219
+ for (i = 0; i < RARRAY (params)->len; i++) {
220
+ VALUE ary;
221
+ VALUE type;
222
+ VALUE element;
223
+
224
+ ary = RARRAY (params)->ptr[i];
225
+ if (NIL_P (ary) || RARRAY (ary)->len != 2)
226
+ continue;
227
+
228
+ type = RARRAY (ary)->ptr[0];
229
+ element = RARRAY (ary)->ptr[1];
230
+
231
+ error = AEPutParamDesc (&ae, RVAL2FOURCHAR (type), rbosa_element_aedesc (element));
232
+ if (error != noErr) {
233
+ AEDisposeDesc (&ae);
234
+ rb_raise (rb_eArgError, "Cannot add Apple Event parameter '%s' : %s (%d)",
235
+ RVAL2CSTR (type), GetMacOSStatusErrorString (error), error);
236
+ }
237
+ }
238
+ }
239
+
240
+ rb_timeout = rb_iv_get (mOSA, "@timeout");
241
+ timeout = NIL_P (rb_timeout) ? kAEDefaultTimeout : NUM2INT (rb_timeout);
242
+
243
+ error = AESend (&ae, &reply, (RVAL2CBOOL(need_retval) ? kAEWaitReply : kAENoReply) | kAENeverInteract | kAECanSwitchLayer,
244
+ kAENormalPriority, timeout, NULL, NULL);
245
+
246
+ AEDisposeDesc (&ae);
247
+
248
+ if (error != noErr)
249
+ rb_raise (rb_eRuntimeError, "Cannot send Apple Event '%s%s' : %s (%d)",
250
+ RVAL2CSTR (event_class), RVAL2CSTR (event_id), GetMacOSStatusErrorString (error), error);
251
+
252
+ if (RTEST (need_retval)) {
253
+ VALUE rb_reply;
254
+ AEDesc replyObject;
255
+
256
+ AEGetParamDesc (&reply, keyDirectObject, typeWildCard, &replyObject);
257
+
258
+ rb_reply = rbosa_element_make (cOSAElement, &replyObject, self);
259
+
260
+ return rb_reply;
261
+ }
262
+ return Qnil;
263
+ }
264
+
265
+ static VALUE
266
+ rbosa_element_type (VALUE self)
267
+ {
268
+ AEDesc *desc;
269
+ char dtStr[5];
270
+
271
+ desc = rbosa_element_aedesc (self);
272
+ *(DescType*)dtStr = CFSwapInt32HostToBig (desc->descriptorType);
273
+
274
+ return rb_str_new (dtStr, 4);
275
+ }
276
+
277
+ static VALUE
278
+ rbosa_element_data (int argc, VALUE *argv, VALUE self)
279
+ {
280
+ VALUE coerce_type;
281
+ AEDesc coerced_desc;
282
+ AEDesc * desc;
283
+ OSErr error;
284
+ void * data;
285
+ Size datasize;
286
+ VALUE retval;
287
+
288
+ rb_scan_args (argc, argv, "01", &coerce_type);
289
+
290
+ desc = rbosa_element_aedesc (self);
291
+
292
+ if (!NIL_P (coerce_type)) {
293
+ error = AECoerceDesc (desc, RVAL2FOURCHAR (coerce_type), &coerced_desc);
294
+ if (error != noErr)
295
+ rb_raise (rb_eRuntimeError, "Cannot coerce desc to type %s : %s (%d)",
296
+ RVAL2CSTR (coerce_type), GetMacOSStatusErrorString (error), error);
297
+
298
+ desc = &coerced_desc;
299
+ }
300
+
301
+ datasize = AEGetDescDataSize (desc);
302
+ data = (void *)malloc (datasize);
303
+ if (data == NULL)
304
+ rb_fatal ("cannot allocate memory");
305
+
306
+ error = AEGetDescData (desc, data, datasize);
307
+ retval = error == noErr ? rb_str_new (data, datasize) : Qnil;
308
+
309
+ if (!NIL_P (coerce_type))
310
+ AEDisposeDesc (&coerced_desc);
311
+ free (data);
312
+
313
+ if (error != noErr)
314
+ rb_raise (rb_eRuntimeError, "Cannot get desc data : %s (%d)",
315
+ GetMacOSStatusErrorString (error), error);
316
+
317
+ return retval;
318
+ }
319
+
320
+ static VALUE
321
+ rbosa_element_eql (VALUE self, VALUE other)
322
+ {
323
+ AEDesc * self_desc;
324
+ AEDesc * other_desc;
325
+ Size data_size;
326
+ void * self_data;
327
+ void * other_data;
328
+ OSErr error;
329
+ Boolean ok;
330
+
331
+ if (!rb_obj_is_kind_of (other, rb_class_real (rb_class_of (self))))
332
+ return Qfalse;
333
+
334
+ self_desc = rbosa_element_aedesc (self);
335
+ other_desc = rbosa_element_aedesc (other);
336
+
337
+ if (self_desc == other_desc)
338
+ return Qtrue;
339
+
340
+ if (self_desc->descriptorType != other_desc->descriptorType)
341
+ return Qfalse;
342
+
343
+ data_size = AEGetDescDataSize (self_desc);
344
+ if (data_size != AEGetDescDataSize (other_desc))
345
+ return Qfalse;
346
+
347
+ self_data = (void *)malloc (data_size);
348
+ other_data = (void *)malloc (data_size);
349
+ ok = 0;
350
+
351
+ if (self_data == NULL || other_data == NULL)
352
+ rb_fatal ("cannot allocate memory");
353
+
354
+ error = AEGetDescData (self_desc, self_data, data_size);
355
+ if (error != noErr)
356
+ goto bails;
357
+
358
+ error = AEGetDescData (other_desc, other_data, data_size);
359
+ if (error != noErr)
360
+ goto bails;
361
+
362
+ ok = memcmp (self_data, other_data, data_size) == 0;
363
+
364
+ bails:
365
+ free (self_data);
366
+ free (other_data);
367
+
368
+ return CBOOL2RVAL (ok);
369
+ }
370
+
371
+ #if INSPECT_AEDESC
372
+ static VALUE
373
+ rbosa_element_inspect (VALUE self)
374
+ {
375
+ Handle h;
376
+ VALUE s;
377
+ char buf[1024];
378
+
379
+ s = rb_call_super (0, NULL);
380
+ if (AEPrintDescToHandle (rbosa_element_aedesc (self), &h) != noErr)
381
+ return s;
382
+
383
+ RSTRING(s)->ptr[RSTRING(s)->len - 1] = '\0';
384
+ snprintf (buf, sizeof buf, "%s aedesc=\"%s\">", RSTRING(s)->ptr, *h);
385
+ DisposeHandle (h);
386
+
387
+ return CSTR2RVAL (buf);
388
+ }
389
+ #endif
390
+
391
+ static long
392
+ __rbosa_elementlist_count (AEDescList *list)
393
+ {
394
+ OSErr error;
395
+ long count;
396
+
397
+ error = AECountItems (list, &count);
398
+ if (error != noErr)
399
+ rb_raise (rb_eRuntimeError, "Cannot count items : %s (%d)",
400
+ GetMacOSStatusErrorString (error), error);
401
+
402
+ return count;
403
+ }
404
+
405
+ static void
406
+ __rbosa_elementlist_add (AEDescList *list, VALUE element, long pos)
407
+ {
408
+ OSErr error;
409
+
410
+ error = AEPutDesc (list, pos, rbosa_element_aedesc (element));
411
+ if (error != noErr)
412
+ rb_raise (rb_eRuntimeError, "Cannot add given descriptor : %s (%d)",
413
+ GetMacOSStatusErrorString (error), error);
414
+ }
415
+
416
+ static VALUE
417
+ rbosa_elementlist_new (int argc, VALUE *argv, VALUE self)
418
+ {
419
+ OSErr error;
420
+ AEDescList list;
421
+ VALUE ary;
422
+ int i;
423
+
424
+ rb_scan_args (argc, argv, "01", &ary);
425
+
426
+ if (!NIL_P (ary))
427
+ Check_Type (ary, T_ARRAY);
428
+
429
+ error = AECreateList (NULL, 0, false, &list);
430
+ if (error != noErr)
431
+ rb_raise (rb_eRuntimeError, "Cannot create Apple Event descriptor list : %s (%d)",
432
+ GetMacOSStatusErrorString (error), error);
433
+
434
+ if (!NIL_P (ary)) {
435
+ for (i = 0; i < RARRAY (ary)->len; i++)
436
+ __rbosa_elementlist_add (&list, RARRAY (ary)->ptr[i], i + 1);
437
+ }
438
+
439
+ return rbosa_element_make (self, &list, Qnil);
440
+ }
441
+
442
+ static VALUE
443
+ rbosa_elementlist_add (VALUE self, VALUE element)
444
+ {
445
+ AEDescList * list;
446
+
447
+ list = (AEDescList *)rbosa_element_aedesc (self);
448
+ __rbosa_elementlist_add (list, __rbosa_elementlist_count (list) + 1, element);
449
+
450
+ return self;
451
+ }
452
+
453
+ static VALUE
454
+ __rbosa_elementlist_get (VALUE self, long index, AEKeyword *keyword)
455
+ {
456
+ OSErr error;
457
+ AEDesc desc;
458
+
459
+ error = AEGetNthDesc ((AEDescList *)rbosa_element_aedesc (self),
460
+ index + 1,
461
+ typeWildCard,
462
+ keyword,
463
+ &desc);
464
+
465
+ if (error != noErr)
466
+ rb_raise (rb_eRuntimeError, "Cannot get desc at index %d : %s (%d)",
467
+ index, GetMacOSStatusErrorString (error), error);
468
+
469
+ return rbosa_element_make (cOSAElement, &desc, rb_ivar_get (self, sApp));
470
+ }
471
+
472
+ static VALUE
473
+ rbosa_elementlist_get (VALUE self, VALUE index)
474
+ {
475
+ AEKeyword keyword;
476
+ return __rbosa_elementlist_get (self, FIX2INT (index), &keyword);
477
+ }
478
+
479
+ static VALUE
480
+ rbosa_elementlist_size (VALUE self)
481
+ {
482
+ return INT2FIX (__rbosa_elementlist_count ((AEDescList *)rbosa_element_aedesc (self)));
483
+ }
484
+
485
+ static VALUE
486
+ rbosa_elementrecord_to_a (VALUE self)
487
+ {
488
+ long i, count;
489
+ VALUE ary;
490
+
491
+ count = FIX2INT (rbosa_elementlist_size (self));
492
+ ary = rb_ary_new ();
493
+ for (i = 0; i < count; i++) {
494
+ AEKeyword keyword;
495
+ char keyStr[5];
496
+ VALUE val;
497
+
498
+ val = __rbosa_elementlist_get (self, i, &keyword);
499
+ *(AEKeyword *)keyStr = CFSwapInt32HostToBig (keyword);
500
+ keyStr[4] = '\0';
501
+ rb_ary_push (ary, rb_ary_new3 (2, CSTR2RVAL (keyStr), val));
502
+ }
503
+
504
+ return ary;
505
+ }
506
+
507
+ #define rbosa_define_param(name,default_value) \
508
+ do { \
509
+ rb_define_attr (CLASS_OF (mOSA), name, 1, 1); \
510
+ if (default_value == Qtrue || default_value == Qfalse) \
511
+ rb_define_alias (CLASS_OF (mOSA), name"?", name); \
512
+ rb_iv_set (mOSA, "@"name, default_value); \
513
+ } \
514
+ while (0)
515
+
516
+ void
517
+ Init_osa (void)
518
+ {
519
+ sClasses = rb_intern ("@classes");
520
+ sApp = rb_intern ("@app");
521
+
522
+ mOSA = rb_define_module ("OSA");
523
+ rb_define_module_function (mOSA, "__scripting_info__", rbosa_scripting_info, 2);
524
+ rb_define_module_function (mOSA, "__four_char_code__", rbosa_four_char_code, 1);
525
+
526
+ cOSAElement = rb_define_class_under (mOSA, "Element", rb_cObject);
527
+ rb_define_singleton_method (cOSAElement, "__new__", rbosa_element_new, 2);
528
+ rb_define_singleton_method (cOSAElement, "__new_object_specifier__", rbosa_element_new_os, 4);
529
+ rb_define_method (cOSAElement, "__type__", rbosa_element_type, 0);
530
+ rb_define_method (cOSAElement, "__data__", rbosa_element_data, -1);
531
+ rb_define_method (cOSAElement, "==", rbosa_element_eql, 1);
532
+ #if INSPECT_AEDESC
533
+ rb_define_method (cOSAElement, "inspect", rbosa_element_inspect, 0);
534
+ #endif
535
+
536
+ cOSAElementList = rb_define_class_under (mOSA, "ElementList", cOSAElement);
537
+ rb_define_singleton_method (cOSAElementList, "__new__", rbosa_elementlist_new, -1);
538
+ rb_define_method (cOSAElementList, "[]", rbosa_elementlist_get, 1);
539
+ rb_define_method (cOSAElementList, "size", rbosa_elementlist_size, 0);
540
+ rb_define_alias (cOSAElementList, "length", "size");
541
+ rb_define_method (cOSAElementList, "add", rbosa_elementlist_add, 1);
542
+
543
+ cOSAElementRecord = rb_define_class_under (mOSA, "ElementRecord", cOSAElement);
544
+ rb_define_method (cOSAElementRecord, "to_a", rbosa_elementrecord_to_a, 0);
545
+
546
+ mOSAEventDispatcher = rb_define_module_under (mOSA, "EventDispatcher");
547
+ rb_define_method (mOSAEventDispatcher, "__send_event__", rbosa_app_send_event, 4);
548
+
549
+ rbosa_define_param ("timeout", INT2NUM (kAEDefaultTimeout));
550
+ rbosa_define_param ("lazy_events", Qtrue);
551
+ }