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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b55e5ef616cbf48bc0a8c3a4c0b9b91dbed01c3b4dc87f53f09fa51ab6c34227
4
- data.tar.gz: 1e34c8882e52d3191092576ab5d056d74c1ea0d130963a37304ce2a8bc6b0fdf
3
+ metadata.gz: 5eb6be3cd4ee3e56a0fa412b29381e336c0b25207ca42d716a8a124cf907a5f8
4
+ data.tar.gz: d3b19a185e0cc03b7abcb00be6a8d7f63340827dafd1136adc941671457b2a80
5
5
  SHA512:
6
- metadata.gz: bb30877c6629814e6aff80127aa63045e4901d416a568d1a6f0e7f268254509903df5df8d90382a7d739ea3b2a6b666a461f57d9af0ce00941152d2296953efd
7
- data.tar.gz: f11178d8bf7c553a1568b67e1a62b4cb747d51c0b67008330b98b17228b82a5d41cecd5b4de2f259d2d572f539e4f55daaa75f79fdd7c3a2608ac4013a2473f7
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 = find_key_from_code(input_event.code)
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
- @fusuma_writer.write(data.to_msgpack)
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
- if remapped.nil?
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, find_code_from_key(remapped), input_event.value)
108
+ remapped_event = InputEvent.new(nil, input_event.type, remapped_code, input_event.value)
75
109
 
76
- # Workaround to solve the problem that the remapped key remains pressed
77
- # when the key pressed before remapping is released after remapping
78
- unless record_virtual_keyboard_event?(remapped, remapped_event.value)
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
- # record virtual keyboard event
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] false if the key was pressed before remapping started and was released
126
- # @return [Boolean] true if the key was not pressed before remapping started
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
- pressed_virtual_keys.delete?(remapped_value)
131
- when 1
132
- pressed_virtual_keys.add?(remapped_value)
133
- true # Always return true because the remapped key may be the same
134
- else
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.error "Invalid emergency ungrab keybinds: #{keybinds}"
213
- MultiLogger.error "Please set two keys separated by '+'"
214
- MultiLogger.error <<~YAML
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
- exit 1
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 = find_code_from_key(keybinds[0])
227
- second_keycode = find_code_from_key(keybinds[1])
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 = find_key_from_code(code) # key = "A"
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
- find_code_from_key(remapped_key) # remapped_code = 48
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
- # find_key_from_code(30) # => "A"
259
- # find_key_from_code(48) # => "B"
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
- def find_key_from_code(code)
263
- # { 30 => :A, 48 => :B, ... }
264
- @keys_per_code ||= Revdev.constants.select { |c| c.start_with? "KEY_" }.map { |c| [Revdev.const_get(c), c.to_s.delete_prefix("KEY_")] }.to_h
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
- # find_code_from_key("A") # => 30
272
- # find_code_from_key("B") # => 48
273
- # find_code_from_key("BTN_LEFT") # => 272
274
- # find_code_from_key("NOT_FOUND") # => nil
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 find_code_from_key(key)
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
- @codes_per_key[key.upcase.to_sym]
331
+ CODEMAP[key.upcase.to_sym]
286
332
  else
287
- @codes_per_key["KEY_#{key}".upcase.to_sym]
333
+ CODEMAP["KEY_#{key}".upcase.to_sym]
288
334
  end
289
335
  when Integer
290
- @codes_per_key["KEY_#{key}".upcase.to_sym]
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
- device.read_input_event
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
@@ -3,7 +3,7 @@
3
3
  module Fusuma
4
4
  module Plugin
5
5
  module Remap
6
- VERSION = "0.11.0"
6
+ VERSION = "0.11.2"
7
7
  end
8
8
  end
9
9
  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.0
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-05-26 00:00:00.000000000 Z
11
+ date: 2025-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fusuma