rbindkeys 0.0.1

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.
@@ -0,0 +1,25 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require "revdev"
4
+
5
+ module Rbindkeys
6
+
7
+ class Device < Revdev::EventDevice
8
+
9
+ def release_all_key
10
+ ie = Revdev::InputEvent.new nil, 0, 0, 0
11
+ Revdev.constants.select{|c| c.match(/^(?:KEY|BTN)/)}.each do |c|
12
+ ie.type = Revdev::EV_KEY
13
+ ie.code = Revdev.const_get c
14
+ ie.value = 0
15
+ write_input_event ie
16
+
17
+ ie.type = Revdev::EV_SYN
18
+ ie.code = 0
19
+ write_input_event ie
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,78 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require 'revdev'
4
+
5
+ module Rbindkeys
6
+
7
+ # device operations like send key event, send LED event, etc.
8
+ class DeviceOperator
9
+
10
+ LOG = LogUtils.get_logger name
11
+
12
+ # real event device
13
+ attr_reader :device
14
+
15
+ # uinput device
16
+ attr_reader :virtural
17
+
18
+ # key code set which was send press event but is not send release event
19
+ attr_reader :pressed_key_set
20
+
21
+ def initialize dev, vdev
22
+ @device = dev
23
+ @virtual = vdev
24
+ @pressed_key_set = []
25
+ end
26
+
27
+ def release_key code
28
+ send_key code, 0
29
+ end
30
+ def press_key code
31
+ send_key code, 1
32
+ end
33
+ def pressing_key code
34
+ send_key code, 2
35
+ end
36
+
37
+ def send_key code, state
38
+ send_event Revdev::EV_KEY, code, state
39
+ end
40
+
41
+ def send_event *args
42
+ event =
43
+ case args.length
44
+ when 1 then args[0]
45
+ when 3 then
46
+ @cache_input_event ||= Revdev::InputEvent.new nil, 0, 0, 0
47
+ @cache_input_event.type = args[0]
48
+ @cache_input_event.code = args[1]
49
+ @cache_input_event.value = args[2]
50
+ @cache_input_event
51
+ else raise ArgumentError, "expect a InputEvent or 3 Fixnums (type, code, state)"
52
+ end
53
+ dev = case event.type
54
+ when Revdev::EV_KEY then @virtual
55
+ when Revdev::EV_LED then @device
56
+ else @virtual
57
+ end
58
+
59
+ update_pressed_key_set event
60
+ dev.write_input_event event
61
+ LOG.info "write\t#{KeyEventHandler.get_state_by_value event} "+
62
+ "#{event.hr_code}(#{event.code})" if LOG.info?
63
+ end
64
+
65
+ def update_pressed_key_set event
66
+ if event.type == Revdev::EV_KEY
67
+ case event.value
68
+ when 0 then @pressed_key_set.delete event.code
69
+ when 1 then @pressed_key_set << event.code
70
+ when 2 then # do nothing
71
+ else raise UnknownKeyValue, "expect 0, 1 or 2"
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,26 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ module Rbindkeys
4
+
5
+ # last resolvers which return @val as the resolve result
6
+ class FixResolver < BindResolver
7
+ private_class_method :new
8
+ @@pool = {}
9
+
10
+ def self.instance val
11
+ @@pool[val] or (@@pool[val] = new val)
12
+ end
13
+
14
+ def initialize val
15
+ @val = val
16
+ end
17
+
18
+ def bind input, output
19
+ raise RuntimeError, 'cannot bind any input/output'
20
+ end
21
+
22
+ def resolve code, code_set
23
+ @val
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ module Rbindkeys
4
+ class KeyBind
5
+
6
+ attr_reader :input
7
+
8
+ attr_reader :output
9
+
10
+ # when a signal of any input release event was accepted,
11
+ # if @inputs_recovery is true, outputs are released and other inputs are pressed,
12
+ # if @inputs_recovery is false or nil, outputs are released.
13
+ attr_reader :input_recovery
14
+
15
+ def initialize input, output, opt = {}
16
+ @input = input
17
+ @output = output
18
+ @input_recovery = opt[:input_recovery]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,243 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require 'rbindkeys/key_event_handler/configurer'
4
+ require 'active_window_x'
5
+
6
+ module Rbindkeys
7
+
8
+ # retrive key binds with key event
9
+ class KeyEventHandler
10
+ include Revdev
11
+
12
+ LOG = LogUtils.get_logger name
13
+
14
+ # device operator
15
+ attr_reader :operator
16
+
17
+ # defaulut key bind set which retrive key binds with a key event
18
+ attr_reader :default_bind_resolver
19
+
20
+ # current key bind set which retrive key binds with a key event
21
+ attr_reader :bind_resolver
22
+
23
+ # a hash (key:WindowMatcher, val:BindResolver) to switch BindResolver
24
+ # by the title or app_name of active window
25
+ attr_reader :window_bind_resolver_map
26
+
27
+ # proccessed resolver before bind_resolver
28
+ attr_reader :pre_bind_resolver
29
+
30
+ # code set of pressed key on the event device
31
+ attr_reader :pressed_key_set
32
+
33
+ # pressed key binds
34
+ attr_reader :active_bind_set
35
+
36
+ def initialize device_operator
37
+ @operator = device_operator
38
+ @default_bind_resolver = BindResolver.new
39
+ @window_bind_resolver = nil
40
+ @bind_resolver = @default_bind_resolver
41
+ @window_bind_resolver_map = []
42
+ @pre_bind_resolver = {}
43
+ @pressed_key_set = []
44
+ @active_bind_set = []
45
+ end
46
+
47
+ def load_config file
48
+ code = File.read file
49
+ instance_eval code, file
50
+ end
51
+
52
+ def handle event
53
+ if LOG.info?
54
+ LOG.info "" unless LOG.debug?
55
+ LOG.info "read\t#{KeyEventHandler.get_state_by_value event} "+
56
+ "#{event.hr_code}(#{event.code})"
57
+ end
58
+
59
+ # handle pre_key_bind_set
60
+ event.code = (@pre_bind_resolver[event.code] or event.code)
61
+
62
+ # swich to handle event with event.value
63
+ result =
64
+ case event.value
65
+ when 0 then handle_release_event event
66
+ when 1 then handle_press_event event
67
+ when 2 then handle_pressing_event event
68
+ else raise UnknownKeyValue, "expect 0, 1 or 2 as event.value(#{event.value})"
69
+ end
70
+
71
+ case result
72
+ when :through
73
+ fill_gap_pressed_state event if event.value == 1
74
+ @operator.send_event event
75
+ when :ignore
76
+ # ignore the original event
77
+ end
78
+
79
+ handle_pressed_keys event
80
+
81
+ LOG.info "pressed_keys real:#{@pressed_key_set.inspect} "+
82
+ "virtual:#{@operator.pressed_key_set.inspect}" if LOG.info?
83
+ end
84
+
85
+ def handle_release_event event
86
+ release_bind_set = []
87
+ @active_bind_set.reject! do |key_bind|
88
+ if key_bind.input.include? event.code
89
+ release_bind_set << key_bind
90
+ true
91
+ else
92
+ false
93
+ end
94
+ end
95
+
96
+ if release_bind_set.empty?
97
+ :through
98
+ else
99
+ release_bind_set.each do |kb|
100
+ kb.output.each {|c|@operator.release_key c}
101
+ if kb.input_recovery
102
+ kb.input.clone.delete_if {|c|c==event.code}.each {|c|@operator.press_key c}
103
+ end
104
+ end
105
+ :ignore
106
+ end
107
+ end
108
+
109
+ def set_bind_resolver resolver
110
+ old_resolver = @bind_resolver if LOG.info?
111
+ @bind_resolver = resolver
112
+ LOG.info "switch bind_resolver: #{old_resolver} => "+
113
+ "#{@bind_resolver}" if LOG.info?
114
+ @bind_resolver
115
+ end
116
+
117
+ def handle_press_event event
118
+ r = @bind_resolver.resolve event.code, @pressed_key_set
119
+
120
+ LOG.debug "resolve result: #{r.inspect}" if LOG.debug?
121
+
122
+ if r.kind_of? KeyBind
123
+
124
+ if @bind_resolver.two_stroke?
125
+ set_bind_resolver (@window_bind_resolver or @default_bind_resolver)
126
+ end
127
+
128
+ if r.output.kind_of? Array
129
+ r.input.clone.delete_if{|c|c==event.code}.each {|c| @operator.release_key c}
130
+ r.output.each {|c| @operator.press_key c}
131
+ @active_bind_set << r
132
+ :ignore
133
+ elsif r.output.kind_of? BindResolver
134
+ set_bind_resolver r.output
135
+ :ignore
136
+ elsif r.output.kind_of? Proc
137
+ r.output.call event, @operator
138
+ elsif r.output.kind_of? Symbol
139
+ r
140
+ end
141
+ else
142
+ r
143
+ end
144
+ end
145
+
146
+ def handle_pressing_event event
147
+ if @active_bind_set.empty?
148
+ :through
149
+ else
150
+ @active_bind_set.each {|kb| kb.output.each {|c| @operator.pressing_key c}}
151
+ :ignore
152
+ end
153
+ end
154
+
155
+ def fill_gap_pressed_state event
156
+ return if @operator.pressed_key_set == @pressed_key_set
157
+ sub = @pressed_key_set - @operator.pressed_key_set
158
+ if event.value == 0
159
+ sub.delete event.code
160
+ end
161
+ sub.each {|code| @operator.press_key code}
162
+ end
163
+
164
+ def handle_pressed_keys event
165
+ if event.value == 1
166
+ @pressed_key_set << event.code
167
+ @pressed_key_set.sort! # TODO do not sort. implement an bubble insertion
168
+ elsif event.value == 0
169
+ if @pressed_key_set.delete(event.code).nil?
170
+ LOG.warn "#{event.code} does not exists on @pressed_keys" if LOG.warn?
171
+ end
172
+ end
173
+ end
174
+
175
+ def active_window_changed window
176
+ if not window.nil?
177
+ title = window.title
178
+ app_name = window.app_name
179
+ if LOG.info?
180
+ LOG.info "" unless LOG.debug?
181
+ LOG.info "change active_window: :class => \"#{app_name}\", :title => \"#{title}\""
182
+ end
183
+
184
+ @window_bind_resolver_map.each do |matcher, bind_resolver|
185
+ if matcher.match? app_name, title
186
+ if LOG.info?
187
+ LOG.info "=> matcher #{matcher.app_name.inspect}, #{matcher.title.inspect}"
188
+ LOG.info " bind_resolver #{bind_resolver.inspect}"
189
+ end
190
+ set_bind_resolver bind_resolver
191
+ @window_bind_resolver = bind_resolver
192
+ return
193
+ end
194
+ end
195
+ else
196
+ if LOG.info?
197
+ LOG.info "" unless LOG.debug?
198
+ LOG.info "change active_window: nil"
199
+ end
200
+ end
201
+
202
+ LOG.info "=> no matcher" if LOG.info?
203
+ set_bind_resolver @default_bind_resolver
204
+ @window_bind_resolver = nil
205
+ return
206
+ end
207
+
208
+ class << self
209
+ # parse and normalize to Fixnum/Array
210
+ def parse_code code, depth = 0
211
+ if code.kind_of? Symbol
212
+ code = parse_symbol code
213
+ elsif code.kind_of? Array
214
+ raise ArgumentError, "expect Array is the depth less than 1" if depth >= 1
215
+ code.map!{|c| parse_code c, (depth+1)}
216
+ elsif code.kind_of? Fixnum and depth == 0
217
+ code = [code]
218
+ elsif not code.kind_of? Fixnum
219
+ raise ArgumentError, "expect Symbol / Fixnum / Array"
220
+ end
221
+ code
222
+ end
223
+
224
+ # TODO convert :j -> KEY_J, :ctrl -> KEY_LEFTCTRL
225
+ def parse_symbol sym
226
+ if not sym.kind_of? Symbol
227
+ raise ArgumentError, "expect Symbol / Fixnum / Array"
228
+ end
229
+ Revdev.const_get sym
230
+ end
231
+
232
+ def get_state_by_value ev
233
+ case ev.value
234
+ when 0 then 'released '
235
+ when 1 then 'pressed '
236
+ when 2 then 'pressing '
237
+ end
238
+ end
239
+ end
240
+
241
+ end
242
+
243
+ end
@@ -0,0 +1,142 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+ #
3
+ # a part of KeyEventHandler (see lib/rbindkeys/key_event_handler.rb)
4
+ #
5
+
6
+ module Rbindkeys
7
+ class KeyEventHandler
8
+
9
+ # pre-prosessed key codes replacement for all inputs
10
+ def pre_bind_key input, output
11
+ if not (input.kind_of? Fixnum and output.kind_of? Fixnum)
12
+ raise ArgumentError, 'expect Fixnum for input and output'
13
+ end
14
+
15
+ if @pre_bind_resolver.has_key? input
16
+ raise DuplicateNodeError, "1st arg (#{input}) was already entried"
17
+ end
18
+
19
+ LOG.info "pre_bind_key #{input.inspect},\t#{output.inspect}" if LOG.info?
20
+
21
+ @pre_bind_resolver[input] = output
22
+ [input, output]
23
+ end
24
+
25
+ # define a new key binding
26
+ def bind_key input, output=nil, resolver=@bind_resolver, &block
27
+ if input.kind_of?(Array) or input.kind_of?(Fixnum)
28
+ input = KeyEventHandler.parse_code input
29
+ else
30
+ raise ArgumentError, '1st arg expect Array / Fixnum'
31
+ end
32
+
33
+ if block_given?
34
+ output = block
35
+ elsif output.nil?
36
+ raise ArgumentError, 'expect 1 arg with a block / 2 args'
37
+ elsif output.kind_of? BindResolver
38
+ elsif output.kind_of?(Array) or output.kind_of?(Fixnum)
39
+ output = KeyEventHandler::parse_code output
40
+ elsif output == :through or output == :ignore
41
+ else
42
+ raise ArgumentError, '2nd arg expect Array / Fixnum / BindResolver / '+
43
+ 'Symbol(:through/:ignore)'
44
+ end
45
+
46
+ LOG.info "bind_key #{input.inspect},\t#{output.inspect}\t#{resolver}" if LOG.info?
47
+
48
+ resolver.bind input, output
49
+ end
50
+
51
+ # setting for 2stroke key binding
52
+ # _input_ :: prefix key. (e.g. [KEY_LEFTCTRL, KEY_X] (C-x)
53
+ # _resolver_ :: upper bind_resolver for fall throught
54
+ # _block_ :: to define sub-binds on this prefix key bind
55
+ def bind_prefix_key input, resolver=@bind_resolver, &block
56
+ if not block_given?
57
+ raise ArgumentError, "expect a block"
58
+ end
59
+
60
+ input = KeyEventHandler::parse_code input
61
+ LOG.info "bind_prefix_key #{input.inspect}\t#{resolver}" if LOG.info?
62
+ tmp = input.clone
63
+ tail_input = tmp.pop
64
+
65
+ binded_resolver = resolver.just_resolve tail_input, tmp
66
+ if binded_resolver == nil
67
+ binded_resolver = BindResolver.new :ignore, true
68
+ resolver.bind input, binded_resolver
69
+ elsif not binded_resolver.kind_of? BindResolver
70
+ raise DuplicateNodeError, "the codes (#{input.inspect}) was already binded"
71
+ end
72
+
73
+ @bind_resolver = binded_resolver
74
+ yield
75
+ @bind_resolver = resolver
76
+ binded_resolver
77
+ end
78
+
79
+ def new_bind_resolver upper_resolver=@bind_resolver, &block
80
+ if not block_given?
81
+ raise ArgumentError, "expect a block"
82
+ end
83
+
84
+ new_resolver = BindResolver.new upper_resolver
85
+
86
+ old_resolver = @bind_resolver
87
+ @bind_resolver = new_resolver
88
+ yield
89
+ @bind_resolver = old_resolver
90
+
91
+ new_resolver
92
+ end
93
+
94
+ # switch bind_resolver if the active window match _arg_
95
+ #
96
+ # _upper\_resolver_ :: a upper bind_resolver, :through, :ignore
97
+ # _arg_ :: a hash or a regexp
98
+ #
99
+ # ==arg
100
+ # if a hash, which have entries :title => Regexp and/or :class => Regexp,
101
+ # was given, this bind is active when the window title match with
102
+ # :title's Regexp AND the window class match with :class's Regexp.
103
+ # if a regexp was given, this bind is active when the window title match
104
+ # with the given regexp
105
+ def window upper_resolver, arg
106
+ if upper_resolver.nil?
107
+ upper_resolver = @bind_resolver
108
+ elsif upper_resolver.kind_of? Symbol
109
+ upper_resolver = FixResolver.instance upper_resolver
110
+ elsif not upper_resolver.kind_of? BindResolver
111
+ raise ArgumentError, "1nd argument is expected to be a BindResolver or"+
112
+ " a Symbol : #{ upper_resolver.to_s}"
113
+ end
114
+
115
+ if arg.kind_of? Regexp
116
+ arg = { :title => arg }
117
+ elsif arg.kind_of? Hash
118
+ arg.each do |k, v|
119
+ if not (k.kind_of?(Symbol) and v.kind_of?(Regexp))
120
+ raise ArgumentError, 'the 2nd argument Hash must only have'+
121
+ " Symbol keys and Regexp values : #{arg.inspect}"
122
+ end
123
+ end
124
+ else
125
+ raise ArgumentError, "2nd argument is expected to be a Hash or a Regexp : #{arg}"
126
+ end
127
+
128
+ resolver = BindResolver.new(upper_resolver)
129
+ @window_bind_resolver_map.push [WindowMatcher.new(arg), resolver]
130
+
131
+ if block_given?
132
+ old_resolver = @bind_resolver
133
+ @bind_resolver = resolver
134
+ yield
135
+ @bind_resolver = old_resolver
136
+ end
137
+
138
+ resolver
139
+ end
140
+
141
+ end
142
+ end