rb-appscript 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. data/CHANGES +243 -0
  2. data/LICENSE +1 -0
  3. data/README +42 -0
  4. data/TODO +31 -0
  5. data/doc/aem-manual/01_introduction.html +48 -0
  6. data/doc/aem-manual/02_apioverview.html +89 -0
  7. data/doc/aem-manual/03_packingandunpackingdata.html +98 -0
  8. data/doc/aem-manual/04_references.html +401 -0
  9. data/doc/aem-manual/05_targettingapplications.html +133 -0
  10. data/doc/aem-manual/06_buildingandsendingevents.html +175 -0
  11. data/doc/aem-manual/07_findapp.html +54 -0
  12. data/doc/aem-manual/08_examples.html +85 -0
  13. data/doc/aem-manual/09_notes.html +41 -0
  14. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  15. data/doc/aem-manual/full.css +21 -0
  16. data/doc/aem-manual/index.html +43 -0
  17. data/doc/appscript-manual/01_introduction.html +82 -0
  18. data/doc/appscript-manual/02_aboutappscripting.html +244 -0
  19. data/doc/appscript-manual/03_quicktutorial.html +154 -0
  20. data/doc/appscript-manual/04_gettinghelp.html +101 -0
  21. data/doc/appscript-manual/05_keywordconversion.html +91 -0
  22. data/doc/appscript-manual/06_classesandenums.html +174 -0
  23. data/doc/appscript-manual/07_applicationobjects.html +181 -0
  24. data/doc/appscript-manual/08_realvsgenericreferences.html +86 -0
  25. data/doc/appscript-manual/09_referenceforms.html +232 -0
  26. data/doc/appscript-manual/10_referenceexamples.html +142 -0
  27. data/doc/appscript-manual/11_applicationcommands.html +204 -0
  28. data/doc/appscript-manual/12_commandexamples.html +129 -0
  29. data/doc/appscript-manual/13_performanceissues.html +115 -0
  30. data/doc/appscript-manual/14_problemapps.html +193 -0
  31. data/doc/appscript-manual/15_notes.html +84 -0
  32. data/doc/appscript-manual/application_architecture.gif +0 -0
  33. data/doc/appscript-manual/application_architecture2.gif +0 -0
  34. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  35. data/doc/appscript-manual/full.css +21 -0
  36. data/doc/appscript-manual/index.html +49 -0
  37. data/doc/appscript-manual/relationships_example.gif +0 -0
  38. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  39. data/doc/index.html +30 -0
  40. data/doc/mactypes-manual/index.html +216 -0
  41. data/doc/osax-manual/index.html +169 -0
  42. data/extconf.rb +54 -0
  43. data/misc/adobeunittypes.rb +14 -0
  44. data/misc/dump.rb +72 -0
  45. data/rb-appscript.gemspec +20 -0
  46. data/sample/AB_list_people_with_emails.rb +8 -0
  47. data/sample/Create_daily_iCal_todos.rb +72 -0
  48. data/sample/Hello_world.rb +9 -0
  49. data/sample/List_iTunes_playlist_names.rb +7 -0
  50. data/sample/Make_Mail_message.rb +29 -0
  51. data/sample/Open_file_in_TextEdit.rb +9 -0
  52. data/sample/Organize_Mail_messages.rb +57 -0
  53. data/sample/Print_folder_tree.rb +12 -0
  54. data/sample/Select_all_HTML_files.rb +8 -0
  55. data/sample/Set_iChat_status.rb +20 -0
  56. data/sample/Simple_Finder_GUI_Scripting.rb +14 -0
  57. data/sample/Stagger_Finder_windows.rb +21 -0
  58. data/sample/TextEdit_demo.rb +126 -0
  59. data/sample/iTunes_top40_to_html.rb +64 -0
  60. data/src/lib/_aem/aemreference.rb +1006 -0
  61. data/src/lib/_aem/codecs.rb +617 -0
  62. data/src/lib/_aem/connect.rb +100 -0
  63. data/src/lib/_aem/findapp.rb +83 -0
  64. data/src/lib/_aem/mactypes.rb +228 -0
  65. data/src/lib/_aem/send.rb +257 -0
  66. data/src/lib/_aem/typewrappers.rb +57 -0
  67. data/src/lib/_appscript/defaultterminology.rb +245 -0
  68. data/src/lib/_appscript/referencerenderer.rb +132 -0
  69. data/src/lib/_appscript/reservedkeywords.rb +107 -0
  70. data/src/lib/_appscript/terminology.rb +314 -0
  71. data/src/lib/aem.rb +216 -0
  72. data/src/lib/appscript.rb +830 -0
  73. data/src/lib/kae.rb +1484 -0
  74. data/src/lib/osax.rb +171 -0
  75. data/src/rbae.c +766 -0
  76. data/test/README +1 -0
  77. data/test/test_aemreference.rb +112 -0
  78. data/test/test_appscriptreference.rb +102 -0
  79. data/test/test_codecs.rb +159 -0
  80. data/test/test_findapp.rb +24 -0
  81. data/test/test_mactypes.rb +67 -0
  82. data/test/testall.sh +9 -0
  83. metadata +143 -0
data/src/rbae.c ADDED
@@ -0,0 +1,766 @@
1
+ /*
2
+ * Copyright (C) 2006 HAS
3
+ *
4
+ * Thanks to: FUJIMOTO Hisakuni, author of RubyAEOSA
5
+ */
6
+
7
+ #include "osx_ruby.h"
8
+ #include <Carbon/Carbon.h>
9
+
10
+ VALUE rb_ll2big(LONG_LONG); // keeps gcc happy
11
+
12
+ // AE module and classes
13
+ static VALUE mAE;
14
+ static VALUE cAEDesc;
15
+ static VALUE cMacOSError;
16
+
17
+ // Note: AEDescs need extra wrapping to avoid nasty problems with Ruby's Data_Wrap_Struct.
18
+ struct rbAE_AEDescWrapper {
19
+ AEDesc desc;
20
+ };
21
+
22
+ // (these two macros are basically cribbed from RubyAEOSA's aedesc.c)
23
+ #define AEDESC_DATA_PTR(o) ((struct rbAE_AEDescWrapper*)(DATA_PTR(o)))
24
+ #define AEDESC_OF(o) (AEDESC_DATA_PTR(o)->desc)
25
+
26
+ // Event handling
27
+ typedef long refcontype;
28
+
29
+ AEEventHandlerUPP upp_GenericEventHandler;
30
+ AECoercionHandlerUPP upp_GenericCoercionHandler;
31
+
32
+
33
+ /**********************************************************************/
34
+ // Raise MacOS error
35
+
36
+ /*
37
+ * Note: MacOSError should only be raised by AE module; attempting to raise it from Ruby
38
+ * just results in unexpected errors. (I've not quite figured out how to implement an
39
+ * Exception class in C that constructs correctly in both C and Ruby. Not serious, since
40
+ * nobody else needs to raise MacOSErrors - just a bit irritating.)
41
+ */
42
+
43
+ static void
44
+ rbAE_raiseMacOSError(const char *description, OSErr number)
45
+ {
46
+ VALUE errObj;
47
+
48
+ errObj = rb_funcall(cMacOSError, rb_intern("new"), 0);
49
+ rb_iv_set(errObj, "@number", INT2NUM(number)); // returns the OS error number
50
+ rb_iv_set(errObj, "@description", rb_str_new2(description)); // troubleshooting info
51
+ rb_exc_raise(errObj);
52
+ }
53
+
54
+
55
+ /**********************************************************************/
56
+ // MacOSError methods
57
+
58
+ static VALUE
59
+ rbAE_MacOSError_inspect(VALUE self)
60
+ {
61
+ char s[32];
62
+
63
+ sprintf(s, "#<AE::MacOSError %i>", NUM2INT(rb_iv_get(self, "@number")));
64
+ return rb_str_new2(s);
65
+ }
66
+
67
+
68
+ /**********************************************************************/
69
+ // AEDesc support functions
70
+
71
+ static DescType
72
+ rbStringToDescType(VALUE obj)
73
+ {
74
+ if (rb_obj_is_kind_of(obj, rb_cString) && RSTRING(obj)->len == 4) {
75
+ return CFSwapInt32HostToBig(*(DescType *)(RSTRING(obj)->ptr));
76
+ } else {
77
+ rb_raise(rb_eArgError, "Not a four-char-code string.");
78
+ }
79
+ }
80
+
81
+ static VALUE
82
+ rbDescTypeToString(DescType descType)
83
+ {
84
+ char s[4];
85
+
86
+ *(DescType*)s = CFSwapInt32HostToBig(descType);
87
+ return rb_str_new(s, 4);
88
+ }
89
+
90
+ /*******/
91
+
92
+ static void
93
+ rbAE_freeAEDesc(struct rbAE_AEDescWrapper *p)
94
+ {
95
+ AEDisposeDesc(&(p->desc));
96
+ free(p);
97
+ }
98
+
99
+ static VALUE
100
+ rbAE_wrapAEDesc(const AEDesc *desc)
101
+ {
102
+ struct rbAE_AEDescWrapper *wrapper;
103
+
104
+ // Found out how to wrap AEDescs so Ruby wouldn't crash by reading RubyAEOSA's aedesc.c
105
+ wrapper = malloc(sizeof(struct rbAE_AEDescWrapper));
106
+ wrapper->desc = *desc;
107
+ return Data_Wrap_Struct(cAEDesc, 0, rbAE_freeAEDesc, wrapper);
108
+ }
109
+
110
+ /*******/
111
+ // Note: clients should not attempt to use retain/use borrowed AE::AEDesc instances after handler callbacks return,
112
+ // as AEM will have disposed of the underlying AEDesc objects by then (TO DO: safety checking in AEDESC_OF?)
113
+
114
+ static void
115
+ rbAE_freeBorrowedAEDesc(struct rbAE_AEDescWrapper *p)
116
+ {
117
+ free(p);
118
+ }
119
+
120
+ static VALUE
121
+ rbAE_wrapBorrowedAEDesc(const AEDesc *desc)
122
+ {
123
+ struct rbAE_AEDescWrapper *wrapper;
124
+
125
+ wrapper = malloc(sizeof(struct rbAE_AEDescWrapper));
126
+ wrapper->desc = *desc;
127
+ return Data_Wrap_Struct(cAEDesc, 0, rbAE_freeBorrowedAEDesc, wrapper);
128
+ }
129
+
130
+ /**********************************************************************/
131
+ // AEDesc constructors
132
+
133
+ static VALUE
134
+ rbAE_AEDesc_new(VALUE class, VALUE type, VALUE data)
135
+ {
136
+ OSErr err = noErr;
137
+ AEDesc desc;
138
+
139
+ Check_Type(data, T_STRING);
140
+ err = AECreateDesc(rbStringToDescType(type),
141
+ RSTRING(data)->ptr, RSTRING(data)->len,
142
+ &desc);
143
+ if (err != noErr) rbAE_raiseMacOSError("Can't create AEDesc.", err);
144
+ return rbAE_wrapAEDesc(&desc);
145
+ }
146
+
147
+
148
+ static VALUE
149
+ rbAE_AEDesc_newList(VALUE class, VALUE isRecord)
150
+ {
151
+ OSErr err = noErr;
152
+ AEDesc desc;
153
+
154
+ err = AECreateList(NULL, 0, RTEST(isRecord), &desc);
155
+ if (err != noErr) rbAE_raiseMacOSError("Can't create AEDescList.", err);
156
+ return rbAE_wrapAEDesc(&desc);
157
+ }
158
+
159
+
160
+ static VALUE
161
+ rbAE_AEDesc_newAppleEvent(VALUE class, VALUE eventClass, VALUE eventID,
162
+ VALUE target, VALUE returnID, VALUE transactionID)
163
+ {
164
+ OSErr err = noErr;
165
+ AEDesc desc;
166
+
167
+ err = AECreateAppleEvent(rbStringToDescType(eventClass),
168
+ rbStringToDescType(eventID),
169
+ &(AEDESC_OF(target)),
170
+ NUM2INT(returnID),
171
+ NUM2LONG(transactionID),
172
+ &desc);
173
+ if (err != noErr) rbAE_raiseMacOSError("Can't create AppleEvent.", err);
174
+ return rbAE_wrapAEDesc(&desc);
175
+ }
176
+
177
+
178
+ /**********************************************************************/
179
+ // AEDesc methods
180
+
181
+ static VALUE
182
+ rbAE_AEDesc_inspect(VALUE self)
183
+ {
184
+ VALUE s, type;
185
+ Size dataSize;
186
+
187
+ s = rb_str_new2("#<AE::AEDesc type=%s size=%i>");
188
+ type = rb_funcall(self, rb_intern("type"), 0);
189
+ dataSize = AEGetDescDataSize(&(AEDESC_OF(self)));
190
+ return rb_funcall(s,
191
+ rb_intern("%"),
192
+ 1,
193
+ rb_ary_new3(2,
194
+ rb_funcall(type, rb_intern("inspect"), 0),
195
+ INT2NUM(dataSize)
196
+ )
197
+ );
198
+ }
199
+
200
+
201
+ /*******/
202
+
203
+ static VALUE
204
+ rbAE_AEDesc_type(VALUE self)
205
+ {
206
+ return rbDescTypeToString(AEDESC_OF(self).descriptorType);
207
+ }
208
+
209
+
210
+ static VALUE
211
+ rbAE_AEDesc_data(VALUE self)
212
+ {
213
+ OSErr err = noErr;
214
+ Size dataSize;
215
+ void *data;
216
+ VALUE result;
217
+
218
+ dataSize = AEGetDescDataSize(&(AEDESC_OF(self)));
219
+ data = malloc(dataSize);
220
+ err = AEGetDescData(&(AEDESC_OF(self)), data, dataSize);
221
+ if (err != noErr) rbAE_raiseMacOSError("Can't get AEDesc data.", err);
222
+ result = rb_str_new(data, dataSize);
223
+ free(data);
224
+ return result;
225
+ }
226
+
227
+
228
+ /*******/
229
+
230
+ static VALUE
231
+ rbAE_AEDesc_isRecord(VALUE self)
232
+ {
233
+ return AECheckIsRecord(&(AEDESC_OF(self))) ? Qtrue : Qfalse;
234
+ }
235
+
236
+ static VALUE
237
+ rbAE_AEDesc_coerce(VALUE self, VALUE type)
238
+ {
239
+ OSErr err = noErr;
240
+ AEDesc desc;
241
+
242
+ err = AECoerceDesc(&(AEDESC_OF(self)), rbStringToDescType(type), &desc);
243
+ if (err != noErr) rbAE_raiseMacOSError("Can't coerce AEDesc.", err);
244
+ return rbAE_wrapAEDesc(&desc);
245
+ }
246
+
247
+ static VALUE
248
+ rbAE_AEDesc_length(VALUE self)
249
+ {
250
+ OSErr err = noErr;
251
+ long length;
252
+
253
+ err = AECountItems(&(AEDESC_OF(self)), &length);
254
+ if (err != noErr) rbAE_raiseMacOSError("Can't get length of AEDesc.", err);
255
+ return INT2NUM(length);
256
+ }
257
+
258
+
259
+ static VALUE
260
+ rbAE_AEDesc_putItem(VALUE self, VALUE index, VALUE desc)
261
+ {
262
+ OSErr err = noErr;
263
+
264
+ if (rb_obj_is_instance_of(desc, cAEDesc) == Qfalse)
265
+ rb_raise(rb_eTypeError, "Can't put non-AEDesc item into AEDesc.");
266
+ err = AEPutDesc(&(AEDESC_OF(self)), NUM2LONG(index), &(AEDESC_OF(desc)));
267
+ if (err != noErr) rbAE_raiseMacOSError("Can't put item into AEDesc.", err);
268
+ return Qnil;
269
+ }
270
+
271
+
272
+ static VALUE
273
+ rbAE_AEDesc_putParam(VALUE self, VALUE key, VALUE desc)
274
+ {
275
+ OSErr err = noErr;
276
+
277
+ if (rb_obj_is_instance_of(desc, cAEDesc) == Qfalse)
278
+ rb_raise(rb_eTypeError, "Can't put non-AEDesc parameter into AEDesc.");
279
+ err = AEPutParamDesc(&(AEDESC_OF(self)), rbStringToDescType(key), &(AEDESC_OF(desc)));
280
+ if (err != noErr) rbAE_raiseMacOSError("Can't put parameter into AEDesc.", err);
281
+ return Qnil;
282
+ }
283
+
284
+
285
+ static VALUE
286
+ rbAE_AEDesc_putAttr(VALUE self, VALUE key, VALUE desc)
287
+ {
288
+ OSErr err = noErr;
289
+
290
+ if (rb_obj_is_instance_of(desc, cAEDesc) == Qfalse)
291
+ rb_raise(rb_eTypeError, "Can't put non-AEDesc attribute into AEDesc.");
292
+ err = AEPutAttributeDesc(&(AEDESC_OF(self)), rbStringToDescType(key), &(AEDESC_OF(desc)));
293
+ if (err != noErr) rbAE_raiseMacOSError("Can't put attribute into AEDesc.", err);
294
+ return Qnil;
295
+ }
296
+
297
+
298
+ static VALUE
299
+ rbAE_AEDesc_get(VALUE self, VALUE index, VALUE type)
300
+ {
301
+ OSErr err = noErr;
302
+ AEKeyword key;
303
+ AEDesc desc;
304
+
305
+ // TO DO: this gives bus error if typeAEList and index = 0 (should be OSErr -1701); why?
306
+ err = AEGetNthDesc(&(AEDESC_OF(self)),
307
+ NUM2LONG(index),
308
+ rbStringToDescType(type),
309
+ &key,
310
+ &desc);
311
+ if (err != noErr) rbAE_raiseMacOSError("Can't get item from AEDesc.", err);
312
+ return rb_ary_new3(2,
313
+ rbDescTypeToString(key),
314
+ rbAE_wrapAEDesc(&desc));
315
+ }
316
+
317
+
318
+ static VALUE
319
+ rbAE_AEDesc_send(VALUE self, VALUE sendMode, VALUE timeout)
320
+ {
321
+ OSErr err = noErr;
322
+ AppleEvent reply;
323
+
324
+ err = AESendMessage(&(AEDESC_OF(self)),
325
+ &reply,
326
+ (AESendMode)NUM2LONG(sendMode),
327
+ NUM2LONG(timeout));
328
+ if (err != noErr) rbAE_raiseMacOSError("Can't send Apple event.", err);
329
+ return rbAE_wrapAEDesc(&reply);
330
+ }
331
+
332
+
333
+ /**********************************************************************/
334
+ // Find and launch applications
335
+
336
+ static VALUE
337
+ rbAE_findApplication(VALUE self, VALUE creator, VALUE bundleID, VALUE name)
338
+ {
339
+ OSStatus err = 0;
340
+
341
+ OSType inCreator;
342
+ CFStringRef inName;
343
+ CFStringRef inBundleID;
344
+ FSRef outAppRef;
345
+ UInt8 path[PATH_MAX];
346
+
347
+ inCreator = (creator == Qnil) ? kLSUnknownCreator : rbStringToDescType(creator);
348
+ if (bundleID != Qnil) {
349
+ inBundleID = CFStringCreateWithBytes(NULL,
350
+ (UInt8 *)(RSTRING(bundleID)->ptr),
351
+ (CFIndex)(RSTRING(bundleID)->len),
352
+ kCFStringEncodingUTF8,
353
+ false);
354
+ if (inBundleID == NULL) rb_raise(rb_eRuntimeError, "Invalid bundle ID string.");
355
+ } else {
356
+ inBundleID = NULL;
357
+ }
358
+ if (name != Qnil) {
359
+ inName = CFStringCreateWithBytes(NULL,
360
+ (UInt8 *)(RSTRING(name)->ptr),
361
+ (CFIndex)(RSTRING(name)->len),
362
+ kCFStringEncodingUTF8,
363
+ false);
364
+ if (inName == NULL) {
365
+ if (inBundleID != NULL) CFRelease(inBundleID);
366
+ rb_raise(rb_eRuntimeError, "Invalid name string.");
367
+ }
368
+ } else {
369
+ inName = NULL;
370
+ }
371
+ err = LSFindApplicationForInfo(inCreator,
372
+ inBundleID,
373
+ inName,
374
+ &outAppRef,
375
+ NULL);
376
+ if (inBundleID != NULL) CFRelease(inBundleID);
377
+ if (inName != NULL) CFRelease(inName);
378
+ if (err != 0) rbAE_raiseMacOSError("Couldn't find application.", err);
379
+ err = FSRefMakePath(&outAppRef, path, PATH_MAX);
380
+ if (err != 0) rbAE_raiseMacOSError("Couldn't get application path.", err);
381
+ return rb_str_new2((char *)path);
382
+ }
383
+
384
+
385
+ static VALUE
386
+ rbAE_psnForApplicationPath(VALUE self, VALUE path)
387
+ {
388
+ OSStatus err = noErr;
389
+ ProcessSerialNumber psn = {0, kNoProcess};
390
+ FSRef appRef, foundRef;
391
+
392
+ err = FSPathMakeRef((UInt8 *)StringValuePtr(path), &appRef, NULL);
393
+ if (err != 0) rbAE_raiseMacOSError("Couldn't make FSRef for application.", err);
394
+ while (1) {
395
+ err = GetNextProcess(&psn);
396
+ if (err != 0) rbAE_raiseMacOSError("Can't get next process.", err); // -600 if no more processes left
397
+ err = GetProcessBundleLocation(&psn, &foundRef);
398
+ if (err != 0) continue;
399
+ if (FSCompareFSRefs(&appRef, &foundRef) == noErr)
400
+ return rb_ary_new3(2, INT2NUM(psn.highLongOfPSN), INT2NUM(psn.lowLongOfPSN));
401
+ }
402
+ }
403
+
404
+
405
+ static VALUE
406
+ rbAE_launchApplication(VALUE self, VALUE path, VALUE firstEvent, VALUE flags)
407
+ {
408
+ FSRef appRef;
409
+ FSSpec fss;
410
+ AEDesc paraDesc;
411
+ Size paraSize;
412
+ AppParametersPtr paraData;
413
+ ProcessSerialNumber psn;
414
+ LaunchParamBlockRec launchParams;
415
+ OSErr err = noErr;
416
+
417
+ err = FSPathMakeRef((UInt8 *)StringValuePtr(path), &appRef, NULL);
418
+ if (err != 0) rbAE_raiseMacOSError("Couldn't make FSRef for application.", err);
419
+ err = FSGetCatalogInfo(&appRef, kFSCatInfoNone, NULL, NULL, &fss, NULL);
420
+ if (err != 0) rbAE_raiseMacOSError("Couldn't make FSSpec for application.", err);
421
+ err = AECoerceDesc(&(AEDESC_OF(firstEvent)), typeAppParameters, &paraDesc);
422
+ paraSize = AEGetDescDataSize(&paraDesc);
423
+ paraData = (AppParametersPtr)NewPtr(paraSize);
424
+ if (paraData == NULL) rbAE_raiseMacOSError("Can't make app parameters AEDesc.", memFullErr);
425
+ err = AEGetDescData(&paraDesc, paraData, paraSize);
426
+ if (err != noErr) rbAE_raiseMacOSError("Can't get AEDesc data.", err);
427
+ launchParams.launchBlockID = extendedBlock;
428
+ launchParams.launchEPBLength = extendedBlockLen;
429
+ launchParams.launchFileFlags = 0;
430
+ launchParams.launchControlFlags = (LaunchFlags)NUM2UINT(flags);
431
+ launchParams.launchAppSpec = &fss;
432
+ launchParams.launchAppParameters = paraData;
433
+ err = LaunchApplication(&launchParams);
434
+ if (err != noErr) rbAE_raiseMacOSError("Can't launch application.", err);
435
+ psn = launchParams.launchProcessSN;
436
+ return rb_ary_new3(2, INT2NUM(psn.highLongOfPSN), INT2NUM(psn.lowLongOfPSN));
437
+ }
438
+
439
+ static VALUE
440
+ rbAE_pidToPsn(VALUE self, VALUE pid) // TO DO: not currently used by aem/appscript; delete?
441
+ {
442
+ OSStatus err = 0;
443
+ ProcessSerialNumber psn;
444
+
445
+ err = GetProcessForPID((pid_t)NUM2INT(pid), &psn);
446
+ if (err != noErr) rbAE_raiseMacOSError("Can't convert PID to PSN.", err);
447
+ return rb_ary_new3(2, INT2NUM(psn.highLongOfPSN), INT2NUM(psn.lowLongOfPSN));
448
+ }
449
+
450
+ /**********************************************************************/
451
+ // Date conversion
452
+
453
+ static VALUE
454
+ rbAE_convertLongDateTimeToUnixSeconds(VALUE self, VALUE ldt)
455
+ {
456
+ OSStatus err = 0;
457
+ CFAbsoluteTime cfTime;
458
+
459
+ err = UCConvertLongDateTimeToCFAbsoluteTime(rb_big2ll(ldt), &cfTime);
460
+ if (err != noErr) rbAE_raiseMacOSError("Can't convert LongDateTime to seconds.", err);
461
+ return rb_float_new(cfTime + kCFAbsoluteTimeIntervalSince1970);
462
+ }
463
+
464
+
465
+ static VALUE
466
+ rbAE_convertUnixSecondsToLongDateTime(VALUE self, VALUE secs)
467
+ {
468
+ OSStatus err = 0;
469
+ SInt64 ldt;
470
+
471
+ err = UCConvertCFAbsoluteTimeToLongDateTime(NUM2DBL(secs) - kCFAbsoluteTimeIntervalSince1970, &ldt);
472
+ if (err != noErr) rbAE_raiseMacOSError("Can't convert seconds to LongDateTime.", err);
473
+ return rb_ll2big(ldt);
474
+ }
475
+
476
+
477
+ /**********************************************************************/
478
+ // Get aete
479
+
480
+ static VALUE
481
+ rbAE_OSAGetAppTerminology(VALUE self, VALUE path)
482
+ {
483
+ FSRef appRef;
484
+ FSSpec fss;
485
+ ComponentInstance defaultComponent;
486
+ Boolean didLaunch;
487
+ AEDesc theDesc;
488
+ OSErr err = noErr;
489
+
490
+ err = FSPathMakeRef((UInt8 *)StringValuePtr(path), &appRef, NULL);
491
+ if (err != 0) rbAE_raiseMacOSError("Couldn't make FSRef.", err);
492
+ err = FSGetCatalogInfo(&appRef, kFSCatInfoNone, NULL, NULL, &fss, NULL);
493
+ if (err != 0) rbAE_raiseMacOSError("Couldn't make FSSpec.", err);
494
+ defaultComponent = OpenDefaultComponent(kOSAComponentType, 'ascr');
495
+ err = GetComponentInstanceError(defaultComponent);
496
+ if (err != 0) rbAE_raiseMacOSError("Couldn't make default component instance.", err);
497
+ err = OSAGetAppTerminology(defaultComponent,
498
+ kOSAModeNull,
499
+ &fss,
500
+ 0,
501
+ &didLaunch,
502
+ &theDesc);
503
+ if (err != 0) rbAE_raiseMacOSError("Couldn't get aete resource.", err);
504
+ return rbAE_wrapAEDesc(&theDesc);
505
+ }
506
+
507
+
508
+ /**********************************************************************/
509
+ // Install event handlers
510
+
511
+ // Based on Python's CarbonX.AE extension
512
+
513
+ // TO DO: make sure GC won't collect handler objects while they're installed as event/coercion handlers
514
+
515
+ static pascal OSErr
516
+ rbAE_GenericEventHandler(const AppleEvent *request, AppleEvent *reply, refcontype refcon)
517
+ {
518
+ VALUE err;
519
+
520
+ err = rb_funcall((VALUE)refcon,
521
+ rb_intern("handle_event"),
522
+ 2,
523
+ rbAE_wrapBorrowedAEDesc(request),
524
+ rbAE_wrapBorrowedAEDesc(reply));
525
+ return NUM2INT(err);
526
+ }
527
+
528
+ /*******/
529
+
530
+ static VALUE
531
+ rbAE_AEInstallEventHandler(VALUE self, VALUE eventClass, VALUE eventID, VALUE handler)
532
+ {
533
+ /*
534
+ * eventClass and eventID must be four-character code strings
535
+ *
536
+ * handler must be a Ruby object containing a method named 'handle_event' that takes two
537
+ * AppleEvent descriptors (request and reply) as arguments, and returns an integer.
538
+ * Note that this object is responsible for trapping any unhandled exceptions and returning
539
+ * an OS error number as appropriate (or 0 if no error), otherwise the program will exit.
540
+ */
541
+ OSErr err = noErr;
542
+
543
+ err = AEInstallEventHandler(rbStringToDescType(eventClass),
544
+ rbStringToDescType(eventID),
545
+ upp_GenericEventHandler, (long)handler,
546
+ 0);
547
+ if (err != noErr) rbAE_raiseMacOSError("Can't install event handler.", err);
548
+ return Qnil;
549
+ }
550
+
551
+
552
+ static VALUE
553
+ rbAE_AERemoveEventHandler(VALUE self, VALUE eventClass, VALUE eventID)
554
+ {
555
+ OSErr err = noErr;
556
+
557
+ err = AERemoveEventHandler(rbStringToDescType(eventClass),
558
+ rbStringToDescType(eventID),
559
+ upp_GenericEventHandler,
560
+ 0);
561
+ if (err != noErr) rbAE_raiseMacOSError("Can't remove event handler.", err);
562
+ return Qnil;
563
+ }
564
+
565
+
566
+ static VALUE
567
+ rbAE_AEGetEventHandler(VALUE self, VALUE eventClass, VALUE eventID)
568
+ {
569
+ OSErr err = noErr;
570
+ AEEventHandlerUPP handlerUPP;
571
+ VALUE handler;
572
+
573
+ err = AEGetEventHandler(rbStringToDescType(eventClass),
574
+ rbStringToDescType(eventID),
575
+ &handlerUPP, (long *)&handler,
576
+ 0);
577
+ if (err != noErr) rbAE_raiseMacOSError("Can't get event handler.", err);
578
+ return handler;
579
+ }
580
+
581
+
582
+ /**********************************************************************/
583
+ // Install coercion handlers
584
+
585
+ // TO DO: make sure GC won't collect handler objects while they're installed as event/coercion handlers
586
+
587
+ static pascal OSErr
588
+ rbAE_GenericCoercionHandler(const AEDesc *fromDesc, DescType toType, refcontype refcon, AEDesc *toDesc)
589
+ {
590
+ // handle_coercion method should return an AE::AEDesc, or nil if an error occurred
591
+ OSErr err = noErr;
592
+ VALUE res;
593
+
594
+ res = rb_funcall((VALUE)refcon,
595
+ rb_intern("handle_coercion"),
596
+ 2,
597
+ rbAE_wrapBorrowedAEDesc(fromDesc),
598
+ rbDescTypeToString(toType));
599
+ if (rb_obj_is_instance_of(res, cAEDesc) != Qtrue) return errAECoercionFail;
600
+ err = AEDuplicateDesc(&AEDESC_OF(res), toDesc);
601
+ return err;
602
+ }
603
+
604
+
605
+ /*******/
606
+
607
+ static VALUE
608
+ rbAE_AEInstallCoercionHandler(VALUE self, VALUE fromType, VALUE toType, VALUE handler)
609
+ {
610
+ /*
611
+ * fromType and toType must be four-character code strings
612
+ *
613
+ * handler must be a Ruby object containing a method named 'handle_coercion' that takes an
614
+ * AEDesc and a four-character code (original value, desired type) as arguments, and returns an
615
+ * AEDesc of the desired type.Note that this object is responsible for trapping any unhandled
616
+ * exceptions and returning nil (or any other non-AEDesc value) as appropriate, otherwise the
617
+ * program will exit.
618
+ */
619
+ OSErr err = noErr;
620
+
621
+ err = AEInstallCoercionHandler(rbStringToDescType(fromType),
622
+ rbStringToDescType(toType),
623
+ upp_GenericCoercionHandler, (long)handler,
624
+ 1, 0);
625
+ if (err != noErr) rbAE_raiseMacOSError("Can't install coercion handler.", err);
626
+ return Qnil;
627
+ }
628
+
629
+
630
+ static VALUE
631
+ rbAE_AERemoveCoercionHandler(VALUE self, VALUE fromType, VALUE toType)
632
+ {
633
+ OSErr err = noErr;
634
+
635
+ err = AERemoveCoercionHandler(rbStringToDescType(fromType),
636
+ rbStringToDescType(toType),
637
+ upp_GenericCoercionHandler,
638
+ 0);
639
+ if (err != noErr) rbAE_raiseMacOSError("Can't remove coercion handler.", err);
640
+ return Qnil;
641
+ }
642
+
643
+
644
+ static VALUE
645
+ rbAE_AEGetCoercionHandler(VALUE self, VALUE fromType, VALUE toType)
646
+ {
647
+ OSErr err = noErr;
648
+ AECoercionHandlerUPP handlerUPP;
649
+ VALUE handler;
650
+ Boolean fromTypeIsDesc;
651
+
652
+ err = AEGetCoercionHandler(rbStringToDescType(fromType),
653
+ rbStringToDescType(toType),
654
+ &handlerUPP, (long *)&handler,
655
+ &fromTypeIsDesc,
656
+ 0);
657
+ if (err != noErr) rbAE_raiseMacOSError("Can't get coercion handler.", err);
658
+ return rb_ary_new3(2, handler, fromTypeIsDesc ? Qtrue : Qfalse);
659
+ }
660
+
661
+
662
+
663
+
664
+ /**********************************************************************/
665
+ // Process management
666
+
667
+ static VALUE
668
+ rbAE_RunApplicationEventLoop(VALUE self)
669
+ {
670
+ RunApplicationEventLoop();
671
+ return Qnil;
672
+ }
673
+
674
+ static VALUE
675
+ rbAE_QuitApplicationEventLoop(VALUE self)
676
+ {
677
+ QuitApplicationEventLoop();
678
+ return Qnil;
679
+ }
680
+
681
+ static VALUE
682
+ rbAE_transformProcessToForegroundApplication(VALUE self)
683
+ {
684
+ OSStatus err = 0;
685
+ ProcessSerialNumber psn = {0, kCurrentProcess};
686
+
687
+ err = TransformProcessType(& psn, kProcessTransformToForegroundApplication);
688
+ if( err != 0) rbAE_raiseMacOSError("Can't transform process.", err);
689
+ return Qnil;
690
+ }
691
+
692
+
693
+ /**********************************************************************/
694
+ // Initialisation
695
+
696
+ void
697
+ Init_ae (void)
698
+ {
699
+
700
+ mAE = rb_define_module("AE");
701
+
702
+ // AE::AEDesc
703
+
704
+ cAEDesc = rb_define_class_under(mAE, "AEDesc", rb_cObject);
705
+
706
+ rb_define_singleton_method(cAEDesc, "new", rbAE_AEDesc_new, 2);
707
+ rb_define_singleton_method(cAEDesc, "new_list", rbAE_AEDesc_newList, 1);
708
+ rb_define_singleton_method(cAEDesc, "new_apple_event", rbAE_AEDesc_newAppleEvent, 5);
709
+
710
+ rb_define_method(cAEDesc, "to_s", rbAE_AEDesc_inspect, 0);
711
+ rb_define_method(cAEDesc, "inspect", rbAE_AEDesc_inspect, 0);
712
+ rb_define_method(cAEDesc, "type", rbAE_AEDesc_type, 0);
713
+ rb_define_method(cAEDesc, "data", rbAE_AEDesc_data, 0);
714
+ rb_define_method(cAEDesc, "is_record?", rbAE_AEDesc_isRecord, 0);
715
+ rb_define_method(cAEDesc, "coerce", rbAE_AEDesc_coerce, 1);
716
+ rb_define_method(cAEDesc, "length", rbAE_AEDesc_length, 0);
717
+ rb_define_method(cAEDesc, "put_item", rbAE_AEDesc_putItem, 2);
718
+ rb_define_method(cAEDesc, "put_param", rbAE_AEDesc_putParam, 2);
719
+ rb_define_method(cAEDesc, "put_attr", rbAE_AEDesc_putAttr, 2);
720
+ rb_define_method(cAEDesc, "get", rbAE_AEDesc_get, 2);
721
+ rb_define_method(cAEDesc, "send", rbAE_AEDesc_send, 2);
722
+
723
+ // AE::MacOSError
724
+
725
+ cMacOSError = rb_define_class_under(mAE, "MacOSError", rb_eStandardError);
726
+
727
+ rb_define_attr(cMacOSError, "number", Qtrue, Qfalse);
728
+ rb_define_attr(cMacOSError, "description", Qtrue, Qfalse);
729
+
730
+ rb_define_alias(cMacOSError, "to_i", "number");
731
+
732
+ rb_define_method(cMacOSError, "to_s", rbAE_MacOSError_inspect, 0);
733
+ rb_define_method(cMacOSError, "inspect", rbAE_MacOSError_inspect, 0);
734
+
735
+ // Support functions
736
+
737
+ rb_define_module_function(mAE, "find_application", rbAE_findApplication, 3);
738
+ rb_define_module_function(mAE, "psn_for_application_path", rbAE_psnForApplicationPath, 1);
739
+ rb_define_module_function(mAE, "launch_application", rbAE_launchApplication, 3);
740
+ rb_define_module_function(mAE, "pid_to_psn", rbAE_pidToPsn, 1);
741
+
742
+ rb_define_module_function(mAE, "convert_long_date_time_to_unix_seconds",
743
+ rbAE_convertLongDateTimeToUnixSeconds, 1);
744
+ rb_define_module_function(mAE, "convert_unix_seconds_to_long_date_time",
745
+ rbAE_convertUnixSecondsToLongDateTime, 1);
746
+
747
+ rb_define_module_function(mAE, "get_app_terminology", rbAE_OSAGetAppTerminology, 1);
748
+
749
+ // Event handling
750
+
751
+ upp_GenericEventHandler = NewAEEventHandlerUPP(rbAE_GenericEventHandler);
752
+ upp_GenericCoercionHandler = NewAECoerceDescUPP(rbAE_GenericCoercionHandler);
753
+
754
+ rb_define_module_function(mAE, "install_event_handler", rbAE_AEInstallEventHandler, 3);
755
+ rb_define_module_function(mAE, "remove_event_handler", rbAE_AERemoveEventHandler, 2);
756
+ rb_define_module_function(mAE, "get_event_handler", rbAE_AEGetEventHandler, 2);
757
+
758
+ rb_define_module_function(mAE, "install_coercion_handler", rbAE_AEInstallCoercionHandler, 3);
759
+ rb_define_module_function(mAE, "remove_coercion_handler", rbAE_AERemoveCoercionHandler, 2);
760
+ rb_define_module_function(mAE, "get_coercion_handler", rbAE_AEGetCoercionHandler, 2);
761
+
762
+ rb_define_module_function(mAE, "run_application_event_loop", rbAE_RunApplicationEventLoop, 0);
763
+ rb_define_module_function(mAE, "quit_application_event_loop", rbAE_QuitApplicationEventLoop, 0);
764
+ rb_define_module_function(mAE, "transform_process_to_foreground_application",
765
+ rbAE_transformProcessToForegroundApplication, 0);
766
+ }