accessibility_bridge 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,5 @@
1
+ # v0.1.0 - Initial release
2
+
3
+ * MRI and MacRuby compatible
4
+ * Bare minimum bridging so that accessibility-core works
5
+
data/README.markdown ADDED
@@ -0,0 +1,39 @@
1
+ # accessibility\_bridge
2
+
3
+ Bare minimum bridging for MRI to easily use Cocoa objects. The intended
4
+ use for this code is as a support for other gems that require some
5
+ bridging to Cocoa stuff.
6
+
7
+ This was extracted from the
8
+ [accessibility\_core](https://github.com/AXElements/accessibility_core)
9
+ project and is a work-in-progress.
10
+
11
+
12
+ ## Copyright
13
+
14
+ Copyright (c)2012, Mark Rada
15
+ All rights reserved.
16
+
17
+ Redistribution and use in source and binary forms, with or without
18
+ modification, are permitted provided that the following conditions are met:
19
+
20
+ * Redistributions of source code must retain the above copyright
21
+ notice, this list of conditions and the following disclaimer.
22
+ * Redistributions in binary form must reproduce the above copyright
23
+ notice, this list of conditions and the following disclaimer in the
24
+ documentation and/or other materials provided with the distribution.
25
+ * Neither the name of Mark Rada nor the names of its
26
+ contributors may be used to endorse or promote products derived
27
+ from this software without specific prior written permission.
28
+
29
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
30
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32
+ DISCLAIMED. IN NO EVENT SHALL Mark Rada BE LIABLE FOR ANY
33
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
35
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
37
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
38
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
39
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
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/bridge/bridge.c"
9
+ end
10
+
11
+ desc 'Startup an IRb console with accessibility-core loaded'
12
+ task :console => [:compile] do
13
+ sh 'irb -Ilib -raccessibility/bridge'
14
+ end
15
+
16
+ desc 'Build the test fixture'
17
+ task :fixture do
18
+ sh 'cd test/AXElementsTester && xcodebuild'
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new do |t|
23
+ t.libs << '.'
24
+ t.pattern = 'test/*_test.rb'
25
+ end
26
+ task :test => :compile
27
+
28
+
29
+ # Gem stuff
30
+
31
+ require 'rubygems/package_task'
32
+ SPEC = Gem::Specification.load('accessibility_bridge.gemspec')
33
+
34
+ Gem::PackageTask.new(SPEC) { }
35
+
36
+ desc 'Build and install gem (not including deps)'
37
+ task :install => :gem do
38
+ require 'rubygems/installer'
39
+ Gem::Installer.new("pkg/#{SPEC.file_name}").install
40
+ end
41
+
42
+
43
+ # C extensions!
44
+
45
+ require 'rake/extensiontask'
46
+
47
+ Rake::ExtensionTask.new('bridge', SPEC) do |ext|
48
+ ext.ext_dir = 'ext/accessibility/bridge'
49
+ ext.lib_dir = 'lib/accessibility'
50
+ end
51
+
@@ -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
@@ -0,0 +1,22 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS << ' -std=c99 -Wall -Werror -pedantic -ObjC'
4
+ $LIBS << ' -framework CoreFoundation -framework ApplicationServices -framework Cocoa'
5
+ $LIBS << ' -framework CoreGraphics' unless `sw_vers -productVersion`.to_f == 10.7
6
+
7
+ if RUBY_ENGINE == 'macruby'
8
+ $CFLAGS << ' -fobjc-gc'
9
+ else
10
+ unless RbConfig::CONFIG["CC"].match /clang/
11
+ clang = `which clang`.chomp
12
+ if clang.empty?
13
+ raise "Clang not installed. Cannot build C extension"
14
+ else
15
+ RbConfig::MAKEFILE_CONFIG["CC"] = clang
16
+ RbConfig::MAKEFILE_CONFIG["CXX"] = clang
17
+ end
18
+ end
19
+ $CFLAGS << ' -DNOT_MACRUBY'
20
+ end
21
+
22
+ create_makefile 'accessibility/bridge'
@@ -0,0 +1,33 @@
1
+ require 'accessibility/bridge/version'
2
+
3
+ if RUBY_ENGINE == 'macruby'
4
+
5
+ ##
6
+ # Whether or not we are running on MacRuby
7
+ def on_macruby?
8
+ true
9
+ end
10
+
11
+ framework 'Cocoa'
12
+
13
+ # A workaround that guarantees that `CGPoint` is defined
14
+ unless defined? MOUNTAIN_LION_APPKIT_VERSION
15
+ MOUNTAIN_LION_APPKIT_VERSION = 1187
16
+ end
17
+
18
+ if NSAppKitVersionNumber >= MOUNTAIN_LION_APPKIT_VERSION
19
+ framework '/System/Library/Frameworks/CoreGraphics.framework'
20
+ end
21
+
22
+ require 'accessibility/bridge/macruby'
23
+
24
+ else
25
+
26
+ def on_macruby?
27
+ false
28
+ end
29
+
30
+ require 'accessibility/bridge/mri'
31
+
32
+ end
33
+
@@ -0,0 +1,57 @@
1
+ class CGPoint
2
+ ##
3
+ # Returns the receiver, since the receiver is already a {CGPoint}
4
+ #
5
+ # @return [CGPoint]
6
+ def to_point
7
+ self
8
+ end
9
+ end
10
+
11
+ class CGSize
12
+ ##
13
+ # Returns the receiver, since the receiver is already a {CGSize}
14
+ #
15
+ # @return [CGSize]
16
+ def to_size
17
+ self
18
+ end
19
+ end
20
+
21
+ class CGRect
22
+ ##
23
+ # Returns the receiver, since the receiver is already a {CGRect}
24
+ #
25
+ # @return [CGRect]
26
+ def to_rect
27
+ self
28
+ end
29
+ end
30
+
31
+ ##
32
+ # accessibility-core extensions to `Array`
33
+ class Array
34
+ ##
35
+ # Coerce the first two elements of the receiver into a {CGPoint}
36
+ #
37
+ # @return [CGPoint]
38
+ def to_point
39
+ CGPoint.new self[0], self[1]
40
+ end
41
+
42
+ ##
43
+ # Coerce the first two elements of the receiver into a {CGSize}
44
+ #
45
+ # @return [CGSize]
46
+ def to_size
47
+ CGSize.new self[0], self[1]
48
+ end
49
+
50
+ ##
51
+ # Coerce the first four elements of the receiver into a {CGRect}
52
+ #
53
+ # @return [CGRect]
54
+ def to_rect
55
+ CGRect.new CGPoint.new(self[0], self[1]), CGSize.new(self[2], self[3])
56
+ end
57
+ end
@@ -0,0 +1,185 @@
1
+ require 'accessibility/bridge/common'
2
+
3
+ ##
4
+ # accessibility-core extensions for `NSURL`
5
+ class NSURL
6
+ ##
7
+ # Return the reciver, for the receiver is already a URL object
8
+ #
9
+ # @return [NSURL]
10
+ def to_url
11
+ self
12
+ end
13
+
14
+ # because printing is easier this way
15
+ alias_method :to_s, :inspect
16
+ end
17
+
18
+ ##
19
+ # accessibility-core extensions for `NSString`
20
+ class NSString
21
+ ##
22
+ # Create an NSURL using the receiver as the initialization string
23
+ #
24
+ # If the receiver is not a valid URL then `nil` will be returned.
25
+ #
26
+ # This exists because of
27
+ # [rdar://11207662](http://openradar.appspot.com/11207662).
28
+ #
29
+ # @return [NSURL,nil]
30
+ def to_url
31
+ NSURL.URLWithString self
32
+ end
33
+ end
34
+
35
+ ##
36
+ # `accessibility-core` extensions for `NSObject`
37
+ class NSObject
38
+ ##
39
+ # Return an object safe for passing to AXAPI
40
+ def to_ax
41
+ self
42
+ end
43
+
44
+ ##
45
+ # Return a usable object from an AXAPI pointer
46
+ def to_ruby
47
+ self
48
+ end
49
+ end
50
+
51
+ ##
52
+ # `accessibility-core` extensions for `CFRange`
53
+ class CFRange
54
+ ##
55
+ # Convert the {CFRange} to a Ruby {Range} object
56
+ #
57
+ # @return [Range]
58
+ def to_ruby
59
+ Range.new location, (location + length - 1)
60
+ end
61
+ end
62
+
63
+ ##
64
+ # `accessibility-core` extensions for `Range`
65
+ class Range
66
+ # @return [AXValueRef]
67
+ def to_ax
68
+ raise ArgumentError, "can't convert negative index" if last < 0 || first < 0
69
+ length = if exclude_end?
70
+ last - first
71
+ else
72
+ last - first + 1
73
+ end
74
+ CFRange.new(first, length).to_ax
75
+ end
76
+ end
77
+
78
+ ##
79
+ # AXElements extensions to the `Boxed` class
80
+ #
81
+ # The `Boxed` class is simply an abstract base class for structs that
82
+ # MacRuby can use via bridge support.
83
+ class Boxed
84
+ ##
85
+ # Returns the number that AXAPI uses in order to know how to wrap
86
+ # a struct.
87
+ #
88
+ # @return [Number]
89
+ def self.ax_value
90
+ raise NotImplementedError, "#{inspect}:#{self.class} cannot be wrapped"
91
+ end
92
+
93
+ ##
94
+ # Create an `AXValueRef` from the `Boxed` instance. This will only
95
+ # work if for the most common boxed types, you will need to check
96
+ # the AXAPI documentation for an up to date list.
97
+ #
98
+ # @example
99
+ #
100
+ # CGPoint.new(12, 34).to_ax # => #<AXValueRef:0x455678e2>
101
+ # CGSize.new(56, 78).to_ax # => #<AXValueRef:0x555678e2>
102
+ #
103
+ # @return [AXValueRef]
104
+ def to_ax
105
+ klass = self.class
106
+ ptr = Pointer.new klass.type
107
+ ptr.assign self
108
+ AXValueCreate(klass.ax_value, ptr)
109
+ end
110
+ end
111
+
112
+ # `accessibility-core` extensions for `CFRange`'s metaclass
113
+ class << CFRange
114
+ # (see Boxed.ax_value)
115
+ def ax_value; KAXValueCFRangeType end
116
+ end
117
+ # `accessibility-core` extensions for `CGSize`'s metaclass
118
+ class << CGSize
119
+ # (see Boxed.ax_value)
120
+ def ax_value; KAXValueCGSizeType end
121
+ end
122
+ # `accessibility-core` extensions for `CGRect`'s metaclass
123
+ class << CGRect
124
+ # (see Boxed.ax_value)
125
+ def ax_value; KAXValueCGRectType end
126
+ end
127
+ # `accessibility-core` extensions for `CGPoint`'s metaclass
128
+ class << CGPoint
129
+ # (see Boxed.ax_value)
130
+ def ax_value; KAXValueCGPointType end
131
+ end
132
+
133
+ ##
134
+ # Mixin for the special hidden class in MacRuby that represents `AXValueRef`
135
+ #
136
+ # This module adds a `#to_ruby` method that actually unwraps the object from
137
+ # the `AXValueRef` to some sort of {Boxed} object, such as a {CGPoint} or a
138
+ # {Range}.
139
+ module ValueWrapper
140
+
141
+
142
+ ##
143
+ # Map of type encodings used for unwrapping structs wrapped in an `AXValueRef`
144
+ #
145
+ # The list is order sensitive, which is why we unshift nil, but
146
+ # should probably be more rigorously defined at runtime.
147
+ #
148
+ # @return [String,nil]
149
+ BOX_TYPES = [CGPoint, CGSize, CGRect, CFRange].map!(&:type).unshift(nil)
150
+
151
+ ##
152
+ # Unwrap an `AXValueRef` into the `Boxed` instance that it is supposed
153
+ # to be. This will only work for the most common boxed types, you will
154
+ # need to check the AXAPI documentation for an up to date list.
155
+ #
156
+ # @example
157
+ #
158
+ # wrapped_point.to_ruby # => #<CGPoint x=44.3 y=99.0>
159
+ # wrapped_range.to_ruby # => #<CFRange begin=7 length=100>
160
+ # wrapped_thing.to_ruby # => wrapped_thing
161
+ #
162
+ # @return [Boxed]
163
+ def to_ruby
164
+ type = AXValueGetType(self)
165
+ return self if type.zero?
166
+
167
+ ptr = Pointer.new BOX_TYPES[type]
168
+ AXValueGetValue(self, type, ptr)
169
+ ptr.value.to_ruby
170
+ end
171
+
172
+ # hack to find the __NSCFType class and mix things in
173
+ klass = AXUIElementCreateSystemWide().class
174
+ klass.send :include, self
175
+
176
+ end
177
+
178
+ ##
179
+ # `accessibility-core` extensions to `NSArray`
180
+ class NSArray
181
+ # @return [Array]
182
+ def to_ruby
183
+ map do |obj| obj.to_ruby end
184
+ end
185
+ end
@@ -0,0 +1,121 @@
1
+ ##
2
+ # A structure that contains a point in a two-dimensional coordinate system
3
+ CGPoint = Struct.new(:x, :y) do
4
+
5
+ # @param x [Number]
6
+ # @param y [Number]
7
+ def initialize x = 0.0, y = 0.0
8
+ super
9
+ end
10
+
11
+ # @!attribute [rw] x
12
+ # The `x` co-ordinate of the screen point
13
+ # @return [Number]
14
+
15
+ # @!attribute [rw] y
16
+ # The `y` co-ordinate of the screen point
17
+ # @return [Number]
18
+
19
+ ##
20
+ # Return a nice string representation of the point
21
+ #
22
+ # Overrides `Object#inspect` to more closely mimic MacRuby `Boxed#inspect`.
23
+ #
24
+ # @return [String]
25
+ def inspect
26
+ "#<CGPoint x=#{self.x.to_f} y=#{self.y.to_f}>"
27
+ end
28
+
29
+ end
30
+
31
+
32
+ ##
33
+ # A structure that contains the size of a rectangle in a 2D co-ordinate system
34
+ CGSize = Struct.new(:width, :height) do
35
+
36
+ # @param width [Number]
37
+ # @param height [Number]
38
+ def initialize width = 0.0, height = 0.0
39
+ super
40
+ end
41
+
42
+ # @!attribute [rw] width
43
+ # The `width` of the box
44
+ # @return [Number]
45
+
46
+ # @!attribute [rw] height
47
+ # The `heighth` of the box
48
+ # @return [Number]
49
+
50
+ ##
51
+ # Return a nice string representation of the size
52
+ #
53
+ # Overrides `Object#inspect` to more closely mimic MacRuby `Boxed#inspect`.
54
+ #
55
+ # @return [String]
56
+ def inspect
57
+ "#<CGSize width=#{self.width.to_f} height=#{self.height.to_f}>"
58
+ end
59
+
60
+ end
61
+
62
+
63
+ ##
64
+ # Complete definition of a rectangle in a 2D coordinate system
65
+ CGRect = Struct.new(:origin, :size) do
66
+
67
+ # @param origin [CGPoint,#to_point]
68
+ # @param size [CGSize,#to_size]
69
+ def initialize origin = CGPoint.new, size = CGSize.new
70
+ super(origin.to_point, size.to_size)
71
+ end
72
+
73
+ # @!attribute [rw] origin
74
+ # The `origin` point
75
+ # @return [CGPoint,#to_point]
76
+
77
+ # @!attribute [rw] size
78
+ # The `size` of the rectangle
79
+ # @return [CGSize,#to_size]
80
+
81
+ ##
82
+ # Return a nice string representation of the rectangle
83
+ #
84
+ # Overrides `Object#inspect` to more closely mimic MacRuby `Boxed#inspect`.
85
+ #
86
+ # @return [String]
87
+ def inspect
88
+ "#<CGRect origin=#{self.origin.inspect} size=#{self.size.inspect}>"
89
+ end
90
+
91
+ end
92
+
93
+
94
+ require 'uri'
95
+
96
+ ##
97
+ # `accessibility-core` extensions to the `URI` family of classes
98
+ class URI::Generic
99
+ ##
100
+ # Returns the receiver (since the receiver is already a `URI` object)
101
+ #
102
+ # @return [URI::Generic]
103
+ def to_url
104
+ self
105
+ end
106
+ end
107
+
108
+ ##
109
+ # `accessibility-core` extensions to the `String` class
110
+ class String
111
+ ##
112
+ # Parse the receiver into a `URI` object
113
+ #
114
+ # @return [URI::Generic]
115
+ def to_url
116
+ URI.parse self
117
+ end
118
+ end
119
+
120
+
121
+ require 'accessibility/bridge/common'
@@ -0,0 +1,6 @@
1
+ module Accessibility
2
+ module Bridge
3
+ # @return [String]
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require 'accessibility/bridge'
4
+
5
+ class MiniTest::Unit::TestCase
6
+
7
+ def rand_nums count, range = 1_000
8
+ Array.new count do
9
+ rand range
10
+ end
11
+ end
12
+
13
+ def rand_floats count, range = 1_000.0
14
+ Array.new count do
15
+ rand * range
16
+ end
17
+ end
18
+
19
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: accessibility_bridge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mark Rada
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: yard
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.3
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: kramdown
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.14.1
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.14.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake-compiler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.1
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.1
62
+ description: ! 'accessibility_bridge is a wrapper around various bits of Cocoa
63
+
64
+ so that the various accessibility projects can run on CRuby.
65
+
66
+
67
+ Originally extracted from the AXElements project.
68
+
69
+ '
70
+ email: markrada26@gmail.com
71
+ executables: []
72
+ extensions:
73
+ - ext/accessibility/bridge/extconf.rb
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/accessibility/bridge/common.rb
77
+ - lib/accessibility/bridge/macruby.rb
78
+ - lib/accessibility/bridge/mri.rb
79
+ - lib/accessibility/bridge/version.rb
80
+ - lib/accessibility/bridge.rb
81
+ - ext/accessibility/bridge/bridge.c
82
+ - ext/accessibility/bridge/extconf.rb
83
+ - Rakefile
84
+ - README.markdown
85
+ - History.markdown
86
+ - .yardopts
87
+ - test/helper.rb
88
+ homepage: http://github.com/AXElements/accessibility_bridge
89
+ licenses:
90
+ - BSD 3-clause
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: -1210497514143783933
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ segments:
111
+ - 0
112
+ hash: -1210497514143783933
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.24
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: A library for bridging into Cocoa on OSX
119
+ test_files:
120
+ - test/helper.rb