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.
- checksums.yaml +4 -4
- data/.travis.yml +11 -0
- data/CHANGELOG.md +20 -0
- data/README.md +19 -10
- data/examples/autofocus.rb +21 -0
- data/examples/capture.rb +8 -0
- data/examples/continuous_burst.rb +27 -0
- data/examples/list_config.rb +1 -0
- data/examples/list_files.rb +52 -0
- data/ffi-gphoto2.gemspec +3 -3
- data/lib/ffi/gphoto2.rb +10 -1
- data/lib/ffi/gphoto2/camera_abilities_list.rb +1 -1
- data/lib/ffi/gphoto2/camera_file_info.rb +10 -0
- data/lib/ffi/gphoto2/camera_file_info_audio.rb +11 -0
- data/lib/ffi/gphoto2/camera_file_info_fields.rb +14 -0
- data/lib/ffi/gphoto2/camera_file_info_file.rb +15 -0
- data/lib/ffi/gphoto2/camera_file_info_preview.rb +13 -0
- data/lib/ffi/gphoto2/camera_file_permissions.rb +9 -0
- data/lib/ffi/gphoto2/camera_file_status.rb +7 -0
- data/lib/ffi/gphoto2/camera_list.rb +1 -1
- data/lib/ffi/gphoto2/camera_widget.rb +1 -1
- data/lib/ffi/gphoto2_port/gp_port_info_list.rb +1 -1
- data/lib/gphoto2.rb +23 -2
- data/lib/gphoto2/camera.rb +9 -1
- data/lib/gphoto2/camera/capture.rb +22 -0
- data/lib/gphoto2/camera/configuration.rb +1 -0
- data/lib/gphoto2/camera/event.rb +3 -6
- data/lib/gphoto2/camera_abilities.rb +9 -0
- data/lib/gphoto2/camera_file.rb +25 -7
- data/lib/gphoto2/camera_file_info/camera_file_info.rb +52 -0
- data/lib/gphoto2/camera_file_info/file_camera_file_info.rb +30 -0
- data/lib/gphoto2/camera_widgets/camera_widget.rb +2 -6
- data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +0 -4
- data/lib/gphoto2/camera_widgets/text_camera_widget.rb +1 -3
- data/lib/gphoto2/entry.rb +0 -4
- data/lib/gphoto2/port_info.rb +0 -4
- data/lib/gphoto2/version.rb +1 -1
- data/spec/gphoto2/camera_abilities_spec.rb +23 -0
- data/spec/gphoto2/camera_file_info/file_camera_file_info_spec.rb +56 -0
- data/spec/gphoto2/camera_file_spec.rb +17 -0
- data/spec/gphoto2/camera_spec.rb +11 -5
- data/spec/gphoto2_spec.rb +18 -0
- data/spec/support/shared_examples/camera/capture_examples.rb +13 -0
- data/spec/support/shared_examples/camera/configuration_examples.rb +26 -18
- data/spec/support/shared_examples/camera_file_info_examples.rb +42 -0
- metadata +29 -8
data/lib/gphoto2/camera.rb
CHANGED
@@ -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
|
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
|
data/lib/gphoto2/camera/event.rb
CHANGED
@@ -21,16 +21,13 @@ module GPhoto2
|
|
21
21
|
|
22
22
|
def wait_for_event(timeout)
|
23
23
|
# assume CameraEventType is an int
|
24
|
-
|
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,
|
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[
|
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
|
data/lib/gphoto2/camera_file.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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,
|
79
|
+
rc = gp_file_get_data_and_size(ptr, data_ptr, size_ptr)
|
75
80
|
GPhoto2.check!(rc)
|
76
81
|
|
77
|
-
size =
|
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
|
-
# @
|
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 =
|
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)
|
data/lib/gphoto2/port_info.rb
CHANGED
@@ -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)
|
data/lib/gphoto2/version.rb
CHANGED
@@ -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
|