fusuma-plugin-remap 0.9.0 → 0.11.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: 71eb89bd5884a491b87dc36ad65728e1069c9073ad9b1971ae989ed3cfad2e3f
4
- data.tar.gz: 43dcf898d9ef9775e0d42b037f1e87daa0d1d36b7a367bcabb7211ce05300e5f
3
+ metadata.gz: b55e5ef616cbf48bc0a8c3a4c0b9b91dbed01c3b4dc87f53f09fa51ab6c34227
4
+ data.tar.gz: 1e34c8882e52d3191092576ab5d056d74c1ea0d130963a37304ce2a8bc6b0fdf
5
5
  SHA512:
6
- metadata.gz: 8c5dd2a0bb627288f3ab2ba47f838f57022feb9786e0e99818a17201c57a7ca35a536b852200e44a7e700a86482cc17daf71594432cc89c6f22d7c90563301b8
7
- data.tar.gz: 3631827a2d5eb1924adfee909f5d1cc721554089f4c2350e33cb063a83dcafb1d282a1b635c5945a2b2e7c525e16692c39c105fc76b47700b3f714162c06b07b
6
+ metadata.gz: bb30877c6629814e6aff80127aa63045e4901d416a568d1a6f0e7f268254509903df5df8d90382a7d739ea3b2a6b666a461f57d9af0ce00941152d2296953efd
7
+ data.tar.gz: f11178d8bf7c553a1568b67e1a62b4cb747d51c0b67008330b98b17228b82a5d41cecd5b4de2f259d2d572f539e4f55daaa75f79fdd7c3a2608ac4013a2473f7
data/README.md CHANGED
@@ -86,6 +86,76 @@ plugin:
86
86
  This configuration allows you to specify which keys will trigger the emergency stop functionality.
87
87
  It is important to verify this keybind to ensure a swift response during unexpected situations.
88
88
 
89
+ ### Input Device Detection
90
+
91
+ #### Keyboard
92
+
93
+ configure `plugin.inputs.remap_keyboard_input` in `~/.config/fusuma/config.yml` to specify which physical keyboard to remap.
94
+
95
+ If your external or built-in keyboard is not detected, run `libinput list-devices` to find its name and add a matching pattern under `keyboard_name_patterns`.
96
+
97
+ ```yaml
98
+ plugin:
99
+ inputs:
100
+ remap_keyboard_input:
101
+ # By default, Fusuma will detect physical keyboards matching these patterns.
102
+ # You can specify multiple regular‐expression strings in an array.
103
+ keyboard_name_patterns:
104
+ # Default value
105
+ - keyboard|Keyboard|KEYBOARD
106
+
107
+ # Emergency stop key combination.
108
+ # Specify exactly two keys joined by '+'.
109
+ emergency_ungrab_keys: RIGHTCTRL+LEFTCTRL
110
+ ```
111
+
112
+ You can customize `keyboard_name_patterns` like this:
113
+
114
+ ```yaml
115
+ plugin:
116
+ inputs:
117
+ remap_keyboard_input:
118
+ keyboard_name_patterns:
119
+ - xremap # Virtual keyboard created by another remapper
120
+ - PFU Limited HHKB-Hybrid # External keyboard
121
+ - keyboard|Keyboard|KEYBOARD # Default pattern
122
+ ```
123
+
124
+ If your keyboard isn’t detected, run:
125
+
126
+ ```sh
127
+ libinput list-devices
128
+ ```
129
+
130
+ and add a suitable name pattern.
131
+
132
+ #### Touchpad
133
+
134
+ To specify touchpad name, configure `plugin.inputs.remap_touchpad_input`:
135
+
136
+ ```yaml
137
+ plugin:
138
+ inputs:
139
+ remap_touchpad_input:
140
+ # By default, Fusuma will detect physical touchpads matching these patterns.
141
+ touchpad_name_patterns:
142
+ # Default values
143
+ - touchpad|Touchpad|TOUCHPAD
144
+ - trackpad|Trackpad|TRACKPAD
145
+ ```
146
+
147
+ You can customize `touchpad_name_patterns` like this:
148
+
149
+ ```yaml
150
+ plugin:
151
+ inputs:
152
+ remap_touchpad_input:
153
+ touchpad_name_patterns:
154
+ - Apple Inc. Magic Trackpad # External Trackpad
155
+ - your touchpad device name # Any other touchpad
156
+ - Touchpad|Trackpad # match to "Touchpad" or "Trackpad"
157
+ ```
158
+
89
159
  ## Contributing
90
160
 
91
161
  Bug reports and pull requests are welcome on GitHub at https://github.com/iberianpig/fusuma-plugin-remap. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fusuma/device"
4
3
  require_relative "../remap/keyboard_remapper"
5
4
  require_relative "../remap/layer_manager"
6
5
 
@@ -14,8 +13,7 @@ module Fusuma
14
13
  def config_param_types
15
14
  {
16
15
  emergency_ungrab_keys: [String],
17
- keyboard_name_patterns: [Array, String],
18
- touchpad_name_patterns: [Array, String]
16
+ keyboard_name_patterns: [Array, String]
19
17
  }
20
18
  end
21
19
 
@@ -49,8 +47,7 @@ module Fusuma
49
47
  def setup_remapper
50
48
  config = {
51
49
  emergency_ungrab_keys: config_params(:emergency_ungrab_keys),
52
- keyboard_name_patterns: config_params(:keyboard_name_patterns),
53
- touchpad_name_patterns: config_params(:touchpad_name_patterns)
50
+ keyboard_name_patterns: config_params(:keyboard_name_patterns)
54
51
  }
55
52
 
56
53
  layer_manager = Remap::LayerManager.instance
@@ -1,7 +1,8 @@
1
1
  plugin:
2
2
  inputs:
3
3
  remap_keyboard_input:
4
- keyboard_name_patterns: ["keyboard", "Keyboard", "KEYBOARD"]
4
+ keyboard_name_patterns:
5
+ - keyboard|Keyboard|KEYBOARD
5
6
  emergency_ungrab_keys: RIGHTCTRL+LEFTCTRL
6
7
  buffers:
7
8
  keypress_buffer:
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fusuma/device"
4
3
  require_relative "../remap/touchpad_remapper"
5
- # require_relative "../remap/layer_manager"
6
4
 
7
5
  module Fusuma
8
6
  module Plugin
@@ -36,22 +34,7 @@ module Fusuma
36
34
 
37
35
  gesture = "touch"
38
36
  finger = data["finger"]
39
-
40
- # @touch_state ||= {}
41
- # @mt_slot ||= 0
42
- # @touch_state[@mt_slot] ||= {
43
- # MT_TRACKING_ID: nil,
44
- # X: nil,
45
- # Y: nil,
46
- # valid_touch_point: false
47
- # }
48
- # TODO: implement update touch_state
49
- status =
50
- if data["touch_state"].any? { |_, v| v["valid_touch_point"] }
51
- "begin"
52
- else
53
- "end"
54
- end
37
+ status = data["status"]
55
38
 
56
39
  Events::Records::GestureRecord.new(gesture: gesture, status: status, finger: finger, delta: nil)
57
40
  rescue EOFError => e
@@ -63,14 +46,14 @@ module Fusuma
63
46
  private
64
47
 
65
48
  def setup_remapper
66
- internal_touchpad = TouchpadSelector.new(config_params(:touchpad_name_patterns)).select.first
67
- if internal_touchpad.nil?
49
+ source_touchpads = TouchpadSelector.new(config_params(:touchpad_name_patterns)).select
50
+ if source_touchpads.empty?
68
51
  MultiLogger.error("No touchpad found: #{config_params(:touchpad_name_patterns)}")
69
52
  exit
70
53
  end
71
54
 
72
55
  MultiLogger.info("set up remapper")
73
- MultiLogger.info("internal_touchpad: #{internal_touchpad.device_name}")
56
+ MultiLogger.info("touchpad: #{source_touchpads}")
74
57
 
75
58
  # layer_manager = Remap::LayerManager.instance
76
59
 
@@ -83,7 +66,7 @@ module Fusuma
83
66
  remapper = Remap::TouchpadRemapper.new(
84
67
  # layer_manager: layer_manager,
85
68
  fusuma_writer: fusuma_writer,
86
- source_touchpad: internal_touchpad
69
+ source_touchpads: source_touchpads
87
70
  )
88
71
  remapper.run
89
72
  end
@@ -1,4 +1,6 @@
1
1
  plugin:
2
2
  inputs:
3
3
  remap_touchpad_input:
4
- touchpad_name_patterns: ["touchpad", "Touchpad", "TOUCHPAD"]
4
+ touchpad_name_patterns:
5
+ - touchpad|Touchpad|TOUCHPAD
6
+ - trackpad|Trackpad|TRACKPAD
@@ -3,6 +3,7 @@ require "msgpack"
3
3
  require "set"
4
4
  require_relative "layer_manager"
5
5
  require_relative "uinput_keyboard"
6
+ require "fusuma/device"
6
7
 
7
8
  module Fusuma
8
9
  module Plugin
@@ -181,7 +182,7 @@ module Fusuma
181
182
 
182
183
  # @param [Array<Revdev::EventDevice>] keyboards
183
184
  def set_trap(keyboards)
184
- @destroy = lambda do
185
+ @destroy = lambda do |status = 0|
185
186
  keyboards.each do |kbd|
186
187
  kbd.ungrab
187
188
  rescue Errno::EINVAL
@@ -195,11 +196,11 @@ module Fusuma
195
196
  # already destroyed
196
197
  end
197
198
 
198
- exit 0
199
+ exit status
199
200
  end
200
201
 
201
202
  Signal.trap(:INT) { @destroy.call }
202
- Signal.trap(:TERM) { @destroy.call }
203
+ Signal.trap(:TERM) { @destroy.call(1) }
203
204
  end
204
205
 
205
206
  # Emergency stop keybind for virtual keyboard
@@ -309,7 +310,7 @@ module Fusuma
309
310
 
310
311
  # Devices to detect key presses and releases
311
312
  class KeyboardSelector
312
- def initialize(names = ["keyboard", "Keyboard", "KEYBOARD"])
313
+ def initialize(names)
313
314
  @names = names
314
315
  end
315
316
 
@@ -13,11 +13,14 @@ module Fusuma
13
13
  VIRTUAL_TOUCHPAD_NAME = "fusuma_virtual_touchpad"
14
14
 
15
15
  # @param fusuma_writer [IO]
16
- # @param source_touchpad [Revdev::Device]
17
- def initialize(fusuma_writer:, source_touchpad:)
18
- @source_touchpad = source_touchpad # original touchpad
16
+ # @param source_touchpads [Revdev::Device]
17
+ def initialize(fusuma_writer:, source_touchpads:)
18
+ @source_touchpads = source_touchpads # original touchpad
19
19
  @fusuma_writer = fusuma_writer # write event to fusuma_input
20
- @palm_detector ||= PalmDetection.new(source_touchpad)
20
+
21
+ @palm_detectors = @source_touchpads.each_with_object({}) do |source_touchpad, palm_detectors|
22
+ palm_detectors[source_touchpad] = PalmDetection.new(source_touchpad)
23
+ end
21
24
 
22
25
  set_trap
23
26
  end
@@ -30,8 +33,14 @@ module Fusuma
30
33
  touch_state = {}
31
34
  mt_slot = 0
32
35
  finger_state = nil
36
+
37
+ prev_valid_touch = false
38
+ prev_status = nil
33
39
  loop do
34
- IO.select([@source_touchpad.file]) # , @layer_manager.reader])
40
+ ios = IO.select(@source_touchpads.map(&:file)) # , @layer_manager.reader])
41
+ io = ios&.first&.first
42
+
43
+ touchpad = @source_touchpads.find { |t| t.file == io }
35
44
 
36
45
  ## example of input_event
37
46
  # Event: time 1698456258.380027, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 43679
@@ -51,7 +60,7 @@ module Fusuma
51
60
  # Event: time 1698456258.382693, type 1 (EV_KEY), code 333 (BTN_TOOL_DOUBLETAP), value 1
52
61
  # Event: time 1698456258.382693, type 4 (EV_MSC), code 5 (MSC_TIMESTAMP), value 7100
53
62
  # Event: time 1698456258.382693, -------------- SYN_REPORT ------------
54
- input_event = @source_touchpad.read_input_event
63
+ input_event = touchpad.read_input_event
55
64
 
56
65
  touch_state[mt_slot] ||= {MT_TRACKING_ID: nil, X: nil, Y: nil, valid_touch_point: false}
57
66
  syn_report = nil
@@ -71,14 +80,16 @@ module Fusuma
71
80
  touch_state[mt_slot][:X] = input_event.value
72
81
  when Revdev::ABS_MT_POSITION_Y
73
82
  touch_state[mt_slot][:Y] = input_event.value
74
- when Revdev::ABS_X, Revdev::ABS_Y
75
- # ignore
76
- when Revdev::ABS_MT_PRESSURE
77
- # ignore
78
- when Revdev::ABS_MT_TOOL_TYPE
83
+ when Revdev::ABS_X, Revdev::ABS_Y,
84
+ Revdev::ABS_MT_PRESSURE,
85
+ Revdev::ABS_MT_TOOL_TYPE,
86
+ Revdev::ABS_MT_TOUCH_MAJOR,
87
+ Revdev::ABS_MT_TOUCH_MINOR,
88
+ Revdev::ABS_MT_ORIENTATION,
89
+ Revdev::ABS_PRESSURE
79
90
  # ignore
80
91
  else
81
- raise "unhandled event"
92
+ MultiLogger.warn "unhandled event: #{input_event.hr_type}, #{input_event.hr_code}, #{input_event.value}"
82
93
  end
83
94
  when Revdev::EV_KEY
84
95
  case input_event.code
@@ -108,23 +119,43 @@ module Fusuma
108
119
  when Revdev::SYN_DROPPED
109
120
  MultiLogger.error "Dropped: #{input_event.value}"
110
121
  else
111
- raise "unhandled event", "#{input_event.hr_type}, #{input_event.hr_code}, #{input_event.value}"
122
+ raise "unhandled event: #{input_event.hr_type}, #{input_event.hr_code}, #{input_event.value}"
112
123
  end
113
124
  else
114
- raise "unhandled event", "#{input_event.hr_type}, #{input_event.hr_code}, #{input_event.value}"
125
+ raise "unhandled event: #{input_event.hr_type}, #{input_event.hr_code}, #{input_event.value}"
115
126
  end
116
127
 
117
128
  # TODO:
118
129
  # Remember the most recent valid touch position and exclude it if it is close to that position
119
130
  # For example, when dragging, it is possible to touch around the edge of the touchpad again after reaching the edge of the touchpad, so in that case, you do not want to execute palm detection
120
131
  if touch_state[mt_slot][:valid_touch_point] != true
121
- touch_state[mt_slot][:valid_touch_point] = @palm_detector.palm?(touch_state[mt_slot])
132
+ touch_state[mt_slot][:valid_touch_point] = @palm_detectors[touchpad].palm?(touch_state[mt_slot])
122
133
  end
123
134
 
124
135
  if syn_report
136
+ # Whether any slot is valid (touching)
137
+ valid_touch = touch_state.any? { |_, st| st[:valid_touch_point] }
138
+
139
+ status =
140
+ if valid_touch
141
+ prev_valid_touch ? "update" : "begin"
142
+ else
143
+ prev_valid_touch ? "end" : "cancelled"
144
+ end
145
+
146
+ if status == prev_status
147
+ next
148
+ end
149
+
125
150
  # TODO: define format as fusuma_input
126
- data = {finger: finger_state, touch_state: touch_state}
151
+ # TODO: Add data to identify multiple touchpads
152
+ data = {
153
+ finger: finger_state,
154
+ status: status
155
+ }
127
156
  @fusuma_writer.write(data.to_msgpack)
157
+ prev_status = status
158
+ prev_valid_touch = valid_touch
128
159
  end
129
160
  end
130
161
  rescue => e
@@ -141,7 +172,8 @@ module Fusuma
141
172
 
142
173
  def create_virtual_touchpad
143
174
  MultiLogger.info "Create virtual touchpad: #{VIRTUAL_TOUCHPAD_NAME}"
144
- uinput.create_from_device(name: VIRTUAL_TOUCHPAD_NAME, device: @source_touchpad)
175
+ # NOTE: Use uinput to create a virtual touchpad that copies from first touchpad
176
+ uinput.create_from_device(name: VIRTUAL_TOUCHPAD_NAME, device: @source_touchpads.first)
145
177
  end
146
178
 
147
179
  def set_trap
@@ -3,7 +3,7 @@
3
3
  module Fusuma
4
4
  module Plugin
5
5
  module Remap
6
- VERSION = "0.9.0"
6
+ VERSION = "0.11.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.9.0
4
+ version: 0.11.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-02-19 00:00:00.000000000 Z
11
+ date: 2025-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fusuma