accessibility_core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|