fusuma-plugin-thumbsense 0.7.0 → 0.9.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 +100 -19
- data/fusuma-plugin-thumbsense.gemspec +2 -2
- data/lib/fusuma/plugin/buffers/thumbsense_buffer.rb +8 -22
- data/lib/fusuma/plugin/buffers/thumbsense_buffer.yml +4 -0
- data/lib/fusuma/plugin/detectors/thumbsense_detector.rb +73 -55
- data/lib/fusuma/plugin/inputs/hidraw/device.rb +102 -0
- data/lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb +162 -0
- data/lib/fusuma/plugin/inputs/hidraw/hhkb_usb_parser.rb +113 -0
- data/lib/fusuma/plugin/inputs/pointing_stick_input.rb +101 -0
- data/lib/fusuma/plugin/inputs/pointing_stick_input.yml +4 -0
- data/lib/fusuma/plugin/thumbsense/version.rb +1 -1
- metadata +13 -10
- data/lib/fusuma/plugin/filters/thumbsense_filter.rb +0 -27
- data/lib/fusuma/plugin/filters/thumbsense_filter.yml +0 -5
- data/lib/fusuma/plugin/parsers/thumbsense_parser.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a991e73e2eace58e3f6732a6a0f39b12b9e968550c613977eeba03ec650dadda
|
4
|
+
data.tar.gz: 8735f0050e321d7171558df00925b94f39ea307e6914f350fdcf69ed805002bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 104fcb225426e2c0310a33277ab82e9a174bfa38dd3212d3b9294f72561b48b8a9ec73860478bc9ab16f53deb6a1272c8d21ca0221c71fe797e571786f6983a8
|
7
|
+
data.tar.gz: d5bf0fba767a5a1d1952cd2fd7f2fd4b9f033ac3c89b76dbfdc31ad5235a5ad0b3727e04437ddd5367eac27ccc256661959859cbd816708ab841588a4a61c524
|
data/README.md
CHANGED
@@ -3,54 +3,135 @@
|
|
3
3
|
Remapper from key to click only while tapping the touchpad.
|
4
4
|
Implemented as [Fusuma](https://github.com/iberianpig/fusuma) Plugin.
|
5
5
|
|
6
|
-
**THIS PLUGIN IS EXPERIMENTAL.**
|
7
|
-
|
8
6
|
## What is ThumbSense?
|
9
7
|
[ThumbSense](https://www2.sonycsl.co.jp/person/rekimoto/tsense/soft/index.html) is a tool that lets you control a laptop's touchpad using the keyboard. It assigns certain keyboard keys as mouse buttons and switches between acting as mouse buttons or normal keyboard keys based on whether the user's thumb is touching the touchpad. ThumbSense aims to make it easier to use the touchpad without moving your hand away from the keyboard.
|
10
8
|
|
11
9
|
## Installation
|
12
10
|
|
13
|
-
###
|
11
|
+
### Prerequisites
|
14
12
|
|
15
13
|
- [fusuma](https://github.com/iberianpig/fusuma#update) 2.0 or later
|
16
|
-
- [fusuma-plugin-keypress](https://github.com/iberianpig/fusuma-plugin-keypress) 0.5 or later
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
- [fusuma-plugin-keypress](https://github.com/iberianpig/fusuma-plugin-keypress) 0.5 or later (automatically installed)
|
15
|
+
- [fusuma-plugin-remap](https://github.com/iberianpig/fusuma-plugin-remap) (udev rules setup required)
|
16
|
+
|
17
|
+
### Steps to Install and Set Up Fusuma::Plugin::Thumbsense
|
18
|
+
|
19
|
+
1. Install the necessary packages for native extensions:
|
20
|
+
```sh
|
21
|
+
$ sudo apt install ruby-dev build-essential
|
22
|
+
```
|
21
23
|
|
22
|
-
|
24
|
+
2. Install the required library for building fusuma-plugin-remap:
|
25
|
+
```sh
|
26
|
+
$ sudo apt install libevdev-dev
|
27
|
+
```
|
23
28
|
|
24
|
-
|
29
|
+
3. Set up udev rules to create a virtual input device (for fusuma-plugin-remap):
|
30
|
+
```sh
|
31
|
+
$ echo 'KERNEL=="uinput", MODE="0660", GROUP="input", OPTIONS+="static_node=uinput"' | sudo tee /etc/udev/rules.d/60-udev-fusuma-remap.rules
|
32
|
+
$ sudo udevadm control --reload-rules && sudo udevadm trigger
|
33
|
+
```
|
25
34
|
|
35
|
+
4. Install fusuma-plugin-thumbsense:
|
26
36
|
```sh
|
27
37
|
$ sudo gem install fusuma-plugin-thumbsense
|
28
38
|
```
|
29
39
|
|
30
|
-
##
|
40
|
+
## Configuration
|
41
|
+
|
42
|
+
### Thumbsense Context
|
43
|
+
|
44
|
+
To add the thumbsense `context`, edit `~/.config/fusuma/config.yml`.
|
45
|
+
The `context` section is separated by `---` and specified as `context: thumbsense`.
|
46
|
+
Fusuma will switch to the `thumbsense` context while tapping the touchpad.
|
47
|
+
|
48
|
+
### Key to Mouse Button Remap
|
31
49
|
|
32
|
-
|
50
|
+
You can remap keys to mouse buttons while tapping the touchpad.
|
51
|
+
The `remap` property is configured within the `thumbsense` context.
|
33
52
|
|
34
|
-
|
35
|
-
|
53
|
+
Available mouse buttons include:
|
54
|
+
- `BTN_LEFT`
|
55
|
+
- `BTN_MIDDLE`
|
56
|
+
- `BTN_RIGHT`
|
57
|
+
- `BTN_SIDE`
|
58
|
+
- `BTN_EXTRA`
|
59
|
+
- `BTN_FORWARD`
|
60
|
+
- `BTN_BACK`
|
61
|
+
- `BTN_TASK`
|
62
|
+
- `BTN_0`
|
63
|
+
- `BTN_1`
|
64
|
+
- ...
|
65
|
+
- `BTN_9`
|
36
66
|
|
37
|
-
|
67
|
+
### Example Configuration
|
38
68
|
|
39
|
-
|
69
|
+
Add the following code to `~/.config/fusuma/config.yml`:
|
40
70
|
|
41
71
|
```yaml
|
72
|
+
# Add thumbsense context
|
42
73
|
---
|
43
74
|
context: thumbsense
|
44
75
|
|
45
76
|
remap:
|
77
|
+
F: BTN_LEFT
|
78
|
+
E: BTN_MIDDLE
|
79
|
+
D: BTN_RIGHT
|
80
|
+
SPACE: BTN_LEFT
|
46
81
|
J: BTN_LEFT
|
47
82
|
K: BTN_RIGHT
|
48
83
|
```
|
49
84
|
|
50
|
-
|
85
|
+
## Pointing Stick Support
|
86
|
+
|
87
|
+
### Overview
|
88
|
+
Fusuma::Plugin::Thumbsense provides experimental support for pointing stick devices. This functionality is currently limited to the **HHKB Studio** and utilizes HIDRAW. Please note that this feature is still in testing, and improvements may be made in future updates.
|
89
|
+
|
90
|
+
see: https://github.com/iberianpig/fusuma-plugin-thumbsense/pull/4
|
91
|
+
|
92
|
+
### Setting Up Udev Rules
|
93
|
+
|
94
|
+
To use the pointing stick touch support, you need to set up the following Udev rules to ensure that the HHKB Studio device is correctly recognized:
|
95
|
+
|
96
|
+
1. **Create the Udev Rule File**:
|
97
|
+
Create a Udev rule file with the following command:
|
98
|
+
|
99
|
+
```sh
|
100
|
+
sudo nano /etc/udev/rules.d/60-udev-fusuma-thumbsense-hhkb-studio.rules
|
101
|
+
```
|
102
|
+
|
103
|
+
Add the following content to the file:
|
104
|
+
|
105
|
+
```plaintext
|
106
|
+
# HHKB Studio (USB)
|
107
|
+
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="04fe", ATTRS{idProduct}=="0016", MODE="0666"
|
108
|
+
|
109
|
+
# HHKB Studio (Bluetooth)
|
110
|
+
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ENV{DEVPATH}=="/devices/virtual/misc/uhid/*:04FE:0016.*/hidraw*", MODE="0666"
|
111
|
+
```
|
112
|
+
|
113
|
+
2. **Reload the Udev Rules**:
|
114
|
+
Execute the following command to reload the Udev rules:
|
115
|
+
|
116
|
+
```sh
|
117
|
+
sudo udevadm control --reload-rules && sudo udevadm trigger
|
118
|
+
```
|
119
|
+
|
120
|
+
## TODO LIST
|
121
|
+
|
122
|
+
- ThumbSense
|
123
|
+
- [x] Change remap layer while tapping
|
124
|
+
- [x] Enable executing commands like `command:` and `sendkey:`
|
125
|
+
- [x] Support pointing stick devices(https://github.com/iberianpig/fusuma-plugin-thumbsense/pull/4)
|
126
|
+
- Now only HHKB Studio is supported using HIDRAW
|
51
127
|
|
52
|
-
-
|
53
|
-
-
|
128
|
+
- Remap
|
129
|
+
- [x] Remap to single key (e.g., `remap: { J: BTN_LEFT }`)
|
130
|
+
- [x] Send mouse clicks with `remap: { I: BTN_MIDDLE }`
|
131
|
+
- [x] Remap multiple keys
|
132
|
+
- Support sending multiple keys with fusuma-plugin-sendkey(https://github.com/iberianpig/fusuma-plugin-sendkey/pull/34)
|
133
|
+
- `remap: { T: { sendkey: [LEFTSHIFT+F10, T, ENTER, ESC] } }`
|
134
|
+
- [ ] Remap POINTER_MOTION to POINTER_SCROLL_FINGER (e.g., `remap: { S: POINTER_SCROLL_FINGER }`)
|
54
135
|
|
55
136
|
## Contributing
|
56
137
|
|
@@ -25,8 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
# https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all§ion=main
|
26
26
|
# support focal (20.04LTS) 2.7
|
27
27
|
spec.add_dependency "fusuma", ">= 3.2"
|
28
|
-
spec.add_dependency "fusuma-plugin-keypress", ">= 0.
|
29
|
-
spec.add_dependency "fusuma-plugin-remap"
|
28
|
+
spec.add_dependency "fusuma-plugin-keypress", ">= 0.11"
|
29
|
+
spec.add_dependency "fusuma-plugin-remap", ">= 0.4"
|
30
30
|
spec.metadata = {
|
31
31
|
"rubygems_mfa_required" => "true"
|
32
32
|
}
|
@@ -5,7 +5,8 @@ module Fusuma
|
|
5
5
|
module Buffers
|
6
6
|
# manage events and generate command
|
7
7
|
class ThumbsenseBuffer < Buffer
|
8
|
-
DEFAULT_SOURCE = "
|
8
|
+
DEFAULT_SOURCE = "remap_touchpad_input"
|
9
|
+
POINTING_STICK_SOURCE = "pointing_stick_input"
|
9
10
|
|
10
11
|
def config_param_types
|
11
12
|
{
|
@@ -20,17 +21,17 @@ module Fusuma
|
|
20
21
|
# skip palm/begin record
|
21
22
|
return if !ended?(@events.last)
|
22
23
|
|
23
|
-
|
24
|
-
@events.delete_if { |e| e.record.finger == released_finger }
|
24
|
+
clear
|
25
25
|
end
|
26
26
|
|
27
27
|
# @param event [Event]
|
28
28
|
# @return [NilClass, ThumbsenseBuffer]
|
29
29
|
def buffer(event)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
case event.tag
|
31
|
+
when source, POINTING_STICK_SOURCE
|
32
|
+
@events.push(event)
|
33
|
+
self
|
34
|
+
end
|
34
35
|
end
|
35
36
|
|
36
37
|
# return [Integer]
|
@@ -38,21 +39,6 @@ module Fusuma
|
|
38
39
|
@events.map { |e| e.record.finger }.max
|
39
40
|
end
|
40
41
|
|
41
|
-
def empty?
|
42
|
-
@events.empty?
|
43
|
-
end
|
44
|
-
|
45
|
-
def present?
|
46
|
-
!empty?
|
47
|
-
end
|
48
|
-
|
49
|
-
def select_by_events(&block)
|
50
|
-
return enum_for(:select) unless block
|
51
|
-
|
52
|
-
events = @events.select(&block)
|
53
|
-
self.class.new events
|
54
|
-
end
|
55
|
-
|
56
42
|
def ended?(event)
|
57
43
|
event.record.status == "end"
|
58
44
|
end
|
@@ -10,8 +10,8 @@ module Fusuma
|
|
10
10
|
module Detectors
|
11
11
|
# Detect Thumbsense context and change remap layer of fusuma-plugin-remap
|
12
12
|
class ThumbsenseDetector < Detector
|
13
|
+
# keypress buffer is used to detect modifier keys
|
13
14
|
SOURCES = %w[thumbsense keypress].freeze
|
14
|
-
BUFFER_TYPE = "thumbsense"
|
15
15
|
|
16
16
|
MODIFIER_KEYS = Set.new(%w[
|
17
17
|
CAPSLOCK
|
@@ -28,51 +28,74 @@ module Fusuma
|
|
28
28
|
LAYER_CONTEXT = {thumbsense: true}.freeze
|
29
29
|
|
30
30
|
# Detect Context event and change remap layer of fusuma-plugin-remap
|
31
|
-
# @param buffers [Array<Buffer>]
|
31
|
+
# @param buffers [Array<Buffer>] ThumbsenseBuffer, KeypressBuffer
|
32
32
|
# @return [Event] if Thumbsense context is detected
|
33
|
+
# @return [Array<Event>] if Thumbsense context and Remap index when keypress is detected
|
33
34
|
# @return [NilClass] if event is NOT detected
|
34
35
|
def detect(buffers)
|
35
|
-
thumbsense_buffer
|
36
|
-
|
37
|
-
return if thumbsense_buffer.empty?
|
38
|
-
|
39
|
-
MultiLogger.debug("thumbsense_buffer: #{thumbsense_buffer.events.map(&:record).map { |r| "#{r.finger} #{r.gesture} #{r.status}" }}")
|
36
|
+
@thumbsense_buffer ||= find_buffer(buffers, "thumbsense")
|
37
|
+
@keypress_buffer ||= find_buffer(buffers, "keypress")
|
40
38
|
|
41
39
|
layer_manager = Fusuma::Plugin::Remap::LayerManager.instance
|
42
40
|
|
43
|
-
|
41
|
+
# layer is thumbsense => create thumbsense context and remap index
|
42
|
+
# touch is touching => create thumbsense context and remap index
|
43
|
+
# touch is released => remove thumbsense context
|
44
|
+
# keypress -> touch => remove thumbsense context
|
45
|
+
if touch_released? && !thumbsense_layer?
|
46
|
+
MultiLogger.debug("thumbsense layer removed")
|
44
47
|
layer_manager.send_layer(layer: LAYER_CONTEXT, remove: true)
|
45
48
|
return
|
46
49
|
end
|
47
50
|
|
48
|
-
|
51
|
+
# When keypress event is first:
|
52
|
+
# If current layer is thumbsense, the layer should not be changed
|
53
|
+
# If current layer is not thumbsense, it should remain a normal key
|
54
|
+
# In other words, if the key event comes first, do nothing
|
55
|
+
if keypress_first?
|
56
|
+
MultiLogger.debug("keypress event is first")
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# Even if the palm is detected, keep the thumbsense layer until `:end` event
|
54
|
-
if palm_detected?(thumbsense_buffer)
|
55
|
-
layer_manager.send_layer(layer: LAYER_CONTEXT, remove: true)
|
56
|
-
return
|
57
|
-
end
|
58
|
+
return
|
59
|
+
end
|
58
60
|
|
59
|
-
|
61
|
+
MultiLogger.debug("thumbsense context created") unless thumbsense_layer?
|
62
|
+
layer_manager.send_layer(layer: LAYER_CONTEXT)
|
60
63
|
|
61
|
-
|
62
|
-
|
64
|
+
# create thumbsense context
|
65
|
+
context = create_event(
|
66
|
+
record: Events::Records::ContextRecord.new(
|
63
67
|
name: :thumbsense,
|
64
68
|
value: true
|
65
69
|
)
|
66
|
-
|
70
|
+
)
|
71
|
+
|
72
|
+
# TODO: Threshold
|
73
|
+
# create remap index
|
74
|
+
index = if (keys = pressed_codes) && !keys.empty?
|
75
|
+
MultiLogger.debug("thumbsense remap index created: #{keys}")
|
76
|
+
combined_keys = keys.join("+")
|
77
|
+
create_event(
|
78
|
+
record: Events::Records::IndexRecord.new(
|
79
|
+
index: Config::Index.new([:remap, combined_keys])
|
80
|
+
)
|
81
|
+
)
|
67
82
|
end
|
68
83
|
|
69
|
-
|
84
|
+
[context, index].compact
|
70
85
|
end
|
71
86
|
|
72
87
|
private
|
73
88
|
|
74
|
-
|
75
|
-
|
89
|
+
# @param buffers [Array<Buffer>]
|
90
|
+
# @param type [String]
|
91
|
+
# @return [Buffer]
|
92
|
+
def find_buffer(buffers, type)
|
93
|
+
buffers.find { |b| b.type == type }
|
94
|
+
end
|
95
|
+
|
96
|
+
# @return [Array<String>]
|
97
|
+
def pressed_codes
|
98
|
+
records = @keypress_buffer.events.map(&:record)
|
76
99
|
codes = []
|
77
100
|
records.each do |r|
|
78
101
|
if r.status == "pressed"
|
@@ -84,45 +107,40 @@ module Fusuma
|
|
84
107
|
codes
|
85
108
|
end
|
86
109
|
|
87
|
-
|
88
|
-
|
110
|
+
# @return [TrueClass, FalseClass]
|
111
|
+
def touch_released?
|
112
|
+
return true if @thumbsense_buffer.empty?
|
113
|
+
|
114
|
+
@thumbsense_buffer.events.map(&:record).last&.status == "end"
|
89
115
|
end
|
90
116
|
|
91
117
|
# @return [TrueClass, FalseClass]
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
118
|
+
def thumbsense_layer?
|
119
|
+
return if @keypress_buffer.empty?
|
120
|
+
|
121
|
+
last_keypress = @keypress_buffer.events.last.record
|
122
|
+
return if last_keypress.status == "released"
|
123
|
+
|
124
|
+
return if MODIFIER_KEYS.include?(last_keypress.code)
|
96
125
|
|
97
|
-
|
126
|
+
current_layer = last_keypress&.layer
|
127
|
+
current_layer && current_layer["thumbsense"]
|
98
128
|
end
|
99
129
|
|
100
|
-
#
|
130
|
+
# Check if keypress event is first, before thumbsense event
|
131
|
+
# If last keypress event is modifier key, return false
|
132
|
+
# @param keypress_buffer [Buffer]
|
101
133
|
# @param thumbsense_buffer [Buffer]
|
102
|
-
# @return [TrueClass
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
touch_state_per_finger[f] ||= 0
|
110
|
-
|
111
|
-
case e.record.status
|
112
|
-
when "begin"
|
113
|
-
touch_state_per_finger[f] += 1
|
114
|
-
when "palm"
|
115
|
-
if touch_state_per_finger[f] < 0
|
116
|
-
# NOTE: If Palm continues, it is equivalent to end
|
117
|
-
touch_state_per_finger[f] = 0
|
118
|
-
else
|
119
|
-
touch_state_per_finger[f] -= 1
|
120
|
-
end
|
121
|
-
when "end"
|
122
|
-
touch_state_per_finger[f] = 0
|
123
|
-
end
|
134
|
+
# @return [TrueClass] if keypress event is first
|
135
|
+
# @return [FalseClass] if keypress event is NOT first or buffers are empty
|
136
|
+
def keypress_first?
|
137
|
+
return false if @thumbsense_buffer.empty? || @keypress_buffer.empty?
|
138
|
+
|
139
|
+
if (keys = pressed_codes) && !keys.empty?
|
140
|
+
return false if MODIFIER_KEYS.include?(keys.first)
|
124
141
|
end
|
125
|
-
|
142
|
+
|
143
|
+
@keypress_buffer.events.first.time < @thumbsense_buffer.events.first.time
|
126
144
|
end
|
127
145
|
end
|
128
146
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fusuma/device"
|
4
|
+
require "fusuma/multi_logger"
|
5
|
+
|
6
|
+
module Fusuma
|
7
|
+
module Plugin
|
8
|
+
module Inputs
|
9
|
+
# Read pointing stick events
|
10
|
+
class Hidraw
|
11
|
+
class Device
|
12
|
+
# Definitions of IOCTL commands
|
13
|
+
HIDIOCGRAWNAME = 0x80804804
|
14
|
+
HIDIOCGRAWPHYS = 0x80404805
|
15
|
+
HIDIOCGRAWINFO = 0x80084803
|
16
|
+
HIDIOCGRDESCSIZE = 0x80044801
|
17
|
+
HIDIOCGRDESC = 0x90044802
|
18
|
+
|
19
|
+
# Definitions of bus types
|
20
|
+
BUS_PCI = 0x01
|
21
|
+
BUS_ISAPNP = 0x02
|
22
|
+
BUS_USB = 0x03
|
23
|
+
BUS_HIL = 0x04
|
24
|
+
BUS_BLUETOOTH = 0x05
|
25
|
+
BUS_VIRTUAL = 0x06
|
26
|
+
|
27
|
+
attr_reader :hidraw_path, :name, :bustype, :vendor_id, :product_id
|
28
|
+
|
29
|
+
def initialize(hidraw_path:)
|
30
|
+
@hidraw_path = hidraw_path
|
31
|
+
load_device_info
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def load_device_info
|
37
|
+
File.open(@hidraw_path, "rb+") do |file|
|
38
|
+
@name = fetch_ioctl_data(file, HIDIOCGRAWNAME).strip
|
39
|
+
|
40
|
+
info = fetch_ioctl_data(file, HIDIOCGRAWINFO, [0, 0, 0].pack("LSS"))
|
41
|
+
@bustype, vendor, product = info.unpack("LSS")
|
42
|
+
@vendor_id = vendor.to_s(16)
|
43
|
+
@product_id = product.to_s(16)
|
44
|
+
end
|
45
|
+
rescue => e
|
46
|
+
MultiLogger.error "Error loading device info: #{e.message}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def fetch_ioctl_data(file, ioctl_command, buffer = " " * 256)
|
50
|
+
file.ioctl(ioctl_command, buffer)
|
51
|
+
buffer
|
52
|
+
rescue Errno::EIO
|
53
|
+
MultiLogger.warn "Failed to retrieve data with IOCTL command #{ioctl_command}, the device might not support this operation."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class DeviceFinder
|
58
|
+
def find(device_name_pattern)
|
59
|
+
device_name_pattern = Regexp.new(device_name_pattern) if device_name_pattern.is_a?(String)
|
60
|
+
event_path = find_pointer_device_path(device_name_pattern)
|
61
|
+
return nil unless event_path
|
62
|
+
|
63
|
+
hidraw_path = find_hidraw_path(event_path)
|
64
|
+
|
65
|
+
return Device.new(hidraw_path: hidraw_path) if hidraw_path
|
66
|
+
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def find_hidraw_path(event_path)
|
73
|
+
event_abs_path = File.realpath(event_path)
|
74
|
+
parent_path = event_abs_path.gsub(%r{/input/input\d+/.*}, "")
|
75
|
+
locate_hidraw_device(parent_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_pointer_device_path(device_name_pattern)
|
79
|
+
Fusuma::Device.reset
|
80
|
+
|
81
|
+
device = Fusuma::Device.all.find do |device|
|
82
|
+
device.name =~ device_name_pattern && device.capabilities == "pointer"
|
83
|
+
end
|
84
|
+
|
85
|
+
device&.then { |d| "/sys/class/input/#{d.id}" }
|
86
|
+
end
|
87
|
+
|
88
|
+
def locate_hidraw_device(parent_path)
|
89
|
+
Dir.glob("#{parent_path}/hidraw/hidraw*").find do |path|
|
90
|
+
if File.exist?(path)
|
91
|
+
hidraw_device_path = path.gsub(%r{^/.*hidraw/hidraw}, "/dev/hidraw")
|
92
|
+
return hidraw_device_path if File.readable?(hidraw_device_path)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fusuma
|
4
|
+
module Plugin
|
5
|
+
module Inputs
|
6
|
+
class Hidraw
|
7
|
+
class HhkbBluetoothParser
|
8
|
+
BASE_TIMEOUT = 0.03 # Base timeout value for reading reports
|
9
|
+
MAX_TIMEOUT = 0.2 # Maximum timeout value before failure
|
10
|
+
MULTIPLIER = 1.1 # Multiplier to exponentially increase timeout
|
11
|
+
|
12
|
+
MAX_REPORT_SIZE = 9 # Maximum report size in bytes
|
13
|
+
|
14
|
+
# @param hidraw_device [Hidraw::Device] the HID raw device
|
15
|
+
def initialize(hidraw_device)
|
16
|
+
@hidraw_device = hidraw_device
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parse HID raw device events.
|
20
|
+
def parse
|
21
|
+
File.open(@hidraw_device.hidraw_path, "rb") do |device|
|
22
|
+
timeout = nil
|
23
|
+
|
24
|
+
# Continuously read reports from the device.
|
25
|
+
while (report = read_with_timeout(device, timeout))
|
26
|
+
mouse_state = if report.empty?
|
27
|
+
# Handle timeout case
|
28
|
+
:end
|
29
|
+
else
|
30
|
+
case parse_hid_report(report)
|
31
|
+
when :mouse
|
32
|
+
case mouse_state
|
33
|
+
when :begin, :update
|
34
|
+
:update
|
35
|
+
else
|
36
|
+
:begin
|
37
|
+
end
|
38
|
+
when :keyboard
|
39
|
+
# Continue mouse_state when keyboard operation
|
40
|
+
mouse_state
|
41
|
+
else
|
42
|
+
:end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
case mouse_state
|
47
|
+
when :begin, :update
|
48
|
+
timeout = update_timeout(timeout)
|
49
|
+
when :end
|
50
|
+
timeout = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
yield mouse_state
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Reads the HID report from the device with a timeout.
|
59
|
+
# @param device [File] the opened device file
|
60
|
+
# @param timeout [Float] the timeout duration
|
61
|
+
# @return [String] the HID report as bytes or an empty string on timeout
|
62
|
+
def read_with_timeout(device, timeout)
|
63
|
+
# puts "Timeout: #{timeout}" # Log timeout for debugging
|
64
|
+
Timeout.timeout(timeout) { device.read(MAX_REPORT_SIZE) }
|
65
|
+
rescue Timeout::Error
|
66
|
+
""
|
67
|
+
end
|
68
|
+
|
69
|
+
# Update the timeout based on previous value.
|
70
|
+
# @param timeout [Float, nil] previously set timeout
|
71
|
+
# @return [Float] the updated timeout value
|
72
|
+
def update_timeout(timeout)
|
73
|
+
return BASE_TIMEOUT if timeout.nil?
|
74
|
+
|
75
|
+
[timeout * MULTIPLIER, MAX_TIMEOUT].min
|
76
|
+
end
|
77
|
+
|
78
|
+
# Parse the HID report to determine its type.
|
79
|
+
# @param report_bytes [String] the HID report as byte data
|
80
|
+
# @return [Symbol, nil] symbol indicating type of report or nil on error
|
81
|
+
def parse_hid_report(report_bytes)
|
82
|
+
report_id = report_bytes.getbyte(0)
|
83
|
+
case report_id
|
84
|
+
when 1
|
85
|
+
# parse_mouse_report(report_bytes)
|
86
|
+
:mouse
|
87
|
+
when 127
|
88
|
+
# parse_keyboard_report(report_bytes)
|
89
|
+
:keyboard
|
90
|
+
else
|
91
|
+
MultiLogger.warn "Unknown Report ID: #{report_id}"
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Parse mouse report data.
|
97
|
+
# @param report_bytes [String] the HID mouse report as byte data
|
98
|
+
def parse_mouse_report(report_bytes)
|
99
|
+
puts "Raw bytes: #{report_bytes.inspect}" # Display raw byte bytes
|
100
|
+
|
101
|
+
report_id, buttons, x, y, wheel, ac_pan = report_bytes.unpack("CCcccc") # Retrieve 6-byte report
|
102
|
+
# - `C`: 1 byte unsigned integer (report ID) (0..255)
|
103
|
+
# - `C`: 1 byte unsigned integer (button state) (0..255)
|
104
|
+
# - `c`: 1 byte signed integer (x-axis) (-128..127)
|
105
|
+
# - `c`: 1 byte signed integer (y-axis) (-128..127)
|
106
|
+
# - `c`: 1 byte signed integer (wheel) (-128..127)
|
107
|
+
# - `c`: 1 byte signed integer (AC pan) (-128..127)
|
108
|
+
button_states = buttons.to_s(2).rjust(8, "0").chars.map(&:to_i)
|
109
|
+
|
110
|
+
puts "# ReportID: #{report_id} / Button: #{button_states.join(" ")} | X: #{x.to_s.rjust(4)} | Y: #{y.to_s.rjust(4)} | Wheel: #{wheel.to_s.rjust(4)} | AC Pan: #{ac_pan.to_s.rjust(4)}"
|
111
|
+
end
|
112
|
+
|
113
|
+
# Parse keyboard report data.
|
114
|
+
# @param report_bytes [String] the HID keyboard report as byte data
|
115
|
+
def parse_keyboard_report(report_bytes)
|
116
|
+
report_id, modifiers, _reserved1, *keys = report_bytes.unpack("CCCC6") # Retrieve 9-byte report
|
117
|
+
# - `C`: 1 byte unsigned integer (report ID) (0..255)
|
118
|
+
# - `C`: 1 byte unsigned integer (modifier keys) (0..255)
|
119
|
+
# - `C`: 1 byte reserved (0)
|
120
|
+
# - `C`: 6 bytes of keycodes (0..255)
|
121
|
+
modifier_states = %w[LeftControl LeftShift LeftAlt LeftGUI RightControl RightShift RightAlt RightGUI].map.with_index { |m, i| "#{m}: #{((modifiers & (1 << i)) != 0) ? 1 : 0}" }
|
122
|
+
keys_output = keys.map { |key| (key == 0) ? "0x70000" : translate_keycode(key) }
|
123
|
+
puts "# ReportID: #{report_id} / #{modifier_states.join(" | ")} | Keyboard #{keys_output}"
|
124
|
+
end
|
125
|
+
|
126
|
+
# Translate keycode to its string representation.
|
127
|
+
# @param keycode [Integer] the keycode to translate
|
128
|
+
# @return [String] the string representation of the keycode
|
129
|
+
def translate_keycode(keycode)
|
130
|
+
# Map of keycodes to their respective characters
|
131
|
+
keycodes = {
|
132
|
+
4 => "a and A", 7 => "d and D", 16 => "s and S", 19 => "w and W",
|
133
|
+
9 => "f and F", 10 => "g and G", 14 => "j and J", 15 => "k and K",
|
134
|
+
33 => "[ and {", 47 => "] and }"
|
135
|
+
# Add more as needed
|
136
|
+
}
|
137
|
+
keycodes[keycode] || "0x#{keycode.to_s(16)}" # Return hexadecimal if not found
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if $PROGRAM_NAME == __FILE__
|
146
|
+
require "timeout"
|
147
|
+
|
148
|
+
require_relative "device"
|
149
|
+
require "fusuma/plugin/inputs/libinput_command_input"
|
150
|
+
|
151
|
+
device = Fusuma::Plugin::Inputs::Hidraw::DeviceFinder.new.find("HHKB-Studio")
|
152
|
+
return if device.nil?
|
153
|
+
|
154
|
+
puts "Device: #{device.name} (#{device.vendor_id}:#{device.product_id})"
|
155
|
+
if device.bustype == Fusuma::Plugin::Inputs::Hidraw::Device::BUS_BLUETOOTH
|
156
|
+
Fusuma::Plugin::Inputs::Hidraw::HhkbBluetoothParser.new(device).parse do |state|
|
157
|
+
puts "Touch state: #{state}"
|
158
|
+
end
|
159
|
+
else
|
160
|
+
puts "Bustype is not BUS_BLUETOOTH"
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fusuma
|
4
|
+
module Plugin
|
5
|
+
module Inputs
|
6
|
+
class Hidraw
|
7
|
+
class HhkbUsbParser
|
8
|
+
BASE_TIMEOUT = 0.03 # Base timeout value for reading reports
|
9
|
+
MAX_TIMEOUT = 0.2 # Maximum timeout value before failure
|
10
|
+
MULTIPLIER = 1.1 # Multiplier to exponentially increase timeout
|
11
|
+
|
12
|
+
MAX_REPORT_SIZE = 5 # Maximum report size in bytes
|
13
|
+
|
14
|
+
# @param hidraw_device [Hidraw::Device] the HID raw device
|
15
|
+
def initialize(hidraw_device)
|
16
|
+
@hidraw_device = hidraw_device
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parse HID raw device events.
|
20
|
+
def parse
|
21
|
+
File.open(@hidraw_device.hidraw_path, "rb") do |device|
|
22
|
+
timeout = nil
|
23
|
+
|
24
|
+
# Continuously read reports from the device.
|
25
|
+
while (report = read_with_timeout(device, timeout))
|
26
|
+
mouse_state = if report.empty?
|
27
|
+
# Handle timeout case
|
28
|
+
:end
|
29
|
+
else
|
30
|
+
# instance.parse_hid_report(report_bytes)
|
31
|
+
case mouse_state
|
32
|
+
when :begin, :update
|
33
|
+
:update
|
34
|
+
else
|
35
|
+
:begin
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
case mouse_state
|
40
|
+
when :begin, :update
|
41
|
+
timeout = update_timeout(timeout)
|
42
|
+
when :end
|
43
|
+
timeout = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
yield mouse_state
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Reads the HID report from the device with a timeout.
|
52
|
+
# @param device [File] the opened device file
|
53
|
+
# @param timeout [Float] the timeout duration
|
54
|
+
# @return [String] the HID report as bytes or an empty string on timeout
|
55
|
+
def read_with_timeout(device, timeout)
|
56
|
+
# puts "Timeout: #{timeout}" # Log timeout for debugging
|
57
|
+
Timeout.timeout(timeout) { device.read(MAX_REPORT_SIZE) }
|
58
|
+
rescue Timeout::Error
|
59
|
+
""
|
60
|
+
end
|
61
|
+
|
62
|
+
# Update the timeout based on previous value.
|
63
|
+
# @param timeout [Float, nil] previously set timeout
|
64
|
+
# @return [Float] the updated timeout value
|
65
|
+
def update_timeout(timeout)
|
66
|
+
return BASE_TIMEOUT if timeout.nil?
|
67
|
+
|
68
|
+
[timeout * MULTIPLIER, MAX_TIMEOUT].min
|
69
|
+
end
|
70
|
+
|
71
|
+
# Parse the HID report to determine its type.
|
72
|
+
# @param report_bytes [String] the HID report as byte data
|
73
|
+
# @return [Symbol, nil] symbol indicating type of report or nil on error
|
74
|
+
def parse_hid_report(report_bytes)
|
75
|
+
return :end if report_bytes.nil?
|
76
|
+
|
77
|
+
# buttons, x, y, wheel, ac_pan = report_bytes.unpack("Ccccc") # Retrieve 5-byte report
|
78
|
+
# - `C`: 1 byte unsigned integer (button state) (0..255)
|
79
|
+
# - `c`: 1 byte signed integer (X-axis) (-127..127)
|
80
|
+
# - `c`: 1 byte signed integer (Y-axis) (-127..127)
|
81
|
+
# - `c`: 1 byte signed integer (Wheel) (-127..127)
|
82
|
+
# - `c`: 1 byte signed integer (AC pan) (-127..127)
|
83
|
+
# button_states = buttons.to_s(2).rjust(8, "0").chars.map(&:to_i)
|
84
|
+
|
85
|
+
# puts "Raw bytes: #{report_bytes.inspect}" # Display raw byte sequence
|
86
|
+
# puts "# Button: #{button_states.join(" ")} | X: #{x.to_s.rjust(4)} | Y: #{y.to_s.rjust(4)} | Wheel: #{wheel.to_s.rjust(4)} | AC Pan: #{ac_pan.to_s.rjust(4)}"
|
87
|
+
|
88
|
+
:begin
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if $PROGRAM_NAME == __FILE__
|
97
|
+
require "timeout"
|
98
|
+
|
99
|
+
require_relative "device"
|
100
|
+
require "fusuma/plugin/inputs/libinput_command_input"
|
101
|
+
|
102
|
+
device = Fusuma::Plugin::Inputs::Hidraw::DeviceFinder.new.find("HHKB-Studio")
|
103
|
+
return if device.nil?
|
104
|
+
|
105
|
+
puts "Device: #{device.name} (#{device.vendor_id}:#{device.product_id})"
|
106
|
+
if device.bustype == Fusuma::Plugin::Inputs::Hidraw::Device::BUS_USB
|
107
|
+
Fusuma::Plugin::Inputs::Hidraw::HhkbUsbParser.new(device).parse do |state|
|
108
|
+
puts "Touch state: #{state}"
|
109
|
+
end
|
110
|
+
else
|
111
|
+
puts "Bustype is not USB"
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fusuma/device"
|
4
|
+
|
5
|
+
require_relative "hidraw/device"
|
6
|
+
require_relative "hidraw/hhkb_bluetooth_parser"
|
7
|
+
require_relative "hidraw/hhkb_usb_parser"
|
8
|
+
|
9
|
+
module Fusuma
|
10
|
+
module Plugin
|
11
|
+
module Inputs
|
12
|
+
# Read pointing stick events
|
13
|
+
class PointingStickInput < Input
|
14
|
+
def config_param_types
|
15
|
+
{
|
16
|
+
device_name_pattern: String
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
super
|
22
|
+
@device_name_pattern = config_params(:device_name_pattern)
|
23
|
+
end
|
24
|
+
|
25
|
+
def io
|
26
|
+
@io ||= begin
|
27
|
+
reader, writer = IO.pipe
|
28
|
+
Thread.new do
|
29
|
+
process_device_events(writer)
|
30
|
+
writer.close
|
31
|
+
end
|
32
|
+
|
33
|
+
reader
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_device_events(writer)
|
38
|
+
hidraw_device = find_hidraw_device(@device_name_pattern, wait: 3)
|
39
|
+
hidraw_parser = select_hidraw_parser(hidraw_device.bustype)
|
40
|
+
|
41
|
+
mouse_state = nil
|
42
|
+
|
43
|
+
hidraw_parser.new(hidraw_device).parse do |new_state|
|
44
|
+
next if mouse_state == new_state
|
45
|
+
|
46
|
+
mouse_state = new_state
|
47
|
+
writer.puts(mouse_state)
|
48
|
+
end
|
49
|
+
rescue Errno::EIO => e
|
50
|
+
MultiLogger.error "#{self.class.name}: #{e}"
|
51
|
+
retry
|
52
|
+
end
|
53
|
+
|
54
|
+
# Override Input#read_from_io
|
55
|
+
def read_from_io
|
56
|
+
status = io.readline(chomp: true)
|
57
|
+
Events::Records::GestureRecord.new(gesture: "touch", status: status, finger: 1, delta: nil)
|
58
|
+
rescue EOFError => e
|
59
|
+
MultiLogger.error "#{self.class.name}: #{e}"
|
60
|
+
MultiLogger.error "Shutdown fusuma process..."
|
61
|
+
Process.kill("TERM", Process.pid)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Retry and wait until hidraw is found
|
67
|
+
def find_hidraw_device(device_name_pattern, wait:)
|
68
|
+
device_finder = Hidraw::DeviceFinder.new
|
69
|
+
logged = false
|
70
|
+
loop do
|
71
|
+
device = device_finder.find(device_name_pattern)
|
72
|
+
if device
|
73
|
+
MultiLogger.info "Found pointing stick device: #{device_name_pattern}"
|
74
|
+
|
75
|
+
return device
|
76
|
+
end
|
77
|
+
|
78
|
+
MultiLogger.warn "No pointing stick device found: #{device_name_pattern}" unless logged
|
79
|
+
logged = true
|
80
|
+
|
81
|
+
sleep wait
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Select parser based on the bus type
|
86
|
+
# @param bustype [Integer]
|
87
|
+
def select_hidraw_parser(bustype)
|
88
|
+
case bustype
|
89
|
+
when Hidraw::Device::BUS_BLUETOOTH
|
90
|
+
Hidraw::HhkbBluetoothParser
|
91
|
+
when Hidraw::Device::BUS_USB
|
92
|
+
Hidraw::HhkbUsbParser
|
93
|
+
else
|
94
|
+
MultiLogger.error "Unsupported bus type: #{bustype}"
|
95
|
+
exit 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fusuma-plugin-thumbsense
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iberianpig
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fusuma
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.11'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.11'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: fusuma-plugin-remap
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
47
|
+
version: '0.4'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
54
|
+
version: '0.4'
|
55
55
|
description: fusuma-plugin-thumbsense is Fusuma plugin for thumbsense.
|
56
56
|
email:
|
57
57
|
- yhkyky@gmail.com
|
@@ -65,10 +65,13 @@ files:
|
|
65
65
|
- bin/setup
|
66
66
|
- fusuma-plugin-thumbsense.gemspec
|
67
67
|
- lib/fusuma/plugin/buffers/thumbsense_buffer.rb
|
68
|
+
- lib/fusuma/plugin/buffers/thumbsense_buffer.yml
|
68
69
|
- lib/fusuma/plugin/detectors/thumbsense_detector.rb
|
69
|
-
- lib/fusuma/plugin/
|
70
|
-
- lib/fusuma/plugin/
|
71
|
-
- lib/fusuma/plugin/
|
70
|
+
- lib/fusuma/plugin/inputs/hidraw/device.rb
|
71
|
+
- lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb
|
72
|
+
- lib/fusuma/plugin/inputs/hidraw/hhkb_usb_parser.rb
|
73
|
+
- lib/fusuma/plugin/inputs/pointing_stick_input.rb
|
74
|
+
- lib/fusuma/plugin/inputs/pointing_stick_input.yml
|
72
75
|
- lib/fusuma/plugin/thumbsense.rb
|
73
76
|
- lib/fusuma/plugin/thumbsense/version.rb
|
74
77
|
homepage: https://github.com/iberianpig/fusuma-plugin-thumbsense
|
@@ -91,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
94
|
- !ruby/object:Gem::Version
|
92
95
|
version: '0'
|
93
96
|
requirements: []
|
94
|
-
rubygems_version: 3.4.
|
97
|
+
rubygems_version: 3.4.19
|
95
98
|
signing_key:
|
96
99
|
specification_version: 4
|
97
100
|
summary: Thumbsense plugin for Fusuma
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fusuma/device"
|
4
|
-
|
5
|
-
module Fusuma
|
6
|
-
module Plugin
|
7
|
-
module Filters
|
8
|
-
# Filter keyboard events from libinput_command_input
|
9
|
-
class ThumbsenseFilter < Filter
|
10
|
-
DEFAULT_SOURCE = "libinput_command_input"
|
11
|
-
|
12
|
-
# @return [TrueClass] when keeping it
|
13
|
-
# @return [FalseClass] when discarding it
|
14
|
-
def keep?(record)
|
15
|
-
case record.to_s
|
16
|
-
when %r{\sevent\d+\s+-\sbutton state: touch (?<finger>[[:digit:]])}
|
17
|
-
true
|
18
|
-
when %r{\sevent\d+\s+-\spalm: touch (?<finger>[[:digit:]])}
|
19
|
-
true
|
20
|
-
else
|
21
|
-
false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# require 'fusuma/plugin/parsers/parser.rb'
|
4
|
-
# require 'fusuma/plugin/events/event.rb'
|
5
|
-
|
6
|
-
module Fusuma
|
7
|
-
module Plugin
|
8
|
-
module Parsers
|
9
|
-
# parse libinput and generate event
|
10
|
-
class ThumbsenseParser < Parser
|
11
|
-
DEFAULT_SOURCE = "libinput_command_input"
|
12
|
-
|
13
|
-
# ... event7 - button state: touch 3 from BUTTON_STATE_AREA event BUTTON_EVENT_UP to BUTTON_STATE_NONE
|
14
|
-
# 10766: event7 - button state: touch 1 from BUTTON_STATE_AREA event BUTTON_EVENT_UP to BUTTON_STATE_NONE
|
15
|
-
# 10768: event7 - button state: touch 0 from BUTTON_STATE_AREA event BUTTON_EVENT_UP to BUTTON_STATE_NONE
|
16
|
-
|
17
|
-
# @param record [String]
|
18
|
-
# @return [Records::Gesture, nil]
|
19
|
-
def parse_record(record)
|
20
|
-
gesture = "touch"
|
21
|
-
|
22
|
-
case record.to_s
|
23
|
-
|
24
|
-
# touched
|
25
|
-
when %r{\sevent\d+\s+-\sbutton state: touch (?<finger>[[:digit:]]) from BUTTON_STATE_NONE}
|
26
|
-
status = "begin"
|
27
|
-
finger = $~[:finger].to_i + 1
|
28
|
-
# released
|
29
|
-
when %r{\sevent\d+\s+-\sbutton state: touch (?<finger>[[:digit:]]) .* to BUTTON_STATE_NONE}
|
30
|
-
status = "end"
|
31
|
-
finger = $~[:finger].to_i + 1
|
32
|
-
|
33
|
-
# palm
|
34
|
-
when %r{\sevent\d+\s+-\spalm: touch (?<finger>[[:digit:]]) .*}
|
35
|
-
status = "palm"
|
36
|
-
finger = $~[:finger].to_i + 1
|
37
|
-
else
|
38
|
-
return
|
39
|
-
end
|
40
|
-
|
41
|
-
Events::Records::GestureRecord.new(status: status, gesture: gesture, finger: finger, delta: nil)
|
42
|
-
end
|
43
|
-
|
44
|
-
def tag
|
45
|
-
"thumbsense_parser"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|