sane-ffi-denkn 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8255a18377ffb370d20eca17c9188717a0a9d284
4
+ data.tar.gz: 800d4c2645f5ba6c63eb7dfb1423f772492b5ab4
5
+ SHA512:
6
+ metadata.gz: 43aa89dca9001f69f5c3d4bb24f64464da68a6ac8e7eb2f748bdc0aeae8914b5f6ee937dd03b53ff28e7599d4767ef2161e0588a5b7ab4e99ea6e0891e405be2
7
+ data.tar.gz: df42360f9cf5f2218a93cb3ce90fc0eb732d8f25a0e12e22b0bed663944dabed0fa5bf6bfd44c1799997a741abdfeb0dd42a7b0812650f08748031c743f2203b
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in scanner.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,36 @@
1
+ = SANE FFI
2
+
3
+ Scanner Access Now Easier in Ruby using FFI. This gem provides
4
+ bindings to SANE library using Ruby FFI. You can easily access to the
5
+ device state (i.e. buttons), setup device parameters, scan images,
6
+ etc.
7
+
8
+ == Usage
9
+
10
+ >> require "sane"
11
+ => true
12
+ >> Sane.open { |sane| puts sane.devices.inspect }
13
+ [#<Sane::Device:"genesys:libusb:002:032">]
14
+ => nil
15
+ >> Sane.open { |sane| puts sane.devices.first.name }
16
+ genesys:libusb:002:032
17
+ => nil
18
+ >> Sane.open { |sane| puts sane.devices.first.vendor }
19
+ Canon
20
+ => nil
21
+ >> Sane.open { |sane| puts sane.devices.first.model }
22
+ LiDE 100
23
+ => nil
24
+ >> Sane.open { |sane| puts sane.devices.first.type }
25
+ flatbed scanner
26
+ => nil
27
+ >> Sane.open { |sane| sane.devices.first.open { |device| puts device.describe(:copy) } }
28
+ {:name=>"copy", :title=>"Copy button", :desc=>"Copy button", :type=>:bool, :unit=>:none, :size=>4, :cap=>70}
29
+ => nil
30
+ >> Sane.open { |sane| sane.devices.first.open { |device| puts device[:copy] } }
31
+ false
32
+ => nil
33
+
34
+ == Copyright
35
+
36
+ Copyright (c) 2011 Jakub Kuźma
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/lib/sane/api.rb ADDED
@@ -0,0 +1,91 @@
1
+ class Sane
2
+ module API
3
+ extend FFI::Library
4
+
5
+ lib_paths = Array(ENV["SANE_LIB"] || Dir["/{opt,usr}/{,local/}lib{,64}/libsane.{1.dylib,so.1*}"])
6
+ fallback_names = %w(libsane.1.dylib libsane.so.1 sane1.dll)
7
+ ffi_lib(lib_paths + fallback_names)
8
+
9
+ enum :status, [:good, 0, :unsupported, :cancelled, :device_busy, :inval, :eof, :jammed, :no_docs, :cover_open, :io_error, :no_mem, :access_denied]
10
+ enum :value_type, [:bool, 0, :int, :fixed, :string, :button, :group]
11
+ enum :unit, [:none, 0, :pixel, :bit, :mm, :dpi, :percent, :microsecond]
12
+ enum :action, [:get_value, 0, :set_value, :set_auto]
13
+ enum :frame, [:gray, :rgb, :red, :green, :blue]
14
+
15
+ class Device < FFI::Struct
16
+ layout :name, :string, :vendor, :string, :model, :string, :type, :string
17
+
18
+ def to_hash
19
+ {
20
+ :name => self[:name],
21
+ :vendor => self[:vendor],
22
+ :model => self[:model],
23
+ :type => self[:type]
24
+ }
25
+ end
26
+ end
27
+
28
+ class OptionDescriptor < FFI::Struct
29
+ class ConstraintType < FFI::Union
30
+ layout :string_list, :pointer, :word_list, :pointer, :range, :pointer
31
+ end
32
+ layout :name, :string, :title, :string, :desc, :string, :type, :value_type, :unit, :unit, :size, :int, :cap, :int, :constraint_type, ConstraintType
33
+
34
+ def to_hash;
35
+ {
36
+ :name => self[:name],
37
+ :title => self[:title],
38
+ :desc => self[:desc],
39
+ :type => self[:type],
40
+ :unit => self[:unit],
41
+ :size => self[:size],
42
+ :cap => self[:cap]
43
+ }
44
+ end
45
+ end
46
+
47
+ class Parameters < FFI::Struct
48
+ layout :format, :frame, :last_frame, :int, :bytes_per_line, :int, :pixels_per_line, :int, :lines, :int, :depth, :int
49
+
50
+ def to_hash
51
+ {
52
+ :format => self[:format],
53
+ :last_frame => self[:last_frame],
54
+ :bytes_per_line => self[:bytes_per_line],
55
+ :pixels_per_line => self[:pixels_per_line],
56
+ :lines => self[:lines],
57
+ :depth => self[:depth]
58
+ }
59
+ end
60
+ end
61
+
62
+ # extern SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
63
+ attach_function :sane_init, [:pointer, :pointer], :status
64
+ # extern void sane_exit (void);
65
+ attach_function :sane_exit, [], :void
66
+ # extern SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);
67
+ attach_function :sane_get_devices, [:pointer, :int], :status
68
+ # extern SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle);
69
+ attach_function :sane_open, [:string, :pointer], :status
70
+ # extern void sane_close (SANE_Handle handle);
71
+ attach_function :sane_close, [:pointer], :void
72
+ # extern const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option);
73
+ attach_function :sane_get_option_descriptor, [:pointer, :int], :pointer
74
+ # extern SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info);
75
+ attach_function :sane_control_option, [:pointer, :int, :action, :pointer, :pointer], :status
76
+ # extern SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params);
77
+ attach_function :sane_get_parameters, [:pointer, :pointer], :status
78
+ # extern SANE_Status sane_start (SANE_Handle handle);
79
+ attach_function :sane_start, [:pointer], :status
80
+ # extern SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length);
81
+ attach_function :sane_read, [:pointer, :pointer, :int, :pointer], :status
82
+ # extern void sane_cancel (SANE_Handle handle);
83
+ attach_function :sane_cancel, [:pointer], :void
84
+ # extern SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking);
85
+ attach_function :sane_set_io_mode, [:pointer, :int], :status
86
+ # extern SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd);
87
+ attach_function :sane_get_select_fd, [:pointer, :pointer], :status
88
+ # extern SANE_String_Const sane_strstatus (SANE_Status status);
89
+ attach_function :sane_strstatus, [:status], :string
90
+ end
91
+ end
@@ -0,0 +1,133 @@
1
+ class Sane
2
+ class Device
3
+ attr_reader :name, :vendor, :model, :type
4
+
5
+ def initialize(options)
6
+ @name = options[:name]
7
+ @vendor = options[:vendor]
8
+ @model = options[:model]
9
+ @type = options[:type]
10
+ @handle = nil
11
+ end
12
+
13
+ def closed?
14
+ @handle.nil?
15
+ end
16
+
17
+ def open?
18
+ !closed?
19
+ end
20
+
21
+ def open
22
+ ensure_closed!
23
+ @handle = Sane.instance.send(:open, @name)
24
+ if block_given?
25
+ begin
26
+ yield(self)
27
+ ensure
28
+ close
29
+ end
30
+ end
31
+ end
32
+
33
+ def close
34
+ ensure_open!
35
+ Sane.instance.send(:close, @handle)
36
+ @handle = nil
37
+ end
38
+
39
+ def start
40
+ ensure_open!
41
+ Sane.instance.start(@handle)
42
+ end
43
+
44
+ def read
45
+ ensure_open!
46
+ Sane.instance.send(:read, @handle)
47
+ end
48
+
49
+ def cancel
50
+ ensure_open!
51
+ Sane.instance.send(:cancel, @handle)
52
+ end
53
+
54
+ def sync
55
+ ensure_open!
56
+ Sane.instance.send(:set_io_mode, @handle, false)
57
+ end
58
+
59
+ def async
60
+ ensure_open!
61
+ Sane.instance.send(:set_io_mode, @handle, true)
62
+ end
63
+
64
+ def option_count
65
+ ensure_open!
66
+ Sane.instance.send(:get_option, @handle, 0)
67
+ end
68
+
69
+ def parameters
70
+ ensure_open!
71
+ Sane.instance.send(:get_parameters, @handle)
72
+ end
73
+
74
+ def [](option)
75
+ ensure_open!
76
+ Sane.instance.send(:get_option, @handle, option_lookup(option))
77
+ end
78
+
79
+ def []=(option, value)
80
+ ensure_open!
81
+ Sane.instance.send(:set_option, @handle, option_lookup(option), value)
82
+ end
83
+
84
+ def option_descriptors
85
+ option_count.times.map { |i| Sane.instance.send(:get_option_descriptor, @handle, i) }
86
+ end
87
+
88
+ def option_names
89
+ option_descriptors.map { |option| option[:name] }
90
+ end
91
+
92
+ def option_values
93
+ option_count.times.map do |i|
94
+ begin
95
+ self[i]
96
+ rescue Error => exception
97
+ if exception.status == :inval
98
+ nil # we can't read values of some options (i.e. buttons), ignore them
99
+ else
100
+ raise exception
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def options
107
+ Hash[*option_names.zip(option_values).flatten]
108
+ end
109
+
110
+ def describe(option)
111
+ option_descriptors[option_lookup(option)]
112
+ end
113
+
114
+ def inspect
115
+ %Q{#<#{self.class.name}:"#{name}">}
116
+ end
117
+
118
+ private
119
+
120
+ def option_lookup(option_name)
121
+ return option_name if (0..option_count).include?(option_name)
122
+ option_descriptors.index { |option| option[:name] == option_name.to_s } or raise(ArgumentError, "Option not found: #{option_name}")
123
+ end
124
+
125
+ def ensure_closed!
126
+ raise("Device is already open") if open?
127
+ end
128
+
129
+ def ensure_open!
130
+ raise("Device is closed") if closed?
131
+ end
132
+ end
133
+ end
data/lib/sane/error.rb ADDED
@@ -0,0 +1,10 @@
1
+ class Sane
2
+ class Error < StandardError
3
+ attr_reader :status
4
+
5
+ def initialize(message, status)
6
+ super(message)
7
+ @status = status
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ class Sane
2
+ VERSION = "0.1.2"
3
+ end
data/lib/sane.rb ADDED
@@ -0,0 +1,175 @@
1
+ require "singleton"
2
+ require "forwardable"
3
+
4
+ require "ffi"
5
+
6
+ require "sane"
7
+ require "sane/api"
8
+ require "sane/error"
9
+ require "sane/device"
10
+
11
+ class Sane
12
+ include Singleton
13
+
14
+ attr_reader :version
15
+
16
+ def self.open
17
+ instance.send(:init)
18
+ yield(instance)
19
+ ensure
20
+ instance.send(:exit)
21
+ end
22
+
23
+ def devices
24
+ get_devices.map { |device| Device.new(device) }
25
+ end
26
+
27
+ def not_initialized?
28
+ version.nil?
29
+ end
30
+
31
+ def initialized?
32
+ !not_initialized?
33
+ end
34
+
35
+ def init
36
+ ensure_not_initialized!
37
+ version_code = FFI::MemoryPointer.new(:int)
38
+ check_status!(API.sane_init(version_code, FFI::Pointer::NULL))
39
+ @version = version_code.read_int
40
+ end
41
+
42
+ def exit
43
+ ensure_initialized!
44
+ API.sane_exit
45
+ @version = nil
46
+ end
47
+
48
+ def get_devices
49
+ ensure_initialized!
50
+ devices_pointer_pointer = FFI::MemoryPointer.new(:pointer)
51
+ check_status!(API.sane_get_devices(devices_pointer_pointer, 0))
52
+ devices_pointer = devices_pointer_pointer.read_pointer
53
+ result = []
54
+ until devices_pointer.read_pointer.null?
55
+ result << API::Device.new(devices_pointer.read_pointer).to_hash
56
+ devices_pointer += FFI.type_size(:pointer)
57
+ end
58
+ result
59
+ end
60
+
61
+ def open(device_name)
62
+ ensure_initialized!
63
+ device_handle_pointer = FFI::MemoryPointer.new(:pointer)
64
+ check_status!(API.sane_open(device_name, device_handle_pointer))
65
+ device_handle_pointer.read_pointer
66
+ end
67
+
68
+ def close(device_handle)
69
+ ensure_initialized!
70
+ API.sane_close(device_handle)
71
+ end
72
+
73
+ def get_option_descriptor(device_handle, option)
74
+ ensure_initialized!
75
+ result = API.sane_get_option_descriptor(device_handle, option)
76
+ API::OptionDescriptor.new(result).to_hash
77
+ end
78
+
79
+ def get_option(device_handle, option)
80
+ ensure_initialized!
81
+ descriptor = get_option_descriptor(device_handle, option)
82
+
83
+ case descriptor[:type]
84
+ when :string
85
+ value_pointer = FFI::MemoryPointer.new(:pointer)
86
+ when :bool, :int, :fixed
87
+ value_pointer = FFI::MemoryPointer.new(:int)
88
+ else
89
+ return nil
90
+ end
91
+
92
+ check_status!(API.sane_control_option(device_handle, option, :get_value, value_pointer, FFI::Pointer::NULL))
93
+
94
+ case descriptor[:type]
95
+ when :string
96
+ value_pointer.read_string
97
+ when :bool
98
+ !value_pointer.read_int.zero?
99
+ when :int, :fixed
100
+ value_pointer.read_int
101
+ end
102
+ end
103
+
104
+ def set_option(device_handle, option, value)
105
+ ensure_initialized!
106
+ descriptor = get_option_descriptor(device_handle, option)
107
+
108
+ case descriptor[:type]
109
+ when :string
110
+ value_pointer = FFI::MemoryPointer.from_string(value)
111
+ when :int, :fixed
112
+ value_pointer = FFI::MemoryPointer.new(:int).write_int(value)
113
+ when :bool
114
+ value_pointer = FFI::MemoryPointer.new(:int).write_int(value ? 1 : 0)
115
+ else
116
+ return nil
117
+ end
118
+
119
+ check_status!(API.sane_control_option(device_handle, option, :set_value, value_pointer, FFI::Pointer::NULL))
120
+
121
+ case descriptor[:type]
122
+ when :string
123
+ value_pointer.read_string
124
+ when :bool
125
+ !value_pointer.read_int.zero?
126
+ when :int, :fixed
127
+ value_pointer.read_int
128
+ end
129
+ end
130
+
131
+ def start(handle)
132
+ ensure_initialized!
133
+ API.sane_start(handle)
134
+ end
135
+
136
+ def read(handle, size = 64 * 1024)
137
+ ensure_initialized!
138
+ data_pointer = FFI::MemoryPointer.new(:char, size)
139
+ length_pointer = FFI::MemoryPointer.new(:int)
140
+ check_status!(API.sane_read(handle, data_pointer, size, length_pointer))
141
+ data_pointer.read_string(length_pointer.read_int)
142
+ end
143
+
144
+ def strstatus(status)
145
+ ensure_initialized!
146
+ API.sane_strstatus(status)
147
+ end
148
+
149
+ def get_parameters(handle)
150
+ ensure_initialized!
151
+ parameters_pointer = FFI::MemoryPointer.new(API::Parameters.size)
152
+ check_status!(API.sane_get_parameters(handle, parameters_pointer))
153
+ API::Parameters.new(parameters_pointer).to_hash
154
+ end
155
+
156
+ def set_io_mode(handle, non_blocking)
157
+ check_status!(API.sane_set_io_mode(handle, non_blocking ? 1 : 0))
158
+ end
159
+
160
+ def cancel(handle)
161
+ API.sane_cancel(handle)
162
+ end
163
+
164
+ def ensure_not_initialized!
165
+ raise("SANE library is already initialized") if initialized?
166
+ end
167
+
168
+ def ensure_initialized!
169
+ raise("SANE library is not initialized") if not_initialized?
170
+ end
171
+
172
+ def check_status!(status)
173
+ raise(Error.new(strstatus(status), status)) unless status == :good
174
+ end
175
+ end
data/sane-ffi.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sane/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sane-ffi-denkn"
7
+ s.version = Sane::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jakub Kuźma & Denis Knauf"]
10
+ s.email = ["qoobaa@gmail.com", %Q[de#{?n}]+'is'+?@+"d#{'en'}k"+?n+?.+%q[at]]
11
+ s.homepage = ""
12
+ s.summary = %q{SANE bindings}
13
+ s.description = %q{Scanner Access now Easier}
14
+
15
+ s.add_dependency "ffi"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sane-ffi-denkn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Kuźma & Denis Knauf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Scanner Access now Easier
28
+ email:
29
+ - qoobaa@gmail.com
30
+ - denis@denkn.at
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - .gitignore
36
+ - Gemfile
37
+ - README.rdoc
38
+ - Rakefile
39
+ - lib/sane.rb
40
+ - lib/sane/api.rb
41
+ - lib/sane/device.rb
42
+ - lib/sane/error.rb
43
+ - lib/sane/version.rb
44
+ - sane-ffi.gemspec
45
+ homepage: ''
46
+ licenses: []
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.0.14
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: SANE bindings
68
+ test_files: []
69
+ has_rdoc: