rbindkeys 0.0.1

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