Class: Rbindkeys::KeyEventHandler

Inherits:
Object
  • Object
show all
Includes:
Revdev
Defined in:
lib/rbindkeys/key_event_handler.rb,
lib/rbindkeys.rb,
lib/rbindkeys/key_event_handler/configurer.rb

Overview

retrive key binds with key event

Constant Summary collapse

LOG =
LogUtils.get_logger name

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(device_operator) ⇒ KeyEventHandler

Returns a new instance of KeyEventHandler.



36
37
38
39
40
41
42
43
44
45
# File 'lib/rbindkeys/key_event_handler.rb', line 36

def initialize device_operator
  @operator = device_operator
  @default_bind_resolver = BindResolver.new
  @window_bind_resolver = nil
  @bind_resolver = @default_bind_resolver
  @window_bind_resolver_map = []
  @pre_bind_resolver = {}
  @pressed_key_set = []
  @active_bind_set = []
end

Instance Attribute Details

#active_bind_setObject (readonly)

pressed key binds



34
35
36
# File 'lib/rbindkeys/key_event_handler.rb', line 34

def active_bind_set
  @active_bind_set
end

#bind_resolverObject (readonly)

current key bind set which retrive key binds with a key event



21
22
23
# File 'lib/rbindkeys/key_event_handler.rb', line 21

def bind_resolver
  @bind_resolver
end

#default_bind_resolverObject (readonly)

defaulut key bind set which retrive key binds with a key event



18
19
20
# File 'lib/rbindkeys/key_event_handler.rb', line 18

def default_bind_resolver
  @default_bind_resolver
end

#operatorObject (readonly)

device operator



15
16
17
# File 'lib/rbindkeys/key_event_handler.rb', line 15

def operator
  @operator
end

#pre_bind_resolverObject (readonly)

proccessed resolver before bind_resolver



28
29
30
# File 'lib/rbindkeys/key_event_handler.rb', line 28

def pre_bind_resolver
  @pre_bind_resolver
end

#pressed_key_setObject (readonly)

code set of pressed key on the event device



31
32
33
# File 'lib/rbindkeys/key_event_handler.rb', line 31

def pressed_key_set
  @pressed_key_set
end

#window_bind_resolver_mapObject (readonly)

a hash (key:WindowMatcher, val:BindResolver) to switch BindResolver by the title or app_name of active window



25
26
27
# File 'lib/rbindkeys/key_event_handler.rb', line 25

def window_bind_resolver_map
  @window_bind_resolver_map
end

Class Method Details

.get_state_by_value(ev) ⇒ Object



232
233
234
235
236
237
238
# File 'lib/rbindkeys/key_event_handler.rb', line 232

def get_state_by_value ev
  case ev.value
  when 0 then 'released '
  when 1 then 'pressed  '
  when 2 then 'pressing '
  end
end

.parse_code(code, depth = 0) ⇒ Object

parse and normalize to Fixnum/Array



210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/rbindkeys/key_event_handler.rb', line 210

def parse_code code, depth = 0
  if code.kind_of? Symbol
    code = parse_symbol code
  elsif code.kind_of? Array
    raise ArgumentError, "expect Array is the depth less than 1" if depth >= 1
    code.map!{|c| parse_code c, (depth+1)}
  elsif code.kind_of? Fixnum and depth == 0
    code = [code]
  elsif not code.kind_of? Fixnum
    raise ArgumentError, "expect Symbol / Fixnum / Array"
  end
  code
end

.parse_symbol(sym) ⇒ Object

TODO convert :j -> KEY_J, :ctrl -> KEY_LEFTCTRL



225
226
227
228
229
230
# File 'lib/rbindkeys/key_event_handler.rb', line 225

def parse_symbol sym
  if not sym.kind_of? Symbol
    raise ArgumentError, "expect Symbol / Fixnum / Array"
  end
  Revdev.const_get sym
end

Instance Method Details

#active_window_changed(window) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/rbindkeys/key_event_handler.rb', line 175

def active_window_changed window
  if not window.nil?
    title = window.title
    app_name = window.app_name
    if LOG.info?
      LOG.info "" unless LOG.debug?
      LOG.info "change active_window: :class => \"#{app_name}\", :title => \"#{title}\""
    end

    @window_bind_resolver_map.each do |matcher, bind_resolver|
      if matcher.match? app_name, title
        if LOG.info?
          LOG.info "=> matcher #{matcher.app_name.inspect}, #{matcher.title.inspect}"
          LOG.info "   bind_resolver #{bind_resolver.inspect}"
        end
        set_bind_resolver bind_resolver
        @window_bind_resolver = bind_resolver
        return
      end
    end
  else
    if LOG.info?
      LOG.info "" unless LOG.debug?
      LOG.info "change active_window: nil"
    end
  end

  LOG.info "=> no matcher" if LOG.info?
  set_bind_resolver @default_bind_resolver
  @window_bind_resolver = nil
  return
end

#bind_key(input, output = nil, resolver = @bind_resolver, &block) ⇒ Object

define a new key binding



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rbindkeys/key_event_handler/configurer.rb', line 26

def bind_key input, output=nil, resolver=@bind_resolver, &block
  if input.kind_of?(Array) or input.kind_of?(Fixnum)
    input = KeyEventHandler.parse_code input
  else
    raise ArgumentError, '1st arg expect Array / Fixnum'
  end

  if block_given?
    output = block
  elsif output.nil?
    raise ArgumentError, 'expect 1 arg with a block / 2 args'
  elsif output.kind_of? BindResolver
  elsif output.kind_of?(Array) or output.kind_of?(Fixnum)
    output = KeyEventHandler::parse_code output
  elsif output == :through or output == :ignore
  else
    raise ArgumentError, '2nd arg expect Array / Fixnum / BindResolver / '+
      'Symbol(:through/:ignore)'
  end

  LOG.info "bind_key #{input.inspect},\t#{output.inspect}\t#{resolver}" if LOG.info?

  resolver.bind input, output
end

#bind_prefix_key(input, resolver = @bind_resolver, &block) ⇒ Object

setting for 2stroke key binding

input

prefix key. (e.g. [KEY_LEFTCTRL, KEY_X] (C-x)

resolver

upper bind_resolver for fall throught

block

to define sub-binds on this prefix key bind



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rbindkeys/key_event_handler/configurer.rb', line 55

def bind_prefix_key input, resolver=@bind_resolver, &block
  if not block_given?
    raise ArgumentError, "expect a block"
  end

  input = KeyEventHandler::parse_code input
  LOG.info "bind_prefix_key #{input.inspect}\t#{resolver}" if LOG.info?
  tmp = input.clone
  tail_input = tmp.pop

  binded_resolver = resolver.just_resolve tail_input, tmp
  if binded_resolver == nil
    binded_resolver = BindResolver.new :ignore, true
    resolver.bind input, binded_resolver
  elsif not binded_resolver.kind_of? BindResolver
    raise DuplicateNodeError, "the codes (#{input.inspect}) was already binded"
  end

  @bind_resolver = binded_resolver
  yield
  @bind_resolver = resolver
  binded_resolver
end

#fill_gap_pressed_state(event) ⇒ Object



155
156
157
158
159
160
161
162
# File 'lib/rbindkeys/key_event_handler.rb', line 155

def fill_gap_pressed_state event
  return if @operator.pressed_key_set == @pressed_key_set
  sub = @pressed_key_set - @operator.pressed_key_set
  if event.value == 0
    sub.delete event.code
  end
  sub.each {|code| @operator.press_key code}
end

#handle(event) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rbindkeys/key_event_handler.rb', line 52

def handle event
  if LOG.info?
    LOG.info "" unless LOG.debug?
    LOG.info "read\t#{KeyEventHandler.get_state_by_value event} "+
      "#{event.hr_code}(#{event.code})"
  end

  # handle pre_key_bind_set
  event.code = (@pre_bind_resolver[event.code] or event.code)

  # swich to handle event with event.value
  result =
    case event.value
    when 0 then handle_release_event event
    when 1 then handle_press_event event
    when 2 then handle_pressing_event event
    else raise UnknownKeyValue, "expect 0, 1 or 2 as event.value(#{event.value})"
    end

  case result
  when :through
    fill_gap_pressed_state event if event.value == 1
    @operator.send_event event
  when :ignore
    # ignore the original event
  end

  handle_pressed_keys event

  LOG.info "pressed_keys real:#{@pressed_key_set.inspect} "+
    "virtual:#{@operator.pressed_key_set.inspect}" if LOG.info?
end

#handle_press_event(event) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/rbindkeys/key_event_handler.rb', line 117

def handle_press_event event
  r = @bind_resolver.resolve event.code, @pressed_key_set

  LOG.debug "resolve result: #{r.inspect}" if LOG.debug?

  if r.kind_of? KeyBind

    if @bind_resolver.two_stroke?
      set_bind_resolver (@window_bind_resolver or @default_bind_resolver)
    end

    if r.output.kind_of? Array
      r.input.clone.delete_if{|c|c==event.code}.each {|c| @operator.release_key c}
      r.output.each {|c| @operator.press_key c}
      @active_bind_set << r
      :ignore
    elsif r.output.kind_of? BindResolver
      set_bind_resolver r.output
      :ignore
    elsif r.output.kind_of? Proc
      r.output.call event, @operator
    elsif r.output.kind_of? Symbol
      r
    end
  else
    r
  end
end

#handle_pressed_keys(event) ⇒ Object



164
165
166
167
168
169
170
171
172
173
# File 'lib/rbindkeys/key_event_handler.rb', line 164

def handle_pressed_keys event
  if event.value == 1
    @pressed_key_set << event.code
    @pressed_key_set.sort! # TODO do not sort. implement an bubble insertion
  elsif event.value == 0
    if @pressed_key_set.delete(event.code).nil?
      LOG.warn "#{event.code} does not exists on @pressed_keys" if LOG.warn?
    end
  end
end

#handle_pressing_event(event) ⇒ Object



146
147
148
149
150
151
152
153
# File 'lib/rbindkeys/key_event_handler.rb', line 146

def handle_pressing_event event
  if @active_bind_set.empty?
    :through
  else
    @active_bind_set.each {|kb| kb.output.each {|c| @operator.pressing_key c}}
    :ignore
  end
end

#handle_release_event(event) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/rbindkeys/key_event_handler.rb', line 85

def handle_release_event event
  release_bind_set = []
  @active_bind_set.reject! do |key_bind|
    if key_bind.input.include? event.code
      release_bind_set << key_bind
      true
    else
      false
    end
  end

  if release_bind_set.empty?
    :through
  else
    release_bind_set.each do |kb|
      kb.output.each {|c|@operator.release_key c}
      if kb.input_recovery
        kb.input.clone.delete_if {|c|c==event.code}.each {|c|@operator.press_key c}
      end
    end
    :ignore
  end
end

#load_config(file) ⇒ Object



47
48
49
50
# File 'lib/rbindkeys/key_event_handler.rb', line 47

def load_config file
  code = File.read file
  instance_eval code, file
end

#new_bind_resolver(upper_resolver = @bind_resolver, &block) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rbindkeys/key_event_handler/configurer.rb', line 79

def new_bind_resolver upper_resolver=@bind_resolver, &block
  if not block_given?
    raise ArgumentError, "expect a block"
  end

  new_resolver = BindResolver.new upper_resolver

  old_resolver = @bind_resolver
  @bind_resolver = new_resolver
  yield
  @bind_resolver = old_resolver

  new_resolver
end

#pre_bind_key(input, output) ⇒ Object

pre-prosessed key codes replacement for all inputs



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/rbindkeys/key_event_handler/configurer.rb', line 10

def pre_bind_key input, output
  if not (input.kind_of? Fixnum and output.kind_of? Fixnum)
    raise ArgumentError, 'expect Fixnum for input and output'
  end

  if @pre_bind_resolver.has_key? input
    raise DuplicateNodeError, "1st arg (#{input}) was already entried"
  end

  LOG.info "pre_bind_key #{input.inspect},\t#{output.inspect}" if LOG.info?

  @pre_bind_resolver[input] = output
  [input, output]
end

#set_bind_resolver(resolver) ⇒ Object



109
110
111
112
113
114
115
# File 'lib/rbindkeys/key_event_handler.rb', line 109

def set_bind_resolver resolver
  old_resolver = @bind_resolver if LOG.info?
  @bind_resolver = resolver
  LOG.info "switch bind_resolver: #{old_resolver} => "+
    "#{@bind_resolver}" if LOG.info?
  @bind_resolver
end

#window(upper_resolver, arg) ⇒ Object

switch bind_resolver if the active window match arg

upperresolver_

a upper bind_resolver, :through, :ignore

arg

a hash or a regexp

arg

if a hash, which have entries :title => Regexp and/or :class => Regexp, was given, this bind is active when the window title match with :title’s Regexp AND the window class match with :class’s Regexp. if a regexp was given, this bind is active when the window title match with the given regexp



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rbindkeys/key_event_handler/configurer.rb', line 105

def window upper_resolver, arg
  if upper_resolver.nil?
    upper_resolver = @bind_resolver
  elsif upper_resolver.kind_of? Symbol
    upper_resolver = FixResolver.instance upper_resolver
  elsif not upper_resolver.kind_of? BindResolver
    raise ArgumentError, "1nd argument is expected to be a BindResolver or"+
      " a Symbol : #{ upper_resolver.to_s}"
  end

  if arg.kind_of? Regexp
    arg = { :title => arg }
  elsif arg.kind_of? Hash
    arg.each do |k, v|
      if not (k.kind_of?(Symbol) and v.kind_of?(Regexp))
        raise ArgumentError, 'the 2nd argument Hash must only have'+
          " Symbol keys and Regexp values : #{arg.inspect}"
      end
    end
  else
    raise ArgumentError, "2nd argument is expected to be a Hash or a Regexp : #{arg}"
  end

  resolver = BindResolver.new(upper_resolver)
  @window_bind_resolver_map.push [WindowMatcher.new(arg), resolver]

  if block_given?
    old_resolver = @bind_resolver
    @bind_resolver = resolver
    yield
    @bind_resolver = old_resolver
  end

  resolver
end