ffi-gphoto2 0.4.1 → 0.5.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.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/Rakefile +2 -0
- data/examples/intervalometer.rb +9 -7
- data/examples/list_config.rb +3 -5
- data/examples/live_view.rb +6 -12
- data/examples/record_movie.rb +10 -13
- data/ffi-gphoto2.gemspec +2 -1
- data/lib/ffi/gphoto2_port.rb +2 -2
- data/lib/gphoto2/camera/capture.rb +59 -0
- data/lib/gphoto2/camera/configuration.rb +136 -0
- data/lib/gphoto2/camera/event.rb +56 -0
- data/lib/gphoto2/camera/filesystem.rb +44 -0
- data/lib/gphoto2/camera.rb +95 -166
- data/lib/gphoto2/camera_abilities.rb +6 -6
- data/lib/gphoto2/camera_abilities_list.rb +7 -6
- data/lib/gphoto2/camera_event.rb +1 -8
- data/lib/gphoto2/camera_file.rb +15 -5
- data/lib/gphoto2/camera_file_path.rb +3 -5
- data/lib/gphoto2/camera_folder.rb +11 -0
- data/lib/gphoto2/camera_list.rb +3 -10
- data/lib/gphoto2/camera_widgets/camera_widget.rb +27 -14
- data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +1 -0
- data/lib/gphoto2/camera_widgets/range_camera_widget.rb +1 -0
- data/lib/gphoto2/context.rb +2 -6
- data/lib/gphoto2/entry.rb +2 -0
- data/lib/gphoto2/port_info.rb +8 -6
- data/lib/gphoto2/port_info_list.rb +5 -6
- data/lib/gphoto2/port_result.rb +2 -0
- data/lib/gphoto2/struct.rb +11 -0
- data/lib/gphoto2/version.rb +1 -1
- data/lib/gphoto2.rb +13 -0
- data/spec/gphoto2/camera_abilities_list_spec.rb +5 -5
- data/spec/gphoto2/camera_abilities_spec.rb +7 -7
- data/spec/gphoto2/camera_file_path_spec.rb +2 -2
- data/spec/gphoto2/camera_file_spec.rb +7 -7
- data/spec/gphoto2/camera_folder_spec.rb +3 -3
- data/spec/gphoto2/camera_list_spec.rb +3 -3
- data/spec/gphoto2/camera_spec.rb +37 -272
- data/spec/gphoto2/camera_widgets/date_camera_widget_spec.rb +1 -1
- data/spec/gphoto2/camera_widgets/radio_camera_widget_spec.rb +4 -4
- data/spec/gphoto2/camera_widgets/range_camera_widget_spec.rb +2 -2
- data/spec/gphoto2/camera_widgets/text_camera_widget_spec.rb +1 -1
- data/spec/gphoto2/camera_widgets/toggle_camera_widget_spec.rb +2 -2
- data/spec/gphoto2/context_spec.rb +1 -1
- data/spec/gphoto2/entry_spec.rb +2 -2
- data/spec/gphoto2/port_info_list_spec.rb +4 -4
- data/spec/gphoto2/port_info_spec.rb +9 -10
- data/spec/spec_helper.rb +0 -7
- data/spec/support/shared_examples/camera/capture_examples.rb +41 -0
- data/spec/support/shared_examples/camera/configuration_examples.rb +161 -0
- data/spec/support/shared_examples/camera/event_examples.rb +39 -0
- data/spec/support/shared_examples/camera/filesystem_examples.rb +46 -0
- data/spec/support/shared_examples/camera_widget_examples.rb +13 -13
- metadata +32 -4
data/lib/gphoto2/camera.rb
CHANGED
@@ -1,9 +1,24 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class Camera
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
+
include Capture
|
7
|
+
include Configuration
|
8
|
+
include Event
|
9
|
+
include Filesystem
|
6
10
|
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :model
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :port
|
16
|
+
|
17
|
+
# @example
|
18
|
+
# cameras = GPhoto2::Camera.all
|
19
|
+
# # => [#<GPhoto2::Camera>, #<GPhoto2::Camera>, ...]
|
20
|
+
#
|
21
|
+
# @return [Array<GPhoto2::Camera>] a list of all available devices
|
7
22
|
def self.all
|
8
23
|
context = Context.new
|
9
24
|
|
@@ -20,37 +35,74 @@ module GPhoto2
|
|
20
35
|
entries
|
21
36
|
end
|
22
37
|
|
23
|
-
|
38
|
+
# @example
|
39
|
+
# camera = GPhoto2::Camera.first
|
40
|
+
#
|
41
|
+
# begin
|
42
|
+
# # ...
|
43
|
+
# ensure
|
44
|
+
# camera.finalize
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # Alternatively, pass a block, which will automatically close the camera.
|
48
|
+
# GPhoto2::Camera.first do |camera|
|
49
|
+
# # ...
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# @return [GPhoto2::Camera] the first detected camera
|
53
|
+
# @raise [RuntimeError] when no devices are detected
|
54
|
+
def self.first(&blk)
|
24
55
|
entries = all
|
25
56
|
raise RuntimeError, 'no devices detected' if entries.empty?
|
26
|
-
entries.first
|
27
|
-
|
28
|
-
|
29
|
-
|
57
|
+
camera = entries.first
|
58
|
+
autorelease(camera, &blk)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @example
|
62
|
+
# model = 'Nikon DSC D5100 (PTP mode)'
|
63
|
+
# port = 'usb:250,006'
|
64
|
+
#
|
65
|
+
# camera = GPhoto2::Camera.open(model, port)
|
66
|
+
#
|
67
|
+
# begin
|
68
|
+
# # ...
|
69
|
+
# ensure
|
70
|
+
# camera.finalize
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # Alternatively, pass a block, which will automatically close the camera.
|
74
|
+
# GPhoto2::Camera.open(model, port) do |camera|
|
75
|
+
# # ...
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @param [String] model
|
79
|
+
# @param [String] port
|
80
|
+
# @return [GPhoto2::Camera]
|
81
|
+
def self.open(model, port, &blk)
|
30
82
|
camera = new(model, port)
|
31
|
-
|
32
|
-
if block_given?
|
33
|
-
begin
|
34
|
-
yield camera
|
35
|
-
ensure
|
36
|
-
camera.finalize
|
37
|
-
end
|
38
|
-
else
|
39
|
-
camera
|
40
|
-
end
|
83
|
+
autorelease(camera, &blk)
|
41
84
|
end
|
42
85
|
|
86
|
+
# @example
|
87
|
+
# # Find the cameras whose model names contain Nikon.
|
88
|
+
# cameras = GPhoto2::Camera.where(model: /nikon/i)
|
89
|
+
#
|
90
|
+
# @param [Hash] condition
|
91
|
+
# @return [Array<GPhoto2::Camera>]
|
43
92
|
def self.where(condition)
|
44
93
|
name = condition.keys.first
|
45
94
|
pattern = condition.values.first
|
46
95
|
all.select { |c| c.send(name).match(pattern) }
|
47
96
|
end
|
48
97
|
|
98
|
+
# @param [String] model
|
99
|
+
# @param [String] port
|
49
100
|
def initialize(model, port)
|
101
|
+
super
|
50
102
|
@model, @port = model, port
|
51
|
-
@dirty = false
|
52
103
|
end
|
53
104
|
|
105
|
+
# @return [void]
|
54
106
|
def finalize
|
55
107
|
@context.finalize if @context
|
56
108
|
@window.finalize if @window
|
@@ -58,111 +110,59 @@ module GPhoto2
|
|
58
110
|
end
|
59
111
|
alias_method :close, :finalize
|
60
112
|
|
113
|
+
# @return [void]
|
61
114
|
def exit
|
62
115
|
_exit
|
63
116
|
end
|
64
117
|
|
65
|
-
|
66
|
-
save
|
67
|
-
path = _capture(type)
|
68
|
-
CameraFile.new(self, path.folder, path.name)
|
69
|
-
end
|
70
|
-
|
71
|
-
def preview
|
72
|
-
save
|
73
|
-
capture_preview
|
74
|
-
end
|
75
|
-
|
76
|
-
# timeout in milliseconds
|
77
|
-
def wait(timeout = 2000)
|
78
|
-
wait_for_event(timeout)
|
79
|
-
end
|
80
|
-
|
81
|
-
def wait_for(event_type)
|
82
|
-
begin
|
83
|
-
event = wait
|
84
|
-
end until event.type == event_type
|
85
|
-
|
86
|
-
event
|
87
|
-
end
|
88
|
-
|
118
|
+
# @return [FFI::GPhoto::Camera]
|
89
119
|
def ptr
|
90
120
|
@ptr || (init && @ptr)
|
91
121
|
end
|
92
122
|
|
123
|
+
# @return [GPhoto2::CameraAbilities]
|
93
124
|
def abilities
|
94
125
|
@abilities || (init && @abilities)
|
95
126
|
end
|
96
127
|
|
128
|
+
# @return [GPhoto2::PortInfo]
|
97
129
|
def port_info
|
98
130
|
@port_info || (init && @port_info)
|
99
131
|
end
|
100
132
|
|
133
|
+
# @return [GPhoto2::Context]
|
101
134
|
def context
|
102
135
|
@context ||= Context.new
|
103
136
|
end
|
104
137
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
def filesystem(root = '/')
|
114
|
-
root = "/#{root}" if root[0] != '/'
|
115
|
-
CameraFolder.new(self, root)
|
116
|
-
end
|
117
|
-
alias_method :/, :filesystem
|
118
|
-
|
119
|
-
def file(file)
|
120
|
-
file_get(file)
|
121
|
-
end
|
122
|
-
|
123
|
-
def delete(file)
|
124
|
-
file_delete(file)
|
125
|
-
end
|
126
|
-
|
127
|
-
def [](key)
|
128
|
-
config[key.to_s]
|
129
|
-
end
|
130
|
-
|
131
|
-
def []=(key, value)
|
132
|
-
self[key].value = value
|
133
|
-
@dirty = true
|
134
|
-
value
|
135
|
-
end
|
136
|
-
|
137
|
-
def update(attributes = {})
|
138
|
-
attributes.each do |key, value|
|
139
|
-
self[key] = value
|
140
|
-
end
|
141
|
-
|
142
|
-
save
|
143
|
-
end
|
144
|
-
|
145
|
-
def dirty?
|
146
|
-
@dirty
|
147
|
-
end
|
148
|
-
|
138
|
+
# @example
|
139
|
+
# camera.can? :capture_image
|
140
|
+
# # => true
|
141
|
+
#
|
142
|
+
# @param [CameraOperation] operation
|
143
|
+
# @return [Boolean]
|
149
144
|
def can?(operation)
|
150
145
|
(abilities[:operations] & CameraOperation[operation]) != 0
|
151
146
|
end
|
152
147
|
|
153
|
-
|
154
|
-
return false unless dirty?
|
155
|
-
set_config
|
156
|
-
@dirty = false
|
157
|
-
true
|
158
|
-
end
|
148
|
+
private
|
159
149
|
|
160
|
-
|
161
|
-
|
150
|
+
# Ensures the given camera is finalized when passed a block.
|
151
|
+
#
|
152
|
+
# If no block is given, the camera is returned and the caller must must
|
153
|
+
# manually close it.
|
154
|
+
def self.autorelease(camera)
|
155
|
+
if block_given?
|
156
|
+
begin
|
157
|
+
yield camera
|
158
|
+
ensure
|
159
|
+
camera.finalize
|
160
|
+
end
|
161
|
+
else
|
162
|
+
camera
|
163
|
+
end
|
162
164
|
end
|
163
165
|
|
164
|
-
private
|
165
|
-
|
166
166
|
def init
|
167
167
|
new
|
168
168
|
set_abilities(CameraAbilities.find(@model))
|
@@ -193,80 +193,9 @@ module GPhoto2
|
|
193
193
|
@abilities = abilities
|
194
194
|
end
|
195
195
|
|
196
|
-
def _capture(type)
|
197
|
-
path = CameraFilePath.new
|
198
|
-
rc = gp_camera_capture(ptr, type, path.ptr, context.ptr)
|
199
|
-
GPhoto2.check!(rc)
|
200
|
-
path
|
201
|
-
end
|
202
|
-
|
203
|
-
def capture_preview
|
204
|
-
file = CameraFile.new(self)
|
205
|
-
rc = gp_camera_capture_preview(ptr, file.ptr, context.ptr)
|
206
|
-
GPhoto2.check!(rc)
|
207
|
-
file
|
208
|
-
end
|
209
|
-
|
210
|
-
def get_config
|
211
|
-
widget_ptr = FFI::MemoryPointer.new(FFI::GPhoto2::CameraWidget)
|
212
|
-
rc = gp_camera_get_config(ptr, widget_ptr, context.ptr)
|
213
|
-
GPhoto2.check!(rc)
|
214
|
-
widget = FFI::GPhoto2::CameraWidget.new(widget_ptr.read_pointer)
|
215
|
-
CameraWidget.factory(widget)
|
216
|
-
end
|
217
|
-
|
218
|
-
def set_config
|
219
|
-
rc = gp_camera_set_config(ptr, window.ptr, context.ptr)
|
220
|
-
GPhoto2.check!(rc)
|
221
|
-
end
|
222
|
-
|
223
|
-
def file_get(file, type = :normal)
|
224
|
-
rc = gp_camera_file_get(ptr, file.folder, file.name, type, file.ptr, context.ptr)
|
225
|
-
GPhoto2.check!(rc)
|
226
|
-
file
|
227
|
-
end
|
228
|
-
|
229
|
-
def file_delete(file)
|
230
|
-
rc = gp_camera_file_delete(ptr, file.folder, file.name, context.ptr)
|
231
|
-
GPhoto2.check!(rc)
|
232
|
-
end
|
233
|
-
|
234
196
|
def unref
|
235
197
|
rc = gp_camera_unref(ptr)
|
236
198
|
GPhoto2.check!(rc)
|
237
199
|
end
|
238
|
-
|
239
|
-
def wait_for_event(timeout)
|
240
|
-
# assume CameraEventType is an int
|
241
|
-
type = FFI::MemoryPointer.new(:int)
|
242
|
-
|
243
|
-
data = FFI::MemoryPointer.new(:pointer)
|
244
|
-
data_ptr = FFI::MemoryPointer.new(:pointer)
|
245
|
-
data_ptr.write_pointer(data)
|
246
|
-
|
247
|
-
rc = gp_camera_wait_for_event(ptr, timeout, type, data_ptr, context.ptr)
|
248
|
-
GPhoto2.check!(rc)
|
249
|
-
|
250
|
-
type = CameraEventType[type.read_int]
|
251
|
-
data = data_ptr.read_pointer
|
252
|
-
|
253
|
-
data =
|
254
|
-
case type
|
255
|
-
when :unknown
|
256
|
-
data.null? ? nil : data.read_string
|
257
|
-
when :file_added
|
258
|
-
path_ptr = FFI::GPhoto2::CameraFilePath.new(data)
|
259
|
-
path = CameraFilePath.new(path_ptr)
|
260
|
-
CameraFile.new(self, path.folder, path.name)
|
261
|
-
when :folder_added
|
262
|
-
path_ptr = FFI::GPhoto2::CameraFilePath.new(data)
|
263
|
-
path = CameraFilePath.new(path_ptr)
|
264
|
-
CameraFolder.new(self, '%s/%s' % [path.folder, path.name])
|
265
|
-
else
|
266
|
-
nil
|
267
|
-
end
|
268
|
-
|
269
|
-
CameraEvent.new(type, data)
|
270
|
-
end
|
271
200
|
end
|
272
201
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class CameraAbilities
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# @param [String] model the name of the device
|
7
|
+
# @return [GPhoto2::CameraAbilities]
|
7
8
|
def self.find(model)
|
8
9
|
context = Context.new
|
9
10
|
|
@@ -16,20 +17,19 @@ module GPhoto2
|
|
16
17
|
abilities
|
17
18
|
end
|
18
19
|
|
20
|
+
# @param [GPhoto2::CameraAbilitiesList] camera_abilities_list
|
21
|
+
# @param [Integer] index
|
19
22
|
def initialize(camera_abilities_list, index)
|
20
23
|
@camera_abilities_list = camera_abilities_list
|
21
24
|
@index = index
|
22
25
|
get_abilities
|
23
26
|
end
|
24
27
|
|
28
|
+
# @return [Object]
|
25
29
|
def [](field)
|
26
30
|
ptr[field]
|
27
31
|
end
|
28
32
|
|
29
|
-
def to_ptr
|
30
|
-
@ptr
|
31
|
-
end
|
32
|
-
|
33
33
|
private
|
34
34
|
|
35
35
|
def get_abilities
|
@@ -1,33 +1,34 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class CameraAbilitiesList
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# @param [GPhoto2::Context] context
|
7
7
|
def initialize(context)
|
8
8
|
@context = context
|
9
9
|
new
|
10
10
|
load
|
11
11
|
end
|
12
12
|
|
13
|
+
# @return [GPhoto2::CameraList]
|
13
14
|
def detect
|
14
15
|
_detect
|
15
16
|
end
|
16
17
|
|
18
|
+
# @param [String] model
|
19
|
+
# @return [Integer]
|
17
20
|
def lookup_model(model)
|
18
21
|
_lookup_model(model)
|
19
22
|
end
|
20
23
|
alias_method :index, :lookup_model
|
21
24
|
|
25
|
+
# @param [Integer] index
|
26
|
+
# @return [GPhoto2::CameraAbilities]
|
22
27
|
def at(index)
|
23
28
|
CameraAbilities.new(self, index)
|
24
29
|
end
|
25
30
|
alias_method :[], :at
|
26
31
|
|
27
|
-
def to_ptr
|
28
|
-
@ptr
|
29
|
-
end
|
30
|
-
|
31
32
|
private
|
32
33
|
|
33
34
|
def new
|
data/lib/gphoto2/camera_event.rb
CHANGED
data/lib/gphoto2/camera_file.rb
CHANGED
@@ -1,39 +1,49 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class CameraFile
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
+
# @return [String]
|
7
|
+
attr_reader :folder
|
6
8
|
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @param [GPhoto2::Camera] camera
|
13
|
+
# @param [String] folder
|
14
|
+
# @param [String] name
|
7
15
|
def initialize(camera, folder = nil, name = nil)
|
8
16
|
@camera = camera
|
9
17
|
@folder, @name = folder, name
|
10
18
|
new
|
11
19
|
end
|
12
20
|
|
21
|
+
# @return [Boolean]
|
13
22
|
def preview?
|
14
23
|
@folder.nil? && @name.nil?
|
15
24
|
end
|
16
25
|
|
26
|
+
# @param [String] pathname
|
27
|
+
# @return [Integer] the number of bytes written
|
17
28
|
def save(pathname = default_filename)
|
18
29
|
File.binwrite(pathname, data)
|
19
30
|
end
|
20
31
|
|
32
|
+
# @return [void]
|
21
33
|
def delete
|
22
34
|
@camera.delete(self)
|
23
35
|
end
|
24
36
|
|
37
|
+
# @return [String]
|
25
38
|
def data
|
26
39
|
data_and_size.first
|
27
40
|
end
|
28
41
|
|
42
|
+
# @return [Integer]
|
29
43
|
def size
|
30
44
|
data_and_size.last
|
31
45
|
end
|
32
46
|
|
33
|
-
def to_ptr
|
34
|
-
@ptr
|
35
|
-
end
|
36
|
-
|
37
47
|
private
|
38
48
|
|
39
49
|
def data_and_size
|