ffi-gphoto2 0.5.1 → 0.8.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 (59) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +11 -0
  3. data/CHANGELOG.md +44 -0
  4. data/README.md +22 -12
  5. data/examples/autofocus.rb +21 -0
  6. data/examples/capture.rb +8 -0
  7. data/examples/continuous_burst.rb +27 -0
  8. data/examples/list_config.rb +1 -0
  9. data/examples/list_files.rb +52 -0
  10. data/ffi-gphoto2.gemspec +9 -8
  11. data/lib/ffi/gphoto2.rb +14 -4
  12. data/lib/ffi/gphoto2/camera_abilities_list.rb +1 -1
  13. data/lib/ffi/gphoto2/camera_file_info.rb +10 -0
  14. data/lib/ffi/gphoto2/camera_file_info_audio.rb +11 -0
  15. data/lib/ffi/gphoto2/camera_file_info_fields.rb +14 -0
  16. data/lib/ffi/gphoto2/camera_file_info_file.rb +15 -0
  17. data/lib/ffi/gphoto2/camera_file_info_preview.rb +13 -0
  18. data/lib/ffi/gphoto2/camera_file_permissions.rb +9 -0
  19. data/lib/ffi/gphoto2/camera_file_status.rb +7 -0
  20. data/lib/ffi/gphoto2/camera_list.rb +1 -1
  21. data/lib/ffi/gphoto2/camera_widget.rb +1 -1
  22. data/lib/ffi/gphoto2_port.rb +21 -1
  23. data/lib/ffi/gphoto2_port/gp_port.rb +17 -0
  24. data/lib/ffi/gphoto2_port/gp_port_info_list.rb +1 -1
  25. data/lib/ffi/gphoto2_port/gp_port_serial_parity.rb +6 -0
  26. data/lib/ffi/gphoto2_port/gp_port_settings.rb +11 -0
  27. data/lib/ffi/gphoto2_port/gp_port_settings_serial.rb +12 -0
  28. data/lib/ffi/gphoto2_port/gp_port_settings_usb.rb +15 -0
  29. data/lib/ffi/gphoto2_port/gp_port_settings_usb_disk_direct.rb +8 -0
  30. data/lib/ffi/gphoto2_port/gp_port_settings_usb_scsi.rb +8 -0
  31. data/lib/gphoto2.rb +24 -4
  32. data/lib/gphoto2/camera.rb +10 -2
  33. data/lib/gphoto2/camera/capture.rb +22 -0
  34. data/lib/gphoto2/camera/configuration.rb +2 -1
  35. data/lib/gphoto2/camera/event.rb +3 -6
  36. data/lib/gphoto2/camera_abilities.rb +9 -0
  37. data/lib/gphoto2/camera_abilities_list.rb +1 -1
  38. data/lib/gphoto2/camera_file.rb +26 -8
  39. data/lib/gphoto2/camera_file_info/camera_file_info.rb +52 -0
  40. data/lib/gphoto2/camera_file_info/file_camera_file_info.rb +30 -0
  41. data/lib/gphoto2/camera_list.rb +1 -1
  42. data/lib/gphoto2/camera_widgets/camera_widget.rb +3 -7
  43. data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +0 -4
  44. data/lib/gphoto2/camera_widgets/text_camera_widget.rb +1 -3
  45. data/lib/gphoto2/entry.rb +0 -4
  46. data/lib/gphoto2/port.rb +61 -0
  47. data/lib/gphoto2/port_info.rb +0 -4
  48. data/lib/gphoto2/port_info_list.rb +1 -1
  49. data/lib/gphoto2/version.rb +1 -1
  50. data/spec/gphoto2/camera_abilities_spec.rb +23 -0
  51. data/spec/gphoto2/camera_file_info/file_camera_file_info_spec.rb +56 -0
  52. data/spec/gphoto2/camera_file_spec.rb +17 -0
  53. data/spec/gphoto2/camera_spec.rb +11 -5
  54. data/spec/gphoto2/port_spec.rb +22 -0
  55. data/spec/gphoto2_spec.rb +18 -0
  56. data/spec/support/shared_examples/camera/capture_examples.rb +13 -0
  57. data/spec/support/shared_examples/camera/configuration_examples.rb +26 -18
  58. data/spec/support/shared_examples/camera_file_info_examples.rb +42 -0
  59. metadata +54 -24
@@ -4,7 +4,7 @@ module FFI
4
4
  # libgphoto2/gphoto2-list.c
5
5
  layout :used, :int,
6
6
  :max, :int,
7
- :_entry, :pointer, # Entry*
7
+ :_entry, Entry.by_ref,
8
8
  :ref_count, :int
9
9
 
10
10
  def self.release(ptr)
@@ -7,7 +7,7 @@ module FFI
7
7
  :info, [:char, 1024],
8
8
  :name, [:char, 256],
9
9
 
10
- :parent, :pointer, # CameraWidget
10
+ :parent, CameraWidget.by_ref,
11
11
 
12
12
  :value_string, :string,
13
13
  :value_int, :int,
@@ -1,3 +1,5 @@
1
+ require 'ffi'
2
+
1
3
  module FFI
2
4
  module GPhoto2Port
3
5
  extend FFI::Library
@@ -8,9 +10,16 @@ module FFI
8
10
  require 'ffi/gphoto2_port/gp_port_result'
9
11
 
10
12
  # enums
13
+ require 'ffi/gphoto2_port/gp_port_serial_parity'
11
14
  require 'ffi/gphoto2_port/gp_port_type'
12
15
 
13
16
  # structs
17
+ require 'ffi/gphoto2_port/gp_port_settings_serial'
18
+ require 'ffi/gphoto2_port/gp_port_settings_usb'
19
+ require 'ffi/gphoto2_port/gp_port_settings_usb_disk_direct'
20
+ require 'ffi/gphoto2_port/gp_port_settings_usb_scsi'
21
+ require 'ffi/gphoto2_port/gp_port_settings'
22
+ require 'ffi/gphoto2_port/gp_port'
14
23
  require 'ffi/gphoto2_port/gp_port_info'
15
24
  require 'ffi/gphoto2_port/gp_port_info_list'
16
25
 
@@ -20,12 +29,23 @@ module FFI
20
29
  attach_function :gp_port_info_get_type, [GPPortInfo, :pointer], :int
21
30
 
22
31
  attach_function :gp_port_info_list_new, [:pointer], :int
23
- attach_function :gp_port_info_list_free, [GPPortInfoList.by_ref], :int
32
+ attach_function :gp_port_info_list_free, [:pointer], :int
24
33
  attach_function :gp_port_info_list_load, [GPPortInfoList.by_ref], :int
25
34
  attach_function :gp_port_info_list_lookup_path, [GPPortInfoList.by_ref, :string], :int
26
35
  attach_function :gp_port_info_list_get_info, [GPPortInfoList.by_ref, :int, :pointer], :int
27
36
 
28
37
  # libgphoto2_port/gphoto2/gphoto2-port-result.h
29
38
  attach_function :gp_port_result_as_string, [:int], :string
39
+
40
+ # libgphoto2_port/gphoto2/gphoto2-port.h
41
+ attach_function :gp_port_new, [:pointer], :int
42
+ attach_function :gp_port_free, [:pointer], :int
43
+
44
+ attach_function :gp_port_set_info, [GPPort.by_ref, GPPortInfo], :int
45
+
46
+ attach_function :gp_port_open, [GPPort.by_ref], :int
47
+ attach_function :gp_port_close, [GPPort.by_ref], :int
48
+
49
+ attach_function :gp_port_reset, [GPPort.by_ref], :int
30
50
  end
31
51
  end
@@ -0,0 +1,17 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ class GPPort < FFI::ManagedStruct
4
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
5
+ layout :type, GPPortType,
6
+ :settings, GPPortSettings,
7
+ :settings_pending, GPPortSettings,
8
+ :timeout, :int,
9
+ :pl, :pointer, # GPPortPrivateLibrary *
10
+ :pc, :pointer # GPPortPrivateCore *
11
+
12
+ def self.release(ptr)
13
+ FFI::GPhoto2.gp_port_free(ptr)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,7 +2,7 @@ module FFI
2
2
  module GPhoto2Port
3
3
  class GPPortInfoList < FFI::ManagedStruct
4
4
  # libgphoto2_port/libgphoto2_port/gphoto2-port-info-list.c
5
- layout :info, :pointer, # GPPortInfo*
5
+ layout :info, GPPortInfo.by_ref,
6
6
  :count, :uint,
7
7
  :iolib_count, :uint
8
8
 
@@ -0,0 +1,6 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
4
+ GPPortSerialParity = enum :off, :even, :odd
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ class GPPortSettings < FFI::Struct
4
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
5
+ layout :serial, GPPortSettingsSerial,
6
+ :usb, GPPortSettingsUSB,
7
+ :usbdiskdirect, GPPortSettingsUsbDiskDirect,
8
+ :usbscsi, GPPortSettingsUsbScsi
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ class GPPortSettingsSerial < FFI::Struct
4
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
5
+ layout :port, [:char, 128],
6
+ :speed, :int,
7
+ :bits, :int,
8
+ :parity, GPPortSerialParity,
9
+ :stopbits, :int
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ class GPPortSettingsUSB < FFI::Struct
4
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
5
+ layout :inep, :int,
6
+ :outep, :int,
7
+ :intep, :int,
8
+ :config, :int,
9
+ :interface, :int,
10
+ :altsetting, :int,
11
+ :maxpacketsize, :int,
12
+ :port, [:char, 64]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ class GPPortSettingsUsbDiskDirect < FFI::Struct
4
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
5
+ layout :path, [:char, 128]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module FFI
2
+ module GPhoto2Port
3
+ class GPPortSettingsUsbScsi < FFI::Struct
4
+ # libgphoto2_port/libgphoto2_port/gphoto2-port.h
5
+ layout :path, [:char, 128]
6
+ end
7
+ end
8
+ end
@@ -1,7 +1,5 @@
1
1
  require 'logger'
2
2
 
3
- require 'ffi'
4
-
5
3
  require 'ffi/gphoto2'
6
4
  require 'ffi/gphoto2_port'
7
5
 
@@ -17,6 +15,9 @@ require 'gphoto2/camera_widgets/text_camera_widget'
17
15
  require 'gphoto2/camera_widgets/toggle_camera_widget'
18
16
  require 'gphoto2/camera_widgets/window_camera_widget'
19
17
 
18
+ require 'gphoto2/camera_file_info/camera_file_info'
19
+ require 'gphoto2/camera_file_info/file_camera_file_info'
20
+
20
21
  require 'gphoto2/camera/capture'
21
22
  require 'gphoto2/camera/configuration'
22
23
  require 'gphoto2/camera/event'
@@ -32,6 +33,7 @@ require 'gphoto2/camera_folder'
32
33
  require 'gphoto2/camera_list'
33
34
  require 'gphoto2/context'
34
35
  require 'gphoto2/entry'
36
+ require 'gphoto2/port'
35
37
  require 'gphoto2/port_info'
36
38
  require 'gphoto2/port_info_list'
37
39
  require 'gphoto2/port_result'
@@ -39,6 +41,24 @@ require 'gphoto2/port_result'
39
41
  require 'gphoto2/version'
40
42
 
41
43
  module GPhoto2
44
+ # A runtime error for unsuccessful return codes.
45
+ class Error < RuntimeError
46
+ # @return [Integer]
47
+ attr_reader :code
48
+
49
+ # @param [String] message
50
+ # @param [Integer] code
51
+ def initialize(message, code)
52
+ super(message)
53
+ @code = code
54
+ end
55
+
56
+ # @return [String]
57
+ def to_s
58
+ "#{super} (#{code})"
59
+ end
60
+ end
61
+
42
62
  # @return [Logger]
43
63
  def self.logger
44
64
  @logger ||= Logger.new(STDERR)
@@ -46,10 +66,10 @@ module GPhoto2
46
66
 
47
67
  # @param [Integer] rc
48
68
  # @return [void]
49
- # @raise RuntimeError when the return code is not {FFI::GPhoto2Port::GP_OK}
69
+ # @raise [GPhoto2::Error] when the return code is not {FFI::GPhoto2Port::GP_OK}
50
70
  def self.check!(rc)
51
71
  logger.debug "#{caller.first} => #{rc}" if ENV['DEBUG']
52
72
  return if rc >= FFI::GPhoto2Port::GP_OK
53
- raise "#{PortResult.as_string(rc)} (#{rc})"
73
+ raise Error.new(PortResult.as_string(rc), rc)
54
74
  end
55
75
  end
@@ -83,10 +83,18 @@ module GPhoto2
83
83
  autorelease(camera, &blk)
84
84
  end
85
85
 
86
+ # Filters devices by a given condition.
87
+ #
88
+ # Filter keys can be either `model` or `port`. Only the first filter is
89
+ # used.
90
+ #
86
91
  # @example
87
92
  # # Find the cameras whose model names contain Nikon.
88
93
  # cameras = GPhoto2::Camera.where(model: /nikon/i)
89
94
  #
95
+ # # Select a camera by its port.
96
+ # camera = GPhoto2::Camera.where(port: 'usb:250,004').first
97
+ #
90
98
  # @param [Hash] condition
91
99
  # @return [Array<GPhoto2::Camera>]
92
100
  def self.where(condition)
@@ -143,7 +151,7 @@ module GPhoto2
143
151
  # @param [CameraOperation] operation
144
152
  # @return [Boolean]
145
153
  def can?(operation)
146
- (abilities[:operations] & CameraOperation[operation]) != 0
154
+ (abilities.operations & (CameraOperation[operation] || 0)) != 0
147
155
  end
148
156
 
149
157
  private
@@ -171,7 +179,7 @@ module GPhoto2
171
179
  end
172
180
 
173
181
  def new
174
- ptr = FFI::MemoryPointer.new(FFI::GPhoto2::Camera)
182
+ ptr = FFI::MemoryPointer.new(:pointer)
175
183
  rc = gp_camera_new(ptr)
176
184
  GPhoto2.check!(rc)
177
185
  @ptr = FFI::GPhoto2::Camera.new(ptr.read_pointer)
@@ -16,6 +16,23 @@ module GPhoto2
16
16
  CameraFile.new(self, path.folder, path.name)
17
17
  end
18
18
 
19
+ # Triggers a capture and immedately returns.
20
+ #
21
+ # A camera trigger is the first half of a {#capture}. Instead of
22
+ # returning a {GPhoto2::CameraFile}, a trigger immediately returns and
23
+ # the caller has to poll for events.
24
+ #
25
+ # @example
26
+ # camera.trigger
27
+ # event = camera.wait_for(:file_added)
28
+ # event.data # => CameraFile
29
+ #
30
+ # @return [void]
31
+ def trigger
32
+ save
33
+ trigger_capture
34
+ end
35
+
19
36
  # Captures a preview from the camera.
20
37
  #
21
38
  # Previews are not stored on the camera but are returned as data in a
@@ -54,6 +71,11 @@ module GPhoto2
54
71
  GPhoto2.check!(rc)
55
72
  file
56
73
  end
74
+
75
+ def trigger_capture
76
+ rc = gp_camera_trigger_capture(ptr, context.ptr)
77
+ GPhoto2.check!(rc)
78
+ end
57
79
  end
58
80
  end
59
81
  end
@@ -70,6 +70,7 @@ module GPhoto2
70
70
  # @param [Object] value
71
71
  # @return [Object]
72
72
  def []=(key, value)
73
+ raise ArgumentError, "invalid key: #{key}" unless self[key]
73
74
  self[key].value = value
74
75
  @dirty = true
75
76
  value
@@ -137,7 +138,7 @@ module GPhoto2
137
138
  end
138
139
 
139
140
  def get_config
140
- widget_ptr = FFI::MemoryPointer.new(FFI::GPhoto2::CameraWidget)
141
+ widget_ptr = FFI::MemoryPointer.new(:pointer)
141
142
  rc = gp_camera_get_config(ptr, widget_ptr, context.ptr)
142
143
  GPhoto2.check!(rc)
143
144
  widget = FFI::GPhoto2::CameraWidget.new(widget_ptr.read_pointer)
@@ -21,16 +21,13 @@ module GPhoto2
21
21
 
22
22
  def wait_for_event(timeout)
23
23
  # assume CameraEventType is an int
24
- type = FFI::MemoryPointer.new(:int)
25
-
26
- data = FFI::MemoryPointer.new(:pointer)
24
+ type_ptr = FFI::MemoryPointer.new(:int)
27
25
  data_ptr = FFI::MemoryPointer.new(:pointer)
28
- data_ptr.write_pointer(data)
29
26
 
30
- rc = gp_camera_wait_for_event(ptr, timeout, type, data_ptr, context.ptr)
27
+ rc = gp_camera_wait_for_event(ptr, timeout, type_ptr, data_ptr, context.ptr)
31
28
  GPhoto2.check!(rc)
32
29
 
33
- type = FFI::GPhoto2::CameraEventType[type.read_int]
30
+ type = FFI::GPhoto2::CameraEventType[type_ptr.read_int]
34
31
  data = data_ptr.read_pointer
35
32
 
36
33
  data =
@@ -30,6 +30,15 @@ module GPhoto2
30
30
  ptr[field]
31
31
  end
32
32
 
33
+ # @return [Integer] a bit field of supported operations
34
+ def operations
35
+ if self[:operations] == :none
36
+ CameraOperation[:none]
37
+ else
38
+ self[:operations]
39
+ end
40
+ end
41
+
33
42
  private
34
43
 
35
44
  def get_abilities
@@ -32,7 +32,7 @@ module GPhoto2
32
32
  private
33
33
 
34
34
  def new
35
- ptr = FFI::MemoryPointer.new(FFI::GPhoto2::CameraAbilitiesList)
35
+ ptr = FFI::MemoryPointer.new(:pointer)
36
36
  rc = gp_abilities_list_new(ptr)
37
37
  GPhoto2.check!(rc)
38
38
  @ptr = FFI::GPhoto2::CameraAbilitiesList.new(ptr.read_pointer)
@@ -3,6 +3,9 @@ module GPhoto2
3
3
  include FFI::GPhoto2
4
4
  include GPhoto2::Struct
5
5
 
6
+ # The preview data is assumed to be a jpg.
7
+ PREVIEW_FILENAME = 'capture_preview.jpg'.freeze
8
+
6
9
  # @return [String]
7
10
  attr_reader :folder
8
11
 
@@ -44,6 +47,11 @@ module GPhoto2
44
47
  data_and_size.last
45
48
  end
46
49
 
50
+ # @return [GPhoto2::CameraFileInfo, nil]
51
+ def info
52
+ preview? ? nil : get_info
53
+ end
54
+
47
55
  private
48
56
 
49
57
  def data_and_size
@@ -54,30 +62,40 @@ module GPhoto2
54
62
  end
55
63
 
56
64
  def default_filename
57
- # previews are always jpg
58
- preview? ? 'capture_preview.jpg' : @name
65
+ preview? ? PREVIEW_FILENAME : @name
59
66
  end
60
67
 
61
68
  def new
62
- ptr = FFI::MemoryPointer.new(FFI::GPhoto2::CameraFile)
69
+ ptr = FFI::MemoryPointer.new(:pointer)
63
70
  rc = gp_file_new(ptr)
64
71
  GPhoto2.check!(rc)
65
72
  @ptr = FFI::GPhoto2::CameraFile.new(ptr.read_pointer)
66
73
  end
67
74
 
68
75
  def get_data_and_size
69
- data = FFI::MemoryPointer.new(:uchar)
70
76
  data_ptr = FFI::MemoryPointer.new(:pointer)
71
- data_ptr.write_pointer(data)
72
- size = FFI::MemoryPointer.new(:ulong)
77
+ size_ptr = FFI::MemoryPointer.new(:ulong)
73
78
 
74
- rc = gp_file_get_data_and_size(ptr, data_ptr, size)
79
+ rc = gp_file_get_data_and_size(ptr, data_ptr, size_ptr)
75
80
  GPhoto2.check!(rc)
76
81
 
77
- size = size.read_ulong
82
+ size = size_ptr.read_ulong
78
83
  data = data_ptr.read_pointer.read_bytes(size)
79
84
 
80
85
  [data, size]
81
86
  end
87
+
88
+ def get_info
89
+ info = FFI::GPhoto2::CameraFileInfo.new
90
+
91
+ rc = gp_camera_file_get_info(@camera.ptr,
92
+ @folder,
93
+ @name,
94
+ info,
95
+ @camera.context.ptr)
96
+ GPhoto2.check!(rc)
97
+
98
+ FileCameraFileInfo.new(info[:file])
99
+ end
82
100
  end
83
101
  end
@@ -0,0 +1,52 @@
1
+ module GPhoto2
2
+ # @abstract
3
+ class CameraFileInfo
4
+ include FFI::GPhoto2
5
+ include GPhoto2::Struct
6
+
7
+ # @param [FFI::GPhoto2::CameraFileInfo] ptr
8
+ def initialize(ptr)
9
+ @ptr = ptr
10
+ end
11
+
12
+ # @return [Integer] a bit field of set info fields
13
+ def fields
14
+ fields = ptr[:fields]
15
+
16
+ if fields.is_a?(Symbol)
17
+ CameraFileInfoFields[fields]
18
+ else
19
+ fields
20
+ end
21
+ end
22
+
23
+ # return [Boolean] whether the given field is set
24
+ def has_field?(field)
25
+ (fields & CameraFileInfoFields[field]) != 0
26
+ end
27
+
28
+ # @return [CameraFileStatus, nil]
29
+ def status
30
+ fetch(:status)
31
+ end
32
+
33
+ # @return [Integer, nil] the size of the file in bytes
34
+ def size
35
+ fetch(:size)
36
+ end
37
+
38
+ # @return [String, nil] the media type of the file
39
+ def type
40
+ type = fetch(:type)
41
+ type ? type.to_s : nil
42
+ end
43
+
44
+ protected
45
+
46
+ # param [Symbol] key
47
+ # @return [Object, nil]
48
+ def fetch(key)
49
+ ptr[key] if has_field?(key)
50
+ end
51
+ end
52
+ end