178 lines
4.2 KiB
Ruby
178 lines
4.2 KiB
Ruby
require "singleton"
|
|
require "forwardable"
|
|
|
|
require "ffi"
|
|
|
|
require "sane"
|
|
require "sane/api"
|
|
require "sane/error"
|
|
require "sane/device"
|
|
|
|
class Sane
|
|
include Singleton
|
|
|
|
attr_reader :version
|
|
|
|
def self.open
|
|
instance.send(:init)
|
|
yield(instance)
|
|
ensure
|
|
instance.send(:exit)
|
|
end
|
|
|
|
def devices
|
|
get_devices.map { |device| Device.new(device) }
|
|
end
|
|
|
|
private
|
|
|
|
def not_initialized?
|
|
version.nil?
|
|
end
|
|
|
|
def initialized?
|
|
!not_initialized?
|
|
end
|
|
|
|
def init
|
|
ensure_not_initialized!
|
|
version_code = FFI::MemoryPointer.new(:int)
|
|
check_status!(API.sane_init(version_code, FFI::Pointer::NULL))
|
|
@version = version_code.read_int
|
|
end
|
|
|
|
def exit
|
|
ensure_initialized!
|
|
API.sane_exit
|
|
@version = nil
|
|
end
|
|
|
|
def get_devices
|
|
ensure_initialized!
|
|
devices_pointer_pointer = FFI::MemoryPointer.new(:pointer)
|
|
check_status!(API.sane_get_devices(devices_pointer_pointer, 0))
|
|
devices_pointer = devices_pointer_pointer.read_pointer
|
|
result = []
|
|
until devices_pointer.read_pointer.null?
|
|
result << API::Device.new(devices_pointer.read_pointer).to_hash
|
|
devices_pointer += FFI.type_size(:pointer)
|
|
end
|
|
result
|
|
end
|
|
|
|
def open(device_name)
|
|
ensure_initialized!
|
|
device_handle_pointer = FFI::MemoryPointer.new(:pointer)
|
|
check_status!(API.sane_open(device_name, device_handle_pointer))
|
|
device_handle_pointer.read_pointer
|
|
end
|
|
|
|
def close(device_handle)
|
|
ensure_initialized!
|
|
API.sane_close(device_handle)
|
|
end
|
|
|
|
def get_option_descriptor(device_handle, option)
|
|
ensure_initialized!
|
|
result = API.sane_get_option_descriptor(device_handle, option)
|
|
API::OptionDescriptor.new(result).to_hash
|
|
end
|
|
|
|
def get_option(device_handle, option)
|
|
ensure_initialized!
|
|
descriptor = get_option_descriptor(device_handle, option)
|
|
|
|
case descriptor[:type]
|
|
when :string
|
|
value_pointer = FFI::MemoryPointer.new(:pointer)
|
|
when :bool, :int, :fixed
|
|
value_pointer = FFI::MemoryPointer.new(:int)
|
|
else
|
|
return nil
|
|
end
|
|
|
|
check_status!(API.sane_control_option(device_handle, option, :get_value, value_pointer, FFI::Pointer::NULL))
|
|
|
|
case descriptor[:type]
|
|
when :string
|
|
value_pointer.read_string
|
|
when :bool
|
|
!value_pointer.read_int.zero?
|
|
when :int, :fixed
|
|
value_pointer.read_int
|
|
end
|
|
end
|
|
|
|
def set_option(device_handle, option, value)
|
|
ensure_initialized!
|
|
descriptor = get_option_descriptor(device_handle, option)
|
|
|
|
case descriptor[:type]
|
|
when :string
|
|
value_pointer = FFI::MemoryPointer.from_string(value)
|
|
when :int, :fixed
|
|
value_pointer = FFI::MemoryPointer.new(:int).write_int(value)
|
|
when :bool
|
|
value_pointer = FFI::MemoryPointer.new(:int).write_int(value ? 1 : 0)
|
|
else
|
|
return nil
|
|
end
|
|
|
|
check_status!(API.sane_control_option(device_handle, option, :set_value, value_pointer, FFI::Pointer::NULL))
|
|
|
|
case descriptor[:type]
|
|
when :string
|
|
value_pointer.read_string
|
|
when :bool
|
|
!value_pointer.read_int.zero?
|
|
when :int, :fixed
|
|
value_pointer.read_int
|
|
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)
|
|
ensure_initialized!
|
|
API.sane_strstatus(status)
|
|
end
|
|
|
|
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 set_io_mode(handle, non_blocking)
|
|
check_status!(API.sane_set_io_mode(handle, non_blocking ? 1 : 0))
|
|
end
|
|
|
|
def cancel(handle)
|
|
API.sane_cancel(handle)
|
|
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)
|
|
raise(Error.new(strstatus(status), status)) unless status == :good
|
|
end
|
|
end
|