rkremap 0.3.0.1 → 0.5.0

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,25 @@
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'
10
+ require_relative 'rkremap/remap'
7
11
 
8
12
  class Rkremap
9
13
  include KeyCode
10
14
 
15
+ # /usr/include/linux/input-event-codes.h
16
+ EV_SYN = 0
17
+ EV_KEY = 1
18
+ EV_REL = 2
19
+ EV_ABS = 3
20
+ EV_MSC = 4
21
+ SYN_REPORT = 0
22
+
11
23
  EVENT_TYPE_VALUE = {
12
24
  release: 0,
13
25
  press: 1,
@@ -18,17 +30,21 @@ class Rkremap
18
30
  attr_accessor :modifiers
19
31
  attr_accessor :x11
20
32
  attr_accessor :auto_detect
33
+ attr_accessor :exclude
34
+ attr_accessor :mouse
21
35
 
22
36
  # @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
37
+ # @param exclude [Regexp]
38
+ # @param mouse [Boolean]
39
+ def initialize(devices=[], exclude: nil, mouse: false)
40
+ if devices.empty?
28
41
  @auto_detect = true
29
- @inputs = Evdev.detect.select(&:keyboard?)
42
+ else
43
+ devices = Array(devices)
30
44
  end
31
- raise 'Unable to detect keyboard device' if @inputs.empty?
45
+ @devices = devices
46
+ @exclude = exclude
47
+ @mouse = mouse
32
48
  @uinput = Uinput.new
33
49
  @grab = false
34
50
  @x11 = false
@@ -38,60 +54,54 @@ class Rkremap
38
54
  KEY_LEFTALT, KEY_RIGHTALT,
39
55
  KEY_LEFTMETA, KEY_RIGHTMETA,
40
56
  ]
41
- @events = []
57
+ @event_procs = []
42
58
  @mutex = Mutex.new
43
59
  end
44
60
 
61
+ # @overload remap(map)
62
+ # @param map [Hash] from => to
63
+ # @overload remap(map:, hold: nil)
64
+ # @param map [Hash] from => to
65
+ # @param hold [Numeric] seconds
66
+ def remap(**opts)
67
+ map, hold = opts[:map], opts[:hold]
68
+ map ||= opts
69
+ Remap.new(self, map: map, hold: hold)
70
+ end
71
+
72
+ # @param device [Symbol, Regexp]
45
73
  # @param code [Integer]
46
74
  # @param type [Symbol] :press / :release / :repeat
47
- def match(code: nil, type: nil, &block)
48
- @events.push [{code: code, type: type}, block]
75
+ # @param app [Regexp, String]
76
+ def match(device: nil, code: nil, type: nil, app: nil, &block)
77
+ @event_procs.push [{device: device, code: code, type: type, app: app}, block]
49
78
  end
50
79
 
51
80
  def start(&block)
52
- detect_device_loop if @auto_detect
81
+ @evdev_list = EvdevList.new(@devices, auto_detect: @auto_detect, exclude: @exclude, detect_mouse: @mouse)
82
+ @evdev_list.grab = @grab
83
+ @evdev_list.detect_loop
53
84
  @mod_state = @modifiers.map.to_h{|m| [m, false]}
54
- @winattr = WinAttr.new if @x11
55
- @inputs.each(&:grab) if @grab
85
+ winattr = WinAttr.new if @x11
56
86
  while true
57
87
  @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
88
+ event = @evdev_list.read_event
89
+ if event.ev_type != EV_KEY
90
+ if event.device.grab?
91
+ @uinput.write_event(event.ev_type, event.code, event.value)
81
92
  end
93
+ next
82
94
  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
95
+ app = App.new(winattr) if winattr
96
+ event.app = app
97
+ @event_procs.each do |cond, b|
98
+ synchronize{ b.call(event, @mod_state.dup, app) } if event.match?(**cond)
99
+ break if event.skipped?
100
+ end
101
+ next if event.skipped?
102
+ synchronize{ proc_event(event, app) }
103
+ @keys.each do |c, mod, app_|
104
+ synchronize{ block.call(c, mod, app_) } if block
95
105
  end
96
106
  end
97
107
  end
@@ -102,20 +112,27 @@ class Rkremap
102
112
 
103
113
  # @param code [Integer]
104
114
  # @param type [Symbol] :press / :release / :repeat
105
- # @param ev [Rkmap::Event]
106
- def event(code: nil, type: nil, event: nil)
115
+ def event(code: nil, type: nil)
107
116
  value = EVENT_TYPE_VALUE[type] or raise "invalid type: #{type.inspect}"
108
- proc_event(code, value, event)
117
+ update_modifiers(code, value)
118
+ write_event(code, value)
109
119
  end
110
120
 
111
121
  # @param code [Integer]
112
122
  # @param mod [Hash] {MOD_KEY => true, ...}
113
123
  def key(code, mod={})
124
+ with_modifier(mod) do
125
+ write_event(code, 1)
126
+ write_event(code, 0)
127
+ end
128
+ end
129
+
130
+ # @param mod [Hash] {MOD_KEY => true, ...}
131
+ def with_modifier(mod, &block)
114
132
  mod_diff(mod).each do |mcode, state|
115
133
  write_event(mcode, state ? 1 : 0)
116
134
  end
117
- write_event(code, 1)
118
- write_event(code, 0)
135
+ block.call
119
136
  mod_diff(mod).each do |mcode, _|
120
137
  write_event(mcode, @mod_state[mcode] ? 1 : 0)
121
138
  end
@@ -123,12 +140,23 @@ class Rkremap
123
140
 
124
141
  private
125
142
 
126
- def proc_event(code, value, event)
143
+ def update_modifiers(code, value)
127
144
  if @mod_state.include?(code)
128
145
  @mod_state[code] = value != 0
146
+ end
147
+ end
148
+
149
+ # @param event [Rkremap::Event]
150
+ # @param app [Rkremap::App]
151
+ def proc_event(event, app)
152
+ code, value = event.code, event.value
153
+ if @mod_state.include?(code)
129
154
  write_event(code, value)
130
- elsif value != 0
131
- @keys.push [code, @mod_state.dup, App.new(event&.win, @winattr)]
155
+ update_modifiers(code, value)
156
+ elsif value == 0
157
+ write_event(code, value)
158
+ else
159
+ @keys.push [code, @mod_state.dup, app]
132
160
  end
133
161
  end
134
162
 
@@ -140,46 +168,46 @@ class Rkremap
140
168
  # @param value [Integer] 0:release / 1:press / 2:repeat
141
169
  def write_event(code, value)
142
170
  @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
171
  end
170
172
 
171
173
  class App
172
- def initialize(win, winattr)
173
- @win = win
174
+ # @param winattr [Rkremap::WinAttr]
175
+ def initialize(winattr)
174
176
  @winattr = winattr
177
+ @win = winattr.focus_win
175
178
  end
176
179
 
180
+ # @return [String, nil]
177
181
  def class_name
178
182
  @winattr.app_win(@win)[2] if @winattr && @win
179
183
  end
180
184
 
185
+ # @return [String, nil]
181
186
  def title
182
187
  @winattr.app_title(@win) if @winattr && @win
183
188
  end
189
+
190
+ # @param app [Array, Hash, String, Regexp]
191
+ # @return [Boolean]
192
+ def match?(app)
193
+ Array(app).each do |a|
194
+ a = {class_name: a, title: a} unless a.is_a? Hash
195
+ return true if match_sub(a[:class_name], class_name) || match_sub(a[:title], title)
196
+ end
197
+ false
198
+ end
199
+
200
+ # @param a [String, Regexp]
201
+ # @param b [String]
202
+ def match_sub(a, b)
203
+ case a
204
+ when String
205
+ a == b
206
+ when Regexp
207
+ a =~ b
208
+ else
209
+ false
210
+ end
211
+ end
184
212
  end
185
213
  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.1
4
+ version: 0.5.0
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-07-18 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-09-04 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
@@ -24,7 +38,11 @@ files:
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/remap.rb
45
+ - lib/rkremap/uinput.rb
28
46
  - lib/rkremap/version.rb
29
47
  - lib/rkremap/winattr.rb
30
48
  homepage: https://gitlab.com/tmtms/rkremap