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.
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