ffi-gphoto2 0.5.1 → 0.6.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -0
  3. data/CHANGELOG.md +20 -0
  4. data/README.md +19 -10
  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 +3 -3
  11. data/lib/ffi/gphoto2.rb +10 -1
  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/gp_port_info_list.rb +1 -1
  23. data/lib/gphoto2.rb +23 -2
  24. data/lib/gphoto2/camera.rb +9 -1
  25. data/lib/gphoto2/camera/capture.rb +22 -0
  26. data/lib/gphoto2/camera/configuration.rb +1 -0
  27. data/lib/gphoto2/camera/event.rb +3 -6
  28. data/lib/gphoto2/camera_abilities.rb +9 -0
  29. data/lib/gphoto2/camera_file.rb +25 -7
  30. data/lib/gphoto2/camera_file_info/camera_file_info.rb +52 -0
  31. data/lib/gphoto2/camera_file_info/file_camera_file_info.rb +30 -0
  32. data/lib/gphoto2/camera_widgets/camera_widget.rb +2 -6
  33. data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +0 -4
  34. data/lib/gphoto2/camera_widgets/text_camera_widget.rb +1 -3
  35. data/lib/gphoto2/entry.rb +0 -4
  36. data/lib/gphoto2/port_info.rb +0 -4
  37. data/lib/gphoto2/version.rb +1 -1
  38. data/spec/gphoto2/camera_abilities_spec.rb +23 -0
  39. data/spec/gphoto2/camera_file_info/file_camera_file_info_spec.rb +56 -0
  40. data/spec/gphoto2/camera_file_spec.rb +17 -0
  41. data/spec/gphoto2/camera_spec.rb +11 -5
  42. data/spec/gphoto2_spec.rb +18 -0
  43. data/spec/support/shared_examples/camera/capture_examples.rb +13 -0
  44. data/spec/support/shared_examples/camera/configuration_examples.rb +26 -18
  45. data/spec/support/shared_examples/camera_file_info_examples.rb +42 -0
  46. metadata +29 -8
@@ -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
@@ -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
@@ -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
@@ -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,8 +62,7 @@ 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
@@ -66,18 +73,29 @@ module GPhoto2
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
@@ -0,0 +1,30 @@
1
+ module GPhoto2
2
+ class FileCameraFileInfo < CameraFileInfo
3
+ # @return [Integer, nil]
4
+ def width
5
+ fetch(:width)
6
+ end
7
+
8
+ # @return [Integer, nil]
9
+ def height
10
+ fetch(:height)
11
+ end
12
+
13
+ # @return [Boolean, nil]
14
+ def readable?
15
+ permissions = fetch(:permissions)
16
+ (permissions & CameraFilePermissions[:read]) != 0 if permissions
17
+ end
18
+
19
+ # @return [Boolean, nil]
20
+ def deletable?
21
+ permissions = fetch(:permissions)
22
+ (permissions & CameraFilePermissions[:delete]) != 0 if permissions
23
+ end
24
+
25
+ # @return [Time, nil] the last modification time
26
+ def mtime
27
+ Time.at(ptr[:mtime]) if has_field?(:mtime)
28
+ end
29
+ end
30
+ end
@@ -70,7 +70,7 @@ module GPhoto2
70
70
  map
71
71
  end
72
72
 
73
- # @param [String]
73
+ # @return [String]
74
74
  def to_s
75
75
  value.to_s
76
76
  end
@@ -93,9 +93,7 @@ module GPhoto2
93
93
  end
94
94
 
95
95
  def get_name
96
- str = FFI::MemoryPointer.new(:string)
97
96
  str_ptr = FFI::MemoryPointer.new(:pointer)
98
- str_ptr.write_pointer(str)
99
97
 
100
98
  rc = gp_widget_get_name(ptr, str_ptr)
101
99
  GPhoto2.check!(rc)
@@ -113,11 +111,9 @@ module GPhoto2
113
111
  end
114
112
 
115
113
  def get_label
116
- str = FFI::MemoryPointer.new(:string)
117
114
  str_ptr = FFI::MemoryPointer.new(:pointer)
118
- str_ptr.write_pointer(str)
119
115
 
120
- rc = gp_widget_get_type(ptr, str_ptr)
116
+ rc = gp_widget_get_label(ptr, str_ptr)
121
117
  GPhoto2.check!(rc)
122
118
 
123
119
  str_ptr = str_ptr.read_pointer
@@ -8,9 +8,7 @@ module GPhoto2
8
8
  protected
9
9
 
10
10
  def get_value
11
- val = FFI::MemoryPointer.new(:string)
12
11
  val_ptr = FFI::MemoryPointer.new(:pointer)
13
- val_ptr.write_pointer(val)
14
12
 
15
13
  rc = gp_widget_get_value(ptr, val_ptr)
16
14
  GPhoto2.check!(rc)
@@ -34,9 +32,7 @@ module GPhoto2
34
32
  end
35
33
 
36
34
  def get_choice(i)
37
- val = FFI::MemoryPointer.new(:string)
38
35
  val_ptr = FFI::MemoryPointer.new(:pointer)
39
- val_ptr.write_pointer(val)
40
36
 
41
37
  rc = gp_widget_get_choice(ptr, i, val_ptr)
42
38
  GPhoto2.check!(rc)
@@ -3,9 +3,7 @@ module GPhoto2
3
3
  protected
4
4
 
5
5
  def get_value
6
- val = FFI::MemoryPointer.new(:string)
7
6
  val_ptr = FFI::MemoryPointer.new(:pointer)
8
- val_ptr.write_pointer(val)
9
7
 
10
8
  rc = gp_widget_get_value(ptr, val_ptr)
11
9
  GPhoto2.check!(rc)
@@ -15,7 +13,7 @@ module GPhoto2
15
13
  end
16
14
 
17
15
  def set_value(text)
18
- val = FFI::MemoryPointer.from_string(text)
16
+ val = FFI::MemoryPointer.from_string(text.to_s)
19
17
  rc = gp_widget_set_value(ptr, val)
20
18
  GPhoto2.check!(rc)
21
19
  end
data/lib/gphoto2/entry.rb CHANGED
@@ -20,9 +20,7 @@ module GPhoto2
20
20
  private
21
21
 
22
22
  def get_name
23
- name = FFI::MemoryPointer.new(:string)
24
23
  ptr = FFI::MemoryPointer.new(:pointer)
25
- ptr.write_pointer(name)
26
24
 
27
25
  rc = gp_list_get_name(@camera_list.ptr, @index, ptr)
28
26
  GPhoto2.check!(rc)
@@ -32,9 +30,7 @@ module GPhoto2
32
30
  end
33
31
 
34
32
  def get_value
35
- value = FFI::MemoryPointer.new(:string)
36
33
  ptr = FFI::MemoryPointer.new(:pointer)
37
- ptr.write_pointer(value)
38
34
 
39
35
  rc = gp_list_get_value(@camera_list.ptr, @index, ptr)
40
36
  GPhoto2.check!(rc)
@@ -44,9 +44,7 @@ module GPhoto2
44
44
  end
45
45
 
46
46
  def get_name
47
- name = FFI::MemoryPointer.new(:string)
48
47
  name_ptr = FFI::MemoryPointer.new(:pointer)
49
- name_ptr.write_pointer(name)
50
48
 
51
49
  rc = gp_port_info_get_name(ptr, name_ptr)
52
50
  GPhoto2.check!(rc)
@@ -56,9 +54,7 @@ module GPhoto2
56
54
  end
57
55
 
58
56
  def get_path
59
- path = FFI::MemoryPointer.new(:string)
60
57
  path_ptr = FFI::MemoryPointer.new(:pointer)
61
- path_ptr.write_pointer(path)
62
58
 
63
59
  rc = gp_port_info_get_path(ptr, path_ptr)
64
60
  GPhoto2.check!(rc)
@@ -1,3 +1,3 @@
1
1
  module GPhoto2
2
- VERSION = '0.5.1'
2
+ VERSION = '0.6.0'
3
3
  end
@@ -34,5 +34,28 @@ module GPhoto2
34
34
  expect(abilities[key]).to eq(value)
35
35
  end
36
36
  end
37
+
38
+ describe '#operations' do
39
+ context 'when no or one operation is supported' do
40
+ it 'returns a bit field of supported operations' do
41
+ abilities = CameraAbilities.new(camera_abilities_list, index)
42
+ allow(abilities).to receive(:[]).and_return(:none)
43
+ expect(abilities.operations).to eq(0)
44
+ end
45
+ end
46
+
47
+ context 'when multiple operations are supported' do
48
+ it 'returns a bit field of supported operations' do
49
+ abilities = CameraAbilities.new(camera_abilities_list, index)
50
+
51
+ capture_image = FFI::GPhoto2::CameraOperation[:capture_image]
52
+ config = FFI::GPhoto2::CameraOperation[:config]
53
+ operations = capture_image | config
54
+
55
+ allow(abilities).to receive(:[]).and_return(operations)
56
+ expect(abilities.operations).to eq(0x11)
57
+ end
58
+ end
59
+ end
37
60
  end
38
61
  end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ module GPhoto2
5
+ describe FileCameraFileInfo do
6
+ it_behaves_like CameraFileInfo
7
+
8
+ let(:camera_file_info_file) do
9
+ OpenStruct.new(
10
+ fields: 0xff,
11
+ status: :not_downloaded,
12
+ size: 7355608,
13
+ type: 'image/jpeg',
14
+ width: 4928,
15
+ height: 3264,
16
+ permissions: 0xff,
17
+ mtime: Time.at(1467863342)
18
+ )
19
+ end
20
+
21
+ describe '#width' do
22
+ it 'returns the width of the file' do
23
+ info = FileCameraFileInfo.new(camera_file_info_file)
24
+ expect(info.width).to eq(4928)
25
+ end
26
+ end
27
+
28
+ describe '#height' do
29
+ it 'returns the height of the file' do
30
+ info = FileCameraFileInfo.new(camera_file_info_file)
31
+ expect(info.height).to eq(3264)
32
+ end
33
+ end
34
+
35
+ describe '#readable?' do
36
+ it 'returns whether the file is readable' do
37
+ info = FileCameraFileInfo.new(camera_file_info_file)
38
+ expect(info.readable?).to be(true)
39
+ end
40
+ end
41
+
42
+ describe '#deletable?' do
43
+ it 'returns whether the file is deletable' do
44
+ info = FileCameraFileInfo.new(camera_file_info_file)
45
+ expect(info.deletable?).to be(true)
46
+ end
47
+ end
48
+
49
+ describe '#mtime' do
50
+ it 'returns the last modification time of the file' do
51
+ info = FileCameraFileInfo.new(camera_file_info_file)
52
+ expect(info.mtime).to eq(Time.at(1467863342))
53
+ end
54
+ end
55
+ end
56
+ end