rkremap 0.3.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rkremap.rb CHANGED
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rkremap/version"
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
- def initialize(devices=nil)
24
- if devices
25
- devices = Array(devices)
26
- @inputs = devices.map{|d| Evdev.new(d)}
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
- @inputs = Evdev.detect.select(&:keyboard?)
41
+ else
42
+ devices = Array(devices)
30
43
  end
31
- raise 'Unable to detect keyboard device' if @inputs.empty?
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
- @events = []
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
- def match(code: nil, type: nil, &block)
48
- @events.push [{code: code, type: type}, block]
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
- detect_device_loop if @auto_detect
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
- @winattr = WinAttr.new if @x11
55
- @inputs.each(&:grab) if @grab
73
+ winattr = WinAttr.new if @x11
56
74
  while true
57
75
  @keys = []
58
- time, type, code, value = read_event
59
- next if type != EV_KEY
60
- event = Event.new(time, code, value, @winattr)
61
- @events.each do |cond, b|
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
- end
84
- end
85
-
86
- def read_event
87
- while true
88
- begin
89
- input = Evdev.select(@inputs, 3)
90
- next unless input
91
- return input.read_event
92
- rescue Errno::ENODEV
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
- # @param ev [Rkmap::Event]
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
- proc_event(code, value, event)
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
- write_event(code, 1)
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 proc_event(code, value, event)
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
- elsif value != 0
131
- @keys.push [code, @mod_state.dup, App.new(event&.win, @winattr)]
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
- def initialize(win, winattr)
173
- @win = win
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.3.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-03-05 00:00:00.000000000 Z
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-alt.rb
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://github.com/tmtm/rkremap
47
+ homepage: https://gitlab.com/tmtms/rkremap
31
48
  licenses:
32
49
  - MIT
33
50
  metadata:
34
- homepage_uri: https://github.com/tmtm/rkremap
35
- source_code_uri: https://github.com/tmtm/rkremap
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.3
70
+ rubygems_version: 3.3.7
54
71
  signing_key:
55
72
  specification_version: 4
56
73
  summary: key remapper
@@ -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