ffi-gphoto2 0.5.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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