fusuma-plugin-remap 0.3.1 → 0.4.1
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 +10 -3
- data/exe/fusuma-remap +74 -25
- data/exe/fusuma-touchpad-remap +42 -0
- data/fusuma-plugin-remap.gemspec +2 -2
- data/lib/fusuma/plugin/inputs/remap_keyboard_input.rb +24 -58
- data/lib/fusuma/plugin/inputs/remap_touchpad_input.rb +116 -0
- data/lib/fusuma/plugin/inputs/remap_touchpad_input.yml +4 -0
- data/lib/fusuma/plugin/remap/{remapper.rb → keyboard_remapper.rb} +145 -43
- data/lib/fusuma/plugin/remap/layer_manager.rb +18 -13
- data/lib/fusuma/plugin/remap/touchpad_remapper.rb +168 -0
- data/lib/fusuma/plugin/remap/uinput_keyboard.rb +95 -0
- data/lib/fusuma/plugin/remap/uinput_touchpad.rb +172 -0
- data/lib/fusuma/plugin/remap/version.rb +1 -1
- metadata +17 -11
- data/lib/fusuma/plugin/remap/ruinput_device_patched.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2455d59dcc7050237331988d27f8def95586791fd1f86974146ceb7a373ff35
|
4
|
+
data.tar.gz: c39286dafd8112602c61953512d16f85f0e3b51c318e215431d5539efd33eadd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a40ff642c159e4f7a8f627929c01f99e44e666c0de24f5c977fff7c2967a37fce1693152053d6477edc9f4c87e7691b4b987ca7bd0fdbade5029d40f12d6e1a5
|
7
|
+
data.tar.gz: efb92a3fb8c1d67b93e811ad631d0437e5f0a2000035a2c2637ae942d25072869efb7c88a00478dfb6606239a3283f86bf6fc4ab970feeb6c295c1ed99f70d5f
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# Fusuma::Plugin::Remap [](https://badge.fury.io/rb/fusuma-plugin-remap) [](https://github.com/iberianpig/fusuma-plugin-remap/actions/workflows/main.yml)
|
2
2
|
|
3
|
-
|
3
|
+
A Fusuma plugin for remapping keyboard events into virtual input devices. Compatible with other Fusuma plugins.
|
4
4
|
|
5
5
|
**THIS PLUGIN IS EXPERIMENTAL.**
|
6
6
|
|
7
|
-
A Fusuma plugin for remapping keyboard events into virtual input devices. Compatible with other Fusuma plugins.
|
8
|
-
|
9
7
|
This plugin empowers users to manipulate keyboard events and convert them into virtual input devices. It is designed to integrate seamlessly with other Fusuma plugins, thus enabling users to construct sophisticated input configurations and achieve distinctive functionalities. A key feature is the dynamic alteration of remapping layers within the Fusuma process, thereby enabling users to adapt their keyboard inputs to suit specific tasks or applications.
|
10
8
|
|
9
|
+
## Installation
|
10
|
+
|
11
11
|
This plugin requires [fusuma](https://github.com/iberianpig/fusuma#update) 2.0
|
12
12
|
|
13
13
|
### Install dependencies
|
@@ -65,6 +65,13 @@ remap:
|
|
65
65
|
SPACE: BTN_LEFT
|
66
66
|
```
|
67
67
|
|
68
|
+
## Emergency stop keybind for virtual keyboard
|
69
|
+
|
70
|
+
This is a special keybind for emergency stop.
|
71
|
+
If you press following keybind, physical keyboard will be ungrabbed and Fusuma process will be terminated.
|
72
|
+
|
73
|
+
<kbd>RIGHTCTRL</kbd> → <kbd>LEFTCTRL</kbd>
|
74
|
+
|
68
75
|
## Contributing
|
69
76
|
|
70
77
|
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.
|
data/exe/fusuma-remap
CHANGED
@@ -1,44 +1,93 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "fusuma/plugin/inputs/input"
|
5
|
+
require_relative "../lib/fusuma/plugin/inputs/remap_keyboard_input"
|
4
6
|
require_relative "../lib/fusuma/plugin/remap/version"
|
5
|
-
require_relative "../lib/fusuma/plugin/remap/
|
7
|
+
require_relative "../lib/fusuma/plugin/remap/keyboard_remapper"
|
6
8
|
require_relative "../lib/fusuma/plugin/remap/layer_manager"
|
7
9
|
require "fusuma/config"
|
8
|
-
require "
|
10
|
+
require "fusuma/multi_logger"
|
9
11
|
require "msgpack"
|
12
|
+
require "irb"
|
10
13
|
|
11
|
-
|
14
|
+
Fusuma::MultiLogger.instance.debug_mode = true
|
15
|
+
Fusuma::Config.instance.custom_path = "~/.config/fusuma/config.yml"
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
warn "Usage: #{$PROGRAM_NAME} /dev/input/KEYBOARD /dev/input/TOUCHPAD"
|
18
|
-
exit false
|
17
|
+
# FIXME: use OptionParser and implement help
|
18
|
+
if ARGV.length < 1
|
19
|
+
puts "you can specify keyboard name pattern and touchpad name pattern"
|
20
|
+
puts "$ #{$PROGRAM_NAME} 'AT Translated Set 2 keyboard' 'SynPS/2 Synaptics TouchPad'"
|
19
21
|
end
|
20
22
|
|
21
|
-
|
23
|
+
keyboard_name_pattern ||= ARGV.shift || ["keyboard", "Keyboard", "KEYBOARD"]
|
24
|
+
touchpad_name_pattern ||= ARGV.shift || ["touchpad", "Touchpad", "TOUCHPAD"]
|
25
|
+
|
26
|
+
source_keyboards = Fusuma::Plugin::Inputs::RemapKeyboardInput::KeyboardSelector.new(keyboard_name_pattern).select
|
27
|
+
internal_touchpad = Fusuma::Plugin::Inputs::RemapKeyboardInput::TouchpadSelector.new(touchpad_name_pattern).select.first
|
28
|
+
|
29
|
+
if source_keyboards.empty?
|
30
|
+
warn "no keyboard found"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
if internal_touchpad.nil?
|
35
|
+
warn "no touchpad found"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
@layer_manager = Fusuma::Plugin::Remap::LayerManager.instance
|
22
40
|
|
23
|
-
|
41
|
+
keyboard_reader, fusuma_writer = IO.pipe
|
24
42
|
|
25
|
-
|
43
|
+
layers = Fusuma::Config.instance.keymap.select { |m| m[:context] && m[:remap] }.map { |m| m[:context] }.uniq
|
26
44
|
Thread.new do
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
45
|
+
layers.each do |layer|
|
46
|
+
@layer_manager.send_layer(layer: layer)
|
47
|
+
@layer_manager.send_layer(layer: layer, remove: true)
|
48
|
+
sleep 1
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "=================================================="
|
52
|
+
puts "press key UP or DOWN to change layer"
|
53
|
+
puts "=================================================="
|
54
|
+
|
55
|
+
layer_selector = Fiber.new do
|
56
|
+
layers = ([{}] | layers)
|
57
|
+
@reverse = false
|
58
|
+
pos = 0
|
59
|
+
loop do
|
60
|
+
pos = if @reverse
|
61
|
+
(pos - 1) % layers.size
|
62
|
+
else
|
63
|
+
(pos + 1) % layers.size
|
64
|
+
end
|
65
|
+
@layer_manager.send_layer(layer: @layer_manager.current_layer, remove: true)
|
66
|
+
@layer_manager.send_layer(layer: layers[pos])
|
67
|
+
Fiber.yield
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
unpacker = MessagePack::Unpacker.new(keyboard_reader)
|
72
|
+
loop do
|
73
|
+
data = unpacker.unpack
|
74
|
+
next unless data.is_a? Hash
|
75
|
+
next unless data["status"] == 1
|
76
|
+
|
77
|
+
case data["key"]
|
78
|
+
when "UP"
|
79
|
+
@reverse = false
|
80
|
+
layer_selector.resume
|
81
|
+
when "DOWN"
|
82
|
+
@reverse = true
|
83
|
+
layer_selector.resume
|
84
|
+
end
|
85
|
+
end
|
37
86
|
end
|
38
87
|
|
39
|
-
Fusuma::Plugin::Remap::
|
40
|
-
layer_manager: layer_manager,
|
41
|
-
|
88
|
+
Fusuma::Plugin::Remap::KeyboardRemapper.new(
|
89
|
+
layer_manager: @layer_manager,
|
90
|
+
fusuma_writer: fusuma_writer,
|
42
91
|
source_keyboards: source_keyboards,
|
43
92
|
internal_touchpad: internal_touchpad
|
44
93
|
).run
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "fusuma/plugin/inputs/input"
|
5
|
+
require_relative "../lib/fusuma/plugin/inputs/remap_touchpad_input"
|
6
|
+
require_relative "../lib/fusuma/plugin/remap/version"
|
7
|
+
require_relative "../lib/fusuma/plugin/remap/touchpad_remapper"
|
8
|
+
require_relative "../lib/fusuma/plugin/remap/layer_manager"
|
9
|
+
require "fusuma/config"
|
10
|
+
require "fusuma/multi_logger"
|
11
|
+
require "revdev"
|
12
|
+
require "msgpack"
|
13
|
+
require "irb"
|
14
|
+
|
15
|
+
Fusuma::MultiLogger.instance.debug_mode = true
|
16
|
+
Fusuma::Config.instance.custom_path = "~/.config/fusuma/config.yml"
|
17
|
+
|
18
|
+
touchpad_name_pattern = ["touchpad", "Touchpad", "TOUCHPAD"]
|
19
|
+
|
20
|
+
# $DEBUG=true # puts events
|
21
|
+
|
22
|
+
internal_touchpad = Fusuma::Plugin::Inputs::RemapKeyboardInput::TouchpadSelector.new(touchpad_name_pattern).select.first
|
23
|
+
|
24
|
+
if internal_touchpad.nil?
|
25
|
+
warn "no touchpad found"
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
touchpad_reader, touchpad_writer = IO.pipe
|
30
|
+
|
31
|
+
Thread.new do
|
32
|
+
unpacker = MessagePack::Unpacker.new(touchpad_reader)
|
33
|
+
loop do
|
34
|
+
data = unpacker.unpack
|
35
|
+
puts data
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Fusuma::Plugin::Remap::TouchpadRemapper.new(
|
40
|
+
touchpad_writer: touchpad_writer,
|
41
|
+
source_touchpad: internal_touchpad
|
42
|
+
).run
|
data/fusuma-plugin-remap.gemspec
CHANGED
@@ -24,8 +24,8 @@ Gem::Specification.new do |spec|
|
|
24
24
|
# https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all§ion=main
|
25
25
|
# support focal (20.04LTS) 2.7
|
26
26
|
|
27
|
-
spec.add_dependency "fusuma", ">= 3.
|
28
|
-
spec.add_dependency "fusuma-plugin-keypress", "
|
27
|
+
spec.add_dependency "fusuma", ">= 3.4"
|
28
|
+
spec.add_dependency "fusuma-plugin-keypress", ">= 0.11.0"
|
29
29
|
spec.add_dependency "msgpack"
|
30
30
|
spec.add_dependency "revdev"
|
31
31
|
spec.add_dependency "ruinput"
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "fusuma/device"
|
4
|
-
|
5
|
-
|
4
|
+
require_relative "../remap/keyboard_remapper"
|
5
|
+
require_relative "../remap/layer_manager"
|
6
6
|
|
7
7
|
module Fusuma
|
8
8
|
module Plugin
|
@@ -26,84 +26,50 @@ module Fusuma
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def io
|
29
|
-
@
|
29
|
+
@fusuma_reader
|
30
30
|
end
|
31
31
|
|
32
|
-
#
|
33
|
-
# @return [
|
34
|
-
def
|
35
|
-
|
32
|
+
# override Input#read_from_io
|
33
|
+
# @return [Record]
|
34
|
+
def read_from_io
|
35
|
+
@unpacker ||= MessagePack::Unpacker.new(io)
|
36
|
+
data = @unpacker.unpack
|
36
37
|
|
37
|
-
unless data.is_a? Hash
|
38
|
-
MultiLogger.error("Invalid record: #{record}", data: data)
|
39
|
-
return
|
40
|
-
end
|
38
|
+
raise "data is not Hash : #{data}" unless data.is_a? Hash
|
41
39
|
|
42
|
-
code = data["key"]
|
43
40
|
status = (data["status"] == 1) ? "pressed" : "released"
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
MultiLogger.
|
48
|
-
|
41
|
+
Events::Records::KeypressRecord.new(status: status, code: data["key"], layer: data["layer"])
|
42
|
+
rescue EOFError => e
|
43
|
+
MultiLogger.error "#{self.class.name}: #{e}"
|
44
|
+
MultiLogger.error "Shutdown fusuma process..."
|
45
|
+
Process.kill("TERM", Process.pid)
|
49
46
|
end
|
50
47
|
|
51
48
|
private
|
52
49
|
|
53
50
|
def setup_remapper
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
config = {
|
52
|
+
keyboard_name_patterns: config_params(:keyboard_name_patterns),
|
53
|
+
touchpad_name_patterns: config_params(:touchpad_name_patterns)
|
54
|
+
}
|
57
55
|
|
58
56
|
layer_manager = Remap::LayerManager.instance
|
59
57
|
|
60
58
|
# physical keyboard input event
|
61
|
-
@
|
59
|
+
@fusuma_reader, fusuma_writer = IO.pipe
|
62
60
|
|
63
61
|
@pid = fork do
|
64
62
|
layer_manager.writer.close
|
65
|
-
@
|
66
|
-
remapper = Remap::
|
63
|
+
@fusuma_reader.close
|
64
|
+
remapper = Remap::KeyboardRemapper.new(
|
67
65
|
layer_manager: layer_manager,
|
68
|
-
|
69
|
-
|
70
|
-
internal_touchpad: internal_touchpad
|
66
|
+
fusuma_writer: fusuma_writer,
|
67
|
+
config: config
|
71
68
|
)
|
72
69
|
remapper.run
|
73
70
|
end
|
74
71
|
layer_manager.reader.close
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
# Devices to detect key presses and releases
|
79
|
-
class KeyboardSelector
|
80
|
-
def initialize(names = ["keyboard", "Keyboard", "KEYBOARD"])
|
81
|
-
@names = names
|
82
|
-
end
|
83
|
-
|
84
|
-
# @return [Array<Revdev::EventDevice>]
|
85
|
-
def select
|
86
|
-
devices = Fusuma::Device.all.select { |d| Array(@names).any? { |name| d.name =~ /#{name}/ } }
|
87
|
-
devices.map { |d| Revdev::EventDevice.new("/dev/input/#{d.id}") }
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class TouchpadSelector
|
92
|
-
def initialize(names)
|
93
|
-
@names = names
|
94
|
-
end
|
95
|
-
|
96
|
-
# @return [Array<Revdev::EventDevice>]
|
97
|
-
def select
|
98
|
-
devices = if @names
|
99
|
-
Fusuma::Device.all.select { |d| Array(@names).any? { |name| d.name =~ /#{name}/ } }
|
100
|
-
else
|
101
|
-
# available returns only touchpad devices
|
102
|
-
Fusuma::Device.available
|
103
|
-
end
|
104
|
-
|
105
|
-
devices.map { |d| Revdev::EventDevice.new("/dev/input/#{d.id}") }
|
106
|
-
end
|
72
|
+
fusuma_writer.close
|
107
73
|
end
|
108
74
|
end
|
109
75
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fusuma/device"
|
4
|
+
require_relative "../remap/touchpad_remapper"
|
5
|
+
# require_relative "../remap/layer_manager"
|
6
|
+
|
7
|
+
module Fusuma
|
8
|
+
module Plugin
|
9
|
+
module Inputs
|
10
|
+
# Get touchpad events from remapper
|
11
|
+
class RemapTouchpadInput < Input
|
12
|
+
include CustomProcess
|
13
|
+
|
14
|
+
def config_param_types
|
15
|
+
{
|
16
|
+
touchpad_name_patterns: [Array, String]
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :pid
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
setup_remapper
|
25
|
+
end
|
26
|
+
|
27
|
+
def io
|
28
|
+
@fusuma_reader
|
29
|
+
end
|
30
|
+
|
31
|
+
# override Input#read_from_io
|
32
|
+
# @return [Record]
|
33
|
+
def read_from_io
|
34
|
+
@unpacker ||= MessagePack::Unpacker.new(io)
|
35
|
+
data = @unpacker.unpack
|
36
|
+
|
37
|
+
raise "data is not Hash : #{data}" unless data.is_a? Hash
|
38
|
+
|
39
|
+
gesture = "touch"
|
40
|
+
finger = data["finger"]
|
41
|
+
|
42
|
+
# @touch_state ||= {}
|
43
|
+
# @mt_slot ||= 0
|
44
|
+
# @touch_state[@mt_slot] ||= {
|
45
|
+
# MT_TRACKING_ID: nil,
|
46
|
+
# X: nil,
|
47
|
+
# Y: nil,
|
48
|
+
# valid_touch_point: false
|
49
|
+
# }
|
50
|
+
# TODO: implement update touch_state
|
51
|
+
status =
|
52
|
+
if data["touch_state"].any? { |_, v| v["valid_touch_point"] }
|
53
|
+
"begin"
|
54
|
+
else
|
55
|
+
"end"
|
56
|
+
end
|
57
|
+
|
58
|
+
Events::Records::GestureRecord.new(gesture: gesture, status: status, finger: finger, delta: nil)
|
59
|
+
rescue EOFError => e
|
60
|
+
MultiLogger.error "#{self.class.name}: #{e}"
|
61
|
+
MultiLogger.error "Shutdown fusuma process..."
|
62
|
+
Process.kill("TERM", Process.pid)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def setup_remapper
|
68
|
+
internal_touchpad = TouchpadSelector.new(config_params(:touchpad_name_patterns)).select.first
|
69
|
+
if internal_touchpad.nil?
|
70
|
+
MultiLogger.error("No touchpad found: #{config_params(:touchpad_name_patterns)}")
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
|
74
|
+
MultiLogger.info("set up remapper")
|
75
|
+
MultiLogger.info("internal_touchpad: #{internal_touchpad.device_name}")
|
76
|
+
|
77
|
+
# layer_manager = Remap::LayerManager.instance
|
78
|
+
|
79
|
+
# physical touchpad input event
|
80
|
+
@fusuma_reader, fusuma_writer = IO.pipe
|
81
|
+
|
82
|
+
@pid = fork do
|
83
|
+
# layer_manager.writer.close
|
84
|
+
@fusuma_reader.close
|
85
|
+
remapper = Remap::TouchpadRemapper.new(
|
86
|
+
# layer_manager: layer_manager,
|
87
|
+
fusuma_writer: fusuma_writer,
|
88
|
+
source_touchpad: internal_touchpad
|
89
|
+
)
|
90
|
+
remapper.run
|
91
|
+
end
|
92
|
+
# layer_manager.reader.close
|
93
|
+
fusuma_writer.close
|
94
|
+
end
|
95
|
+
|
96
|
+
class TouchpadSelector
|
97
|
+
def initialize(names = nil)
|
98
|
+
@names = names
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [Array<Revdev::EventDevice>]
|
102
|
+
def select
|
103
|
+
devices = if @names
|
104
|
+
Fusuma::Device.all.select { |d| Array(@names).any? { |name| d.name =~ /#{name}/ } }
|
105
|
+
else
|
106
|
+
# available returns only touchpad devices
|
107
|
+
Fusuma::Device.available
|
108
|
+
end
|
109
|
+
|
110
|
+
devices.map { |d| Revdev::EventDevice.new("/dev/input/#{d.id}") }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|