ffi-gphoto2 0.5.1 → 0.6.0

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