rbindkeys 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ *.o
4
+ *.so
5
+ *.dll
6
+ *~
7
+ .bundle
8
+ .config
9
+ .yardoc
10
+ Gemfile.lock
11
+ InstalledFiles
12
+ Makefile
13
+ _yardoc
14
+ core
15
+ coverage
16
+ doc/
17
+ lib/bundler/man
18
+ pkg
19
+ rdoc
20
+ spec/reports
21
+ test/tmp
22
+ test/version_tmp
23
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rbindkeys.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Keiichiro Ui
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # rbindkeys
2
+
3
+ a key remapper, which is configurable in ruby, for Linux and X Window System
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rbindkeys'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rbindkeys
18
+
19
+ ## Usage
20
+
21
+ 1. `rbindkeys -e > ~/.rbindkeys.rb`
22
+ 2. edit `~.rbindkeys.rb`
23
+ 3. select a keyboard device (see `sudo rbindkeys --evdev-list`)
24
+ 4. `sudo rbindkeys /dev/input/event2` if you selected "/dev/input/event2"
25
+ as a target keyboard
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create new Pull Request
34
+
35
+ ## TODO
36
+
37
+ * write documents and publish on rubygem.org
38
+ * a daemonize script
39
+ * remove @two_storoke and add PrefixBindResolver class
40
+ * change BindResolver on input method system
41
+ * simplify config file (e.g. `bind_key [:ctrl, :m], :enter`, `bind_key "ctrl+m", "enter"` )
42
+ * integrate ibus controller (e.g. `bind_key "alt-grave", "toggle_ibus"` )
43
+ * notification when active a prefix key, changing ibus status, etc..
44
+ * the LED manipulation does not work for bluetooth devices
45
+ * fix bug
46
+ * the enter key cannot be release when `rbindkey` is executed
47
+
48
+ ## Other Configurable Key Remappers For Linux
49
+
50
+ * [x11keymacs](http://yashiromann.sakura.ne.jp/x11keymacs/index-en.html)
51
+ * [xfumble](http://endoh-namazu.tierra.ne.jp/xfumble/)
52
+ * [私家版 窓使いの憂鬱 Linux & Mac (Darwin) 対応版](http://www42.tok2.com/home/negidakude/)
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core"
5
+ require "rspec/core/rake_task"
6
+
7
+ desc "Run all specs in spec/*_spec.rb"
8
+ RSpec::Core::RakeTask.new :spec
9
+
10
+ task :build => :spec
11
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require "rbindkeys"
4
+
5
+ Rbindkeys::CLI::main
@@ -0,0 +1,38 @@
1
+ # -*- coding:undecided-unix; mode:ruby; -*-
2
+
3
+ require 'rubygems'
4
+ require 'rbindkeys/version'
5
+ require 'rbindkeys/log_utils'
6
+
7
+ require 'rbindkeys/key_bind'
8
+ require 'rbindkeys/bind_tree'
9
+ require 'rbindkeys/observer'
10
+ require 'rbindkeys/device'
11
+ require 'rbindkeys/virtual_device'
12
+
13
+ require 'rbindkeys/device_operator'
14
+ require 'rbindkeys/key_event_handler'
15
+ require 'rbindkeys/window_matcher'
16
+ require 'rbindkeys/bind_resolver'
17
+ require 'rbindkeys/fix_resolver'
18
+
19
+ require 'rbindkeys/cli'
20
+
21
+ module Rbindkeys
22
+
23
+ class BindTree; end
24
+ class Observer; end
25
+ class Devicie; end
26
+ class VirtualDevice; end
27
+
28
+ class DeviceOperator; end
29
+ class WindowMatcher; end
30
+ class KeyEventHandler; end
31
+ class BindResolver; end
32
+ class FixResolver; end
33
+
34
+ class CLI; end
35
+
36
+ class DuplicateNodeError < ArgumentError; end
37
+ class UnknownKeyValue < Exception; end
38
+ end
@@ -0,0 +1,58 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ module Rbindkeys
4
+
5
+ class BindResolver
6
+
7
+ LOG = LogUtils.get_logger name
8
+ DEFAULT_VALUE = :through
9
+
10
+ attr_reader :tree
11
+
12
+ # delegate if cannot resolved
13
+ attr_reader :upper_resolver
14
+
15
+ # if this resolver is set by prefix key, then true
16
+ # else, false
17
+ attr_reader :two_stroke
18
+ alias :two_stroke? :two_stroke
19
+
20
+ def initialize upper_resolver=:through, two_stroke=false
21
+ @tree = {}
22
+ if upper_resolver.kind_of? Symbol
23
+ upper_resolver = FixResolver.instance upper_resolver
24
+ end
25
+ @upper_resolver = upper_resolver
26
+ @two_stroke = two_stroke
27
+ end
28
+
29
+ def bind input, output
30
+ @tree[input.last] ||= []
31
+ @tree[input.last].each do |b|
32
+ if b.input == input
33
+ raise DuplicateNodeError, "already this input(#{input.inspect}) was binded"
34
+ end
35
+ end
36
+
37
+ kb = KeyBind.new input, output
38
+ @tree[input.last] << kb # TODO implement a bubble insertion
39
+ @tree[input.last].sort!{|a,b| b.input.length <=> a.input.length}
40
+ kb
41
+ end
42
+
43
+ def resolve key_code, key_code_set
44
+ just_resolve(key_code, key_code_set) or
45
+ @upper_resolver.resolve(key_code, key_code_set)
46
+ end
47
+
48
+ def just_resolve key_code, key_code_set
49
+ arr = @tree[key_code]
50
+ arr.each do |kb|
51
+ sub = kb.input - key_code_set
52
+ sub.first == kb.input.last and
53
+ return kb
54
+ end if not arr.nil?
55
+ nil
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ module Rbindkeys
4
+
5
+ # BindSet is a implementation for replace with BindTree
6
+ # using Array#& (set intersection).
7
+ # because BindTree drop bind order infomation.
8
+ class BindSet
9
+ end
10
+ end
@@ -0,0 +1,148 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ module Rbindkeys
4
+ class BindTree
5
+
6
+ DEFAULT_DEFAULT_VALUE = :through
7
+ AVAIVABLE_DEFAULT_VALUE = [:through, :ignore]
8
+
9
+ # a tree structure which that nodes are Fixnum(keycode) and
10
+ # leaves are Leaf
11
+ attr_reader :tree
12
+
13
+ attr_reader :main_tree
14
+
15
+ # active KeyBind
16
+ # TODO create KeyEventHandler which exist between Observer and BindTree
17
+ # TODO move out @active_key_binds to KeyEventHandler
18
+ attr_reader :active_key_binds
19
+
20
+ # a value if no binds hit
21
+ attr_reader :default_value
22
+
23
+ def initialize default_value=DEFAULT_DEFAULT_VALUE
24
+ @tree = {}
25
+ @active_key_binds = []
26
+ if AVAIVABLE_DEFAULT_VALUE.include? default_value
27
+ @default_value = default_value
28
+ else
29
+ raise ArgumentError, "expect #{AVAIVABLE_DEFAULT_VALUE.join('/')}"
30
+ end
31
+ end
32
+
33
+ # register an input-output pair
34
+ # _input_: Array of (Array of) input keycodes
35
+ # _output_: Array of send keycodes or Proc
36
+ def bind input, output=nil
37
+ input = input.clone
38
+ new_input = []
39
+
40
+ if input.kind_of? Array and input[0].kind_of? Array
41
+ new_input = input
42
+ input = new_input.shift
43
+ end
44
+
45
+ tail_code = input.pop
46
+ input.sort!
47
+
48
+ subtree = @tree
49
+ input.each do |code|
50
+ if subtree.has_key? code and (not subtree[code].kind_of? Hash)
51
+ raise DuplicateNodeError, "already register an input:#{input}"
52
+ end
53
+ subtree[code] ||= {}
54
+ subtree = subtree[code]
55
+ end
56
+
57
+ if not new_input.empty?
58
+ if subtree.has_key?(tail_code) and
59
+ not (subtree[tail_code].kind_of?(Leaf) and
60
+ subtree[tail_code].payload.kind_of?(BindTree))
61
+ raise DuplicateNodeError, "already register an input:#{input}"
62
+ end
63
+
64
+ if new_input.length == 1
65
+ new_input = new_input.first
66
+ end
67
+
68
+ subtree[tail_code] ||= Leaf.new BindTree.new :ignore
69
+ subtree[tail_code].payload.bind new_input, output
70
+
71
+ elsif subtree.has_key? tail_code
72
+ raise DuplicateNodeError, "already register an input:#{input}"
73
+
74
+ else
75
+ subtree[tail_code] = Leaf.new KeyBind.new input.push(tail_code), output
76
+ end
77
+ end
78
+
79
+ # called when event.value == 0
80
+ def resolve_for_released_event event, pressed_keys
81
+ release_binds = []
82
+ @active_key_binds.reject! do |key_bind|
83
+ if key_bind.input.include? event.code
84
+ release_binds << key_bind
85
+ true
86
+ else
87
+ false
88
+ end
89
+ end
90
+
91
+ if release_binds.empty?
92
+ :through
93
+ else
94
+ release_binds
95
+ end
96
+ end
97
+
98
+ # called when event.value == 1
99
+ def resolve_for_pressed_event event, pressed_keys
100
+ subtree = @tree
101
+ last_code = -1
102
+ pressed_keys.each do |code|
103
+ if last_code >= code
104
+ raise ArgumentError, "expect a sorted Array for 2nd arg (pressed_keys)"
105
+ end
106
+ last_code = code
107
+
108
+ if subtree.has_key? code
109
+ subtree = subtree[code]
110
+ end
111
+ end
112
+
113
+ subtree = (subtree.kind_of?(Hash) and subtree[event.code])
114
+
115
+ if not subtree or subtree.kind_of? Hash
116
+ return @default_value
117
+ elsif subtree.kind_of? Leaf
118
+ if subtree.payload.kind_of? KeyBind
119
+ @active_key_binds << subtree.payload
120
+ return subtree.payload
121
+ elsif subtree.payload.kind_of? BindTree
122
+ return subtree.payload
123
+ end
124
+ else
125
+ raise UnexpecedLeafError, "unexpeced Leaf: #{subtree.inspect}"
126
+ end
127
+ end
128
+
129
+ # called when event.value == 2
130
+ def resolve_for_pressing_event event, pressed_keys
131
+ if @active_key_binds.empty?
132
+ @default_value
133
+ else
134
+ @active_key_binds
135
+ end
136
+ end
137
+
138
+ class Leaf
139
+ attr_reader :payload
140
+
141
+ def initialize payload
142
+ @payload = payload
143
+ end
144
+ end
145
+
146
+ class UnexpecedLeafError < RuntimeError; end
147
+ end
148
+ end
@@ -0,0 +1,103 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ module Rbindkeys
4
+
5
+ SUMMARY = 'key remapper for Linux which is configured in ruby'
6
+
7
+ # a class is executed by bin/rbindkeys
8
+ class CLI
9
+
10
+ EVDEVS = '/dev/input/event*'
11
+
12
+ class << self
13
+ require 'optparse'
14
+
15
+ # if @@cmd == :observe then CLI excecute to observe a given event device
16
+ # else if @@cmd == :ls then CLI list event devices
17
+ # (default: :observe)
18
+ @@cmd = :observe
19
+ def cmd; @@cmd end
20
+
21
+ # a location of a config file (default: "~/.rbindkeys.rb")
22
+ @@config = "#{ENV['HOME']}/.rbindkeys.rb"
23
+ def config; @@config end
24
+
25
+ @@usage = SUMMARY
26
+
27
+ def main
28
+ begin
29
+ parse_opt
30
+ rescue OptionParser::ParseError => e
31
+ puts "ERROR #{e.to_s}"
32
+ err
33
+ end
34
+
35
+ method(@@cmd).call
36
+ end
37
+
38
+ def err code=1
39
+ puts @@usage
40
+ exit code
41
+ end
42
+
43
+ def parse_opt
44
+ opt = OptionParser.new <<BANNER
45
+ #{SUMMARY}
46
+ Usage: sudo #{$0} [--config file] #{EVDEVS}
47
+ or: sudo #{$0} --evdev-list
48
+ BANNER
49
+ opt.version = VERSION
50
+ opt.on '-l', '--evdev-list', 'a list of event devices' do
51
+ @@cmd = :ls
52
+ end
53
+ opt.on '-c VAL', '--config VAL', 'specifying your configure file' do |v|
54
+ @@config = v
55
+ end
56
+ opt.on '-e', '--print-example', 'print an example config' do |v|
57
+ @@cmd = :print_example
58
+ end
59
+
60
+ opt.parse! ARGV
61
+
62
+ @@usage = opt.help
63
+ end
64
+
65
+ def observe
66
+ if ARGV.length != 1
67
+ puts 'ERROR invalid arguments'
68
+ err
69
+ end
70
+ evdev = ARGV.first
71
+ Observer.new(@@config, evdev).start
72
+ end
73
+
74
+ def ls
75
+ require 'revdev'
76
+ Dir::glob(EVDEVS).sort do |a,b|
77
+ am = a.match(/[0-9]+$/)
78
+ bm = b.match(/[0-9]+$/)
79
+ ai = am[0] ? am[0].to_i : 0
80
+ bi = bm[0] ? bm[0].to_i : 0
81
+ ai <=> bi
82
+ end.each do |f|
83
+ begin
84
+ e = Revdev::EventDevice.new f
85
+ puts "#{f}: #{e.device_name} (#{e.device_id.hr_bustype})"
86
+ rescue => ex
87
+ puts ex
88
+ end
89
+ end
90
+ end
91
+
92
+ def print_example
93
+ dir = File.dirname File.expand_path __FILE__
94
+ dir = File.expand_path File.join dir, '..', '..', 'sample'
95
+ file = File.join dir, 'emacs.rb'
96
+ IO.foreach file do |line|
97
+ puts "# #{line}"
98
+ end
99
+ end
100
+
101
+ end
102
+ end # of class Runner
103
+ end