fusuma-plugin-remap 0.11.0 → 0.11.2
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/lib/fusuma/plugin/remap/keyboard_remapper.rb +98 -47
- data/lib/fusuma/plugin/remap/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5eb6be3cd4ee3e56a0fa412b29381e336c0b25207ca42d716a8a124cf907a5f8
|
4
|
+
data.tar.gz: d3b19a185e0cc03b7abcb00be6a8d7f63340827dafd1136adc941671457b2a80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 593c365c6ef1ffc67a3865e188baeb8c59fa69a37aaaa3f75bad64b251d77d9abd9f914f2b90739f59dffc40b94dbb29c50fb894b8a524ba5820980ba364d8e6
|
7
|
+
data.tar.gz: 4cbd18fda7bfa3a05622f82cb4d558e7d00ea4b6d1a97c7c6617003eec64e1b6cb6caabd3fcfff91ef9c6c5c957b7681004c4a234f5c3a5e75a6878ba05526ca
|
@@ -12,6 +12,15 @@ module Fusuma
|
|
12
12
|
include Revdev
|
13
13
|
|
14
14
|
VIRTUAL_KEYBOARD_NAME = "fusuma_virtual_keyboard"
|
15
|
+
DEFAULT_EMERGENCY_KEYBIND = "RIGHTCTRL+LEFTCTRL".freeze
|
16
|
+
|
17
|
+
# Key conversion tables for better performance and readability
|
18
|
+
KEYMAP = Revdev.constants.select { |c| c.start_with?("KEY_", "BTN_") }
|
19
|
+
.map { |c| [Revdev.const_get(c), c.to_s.delete_prefix("KEY_")] }
|
20
|
+
.to_h.freeze
|
21
|
+
CODEMAP = Revdev.constants.select { |c| c.start_with?("KEY_", "BTN_") }
|
22
|
+
.map { |c| [c, Revdev.const_get(c)] }
|
23
|
+
.to_h.freeze
|
15
24
|
|
16
25
|
# @param layer_manager [Fusuma::Plugin::Remap::LayerManager]
|
17
26
|
# @param fusuma_writer [IO]
|
@@ -53,7 +62,7 @@ module Fusuma
|
|
53
62
|
end
|
54
63
|
|
55
64
|
input_event = @source_keyboards.find { |kbd| kbd.file == io }.read_input_event
|
56
|
-
input_key =
|
65
|
+
input_key = code_to_key(input_event.code)
|
57
66
|
|
58
67
|
if input_event.type == EV_KEY
|
59
68
|
@emergency_stop.call(old_ie, input_event)
|
@@ -61,23 +70,50 @@ module Fusuma
|
|
61
70
|
old_ie = input_event
|
62
71
|
if input_event.value != 2 # repeat
|
63
72
|
data = {key: input_key, status: input_event.value, layer: layer}
|
64
|
-
|
73
|
+
begin
|
74
|
+
@fusuma_writer.write(data.to_msgpack)
|
75
|
+
rescue IOError => e
|
76
|
+
MultiLogger.error("Failed to write to fusuma_writer: #{e.message}")
|
77
|
+
@destroy&.call(1)
|
78
|
+
return
|
79
|
+
end
|
65
80
|
end
|
66
81
|
end
|
67
82
|
|
68
83
|
remapped = current_mapping.fetch(input_key.to_sym, nil)
|
69
|
-
|
84
|
+
case remapped
|
85
|
+
when String, Symbol
|
86
|
+
# Remapped to another key - continue processing below
|
87
|
+
when Hash
|
88
|
+
# Command execution (e.g., {:SENDKEY=>"LEFTCTRL+BTN_LEFT", :CLEARMODIFIERS=>true})
|
89
|
+
# Skip input event processing and let Fusuma's Executor handle this
|
90
|
+
next
|
91
|
+
when nil
|
92
|
+
# Not remapped - write original key event as-is
|
93
|
+
uinput_keyboard.write_input_event(input_event)
|
94
|
+
next
|
95
|
+
else
|
96
|
+
# Invalid remapping configuration
|
97
|
+
MultiLogger.warn("Invalid remapped value - type: #{remapped.class}, key: #{input_key}")
|
98
|
+
next
|
99
|
+
end
|
100
|
+
|
101
|
+
remapped_code = key_to_code(remapped)
|
102
|
+
if remapped_code.nil?
|
103
|
+
MultiLogger.warn("Invalid remapped value - unknown key: #{remapped}, input: #{input_key}")
|
70
104
|
uinput_keyboard.write_input_event(input_event)
|
71
105
|
next
|
72
106
|
end
|
73
107
|
|
74
|
-
remapped_event = InputEvent.new(nil, input_event.type,
|
108
|
+
remapped_event = InputEvent.new(nil, input_event.type, remapped_code, input_event.value)
|
75
109
|
|
76
|
-
# Workaround
|
77
|
-
#
|
78
|
-
|
79
|
-
# set original key before remapping
|
110
|
+
# Workaround: If a key was pressed before remapping started and is being released,
|
111
|
+
# use the original key code to ensure proper key release
|
112
|
+
if should_use_original_key?(remapped, remapped_event.value)
|
80
113
|
remapped_event.code = input_event.code
|
114
|
+
else
|
115
|
+
# Only update virtual key state if we're using the remapped key
|
116
|
+
update_virtual_key_state(remapped, remapped_event.value)
|
81
117
|
end
|
82
118
|
|
83
119
|
# remap to command will be nil
|
@@ -119,21 +155,34 @@ module Fusuma
|
|
119
155
|
@pressed_virtual_keys ||= Set.new
|
120
156
|
end
|
121
157
|
|
122
|
-
#
|
158
|
+
# Update virtual keyboard key state
|
159
|
+
# @param [String] remapped_value remapped key name
|
160
|
+
# @param [Integer] event_value event value (0: release, 1: press, 2: repeat)
|
161
|
+
# @return [void]
|
162
|
+
def update_virtual_key_state(remapped_value, event_value)
|
163
|
+
case event_value
|
164
|
+
when 0 # key release
|
165
|
+
pressed_virtual_keys.delete(remapped_value)
|
166
|
+
when 1 # key press
|
167
|
+
pressed_virtual_keys.add(remapped_value)
|
168
|
+
# when 2 is repeat - no state change needed
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Check if we should use the original key code instead of remapped key
|
173
|
+
# This handles the case where a key was pressed before remapping started
|
174
|
+
# and is released after remapping
|
123
175
|
# @param [String] remapped_value remapped key name
|
124
|
-
# @param [Integer] event_value event value
|
125
|
-
# @return [Boolean]
|
126
|
-
|
127
|
-
def record_virtual_keyboard_event?(remapped_value, event_value)
|
176
|
+
# @param [Integer] event_value event value (0: release, 1: press, 2: repeat)
|
177
|
+
# @return [Boolean] true if we should use original key code
|
178
|
+
def should_use_original_key?(remapped_value, event_value)
|
128
179
|
case event_value
|
129
|
-
when 0
|
130
|
-
|
131
|
-
|
132
|
-
pressed_virtual_keys.
|
133
|
-
|
134
|
-
|
135
|
-
# 2 is repeat
|
136
|
-
true
|
180
|
+
when 0 # key release
|
181
|
+
# If the key was not in our pressed set, it means it was pressed
|
182
|
+
# before remapping started, so we should use original key
|
183
|
+
!pressed_virtual_keys.include?(remapped_value)
|
184
|
+
when 1, 2 # key press or repeat
|
185
|
+
false # Always use remapped key for press/repeat events
|
137
186
|
end
|
138
187
|
end
|
139
188
|
|
@@ -209,22 +258,22 @@ module Fusuma
|
|
209
258
|
# TODO: Extract to a configuration file or make it optional
|
210
259
|
# it should stop other remappers
|
211
260
|
if keybinds&.size != 2
|
212
|
-
MultiLogger.
|
213
|
-
MultiLogger.
|
214
|
-
MultiLogger.
|
261
|
+
MultiLogger.warn "Invalid emergency ungrab keybinds: #{keybinds}, fallback to #{DEFAULT_EMERGENCY_KEYBIND}"
|
262
|
+
MultiLogger.warn "Please set two keys separated by '+'"
|
263
|
+
MultiLogger.warn <<~YAML
|
215
264
|
plugin:
|
216
265
|
inputs:
|
217
266
|
remap_keyboard_input:
|
218
267
|
emergency_ungrab_keys: RIGHTCTRL+LEFTCTRL
|
219
268
|
YAML
|
220
269
|
|
221
|
-
|
270
|
+
keybinds = DEFAULT_EMERGENCY_KEYBIND.split("+")
|
222
271
|
end
|
223
272
|
|
224
273
|
MultiLogger.info "Emergency ungrab keybind: #{keybinds[0]}+#{keybinds[1]}"
|
225
274
|
|
226
|
-
first_keycode =
|
227
|
-
second_keycode =
|
275
|
+
first_keycode = key_to_code(keybinds[0])
|
276
|
+
second_keycode = key_to_code(keybinds[1])
|
228
277
|
|
229
278
|
@emergency_stop = lambda do |prev, current|
|
230
279
|
if (prev&.code == first_keycode && prev.value != 0) && (current.code == second_keycode && current.value != 0)
|
@@ -246,48 +295,45 @@ module Fusuma
|
|
246
295
|
# @param [Integer] code
|
247
296
|
# @return [Integer, nil]
|
248
297
|
def find_remapped_code(mapping, code)
|
249
|
-
key =
|
298
|
+
key = code_to_key(code) # key = "A"
|
250
299
|
remapped_key = mapping.fetch(key.to_sym, nil) # remapped_key = "b"
|
251
300
|
return code unless remapped_key # return original code if key is not found
|
252
301
|
|
253
|
-
|
302
|
+
key_to_code(remapped_key) # remapped_code = 48
|
254
303
|
end
|
255
304
|
|
256
305
|
# Find key name from key code
|
257
306
|
# @example
|
258
|
-
#
|
259
|
-
#
|
307
|
+
# code_to_key(30) # => "A"
|
308
|
+
# code_to_key(48) # => "B"
|
309
|
+
# code_to_key(272) # => "BTN_LEFT"
|
260
310
|
# @param [Integer] code
|
261
311
|
# @return [String]
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
@keys_per_code[code]
|
312
|
+
# @return [nil] when key is not found
|
313
|
+
def code_to_key(code)
|
314
|
+
KEYMAP[code]
|
266
315
|
end
|
267
316
|
|
268
317
|
# Find key code from key name (e.g. "A", "B", "BTN_LEFT")
|
269
318
|
# If key name is not found, return nil
|
270
319
|
# @example
|
271
|
-
#
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
320
|
+
# key_to_code("A") # => 30
|
321
|
+
# key_to_code("B") # => 48
|
322
|
+
# key_to_code("BTN_LEFT") # => 272
|
323
|
+
# key_to_code("NOT_FOUND") # => nil
|
275
324
|
# @param [String] key
|
276
325
|
# @return [Integer] when key is available
|
277
326
|
# @return [nil] when key is not available
|
278
|
-
def
|
279
|
-
# { KEY_A => 30, KEY_B => 48, ... }
|
280
|
-
@codes_per_key ||= Revdev.constants.select { |c| c.start_with?("KEY_", "BTN_") }.map { |c| [c, Revdev.const_get(c)] }.to_h
|
281
|
-
|
327
|
+
def key_to_code(key)
|
282
328
|
case key
|
283
329
|
when String
|
284
330
|
if key.start_with?("BTN_")
|
285
|
-
|
331
|
+
CODEMAP[key.upcase.to_sym]
|
286
332
|
else
|
287
|
-
|
333
|
+
CODEMAP["KEY_#{key}".upcase.to_sym]
|
288
334
|
end
|
289
335
|
when Integer
|
290
|
-
|
336
|
+
CODEMAP["KEY_#{key}".upcase.to_sym]
|
291
337
|
end
|
292
338
|
end
|
293
339
|
|
@@ -303,7 +349,12 @@ module Fusuma
|
|
303
349
|
break true
|
304
350
|
else
|
305
351
|
# wait until all keys are released
|
306
|
-
|
352
|
+
begin
|
353
|
+
device.read_input_event
|
354
|
+
rescue Errno::ENODEV => e
|
355
|
+
MultiLogger.warn("Device removed while waiting to release keys: #{e.message}")
|
356
|
+
return false
|
357
|
+
end
|
307
358
|
end
|
308
359
|
end
|
309
360
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fusuma-plugin-remap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iberianpig
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fusuma
|