ffi-gphoto2 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/Rakefile +2 -0
  4. data/examples/intervalometer.rb +9 -7
  5. data/examples/list_config.rb +3 -5
  6. data/examples/live_view.rb +6 -12
  7. data/examples/record_movie.rb +10 -13
  8. data/ffi-gphoto2.gemspec +2 -1
  9. data/lib/ffi/gphoto2_port.rb +2 -2
  10. data/lib/gphoto2/camera/capture.rb +59 -0
  11. data/lib/gphoto2/camera/configuration.rb +136 -0
  12. data/lib/gphoto2/camera/event.rb +56 -0
  13. data/lib/gphoto2/camera/filesystem.rb +44 -0
  14. data/lib/gphoto2/camera.rb +95 -166
  15. data/lib/gphoto2/camera_abilities.rb +6 -6
  16. data/lib/gphoto2/camera_abilities_list.rb +7 -6
  17. data/lib/gphoto2/camera_event.rb +1 -8
  18. data/lib/gphoto2/camera_file.rb +15 -5
  19. data/lib/gphoto2/camera_file_path.rb +3 -5
  20. data/lib/gphoto2/camera_folder.rb +11 -0
  21. data/lib/gphoto2/camera_list.rb +3 -10
  22. data/lib/gphoto2/camera_widgets/camera_widget.rb +27 -14
  23. data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +1 -0
  24. data/lib/gphoto2/camera_widgets/range_camera_widget.rb +1 -0
  25. data/lib/gphoto2/context.rb +2 -6
  26. data/lib/gphoto2/entry.rb +2 -0
  27. data/lib/gphoto2/port_info.rb +8 -6
  28. data/lib/gphoto2/port_info_list.rb +5 -6
  29. data/lib/gphoto2/port_result.rb +2 -0
  30. data/lib/gphoto2/struct.rb +11 -0
  31. data/lib/gphoto2/version.rb +1 -1
  32. data/lib/gphoto2.rb +13 -0
  33. data/spec/gphoto2/camera_abilities_list_spec.rb +5 -5
  34. data/spec/gphoto2/camera_abilities_spec.rb +7 -7
  35. data/spec/gphoto2/camera_file_path_spec.rb +2 -2
  36. data/spec/gphoto2/camera_file_spec.rb +7 -7
  37. data/spec/gphoto2/camera_folder_spec.rb +3 -3
  38. data/spec/gphoto2/camera_list_spec.rb +3 -3
  39. data/spec/gphoto2/camera_spec.rb +37 -272
  40. data/spec/gphoto2/camera_widgets/date_camera_widget_spec.rb +1 -1
  41. data/spec/gphoto2/camera_widgets/radio_camera_widget_spec.rb +4 -4
  42. data/spec/gphoto2/camera_widgets/range_camera_widget_spec.rb +2 -2
  43. data/spec/gphoto2/camera_widgets/text_camera_widget_spec.rb +1 -1
  44. data/spec/gphoto2/camera_widgets/toggle_camera_widget_spec.rb +2 -2
  45. data/spec/gphoto2/context_spec.rb +1 -1
  46. data/spec/gphoto2/entry_spec.rb +2 -2
  47. data/spec/gphoto2/port_info_list_spec.rb +4 -4
  48. data/spec/gphoto2/port_info_spec.rb +9 -10
  49. data/spec/spec_helper.rb +0 -7
  50. data/spec/support/shared_examples/camera/capture_examples.rb +41 -0
  51. data/spec/support/shared_examples/camera/configuration_examples.rb +161 -0
  52. data/spec/support/shared_examples/camera/event_examples.rb +39 -0
  53. data/spec/support/shared_examples/camera/filesystem_examples.rb +46 -0
  54. data/spec/support/shared_examples/camera_widget_examples.rb +13 -13
  55. metadata +32 -4
@@ -1,9 +1,24 @@
1
1
  module GPhoto2
2
2
  class Camera
3
3
  include FFI::GPhoto2
4
+ include GPhoto2::Struct
4
5
 
5
- attr_reader :context, :model, :port
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
- def self.first
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
- end
28
-
29
- def self.open(model, port)
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
- def capture(type = :image)
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
- def window
106
- @window ||= get_config
107
- end
108
-
109
- def config
110
- @config ||= window.flatten
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
- def save
154
- return false unless dirty?
155
- set_config
156
- @dirty = false
157
- true
158
- end
148
+ private
159
149
 
160
- def to_ptr
161
- @ptr
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
- attr_reader :ptr
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
- attr_reader :ptr
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
@@ -1,10 +1,3 @@
1
1
  module GPhoto2
2
- class CameraEvent
3
- attr_reader :type, :data
4
-
5
- def initialize(type, data)
6
- @type = type
7
- @data = data
8
- end
9
- end
2
+ CameraEvent = ::Struct.new(:type, :data)
10
3
  end
@@ -1,39 +1,49 @@
1
1
  module GPhoto2
2
2
  class CameraFile
3
3
  include FFI::GPhoto2
4
+ include GPhoto2::Struct
4
5
 
5
- attr_reader :folder, :name, :ptr
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