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.
- 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
|