fusuma-plugin-remap 0.11.2 → 0.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5eb6be3cd4ee3e56a0fa412b29381e336c0b25207ca42d716a8a124cf907a5f8
4
- data.tar.gz: d3b19a185e0cc03b7abcb00be6a8d7f63340827dafd1136adc941671457b2a80
3
+ metadata.gz: 22d4ef7f153876d648359c14463da56777ecc7878cac97f79d1e29784a6673e8
4
+ data.tar.gz: f4381c0dcc9c5277fdeb14f0a19ee49772a3a06b07adfd9a31e0f43f5fff99a2
5
5
  SHA512:
6
- metadata.gz: 593c365c6ef1ffc67a3865e188baeb8c59fa69a37aaaa3f75bad64b251d77d9abd9f914f2b90739f59dffc40b94dbb29c50fb894b8a524ba5820980ba364d8e6
7
- data.tar.gz: 4cbd18fda7bfa3a05622f82cb4d558e7d00ea4b6d1a97c7c6617003eec64e1b6cb6caabd3fcfff91ef9c6c5c957b7681004c4a234f5c3a5e75a6878ba05526ca
6
+ metadata.gz: a8111498a2c1d85caaf3a3c03af770c308f4e1b4c395a739f14e7832fd91280755b65424a9626a700b411112ab54b25a27f61a2d37aa64daf460573a3a4fe925
7
+ data.tar.gz: 54b0014a0f13b5a040f284ba8c3f87ecde1729ac0643b1fcb9d17411dfa9f1cd79ed03c8aa24dfeed2c2856df47ac76e69970eddba433b09a223bd695635edb2
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
25
25
  # support focal (20.04LTS) 2.7
26
26
 
27
- spec.add_dependency "fusuma", ">= 3.4"
27
+ spec.add_dependency "fusuma", ">= 3.11.0"
28
28
  spec.add_dependency "fusuma-plugin-keypress", ">= 0.11.0"
29
29
  spec.add_dependency "fusuma-plugin-sendkey", ">= 0.12.0"
30
30
  spec.add_dependency "msgpack"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../remap/touchpad_remapper"
4
+ require_relative "../remap/device_selector"
4
5
 
5
6
  module Fusuma
6
7
  module Plugin
@@ -46,51 +47,37 @@ module Fusuma
46
47
  private
47
48
 
48
49
  def setup_remapper
49
- source_touchpads = TouchpadSelector.new(config_params(:touchpad_name_patterns)).select
50
- if source_touchpads.empty?
51
- MultiLogger.error("No touchpad found: #{config_params(:touchpad_name_patterns)}")
52
- exit
53
- end
54
-
55
- MultiLogger.info("set up remapper")
56
- MultiLogger.info("touchpad: #{source_touchpads}")
57
-
58
50
  # layer_manager = Remap::LayerManager.instance
59
51
 
60
52
  # physical touchpad input event
61
53
  @fusuma_reader, fusuma_writer = IO.pipe
54
+ touchpad_name_patterns = config_params(:touchpad_name_patterns)
62
55
 
63
56
  fork do
64
57
  # layer_manager.writer.close
65
58
  @fusuma_reader.close
59
+
60
+ # DeviceSelector waits until touchpad is found (like KeyboardSelector)
61
+ # NOTE: This must be inside fork to avoid blocking the main Fusuma process
62
+ source_touchpads = Remap::DeviceSelector.new(
63
+ name_patterns: touchpad_name_patterns,
64
+ device_type: :touchpad
65
+ ).select(wait: true)
66
+
67
+ MultiLogger.info("set up remapper")
68
+ MultiLogger.info("touchpad: #{source_touchpads}")
69
+
66
70
  remapper = Remap::TouchpadRemapper.new(
67
71
  # layer_manager: layer_manager,
68
72
  fusuma_writer: fusuma_writer,
69
- source_touchpads: source_touchpads
73
+ source_touchpads: source_touchpads,
74
+ touchpad_name_patterns: touchpad_name_patterns
70
75
  )
71
76
  remapper.run
72
77
  end
73
78
  # layer_manager.reader.close
74
79
  fusuma_writer.close
75
80
  end
76
-
77
- class TouchpadSelector
78
- def initialize(names = nil)
79
- @names = names
80
- end
81
-
82
- # @return [Array<Revdev::EventDevice>]
83
- def select
84
- devices = if @names
85
- Fusuma::Device.all.select { |d| Array(@names).any? { |name| d.name =~ /#{name}/ } }
86
- else
87
- # available returns only touchpad devices
88
- Fusuma::Device.available
89
- end
90
-
91
- devices.map { |d| Revdev::EventDevice.new("/dev/input/#{d.id}") }
92
- end
93
- end
94
81
  end
95
82
  end
96
83
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "revdev"
4
+ require "fusuma/device"
5
+
6
+ module Fusuma
7
+ module Plugin
8
+ module Remap
9
+ # Common device selector for touchpad and keyboard detection
10
+ # Unifies TouchpadSelector implementations across the codebase
11
+ class DeviceSelector
12
+ POLL_INTERVAL = 3 # seconds
13
+
14
+ # @param name_patterns [Array, String, nil] patterns for device names
15
+ # @param device_type [Symbol] :touchpad or :keyboard (for logging)
16
+ def initialize(name_patterns: nil, device_type: :touchpad)
17
+ @name_patterns = name_patterns
18
+ @device_type = device_type
19
+ @displayed_waiting = false
20
+ end
21
+
22
+ # Select devices that match the name patterns
23
+ # @param wait [Boolean] if true, wait until device is found (polling loop)
24
+ # @return [Array<Revdev::EventDevice>]
25
+ def select(wait: false)
26
+ loop do
27
+ Fusuma::Device.reset # reset cache to get the latest device information
28
+ devices = find_devices
29
+ return to_event_devices(devices) unless devices.empty?
30
+ return [] unless wait
31
+
32
+ log_waiting_message unless @displayed_waiting
33
+ sleep POLL_INTERVAL
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def find_devices
40
+ if @name_patterns
41
+ Fusuma::Device.all.select { |d|
42
+ Array(@name_patterns).any? { |name| d.name =~ /#{name}/ }
43
+ }
44
+ else
45
+ # available returns only touchpad devices
46
+ Fusuma::Device.available
47
+ end
48
+ end
49
+
50
+ def to_event_devices(devices)
51
+ devices.map { |d| Revdev::EventDevice.new("/dev/input/#{d.id}") }
52
+ end
53
+
54
+ def log_waiting_message
55
+ MultiLogger.warn "No #{@device_type} found: #{@name_patterns || "(default patterns)"}"
56
+ MultiLogger.warn "Waiting for #{@device_type} to be connected..."
57
+ @displayed_waiting = true
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -3,6 +3,7 @@ require "msgpack"
3
3
  require "set"
4
4
  require_relative "layer_manager"
5
5
  require_relative "uinput_keyboard"
6
+ require_relative "device_selector"
6
7
  require "fusuma/device"
7
8
 
8
9
  module Fusuma
@@ -192,29 +193,36 @@ module Fusuma
192
193
 
193
194
  def create_virtual_keyboard
194
195
  touchpad_name_patterns = @config[:touchpad_name_patterns]
195
- internal_touchpad = TouchpadSelector.new(touchpad_name_patterns).select.first
196
-
197
- if internal_touchpad.nil?
198
- MultiLogger.error("No touchpad found: #{touchpad_name_patterns}")
199
- exit
200
- end
196
+ # Use DeviceSelector without wait - keyboard remap should work even without touchpad
197
+ internal_touchpad = DeviceSelector.new(
198
+ name_patterns: touchpad_name_patterns,
199
+ device_type: :touchpad
200
+ ).select(wait: false).first
201
201
 
202
202
  MultiLogger.info "Create virtual keyboard: #{VIRTUAL_KEYBOARD_NAME}"
203
203
 
204
- uinput_keyboard.create VIRTUAL_KEYBOARD_NAME,
205
- Revdev::InputId.new(
206
- # disable while typing is enabled when
207
- # - Both the keyboard and touchpad are BUS_I8042
208
- # - The touchpad and keyboard have the same vendor/product
209
- # ref: (https://wayland.freedesktop.org/libinput/doc/latest/palm-detection.html#disable-while-typing)
210
- #
211
- {
212
- bustype: Revdev::BUS_I8042,
213
- vendor: internal_touchpad.device_id.vendor,
214
- product: internal_touchpad.device_id.product,
215
- version: internal_touchpad.device_id.version
216
- }
217
- )
204
+ if internal_touchpad.nil?
205
+ MultiLogger.warn("No touchpad found: #{touchpad_name_patterns}")
206
+ MultiLogger.warn("Disable-while-typing feature will not work without a touchpad")
207
+ # Create virtual keyboard without touchpad device ID
208
+ # disable-while-typing will not work in this case
209
+ uinput_keyboard.create VIRTUAL_KEYBOARD_NAME
210
+ else
211
+ uinput_keyboard.create VIRTUAL_KEYBOARD_NAME,
212
+ Revdev::InputId.new(
213
+ # disable while typing is enabled when
214
+ # - Both the keyboard and touchpad are BUS_I8042
215
+ # - The touchpad and keyboard have the same vendor/product
216
+ # ref: (https://wayland.freedesktop.org/libinput/doc/latest/palm-detection.html#disable-while-typing)
217
+ #
218
+ {
219
+ bustype: Revdev::BUS_I8042,
220
+ vendor: internal_touchpad.device_id.vendor,
221
+ product: internal_touchpad.device_id.product,
222
+ version: internal_touchpad.device_id.version
223
+ }
224
+ )
225
+ end
218
226
  end
219
227
 
220
228
  def grab_keyboards(keyboards)
@@ -395,24 +403,6 @@ module Fusuma
395
403
  sleep 3
396
404
  end
397
405
  end
398
-
399
- class TouchpadSelector
400
- def initialize(names = nil)
401
- @names = names
402
- end
403
-
404
- # @return [Array<Revdev::EventDevice>]
405
- def select
406
- devices = if @names
407
- Fusuma::Device.all.select { |d| Array(@names).any? { |name| d.name =~ /#{name}/ } }
408
- else
409
- # available returns only touchpad devices
410
- Fusuma::Device.available
411
- end
412
-
413
- devices.map { |d| Revdev::EventDevice.new("/dev/input/#{d.id}") }
414
- end
415
- end
416
406
  end
417
407
  end
418
408
  end
@@ -3,6 +3,8 @@ require "msgpack"
3
3
  require "set"
4
4
 
5
5
  require_relative "uinput_touchpad"
6
+ require_relative "device_selector"
7
+ require "fusuma/device"
6
8
 
7
9
  module Fusuma
8
10
  module Plugin
@@ -14,9 +16,11 @@ module Fusuma
14
16
 
15
17
  # @param fusuma_writer [IO]
16
18
  # @param source_touchpads [Revdev::Device]
17
- def initialize(fusuma_writer:, source_touchpads:)
19
+ # @param touchpad_name_patterns [Array, String, nil] patterns for touchpad device names (for reconnection)
20
+ def initialize(fusuma_writer:, source_touchpads:, touchpad_name_patterns: nil)
18
21
  @source_touchpads = source_touchpads # original touchpad
19
22
  @fusuma_writer = fusuma_writer # write event to fusuma_input
23
+ @touchpad_name_patterns = touchpad_name_patterns # for reconnection
20
24
 
21
25
  @palm_detectors = @source_touchpads.each_with_object({}) do |source_touchpad, palm_detectors|
22
26
  palm_detectors[source_touchpad] = PalmDetection.new(source_touchpad)
@@ -157,7 +161,19 @@ module Fusuma
157
161
  prev_status = status
158
162
  prev_valid_touch = valid_touch
159
163
  end
164
+ rescue Errno::ENODEV => e
165
+ MultiLogger.error "Touchpad device is removed: #{e.message}"
166
+ MultiLogger.info "Waiting for touchpad to reconnect..."
167
+ reload_touchpads
168
+ touch_state = {}
169
+ mt_slot = 0
170
+ finger_state = nil
171
+ prev_valid_touch = false
172
+ prev_status = nil
173
+ retry
160
174
  end
175
+ rescue IOError => e
176
+ MultiLogger.error "Touchpad IO error: #{e.message}"
161
177
  rescue => e
162
178
  MultiLogger.error "An error occurred: #{e.message}"
163
179
  ensure
@@ -176,6 +192,34 @@ module Fusuma
176
192
  uinput.create_from_device(name: VIRTUAL_TOUCHPAD_NAME, device: @source_touchpads.first)
177
193
  end
178
194
 
195
+ # Reload touchpads after device disconnection
196
+ # This method waits until a touchpad is reconnected
197
+ def reload_touchpads
198
+ # Destroy virtual touchpad
199
+ begin
200
+ uinput.destroy
201
+ rescue IOError
202
+ # already destroyed
203
+ end
204
+ @uinput = nil
205
+
206
+ # Wait and detect touchpad using DeviceSelector
207
+ @source_touchpads = DeviceSelector.new(
208
+ name_patterns: @touchpad_name_patterns,
209
+ device_type: :touchpad
210
+ ).select(wait: true)
211
+
212
+ # Reinitialize palm detectors
213
+ @palm_detectors = @source_touchpads.each_with_object({}) do |source_touchpad, palm_detectors|
214
+ palm_detectors[source_touchpad] = PalmDetection.new(source_touchpad)
215
+ end
216
+
217
+ # Recreate virtual touchpad
218
+ create_virtual_touchpad
219
+
220
+ MultiLogger.info "Touchpad reconnected: #{@source_touchpads}"
221
+ end
222
+
179
223
  def set_trap
180
224
  @destroy = lambda do
181
225
  begin
@@ -3,7 +3,7 @@
3
3
  module Fusuma
4
4
  module Plugin
5
5
  module Remap
6
- VERSION = "0.11.2"
6
+ VERSION = "0.12.0"
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.2
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - iberianpig
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-14 00:00:00.000000000 Z
11
+ date: 2026-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fusuma
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.4'
19
+ version: 3.11.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '3.4'
26
+ version: 3.11.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: fusuma-plugin-keypress
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +120,7 @@ files:
120
120
  - lib/fusuma/plugin/inputs/remap_touchpad_input.rb
121
121
  - lib/fusuma/plugin/inputs/remap_touchpad_input.yml
122
122
  - lib/fusuma/plugin/remap.rb
123
+ - lib/fusuma/plugin/remap/device_selector.rb
123
124
  - lib/fusuma/plugin/remap/keyboard_remapper.rb
124
125
  - lib/fusuma/plugin/remap/layer_manager.rb
125
126
  - lib/fusuma/plugin/remap/touchpad_remapper.rb