vigilem-support 0.0.9
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 +7 -0
- data/lib/vigilem/ffi.rb +19 -0
- data/lib/vigilem/ffi/array_pointer_sync.rb +382 -0
- data/lib/vigilem/ffi/struct.rb +54 -0
- data/lib/vigilem/ffi/utils.rb +240 -0
- data/lib/vigilem/ffi/utils/struct.rb +93 -0
- data/lib/vigilem/support.rb +31 -0
- data/lib/vigilem/support/core_ext.rb +13 -0
- data/lib/vigilem/support/core_ext/debug_puts.rb +5 -0
- data/lib/vigilem/support/core_ext/enumerable.rb +25 -0
- data/lib/vigilem/support/key_map.rb +323 -0
- data/lib/vigilem/support/key_map_info.rb +85 -0
- data/lib/vigilem/support/lazy_simple_delegator.rb +81 -0
- data/lib/vigilem/support/max_size_error.rb +17 -0
- data/lib/vigilem/support/metadata.rb +13 -0
- data/lib/vigilem/support/obj_space.rb +28 -0
- data/lib/vigilem/support/patch/cucumber/c_lexer.rb +30 -0
- data/lib/vigilem/support/patch/ffi/pointer.rb +19 -0
- data/lib/vigilem/support/size_error.rb +6 -0
- data/lib/vigilem/support/sys.rb +5 -0
- data/lib/vigilem/support/system.rb +92 -0
- data/lib/vigilem/support/transmutable_hash.rb +206 -0
- data/lib/vigilem/support/utils.rb +224 -0
- data/lib/vigilem/support/version.rb +5 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/vigilem/ffi/array_pointer_sync_spec.rb +728 -0
- data/spec/vigilem/ffi/struct_spec.rb +22 -0
- data/spec/vigilem/ffi/utils/struct_spec.rb +79 -0
- data/spec/vigilem/ffi/utils_spec.rb +240 -0
- data/spec/vigilem/support/core_ext/enumerable_spec.rb +38 -0
- data/spec/vigilem/support/data/cached.kmap +130 -0
- data/spec/vigilem/support/data/cached_UTF-8_del.kmap +142 -0
- data/spec/vigilem/support/data/cached_short.kmap +38 -0
- data/spec/vigilem/support/data/dump_keys_short.kmap +128 -0
- data/spec/vigilem/support/key_map_spec.rb +767 -0
- data/spec/vigilem/support/lazy_simple_delegator_spec.rb +77 -0
- data/spec/vigilem/support/obj_space_spec.rb +47 -0
- data/spec/vigilem/support/sys_spec.rb +21 -0
- data/spec/vigilem/support/system_spec.rb +31 -0
- data/spec/vigilem/support/transmutable_hash_spec.rb +175 -0
- data/spec/vigilem/support/utils_spec.rb +214 -0
- metadata +240 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
module Vigilem
|
2
|
+
module FFI
|
3
|
+
# utilities for FFI
|
4
|
+
module Utils
|
5
|
+
|
6
|
+
# converts struct to a Hash
|
7
|
+
# @param [#members, #values] struct
|
8
|
+
# @param [Integer || NilClass] limit
|
9
|
+
# @return [Hash]
|
10
|
+
def struct_to_h(struct, limit=nil)
|
11
|
+
Utils._struct_to_h(struct, limit)
|
12
|
+
end
|
13
|
+
|
14
|
+
# gets the FFI::Type::Builtin a type
|
15
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
16
|
+
# @return [Symbol] the builtin type name
|
17
|
+
def get_builtin_type_name(type)
|
18
|
+
builtin = ::FFI.find_type(type) || type
|
19
|
+
raise TypeError, "unable to resolve type '#{type}'" unless builtin.is_a? ::FFI::Type::Builtin
|
20
|
+
(nt = ::FFI::NativeType).constants.find {|const| nt.const_get(const) == builtin }.downcase
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :get_native_type_name, :get_builtin_type_name
|
24
|
+
|
25
|
+
# @todo this is a problem, like XOpenDisplay, the ptr returned
|
26
|
+
# maybe huge, but that;s not the size of the object in the pointer
|
27
|
+
# @param [Pointer] pointer
|
28
|
+
# @return [Integer]
|
29
|
+
def ptr_capacity(pointer)
|
30
|
+
pointer.size/pointer.type_size
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# @param [#type] obj
|
35
|
+
# @return [TrueClass || FalseClass]
|
36
|
+
def is_a_struct?(obj)
|
37
|
+
[::FFI::Struct, ::FFI::StructByValue, ::FFI::StructByReference].any? {|struct_klass| obj.is_a?(struct_klass) or obj.respond(:type).is_a? struct_klass }
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# @param [::FFI::Pointer] pointer
|
42
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
43
|
+
# @param value
|
44
|
+
# @return [::FFI::Pointer] pointer
|
45
|
+
def write_typedef(pointer, type, value)
|
46
|
+
pointer.__send__(:"write_#{get_builtin_type_name(type) }", value)
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @param [::FFI::Pointer] pointer
|
51
|
+
# @param [Class] struct_class
|
52
|
+
# @param [Integer] num
|
53
|
+
# @return [Array]
|
54
|
+
def read_array_of_structs(pointer, struct_class, num=nil)
|
55
|
+
1.upto(num||pointer.type_size/struct_class.size).map { struct_class.new(pointer) }
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# possible refactor `pointer.write_array_of_type(type, :"write_array_of_#{get_builtin_type_name(type) }", [*value])`
|
60
|
+
# @param [::FFI::Pointer] pointer
|
61
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
62
|
+
# @param [Array] value
|
63
|
+
# @return [::FFI::Pointer] pointer
|
64
|
+
def write_array_typedef(pointer, type, value)
|
65
|
+
pointer.__send__(:"write_array_of_#{get_builtin_type_name(type) }", [*value])
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# @param [::FFI::Pointer] pointer
|
70
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
71
|
+
# @return value from pointer
|
72
|
+
def read_typedef(pointer, type)
|
73
|
+
pointer.__send__(:"read_#{get_builtin_type_name(type) }")
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# @param [::FFI::Pointer] pointer
|
78
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
79
|
+
# @param [Integer] num
|
80
|
+
# @return [Array]
|
81
|
+
def read_array_typedef(pointer, type, num=1)
|
82
|
+
pointer.__send__(:"read_array_of_#{get_builtin_type_name(type) }", num)
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# @param [::FFI::Pointer] pointer
|
87
|
+
# @param [String] type
|
88
|
+
# @param value
|
89
|
+
# @param [Integer] offset
|
90
|
+
# @return [::FFI::Pointer] pointer
|
91
|
+
def put_typedef(pointer, type, value, offset=0)
|
92
|
+
pointer.__send__(:"put_#{get_builtin_type_name(type) }", offset, value)
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# @param [::FFI::Pointer] pointer
|
97
|
+
# @param [String] type
|
98
|
+
# @param [Array] value
|
99
|
+
# @param [Integer] offset
|
100
|
+
# @return pointer
|
101
|
+
def put_array_typedef(pointer, type, value, offset=0)
|
102
|
+
pointer.__send__(:"put_array_of_#{get_builtin_type_name(type) }", offset, [*value])
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# @param [::FFI::Pointer] pointer
|
107
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
108
|
+
# @param value
|
109
|
+
# @return [::FFI::Pointer] pointer
|
110
|
+
def replace_typedef(pointer, type, value)
|
111
|
+
put_typedef(pointer, type, value)
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# @param [::FFI::Pointer] pointer
|
116
|
+
# @param [Symbol || ::FFI::Type::Builtin] type
|
117
|
+
# @param value
|
118
|
+
# @return pointer
|
119
|
+
def add_int_typedef(pointer, type, value)
|
120
|
+
replace_typedef(pointer, type, (value + self.read_typedef(pointer, type)))
|
121
|
+
end
|
122
|
+
|
123
|
+
# @todo needed?
|
124
|
+
# @param struct
|
125
|
+
# @param [Proc] block
|
126
|
+
# @return the type of the fields or the result of the block
|
127
|
+
def types(struct, &block)
|
128
|
+
struct = Support::Utils.get_class(struct) unless struct.respond_to? :layout
|
129
|
+
struct.layout.fields.map do |field|
|
130
|
+
if block
|
131
|
+
yield field
|
132
|
+
else
|
133
|
+
field.type
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @todo refactor me
|
139
|
+
# assigns values to the struct
|
140
|
+
# @param [::FFI::Struct] struct
|
141
|
+
# @param [Array || Hash] vals
|
142
|
+
# @return struct
|
143
|
+
def struct_bulk_assign(struct, vals)
|
144
|
+
if vals.is_a? Hash
|
145
|
+
self.struct_bulk_assign_hash(struct, vals)
|
146
|
+
else
|
147
|
+
types(struct) do |fld|
|
148
|
+
_struct_bulk_assign(struct, fld, fld.name, vals)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
struct
|
152
|
+
end
|
153
|
+
|
154
|
+
alias_method :from_array, :struct_bulk_assign
|
155
|
+
|
156
|
+
#
|
157
|
+
# @param [::FFI::Struct] struct
|
158
|
+
# @param [Hash] hsh
|
159
|
+
# @return struct
|
160
|
+
def struct_bulk_assign_hash(struct, hsh)
|
161
|
+
hsh.each do |key, value|
|
162
|
+
the_field = struct.layout.fields.find {|fld| fld.name == key }
|
163
|
+
raise "on #{struct.class} attr #{key} not found in #{struct.layout.fields.map(&:name)}" unless the_field
|
164
|
+
_struct_bulk_assign(struct, the_field, key, [hsh[key]])
|
165
|
+
end
|
166
|
+
struct
|
167
|
+
end
|
168
|
+
|
169
|
+
alias_method :from_hash, :struct_bulk_assign_hash
|
170
|
+
|
171
|
+
# @todo unions don't need know the member name, the space taken up is the same
|
172
|
+
# @todo refactor me
|
173
|
+
# @param [::FFI::Struct] struct
|
174
|
+
# @param [::FFI::StructLayout::Field] field
|
175
|
+
# @param [Symbol] attr_name
|
176
|
+
# @param [Array || Hash] vals
|
177
|
+
# @return
|
178
|
+
def _struct_bulk_assign(struct, field, attr_name, vals)
|
179
|
+
# struct.type.struct_class, why use field?
|
180
|
+
#if is_a_struct?(struct.type)
|
181
|
+
if is_a_struct?(field)
|
182
|
+
if is_a_struct?(frst = vals.first) or frst.is_a? ::FFI::Union
|
183
|
+
struct[attr_name] = vals.shift
|
184
|
+
else
|
185
|
+
ptr_offset = struct.offsets.assoc(attr_name).last
|
186
|
+
struct_obj = if (struct_klass = field.type.struct_class) <= VFFIStruct
|
187
|
+
struct_klass.new(ptr_offset)
|
188
|
+
else
|
189
|
+
struct_klass.new
|
190
|
+
end
|
191
|
+
struct[attr_name] = struct_bulk_assign(struct_obj, vals.shift)
|
192
|
+
end
|
193
|
+
else
|
194
|
+
raise ArgumentError, "Arity mismatch: complex type `#{struct.inspect}' does not match argument `#{vals.inspect}'" unless vals.respond_to? :shift
|
195
|
+
struct[attr_name] = vals.shift
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
#
|
200
|
+
# @param [Class] struct_class
|
201
|
+
# @param [String] str_bytes
|
202
|
+
# @return [Array<FFI::Struct>]
|
203
|
+
def bytes_to_structs(struct_class, str_bytes)
|
204
|
+
ary_of_type(struct_class, struct_class.size, ::FFI::MemoryPointer.from_string(str_bytes))
|
205
|
+
end
|
206
|
+
|
207
|
+
# converts a pointer to an ary of ary_type
|
208
|
+
# @param type
|
209
|
+
# @param [Fixnum] type_size, this is given because a ::from_string has type_size of 1
|
210
|
+
# @param [FFI::Pointer] pointer
|
211
|
+
# @return [Array] of type
|
212
|
+
def ary_of_type(type, type_size, pointer, len=-1)
|
213
|
+
if type.is_a? Class and type <= ::FFI::Struct
|
214
|
+
# @todo slice is supposed to return new a pointer, when atype.type_size == pointer.type_size does this return the old pointer?
|
215
|
+
0.upto((pointer.size/type_size) - 1).map {|n| type.new(pointer.slice(n * type_size, type_size).dup) }
|
216
|
+
else
|
217
|
+
::Vigilem::FFI::Utils.read_array_typedef(pointer, type, ptr_capacity(pointer))
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
extend self
|
222
|
+
|
223
|
+
module_function
|
224
|
+
#
|
225
|
+
# @param [#members, #values] struct
|
226
|
+
# @param [Integer || NilClass] limit
|
227
|
+
# @return [Hash || Struct]
|
228
|
+
def _struct_to_h(struct, current=1, limit=nil)
|
229
|
+
if limit.nil? or limit <= current
|
230
|
+
Hash[struct.members.zip(struct.values.map {|val| is_a_struct?(val) ? _struct_to_h(val) : val } )]
|
231
|
+
else
|
232
|
+
struct
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
require 'vigilem/ffi/utils/struct'
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Vigilem
|
2
|
+
module FFI
|
3
|
+
module Utils
|
4
|
+
# Utils specfic to Vigilem::FFI::Struct
|
5
|
+
module Struct
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
# methods to be ::extend'd
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
# converts the members of the struct to methods
|
13
|
+
# @return
|
14
|
+
def members_to_methods
|
15
|
+
members.each do |member_name|
|
16
|
+
define_method(member_name, &lambda { self[member_name] })
|
17
|
+
define_method(:"#{member_name}=", &lambda {|val| self[member_name] = val })
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @see FFI::Struct::layout
|
22
|
+
# @param [Array] args
|
23
|
+
# @return
|
24
|
+
def layout_with_methods(*args)
|
25
|
+
raise SizeError, "Odd number of aguments:`#{args}'" if args.size.odd?
|
26
|
+
aliases = []
|
27
|
+
layout *(args.map do |names_or_value|
|
28
|
+
if names_or_value.is_a? Array
|
29
|
+
aliases << names_or_value
|
30
|
+
names_or_value.first
|
31
|
+
else
|
32
|
+
names_or_value
|
33
|
+
end
|
34
|
+
end)
|
35
|
+
ret = members_to_methods
|
36
|
+
aliases.each {|name_group| name_group.each {|alias_name| alias_method(alias_name, name_group[0]) } }
|
37
|
+
ret
|
38
|
+
end
|
39
|
+
|
40
|
+
# @todo update the layout without overwriting, when called after layout
|
41
|
+
# acts "like" the union keyword
|
42
|
+
# @return
|
43
|
+
def union(name, *args, &block)
|
44
|
+
# can't be Ruby Struct, because of layout_with_methods
|
45
|
+
unyun = Class.new(::FFI::Union) do
|
46
|
+
include Vigilem::FFI::Utils::Struct
|
47
|
+
layout_with_methods *(args.empty? ? block.call : args)
|
48
|
+
end
|
49
|
+
const_set(:"#{name[0].upcase}#{name[1..-1]}", unyun)
|
50
|
+
[name.to_sym, unyun]
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# @param [String] str_bytes
|
55
|
+
# @return [Array<VFFIStruct>]
|
56
|
+
def from_string(str_bytes)
|
57
|
+
Utils.bytes_to_structs(self, str_bytes)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @see FFI::Utils#bulk_assign
|
62
|
+
# @param [Hash || Array] vals
|
63
|
+
# @return
|
64
|
+
def bulk_assign(vals)
|
65
|
+
::Vigilem::FFI::Utils.struct_bulk_assign(self, vals)
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# @param [Symbol] attr_name
|
70
|
+
# @return
|
71
|
+
def type_of(attr_name)
|
72
|
+
field = self.class.layout.fields.find {|fld| fld.name == attr_name }
|
73
|
+
raise "no field by the name #{attr_name} for #{self}" unless field
|
74
|
+
field.type
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# @param [Proc] block
|
79
|
+
# @return
|
80
|
+
def types(&block)
|
81
|
+
::Vigilem::FFI::Utils.types(self, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# @see Vigilem::FFI::Utils.struct_to_h
|
86
|
+
# @return [Hash]
|
87
|
+
def to_h
|
88
|
+
::Vigilem::FFI::Utils.struct_to_h(self)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'vigilem/support/version'
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
require 'vigilem/support/utils'
|
6
|
+
|
7
|
+
require 'vigilem/support/core_ext'
|
8
|
+
require 'vigilem/support/system'
|
9
|
+
|
10
|
+
require 'vigilem/ffi'
|
11
|
+
|
12
|
+
require 'vigilem/support/metadata'
|
13
|
+
|
14
|
+
module Vigilem
|
15
|
+
#
|
16
|
+
module Support
|
17
|
+
|
18
|
+
# autoload is deprecated
|
19
|
+
# @param [Symbol]
|
20
|
+
# @return
|
21
|
+
def self.const_missing(const)
|
22
|
+
if [:System, :TransmutableHash, :LazySimpleDelegator, :MaxSizeError, :SizeError, :Sys, :KeyMap].include? const
|
23
|
+
require "vigilem/support/#{const.to_s.snakecase}"
|
24
|
+
const_get(const)
|
25
|
+
else
|
26
|
+
super(const)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'vigilem/support/core_ext/enumerable'
|
2
|
+
|
3
|
+
require 'facets/kernel/respond'
|
4
|
+
|
5
|
+
if defined?(Delegator) and not Delegator.method_defined?(:respond)
|
6
|
+
load Delegator.instance_method(:__getobj__).source_location.first
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'facets/hash/autonew' # @todo in use?
|
10
|
+
require 'facets/string/snakecase'
|
11
|
+
require 'facets/string/titlecase'
|
12
|
+
|
13
|
+
require 'facets/enumerable/every'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'facets/enumerable/find_yield'
|
2
|
+
|
3
|
+
#
|
4
|
+
#
|
5
|
+
module Enumerable
|
6
|
+
|
7
|
+
# gets all execpt the parameters listed
|
8
|
+
# on a hash this is a key
|
9
|
+
# @see #reject
|
10
|
+
# @param [Array] objs
|
11
|
+
# @return [Enumerable]
|
12
|
+
def except(*objs)
|
13
|
+
reject {|item| objs.include?(item) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# @todo move to Array?
|
17
|
+
# gets all except items listed at specific indexes
|
18
|
+
# @param [Array<Integer>] indexes
|
19
|
+
# @return [Enumerable]
|
20
|
+
def except_at(*indexes)
|
21
|
+
reject.with_index {|item, idx| indexes.include?(idx) }
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :find_result, :find_yield
|
25
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
require 'vigilem/support/transmutable_hash'
|
2
|
+
|
3
|
+
require 'vigilem/support/utils'
|
4
|
+
|
5
|
+
require 'vigilem/support/metadata'
|
6
|
+
|
7
|
+
require 'vigilem/support/key_map_info'
|
8
|
+
|
9
|
+
module Vigilem
|
10
|
+
module Support
|
11
|
+
#
|
12
|
+
#
|
13
|
+
# a TransmutableHash representing a keyboard map
|
14
|
+
# keymap = system, KeyMap ruby object
|
15
|
+
# @see http://kbd-project.org/manpages/man5/keymaps.5.html
|
16
|
+
# @see man dumpkeys -f
|
17
|
+
# @todo to_file
|
18
|
+
# @todo win32
|
19
|
+
# @todo document the expected columns
|
20
|
+
# @todo default for each key default_proc point
|
21
|
+
# @todo move to a parser object
|
22
|
+
# @todo update to fit Win32#LoadKeyboardLayout
|
23
|
+
# @todo eject os specific, and make them loadable/injectable
|
24
|
+
# @todo some methods return Hash instead of KeyMap
|
25
|
+
class KeyMap < TransmutableHash
|
26
|
+
|
27
|
+
# starts with keycode | spaces or '='
|
28
|
+
KEYCODE_SPLIT_REGEX = /(keycode\s+\d{1,3})|\s(=)?\s?/
|
29
|
+
|
30
|
+
include Metadata
|
31
|
+
|
32
|
+
attr_writer :mod_weights, :short_inspect, :info
|
33
|
+
attr_accessor :charset
|
34
|
+
|
35
|
+
#
|
36
|
+
# @param [Hash] init_hash_or_default the initial hash or default value
|
37
|
+
# @param default_value, the default value if not given in init_hash_or_default
|
38
|
+
def initialize(init_hash_or_default={}, default_value=nil)
|
39
|
+
if init_hash_or_default.is_a? Hash
|
40
|
+
super(init_hash_or_default, default_value)
|
41
|
+
else
|
42
|
+
super({}, init_hash_or_default)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# @return [KeyMap]
|
48
|
+
def each(&block)
|
49
|
+
self.class[super(&block)]
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# @return [KeyMap]
|
54
|
+
def select(&block)
|
55
|
+
self.class[super(&block)]
|
56
|
+
end
|
57
|
+
|
58
|
+
# @todo overwrite from info or move to info
|
59
|
+
# @return [Hash]
|
60
|
+
def mod_weights
|
61
|
+
if self.class.mod_weights != @mod_weights
|
62
|
+
@mod_weights = self.class.mod_weights.merge(@mod_weights)
|
63
|
+
else
|
64
|
+
@mod_weights
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# I.E. keymaps 0-2,4-5,8,12
|
69
|
+
# @return [Array<Range || Integer>] the parsed keymap spec
|
70
|
+
def spec
|
71
|
+
@spec ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# @param [String || NilClass] str, defaults to nil
|
76
|
+
# @return [NilClass || KeyMapInfo]
|
77
|
+
def info(str=nil)
|
78
|
+
if str
|
79
|
+
@info = KeyMapInfo.load_string(str)
|
80
|
+
else
|
81
|
+
@info
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# gets the left_side from the right_side_values
|
86
|
+
# @param right_side_values
|
87
|
+
# @return right_side_values or w/e the left_side it is mapped to
|
88
|
+
def left_side(right_side_values=nil)
|
89
|
+
if right_side_values
|
90
|
+
if inverted?
|
91
|
+
self[right_side_values]
|
92
|
+
else
|
93
|
+
invert[right_side_values]
|
94
|
+
end
|
95
|
+
else
|
96
|
+
inverted? ? self.values : self.keys
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# @param [Array<Symbol>] method_names
|
102
|
+
# @return [Proc]
|
103
|
+
def left_side_aliases(*method_names)
|
104
|
+
method_names.each do |method_name|
|
105
|
+
define_singleton_method(method_name) {|right_side_value=nil| self.left_side(right_side_value) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
alias_method :left_side_alias, :left_side_aliases
|
110
|
+
|
111
|
+
# keycode keynumber = keysym keysym keysym...
|
112
|
+
# modsym modsym modsym keycode keynumber = keysym keysym keysym...
|
113
|
+
# get the right_side from the left_side_values
|
114
|
+
# @param left_side_values
|
115
|
+
# @return
|
116
|
+
def right_side(left_side_values=nil)
|
117
|
+
if left_side_values
|
118
|
+
if inverted?
|
119
|
+
invert[left_side_values]
|
120
|
+
else
|
121
|
+
self[left_side_values]
|
122
|
+
end
|
123
|
+
else
|
124
|
+
inverted? ? self.keys : self.values
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# essentially an alias_method
|
129
|
+
# @param [Symbol] method_names
|
130
|
+
# @return [Proc]
|
131
|
+
def right_side_aliases(*method_names)
|
132
|
+
method_names.each do |method_name|
|
133
|
+
define_singleton_method(method_name) {|left_side_values=nil| self.right_side(left_side_values) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
alias_method :right_side_alias, :right_side_aliases
|
138
|
+
|
139
|
+
# @see TransmutableHash#invert
|
140
|
+
# @return [KeyMap]
|
141
|
+
def invert
|
142
|
+
self.class[super]
|
143
|
+
end
|
144
|
+
|
145
|
+
#=== @todo move to parser
|
146
|
+
|
147
|
+
# takes keycode expresssion blocks and adds them to this KeyMap
|
148
|
+
# @param [Array<String>] expresssion_blocks array of ["keycode = vvv", "keycode = vvvv", "alt keycode = vvvv"]
|
149
|
+
# @return [nil]
|
150
|
+
def parse_keymap_expressions(*expresssion_blocks)
|
151
|
+
expresssion_blocks.each {|expr_block| parse_expression_block(expr_block) }.compact
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
# @todo cleanup
|
156
|
+
# takes a keycode expresssion block and adds them to this KeyMap
|
157
|
+
# dumpkeys -f || cached.kmap.gz
|
158
|
+
# @param [String] expr_blk a String block from keycode to next keycode
|
159
|
+
# @return [NilCLass]
|
160
|
+
def parse_expression_block(expr_blk)
|
161
|
+
expr_blk.split("\n").map do |line|
|
162
|
+
# skip comments
|
163
|
+
unless line.split('#').first.to_s.empty?
|
164
|
+
if line =~ /^keymaps/
|
165
|
+
parse_keymap_spec(line)
|
166
|
+
else
|
167
|
+
line_ary = line.to_s.split(KEYCODE_SPLIT_REGEX).reject(&:empty?)
|
168
|
+
if eq_index = line_ary.index('=')
|
169
|
+
# ["keycode 2", "=", "U+0031", "U+0021", "U+0031", "U+0031", "VoidSymbol",... ]
|
170
|
+
_parse_sides(*Utils.split_on(line_ary, eq_index))
|
171
|
+
end #if eq_index
|
172
|
+
end #if line
|
173
|
+
end #unless
|
174
|
+
end #expr
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# @param [String] left_side
|
180
|
+
# @param [String] right_side
|
181
|
+
# @return
|
182
|
+
def _parse_sides(left_side, right_side)
|
183
|
+
#["alt", "keycode 2"]
|
184
|
+
keycode = left_side.pop.gsub(/\s+/, '')
|
185
|
+
if left_side.size > 0
|
186
|
+
self[Utils.unwrap_ary([*left_side, keycode].flatten)] = Utils.unwrap_ary(right_side)
|
187
|
+
else
|
188
|
+
build_hash_from_full_table_line(right_side, keycode)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private :_parse_sides
|
193
|
+
|
194
|
+
# takes a list of keysyms configures thier modifiers and adds them to this keymap
|
195
|
+
# @see ::build_hash_from_full_table_line
|
196
|
+
# @param [Array<String>] right_side an array of keysyms, in a full table this is the right side
|
197
|
+
# @param [String] keycode the keycode in that is being mapped
|
198
|
+
# @return [TransmutableHash] the results of
|
199
|
+
def build_hash_from_full_table_line(right_side, keycode)
|
200
|
+
raise 'keymap specification missing' if spec.empty?
|
201
|
+
merge!(self.class.build_hash_from_full_table_line(right_side, keycode, spec))
|
202
|
+
end
|
203
|
+
|
204
|
+
# converts keymap spec to ranged array
|
205
|
+
# @param [String] expr 'keymaps 0-127' or 'keymaps ?-?,?'
|
206
|
+
# @return [Array<Range|| Integer>] the parsed keymap spec that
|
207
|
+
def parse_keymap_spec(expr)
|
208
|
+
@spec = expr.split(/\s|,/)[1..-1].map {|str| str =~ /\-/ ? Range.new(*str.split('-').map(&:to_i)) : str.to_i }
|
209
|
+
end
|
210
|
+
|
211
|
+
# tests whether or not num is in the spec
|
212
|
+
# @param [Numeric] num is this number within 'keymaps 0-127' or 'keymaps ?-?,?'
|
213
|
+
# @return [TrueClass || FalseClass]
|
214
|
+
def in_spec?(num)
|
215
|
+
Utils.in_ranged_array?(spec, num)
|
216
|
+
end
|
217
|
+
|
218
|
+
class << self
|
219
|
+
|
220
|
+
# weights of the modifiers set by `keymaps`
|
221
|
+
def mod_weights
|
222
|
+
@mod_weights ||= { 'shift' => 1,
|
223
|
+
'altgr' => 2, 'control' => 4,
|
224
|
+
'alt' => 8, 'shiftl' => 16,
|
225
|
+
'shiftr' => 32, 'ctrll' => 64,
|
226
|
+
'ctrlr' => 128, 'capsshift' => 256 }
|
227
|
+
end
|
228
|
+
|
229
|
+
alias_method :columns, :mod_weights
|
230
|
+
|
231
|
+
# converts a a keymap file to a ruby object
|
232
|
+
# @todo change to stream reading
|
233
|
+
# @param [String] path the path of the File
|
234
|
+
# @return [KeyMap] the String as a Ruby object
|
235
|
+
def load_file(path)
|
236
|
+
load_string(File.binread(path))
|
237
|
+
end
|
238
|
+
|
239
|
+
# loads a keymap string and converts it to a Ruby object
|
240
|
+
# @param [String] str string to convert to KeyMap
|
241
|
+
# @return [KeyMap] the String as a Ruby object
|
242
|
+
def load_string(str, str_info=nil)
|
243
|
+
exprs = str.split(/^keycode/)
|
244
|
+
inst = self.new()
|
245
|
+
inst.parse_keymap_expressions(exprs[0], *exprs[1..-1].map {|exp| "keycode#{exp}" })
|
246
|
+
inst.info(KeyMapInfo.load_string(str_info)) if str_info
|
247
|
+
inst
|
248
|
+
end
|
249
|
+
|
250
|
+
# takes a list of keysyms and converts it to a TransmutableHash
|
251
|
+
# @todo name change?
|
252
|
+
# @param [Array<String>] right_side an array of keysyms, in a full table this is the right side
|
253
|
+
# @param [String] keycode the keycode in that is being mapped
|
254
|
+
# @param [Array<Numeric||Range>] columns
|
255
|
+
# @return [TransmutableHash] of the keycode combinations mapped to keysyms
|
256
|
+
def build_hash_from_full_table_line(right_side, keycode, columns=[0..127])
|
257
|
+
raise 'cannot build expresssions for full table with no columns' if columns.empty?
|
258
|
+
sze = right_side.size
|
259
|
+
right_side.each_with_object(TransmutableHash.new).with_index do |(keysyms, hsh), idx|
|
260
|
+
if Utils.in_ranged_array?(columns, idx)
|
261
|
+
# not sure why a 'b' is in the mapping when that's not an available range
|
262
|
+
(flat = [keysyms].flatten).last.gsub!(/^\+?0x0b/, '0x00')
|
263
|
+
hsh[Utils.unwrap_ary([mod_combin(idx), keycode].flatten)] = Utils.unwrap_ary(flat)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# finds the Combination (without repeats) of modifiers that fit into n
|
269
|
+
# @param [Integer] n the number to fit the modifiers into
|
270
|
+
# @return [Array<String>]
|
271
|
+
def modifier_combination(n)
|
272
|
+
mod_weights.map {|name, weight| name if weight & n != 0 }.compact
|
273
|
+
end
|
274
|
+
|
275
|
+
alias_method :mod_combin, :modifier_combination
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
#
|
280
|
+
# @return [TrueClass || FalseClass]
|
281
|
+
def short_inspect?
|
282
|
+
!!@short_inspect
|
283
|
+
end
|
284
|
+
|
285
|
+
#
|
286
|
+
# @return [TrueClass || FalseClass]
|
287
|
+
def short_inspect!
|
288
|
+
self.short_inpsect = true
|
289
|
+
end
|
290
|
+
|
291
|
+
#
|
292
|
+
# @return [String]
|
293
|
+
def short_inspect
|
294
|
+
num = (not @short_inspect.is_a?(Integer)) ? 30 : @short_inspect
|
295
|
+
"#{Hash[self.to_a.sample(num)].inspect.chomp('}')}...snip...@metadata.keys=#{metadata.keys}}"
|
296
|
+
end
|
297
|
+
|
298
|
+
#
|
299
|
+
# @return [String]
|
300
|
+
def inspect
|
301
|
+
if short_inspect?
|
302
|
+
short_inspect
|
303
|
+
else
|
304
|
+
super
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
#
|
309
|
+
# @param [Symbol] method_name
|
310
|
+
# @param [Array] args
|
311
|
+
# @param [Proc] block
|
312
|
+
# @return
|
313
|
+
def method_missing(method_name, *args, &block)
|
314
|
+
if self.keys.include? method_name
|
315
|
+
self[method_name]
|
316
|
+
else
|
317
|
+
super(method_name, *args, &block)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|