accessibility_core 0.1.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 (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