rbindkeys 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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