accessibility_core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.yardopts +9 -0
  2. data/History.markdown +36 -0
  3. data/README.markdown +66 -0
  4. data/Rakefile +72 -0
  5. data/ext/accessibility/bridge/ext/accessibility/bridge/bridge.c +490 -0
  6. data/ext/accessibility/bridge/ext/accessibility/bridge/extconf.rb +22 -0
  7. data/ext/accessibility/bridge/lib/accessibility/bridge.rb +33 -0
  8. data/ext/accessibility/bridge/lib/accessibility/bridge/common.rb +57 -0
  9. data/ext/accessibility/bridge/lib/accessibility/bridge/macruby.rb +185 -0
  10. data/ext/accessibility/bridge/lib/accessibility/bridge/mri.rb +121 -0
  11. data/ext/accessibility/bridge/lib/accessibility/bridge/version.rb +6 -0
  12. data/ext/accessibility/bridge/test/array_test.rb +31 -0
  13. data/ext/accessibility/bridge/test/boxed_test.rb +23 -0
  14. data/ext/accessibility/bridge/test/cfrange_test.rb +21 -0
  15. data/ext/accessibility/bridge/test/cgpoint_test.rb +54 -0
  16. data/ext/accessibility/bridge/test/cgrect_test.rb +60 -0
  17. data/ext/accessibility/bridge/test/cgsize_test.rb +54 -0
  18. data/ext/accessibility/bridge/test/helper.rb +19 -0
  19. data/ext/accessibility/bridge/test/nsstring_test.rb +22 -0
  20. data/ext/accessibility/bridge/test/nsurl_test.rb +17 -0
  21. data/ext/accessibility/bridge/test/object_test.rb +19 -0
  22. data/ext/accessibility/bridge/test/range_test.rb +16 -0
  23. data/ext/accessibility/bridge/test/string_test.rb +35 -0
  24. data/ext/accessibility/bridge/test/uri_test.rb +15 -0
  25. data/ext/accessibility/core/bridge.h +1 -0
  26. data/ext/accessibility/core/core.c +705 -0
  27. data/ext/accessibility/core/extconf.rb +22 -0
  28. data/ext/accessibility/highlighter/extconf.rb +22 -0
  29. data/ext/accessibility/highlighter/highlighter.c +7 -0
  30. data/lib/accessibility/core.rb +12 -0
  31. data/lib/accessibility/core/core_ext/common.rb +57 -0
  32. data/lib/accessibility/core/core_ext/macruby.rb +140 -0
  33. data/lib/accessibility/core/core_ext/mri.rb +121 -0
  34. data/lib/accessibility/core/macruby.rb +858 -0
  35. data/lib/accessibility/core/version.rb +6 -0
  36. data/test/helper.rb +48 -0
  37. metadata +158 -0
@@ -0,0 +1,23 @@
1
+ require 'test/helper'
2
+
3
+ if on_macruby?
4
+
5
+ ##
6
+ # Boxed is the common ancestor of all structs defined by bridge support
7
+ # in MacRuby.
8
+ class BoxedTest < MiniTest::Unit::TestCase
9
+
10
+ def test_to_ax_raises_for_arbitrary_boxes
11
+ assert_raises(NotImplementedError) { NSEdgeInsets.new.to_ax }
12
+ end
13
+
14
+ def test_to_ruby # bet you would never dare to abuse the parser like this!
15
+ assert_equal CGPointZero, CGPointZero .to_ax.to_ruby
16
+ assert_equal CGSize.new(10,10), CGSize.new(10, 10) .to_ax.to_ruby
17
+ assert_equal CGRectMake(1,2,3,4), CGRectMake(1,2,3,4).to_ax.to_ruby
18
+ assert_equal 1..10, CFRange.new(1, 10) .to_ax.to_ruby
19
+ assert_equal Range.new(1,10), CFRange.new(1,10) .to_ax.to_ruby
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'test/helper'
2
+
3
+ if on_macruby?
4
+ class CFRangeTest < MiniTest::Unit::TestCase
5
+
6
+ def test_to_ax
7
+ range = CFRange.new(5, 4)
8
+ value = range.to_ax
9
+ ptr = Pointer.new CFRange.type
10
+ AXValueGetValue(value, 4, ptr)
11
+ assert_equal range, ptr.value, 'range makes a value'
12
+ end
13
+
14
+ def test_cf_range_to_ruby_range_compatability
15
+ assert_equal CFRangeMake(1,10).to_ax, (1..10 ).to_ax
16
+ assert_equal CFRangeMake(1, 9).to_ax, (1...10 ).to_ax
17
+ assert_equal CFRangeMake(0, 3).to_ax, (0..2 ).to_ax
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ require 'test/helper'
2
+
3
+ class CGPointTest < MiniTest::Unit::TestCase
4
+
5
+ def test_attributes
6
+ p = CGPoint.new
7
+ assert_respond_to p, :x
8
+ assert_respond_to p, :y
9
+ end
10
+
11
+ def test_to_a
12
+ p = CGPoint.new 1, 2
13
+ assert_equal [1, 2], p.to_a
14
+ end
15
+
16
+ def test_to_point
17
+ p = CGPoint.new
18
+ assert_same p, p.to_point
19
+
20
+ p = CGPoint.new 4, 2
21
+ assert_same p, p.to_point
22
+ end
23
+
24
+ def test_initialize
25
+ p = CGPoint.new
26
+ assert_equal 0, p.x
27
+ assert_equal 0, p.y
28
+
29
+ x, y = rand_nums 2
30
+ p = CGPoint.new x, y
31
+ assert_equal x, p.x
32
+ assert_equal y, p.y
33
+
34
+ x, y = rand_floats 2
35
+ p = CGPoint.new x, y
36
+ assert_equal x, p.x
37
+ assert_equal y, p.y
38
+ end
39
+
40
+ def test_inspect
41
+ assert_match /Point x=1.0 y=2.0>/, CGPoint.new(1, 2).inspect
42
+ assert_match /Point x=3.0 y=5.0>/, CGPoint.new(3, 5).inspect
43
+ end
44
+
45
+ if on_macruby?
46
+ def test_to_ax
47
+ value = CGPointZero.to_ax
48
+ ptr = Pointer.new CGPoint.type
49
+ AXValueGetValue(value, 1, ptr)
50
+ assert_equal CGPointZero, ptr.value, 'point makes a value'
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,60 @@
1
+ require 'test/helper'
2
+
3
+ class CGRectTest < MiniTest::Unit::TestCase
4
+
5
+ def test_attributes
6
+ r = CGRect.new
7
+ assert_respond_to r, :origin
8
+ assert_respond_to r, :size
9
+ end
10
+
11
+ def test_to_a # we want to match MacRuby here
12
+ r = CGRect.new
13
+ assert_equal [CGPoint.new, CGSize.new], r.to_a
14
+ end
15
+
16
+ def test_to_rect
17
+ r = CGRect.new
18
+ assert_same r, r.to_rect
19
+
20
+ r = CGRect.new CGPoint.new(4, 2), CGSize.new(8, 3)
21
+ assert_same r, r.to_rect
22
+ end
23
+
24
+ def test_initialize
25
+ r = CGRect.new
26
+ assert_equal 0, r.origin.x
27
+ assert_equal 0, r.origin.y
28
+ assert_equal 0, r.size.width
29
+ assert_equal 0, r.size.height
30
+
31
+ x, y, w, h = rand_nums 4
32
+ p = CGPoint.new x, y
33
+ s = CGSize.new w, h
34
+ r = CGRect.new p, s
35
+ assert_equal p, r.origin
36
+ assert_equal s, r.size
37
+ end
38
+
39
+ def test_inspect
40
+ p1 = CGPoint.new *rand_nums(2)
41
+ p2 = CGPoint.new *rand_nums(2)
42
+ s1 = CGSize.new *rand_nums(2)
43
+ s2 = CGSize.new *rand_nums(2)
44
+ r1 = CGRect.new p1, s1
45
+ r2 = CGRect.new p2, s2
46
+ assert_match /Rect origin=#{p1.inspect} size=#{s1.inspect}>/, r1.inspect
47
+ assert_match /Rect origin=#{p2.inspect} size=#{s2.inspect}>/, r2.inspect
48
+ end
49
+
50
+ if on_macruby?
51
+ def test_to_ax
52
+ value = CGRectZero.to_ax
53
+ ptr = Pointer.new CGRect.type
54
+ AXValueGetValue(value, 3, ptr)
55
+ assert_equal CGRectZero, ptr.value, 'rect makes a value'
56
+ end
57
+ end
58
+
59
+
60
+ end
@@ -0,0 +1,54 @@
1
+ require 'test/helper'
2
+
3
+ class CGSizeTest < MiniTest::Unit::TestCase
4
+
5
+ def test_attributes
6
+ s = CGSize.new
7
+ assert_respond_to s, :width
8
+ assert_respond_to s, :height
9
+ end
10
+
11
+ def test_to_a
12
+ s = CGSize.new 1, 2
13
+ assert_equal [1, 2], s.to_a
14
+ end
15
+
16
+ def test_to_size
17
+ s = CGSize.new
18
+ assert_same s, s.to_size
19
+
20
+ s = CGSize.new 4, 2
21
+ assert_same s, s.to_size
22
+ end
23
+
24
+ def test_initialize
25
+ s = CGSize.new
26
+ assert_equal 0, s.width
27
+ assert_equal 0, s.height
28
+
29
+ w, h = rand_nums 2
30
+ s = CGSize.new w, h
31
+ assert_equal w, s.width
32
+ assert_equal h, s.height
33
+
34
+ w, h = rand_floats 2
35
+ s = CGSize.new w, h
36
+ assert_equal w, s.width
37
+ assert_equal h, s.height
38
+ end
39
+
40
+ def test_inspect
41
+ assert_match /Size width=1.0 height=2.0>/, CGSize.new(1, 2).inspect
42
+ assert_match /Size width=3.0 height=5.0>/, CGSize.new(3, 5).inspect
43
+ end
44
+
45
+ if on_macruby?
46
+ def test_to_ax
47
+ value = CGSizeZero.to_ax
48
+ ptr = Pointer.new CGSize.type
49
+ AXValueGetValue(value, 2, ptr)
50
+ assert_equal CGSizeZero, ptr.value, 'size makes a value'
51
+ end
52
+ end
53
+
54
+ end
@@ -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
@@ -0,0 +1,22 @@
1
+ require 'test/helper'
2
+
3
+ if on_macruby?
4
+ class NSStringTest < MiniTest::Unit::TestCase
5
+
6
+ def test_to_url
7
+ site = 'http://marketcircle.com/'
8
+ url = site.to_url
9
+ refute_nil url
10
+ assert_equal NSURL.URLWithString(site), url
11
+
12
+ file = 'file://localhost/Applications/Calculator.app/'
13
+ url = file.to_url
14
+ refute_nil url
15
+ assert_equal NSURL.fileURLWithPath('/Applications/Calculator.app/'), url
16
+
17
+ void = 'not a url at all'
18
+ assert_nil void.to_url
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'test/helper'
2
+
3
+ if on_macruby?
4
+ class TestNSURL < MiniTest::Unit::TestCase
5
+
6
+ def test_to_url_returns_self
7
+ url = NSURL.URLWithString 'http://macruby.org'
8
+ assert_same url, url.to_url
9
+ end
10
+
11
+ def test_to_s
12
+ s = 'http://marketcircle.com'
13
+ assert_equal s, NSURL.URLWithString(s).to_s
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ require 'test/helper'
2
+
3
+ class ObjectTest < MiniTest::Unit::TestCase
4
+
5
+ if on_macruby?
6
+ def test_to_ruby
7
+ assert_same Object, Object.to_ruby
8
+ assert_same 10, 10.to_ruby
9
+ end
10
+
11
+ def test_to_ax
12
+ assert_same Object, Object.to_ax
13
+ assert_same 10, 10.to_ax
14
+ end
15
+ end
16
+
17
+ # we may have other tests for extensions to the Object class
18
+
19
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/helper'
2
+
3
+ if on_macruby?
4
+ class RangeTest < MiniTest::Unit::TestCase
5
+
6
+ def test_to_ax
7
+ assert_equal CFRange.new(5, 4).to_ax, (5..8).to_ax
8
+ end
9
+
10
+ def test_to_ax_raises_when_given_negative_indicies
11
+ assert_raises(ArgumentError) { (1..-10).to_ax }
12
+ assert_raises(ArgumentError) { (-5...10).to_ax }
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ require 'test/helper'
2
+
3
+ class StringTest < MiniTest::Unit::TestCase
4
+
5
+ if on_macruby?
6
+ def test_to_url
7
+ site = 'http://marketcircle.com/'
8
+ url = site.to_url
9
+ refute_nil url
10
+ assert_equal NSURL.URLWithString(site), url
11
+
12
+ file = 'file://localhost/Applications/Calculator.app/'
13
+ url = file.to_url
14
+ refute_nil url
15
+ assert_equal NSURL.fileURLWithPath('/Applications/Calculator.app/'), url
16
+
17
+ assert_nil 'not a url at all'.to_url
18
+ end
19
+
20
+ else
21
+
22
+ def test_to_url
23
+ site = 'http://marketcircle.com/'
24
+ url = site.to_url
25
+ refute_nil url
26
+ assert_equal URI.parse(site), url
27
+
28
+ # Ruby's URI class does not handle the file scheme :(
29
+ # @todo propose the change upstream at some point?
30
+
31
+ assert_raises(URI::InvalidURIError) { 'not a url at all'.to_url }
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,15 @@
1
+ require 'test/helper'
2
+
3
+ unless on_macruby?
4
+ class TestURIExtensions < MiniTest::Unit::TestCase
5
+
6
+ def test_to_url_returns_self
7
+ url = URI.parse 'http://macruby.org'
8
+ assert_same url, url.to_url
9
+
10
+ url = URI.parse 'ftp://herp.com'
11
+ assert_same url, url.to_url
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ #include "../bridge/ext/accessibility/bridge/bridge.c"
@@ -0,0 +1,705 @@
1
+ #include "ruby.h"
2
+
3
+ #ifdef NOT_MACRUBY /* This entire extension is pointless when running on MacRuby */
4
+
5
+ #import <Cocoa/Cocoa.h>
6
+ #include "bridge.h"
7
+
8
+
9
+ static ID ivar_attrs;
10
+ static ID ivar_param_attrs;
11
+ static ID ivar_actions;
12
+ static ID ivar_pid;
13
+ static ID ivar_key_rate;
14
+
15
+ static ID sel_to_f;
16
+
17
+ static ID rate_very_slow;
18
+ static ID rate_slow;
19
+ static ID rate_normal;
20
+ static ID rate_default;
21
+ static ID rate_fast;
22
+ static ID rate_zomg;
23
+
24
+
25
+ static
26
+ VALUE
27
+ handle_error(VALUE self, AXError code)
28
+ {
29
+ // TODO port the error handler from AXElements
30
+ switch (code)
31
+ {
32
+ case kAXErrorInvalidUIElement:
33
+ rb_raise(rb_eArgError, "invalid element (probably dead)");
34
+ case kAXErrorAttributeUnsupported:
35
+ rb_raise(rb_eArgError, "attribute unsupported");
36
+ case kAXErrorActionUnsupported:
37
+ rb_raise(rb_eArgError, "action unsupported");
38
+ case kAXErrorParameterizedAttributeUnsupported:
39
+ rb_raise(rb_eArgError, "parameterized attribute unsupported");
40
+ default:
41
+ rb_raise(rb_eRuntimeError, "you done goofed [%d]", code);
42
+ }
43
+
44
+ return Qnil;
45
+ }
46
+
47
+
48
+ static
49
+ VALUE
50
+ rb_acore_application_for(VALUE self, VALUE pid)
51
+ {
52
+ NSDate* date = [NSDate date];
53
+ [[NSRunLoop currentRunLoop] runUntilDate:date];
54
+ CFRelease(date);
55
+
56
+ pid_t the_pid = NUM2PIDT(pid);
57
+ NSRunningApplication* running_app =
58
+ [NSRunningApplication runningApplicationWithProcessIdentifier:the_pid];
59
+
60
+ if (running_app) {
61
+ VALUE app = wrap_ref(AXUIElementCreateApplication(the_pid));
62
+ CFRelease(running_app);
63
+ return app;
64
+ }
65
+
66
+ rb_raise(
67
+ rb_eArgError,
68
+ "pid `%d' must belong to a running application",
69
+ the_pid
70
+ );
71
+
72
+ return Qnil; // unreachable
73
+ }
74
+
75
+
76
+ static
77
+ VALUE
78
+ rb_acore_system_wide(VALUE self)
79
+ {
80
+ return wrap_ref(AXUIElementCreateSystemWide());
81
+ }
82
+
83
+
84
+ static
85
+ VALUE
86
+ rb_acore_key_rate(VALUE self)
87
+ {
88
+ return rb_ivar_get(self, ivar_key_rate);
89
+ }
90
+
91
+
92
+ static
93
+ VALUE
94
+ rb_acore_set_key_rate(VALUE self, VALUE rate)
95
+ {
96
+ if (TYPE(rate) == T_SYMBOL) {
97
+ ID key_rate = SYM2ID(rate);
98
+ if (key_rate == rate_very_slow)
99
+ rate = DBL2NUM(0.9);
100
+ else if (key_rate == rate_slow)
101
+ rate = DBL2NUM(0.09);
102
+ else if (key_rate == rate_normal || rate == rate_default)
103
+ rate = DBL2NUM(0.009);
104
+ else if (key_rate == rate_fast)
105
+ rate = DBL2NUM(0.0009);
106
+ else if (key_rate == rate_zomg)
107
+ rate = DBL2NUM(0.00009);
108
+ else
109
+ rb_raise(rb_eArgError, "Unknown rate `%s'", rb_id2name(key_rate));
110
+ }
111
+ else {
112
+ rate = rb_funcall(rate, sel_to_f, 0);
113
+ }
114
+
115
+ return rb_ivar_set(self, ivar_key_rate, rate);
116
+ }
117
+
118
+
119
+ static
120
+ int
121
+ acore_is_system_wide(VALUE other)
122
+ {
123
+ AXUIElementRef system = AXUIElementCreateSystemWide();
124
+ int result = CFEqual(unwrap_ref(other), system);
125
+ CFRelease(system);
126
+ return result;
127
+ }
128
+ #define IS_SYSTEM_WIDE(x) (acore_is_system_wide(x))
129
+
130
+
131
+ static
132
+ VALUE
133
+ rb_acore_attributes(VALUE self)
134
+ {
135
+ VALUE cached_attrs = rb_ivar_get(self, ivar_attrs);
136
+ if (cached_attrs != Qnil)
137
+ return cached_attrs;
138
+
139
+ CFArrayRef attrs = NULL;
140
+ AXError code = AXUIElementCopyAttributeNames(unwrap_ref(self), &attrs);
141
+ switch (code)
142
+ {
143
+ case kAXErrorSuccess:
144
+ cached_attrs = wrap_array_strings(attrs);
145
+ rb_ivar_set(self, ivar_attrs, cached_attrs);
146
+ CFRelease(attrs);
147
+ return cached_attrs;
148
+ case kAXErrorInvalidUIElement:
149
+ return rb_ary_new();
150
+ default:
151
+ // TODO we should actually allow for a grace period and try again in
152
+ // every case where would be deferring to the default error handler,
153
+ // and maybe even in every case where we get a non-zero result code
154
+ //
155
+ // WE SHOULD HANDLE THINGS LIKE FAILURE AND CANNOT COMPLETE LIKE THIS
156
+ return handle_error(self, code);
157
+ }
158
+ }
159
+
160
+
161
+ static
162
+ VALUE
163
+ rb_acore_attribute(VALUE self, VALUE name)
164
+ {
165
+ CFTypeRef attr = NULL;
166
+ CFStringRef attr_name = unwrap_string(name);
167
+ AXError code = AXUIElementCopyAttributeValue(
168
+ unwrap_ref(self),
169
+ attr_name,
170
+ &attr
171
+ );
172
+ CFRelease(attr_name);
173
+ switch (code)
174
+ {
175
+ case kAXErrorSuccess:
176
+ return to_ruby(attr);
177
+ case kAXErrorFailure:
178
+ case kAXErrorNoValue:
179
+ case kAXErrorInvalidUIElement:
180
+ case kAXErrorAttributeUnsupported:
181
+ return Qnil;
182
+ default:
183
+ return handle_error(self, code);
184
+ }
185
+ }
186
+
187
+
188
+ static
189
+ VALUE
190
+ rb_acore_size_of(VALUE self, VALUE name)
191
+ {
192
+ CFIndex size = 0;
193
+ CFStringRef attr_name = unwrap_string(name);
194
+ AXError code = AXUIElementGetAttributeValueCount(
195
+ unwrap_ref(self),
196
+ attr_name,
197
+ &size
198
+ );
199
+ CFRelease(attr_name);
200
+ switch (code)
201
+ {
202
+ case kAXErrorSuccess:
203
+ return INT2FIX(size);
204
+ case kAXErrorFailure:
205
+ case kAXErrorNoValue:
206
+ case kAXErrorInvalidUIElement:
207
+ return INT2FIX(0);
208
+ default:
209
+ return handle_error(self, code);
210
+ }
211
+ }
212
+
213
+
214
+ static
215
+ VALUE
216
+ rb_acore_is_writable(VALUE self, VALUE name)
217
+ {
218
+ Boolean result;
219
+ CFStringRef attr_name = unwrap_string(name);
220
+ AXError code = AXUIElementIsAttributeSettable(
221
+ unwrap_ref(self),
222
+ attr_name,
223
+ &result
224
+ );
225
+ CFRelease(attr_name);
226
+ switch (code)
227
+ {
228
+ case kAXErrorSuccess:
229
+ return (result ? Qtrue : Qfalse);
230
+ case kAXErrorFailure:
231
+ case kAXErrorNoValue:
232
+ case kAXErrorInvalidUIElement:
233
+ return Qfalse;
234
+ default:
235
+ return handle_error(self, code);
236
+ }
237
+ }
238
+
239
+
240
+ static
241
+ VALUE
242
+ rb_acore_set(VALUE self, VALUE name, VALUE value)
243
+ {
244
+ CFTypeRef ax_value = to_ax(value);
245
+ CFStringRef attr_name = unwrap_string(name);
246
+ AXError code = AXUIElementSetAttributeValue(
247
+ unwrap_ref(self),
248
+ attr_name,
249
+ ax_value
250
+ );
251
+ switch (code)
252
+ {
253
+ case kAXErrorSuccess:
254
+ return value;
255
+ default:
256
+ return handle_error(self, code); // name, value
257
+ }
258
+ }
259
+
260
+
261
+ static
262
+ VALUE
263
+ rb_acore_role(VALUE self)
264
+ {
265
+ CFTypeRef value = NULL;
266
+ AXError code = AXUIElementCopyAttributeValue(
267
+ unwrap_ref(self),
268
+ kAXRoleAttribute,
269
+ &value
270
+ );
271
+ switch (code)
272
+ {
273
+ case kAXErrorSuccess:
274
+ return wrap_string(value);
275
+ case kAXErrorNoValue:
276
+ case kAXErrorInvalidUIElement:
277
+ return Qnil;
278
+ default:
279
+ return handle_error(self, code);
280
+ }
281
+ }
282
+
283
+
284
+ static
285
+ VALUE
286
+ rb_acore_subrole(VALUE self)
287
+ {
288
+ CFTypeRef value = NULL;
289
+ AXError code = AXUIElementCopyAttributeValue(
290
+ unwrap_ref(self),
291
+ kAXSubroleAttribute,
292
+ &value
293
+ );
294
+ switch (code)
295
+ {
296
+ case kAXErrorSuccess:
297
+ return wrap_string((CFStringRef)value);
298
+ case kAXErrorFailure:
299
+ case kAXErrorNoValue:
300
+ case kAXErrorInvalidUIElement:
301
+ case kAXErrorAttributeUnsupported:
302
+ return Qnil;
303
+ default:
304
+ return handle_error(self, code);
305
+ }
306
+ }
307
+
308
+
309
+ static
310
+ VALUE
311
+ rb_acore_parent(VALUE self)
312
+ {
313
+ CFTypeRef value = NULL;
314
+ AXError code = AXUIElementCopyAttributeValue(
315
+ unwrap_ref(self),
316
+ kAXParentAttribute,
317
+ &value
318
+ );
319
+ switch (code)
320
+ {
321
+ case kAXErrorSuccess:
322
+ return wrap_ref(value);
323
+ case kAXErrorFailure:
324
+ case kAXErrorNoValue:
325
+ case kAXErrorInvalidUIElement:
326
+ case kAXErrorAttributeUnsupported:
327
+ return Qnil;
328
+ default:
329
+ return handle_error(self, code);
330
+ }
331
+ }
332
+
333
+
334
+ static
335
+ VALUE
336
+ rb_acore_children(VALUE self)
337
+ {
338
+ CFTypeRef value = NULL;
339
+ AXError code = AXUIElementCopyAttributeValue(
340
+ unwrap_ref(self),
341
+ kAXChildrenAttribute,
342
+ &value
343
+ );
344
+ switch (code)
345
+ {
346
+ case kAXErrorSuccess:
347
+ return wrap_array_refs(value);
348
+ case kAXErrorNoValue:
349
+ case kAXErrorInvalidUIElement:
350
+ return rb_ary_new();
351
+ default:
352
+ return handle_error(self, code);
353
+ }
354
+ }
355
+
356
+
357
+ static
358
+ VALUE
359
+ rb_acore_value(VALUE self)
360
+ {
361
+ CFTypeRef value = NULL;
362
+ AXError code = AXUIElementCopyAttributeValue(
363
+ unwrap_ref(self),
364
+ kAXValueAttribute,
365
+ &value
366
+ );
367
+ switch (code)
368
+ {
369
+ case kAXErrorSuccess:
370
+ return to_ruby(value);
371
+ default:
372
+ return handle_error(self, code);
373
+ }
374
+ }
375
+
376
+
377
+ static
378
+ VALUE
379
+ rb_acore_pid(VALUE self)
380
+ {
381
+ VALUE cached_pid = rb_ivar_get(self, ivar_pid);
382
+ if (cached_pid != Qnil)
383
+ return cached_pid;
384
+
385
+ pid_t pid = 0;
386
+ AXError code = AXUIElementGetPid(unwrap_ref(self), &pid);
387
+
388
+ switch (code)
389
+ {
390
+ case kAXErrorSuccess:
391
+ break;
392
+ case kAXErrorInvalidUIElement:
393
+ if (IS_SYSTEM_WIDE(self)) {
394
+ pid = 0;
395
+ break;
396
+ }
397
+ default:
398
+ handle_error(self, code);
399
+ }
400
+
401
+ cached_pid = PIDT2NUM(pid);
402
+ rb_ivar_set(self, ivar_pid, cached_pid);
403
+ return cached_pid;
404
+ }
405
+
406
+
407
+ static
408
+ VALUE
409
+ rb_acore_parameterized_attributes(VALUE self)
410
+ {
411
+ VALUE cached_attrs = rb_ivar_get(self, ivar_param_attrs);
412
+ if (cached_attrs != Qnil)
413
+ return cached_attrs;
414
+
415
+ CFArrayRef attrs = NULL;
416
+ AXError code = AXUIElementCopyParameterizedAttributeNames(
417
+ unwrap_ref(self),
418
+ &attrs
419
+ );
420
+ switch (code)
421
+ {
422
+ case kAXErrorSuccess:
423
+ cached_attrs = wrap_array_strings(attrs);
424
+ rb_ivar_set(self, ivar_param_attrs, cached_attrs);
425
+ CFRelease(attrs);
426
+ return cached_attrs;
427
+ case kAXErrorInvalidUIElement:
428
+ return rb_ary_new();
429
+ default:
430
+ return handle_error(self, code);
431
+ }
432
+ }
433
+
434
+
435
+ static
436
+ VALUE
437
+ rb_acore_parameterized_attribute(VALUE self, VALUE name, VALUE parameter)
438
+ {
439
+ CFTypeRef param = to_ax(parameter);
440
+ CFTypeRef attr = NULL;
441
+ CFStringRef attr_name = unwrap_string(name);
442
+ AXError code = AXUIElementCopyParameterizedAttributeValue(
443
+ unwrap_ref(self),
444
+ attr_name,
445
+ param,
446
+ &attr
447
+ );
448
+ CFRelease(param);
449
+ CFRelease(attr_name);
450
+ switch (code)
451
+ {
452
+ case kAXErrorSuccess:
453
+ return to_ruby(attr);
454
+ case kAXErrorNoValue:
455
+ case kAXErrorInvalidUIElement:
456
+ return Qnil;
457
+ default:
458
+ return handle_error(self, code);
459
+ }
460
+ }
461
+
462
+
463
+ static
464
+ VALUE
465
+ rb_acore_actions(VALUE self)
466
+ {
467
+ VALUE cached_actions = rb_ivar_get(self, ivar_actions);
468
+ if (cached_actions != Qnil)
469
+ return cached_actions;
470
+
471
+ CFArrayRef actions = NULL;
472
+ AXError code = AXUIElementCopyActionNames(unwrap_ref(self), &actions);
473
+ switch (code)
474
+ {
475
+ case kAXErrorSuccess:
476
+ cached_actions = wrap_array_strings(actions);
477
+ rb_ivar_set(self, ivar_actions, cached_actions);
478
+ CFRelease(actions);
479
+ return cached_actions;
480
+ case kAXErrorInvalidUIElement:
481
+ return rb_ary_new();
482
+ default:
483
+ return handle_error(self, code);
484
+ }
485
+ }
486
+
487
+
488
+ static
489
+ VALUE
490
+ rb_acore_perform(VALUE self, VALUE name)
491
+ {
492
+ CFStringRef action = unwrap_string(name);
493
+ AXError code = AXUIElementPerformAction(unwrap_ref(self), action);
494
+
495
+ CFRelease(action);
496
+ switch (code)
497
+ {
498
+ case kAXErrorSuccess:
499
+ return Qtrue;
500
+ case kAXErrorInvalidUIElement:
501
+ return Qfalse;
502
+ default:
503
+ return handle_error(self, code);
504
+ }
505
+ }
506
+
507
+
508
+ static
509
+ VALUE
510
+ rb_acore_post(VALUE self, VALUE events)
511
+ {
512
+ events = rb_ary_to_ary(events);
513
+ long length = RARRAY_LEN(events);
514
+ useconds_t sleep_time = NUM2DBL(rb_ivar_get(rb_cElement, ivar_key_rate)) * 100000;
515
+
516
+ // CGCharCode key_char = 0; // TODO this value seems to not matter?
517
+ VALUE pair;
518
+ CGKeyCode virtual_key;
519
+ int key_state;
520
+ AXError code;
521
+
522
+
523
+ for (long i = 0; i < length; i++) {
524
+ pair = rb_ary_entry(events, i);
525
+ virtual_key = NUM2INT(rb_ary_entry(pair, 0));
526
+ key_state = rb_ary_entry(pair, 1) == Qtrue ? true : false;
527
+ code = AXUIElementPostKeyboardEvent(
528
+ unwrap_ref(self),
529
+ 0,
530
+ virtual_key,
531
+ key_state
532
+ );
533
+ switch (code)
534
+ {
535
+ case kAXErrorSuccess:
536
+ break;
537
+ default:
538
+ handle_error(self, code);
539
+ }
540
+
541
+ usleep(sleep_time);
542
+ }
543
+
544
+ return self;
545
+ }
546
+
547
+
548
+ static
549
+ VALUE
550
+ rb_acore_is_invalid(VALUE self)
551
+ {
552
+ CFTypeRef value = NULL;
553
+ AXError code = AXUIElementCopyAttributeValue(
554
+ unwrap_ref(self),
555
+ kAXRoleAttribute,
556
+ &value
557
+ );
558
+ if (value)
559
+ CFRelease(value);
560
+ return (code == kAXErrorInvalidUIElement ? Qtrue : Qfalse);
561
+ }
562
+
563
+
564
+ static
565
+ VALUE
566
+ rb_acore_application(VALUE self)
567
+ {
568
+ return rb_acore_application_for(rb_cElement, rb_acore_pid(self));
569
+ }
570
+
571
+
572
+ static
573
+ VALUE
574
+ rb_acore_set_timeout_to(VALUE self, VALUE seconds)
575
+ {
576
+ float timeout = NUM2DBL(seconds);
577
+ AXError code = AXUIElementSetMessagingTimeout(unwrap_ref(self), timeout);
578
+
579
+ switch (code)
580
+ {
581
+ case kAXErrorSuccess:
582
+ return seconds;
583
+ default:
584
+ return handle_error(self, code); // seconds
585
+ }
586
+ }
587
+
588
+
589
+ static
590
+ VALUE
591
+ rb_acore_element_at(VALUE self, VALUE point)
592
+ {
593
+ if (self == rb_cElement)
594
+ self = rb_acore_system_wide(self);
595
+
596
+ AXUIElementRef ref = NULL;
597
+ CGPoint p = unwrap_point(point);
598
+ AXError code = AXUIElementCopyElementAtPosition(
599
+ unwrap_ref(self),
600
+ p.x,
601
+ p.y,
602
+ &ref
603
+ );
604
+ switch (code)
605
+ {
606
+ case kAXErrorSuccess:
607
+ return wrap_ref(ref);
608
+ case kAXErrorNoValue:
609
+ return Qnil;
610
+ case kAXErrorInvalidUIElement:
611
+ if (!IS_SYSTEM_WIDE(self))
612
+ return rb_acore_element_at(rb_acore_system_wide(rb_cElement), point);
613
+ else
614
+ return Qnil;
615
+ default:
616
+ return handle_error(self, code); // point, nil, nil
617
+ }
618
+ }
619
+
620
+
621
+ static
622
+ VALUE
623
+ rb_acore_equality(VALUE self, VALUE other)
624
+ {
625
+ if (CLASS_OF(other) == rb_cElement)
626
+ if (CFEqual(unwrap_ref(self), unwrap_ref(other)))
627
+ return Qtrue;
628
+ return Qfalse;
629
+ }
630
+ #endif
631
+
632
+
633
+ void
634
+ Init_core()
635
+ {
636
+ #ifdef NOT_MACRUBY
637
+ if (!AXAPIEnabled())
638
+ rb_raise(
639
+ rb_eRuntimeError,
640
+ "\n" \
641
+ "------------------------------------------------------------------------\n" \
642
+ "Universal Access is disabled on this machine.\n" \
643
+ "Please enable it in the System Preferences.\n" \
644
+ "See https://github.com/Marketcircle/AXElements#getting-setup\n" \
645
+ "------------------------------------------------------------------------\n"
646
+ );
647
+
648
+ Init_bridge();
649
+
650
+ // these should be defined by now
651
+ rb_mAccessibility = rb_const_get(rb_cObject, rb_intern("Accessibility"));
652
+ rb_cElement = rb_define_class_under(rb_mAccessibility, "Element", rb_cObject);
653
+
654
+ ivar_attrs = rb_intern("@attrs");
655
+ ivar_param_attrs = rb_intern("@param_attrs");
656
+ ivar_actions = rb_intern("@actions");
657
+ ivar_pid = rb_intern("@pid");
658
+ ivar_key_rate = rb_intern("@key_rate");
659
+
660
+ rb_define_singleton_method(rb_cElement, "application_for", rb_acore_application_for, 1);
661
+ rb_define_singleton_method(rb_cElement, "system_wide", rb_acore_system_wide, 0);
662
+ rb_define_singleton_method(rb_cElement, "element_at", rb_acore_element_at, 1);
663
+ rb_define_singleton_method(rb_cElement, "key_rate", rb_acore_key_rate, 0);
664
+ rb_define_singleton_method(rb_cElement, "key_rate=", rb_acore_set_key_rate, 1);
665
+
666
+ sel_to_f = rb_intern("to_f");
667
+ rate_very_slow = rb_intern("very_slow");
668
+ rate_slow = rb_intern("slow");
669
+ rate_normal = rb_intern("normal");
670
+ rate_default = rb_intern("default");
671
+ rate_fast = rb_intern("fast");
672
+ rate_zomg = rb_intern("zomg");
673
+ rb_acore_set_key_rate(rb_cElement, DBL2NUM(0.009)); // initialize the value right now
674
+
675
+ rb_define_method(rb_cElement, "attributes", rb_acore_attributes, 0);
676
+ rb_define_method(rb_cElement, "attribute", rb_acore_attribute, 1);
677
+ rb_define_method(rb_cElement, "size_of", rb_acore_size_of, 1);
678
+ rb_define_method(rb_cElement, "writable?", rb_acore_is_writable, 1);
679
+ rb_define_method(rb_cElement, "set", rb_acore_set, 2);
680
+
681
+ rb_define_method(rb_cElement, "role", rb_acore_role, 0);
682
+ rb_define_method(rb_cElement, "subrole", rb_acore_subrole, 0);
683
+ rb_define_method(rb_cElement, "parent", rb_acore_parent, 0);
684
+ rb_define_method(rb_cElement, "children", rb_acore_children, 0);
685
+ rb_define_method(rb_cElement, "value", rb_acore_value, 0);
686
+ rb_define_method(rb_cElement, "pid", rb_acore_pid, 0);
687
+
688
+ rb_define_method(rb_cElement, "parameterized_attributes", rb_acore_parameterized_attributes, 0);
689
+ rb_define_method(rb_cElement, "parameterized_attribute", rb_acore_parameterized_attribute, 2);
690
+
691
+ rb_define_method(rb_cElement, "actions", rb_acore_actions, 0);
692
+ rb_define_method(rb_cElement, "perform", rb_acore_perform, 1);
693
+ rb_define_method(rb_cElement, "post", rb_acore_post, 1);
694
+
695
+ rb_define_method(rb_cElement, "invalid?", rb_acore_is_invalid, 0);
696
+ rb_define_method(rb_cElement, "set_timeout_to", rb_acore_set_timeout_to, 1);
697
+ // TODO make this meaningful, currently has no effect on calling rb_acore_post
698
+ rb_define_method(rb_cElement, "key_rate", rb_acore_key_rate, 0);
699
+ rb_define_method(rb_cElement, "key_rate=", rb_acore_set_key_rate, 1);
700
+ rb_define_method(rb_cElement, "application", rb_acore_application, 0);
701
+ rb_define_method(rb_cElement, "element_at", rb_acore_element_at, 1);
702
+ rb_define_method(rb_cElement, "==", rb_acore_equality, 1);
703
+
704
+ #endif
705
+ }