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.
- data/.yardopts +9 -0
- data/History.markdown +36 -0
- data/README.markdown +66 -0
- data/Rakefile +72 -0
- data/ext/accessibility/bridge/ext/accessibility/bridge/bridge.c +490 -0
- data/ext/accessibility/bridge/ext/accessibility/bridge/extconf.rb +22 -0
- data/ext/accessibility/bridge/lib/accessibility/bridge.rb +33 -0
- data/ext/accessibility/bridge/lib/accessibility/bridge/common.rb +57 -0
- data/ext/accessibility/bridge/lib/accessibility/bridge/macruby.rb +185 -0
- data/ext/accessibility/bridge/lib/accessibility/bridge/mri.rb +121 -0
- data/ext/accessibility/bridge/lib/accessibility/bridge/version.rb +6 -0
- data/ext/accessibility/bridge/test/array_test.rb +31 -0
- data/ext/accessibility/bridge/test/boxed_test.rb +23 -0
- data/ext/accessibility/bridge/test/cfrange_test.rb +21 -0
- data/ext/accessibility/bridge/test/cgpoint_test.rb +54 -0
- data/ext/accessibility/bridge/test/cgrect_test.rb +60 -0
- data/ext/accessibility/bridge/test/cgsize_test.rb +54 -0
- data/ext/accessibility/bridge/test/helper.rb +19 -0
- data/ext/accessibility/bridge/test/nsstring_test.rb +22 -0
- data/ext/accessibility/bridge/test/nsurl_test.rb +17 -0
- data/ext/accessibility/bridge/test/object_test.rb +19 -0
- data/ext/accessibility/bridge/test/range_test.rb +16 -0
- data/ext/accessibility/bridge/test/string_test.rb +35 -0
- data/ext/accessibility/bridge/test/uri_test.rb +15 -0
- data/ext/accessibility/core/bridge.h +1 -0
- data/ext/accessibility/core/core.c +705 -0
- data/ext/accessibility/core/extconf.rb +22 -0
- data/ext/accessibility/highlighter/extconf.rb +22 -0
- data/ext/accessibility/highlighter/highlighter.c +7 -0
- data/lib/accessibility/core.rb +12 -0
- data/lib/accessibility/core/core_ext/common.rb +57 -0
- data/lib/accessibility/core/core_ext/macruby.rb +140 -0
- data/lib/accessibility/core/core_ext/mri.rb +121 -0
- data/lib/accessibility/core/macruby.rb +858 -0
- data/lib/accessibility/core/version.rb +6 -0
- data/test/helper.rb +48 -0
- metadata +158 -0
data/.yardopts
ADDED
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
|