fusuma 2.1.0 → 2.2.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 +60 -39
- data/fusuma.gemspec +4 -1
- data/lib/fusuma/libinput_command.rb +1 -8
- data/lib/fusuma/multi_logger.rb +6 -7
- data/lib/fusuma/plugin/buffers/gesture_buffer.rb +6 -1
- data/lib/fusuma/plugin/detectors/hold_detector.rb +129 -0
- data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +9 -4
- data/lib/fusuma/version.rb +1 -1
- data/spec/lib/libinput_command_spec.rb +1 -1
- data/spec/lib/plugin/buffers/gesture_buffer_spec.rb +34 -14
- data/spec/lib/plugin/detectors/hold_detector_spec.rb +145 -0
- data/spec/lib/plugin/parsers/libinput_gesture_parser_spec.rb +76 -0
- metadata +8 -3
- data/lib/fusuma/plugin/filters/libinput_timeout_filter.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 701d329da6fb23d22958f210648a1bf30b73f947064c94c2f30a1ab634edf5e4
|
4
|
+
data.tar.gz: 6335546af4a4624fd450107800b5fde80692f0aa60ab910758be940163006d54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 931976b9864eb6077a6ebbc34b4425848782cb1b40358ada6e1b2dae6b145bff5382ec61b17ef58835ce1b179be28fca13d76e870243d0f70c40cddb67317700
|
7
|
+
data.tar.gz: 4ccc5737a5ae4d9f979fb409374f3f02c285046f266e368a05c1f5f6e846bfb9ddd47af3bff809f9f6ed1329564aeaaa66b5723ac7e7f342b2031b0322cefbea
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Fusuma [](https://badge.fury.io/rb/fusuma) [](https://badge.fury.io/rb/fusuma) [](https://github.com/iberianpig/fusuma/actions/workflows/ubuntu.yml) [](https://coveralls.io/github/iberianpig/fusuma?branch=master)
|
2
2
|
|
3
3
|
Fusuma is multitouch gesture recognizer.
|
4
4
|
This gem makes your linux able to recognize swipes or pinchs and assign commands to them.
|
@@ -22,13 +22,13 @@ This gem makes your linux able to recognize swipes or pinchs and assign commands
|
|
22
22
|
**IMPORTANT**: You **MUST** be a member of the **INPUT** group to read touchpad by Fusuma.
|
23
23
|
|
24
24
|
```bash
|
25
|
-
|
25
|
+
sudo gpasswd -a $USER input
|
26
26
|
```
|
27
27
|
|
28
28
|
Then, You apply the change with no logout or reboot.
|
29
29
|
|
30
30
|
```bash
|
31
|
-
|
31
|
+
newgrp input
|
32
32
|
```
|
33
33
|
|
34
34
|
### For Debian Based Distros (Ubuntu, Debian, Mint, Pop!OS)
|
@@ -38,7 +38,7 @@ $ newgrp input
|
|
38
38
|
You need `libinput` release 1.0 or later.
|
39
39
|
|
40
40
|
```bash
|
41
|
-
|
41
|
+
sudo apt-get install libinput-tools
|
42
42
|
```
|
43
43
|
|
44
44
|
#### 2. Install Ruby
|
@@ -46,13 +46,13 @@ $ sudo apt-get install libinput-tools
|
|
46
46
|
Fusuma runs in Ruby, so you must install it first.
|
47
47
|
|
48
48
|
```bash
|
49
|
-
|
49
|
+
sudo apt-get install ruby
|
50
50
|
```
|
51
51
|
|
52
52
|
#### 3. Install Fusuma
|
53
53
|
|
54
54
|
```bash
|
55
|
-
|
55
|
+
sudo gem install fusuma
|
56
56
|
```
|
57
57
|
|
58
58
|
#### 4. Install xdotool (optional)
|
@@ -60,7 +60,7 @@ $ sudo gem install fusuma
|
|
60
60
|
For sending shortcuts:
|
61
61
|
|
62
62
|
```bash
|
63
|
-
|
63
|
+
sudo apt-get install xdotool
|
64
64
|
```
|
65
65
|
|
66
66
|
### For Arch Based Distros (Manjaro, Arch)
|
@@ -70,7 +70,7 @@ $ sudo apt-get install xdotool
|
|
70
70
|
You need `libinput` release 1.0 or later. This is most probably installed by default on Manjaro
|
71
71
|
|
72
72
|
```z-h
|
73
|
-
|
73
|
+
sudo pacman -S libinput
|
74
74
|
```
|
75
75
|
|
76
76
|
#### 2. Install Ruby
|
@@ -78,7 +78,7 @@ $ sudo pacman -S libinput
|
|
78
78
|
Fusuma runs in Ruby, so you must install it first.
|
79
79
|
|
80
80
|
```zsh
|
81
|
-
|
81
|
+
sudo pacman -S ruby
|
82
82
|
```
|
83
83
|
|
84
84
|
#### 3. Install Fusuma
|
@@ -88,7 +88,7 @@ $ sudo pacman -S ruby
|
|
88
88
|
To install gems system-wide, see any of the methods listed on [Arch Wiki](https://wiki.archlinux.org/index.php/ruby#Installing_gems_system-wide)
|
89
89
|
|
90
90
|
```zsh
|
91
|
-
|
91
|
+
sudo gem install fusuma
|
92
92
|
```
|
93
93
|
|
94
94
|
#### 4. Install xdotool (optional)
|
@@ -96,7 +96,7 @@ $ sudo gem install fusuma
|
|
96
96
|
For sending shortcuts:
|
97
97
|
|
98
98
|
```zsh
|
99
|
-
|
99
|
+
sudo pacman -S xdotool
|
100
100
|
```
|
101
101
|
|
102
102
|
### Touchpad not working in GNOME
|
@@ -104,19 +104,19 @@ $ sudo pacman -S xdotool
|
|
104
104
|
Ensure the touchpad events are being sent to the GNOME desktop by running the following command:
|
105
105
|
|
106
106
|
```bash
|
107
|
-
|
107
|
+
gsettings set org.gnome.desktop.peripherals.touchpad send-events enabled
|
108
108
|
```
|
109
109
|
|
110
110
|
## Usage
|
111
111
|
|
112
112
|
```bash
|
113
|
-
|
113
|
+
fusuma
|
114
114
|
```
|
115
115
|
|
116
116
|
## Update
|
117
117
|
|
118
118
|
```bash
|
119
|
-
|
119
|
+
sudo gem update fusuma
|
120
120
|
```
|
121
121
|
|
122
122
|
## Customize Gesture Mapping
|
@@ -125,25 +125,35 @@ You can customize the settings for gestures to put and edit `~/.config/fusuma/co
|
|
125
125
|
**NOTE: You will need to create the `~/.config/fusuma` directory if it doesn't exist yet.**
|
126
126
|
|
127
127
|
```bash
|
128
|
-
|
129
|
-
|
128
|
+
mkdir -p ~/.config/fusuma # create config directory
|
129
|
+
nano ~/.config/fusuma/config.yml # edit config file.
|
130
130
|
```
|
131
131
|
|
132
132
|
|
133
133
|
### Available gestures
|
134
134
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
* `
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
* `
|
144
|
-
|
145
|
-
|
146
|
-
|
135
|
+
#### swipe:
|
136
|
+
|
137
|
+
* support `3:`, `4:` fingers
|
138
|
+
* support `left:`, `right:`, `up:`, `down:` directions
|
139
|
+
* support `begin:`, `update:`, `end:` events
|
140
|
+
|
141
|
+
#### pinch:
|
142
|
+
|
143
|
+
* support `2:`, `3:`, `4:` fingers
|
144
|
+
* support `in:`, `out:` directions
|
145
|
+
* support `begin:`, `update:`, `end:` events
|
146
|
+
|
147
|
+
#### rotate:
|
148
|
+
|
149
|
+
* support `2:`, `3:`, `4:` fingers
|
150
|
+
* support `clockwise:`,`counterclockwise:` directions
|
151
|
+
* support `begin:`, `update:`, `end:` events
|
152
|
+
|
153
|
+
#### hold:
|
154
|
+
* require libinput version 1.19.0 or later
|
155
|
+
* support `1:`, `2:`, `3:`, `4:` fingers
|
156
|
+
* support `begin:`, `end:`, `cancelled:` events
|
147
157
|
|
148
158
|
### About YAML Basic Syntax
|
149
159
|
|
@@ -181,6 +191,9 @@ pinch:
|
|
181
191
|
command: "xdotool keydown ctrl click 4 keyup ctrl" # Zoom in
|
182
192
|
out:
|
183
193
|
command: "xdotool keydown ctrl click 5 keyup ctrl" # Zoom out
|
194
|
+
hold:
|
195
|
+
4:
|
196
|
+
command: "xdotool key super" # Activity
|
184
197
|
```
|
185
198
|
|
186
199
|
### More Example of config.yml
|
@@ -201,12 +214,12 @@ If you have a nice configuration, please share `~/.config/fusuma/config.yml` wit
|
|
201
214
|
|
202
215
|
### Threshold and Interval
|
203
216
|
|
204
|
-
if `command:` properties are blank, the swipe/pinch doesn't execute command.
|
217
|
+
if `command:` properties are blank, the swipe/pinch/hold doesn't execute command.
|
205
218
|
|
206
|
-
`threshold:` is sensitivity to swipe/pinch. Default value is 1.
|
219
|
+
`threshold:` is sensitivity to swipe/pinch/hold. Default value is 1.
|
207
220
|
If the swipe's threshold is `0.5`, shorten swipe-length by half.
|
208
221
|
|
209
|
-
`interval:` is delay between swipes/pinches. Default value is 1.
|
222
|
+
`interval:` is delay between swipes/pinches/hold. Default value is 1.
|
210
223
|
If the swipe's interval is `0.5`, shorten swipe-interval by half to recognize a next swipe.
|
211
224
|
|
212
225
|
### Example of `threshold:` / `interval:` settings
|
@@ -243,7 +256,7 @@ There are three priorities of `threshold:` and `interval:`.
|
|
243
256
|
The individual `threshold:` and `interval:` settings (under "direction") have a higher priority than the global one (under "root")
|
244
257
|
|
245
258
|
1. child elements in the direction (left/right/down/up → threshold/interval)
|
246
|
-
1. root child elements (threshold/interval → swipe/pinch)
|
259
|
+
1. root child elements (threshold/interval → swipe/pinch/hold)
|
247
260
|
1. default value (= 1)
|
248
261
|
|
249
262
|
### `command:` property for assigning commands
|
@@ -314,8 +327,8 @@ plugin:
|
|
314
327
|
|
315
328
|
## Autostart (gnome-session-properties)
|
316
329
|
|
317
|
-
1. Check the path where you installed fusuma with
|
318
|
-
2. Open
|
330
|
+
1. Check the path where you installed fusuma with `which fusuma`
|
331
|
+
2. Open `gnome-session-properties`
|
319
332
|
3. Add Fusuma and enter the location where the above path was checked in the command input field
|
320
333
|
4. Add the `-d` option at the end of the command input field
|
321
334
|
|
@@ -327,14 +340,10 @@ Following features are provided as plugins.
|
|
327
340
|
- Features for specific Linux distributions
|
328
341
|
- Setting different gestures per applications
|
329
342
|
|
330
|
-
###
|
343
|
+
### Available plugins
|
331
344
|
|
332
345
|
Fusuma plugins are provided with the `fusuma-plugin-XXXXX` naming convention and hosted on [RubyGems](https://rubygems.org/search?utf8=%E2%9C%93&query=fusuma-plugins).
|
333
346
|
|
334
|
-
`$ sudo gem install fusuma-plugin-XXXXX`
|
335
|
-
|
336
|
-
### Available plugins
|
337
|
-
|
338
347
|
| Name | Version | About |
|
339
348
|
| ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------- |
|
340
349
|
| [fusuma-plugin-sendkey](https://github.com/iberianpig/fusuma-plugin-sendkey) |  | Emulates keyboard events |
|
@@ -343,6 +352,18 @@ Fusuma plugins are provided with the `fusuma-plugin-XXXXX` naming convention and
|
|
343
352
|
| [fusuma-plugin-tap](https://github.com/iberianpig/fusuma-plugin-tap) |  | Detects Tap and Hold gestures |
|
344
353
|
| [fusuma-plugin-appmatcher](https://github.com/iberianpig/fusuma-plugin-appmatcher) |  | Configure app-specific gestures |
|
345
354
|
|
355
|
+
|
356
|
+
### Installation of Fusuma plugins
|
357
|
+
|
358
|
+
```bash
|
359
|
+
# install fusuma-plugin-XXXX
|
360
|
+
sudo gem install fusuma-plugin-XXXXX`
|
361
|
+
```
|
362
|
+
```bash
|
363
|
+
# update
|
364
|
+
sudo gem list fusuma-plugin- | cut -d' ' -f1 | xargs --no-run-if-empty sudo gem update
|
365
|
+
```
|
366
|
+
|
346
367
|
## Tutorial Video
|
347
368
|
|
348
369
|
[](http://www.youtube.com/watch?v=bn11Iwvf29I "Multitouch Touchpad Gestures in Linux with Fusuma")
|
data/fusuma.gemspec
CHANGED
@@ -20,7 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.bindir = 'exe'
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ['lib']
|
23
|
-
spec.metadata
|
23
|
+
spec.metadata = {
|
24
|
+
'rubygems_mfa_required' => 'true',
|
25
|
+
'yard.run' => 'yri' # use "yard" to build full HTML docs.
|
26
|
+
}
|
24
27
|
|
25
28
|
spec.required_ruby_version = '>= 2.5.1' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all§ion=main
|
26
29
|
# support bionic (18.04LTS) 2.5.1
|
@@ -13,10 +13,7 @@ module Fusuma
|
|
13
13
|
|
14
14
|
# `libinput-list-devices` and `libinput-debug-events` are deprecated,
|
15
15
|
# use `libinput list-devices` and `libinput debug-events` from 1.8.
|
16
|
-
NEW_CLI_OPTION_VERSION = 1.8
|
17
|
-
|
18
|
-
DEFAULT_WAIT_TIME = 0.3
|
19
|
-
TIMEOUT_MESSAGE = 'LIBINPUT TIMEOUT'
|
16
|
+
NEW_CLI_OPTION_VERSION = '1.8'
|
20
17
|
|
21
18
|
# @return [Boolean]
|
22
19
|
def new_cli_option_available?
|
@@ -92,10 +89,6 @@ module Fusuma
|
|
92
89
|
|
93
90
|
private
|
94
91
|
|
95
|
-
def wait_time
|
96
|
-
DEFAULT_WAIT_TIME
|
97
|
-
end
|
98
|
-
|
99
92
|
# which in ruby: Checking if program exists in $PATH from ruby
|
100
93
|
# (https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby)
|
101
94
|
# Cross-platform way of finding an executable in the $PATH.
|
data/lib/fusuma/multi_logger.rb
CHANGED
@@ -11,21 +11,20 @@ module Fusuma
|
|
11
11
|
attr_reader :err_logger
|
12
12
|
attr_accessor :debug_mode
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
@@filepath = filepath
|
14
|
+
class << self
|
15
|
+
attr_writer :filepath
|
17
16
|
end
|
18
17
|
|
19
18
|
def initialize
|
20
|
-
|
21
|
-
|
19
|
+
filepath = self.class.instance_variable_get('@filepath')
|
20
|
+
if filepath
|
21
|
+
logfile = File.new(filepath, 'a')
|
22
22
|
super(logfile)
|
23
23
|
$stderr = logfile
|
24
|
-
@err_logger = Logger.new($stderr)
|
25
24
|
else
|
26
25
|
super($stdout)
|
27
|
-
@err_logger = Logger.new($stderr)
|
28
26
|
end
|
27
|
+
@err_logger = Logger.new($stderr)
|
29
28
|
@debug_mode = false
|
30
29
|
end
|
31
30
|
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './detector'
|
4
|
+
|
5
|
+
module Fusuma
|
6
|
+
module Plugin
|
7
|
+
module Detectors
|
8
|
+
# Detect Hold gesture
|
9
|
+
class HoldDetector < Detector
|
10
|
+
SOURCES = %w[gesture timer].freeze
|
11
|
+
BUFFER_TYPE = 'gesture'
|
12
|
+
GESTURE_RECORD_TYPE = 'hold'
|
13
|
+
|
14
|
+
BASE_THERESHOLD = 0.7
|
15
|
+
|
16
|
+
# @param buffers [Array<Buffers::Buffer>]
|
17
|
+
# @return [Events::Event] if event is detected
|
18
|
+
# @return [Array<Events::Event>] if hold end event is detected
|
19
|
+
# @return [NilClass] if event is NOT detected
|
20
|
+
def detect(buffers)
|
21
|
+
hold_buffer = find_hold_buffer(buffers)
|
22
|
+
return if hold_buffer.empty?
|
23
|
+
|
24
|
+
hold_events = hold_buffer.events
|
25
|
+
|
26
|
+
timer_buffer = buffers.find { |b| b.type == 'timer' }
|
27
|
+
timer_events = timer_buffer.events
|
28
|
+
|
29
|
+
finger = hold_buffer.finger
|
30
|
+
holding_time = calc_holding_time(hold_events: hold_events, timer_events: timer_events)
|
31
|
+
|
32
|
+
@timeout ||= nil
|
33
|
+
status = case hold_events.last.record.status
|
34
|
+
when 'begin'
|
35
|
+
if holding_time.zero?
|
36
|
+
'begin'
|
37
|
+
else
|
38
|
+
'timer'
|
39
|
+
end
|
40
|
+
when 'cancelled'
|
41
|
+
'cancelled'
|
42
|
+
when 'end'
|
43
|
+
'end'
|
44
|
+
else
|
45
|
+
last_record = hold_events.last.record.status
|
46
|
+
raise "Unexpected Status:#{last_record.status} in #{last_record}"
|
47
|
+
end
|
48
|
+
|
49
|
+
repeat_index = create_repeat_index(finger: finger, status: status)
|
50
|
+
oneshot_index = create_oneshot_index(finger: finger)
|
51
|
+
|
52
|
+
@timeout = nil if status == 'begin'
|
53
|
+
|
54
|
+
if status == 'timer'
|
55
|
+
return if @timeout
|
56
|
+
|
57
|
+
return unless enough?(index: oneshot_index, holding_time: holding_time)
|
58
|
+
|
59
|
+
@timeout = holding_time
|
60
|
+
return create_event(record: Events::Records::IndexRecord.new(
|
61
|
+
index: oneshot_index, trigger: :oneshot
|
62
|
+
))
|
63
|
+
end
|
64
|
+
|
65
|
+
create_event(record: Events::Records::IndexRecord.new(
|
66
|
+
index: repeat_index, trigger: :repeat
|
67
|
+
))
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param [Integer] finger
|
71
|
+
# @return [Config::Index]
|
72
|
+
def create_oneshot_index(finger:)
|
73
|
+
Config::Index.new(
|
74
|
+
[
|
75
|
+
Config::Index::Key.new(type),
|
76
|
+
Config::Index::Key.new(finger.to_i)
|
77
|
+
]
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param [Integer] finger
|
82
|
+
# @return [Config::Index]
|
83
|
+
def create_repeat_index(finger:, status:)
|
84
|
+
Config::Index.new(
|
85
|
+
[
|
86
|
+
Config::Index::Key.new(type),
|
87
|
+
Config::Index::Key.new(finger.to_i),
|
88
|
+
Config::Index::Key.new(status)
|
89
|
+
]
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# @param buffers [Array<Buffers::Buffer>]
|
96
|
+
# @return [Buffers::GestureBuffer]
|
97
|
+
def find_hold_buffer(buffers)
|
98
|
+
buffers.find { |b| b.type == BUFFER_TYPE }
|
99
|
+
.select_from_last_begin
|
100
|
+
.select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
|
101
|
+
end
|
102
|
+
|
103
|
+
def calc_holding_time(hold_events:, timer_events:)
|
104
|
+
last_time = if !timer_events.empty? && (hold_events.last.time < timer_events.last.time)
|
105
|
+
timer_events.last.time
|
106
|
+
else
|
107
|
+
hold_events.last.time
|
108
|
+
end
|
109
|
+
last_time - hold_events.first.time
|
110
|
+
end
|
111
|
+
|
112
|
+
def enough?(index:, holding_time:)
|
113
|
+
holding_time > threshold(index: index)
|
114
|
+
end
|
115
|
+
|
116
|
+
def threshold(index:)
|
117
|
+
@threshold ||= {}
|
118
|
+
@threshold[index.cache_key] ||= begin
|
119
|
+
keys_specific = Config::Index.new [*index.keys, 'threshold']
|
120
|
+
keys_global = Config::Index.new ['threshold', type]
|
121
|
+
config_value = Config.search(keys_specific) ||
|
122
|
+
Config.search(keys_global) || 1
|
123
|
+
BASE_THERESHOLD * config_value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -14,7 +14,7 @@ module Fusuma
|
|
14
14
|
# @return [Records::GestureRecord, nil]
|
15
15
|
def parse_record(record)
|
16
16
|
case line = record.to_s
|
17
|
-
when /GESTURE_SWIPE|GESTURE_PINCH/
|
17
|
+
when /GESTURE_SWIPE|GESTURE_PINCH|GESTURE_HOLD/
|
18
18
|
gesture, status, finger, delta = parse_libinput(line)
|
19
19
|
else
|
20
20
|
return
|
@@ -32,13 +32,18 @@ module Fusuma
|
|
32
32
|
_device, event_name, _time, other = line.strip.split(nil, 4)
|
33
33
|
finger, other = other.split(nil, 2)
|
34
34
|
|
35
|
+
gesture, status = *detect_gesture(event_name)
|
36
|
+
|
37
|
+
status = 'cancelled' if gesture == 'hold' && status == 'end' && other == 'cancelled'
|
35
38
|
delta = parse_delta(other)
|
36
|
-
[
|
39
|
+
[gesture, status, finger, delta]
|
37
40
|
end
|
38
41
|
|
39
42
|
def detect_gesture(event_name)
|
40
|
-
event_name =~ /GESTURE_(SWIPE|PINCH)_(BEGIN|UPDATE|END)/
|
41
|
-
|
43
|
+
event_name =~ /GESTURE_(SWIPE|PINCH|HOLD)_(BEGIN|UPDATE|END)/
|
44
|
+
gesture = Regexp.last_match(1).downcase
|
45
|
+
status = Regexp.last_match(2).downcase
|
46
|
+
[gesture, status]
|
42
47
|
end
|
43
48
|
|
44
49
|
def parse_delta(line)
|
data/lib/fusuma/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
+
require 'rspec-parameterized'
|
4
5
|
|
5
6
|
require './lib/fusuma/plugin/events/event'
|
6
7
|
require './lib/fusuma/plugin/events/records/gesture_record'
|
@@ -13,7 +14,7 @@ module Fusuma
|
|
13
14
|
before do
|
14
15
|
@buffer = GestureBuffer.new
|
15
16
|
delta = Events::Records::GestureRecord::Delta.new(-1, 0, 0, 0, 0, 0)
|
16
|
-
@event_generator = lambda { |time = nil, status = '
|
17
|
+
@event_generator = lambda { |time = nil, status = 'update'|
|
17
18
|
Events::Event.new(time: time,
|
18
19
|
tag: 'libinput_gesture_parser',
|
19
20
|
record: Events::Records::GestureRecord.new(
|
@@ -53,20 +54,39 @@ module Fusuma
|
|
53
54
|
end
|
54
55
|
|
55
56
|
describe '#clear_expired' do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
context 'default' do
|
58
|
+
before do
|
59
|
+
@time = Time.now
|
60
|
+
event1 = @event_generator.call(@time)
|
61
|
+
@buffer.buffer(event1)
|
62
|
+
@event2 = @event_generator.call(@time + 100)
|
63
|
+
@event3 = @event_generator.call(@time + 100)
|
64
|
+
@buffer.buffer(@event2)
|
65
|
+
@buffer.buffer(@event3)
|
66
|
+
end
|
67
|
+
it 'should keep only events generated within 30 seconds' do
|
68
|
+
@buffer.clear_expired(current_time: @time + 100.1)
|
69
|
+
expect(@buffer.events).to eq [@event2, @event3]
|
70
|
+
end
|
71
|
+
context 'with cancelled or ended' do
|
72
|
+
where(:last_state, :want) do
|
73
|
+
[
|
74
|
+
['end', []],
|
75
|
+
['cancelled', []]
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
with_them do
|
80
|
+
it 'should clear events' do
|
81
|
+
event4 = @event_generator.call(@time + 100, last_state)
|
82
|
+
@buffer.buffer(event4)
|
83
|
+
|
84
|
+
@buffer.clear_expired(current_time: @time + 100.1)
|
85
|
+
expect(@buffer.events).to eq want
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
68
89
|
end
|
69
|
-
|
70
90
|
context 'change seconds to keep' do
|
71
91
|
around do |example|
|
72
92
|
ConfigHelper.load_config_yml = <<~CONFIG
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require './lib/fusuma/plugin/detectors/hold_detector'
|
6
|
+
require './lib/fusuma/plugin/buffers/gesture_buffer'
|
7
|
+
require './lib/fusuma/plugin/events/records/gesture_record'
|
8
|
+
require './lib/fusuma/config'
|
9
|
+
|
10
|
+
module Fusuma
|
11
|
+
module Plugin
|
12
|
+
module Detectors
|
13
|
+
RSpec.describe HoldDetector do
|
14
|
+
before do
|
15
|
+
@detector = HoldDetector.new
|
16
|
+
@buffer = Buffers::GestureBuffer.new
|
17
|
+
@timer_buffer = Buffers::TimerBuffer.new
|
18
|
+
end
|
19
|
+
|
20
|
+
around do |example|
|
21
|
+
ConfigHelper.load_config_yml = <<~CONFIG
|
22
|
+
threshold:
|
23
|
+
hold: 1
|
24
|
+
CONFIG
|
25
|
+
|
26
|
+
example.run
|
27
|
+
|
28
|
+
Config.custom_path = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#detect' do
|
32
|
+
context 'with no hold event in buffer' do
|
33
|
+
before do
|
34
|
+
@buffer.clear
|
35
|
+
end
|
36
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).to eq nil }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with only hold begin event' do
|
40
|
+
before do
|
41
|
+
events = create_hold_events(statuses: ['begin'])
|
42
|
+
events.each { |event| @buffer.buffer(event) }
|
43
|
+
end
|
44
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
|
45
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
|
46
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
|
47
|
+
it 'should detect 3 fingers hold' do
|
48
|
+
event = @detector.detect([@buffer, @timer_buffer])
|
49
|
+
expect(event.record.index.keys.map(&:symbol)).to eq([:hold, 3, :begin])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with hold events(begin,cancelled)' do
|
54
|
+
before do
|
55
|
+
events = create_hold_events(statuses: %w[begin cancelled])
|
56
|
+
events.each { |event| @buffer.buffer(event) }
|
57
|
+
end
|
58
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
|
59
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
|
60
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
|
61
|
+
it 'should detect 3 fingers hold canclled' do
|
62
|
+
event = @detector.detect([@buffer, @timer_buffer])
|
63
|
+
expect(event.record.index.keys.map(&:symbol)).to eq([:hold, 3, :cancelled])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with hold events(begin,end)' do
|
68
|
+
before do
|
69
|
+
events = create_hold_events(statuses: %w[begin end])
|
70
|
+
events.each { |event| @buffer.buffer(event) }
|
71
|
+
end
|
72
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
|
73
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
|
74
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
|
75
|
+
it 'should detect 3 fingers hold' do
|
76
|
+
events = @detector.detect([@buffer, @timer_buffer])
|
77
|
+
expect(events.record.index.keys.map(&:symbol)).to eq([:hold, 3, :end])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with hold events and timer events' do
|
82
|
+
context 'with begin event and timer events' do
|
83
|
+
before do
|
84
|
+
events = create_hold_events(statuses: %w[begin])
|
85
|
+
events.each { |event| @buffer.buffer(event) }
|
86
|
+
@time = events.last.time
|
87
|
+
@timer_buffer.buffer(create_timer_event(time: @time + HoldDetector::BASE_THERESHOLD))
|
88
|
+
end
|
89
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).to eq nil }
|
90
|
+
|
91
|
+
context 'with enough holding time' do
|
92
|
+
before do
|
93
|
+
@timer_buffer.clear
|
94
|
+
@timer_buffer.buffer(create_timer_event(time: @time + HoldDetector::BASE_THERESHOLD + 0.01))
|
95
|
+
end
|
96
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
|
97
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
|
98
|
+
it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
|
99
|
+
it 'should detect 3 fingers hold' do
|
100
|
+
events = @detector.detect([@buffer, @timer_buffer])
|
101
|
+
expect(events.record.index.keys.map(&:symbol)).to eq([:hold, 3])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
context 'with changing threshold' do
|
105
|
+
around do |example|
|
106
|
+
ConfigHelper.load_config_yml = <<~CONFIG
|
107
|
+
threshold:
|
108
|
+
hold: 0.9
|
109
|
+
CONFIG
|
110
|
+
|
111
|
+
example.run
|
112
|
+
|
113
|
+
Config.custom_path = nil
|
114
|
+
end
|
115
|
+
|
116
|
+
it { expect(@detector.detect([@buffer, @timer_buffer])).not_to eq nil }
|
117
|
+
it 'should detect 3 fingers hold' do
|
118
|
+
events = @detector.detect([@buffer, @timer_buffer])
|
119
|
+
expect(events.record.index.keys.map(&:symbol)).to eq([:hold, 3])
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def create_hold_events(statuses:)
|
129
|
+
record_type = HoldDetector::GESTURE_RECORD_TYPE
|
130
|
+
statuses.map do |status|
|
131
|
+
gesture_record = Events::Records::GestureRecord.new(status: status,
|
132
|
+
gesture: record_type,
|
133
|
+
finger: 3,
|
134
|
+
delta: nil)
|
135
|
+
Events::Event.new(tag: 'libinput_gesture_parser', record: gesture_record)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_timer_event(time: Time.now)
|
140
|
+
Events::Event.new(time: time, tag: 'timer_input', record: Events::Records::TextRecord.new('timer'))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require './lib/fusuma/plugin/parsers/parser'
|
5
|
+
require './lib/fusuma/plugin/events/event'
|
6
|
+
|
7
|
+
module Fusuma
|
8
|
+
module Plugin
|
9
|
+
module Parsers
|
10
|
+
RSpec.describe LibinputGestureParser do
|
11
|
+
let(:parser) { LibinputGestureParser.new }
|
12
|
+
|
13
|
+
around do |example|
|
14
|
+
ConfigHelper.load_config_yml = <<~CONFIG
|
15
|
+
plugin:
|
16
|
+
parsers:
|
17
|
+
libinput_gesture_parser:
|
18
|
+
dummy: dummy
|
19
|
+
CONFIG
|
20
|
+
|
21
|
+
example.run
|
22
|
+
|
23
|
+
Config.custom_path = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#source' do
|
27
|
+
subject { parser.source }
|
28
|
+
|
29
|
+
it { is_expected.to be LibinputGestureParser::DEFAULT_SOURCE }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#parse' do
|
33
|
+
context 'with different tag(dummy) event' do
|
34
|
+
let(:event) { Events::Event.new(tag: 'dummy_input', record: 'dummy') }
|
35
|
+
it { expect(parser.parse(event).record).not_to be_a Events::Records::GestureRecord }
|
36
|
+
it { expect(parser.parse(event)).to eq event }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with libinput_command_input event' do
|
40
|
+
let(:event) { Events::Event.new(tag: 'libinput_command_input', record: record) }
|
41
|
+
context 'with swipe gestures' do
|
42
|
+
# event10 GESTURE_SWIPE_BEGIN +0.728s 3
|
43
|
+
# event10 GESTURE_SWIPE_UPDATE +0.948s 3 0.23/ 0.00 ( 0.29/ 0.00 unaccelerated)
|
44
|
+
# event10 GESTURE_SWIPE_END +0.989s 3
|
45
|
+
let(:record) { 'event10 GESTURE_SWIPE_BEGIN +0.728s 3' }
|
46
|
+
it { expect(parser.parse(event).record).to be_a Events::Records::GestureRecord }
|
47
|
+
it { expect(parser.parse(event).record.status).to eq 'begin' }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with hold gestures' do
|
51
|
+
# -event10 GESTURE_HOLD_BEGIN +2.125s 3
|
52
|
+
# event10 GESTURE_HOLD_END +3.274s 3
|
53
|
+
# event10 GESTURE_HOLD_BEGIN +5.573s 4
|
54
|
+
# event10 GESTURE_HOLD_END +6.462s 4 cancelled
|
55
|
+
context 'with begin' do
|
56
|
+
let(:record) { '-event10 GESTURE_HOLD_BEGIN +2.125s 3' }
|
57
|
+
it { expect(parser.parse(event).record).to be_a Events::Records::GestureRecord }
|
58
|
+
it { expect(parser.parse(event).record.status).to eq 'begin' }
|
59
|
+
end
|
60
|
+
context 'with end' do
|
61
|
+
let(:record) { ' event10 GESTURE_HOLD_END +3.274s 3' }
|
62
|
+
it { expect(parser.parse(event).record).to be_a Events::Records::GestureRecord }
|
63
|
+
it { expect(parser.parse(event).record.status).to eq 'end' }
|
64
|
+
end
|
65
|
+
context 'with end(cancelled)' do
|
66
|
+
let(:record) { ' event10 GESTURE_HOLD_END +6.462s 4 cancelled' }
|
67
|
+
it { expect(parser.parse(event).record).to be_a Events::Records::GestureRecord }
|
68
|
+
it { expect(parser.parse(event).record.status).to eq 'cancelled' }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fusuma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iberianpig
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Fusuma is multitouch gesture recognizer. This gem makes your touchpad
|
14
14
|
on Linux able to recognize swipes or pinchs and assign command to them. Read installation
|
@@ -43,6 +43,7 @@ files:
|
|
43
43
|
- lib/fusuma/plugin/buffers/gesture_buffer.rb
|
44
44
|
- lib/fusuma/plugin/buffers/timer_buffer.rb
|
45
45
|
- lib/fusuma/plugin/detectors/detector.rb
|
46
|
+
- lib/fusuma/plugin/detectors/hold_detector.rb
|
46
47
|
- lib/fusuma/plugin/detectors/pinch_detector.rb
|
47
48
|
- lib/fusuma/plugin/detectors/rotate_detector.rb
|
48
49
|
- lib/fusuma/plugin/detectors/swipe_detector.rb
|
@@ -56,7 +57,6 @@ files:
|
|
56
57
|
- lib/fusuma/plugin/executors/executor.rb
|
57
58
|
- lib/fusuma/plugin/filters/filter.rb
|
58
59
|
- lib/fusuma/plugin/filters/libinput_device_filter.rb
|
59
|
-
- lib/fusuma/plugin/filters/libinput_timeout_filter.rb
|
60
60
|
- lib/fusuma/plugin/inputs/input.rb
|
61
61
|
- lib/fusuma/plugin/inputs/libinput_command_input.rb
|
62
62
|
- lib/fusuma/plugin/inputs/timer_input.rb
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- spec/lib/plugin/buffers/gesture_buffer_spec.rb
|
85
85
|
- spec/lib/plugin/detectors/detector_spec.rb
|
86
86
|
- spec/lib/plugin/detectors/dummy_detector.rb
|
87
|
+
- spec/lib/plugin/detectors/hold_detector_spec.rb
|
87
88
|
- spec/lib/plugin/detectors/pinch_detector_spec.rb
|
88
89
|
- spec/lib/plugin/detectors/rotate_detector_spec.rb
|
89
90
|
- spec/lib/plugin/detectors/swipe_detector_spec.rb
|
@@ -99,12 +100,14 @@ files:
|
|
99
100
|
- spec/lib/plugin/inputs/libinput_command_input_spec.rb
|
100
101
|
- spec/lib/plugin/inputs/timer_input_spec.rb
|
101
102
|
- spec/lib/plugin/manager_spec.rb
|
103
|
+
- spec/lib/plugin/parsers/libinput_gesture_parser_spec.rb
|
102
104
|
- spec/lib/plugin/parsers/parser_spec.rb
|
103
105
|
- spec/spec_helper.rb
|
104
106
|
homepage: https://github.com/iberianpig/fusuma
|
105
107
|
licenses:
|
106
108
|
- MIT
|
107
109
|
metadata:
|
110
|
+
rubygems_mfa_required: 'true'
|
108
111
|
yard.run: yri
|
109
112
|
post_install_message:
|
110
113
|
rdoc_options: []
|
@@ -145,6 +148,7 @@ test_files:
|
|
145
148
|
- spec/lib/plugin/buffers/gesture_buffer_spec.rb
|
146
149
|
- spec/lib/plugin/detectors/detector_spec.rb
|
147
150
|
- spec/lib/plugin/detectors/dummy_detector.rb
|
151
|
+
- spec/lib/plugin/detectors/hold_detector_spec.rb
|
148
152
|
- spec/lib/plugin/detectors/pinch_detector_spec.rb
|
149
153
|
- spec/lib/plugin/detectors/rotate_detector_spec.rb
|
150
154
|
- spec/lib/plugin/detectors/swipe_detector_spec.rb
|
@@ -160,5 +164,6 @@ test_files:
|
|
160
164
|
- spec/lib/plugin/inputs/libinput_command_input_spec.rb
|
161
165
|
- spec/lib/plugin/inputs/timer_input_spec.rb
|
162
166
|
- spec/lib/plugin/manager_spec.rb
|
167
|
+
- spec/lib/plugin/parsers/libinput_gesture_parser_spec.rb
|
163
168
|
- spec/lib/plugin/parsers/parser_spec.rb
|
164
169
|
- spec/spec_helper.rb
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative './filter'
|
4
|
-
require_relative '../../libinput_command'
|
5
|
-
|
6
|
-
module Fusuma
|
7
|
-
module Plugin
|
8
|
-
module Filters
|
9
|
-
# Filter device log
|
10
|
-
class LibinputTimeoutFilter < Filter
|
11
|
-
DEFAULT_SOURCE = 'libinput_command_input'
|
12
|
-
|
13
|
-
# @return [TrueClass] when keeping it
|
14
|
-
# @return [FalseClass] when discarding it
|
15
|
-
def keep?(record)
|
16
|
-
record.to_s == LibinputCommand::TIMEOUT_MESSAGE
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|