AXElements 1.0.0.alpha8 → 1.0.0.alpha9
Sign up to get free protection for your applications and to get access to all the features.
- data/History.markdown +7 -0
- data/README.markdown +4 -7
- data/lib/accessibility/core.rb +0 -3
- data/lib/accessibility/dsl.rb +5 -4
- data/lib/accessibility/version.rb +1 -1
- data/lib/ax/application.rb +49 -0
- data/lib/ax/systemwide.rb +27 -0
- data/lib/ax_elements/core_graphics_workaround.rb +1 -1
- metadata +36 -5
- data/lib/accessibility/screen_recorder.rb +0 -217
data/History.markdown
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
# 1.0.0
|
2
2
|
|
3
3
|
* Added History.markdown to track notable changes
|
4
|
+
* Added `Application.frontmost_application`
|
5
|
+
* Added `Application.menu_bar_owner`
|
6
|
+
* Added `SystemWide.status_bar_items`
|
7
|
+
* Added `SystemWide.desktop`
|
8
|
+
* Added `Application.finder`
|
9
|
+
* Added `Application.dock`
|
10
|
+
* Added `DSL#record` to run a screen recording of the given block
|
4
11
|
|
5
12
|
* Ported `mouse.rb` to C and moved code to [MRMouse](https://github.com/ferrous26/MRMouse)
|
6
13
|
|
data/README.markdown
CHANGED
@@ -64,13 +64,10 @@ The code from the demo video is right here:
|
|
64
64
|
|
65
65
|
## Getting Setup
|
66
66
|
|
67
|
-
You need
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
you should install MacRuby, version 0.12 or newer is required. If you
|
72
|
-
are on Snow Leopard, you will also need to install the
|
73
|
-
[Bridge Support Preview](http://www.macruby.org/blog/2010/10/08/bridgesupport-preview.html).
|
67
|
+
You will need a MacRuby nightly build for installation. You can get help setting
|
68
|
+
up by referencing the
|
69
|
+
[Setup MacRuby](https://github.com/MacRuby/MacRuby/wiki/Setting-up-MacRuby)
|
70
|
+
guide on Github.
|
74
71
|
|
75
72
|
You will also need to make sure you "enable access for assistive devices".
|
76
73
|
This can be done in System Preferences in the Universal Access section:
|
data/lib/accessibility/core.rb
CHANGED
@@ -914,9 +914,6 @@ class NSArray
|
|
914
914
|
def to_size; CGSize.new(first, at(1)) end
|
915
915
|
# @return [CGRect]
|
916
916
|
def to_rect; CGRectMake(*self[0..3]) end
|
917
|
-
##
|
918
|
-
# Override `super` to exploit trivial parallelism.
|
919
|
-
#
|
920
917
|
# @return [Array]
|
921
918
|
def to_ruby
|
922
919
|
map do |obj| obj.to_ruby end
|
data/lib/accessibility/dsl.rb
CHANGED
@@ -693,16 +693,17 @@ module Accessibility::DSL
|
|
693
693
|
alias_method :capture_screen, :screenshot
|
694
694
|
|
695
695
|
##
|
696
|
-
# See
|
696
|
+
# See (ScreenRecorder)[http://rdoc.info/gems/screen_recorder/frames]
|
697
|
+
# for details on the screen recording options
|
697
698
|
#
|
698
699
|
# @param file [String]
|
699
700
|
# @return [String]
|
700
701
|
def record file = nil, &block
|
701
|
-
require '
|
702
|
+
require 'screen_recorder'
|
702
703
|
if file
|
703
|
-
|
704
|
+
ScreenRecorder.record file, &block
|
704
705
|
else
|
705
|
-
|
706
|
+
ScreenRecorder.record &block
|
706
707
|
end
|
707
708
|
end
|
708
709
|
|
data/lib/ax/application.rb
CHANGED
@@ -23,6 +23,55 @@ class AX::Application < AX::Element
|
|
23
23
|
additionalEventParamDescriptor: nil,
|
24
24
|
launchIdentifier: nil
|
25
25
|
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Find and return the dock application
|
29
|
+
#
|
30
|
+
# @return [AX::Application]
|
31
|
+
def dock
|
32
|
+
new 'com.apple.dock'
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Find and return the dock application
|
37
|
+
#
|
38
|
+
# @return [AX::Application]
|
39
|
+
def finder
|
40
|
+
new 'com.apple.finder'
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Find and return the notification center UI app
|
45
|
+
#
|
46
|
+
# Obviously, this will only work on OS X 10.8+
|
47
|
+
#
|
48
|
+
# @return [AX::Application]
|
49
|
+
def notification_center
|
50
|
+
new 'com.apple.notificationcenterui'
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Find and return the application which is frontmost
|
55
|
+
#
|
56
|
+
# This is often, but not necessarily, the same as the app that
|
57
|
+
# owns the menu bar.
|
58
|
+
#
|
59
|
+
# @return [AX::Application]
|
60
|
+
def frontmost_application
|
61
|
+
new NSWorkspace.sharedWorkspace.frontmostApplication
|
62
|
+
end
|
63
|
+
alias_method :frontmost_app, :frontmost_application
|
64
|
+
|
65
|
+
##
|
66
|
+
# Find and return the application which owns the menu bar
|
67
|
+
#
|
68
|
+
# This is often, but not necessarily, the same as the app that
|
69
|
+
# is frontmost.
|
70
|
+
#
|
71
|
+
# @return [AX::Application]
|
72
|
+
def menu_bar_owner
|
73
|
+
new NSWorkspace.sharedWorkspace.menuBarOwningApplication
|
74
|
+
end
|
26
75
|
end
|
27
76
|
|
28
77
|
##
|
data/lib/ax/systemwide.rb
CHANGED
@@ -11,6 +11,33 @@ require 'accessibility/string'
|
|
11
11
|
class AX::SystemWide < AX::Element
|
12
12
|
include Accessibility::String
|
13
13
|
|
14
|
+
class << self
|
15
|
+
##
|
16
|
+
# Find and return the group that represents the dock
|
17
|
+
#
|
18
|
+
# @return [AX::Group]
|
19
|
+
def desktop
|
20
|
+
AX::Application.finder.scroll_areas.first.groups.first
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# @note This currently does not include spotlight or the
|
25
|
+
# notification center as they interact oddly with
|
26
|
+
# accessibility APIs and how AXElements handle errors
|
27
|
+
#
|
28
|
+
# Find and return menu bar items for the system
|
29
|
+
#
|
30
|
+
# That is, menu bar items that do not belong to the current
|
31
|
+
# app, but that belong to the system, such as the clock or
|
32
|
+
# wi-fi menu.
|
33
|
+
#
|
34
|
+
# @return [AX::MenuBarItem]
|
35
|
+
def status_items
|
36
|
+
AX::Application.new('SystemUIServer').menu_bar.children
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
14
41
|
##
|
15
42
|
# Overridden since there is only one way to get the element ref.
|
16
43
|
def initialize
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: AXElements
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 6
|
5
|
-
version: 1.0.0.
|
5
|
+
version: 1.0.0.alpha9
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Mark Rada
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-11 00:00:00 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mouse
|
@@ -19,14 +19,46 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.0.
|
22
|
+
version: 1.0.2
|
23
23
|
type: :runtime
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 1.0.
|
29
|
+
version: 1.0.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: screen_recorder
|
32
|
+
prerelease: false
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 0.1.3
|
39
|
+
type: :runtime
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
prerelease: false
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "4.3"
|
55
|
+
type: :development
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "4.3"
|
30
62
|
- !ruby/object:Gem::Dependency
|
31
63
|
name: yard
|
32
64
|
prerelease: false
|
@@ -81,7 +113,6 @@ files:
|
|
81
113
|
- lib/accessibility/highlighter.rb
|
82
114
|
- lib/accessibility/pretty_printer.rb
|
83
115
|
- lib/accessibility/qualifier.rb
|
84
|
-
- lib/accessibility/screen_recorder.rb
|
85
116
|
- lib/accessibility/statistics.rb
|
86
117
|
- lib/accessibility/string.rb
|
87
118
|
- lib/accessibility/translator.rb
|
@@ -1,217 +0,0 @@
|
|
1
|
-
framework 'AVFoundation'
|
2
|
-
require 'accessibility/version'
|
3
|
-
require 'ax_elements/core_graphics_workaround'
|
4
|
-
|
5
|
-
##
|
6
|
-
# Screen recordings, easy as pie.
|
7
|
-
#
|
8
|
-
# Things that you need to be concerned about:
|
9
|
-
# - screen going to sleep
|
10
|
-
# - short recordings (~1 second) don't work too well; it looks like
|
11
|
-
# the last bit of the buffer does not get saved so the last ~0.5
|
12
|
-
# seconds are not saved to disk (we could add a 0.5 second sleep)
|
13
|
-
# - small memory leak when a recording starts on Mountain Lion (GC)
|
14
|
-
# - constantly leaking memory during recording on Lion (GC)
|
15
|
-
# - run loop hack is not needed if code is already being called from
|
16
|
-
# in a run loop
|
17
|
-
# - pausing is not working...not sure why
|
18
|
-
#
|
19
|
-
class Accessibility::ScreenRecorder
|
20
|
-
|
21
|
-
##
|
22
|
-
# Record the screen while executing the given block. The path to the
|
23
|
-
# recording will be returned.
|
24
|
-
#
|
25
|
-
# The recorder object is yielded.
|
26
|
-
#
|
27
|
-
# @yield
|
28
|
-
# @yieldparam recorder [ScreenRecorder]
|
29
|
-
# @return [String]
|
30
|
-
def self.record file_name = nil
|
31
|
-
raise 'block required' unless block_given?
|
32
|
-
|
33
|
-
recorder = new
|
34
|
-
file_name ? recorder.start(file_name) : recorder.start
|
35
|
-
yield recorder
|
36
|
-
recorder.file
|
37
|
-
|
38
|
-
ensure
|
39
|
-
recorder.stop
|
40
|
-
end
|
41
|
-
|
42
|
-
##
|
43
|
-
# Path to the screen recording. This is `nil` until the screen
|
44
|
-
# recording begins.
|
45
|
-
#
|
46
|
-
# @return [String]
|
47
|
-
attr_reader :file
|
48
|
-
|
49
|
-
##
|
50
|
-
# @todo Expose configuration options at initialie time
|
51
|
-
def initialize
|
52
|
-
@session = AVCaptureSession.alloc.init
|
53
|
-
|
54
|
-
@input = AVCaptureScreenInput.alloc.initWithDisplayID CGMainDisplayID()
|
55
|
-
@input.capturesMouseClicks = true
|
56
|
-
|
57
|
-
@output = AVCaptureMovieFileOutput.alloc.init
|
58
|
-
@output.setDelegate self
|
59
|
-
|
60
|
-
@session.addInput @input
|
61
|
-
@session.addOutput @output
|
62
|
-
|
63
|
-
@sema = Dispatch::Semaphore.new 0
|
64
|
-
end
|
65
|
-
|
66
|
-
##
|
67
|
-
# Synchrnously start recording. You can optionally specify a file
|
68
|
-
# name for the recording; if you do not then a default name will be
|
69
|
-
# provided in the form `~/Movies/TestRecording-20121017123230.mov`
|
70
|
-
# (the timestamp will be different for you).
|
71
|
-
#
|
72
|
-
# @param file_name [String]
|
73
|
-
def start file_name = default_file_name
|
74
|
-
@file = default_file_name
|
75
|
-
file_url = NSURL.fileURLWithPath @file, isDirectory: false
|
76
|
-
|
77
|
-
@session.startRunning
|
78
|
-
@output.startRecordingToOutputFileURL file_url,
|
79
|
-
recordingDelegate: self
|
80
|
-
|
81
|
-
@sema.wait
|
82
|
-
end
|
83
|
-
|
84
|
-
##
|
85
|
-
# Whether or not the recording has begun. This will be `true`
|
86
|
-
# after calling {#start} until {#stop} is called. It will be
|
87
|
-
# `true` while the recording is paused.
|
88
|
-
def started?
|
89
|
-
@output.recording?
|
90
|
-
end
|
91
|
-
|
92
|
-
# ##
|
93
|
-
# # Whether or not the recording has been paused.
|
94
|
-
# def paused?
|
95
|
-
# @output.paused?
|
96
|
-
# end
|
97
|
-
|
98
|
-
##
|
99
|
-
# Duration of the recording, in seconds.
|
100
|
-
#
|
101
|
-
# @return [Float]
|
102
|
-
def length
|
103
|
-
duration = @output.recordedDuration
|
104
|
-
(duration.value.to_f / duration.timescale.to_f)
|
105
|
-
end
|
106
|
-
|
107
|
-
##
|
108
|
-
# Size of the recording on disk, in bytes.
|
109
|
-
#
|
110
|
-
# @return [Fixnum]
|
111
|
-
def size
|
112
|
-
@output.recordedFileSize
|
113
|
-
end
|
114
|
-
|
115
|
-
# ##
|
116
|
-
# # Synchronously pause the recording. You can optionally pass a block
|
117
|
-
# # to this method.
|
118
|
-
# #
|
119
|
-
# # If you pass a block, the recording is paused so that the block
|
120
|
-
# # can execute and recording resumes after the block finishes. If
|
121
|
-
# # you do not pass a block then the recording is paused until you
|
122
|
-
# # call {#resume} on the receiver.
|
123
|
-
# #
|
124
|
-
# # @yield Optionally pass a block
|
125
|
-
# def pause
|
126
|
-
# @output.pauseRecording
|
127
|
-
# wait_for_callback
|
128
|
-
# @sema.wait
|
129
|
-
|
130
|
-
# if block_given?
|
131
|
-
# yield
|
132
|
-
# resume
|
133
|
-
# end
|
134
|
-
# end
|
135
|
-
|
136
|
-
# ##
|
137
|
-
# # Synchronously resume a {#pause}d recording.
|
138
|
-
# def resume
|
139
|
-
# @output.resumeRecording
|
140
|
-
# wait_for_callback
|
141
|
-
# @sema.wait
|
142
|
-
# end
|
143
|
-
|
144
|
-
##
|
145
|
-
# Synchronously stop recording and finish up commiting any data to disk.
|
146
|
-
# A recording cannot be {#start}ed again after it has been stopped; if
|
147
|
-
# you want to pause a recording then you should use {#pause} instead.
|
148
|
-
def stop
|
149
|
-
@session.stopRunning
|
150
|
-
@output.stopRecording
|
151
|
-
@sema.wait
|
152
|
-
wait_for_callback
|
153
|
-
@sema.wait
|
154
|
-
end
|
155
|
-
|
156
|
-
|
157
|
-
# @!group AVCaptureFileOutputDelegate
|
158
|
-
|
159
|
-
def captureOutput captureOutput, didOutputSampleBuffer:sampleBuffer, fromConnection:connection
|
160
|
-
# gets called for every chunk of the recording getting committed to disk
|
161
|
-
end
|
162
|
-
|
163
|
-
def captureOutput captureOutput, didDropSampleBuffer:sampleBuffer, fromConnection:connection
|
164
|
-
NSLog("Error: dropped same data from recording")
|
165
|
-
end
|
166
|
-
|
167
|
-
|
168
|
-
# @!group AVCaptureFileOutputRecordingDelegate
|
169
|
-
|
170
|
-
def captureOutput captureOutput, didFinishRecordingToOutputFileAtURL:outputFileURL, fromConnections:connections, error:error
|
171
|
-
NSLog('Finishing')
|
172
|
-
CFRunLoopStop(CFRunLoopGetCurrent())
|
173
|
-
@sema.signal
|
174
|
-
end
|
175
|
-
|
176
|
-
def captureOutput captureOutput, didPauseRecordingToOutputFileAtURL:fileURL, fromConnections:connections
|
177
|
-
NSLog('Pausing')
|
178
|
-
CFRunLoopStop(CFRunLoopGetCurrent())
|
179
|
-
@sema.signal
|
180
|
-
end
|
181
|
-
|
182
|
-
def captureOutput captureOutput, didResumeRecordingToOutputFileAtURL:fileURL, fromConnections:connections
|
183
|
-
NSLog('Resuming')
|
184
|
-
CFRunLoopStop(CFRunLoopGetCurrent())
|
185
|
-
@sema.signal
|
186
|
-
end
|
187
|
-
|
188
|
-
def captureOutput captureOutput, didStartRecordingToOutputFileAtURL:fileURL, fromConnections:connections
|
189
|
-
NSLog('Starting')
|
190
|
-
@sema.signal
|
191
|
-
end
|
192
|
-
|
193
|
-
def captureOutput captureOutput, willFinishRecordingToOutputFileAtURL:fileURL, fromConnections:connections, error:error
|
194
|
-
NSLog('Will Finish')
|
195
|
-
@sema.signal
|
196
|
-
end
|
197
|
-
|
198
|
-
|
199
|
-
private
|
200
|
-
|
201
|
-
def default_file_name
|
202
|
-
date = Time.now.strftime '%Y%m%d%H%M%S'
|
203
|
-
File.expand_path("~/Movies/TestRecording-#{date}.mov")
|
204
|
-
end
|
205
|
-
|
206
|
-
def wait_for_callback
|
207
|
-
case CFRunLoopRunInMode(KCFRunLoopDefaultMode, 30, false)
|
208
|
-
when KCFRunLoopRunStopped
|
209
|
-
true
|
210
|
-
when KCFRunLoopRunTimedOut
|
211
|
-
raise 'did not get callback'
|
212
|
-
else
|
213
|
-
raise 'unexpected result from waiting for callback'
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
end
|