ffi-gphoto2 0.5.1 → 0.8.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 +5 -5
- data/.travis.yml +11 -0
- data/CHANGELOG.md +44 -0
- data/README.md +22 -12
- 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 +9 -8
- data/lib/ffi/gphoto2.rb +14 -4
- 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.rb +21 -1
- data/lib/ffi/gphoto2_port/gp_port.rb +17 -0
- data/lib/ffi/gphoto2_port/gp_port_info_list.rb +1 -1
- data/lib/ffi/gphoto2_port/gp_port_serial_parity.rb +6 -0
- data/lib/ffi/gphoto2_port/gp_port_settings.rb +11 -0
- data/lib/ffi/gphoto2_port/gp_port_settings_serial.rb +12 -0
- data/lib/ffi/gphoto2_port/gp_port_settings_usb.rb +15 -0
- data/lib/ffi/gphoto2_port/gp_port_settings_usb_disk_direct.rb +8 -0
- data/lib/ffi/gphoto2_port/gp_port_settings_usb_scsi.rb +8 -0
- data/lib/gphoto2.rb +24 -4
- data/lib/gphoto2/camera.rb +10 -2
- data/lib/gphoto2/camera/capture.rb +22 -0
- data/lib/gphoto2/camera/configuration.rb +2 -1
- data/lib/gphoto2/camera/event.rb +3 -6
- data/lib/gphoto2/camera_abilities.rb +9 -0
- data/lib/gphoto2/camera_abilities_list.rb +1 -1
- data/lib/gphoto2/camera_file.rb +26 -8
- 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_list.rb +1 -1
- data/lib/gphoto2/camera_widgets/camera_widget.rb +3 -7
- 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.rb +61 -0
- data/lib/gphoto2/port_info.rb +0 -4
- data/lib/gphoto2/port_info_list.rb +1 -1
- 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/port_spec.rb +22 -0
- 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 +54 -24
data/lib/ffi/gphoto2_port.rb
CHANGED
@@ -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, [
|
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
|
@@ -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,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
|
data/lib/gphoto2.rb
CHANGED
@@ -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
|
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
|
73
|
+
raise Error.new(PortResult.as_string(rc), rc)
|
54
74
|
end
|
55
75
|
end
|
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
|
@@ -171,7 +179,7 @@ module GPhoto2
|
|
171
179
|
end
|
172
180
|
|
173
181
|
def new
|
174
|
-
ptr = FFI::MemoryPointer.new(
|
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(
|
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)
|
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
|
@@ -32,7 +32,7 @@ module GPhoto2
|
|
32
32
|
private
|
33
33
|
|
34
34
|
def new
|
35
|
-
ptr = FFI::MemoryPointer.new(
|
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)
|
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,30 +62,40 @@ 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
|
62
|
-
ptr = FFI::MemoryPointer.new(
|
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
|
-
|
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
|