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 +4 -4
- data/README.md +70 -0
- data/lib/fusuma/plugin/inputs/remap_keyboard_input.rb +2 -5
- data/lib/fusuma/plugin/inputs/remap_keyboard_input.yml +2 -1
- data/lib/fusuma/plugin/inputs/remap_touchpad_input.rb +5 -22
- data/lib/fusuma/plugin/inputs/remap_touchpad_input.yml +3 -1
- data/lib/fusuma/plugin/remap/keyboard_remapper.rb +5 -4
- data/lib/fusuma/plugin/remap/touchpad_remapper.rb +49 -17
- 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: b55e5ef616cbf48bc0a8c3a4c0b9b91dbed01c3b4dc87f53f09fa51ab6c34227
|
4
|
+
data.tar.gz: 1e34c8882e52d3191092576ab5d056d74c1ea0d130963a37304ce2a8bc6b0fdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,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
|
-
|
67
|
-
if
|
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("
|
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
|
-
|
69
|
+
source_touchpads: source_touchpads
|
87
70
|
)
|
88
71
|
remapper.run
|
89
72
|
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 "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
|
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
|
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
|
17
|
-
def initialize(fusuma_writer:,
|
18
|
-
@
|
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
|
-
|
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(
|
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 =
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
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
|
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
|
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] = @
|
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
|
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
|
-
|
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
|
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.
|
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-
|
11
|
+
date: 2025-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fusuma
|