accessibility_core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.yardopts +9 -0
  2. data/History.markdown +36 -0
  3. data/README.markdown +66 -0
  4. data/Rakefile +72 -0
  5. data/ext/accessibility/bridge/ext/accessibility/bridge/bridge.c +490 -0
  6. data/ext/accessibility/bridge/ext/accessibility/bridge/extconf.rb +22 -0
  7. data/ext/accessibility/bridge/lib/accessibility/bridge.rb +33 -0
  8. data/ext/accessibility/bridge/lib/accessibility/bridge/common.rb +57 -0
  9. data/ext/accessibility/bridge/lib/accessibility/bridge/macruby.rb +185 -0
  10. data/ext/accessibility/bridge/lib/accessibility/bridge/mri.rb +121 -0
  11. data/ext/accessibility/bridge/lib/accessibility/bridge/version.rb +6 -0
  12. data/ext/accessibility/bridge/test/array_test.rb +31 -0
  13. data/ext/accessibility/bridge/test/boxed_test.rb +23 -0
  14. data/ext/accessibility/bridge/test/cfrange_test.rb +21 -0
  15. data/ext/accessibility/bridge/test/cgpoint_test.rb +54 -0
  16. data/ext/accessibility/bridge/test/cgrect_test.rb +60 -0
  17. data/ext/accessibility/bridge/test/cgsize_test.rb +54 -0
  18. data/ext/accessibility/bridge/test/helper.rb +19 -0
  19. data/ext/accessibility/bridge/test/nsstring_test.rb +22 -0
  20. data/ext/accessibility/bridge/test/nsurl_test.rb +17 -0
  21. data/ext/accessibility/bridge/test/object_test.rb +19 -0
  22. data/ext/accessibility/bridge/test/range_test.rb +16 -0
  23. data/ext/accessibility/bridge/test/string_test.rb +35 -0
  24. data/ext/accessibility/bridge/test/uri_test.rb +15 -0
  25. data/ext/accessibility/core/bridge.h +1 -0
  26. data/ext/accessibility/core/core.c +705 -0
  27. data/ext/accessibility/core/extconf.rb +22 -0
  28. data/ext/accessibility/highlighter/extconf.rb +22 -0
  29. data/ext/accessibility/highlighter/highlighter.c +7 -0
  30. data/lib/accessibility/core.rb +12 -0
  31. data/lib/accessibility/core/core_ext/common.rb +57 -0
  32. data/lib/accessibility/core/core_ext/macruby.rb +140 -0
  33. data/lib/accessibility/core/core_ext/mri.rb +121 -0
  34. data/lib/accessibility/core/macruby.rb +858 -0
  35. data/lib/accessibility/core/version.rb +6 -0
  36. data/test/helper.rb +48 -0
  37. metadata +158 -0
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --no-cache
2
+ --no-output
3
+ --verbose
4
+ --markup markdown
5
+ --markup-provider kramdown
6
+ --readme README.markdown
7
+ --hide-void-return
8
+ lib/**/*.rb
9
+ ext/**/*{.m,.c}
data/History.markdown ADDED
@@ -0,0 +1,36 @@
1
+ # 0.0.1 - Initial Release
2
+
3
+ * CRuby and MacRuby compatible
4
+
5
+ * Added `Accessibility` namespace
6
+ * Added `Accessibility::Element` class/module as wrapper for `AXUIElementRef` structs
7
+
8
+ * Added `Accessibility::Element.application_for` that takes a pid and returns an app reference
9
+ * Added `Accessibility::Element.system_wide` which returns the reference for the system wide reference
10
+ * Added `Accessibility::Element.element_at` which returns the reference for the element at the given co-ordinates
11
+ * Added `Accessibility::Element.key_rate` for querying the typing speed
12
+ * Added `Accessibility::Element.key_rate=` for setting the typing speed
13
+
14
+ * Added `Accessibility::Element#attributes` for getting a list of the elements's plain attributes
15
+ * Added `Accessibility::Element#attribute` for getting the value of an attribute
16
+ * Added `Accessibility::Element#size_of` for getting the size of an array attribute
17
+ * Added `Accessibility::Element#writable?` for checking writability of an attribute
18
+ * Added `Accessibility::Element#set` for setting the value of an attribute
19
+ * Added `Accessibility::Element#role` for getting the value of the role attribute
20
+ * Added `Accessibility::Element#subrole` for getting the value of the subrole attribute
21
+ * Added `Accessibility::Element#parent` for getting the value of the parent attribute
22
+ * Added `Accessibility::Element#children` for getting the value of the children attribute
23
+ * Added `Accessibility::Element#value` for getting the value of the value attribute
24
+ * Added `Accessibility::Element#pid` for getting the process identifier for the app for an element
25
+ * Added `Accessibility::Element#parameterized_attributes` for getting the list of the element's parameterized attributes
26
+ * Added `Accessibility::Element#parameterized_attribute` for getting the value of a parameterized attribute
27
+ * Added `Accessibility::Element#actions` for getting the list of actions an element can perform
28
+ * Added `Accessibility::Element#perform` for telling an element to perform an action
29
+ * Added `Accessibility::Element#post` for posting key events to an application
30
+ * Added `Accessibility::Element#invalid?` for checking if an element is alive or dead
31
+ * Added `Accessibility::Element#set_timeout_to` for overriding the default messaging timeout
32
+ * Added `Accessibility::Element#application` for getting the toplevel element for an arbitrary element
33
+ * Added `Accessibility::Element#element_at` for getting app specific element at arbitrary co-ordinates
34
+ * Added `Accessibility::Element#==` for testing equality of elements
35
+
36
+ * Depend on `accessibility_bridge` for bridging needs and core extensions
data/README.markdown ADDED
@@ -0,0 +1,66 @@
1
+ # accessibility\_core
2
+
3
+ A port of accessibility/core.rb from [AXElements](http://github.com/Marketcircle/AXElements),
4
+ but cleaned up and rewritten in C to be more portable across languages and
5
+ runtimes.
6
+
7
+ [Documentation](http://rdoc.info/gems/accessibility_core/frames)
8
+
9
+
10
+ ## Examples
11
+
12
+ require 'accessibility/core'
13
+
14
+ app = Accessibility::Element.application_for 276 # PID for some app
15
+ app.attributes
16
+
17
+ window = app.main_window
18
+ window.attributes
19
+ window.attribute 'AXPosition'
20
+ window.set 'AXPosition', CGPoint.new(100, 100)
21
+
22
+
23
+ ## Note
24
+
25
+ At this point, `rake-compiler` does not run on MacRuby
26
+ 'out-of-the-box'. Actually, it does, but it declares a dependency on
27
+ rake which causes rake to be installed from rubygems. Rake from
28
+ rubygems does not work with MacRuby. So you need to either fix the
29
+ probem with MacRuby or just hack the `rake-compiler.gemspec` file
30
+ after you install `rake-compiler` so it does not think it depends on
31
+ rake anymore.
32
+
33
+
34
+ ## TODO
35
+
36
+ * bridging for `NSAttributedString`
37
+ * more descriptive error handling for the C extension
38
+
39
+ ## Copyright
40
+
41
+ Copyright (c) 2012, Mark Rada
42
+ All rights reserved.
43
+
44
+ Redistribution and use in source and binary forms, with or without
45
+ modification, are permitted provided that the following conditions are met:
46
+
47
+ * Redistributions of source code must retain the above copyright
48
+ notice, this list of conditions and the following disclaimer.
49
+ * Redistributions in binary form must reproduce the above copyright
50
+ notice, this list of conditions and the following disclaimer in the
51
+ documentation and/or other materials provided with the distribution.
52
+ * Neither the name of Mark Rada nor the names of its
53
+ contributors may be used to endorse or promote products derived
54
+ from this software without specific prior written permission.
55
+
56
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
57
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
58
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
59
+ DISCLAIMED. IN NO EVENT SHALL Mark Rada BE LIABLE FOR ANY
60
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
62
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
64
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
65
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
66
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ task :default => :test
2
+
3
+ require 'rake/clean'
4
+ CLEAN.include '*.plist', '*.gch'
5
+
6
+ desc 'Run the Clang static analyzer'
7
+ task :analyze do
8
+ sh "clang --analyze ext/accessibility/core/core.c"
9
+ end
10
+
11
+ desc 'Startup an IRb console with accessibility-core loaded'
12
+ task :console => [:compile] do
13
+ sh 'irb -Ilib -raccessibility/core'
14
+ end
15
+
16
+ desc 'Open the fixture app'
17
+ task :run_fixture => :fixture do
18
+ sh 'open test/fixture/Release/AXElementsTester.app'
19
+ end
20
+
21
+ desc 'Build the test fixture'
22
+ task :fixture do
23
+ sh 'cd test/AXElementsTester && xcodebuild'
24
+ end
25
+
26
+ desc 'Remove the built fixture app'
27
+ task :clobber_fixture do
28
+ $stdout.puts 'rm -rf test/fixture'
29
+ rm_rf 'test/fixture'
30
+ end
31
+ task :clobber => :clobber_fixture
32
+
33
+ require 'rake/testtask'
34
+ Rake::TestTask.new do |t|
35
+ t.libs << '.'
36
+ t.pattern = 'test/*_test.rb'
37
+ end
38
+ task :test => [:compile, :fixture]
39
+
40
+
41
+ # Gem stuff
42
+
43
+ require 'rubygems/package_task'
44
+ SPEC = Gem::Specification.load('accessibility_core.gemspec')
45
+
46
+ Gem::PackageTask.new(SPEC) { }
47
+
48
+ desc 'Build and install gem (not including deps)'
49
+ task :install => :gem do
50
+ require 'rubygems/installer'
51
+ Gem::Installer.new("pkg/#{SPEC.file_name}").install
52
+ end
53
+
54
+
55
+ # C extensions!
56
+
57
+ require 'rake/extensiontask'
58
+
59
+ Rake::ExtensionTask.new('core', SPEC) do |ext|
60
+ ext.ext_dir = 'ext/accessibility/core'
61
+ ext.lib_dir = 'lib/accessibility/core'
62
+ end
63
+
64
+ #Rake::ExtensionTask.new('highlighter', SPEC) do |ext|
65
+ # ext.ext_dir = 'ext/accessibility/highlighter'
66
+ # ext.lib_dir = 'lib/accessibility/core'
67
+ #end
68
+
69
+ # Rake::ExtensionTask.new('running_application', SPEC) do |ext|
70
+ # ext.ext_dir = 'ext/accessibility/running_application'
71
+ # ext.lib_dir = 'lib/accessibility/core'
72
+ # end
@@ -0,0 +1,490 @@
1
+ #include "ruby.h"
2
+ #import <Cocoa/Cocoa.h>
3
+
4
+ #ifndef ACCESSIBILITY_BRIDGE
5
+ #define ACCESSIBILITY_BRIDGE 1
6
+
7
+ #ifdef NOT_MACRUBY
8
+
9
+ VALUE rb_mAccessibility;
10
+ VALUE rb_cElement;
11
+ VALUE rb_cCGPoint;
12
+ VALUE rb_cCGSize;
13
+ VALUE rb_cCGRect;
14
+ VALUE rb_mURI; // URI module
15
+ VALUE rb_cURI; // URI::Generic class
16
+
17
+ ID sel_x;
18
+ ID sel_y;
19
+ ID sel_width;
20
+ ID sel_height;
21
+ ID sel_origin;
22
+ ID sel_size;
23
+ ID sel_to_point;
24
+ ID sel_to_size;
25
+ ID sel_to_rect;
26
+ ID sel_to_s;
27
+ ID sel_parse;
28
+
29
+
30
+ #define WRAP_ARRAY(wrapper) do { \
31
+ CFIndex length = CFArrayGetCount(array); \
32
+ VALUE ary = rb_ary_new2(length); \
33
+ \
34
+ for (CFIndex idx = 0; idx < length; idx++) \
35
+ rb_ary_store( \
36
+ ary, \
37
+ idx, \
38
+ wrapper(CFArrayGetValueAtIndex(array, idx)) \
39
+ ); \
40
+ return ary; \
41
+ } while (false);
42
+
43
+
44
+ VALUE
45
+ wrap_unknown(CFTypeRef obj)
46
+ {
47
+ // TODO: this will leak, can we use something like alloca?
48
+ CFStringRef description = CFCopyDescription(obj);
49
+ rb_raise(
50
+ rb_eRuntimeError,
51
+ "accessibility-core doesn't know how to wrap `%s` objects yet",
52
+ CFStringGetCStringPtr(description, kCFStringEncodingMacRoman)
53
+ );
54
+ return Qnil; // unreachable
55
+ }
56
+
57
+ CFTypeRef
58
+ unwrap_unknown(VALUE obj)
59
+ {
60
+ obj = rb_funcall(obj, sel_to_s, 0);
61
+ rb_raise(
62
+ rb_eRuntimeError,
63
+ "accessibility-core doesn't know how to unwrap `%s'",
64
+ StringValuePtr(obj)
65
+ );
66
+ return NULL; // unreachable
67
+ }
68
+
69
+
70
+ VALUE
71
+ wrap_point(CGPoint point)
72
+ {
73
+ return rb_struct_new(rb_cCGPoint, DBL2NUM(point.x), DBL2NUM(point.y));
74
+ }
75
+
76
+ CGPoint
77
+ unwrap_point(VALUE point)
78
+ {
79
+ point = rb_funcall(point, sel_to_point, 0);
80
+ double x = NUM2DBL(rb_struct_getmember(point, sel_x));
81
+ double y = NUM2DBL(rb_struct_getmember(point, sel_y));
82
+ return CGPointMake(x, y);
83
+ }
84
+
85
+
86
+ VALUE
87
+ wrap_size(CGSize size)
88
+ {
89
+ return rb_struct_new(rb_cCGSize, DBL2NUM(size.width), DBL2NUM(size.height));
90
+ }
91
+
92
+ CGSize
93
+ unwrap_size(VALUE size)
94
+ {
95
+ size = rb_funcall(size, sel_to_size, 0);
96
+ double width = NUM2DBL(rb_struct_getmember(size, sel_width));
97
+ double height = NUM2DBL(rb_struct_getmember(size, sel_height));
98
+ return CGSizeMake(width, height);
99
+ }
100
+
101
+
102
+ VALUE
103
+ wrap_rect(CGRect rect)
104
+ {
105
+ VALUE point = wrap_point(rect.origin);
106
+ VALUE size = wrap_size(rect.size);
107
+ return rb_struct_new(rb_cCGRect, point, size);
108
+ }
109
+
110
+ CGRect
111
+ unwrap_rect(VALUE rect)
112
+ {
113
+ rect = rb_funcall(rect, sel_to_rect, 0);
114
+ CGPoint origin = unwrap_point(rb_struct_getmember(rect, sel_origin));
115
+ CGSize size = unwrap_size(rb_struct_getmember(rect, sel_size));
116
+ return CGRectMake(origin.x, origin.y, size.width, size.height);
117
+ }
118
+
119
+
120
+ VALUE
121
+ convert_cf_range(CFRange range)
122
+ {
123
+ CFIndex end_index = range.location + range.length;
124
+ if (range.length != 0)
125
+ end_index -= 1;
126
+ return rb_range_new(INT2FIX(range.location), INT2FIX(end_index), 0);
127
+ }
128
+
129
+ CFRange
130
+ convert_rb_range(VALUE range)
131
+ {
132
+ VALUE b, e;
133
+ int exclusive;
134
+
135
+ rb_range_values(range, &b, &e, &exclusive);
136
+
137
+ int begin = NUM2INT(b);
138
+ int end = NUM2INT(e);
139
+
140
+ if (begin < 0 || end < 0)
141
+ // We don't know what the max length of the range will be, so we
142
+ // can't count backwards.
143
+ rb_raise(
144
+ rb_eArgError,
145
+ "negative values are not allowed in ranges " \
146
+ "that are converted to CFRange structures."
147
+ );
148
+
149
+ int length = exclusive ? end-begin : end-begin + 1;
150
+ return CFRangeMake(begin, length);
151
+ }
152
+
153
+
154
+ #define WRAP_VALUE(type, cookie, wrapper) do { \
155
+ type st; \
156
+ AXValueGetValue(value, cookie, &st); \
157
+ return wrapper(st); \
158
+ } while (0); \
159
+
160
+ VALUE wrap_value_point(AXValueRef value) { WRAP_VALUE(CGPoint, kAXValueCGPointType, wrap_point) }
161
+ VALUE wrap_value_size(AXValueRef value) { WRAP_VALUE(CGSize, kAXValueCGSizeType, wrap_size) }
162
+ VALUE wrap_value_rect(AXValueRef value) { WRAP_VALUE(CGRect, kAXValueCGRectType, wrap_rect) }
163
+ VALUE wrap_value_range(AXValueRef value) { WRAP_VALUE(CFRange, kAXValueCFRangeType, convert_cf_range) }
164
+ VALUE wrap_value_error(AXValueRef value) { WRAP_VALUE(AXError, kAXValueAXErrorType, INT2NUM) }
165
+
166
+ #define UNWRAP_VALUE(type, value, unwrapper) do { \
167
+ type st = unwrapper(val); \
168
+ return AXValueCreate(value, &st); \
169
+ } while(0);
170
+
171
+ AXValueRef unwrap_value_point(VALUE val) { UNWRAP_VALUE(CGPoint, kAXValueCGPointType, unwrap_point) }
172
+ AXValueRef unwrap_value_size(VALUE val) { UNWRAP_VALUE(CGSize, kAXValueCGSizeType, unwrap_size) }
173
+ AXValueRef unwrap_value_rect(VALUE val) { UNWRAP_VALUE(CGRect, kAXValueCGRectType, unwrap_rect) }
174
+ AXValueRef unwrap_value_range(VALUE val) { UNWRAP_VALUE(CFRange, kAXValueCFRangeType, convert_rb_range) }
175
+
176
+
177
+ VALUE
178
+ wrap_value(AXValueRef value)
179
+ {
180
+ switch (AXValueGetType(value))
181
+ {
182
+ case kAXValueIllegalType:
183
+ // TODO better error message
184
+ rb_raise(rb_eArgError, "herped when you should have derped");
185
+ case kAXValueCGPointType:
186
+ return wrap_value_point(value);
187
+ case kAXValueCGSizeType:
188
+ return wrap_value_size(value);
189
+ case kAXValueCGRectType:
190
+ return wrap_value_rect(value);
191
+ case kAXValueCFRangeType:
192
+ return wrap_value_range(value);
193
+ case kAXValueAXErrorType:
194
+ return wrap_value_error(value);
195
+ default:
196
+ // TODO better error message
197
+ rb_raise(
198
+ rb_eRuntimeError,
199
+ "Could not wrap You've found a bug in something...not sure who to blame"
200
+ );
201
+ }
202
+
203
+ return Qnil; // unreachable
204
+ }
205
+
206
+ AXValueRef
207
+ unwrap_value(VALUE value)
208
+ {
209
+ VALUE type = CLASS_OF(value);
210
+ if (type == rb_cCGPoint)
211
+ return unwrap_value_point(value);
212
+ else if (type == rb_cCGSize)
213
+ return unwrap_value_size(value);
214
+ else if (type == rb_cCGRect)
215
+ return unwrap_value_rect(value);
216
+ else if (type == rb_cRange)
217
+ return unwrap_value_range(value);
218
+
219
+ rb_raise(rb_eArgError, "could not wrap %s", rb_class2name(type));
220
+ return NULL; // unreachable
221
+ }
222
+
223
+ VALUE wrap_array_values(CFArrayRef array) { WRAP_ARRAY(wrap_value) }
224
+
225
+
226
+ static
227
+ void
228
+ ref_finalizer(void* obj)
229
+ {
230
+ CFRelease((CFTypeRef)obj);
231
+ }
232
+
233
+ VALUE
234
+ wrap_ref(AXUIElementRef ref)
235
+ {
236
+ return Data_Wrap_Struct(rb_cElement, NULL, ref_finalizer, (void*)ref);
237
+ }
238
+
239
+ AXUIElementRef
240
+ unwrap_ref(VALUE obj)
241
+ {
242
+ AXUIElementRef* ref;
243
+ Data_Get_Struct(obj, AXUIElementRef, ref);
244
+ // TODO we should return *ref? but that seems to fuck things up...
245
+ return (AXUIElementRef)ref;
246
+ }
247
+
248
+ VALUE wrap_array_refs(CFArrayRef array) { WRAP_ARRAY(wrap_ref) }
249
+
250
+
251
+ VALUE
252
+ wrap_string(CFStringRef string)
253
+ {
254
+ // flying by the seat of our pants here, this hasn't failed yet
255
+ // but probably will one day when I'm not looking
256
+ const char* name = CFStringGetCStringPtr(string, kCFStringEncodingMacRoman);
257
+ if (name)
258
+ return rb_str_new(name, CFStringGetLength(string));
259
+ else
260
+ // use rb_external_str_new() ? assume always UTF-8?
261
+ rb_raise(rb_eRuntimeError, "NEED TO IMPLEMNET STRING COPYING");
262
+
263
+ return Qnil; // unreachable
264
+ }
265
+
266
+ CFStringRef
267
+ unwrap_string(VALUE string)
268
+ {
269
+ return CFStringCreateWithCStringNoCopy(
270
+ NULL,
271
+ StringValueCStr(string),
272
+ 0,
273
+ kCFAllocatorNull
274
+ );
275
+ /* return CFStringCreateWithCString( */
276
+ /* NULL, */
277
+ /* StringValuePtr(string), */
278
+ /* kCFStringEncodingUTF8 */
279
+ /* ); */
280
+ }
281
+
282
+ VALUE wrap_array_strings(CFArrayRef array) { WRAP_ARRAY(wrap_string) }
283
+
284
+
285
+ #define WRAP_NUM(type, cookie, macro) do { \
286
+ type value; \
287
+ if (CFNumberGetValue(num, cookie, &value)) \
288
+ return macro(value); \
289
+ rb_raise(rb_eRuntimeError, "I goofed wrapping a number"); \
290
+ return Qnil; \
291
+ } while(0);
292
+
293
+ VALUE wrap_long(CFNumberRef num) { WRAP_NUM(long, kCFNumberLongType, LONG2FIX) }
294
+ VALUE wrap_long_long(CFNumberRef num) { WRAP_NUM(long long, kCFNumberLongLongType, LL2NUM) }
295
+ VALUE wrap_float(CFNumberRef num) { WRAP_NUM(double, kCFNumberDoubleType, DBL2NUM) }
296
+
297
+ #define UNWRAP_NUM(type, cookie, macro) do { \
298
+ type base = macro(num); \
299
+ return CFNumberCreate(NULL, cookie, &base); \
300
+ } while(0);
301
+
302
+ CFNumberRef unwrap_long(VALUE num) { UNWRAP_NUM(long, kCFNumberLongType, NUM2LONG) }
303
+ CFNumberRef unwrap_long_long(VALUE num) { UNWRAP_NUM(long long, kCFNumberLongLongType, NUM2LL) }
304
+ CFNumberRef unwrap_float(VALUE num) { UNWRAP_NUM(double, kCFNumberDoubleType, NUM2DBL) }
305
+
306
+ VALUE
307
+ wrap_number(CFNumberRef number)
308
+ {
309
+ switch (CFNumberGetType(number))
310
+ {
311
+ case kCFNumberSInt8Type:
312
+ case kCFNumberSInt16Type:
313
+ case kCFNumberSInt32Type:
314
+ case kCFNumberSInt64Type:
315
+ return wrap_long(number);
316
+ case kCFNumberFloat32Type:
317
+ case kCFNumberFloat64Type:
318
+ return wrap_float(number);
319
+ case kCFNumberCharType:
320
+ case kCFNumberShortType:
321
+ case kCFNumberIntType:
322
+ case kCFNumberLongType:
323
+ return wrap_long(number);
324
+ case kCFNumberLongLongType:
325
+ return wrap_long_long(number);
326
+ case kCFNumberFloatType:
327
+ case kCFNumberDoubleType:
328
+ return wrap_float(number);
329
+ case kCFNumberCFIndexType:
330
+ case kCFNumberNSIntegerType:
331
+ return wrap_long(number);
332
+ case kCFNumberCGFloatType: // == kCFNumberMaxType
333
+ return wrap_float(number);
334
+ default:
335
+ return INT2NUM(0); // unreachable unless system goofed
336
+ }
337
+ }
338
+
339
+ CFNumberRef
340
+ unwrap_number(VALUE number)
341
+ {
342
+ switch (TYPE(number))
343
+ {
344
+ case T_FIXNUM:
345
+ return unwrap_long(number);
346
+ case T_FLOAT:
347
+ return unwrap_float(number);
348
+ default:
349
+ rb_raise(
350
+ rb_eRuntimeError,
351
+ "wrapping %s is not supported; log a bug?",
352
+ rb_string_value_cstr(&number)
353
+ );
354
+ return kCFNumberNegativeInfinity; // unreachable
355
+ }
356
+ }
357
+
358
+ VALUE wrap_array_numbers(CFArrayRef array) { WRAP_ARRAY(wrap_number) }
359
+
360
+
361
+ VALUE
362
+ wrap_url(CFURLRef url)
363
+ {
364
+ return rb_funcall(rb_mURI, sel_parse, 1, wrap_string(CFURLGetString(url)));
365
+ }
366
+
367
+ CFURLRef
368
+ unwrap_url(VALUE url)
369
+ {
370
+ url = rb_funcall(url, sel_to_s, 0);
371
+ CFStringRef string = CFStringCreateWithCString(
372
+ NULL,
373
+ StringValuePtr(url),
374
+ kCFStringEncodingUTF8
375
+ );
376
+ CFURLRef url_ref = CFURLCreateWithString(NULL, string, NULL);
377
+ CFRelease(string);
378
+ return url_ref;
379
+ }
380
+
381
+ VALUE wrap_array_urls(CFArrayRef array) { WRAP_ARRAY(wrap_url) }
382
+
383
+ VALUE
384
+ wrap_date(CFDateRef date)
385
+ {
386
+ NSTimeInterval time = [(NSDate*)date timeIntervalSince1970];
387
+ return rb_time_new((time_t)time, 0);
388
+ }
389
+
390
+ CFDateRef
391
+ unwrap_date(VALUE date)
392
+ {
393
+ struct timeval t = rb_time_timeval(date);
394
+ NSDate* ns_date = [NSDate dateWithTimeIntervalSince1970:t.tv_sec];
395
+ return (CFDateRef)ns_date;
396
+ }
397
+
398
+ VALUE wrap_array_dates(CFArrayRef array) { WRAP_ARRAY(wrap_date) }
399
+
400
+
401
+ VALUE
402
+ wrap_boolean(CFBooleanRef bool_val)
403
+ {
404
+ return (CFBooleanGetValue(bool_val) ? Qtrue : Qfalse);
405
+ }
406
+
407
+ CFBooleanRef
408
+ unwrap_boolean(VALUE bool_val)
409
+ {
410
+ return (bool_val == Qtrue ? kCFBooleanTrue : kCFBooleanFalse);
411
+ }
412
+
413
+ VALUE wrap_array_booleans(CFArrayRef array) { WRAP_ARRAY(wrap_boolean) }
414
+
415
+
416
+ VALUE
417
+ wrap_array(CFArrayRef array)
418
+ {
419
+ CFTypeRef obj = CFArrayGetValueAtIndex(array, 0);
420
+ CFTypeID di = CFGetTypeID(obj);
421
+ if (di == AXUIElementGetTypeID()) return wrap_array_refs(array);
422
+ else if (di == AXValueGetTypeID()) return wrap_array_values(array);
423
+ else if (di == CFStringGetTypeID()) return wrap_array_strings(array);
424
+ else if (di == CFNumberGetTypeID()) return wrap_array_numbers(array);
425
+ else if (di == CFBooleanGetTypeID()) return wrap_array_booleans(array);
426
+ else if (di == CFURLGetTypeID()) return wrap_array_urls(array);
427
+ else if (di == CFDateGetTypeID()) return wrap_array_dates(array);
428
+ else return wrap_unknown(obj);
429
+ }
430
+
431
+ VALUE
432
+ to_ruby(CFTypeRef obj)
433
+ {
434
+ CFTypeID di = CFGetTypeID(obj);
435
+ if (di == CFArrayGetTypeID()) return wrap_array(obj);
436
+ else if (di == AXUIElementGetTypeID()) return wrap_ref(obj);
437
+ else if (di == AXValueGetTypeID()) return wrap_value(obj);
438
+ else if (di == CFStringGetTypeID()) return wrap_string(obj);
439
+ else if (di == CFNumberGetTypeID()) return wrap_number(obj);
440
+ else if (di == CFBooleanGetTypeID()) return wrap_boolean(obj);
441
+ else if (di == CFURLGetTypeID()) return wrap_url(obj);
442
+ else if (di == CFDateGetTypeID()) return wrap_date(obj);
443
+ else return wrap_unknown(obj);
444
+ }
445
+
446
+ CFTypeRef
447
+ to_ax(VALUE obj)
448
+ {
449
+ // TODO we can better optimize this when running under MacRuby
450
+ VALUE type = CLASS_OF(obj);
451
+ if (type == rb_cElement) return unwrap_ref(obj);
452
+ else if (type == rb_cString) return unwrap_string(obj);
453
+ else if (type == rb_cStruct) return unwrap_value(obj);
454
+ else if (type == rb_cRange) return unwrap_value(obj);
455
+ else if (type == rb_cFixnum) return unwrap_number(obj);
456
+ else if (type == rb_cFloat) return unwrap_number(obj);
457
+ else if (type == rb_cTime) return unwrap_date(obj);
458
+ else if (type == rb_cURI) return unwrap_url(obj);
459
+ else if (obj == Qtrue || obj == Qfalse) return unwrap_boolean(obj);
460
+ else return unwrap_unknown(obj);
461
+ }
462
+
463
+ #endif
464
+
465
+
466
+ void
467
+ Init_bridge()
468
+ {
469
+ #ifdef NOT_MACRUBY
470
+ sel_x = rb_intern("x");
471
+ sel_y = rb_intern("y");
472
+ sel_width = rb_intern("width");
473
+ sel_height = rb_intern("height");
474
+ sel_origin = rb_intern("origin");
475
+ sel_size = rb_intern("size");
476
+ sel_to_point = rb_intern("to_point");
477
+ sel_to_size = rb_intern("to_size");
478
+ sel_to_rect = rb_intern("to_rect");
479
+ sel_to_s = rb_intern("to_s");
480
+ sel_parse = rb_intern("parse");
481
+
482
+ rb_cCGPoint = rb_const_get(rb_cObject, rb_intern("CGPoint"));
483
+ rb_cCGSize = rb_const_get(rb_cObject, rb_intern("CGSize"));
484
+ rb_cCGRect = rb_const_get(rb_cObject, rb_intern("CGRect"));
485
+ rb_mURI = rb_const_get(rb_cObject, rb_intern("URI"));
486
+ rb_cURI = rb_const_get(rb_mURI, rb_intern("Generic"));
487
+ #endif
488
+ }
489
+
490
+ #endif