rkremap 0.3.0.1 → 0.5.0

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.
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