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,51 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require 'logger'
4
+
5
+ module Rbindkeys
6
+
7
+ class LogUtils
8
+
9
+ DEFAULT_LOG_OUTPUT = STDOUT
10
+ DEFAULT_FORMAT = :simple
11
+ DEFAULT_LEVEL = Logger::INFO
12
+ @@output = DEFAULT_LOG_OUTPUT
13
+ @@format = DEFAULT_FORMAT
14
+ @@level = DEFAULT_LEVEL
15
+
16
+ class << self
17
+
18
+ def get_logger progname
19
+ logger = Logger.new @@output
20
+
21
+ logger.progname = progname
22
+ logger.level = @@level
23
+ set_formatter logger
24
+
25
+ logger
26
+ end
27
+
28
+ def set_formatter logger
29
+ case @@format
30
+ when :simple
31
+ logger.formatter = proc do |sev, date, prog, msg|
32
+ "* #{msg}\n"
33
+ end
34
+ when :default
35
+ else
36
+ l = Logger.new STDERR
37
+ l.fatal "unknown logger format"
38
+ exit false
39
+ end
40
+ end
41
+
42
+ # setter and reader methods for the class variables
43
+ [:output, :format, :level].each do |name|
44
+ eval "def #{name}; @@#{name} end"
45
+ eval "def #{name}= o; @@#{name} = o end"
46
+ end
47
+
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,140 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require "revdev"
4
+
5
+ module Rbindkeys
6
+
7
+ # main event loop class
8
+ class Observer
9
+ include Revdev
10
+
11
+ LOG = LogUtils.get_logger name
12
+ VIRTUAL_DEVICE_NAME = "rbindkyes"
13
+ DEFAULT_TIMEOUT = 0.5
14
+
15
+ attr_reader :device
16
+ attr_reader :virtual
17
+ attr_reader :event_handler
18
+ attr_reader :config_file
19
+
20
+ def initialize config_name, device_location
21
+ @device = Device.new device_location
22
+ @virtual = VirtualDevice.new
23
+ operator = DeviceOperator.new @device, @virtual
24
+ @event_handler = KeyEventHandler.new operator
25
+ @config_file = config_name
26
+ @started = false
27
+ @window_observer = ActiveWindowX::EventListener.new
28
+ @timeout = DEFAULT_TIMEOUT
29
+ end
30
+
31
+ # main loop
32
+ def start
33
+ @device.grab
34
+ @device.release_all_key
35
+
36
+ @virtual.create VIRTUAL_DEVICE_NAME #, @device.device_id
37
+
38
+ @event_handler.load_config @config_file
39
+
40
+ @event_handler.bind_resolver.tree.each do |k,v|
41
+ puts "#{k} => #{v.inspect}"
42
+ end
43
+
44
+ trap :INT, method(:destroy)
45
+ trap :TERM, method(:destroy)
46
+
47
+ active_window = @window_observer.active_window
48
+ @event_handler.active_window_changed active_window if active_window
49
+
50
+ # start main loop
51
+ @started = true
52
+ while true
53
+ ios = select_ios
54
+ if ios.nil?
55
+ # select timeout
56
+ # no op
57
+ next
58
+ end
59
+
60
+ if LOG.debug?
61
+ LOG.debug ""
62
+ LOG.debug "select => #{ios.inspect}"
63
+ end
64
+
65
+ ios.each do |io|
66
+ case io
67
+ when @window_observer.connection then handle_x_event
68
+ when @device.file then handle_device_event
69
+ else LOG.error "unknown IO #{io.inspect}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def select_ios
76
+ if @window_observer.pending_events_num != 0
77
+ [@window_observer.connection]
78
+ else
79
+ ios = select [@window_observer.connection, @device.file], nil, nil, @timeout
80
+ if ios.nil?
81
+ nil
82
+ else
83
+ ios.first
84
+ end
85
+ end
86
+ end
87
+
88
+ def handle_x_event
89
+ event = @window_observer.listen_with_no_select
90
+ return if event.nil? or event.window.nil?
91
+
92
+ @event_handler.active_window_changed event.window
93
+ end
94
+
95
+ def handle_device_event
96
+ event = @device.read_input_event
97
+
98
+ if event.type != Revdev::EV_KEY
99
+ @virtual.write_input_event event
100
+ else
101
+ @event_handler.handle event
102
+ end
103
+ end
104
+
105
+ def destroy *args
106
+ if not @started
107
+ LOG.error 'did not start to observe'
108
+ return
109
+ end
110
+
111
+ begin
112
+ LOG.info "try @device.ungrab"
113
+ @device.ungrab
114
+ LOG.info "=> success"
115
+ rescue => e
116
+ LOG.error e
117
+ end
118
+
119
+ begin
120
+ LOG.info "try @virtural.destroy"
121
+ @virtual.destroy
122
+ LOG.info "=> success"
123
+ rescue => e
124
+ LOG.error e
125
+ end
126
+
127
+ begin
128
+ LOG.info "try @window_observer.destory"
129
+ @window_observer.destroy
130
+ LOG.info "=> success"
131
+ rescue => e
132
+ LOG.error e
133
+ end
134
+
135
+ exit true
136
+ end
137
+
138
+ end # of class
139
+
140
+ end # of module Rbindkeys
@@ -0,0 +1,3 @@
1
+ module Rbindkeys
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require "ruinput"
4
+
5
+ module Rbindkeys
6
+
7
+ class VirtualDevice < Ruinput::UinputDevice
8
+
9
+ def release_all
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,35 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+ #
3
+ # matcher for windows to use the app_class(app_name), title of the windows
4
+ #
5
+
6
+ module Rbindkeys
7
+ class WindowMatcher
8
+
9
+ attr_reader :app_name, :title
10
+
11
+ def initialize h
12
+ @app_name = (h[:class] or h[:app_name] or h[:app_class])
13
+ @title = (h[:title] or h[:name])
14
+
15
+ if not @app_name.nil? and not @title.nil?
16
+ raise ArgumentError, 'expect to be given :class, :app_name,'+
17
+ ' :app_class, :title or :name '
18
+ end
19
+ end
20
+
21
+ def match? app_name, title
22
+ (@app_name.nil? or match_app?(app_name)) and
23
+ (@title.nil? or match_title?(title))
24
+ end
25
+
26
+ def match_app? app_name
27
+ app_name and app_name.match @app_name
28
+ end
29
+
30
+ def match_title? title
31
+ title and title.match @title
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # -*- coding:utf-8-unix; mode:ruby; -*-
2
+
3
+ require File.expand_path('../lib/rbindkeys/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Keiichiro Ui"]
7
+ gem.email = ["keiichiro.ui@gmail.com"]
8
+ gem.description = "key remap"
9
+ gem.summary = "key remap"
10
+ gem.homepage = ""
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "rbindkeys"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Rbindkeys::VERSION
18
+
19
+ gem.add_dependency 'revdev'
20
+ gem.add_dependency 'ruinput'
21
+ gem.add_dependency 'active_window_x'
22
+ gem.add_development_dependency 'bundler'
23
+ gem.add_development_dependency 'rake'
24
+ gem.add_development_dependency 'rspec'
25
+
26
+ end
@@ -0,0 +1,100 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ ## user settings
4
+
5
+ # if you use a keyboard which have a left ctrl key at the left of "A" key,
6
+ # then you must set false
7
+ @swap_left_ctrl_with_caps = true
8
+
9
+ # for apple keyboard
10
+ @swap_left_opt_with_left_cmd = true
11
+
12
+ ##
13
+
14
+ # pre_bind_key: pre-proccessed keybinding
15
+ if @swap_left_ctrl_with_caps
16
+ pre_bind_key KEY_CAPSLOCK, KEY_LEFTCTRL
17
+ pre_bind_key KEY_LEFTCTRL, KEY_CAPSLOCK
18
+ end
19
+
20
+ if @swap_left_opt_with_left_cmd
21
+ pre_bind_key KEY_LEFTMETA, KEY_LEFTALT
22
+ pre_bind_key KEY_LEFTALT, KEY_LEFTMETA
23
+ end
24
+
25
+ # cursor move
26
+ bind_key [KEY_LEFTCTRL, KEY_F], KEY_RIGHT
27
+ bind_key [KEY_LEFTCTRL, KEY_B], KEY_LEFT
28
+ bind_key [KEY_LEFTCTRL, KEY_P], KEY_UP
29
+ bind_key [KEY_LEFTCTRL, KEY_N], KEY_DOWN
30
+ bind_key [KEY_LEFTCTRL, KEY_A], KEY_HOME
31
+ bind_key [KEY_LEFTCTRL, KEY_E], KEY_END
32
+
33
+ # page scroll
34
+ bind_key [KEY_LEFTCTRL, KEY_V], KEY_PAGEDOWN
35
+ bind_key [KEY_LEFTALT, KEY_V], KEY_PAGEUP
36
+
37
+ # edit
38
+ bind_key [KEY_LEFTCTRL, KEY_D], KEY_DELETE
39
+ bind_key [KEY_LEFTCTRL, KEY_H], KEY_BACKSPACE
40
+ bind_key [KEY_LEFTCTRL, KEY_M], KEY_ENTER
41
+ bind_key [KEY_LEFTCTRL, KEY_I], KEY_TAB
42
+ bind_key [KEY_LEFTCTRL, KEY_LEFTBRACE], KEY_ESC
43
+
44
+ # give a block sample
45
+ @caps_led_state = 0
46
+ bind_key KEY_CAPSLOCK do |event, operator|
47
+ @caps_led_state = @caps_led_state ^ 1
48
+ puts "########## CAPSLOCK LED #{@caps_led_state.zero? ? 'off' : 'on'} ##########"
49
+ operator.send_event EV_LED, LED_CAPSL, @caps_led_state
50
+ end
51
+
52
+ # cut, copy and paste
53
+ bind_key [KEY_LEFTCTRL, KEY_W], [KEY_LEFTCTRL,KEY_X]
54
+ bind_key [KEY_LEFTALT, KEY_W], [KEY_LEFTCTRL,KEY_C]
55
+ bind_key [KEY_LEFTCTRL, KEY_Y], [KEY_LEFTCTRL,KEY_V]
56
+
57
+ # kill line
58
+ bind_key [KEY_LEFTCTRL, KEY_K] do |event, operator|
59
+ # Shift+End : select text to end of line
60
+ operator.press_key KEY_LEFTSHIFT
61
+ operator.press_key KEY_END
62
+ operator.release_key KEY_END
63
+ operator.release_key KEY_LEFTSHIFT
64
+
65
+ # Ctrl+x : cut
66
+ operator.press_key KEY_LEFTCTRL
67
+ operator.press_key KEY_X
68
+ operator.release_key KEY_X
69
+ operator.release_key KEY_LEFTCTRL
70
+ end
71
+
72
+ # 2 stroke key binds
73
+ # if your input was not hit any bind_key, the input will be ignored
74
+ bind_prefix_key [KEY_LEFTCTRL, KEY_X] do
75
+
76
+ # C-xk: close tab, etc.
77
+ bind_key KEY_K, [KEY_LEFTCTRL, KEY_W]
78
+
79
+ # C-xC-s: save
80
+ bind_key [KEY_LEFTCTRL, KEY_S], [KEY_LEFTCTRL, KEY_S]
81
+
82
+ # C-xb: next tab, etc.
83
+ bind_key KEY_B, [KEY_LEFTCTRL, KEY_TAB]
84
+
85
+ # C-xC-g: ignore C-x prefix bind
86
+ bind_key [KEY_LEFTCTRL, KEY_G], :ignore
87
+
88
+ # C-xC-c: close window
89
+ bind_key [KEY_LEFTCTRL, KEY_C], [KEY_LEFTALT, KEY_F4]
90
+ end
91
+
92
+ # settings per window class (or title)
93
+
94
+ # through all key inputs if active
95
+ window(:through, :class => /gnome-terminal/)
96
+
97
+ # add new bind_key to default binds
98
+ window(@default_bind_resolver, :class => /google-chrome/) do
99
+ bind_key [KEY_LEFTCTRL, KEY_S], [KEY_LEFTCTRL, KEY_F]
100
+ end
@@ -0,0 +1,4 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ pre_bind_key KEY_CAPSLOCK, KEY_LEFTCTRL
4
+ pre_bind_key KEY_LEFTCTRL, KEY_CAPSLOCK
@@ -0,0 +1,101 @@
1
+ # -*- coding:utf-8; mode:ruby; -*-
2
+
3
+ require 'rbindkeys'
4
+ require 'revdev'
5
+
6
+ include Rbindkeys
7
+
8
+ describe BindResolver do
9
+ before do
10
+ @resolver = BindResolver.new :foo
11
+ end
12
+
13
+ describe "#bind" do
14
+ context "with two Fixnum Array" do
15
+ before do
16
+ @input = [0, 1]
17
+ @output = [2, 3]
18
+ end
19
+ it "should update @tree" do
20
+ @resolver.bind @input, @output
21
+ @resolver.bind [0,2], [2,3]
22
+ @resolver.tree[@input.last].first.input.should == @input
23
+ @resolver.tree[@input.last].first.output.should == @output
24
+ end
25
+ end
26
+ context "with a Fixnum Array and a BindResolver" do
27
+ before do
28
+ @input = [0, 1]
29
+ @output = BindResolver.new :bar
30
+ end
31
+ it "should update @tree" do
32
+ @resolver.bind @input, @output
33
+ @resolver.bind [0,2], [2,3]
34
+ @resolver.tree[@input.last].first.input.should == @input
35
+ @resolver.tree[@input.last].first.output.should == @output
36
+ end
37
+ end
38
+ context "with 2 BindResolver which is same as other one" do
39
+ before do
40
+ @input = [0, 1]
41
+ @output = BindResolver.new :bar
42
+ end
43
+ it "should update @tree" do
44
+ @resolver.bind @input, @output
45
+ lambda{@resolver.bind @input, [2,4]}.should raise_error(DuplicateNodeError)
46
+ end
47
+ end
48
+ context "with Fixnum Arrays" do
49
+ before do
50
+ end
51
+ it "should update @tree which store binds sort by modkey numbers" do
52
+ @resolver.bind [0,2], [0,3]
53
+ @resolver.bind [0,1,2], [1,3]
54
+ @resolver.bind [3,2], [2,3]
55
+
56
+ @resolver.tree[2][0].output.should == [1,3]
57
+ @resolver.tree[2][1].output.should == [0,3]
58
+ @resolver.tree[2][2].output.should == [2,3]
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "#resolve" do
64
+ before do
65
+ @resolver2 = BindResolver.new(:ignore)
66
+ @resolver.bind [0, 1], [2, 3]
67
+ @resolver.bind [3, 1], [2, 4]
68
+ @resolver.bind [0, 2], [2, 5]
69
+ @resolver.bind [0, 1, 2], @resolver2
70
+ end
71
+ context "with an input which hit a bind" do
72
+ before do
73
+ @input = 1
74
+ @pressed_key_set = [0]
75
+ end
76
+ it "should return the bind" do
77
+ @resolver.resolve(@input, @pressed_key_set).kind_of?(KeyBind).should be_true
78
+ @resolver.resolve(@input, @pressed_key_set).output.should == [2, 3]
79
+ end
80
+ end
81
+ context "with an input which hit a BindResolver" do
82
+ before do
83
+ @input = 2
84
+ @pressed_key_set = [0,1]
85
+ end
86
+ it "should return the bind" do
87
+ @resolver.resolve(@input, @pressed_key_set).kind_of?(KeyBind).should be_true
88
+ @resolver.resolve(@input, @pressed_key_set).output.should == @resolver2
89
+ end
90
+ end
91
+ context "with an input which hit no binds" do
92
+ before do
93
+ @input = 2
94
+ @pressed_key_set = [1]
95
+ end
96
+ it "should return default value" do
97
+ @resolver.resolve(@input, @pressed_key_set).should == :foo
98
+ end
99
+ end
100
+ end
101
+ end