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,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