working draft
This commit is contained in:
parent
4a6b2d5739
commit
cf3c2b8c6e
3 changed files with 162 additions and 27 deletions
80
lib/sane.rb
80
lib/sane.rb
|
@ -1,53 +1,75 @@
|
||||||
class Sane
|
class Sane
|
||||||
include Singleton
|
include Singleton
|
||||||
|
|
||||||
class << self
|
attr_reader :version
|
||||||
extend Forwardable
|
|
||||||
delegate [:init, :exit, :get_devices, :open, :close, :get_option_descriptor, :get_option, :set_option, :strstatus] => :instance
|
def self.open
|
||||||
|
instance.send(:init)
|
||||||
|
yield instance
|
||||||
|
ensure
|
||||||
|
instance.send(:exit)
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def devices
|
||||||
init
|
get_devices.map { |device| Device.new(device) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def not_initialized?
|
||||||
|
version.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialized?
|
||||||
|
!not_initialized?
|
||||||
end
|
end
|
||||||
|
|
||||||
def init
|
def init
|
||||||
|
ensure_not_initialized!
|
||||||
version_code = FFI::MemoryPointer.new(:int)
|
version_code = FFI::MemoryPointer.new(:int)
|
||||||
check_status!(API.sane_init(version_code, FFI::Pointer::NULL))
|
check_status!(API.sane_init(version_code, FFI::Pointer::NULL))
|
||||||
version_code.read_int
|
@version = version_code.read_int
|
||||||
end
|
end
|
||||||
|
|
||||||
def exit
|
def exit
|
||||||
|
ensure_initialized!
|
||||||
API.sane_exit
|
API.sane_exit
|
||||||
|
@version = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_devices
|
def get_devices
|
||||||
devices_pointer = FFI::MemoryPointer.new(:pointer)
|
ensure_initialized!
|
||||||
check_status!(API.sane_get_devices(devices_pointer, 0))
|
devices_pointer_pointer = FFI::MemoryPointer.new(:pointer)
|
||||||
devices = devices_pointer.read_pointer
|
check_status!(API.sane_get_devices(devices_pointer_pointer, 0))
|
||||||
|
devices_pointer = devices_pointer_pointer.read_pointer
|
||||||
[].tap do |result|
|
[].tap do |result|
|
||||||
until devices.read_pointer.null?
|
until devices_pointer.read_pointer.null?
|
||||||
result << API::Device.new(devices.read_pointer)
|
result << API::Device.new(devices_pointer.read_pointer).to_hash
|
||||||
devices += FFI.type_size(:pointer)
|
devices_pointer += FFI.type_size(:pointer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def open(device_name)
|
def open(device_name)
|
||||||
|
ensure_initialized!
|
||||||
device_handle_pointer = FFI::MemoryPointer.new(:pointer)
|
device_handle_pointer = FFI::MemoryPointer.new(:pointer)
|
||||||
check_status!(API.sane_open(device_name, device_handle_pointer))
|
check_status!(API.sane_open(device_name, device_handle_pointer))
|
||||||
device_handle_pointer.read_pointer
|
device_handle_pointer.read_pointer
|
||||||
end
|
end
|
||||||
|
|
||||||
def close(device_handle)
|
def close(device_handle)
|
||||||
|
ensure_initialized!
|
||||||
API.sane_close(device_handle)
|
API.sane_close(device_handle)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_option_descriptor(device_handle, option)
|
def get_option_descriptor(device_handle, option)
|
||||||
|
ensure_initialized!
|
||||||
result = API.sane_get_option_descriptor(device_handle, option)
|
result = API.sane_get_option_descriptor(device_handle, option)
|
||||||
API::OptionDescriptor.new(result)
|
API::OptionDescriptor.new(result).to_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_option(device_handle, option)
|
def get_option(device_handle, option)
|
||||||
|
ensure_initialized!
|
||||||
descriptor = get_option_descriptor(device_handle, option)
|
descriptor = get_option_descriptor(device_handle, option)
|
||||||
|
|
||||||
case descriptor[:type]
|
case descriptor[:type]
|
||||||
|
@ -72,6 +94,7 @@ class Sane
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_option(device_handle, option, value)
|
def set_option(device_handle, option, value)
|
||||||
|
ensure_initialized!
|
||||||
descriptor = get_option_descriptor(device_handle, option)
|
descriptor = get_option_descriptor(device_handle, option)
|
||||||
|
|
||||||
case descriptor[:type]
|
case descriptor[:type]
|
||||||
|
@ -97,13 +120,40 @@ class Sane
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def start(handle)
|
||||||
|
ensure_initialized!
|
||||||
|
API.sane_start(handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(handle, size = 64 * 1024)
|
||||||
|
ensure_initialized!
|
||||||
|
data_pointer = FFI::MemoryPointer.new(:char, size)
|
||||||
|
length_pointer = FFI::MemoryPointer.new(:int)
|
||||||
|
check_status!(API.sane_read(handle, data_pointer, size, length_pointer))
|
||||||
|
data_pointer.read_string(length_pointer.read_int)
|
||||||
|
end
|
||||||
|
|
||||||
def strstatus(status)
|
def strstatus(status)
|
||||||
|
ensure_initialized!
|
||||||
API.sane_strstatus(status)
|
API.sane_strstatus(status)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def get_parameters(handle)
|
||||||
|
ensure_initialized!
|
||||||
|
parameters_pointer = FFI::MemoryPointer.new(API::Parameters.size)
|
||||||
|
check_status!(API.sane_get_parameters(handle, parameters_pointer))
|
||||||
|
API::Parameters.new(parameters_pointer).to_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_not_initialized!
|
||||||
|
raise "SANE library is already initialized" if initialized?
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_initialized!
|
||||||
|
raise "SANE library is not initialized" if not_initialized?
|
||||||
|
end
|
||||||
|
|
||||||
def check_status!(status)
|
def check_status!(status)
|
||||||
raise Error.new(strstatus(status), status) if status != :good
|
raise Error.new(strstatus(status), status) unless status == :good
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,8 @@ class Sane
|
||||||
|
|
||||||
class Device < FFI::Struct
|
class Device < FFI::Struct
|
||||||
layout :name, :string, :vendor, :string, :model, :string, :type, :string
|
layout :name, :string, :vendor, :string, :model, :string, :type, :string
|
||||||
|
|
||||||
|
def to_hash; {name: self[:name], vendor: self[:vendor], model: self[:model], type: self[:type]} end
|
||||||
end
|
end
|
||||||
|
|
||||||
class OptionDescriptor < FFI::Struct
|
class OptionDescriptor < FFI::Struct
|
||||||
|
@ -19,10 +21,14 @@ class Sane
|
||||||
layout :string_list, :pointer, :word_list, :pointer, :range, :pointer
|
layout :string_list, :pointer, :word_list, :pointer, :range, :pointer
|
||||||
end
|
end
|
||||||
layout :name, :string, :title, :string, :desc, :string, :type, :value_type, :unit, :unit, :size, :int, :cap, :int, :constraint_type, ConstraintType
|
layout :name, :string, :title, :string, :desc, :string, :type, :value_type, :unit, :unit, :size, :int, :cap, :int, :constraint_type, ConstraintType
|
||||||
|
|
||||||
|
def to_hash; {name: self[:name], title: self[:title], desc: self[:desc], type: self[:type], unit: self[:unit], size: self[:size], cap: self[:cap]} end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Parameters < FFI::Struct
|
class Parameters < FFI::Struct
|
||||||
layout :format, :frame, :last_frame, :int, :bytes_per_line, :int, :pixels_per_line, :int, :lines, :int, :depth, :int
|
layout :format, :frame, :last_frame, :int, :bytes_per_line, :int, :pixels_per_line, :int, :lines, :int, :depth, :int
|
||||||
|
|
||||||
|
def to_hash; {format: self[:format], last_frame: self[:last_frame], bytes_per_line: self[:bytes_per_line], pixels_per_line: self[:pixels_per_line], lines: self[:lines], depth: self[:depth]} end
|
||||||
end
|
end
|
||||||
|
|
||||||
# extern SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
|
# extern SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
|
||||||
|
|
|
@ -1,29 +1,108 @@
|
||||||
class Sane
|
class Sane
|
||||||
class Device
|
class Device
|
||||||
def initialize(name)
|
def initialize(options)
|
||||||
@name = name
|
@name = options[:name]
|
||||||
|
@vendor = options[:vendor]
|
||||||
|
@model = options[:model]
|
||||||
|
@type = options[:type]
|
||||||
@handle = nil
|
@handle = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def open
|
|
||||||
@handle = Sane.open(@name) if closed?
|
|
||||||
if block_given?
|
|
||||||
yield self
|
|
||||||
close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def closed?
|
def closed?
|
||||||
@handle.nil?
|
@handle.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def opened?
|
def open?
|
||||||
!closed?
|
!closed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def open
|
||||||
|
ensure_closed!
|
||||||
|
@handle = Sane.instance.send(:open, @name)
|
||||||
|
if block_given?
|
||||||
|
begin
|
||||||
|
yield(self)
|
||||||
|
ensure
|
||||||
|
close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def close
|
def close
|
||||||
Sane.close(@handle) if opened?
|
ensure_open!
|
||||||
|
Sane.instance.send(:close, @handle)
|
||||||
@handle = nil
|
@handle = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def start
|
||||||
|
ensure_open!
|
||||||
|
Sane.instance.start(@handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read
|
||||||
|
ensure_open!
|
||||||
|
Sane.instance.send(:read, @handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
def option_count
|
||||||
|
ensure_open!
|
||||||
|
Sane.instance.send(:get_option, @handle, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
ensure_open!
|
||||||
|
Sane.instance.send(:get_parameters, @handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](option)
|
||||||
|
ensure_open!
|
||||||
|
Sane.instance.send(:get_option, @handle, option_lookup(option))
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(option, value)
|
||||||
|
ensure_open!
|
||||||
|
Sane.instance.send(:set_option, @handle, option_lookup(option), value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def option_descriptors
|
||||||
|
option_count.times.map { |i| Sane.instance.send(:get_option_descriptor, @handle, i) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def option_names
|
||||||
|
option_descriptors.map { |option| option[:name] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def option_values
|
||||||
|
option_count.times.map do |i|
|
||||||
|
begin
|
||||||
|
self[i]
|
||||||
|
rescue Error
|
||||||
|
nil # we can't get values of some options, ignore them
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def options
|
||||||
|
{}.tap { |hash| option_count.times { |i| hash[option_names[i]] = option_values[i] } }
|
||||||
|
end
|
||||||
|
|
||||||
|
def option_lookup(option_name)
|
||||||
|
return option_name if (0..option_count).include?(option_name)
|
||||||
|
option_descriptors.index { |option| option[:name] == option_name.to_s } or raise ArgumentError, "Option not found: #{option_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe(option)
|
||||||
|
option_descriptors[option_lookup(option)]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_closed!
|
||||||
|
raise "Device is already open" if open?
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_open!
|
||||||
|
raise "Device is closed" if closed?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue