rkremap 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +69 -5
- data/example/henkan-super.rb +52 -0
- data/example/keylogger.rb +2 -2
- data/example/mac-shortcut.rb +54 -11
- data/example/tmtms.rb +102 -89
- data/lib/rkremap/evdev.rb +47 -85
- data/lib/rkremap/evdev_list.rb +90 -0
- data/lib/rkremap/event.rb +48 -0
- data/lib/rkremap/keycode.rb +81 -1
- data/lib/rkremap/uinput.rb +40 -0
- data/lib/rkremap/version.rb +1 -1
- data/lib/rkremap/winattr.rb +4 -1
- data/lib/rkremap.rb +101 -85
- metadata +25 -8
- data/example/henkan-alt.rb +0 -93
data/lib/rkremap.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'rkremap/version'
|
4
4
|
require_relative 'rkremap/keycode'
|
5
5
|
require_relative 'rkremap/winattr'
|
6
|
+
require_relative 'rkremap/evdev_list'
|
6
7
|
require_relative 'rkremap/evdev'
|
8
|
+
require_relative 'rkremap/event'
|
9
|
+
require_relative 'rkremap/uinput'
|
7
10
|
|
8
11
|
class Rkremap
|
9
12
|
include KeyCode
|
10
13
|
|
14
|
+
# /usr/include/linux/input-event-codes.h
|
15
|
+
EV_SYN = 0
|
16
|
+
EV_KEY = 1
|
17
|
+
EV_REL = 2
|
18
|
+
EV_ABS = 3
|
19
|
+
EV_MSC = 4
|
20
|
+
SYN_REPORT = 0
|
21
|
+
|
11
22
|
EVENT_TYPE_VALUE = {
|
12
23
|
release: 0,
|
13
24
|
press: 1,
|
@@ -18,17 +29,21 @@ class Rkremap
|
|
18
29
|
attr_accessor :modifiers
|
19
30
|
attr_accessor :x11
|
20
31
|
attr_accessor :auto_detect
|
32
|
+
attr_accessor :exclude
|
33
|
+
attr_accessor :mouse
|
21
34
|
|
22
35
|
# @param devices [Array<String>, String]
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
else
|
36
|
+
# @param exclude [Regexp]
|
37
|
+
# @param mouse [Boolean]
|
38
|
+
def initialize(devices=[], exclude: nil, mouse: false)
|
39
|
+
if devices.empty?
|
28
40
|
@auto_detect = true
|
29
|
-
|
41
|
+
else
|
42
|
+
devices = Array(devices)
|
30
43
|
end
|
31
|
-
|
44
|
+
@devices = devices
|
45
|
+
@exclude = exclude
|
46
|
+
@mouse = mouse
|
32
47
|
@uinput = Uinput.new
|
33
48
|
@grab = false
|
34
49
|
@x11 = false
|
@@ -38,60 +53,43 @@ class Rkremap
|
|
38
53
|
KEY_LEFTALT, KEY_RIGHTALT,
|
39
54
|
KEY_LEFTMETA, KEY_RIGHTMETA,
|
40
55
|
]
|
41
|
-
@
|
56
|
+
@event_procs = []
|
42
57
|
@mutex = Mutex.new
|
43
58
|
end
|
44
59
|
|
60
|
+
# @param device [Symbol, Regexp]
|
45
61
|
# @param code [Integer]
|
46
62
|
# @param type [Symbol] :press / :release / :repeat
|
47
|
-
|
48
|
-
|
63
|
+
# @param app [Regexp, String]
|
64
|
+
def match(device: nil, code: nil, type: nil, app: nil, &block)
|
65
|
+
@event_procs.push [{device: device, code: code, type: type, app: app}, block]
|
49
66
|
end
|
50
67
|
|
51
68
|
def start(&block)
|
52
|
-
|
69
|
+
@evdev_list = EvdevList.new(@devices, auto_detect: @auto_detect, exclude: @exclude, detect_mouse: @mouse)
|
70
|
+
@evdev_list.grab = @grab
|
71
|
+
@evdev_list.detect_loop
|
53
72
|
@mod_state = @modifiers.map.to_h{|m| [m, false]}
|
54
|
-
|
55
|
-
@inputs.each(&:grab) if @grab
|
73
|
+
winattr = WinAttr.new if @x11
|
56
74
|
while true
|
57
75
|
@keys = []
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
synchronize{ b.call event } if event.match?(**cond)
|
63
|
-
break if event.skipped
|
64
|
-
end
|
65
|
-
next if event.skipped
|
66
|
-
synchronize{ proc_event(code, value, event) }
|
67
|
-
@keys.each do |c, mod, app|
|
68
|
-
synchronize{ block.call(c, mod, app) } if block
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def detect_device_loop
|
74
|
-
Thread.new do
|
75
|
-
while true
|
76
|
-
sleep 3
|
77
|
-
new_devs = Evdev.detect.select(&:keyboard?)
|
78
|
-
unless new_devs.empty?
|
79
|
-
new_devs.each(&:grab) if @grab
|
80
|
-
@inputs += new_devs
|
76
|
+
event = @evdev_list.read_event
|
77
|
+
if event.ev_type != EV_KEY
|
78
|
+
if event.device.grab?
|
79
|
+
@uinput.write_event(event.ev_type, event.code, event.value)
|
81
80
|
end
|
81
|
+
next
|
82
82
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
input.close rescue nil
|
94
|
-
@inputs.delete input
|
83
|
+
app = App.new(winattr) if winattr
|
84
|
+
event.app = app
|
85
|
+
@event_procs.each do |cond, b|
|
86
|
+
synchronize{ b.call(event, @mod_state.dup, app) } if event.match?(**cond)
|
87
|
+
break if event.skipped?
|
88
|
+
end
|
89
|
+
next if event.skipped?
|
90
|
+
synchronize{ proc_event(event, app) }
|
91
|
+
@keys.each do |c, mod, app_|
|
92
|
+
synchronize{ block.call(c, mod, app_) } if block
|
95
93
|
end
|
96
94
|
end
|
97
95
|
end
|
@@ -102,20 +100,27 @@ class Rkremap
|
|
102
100
|
|
103
101
|
# @param code [Integer]
|
104
102
|
# @param type [Symbol] :press / :release / :repeat
|
105
|
-
|
106
|
-
def event(code: nil, type: nil, event: nil)
|
103
|
+
def event(code: nil, type: nil)
|
107
104
|
value = EVENT_TYPE_VALUE[type] or raise "invalid type: #{type.inspect}"
|
108
|
-
|
105
|
+
update_modifiers(code, value)
|
106
|
+
write_event(code, value)
|
109
107
|
end
|
110
108
|
|
111
109
|
# @param code [Integer]
|
112
110
|
# @param mod [Hash] {MOD_KEY => true, ...}
|
113
111
|
def key(code, mod={})
|
112
|
+
with_modifier(mod) do
|
113
|
+
write_event(code, 1)
|
114
|
+
write_event(code, 0)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param mod [Hash] {MOD_KEY => true, ...}
|
119
|
+
def with_modifier(mod, &block)
|
114
120
|
mod_diff(mod).each do |mcode, state|
|
115
121
|
write_event(mcode, state ? 1 : 0)
|
116
122
|
end
|
117
|
-
|
118
|
-
write_event(code, 0)
|
123
|
+
block.call
|
119
124
|
mod_diff(mod).each do |mcode, _|
|
120
125
|
write_event(mcode, @mod_state[mcode] ? 1 : 0)
|
121
126
|
end
|
@@ -123,12 +128,23 @@ class Rkremap
|
|
123
128
|
|
124
129
|
private
|
125
130
|
|
126
|
-
def
|
131
|
+
def update_modifiers(code, value)
|
127
132
|
if @mod_state.include?(code)
|
128
133
|
@mod_state[code] = value != 0
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# @param event [Rkremap::Event]
|
138
|
+
# @param app [Rkremap::App]
|
139
|
+
def proc_event(event, app)
|
140
|
+
code, value = event.code, event.value
|
141
|
+
if @mod_state.include?(code)
|
142
|
+
write_event(code, value)
|
143
|
+
update_modifiers(code, value)
|
144
|
+
elsif value == 0
|
129
145
|
write_event(code, value)
|
130
|
-
|
131
|
-
@keys.push [code, @mod_state.dup,
|
146
|
+
else
|
147
|
+
@keys.push [code, @mod_state.dup, app]
|
132
148
|
end
|
133
149
|
end
|
134
150
|
|
@@ -140,46 +156,46 @@ class Rkremap
|
|
140
156
|
# @param value [Integer] 0:release / 1:press / 2:repeat
|
141
157
|
def write_event(code, value)
|
142
158
|
@uinput.write_event(EV_KEY, code, value)
|
143
|
-
@uinput.write_event(EV_SYN, SYN_REPORT, 0)
|
144
|
-
end
|
145
|
-
|
146
|
-
class Event
|
147
|
-
attr_reader :time
|
148
|
-
attr_reader :code
|
149
|
-
attr_reader :type
|
150
|
-
attr_reader :win
|
151
|
-
attr_reader :skipped
|
152
|
-
|
153
|
-
def initialize(time, code, value, winattr)
|
154
|
-
@time = time
|
155
|
-
@code = code
|
156
|
-
@value = value
|
157
|
-
@type = value == 1 ? :press : value == 0 ? :release : :repeat
|
158
|
-
@win = winattr&.focus_win
|
159
|
-
@skipped = false
|
160
|
-
end
|
161
|
-
|
162
|
-
def match?(code: nil, type: nil)
|
163
|
-
(code.nil? || Array(code).include?(@code)) && (type.nil? || Array(type).include?(@type))
|
164
|
-
end
|
165
|
-
|
166
|
-
def skip
|
167
|
-
@skipped = true
|
168
|
-
end
|
169
159
|
end
|
170
160
|
|
171
161
|
class App
|
172
|
-
|
173
|
-
|
162
|
+
# @param winattr [Rkremap::WinAttr]
|
163
|
+
def initialize(winattr)
|
174
164
|
@winattr = winattr
|
165
|
+
@win = winattr.focus_win
|
175
166
|
end
|
176
167
|
|
168
|
+
# @return [String, nil]
|
177
169
|
def class_name
|
178
170
|
@winattr.app_win(@win)[2] if @winattr && @win
|
179
171
|
end
|
180
172
|
|
173
|
+
# @return [String, nil]
|
181
174
|
def title
|
182
175
|
@winattr.app_title(@win) if @winattr && @win
|
183
176
|
end
|
177
|
+
|
178
|
+
# @param app [Array, Hash, String, Regexp]
|
179
|
+
# @return [Boolean]
|
180
|
+
def match?(app)
|
181
|
+
Array(app).each do |a|
|
182
|
+
a = {class_name: a, title: a} unless a.is_a? Hash
|
183
|
+
return true if match_sub(a[:class_name], class_name) || match_sub(a[:title], title)
|
184
|
+
end
|
185
|
+
false
|
186
|
+
end
|
187
|
+
|
188
|
+
# @param a [String, Regexp]
|
189
|
+
# @param b [String]
|
190
|
+
def match_sub(a, b)
|
191
|
+
case a
|
192
|
+
when String
|
193
|
+
a == b
|
194
|
+
when Regexp
|
195
|
+
a =~ b
|
196
|
+
else
|
197
|
+
false
|
198
|
+
end
|
199
|
+
end
|
184
200
|
end
|
185
201
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rkremap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TOMITA Masahiro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
12
|
-
dependencies:
|
11
|
+
date: 2022-08-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tmtms_timer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
13
27
|
description: key remapper
|
14
28
|
email:
|
15
29
|
- tommy@tmtm.org
|
@@ -18,21 +32,24 @@ extensions: []
|
|
18
32
|
extra_rdoc_files: []
|
19
33
|
files:
|
20
34
|
- README.md
|
21
|
-
- example/henkan-
|
35
|
+
- example/henkan-super.rb
|
22
36
|
- example/keylogger.rb
|
23
37
|
- example/mac-shortcut.rb
|
24
38
|
- example/tmtms.rb
|
25
39
|
- lib/rkremap.rb
|
26
40
|
- lib/rkremap/evdev.rb
|
41
|
+
- lib/rkremap/evdev_list.rb
|
42
|
+
- lib/rkremap/event.rb
|
27
43
|
- lib/rkremap/keycode.rb
|
44
|
+
- lib/rkremap/uinput.rb
|
28
45
|
- lib/rkremap/version.rb
|
29
46
|
- lib/rkremap/winattr.rb
|
30
|
-
homepage: https://
|
47
|
+
homepage: https://gitlab.com/tmtms/rkremap
|
31
48
|
licenses:
|
32
49
|
- MIT
|
33
50
|
metadata:
|
34
|
-
homepage_uri: https://
|
35
|
-
source_code_uri: https://
|
51
|
+
homepage_uri: https://gitlab.com/tmtms/rkremap
|
52
|
+
source_code_uri: https://gitlab.com/tmtms/rkremap
|
36
53
|
documentation_uri: https://www.rubydoc.info/gems/rkremap
|
37
54
|
rubygems_mfa_required: 'true'
|
38
55
|
post_install_message:
|
@@ -50,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
67
|
- !ruby/object:Gem::Version
|
51
68
|
version: '0'
|
52
69
|
requirements: []
|
53
|
-
rubygems_version: 3.3.
|
70
|
+
rubygems_version: 3.3.7
|
54
71
|
signing_key:
|
55
72
|
specification_version: 4
|
56
73
|
summary: key remapper
|
data/example/henkan-alt.rb
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
# 変換/無変換キーを0.5秒以上押してるとALTキーとして扱う。
|
2
|
-
# 0.5秒以内でも変換/無変換キーを押しながら他のキーを押すとALTキーとして扱う。
|
3
|
-
|
4
|
-
require 'rkremap'
|
5
|
-
include Rkremap::KeyCode
|
6
|
-
|
7
|
-
rk = Rkremap.new
|
8
|
-
rk.grab = true
|
9
|
-
|
10
|
-
class State
|
11
|
-
attr_accessor :converted
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@press = nil
|
15
|
-
@converted = false
|
16
|
-
@thread = nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def press(thread)
|
20
|
-
@press = true
|
21
|
-
@converted = false
|
22
|
-
@thread = thread
|
23
|
-
end
|
24
|
-
|
25
|
-
def release
|
26
|
-
@press = false
|
27
|
-
@converted = false
|
28
|
-
@thread.kill
|
29
|
-
@thread = nil
|
30
|
-
end
|
31
|
-
|
32
|
-
def pressed?
|
33
|
-
@press
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
state = {
|
38
|
-
KEY_HENKAN => State.new,
|
39
|
-
KEY_MUHENKAN => State.new,
|
40
|
-
}
|
41
|
-
|
42
|
-
MAP = {
|
43
|
-
KEY_HENKAN => KEY_RIGHTALT,
|
44
|
-
KEY_MUHENKAN => KEY_LEFTALT,
|
45
|
-
}
|
46
|
-
|
47
|
-
rk.match(code: [KEY_HENKAN, KEY_MUHENKAN], type: :press) do |event|
|
48
|
-
thr = Thread.new(event.code) do |code|
|
49
|
-
sleep 0.5
|
50
|
-
rk.synchronize do
|
51
|
-
if state[code].pressed?
|
52
|
-
rk.event(code: MAP[code], type: :press)
|
53
|
-
state[code].converted = true
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
state[event.code].press(thr)
|
58
|
-
event.skip
|
59
|
-
end
|
60
|
-
|
61
|
-
rk.match(code: [KEY_HENKAN, KEY_MUHENKAN], type: :release) do |event|
|
62
|
-
if state[event.code].converted
|
63
|
-
rk.event(code: MAP[event.code], type: :release)
|
64
|
-
event.skip
|
65
|
-
else
|
66
|
-
rk.event(code: event.code, type: :press)
|
67
|
-
rk.event(code: event.code, type: :release)
|
68
|
-
end
|
69
|
-
state[event.code].release
|
70
|
-
end
|
71
|
-
|
72
|
-
rk.match(code: [KEY_HENKAN, KEY_MUHENKAN], type: :repeat) do |event|
|
73
|
-
event.skip
|
74
|
-
end
|
75
|
-
|
76
|
-
rk.match(type: :press) do
|
77
|
-
[KEY_HENKAN, KEY_MUHENKAN].each do |code|
|
78
|
-
if state[code].pressed?
|
79
|
-
rk.event(code: MAP[code], type: :press)
|
80
|
-
state[code].converted = true
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def code2key(code)
|
86
|
-
Rkremap::CODE_KEY[code].to_s.sub(/\AKEY_/, '')
|
87
|
-
end
|
88
|
-
|
89
|
-
rk.start do |code, mod|
|
90
|
-
key = (mod.select{|_, v| v}.keys + [code]).map{|c| code2key(c)}.join('-')
|
91
|
-
puts key
|
92
|
-
rk.key(code, mod)
|
93
|
-
end
|