fusuma-plugin-touchscreen 0.0.1.alpha → 0.1.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -0
  3. data/README.md +71 -2
  4. data/fusuma-plugin-touchscreen.gemspec +3 -2
  5. data/lib/fusuma/plugin/buffers/touch_buffer.rb +17 -81
  6. data/lib/fusuma/plugin/detectors/touch_detector.rb +31 -14
  7. data/lib/fusuma/plugin/detectors/touch_detectors/base.rb +7 -0
  8. data/lib/fusuma/plugin/detectors/touch_detectors/edge_detector.rb +0 -0
  9. data/lib/fusuma/plugin/detectors/touch_detectors/hold_detector.rb +11 -3
  10. data/lib/fusuma/plugin/detectors/touch_detectors/pinch_detector.rb +59 -1
  11. data/lib/fusuma/plugin/detectors/touch_detectors/rotate_detector.rb +65 -1
  12. data/lib/fusuma/plugin/detectors/touch_detectors/swipe_detector.rb +11 -9
  13. data/lib/fusuma/plugin/detectors/touch_detectors/tap_detector.rb +2 -2
  14. data/lib/fusuma/plugin/detectors/touch_detectors/tap_hold_base.rb +21 -0
  15. data/lib/fusuma/plugin/devices/touchscreen_device.rb +0 -0
  16. data/lib/fusuma/plugin/events/records/touch_record.rb +14 -1
  17. data/lib/fusuma/plugin/events/records/touch_records/base.rb +2 -0
  18. data/lib/fusuma/plugin/events/records/touch_records/features/direction.rb +36 -0
  19. data/lib/fusuma/plugin/events/records/touch_records/hold_record.rb +0 -0
  20. data/lib/fusuma/plugin/events/records/touch_records/pinch_record.rb +23 -0
  21. data/lib/fusuma/plugin/events/records/touch_records/rotate_record.rb +23 -0
  22. data/lib/fusuma/plugin/events/records/touch_records/swipe_record.rb +2 -12
  23. data/lib/fusuma/plugin/events/records/touch_records/tap_record.rb +0 -0
  24. data/lib/fusuma/plugin/parsers/touch_parser.rb +2 -47
  25. data/lib/fusuma/plugin/touchscreen/math.rb +68 -0
  26. data/lib/fusuma/plugin/touchscreen/version.rb +1 -1
  27. data/lib/fusuma/plugin/touchscreen.rb +0 -0
  28. data/spec/fusuma/plugin/buffers/touch_buffer_spec.rb +216 -0
  29. data/spec/fusuma/plugin/detectors/hold_detector_spec.rb +71 -0
  30. data/spec/fusuma/plugin/detectors/pinch_detector_spec.rb +96 -0
  31. data/spec/fusuma/plugin/detectors/rotate_detector_spec.rb +123 -0
  32. data/spec/fusuma/plugin/detectors/swipe_detector_spec.rb +141 -0
  33. data/spec/fusuma/plugin/detectors/tap_detector_spec.rb +84 -0
  34. data/spec/fusuma/plugin/detectors/touch_detector_spec.rb +266 -0
  35. data/spec/fusuma/plugin/devices/touchscreen_device_spec.rb +6 -3
  36. data/spec/fusuma/plugin/parsers/touch_parser_spec.rb +30 -5
  37. data/spec/samples/1-finger-hold.txt +0 -0
  38. data/spec/samples/2-fingers-rotate-clockwise.txt +426 -0
  39. data/spec/samples/2-fingers-rotate-counterclockwise.txt +459 -0
  40. data/spec/samples/2-fingers-swipe-right.txt +0 -0
  41. data/spec/samples/3-fingers-pinch-in.txt +140 -0
  42. data/spec/samples/3-fingers-pinch-out.txt +171 -0
  43. data/spec/samples/3-fingers-tap.txt +0 -0
  44. data/spec/{fixtures/libinput-list-devices_ms-surface-3-pro.txt → samples/libinput-list-devices.txt} +0 -0
  45. data/spec/spec_helper.rb +8 -0
  46. data/spec/support/helpers/events_and_records.rb +43 -0
  47. data/spec/support/shared_contexts/with_touch_buffer.rb +10 -0
  48. data/spec/support/shared_examples/real_sample.rb +29 -0
  49. metadata +40 -10
  50. data/lib/fusuma/utils/angle.rb +0 -12
  51. data/spec/samples/libinput-devices.txt +0 -182
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+ require 'fusuma/plugin/detectors/touch_detectors/rotate_detector'
3
+
4
+ module Fusuma
5
+ RSpec.describe Plugin::Detectors::TouchDetectors::RotateDetector do
6
+ include_context 'with touch buffer'
7
+ subject { described_class.new }
8
+
9
+ before do
10
+ allow(subject).to receive(:angle_threshold).and_return(0.1)
11
+ end
12
+
13
+ it 'no events' do
14
+ expect(subject.detect(touch_buffer)).to be_nil
15
+ end
16
+
17
+ it 'not moved' do
18
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
19
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
20
+
21
+ expect(subject.detect(touch_buffer)).to be_nil
22
+ end
23
+
24
+ it 'moved, only one finger' do
25
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
26
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 20, y_mm: 20)))
27
+
28
+ expect(subject.detect(touch_buffer)).to be_nil
29
+ end
30
+
31
+ it 'moved, 2 fingers, but not enough angle change' do
32
+ # left finger, a little bit to the down
33
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
34
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 10.1)))
35
+ # right finger, a little bit to the up
36
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 20, y_mm: 10)))
37
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 9.99)))
38
+
39
+ expect(subject.detect(touch_buffer)).to be_nil
40
+ end
41
+
42
+ it 'moved, 2 fingers, enough angle change, but not same direction' do
43
+ # left finger, to the down
44
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
45
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 15)))
46
+ # right finger, to the down
47
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 20, y_mm: 10)))
48
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 15)))
49
+
50
+ expect(subject.detect(touch_buffer)).to be_nil
51
+ end
52
+
53
+ it 'moved, 2 fingers, enough angle change, same direction' do
54
+ # left finger, to the down
55
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
56
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 15)))
57
+ # right finger, to the up
58
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 20, y_mm: 10)))
59
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 5)))
60
+
61
+ gesture = subject.detect(touch_buffer)
62
+ expect(gesture).to be_a Plugin::Events::Records::TouchRecords::RotateRecord
63
+ expect(gesture.finger).to eq 2
64
+ expect(gesture.direction).to eq 'counterclockwise'
65
+ end
66
+
67
+ it '2 fingers, clockwise' do
68
+ # left finger, to the up
69
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
70
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 5)))
71
+ # right finger, to the down
72
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 20, y_mm: 10)))
73
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 15)))
74
+
75
+ gesture = subject.detect(touch_buffer)
76
+ expect(gesture).to be_a Plugin::Events::Records::TouchRecords::RotateRecord
77
+ expect(gesture.finger).to eq 2
78
+ expect(gesture.direction).to eq 'clockwise'
79
+ end
80
+
81
+ it '3 fingers, one moved into wrong direction' do
82
+ # left finger, to the up
83
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
84
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 5)))
85
+ # right finger, to the down
86
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 20, y_mm: 10)))
87
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 15)))
88
+ # top finger, to the left
89
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 3, x_mm: 10, y_mm: 0)))
90
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 3, x_mm: 5, y_mm: 0)))
91
+
92
+ expect(subject.detect(touch_buffer)).to be_nil
93
+ end
94
+
95
+ it '3 fingers, same direction' do
96
+ # left finger, to the up
97
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
98
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 5)))
99
+ # right finger, to the down
100
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 20, y_mm: 10)))
101
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 15)))
102
+ # top finger, to the right
103
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 3, x_mm: 10, y_mm: 0)))
104
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 3, x_mm: 15, y_mm: 0)))
105
+
106
+ gesture = subject.detect(touch_buffer)
107
+ expect(gesture).to be_a Plugin::Events::Records::TouchRecords::RotateRecord
108
+ expect(gesture.finger).to eq 3
109
+ expect(gesture.direction).to eq 'clockwise'
110
+ end
111
+
112
+ it_behaves_like 'real sample',
113
+ detector_class: Plugin::Detectors::TouchDetectors::RotateDetector,
114
+ sample_path: 'spec/samples/2-fingers-rotate-clockwise.txt',
115
+ expected_gesture_class: Plugin::Events::Records::TouchRecords::RotateRecord,
116
+ expected_gesture_attributes: { finger: 2, direction: 'clockwise' }
117
+ it_behaves_like 'real sample',
118
+ detector_class: Plugin::Detectors::TouchDetectors::RotateDetector,
119
+ sample_path: 'spec/samples/2-fingers-rotate-counterclockwise.txt',
120
+ expected_gesture_class: Plugin::Events::Records::TouchRecords::RotateRecord,
121
+ expected_gesture_attributes: { finger: 2, direction: 'counterclockwise' }
122
+ end
123
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+ require 'fusuma/plugin/events/event'
3
+ require 'fusuma/plugin/parsers/touch_parser'
4
+ require 'fusuma/plugin/buffers/touch_buffer'
5
+ require 'fusuma/plugin/detectors/touch_detectors/swipe_detector'
6
+ require 'fusuma/plugin/events/records/touch_records/swipe_record'
7
+
8
+ module Fusuma
9
+ RSpec.describe Plugin::Detectors::TouchDetectors::SwipeDetector do
10
+ subject { described_class.new }
11
+ let(:touch_buffer) { Plugin::Buffers::TouchBuffer.new }
12
+ let(:t) { Time.now }
13
+
14
+ before do
15
+ allow(touch_buffer).to receive(:movement_threshold).and_return(0.5)
16
+ allow(subject).to receive(:movement_angle_threshold).and_return(30)
17
+ allow(subject).to receive(:direction_angle_width).and_return(45)
18
+ end
19
+
20
+ it 'no events' do
21
+ expect(subject.detect(touch_buffer)).to be_nil
22
+ end
23
+
24
+ it 'not moved' do
25
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
26
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
27
+
28
+ expect(subject.detect(touch_buffer)).to be_nil
29
+ end
30
+
31
+ it 'moved, just one finger' do
32
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
33
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 20, y_mm: 10)))
34
+
35
+ gesture = subject.detect(touch_buffer)
36
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
37
+ expect(gesture.finger).to eq(1)
38
+ end
39
+
40
+ it 'moved, more fingers, angle difference too big' do
41
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
42
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 20, y_mm: 10)))
43
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 10, y_mm: 10)))
44
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 10, y_mm: 20)))
45
+
46
+ expect(subject.detect(touch_buffer)).to be_nil
47
+ end
48
+
49
+ it 'moved, more fingers, angle difference under movement_angle_threshold' do
50
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
51
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 20, y_mm: 8)))
52
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 2, x_mm: 10, y_mm: 10)))
53
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 2, x_mm: 20, y_mm: 12)))
54
+
55
+ gesture = subject.detect(touch_buffer)
56
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
57
+ expect(gesture.finger).to eq(2)
58
+ end
59
+
60
+ it 'swipe left' do
61
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
62
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 0, y_mm: 10)))
63
+
64
+ gesture = subject.detect(touch_buffer)
65
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
66
+ expect(gesture.direction).to eq('left')
67
+ end
68
+
69
+ it 'swipe right' do
70
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 10)))
71
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
72
+
73
+ gesture = subject.detect(touch_buffer)
74
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
75
+ expect(gesture.direction).to eq('right')
76
+ end
77
+
78
+ it 'swipe up' do
79
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
80
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 0)))
81
+
82
+ gesture = subject.detect(touch_buffer)
83
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
84
+ expect(gesture.direction).to eq('up')
85
+ end
86
+
87
+ it 'swipe down' do
88
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 0)))
89
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
90
+
91
+ gesture = subject.detect(touch_buffer)
92
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
93
+ expect(gesture.direction).to eq('down')
94
+ end
95
+
96
+ it 'wrong direction, up left' do
97
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 20, y_mm: 20)))
98
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
99
+
100
+ expect(subject.detect(touch_buffer)).to be_nil
101
+ end
102
+
103
+ it 'wrong direction, up right' do
104
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 20)))
105
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
106
+
107
+ expect(subject.detect(touch_buffer)).to be_nil
108
+ end
109
+
110
+ it 'wrong direction, down left' do
111
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 20, y_mm: 0)))
112
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
113
+
114
+ expect(subject.detect(touch_buffer)).to be_nil
115
+ end
116
+
117
+ it 'wrong direction, down right' do
118
+ touch_buffer.buffer(generate_touch_event(time: t - 1, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 0, y_mm: 0)))
119
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
120
+
121
+ expect(subject.detect(touch_buffer)).to be_nil
122
+ end
123
+
124
+ it 'works on real data' do
125
+ parser = Plugin::Parsers::TouchParser.new
126
+ buffer = Plugin::Buffers::TouchBuffer.new
127
+ lines = File.readlines("spec/samples/2-fingers-swipe-right.txt").map(&:strip).reject(&:empty?)
128
+ lines.each do |line|
129
+ record = parser.parse_record(line)
130
+ next unless record
131
+
132
+ event = Plugin::Events::Event.new(tag: 'libinput_touch_parser', record: record)
133
+ buffer.buffer(event)
134
+ end
135
+ gesture = subject.detect(buffer)
136
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::SwipeRecord)
137
+ expect(gesture.finger).to eq(2)
138
+ expect(gesture.direction).to eq('right')
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+ require 'fusuma/plugin/events/event'
3
+ require 'fusuma/plugin/parsers/touch_parser'
4
+ require 'fusuma/plugin/buffers/touch_buffer'
5
+ require 'fusuma/plugin/detectors/touch_detectors/tap_detector'
6
+ require 'fusuma/plugin/events/records/touch_records/tap_record'
7
+
8
+ module Fusuma
9
+ RSpec.describe Plugin::Detectors::TouchDetectors::TapDetector do
10
+ include_context 'with touch buffer'
11
+ subject { described_class.new }
12
+
13
+ before do
14
+ allow(touch_buffer).to receive(:movement_threshold).and_return(0.5)
15
+ allow(subject).to receive(:tap_hold_threshold).and_return(0.5)
16
+ allow(subject).to receive(:jitter_threshold).and_return(5.0)
17
+ end
18
+
19
+ it 'no events' do
20
+ expect(subject.detect(touch_buffer)).to be nil
21
+ end
22
+
23
+ it 'not began in this cycle' do
24
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
25
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'end', finger: 1)))
26
+
27
+ expect(subject.detect(touch_buffer)).to be nil
28
+ end
29
+
30
+ it 'not ended in this cycle' do
31
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
32
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10, y_mm: 10)))
33
+
34
+ expect(subject.detect(touch_buffer)).to be nil
35
+ end
36
+
37
+ it 'moved too much' do
38
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
39
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'update', finger: 1, x_mm: 20, y_mm: 20)))
40
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'end', finger: 1)))
41
+
42
+ expect(subject.detect(touch_buffer)).to be nil
43
+ end
44
+
45
+ it 'too long' do
46
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
47
+ touch_buffer.buffer(generate_touch_event(time: t + 0.6, record: generate_touch_record(status: 'end', finger: 1)))
48
+
49
+ expect(subject.detect(touch_buffer)).to be nil
50
+ end
51
+
52
+ it 'not moved' do
53
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
54
+ touch_buffer.buffer(generate_touch_event(time: t + 0.1, record: generate_touch_record(status: 'end', finger: 1)))
55
+ pp touch_buffer.finger_movements
56
+
57
+ expect(subject.detect(touch_buffer)).to be_a(Plugin::Events::Records::TouchRecords::TapRecord)
58
+ end
59
+
60
+ it 'moved under jitter threshold' do
61
+ touch_buffer.buffer(generate_touch_event(time: t, record: generate_touch_record(status: 'begin', finger: 1, x_mm: 10, y_mm: 10)))
62
+ touch_buffer.buffer(generate_touch_event(time: t + 0.1, record: generate_touch_record(status: 'update', finger: 1, x_mm: 10.1, y_mm: 10.1)))
63
+ touch_buffer.buffer(generate_touch_event(time: t + 0.2, record: generate_touch_record(status: 'end', finger: 1)))
64
+
65
+ expect(subject.detect(touch_buffer)).to be_a(Plugin::Events::Records::TouchRecords::TapRecord)
66
+ end
67
+
68
+ it 'works on real data' do
69
+ parser = Plugin::Parsers::TouchParser.new
70
+ buffer = Plugin::Buffers::TouchBuffer.new
71
+ lines = File.readlines("spec/samples/3-fingers-tap.txt").map(&:strip).reject(&:empty?)
72
+ lines.each do |line|
73
+ record = parser.parse_record(line)
74
+ next unless record
75
+
76
+ event = Plugin::Events::Event.new(tag: 'libinput_touch_parser', record: record)
77
+ buffer.buffer(event)
78
+ end
79
+ gesture = subject.detect(buffer)
80
+ expect(gesture).to be_a(Plugin::Events::Records::TouchRecords::TapRecord)
81
+ expect(gesture.finger).to eq(3)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,266 @@
1
+ require 'timecop'
2
+ require 'spec_helper'
3
+ require 'fusuma/plugin/events/event'
4
+ require 'fusuma/plugin/buffers/touch_buffer'
5
+ require 'fusuma/plugin/detectors/touch_detector'
6
+
7
+ module Fusuma
8
+ RSpec.describe Plugin::Detectors::TouchDetector do
9
+ subject { described_class.new }
10
+ let(:detectors) { subject.instance_variable_get(:@detectors) }
11
+ let(:tap_detector) { detectors.find { |d| d.is_a?(Plugin::Detectors::TouchDetectors::TapDetector) } }
12
+ let(:hold_detector) { subject.instance_variable_get(:@hold_detector) }
13
+ let(:swipe_detector) { detectors.find { |d| d.is_a?(Plugin::Detectors::TouchDetectors::SwipeDetector) } }
14
+ before do
15
+ expect(:detectors).not_to be_empty
16
+ expect(tap_detector).to be_a(Plugin::Detectors::TouchDetectors::TapDetector)
17
+ expect(hold_detector).to be_a(Plugin::Detectors::TouchDetectors::HoldDetector)
18
+ expect(swipe_detector).to be_a(Plugin::Detectors::TouchDetectors::SwipeDetector)
19
+
20
+ detectors.each do |detector|
21
+ allow(detector).to receive(:detect).and_return(nil)
22
+ end
23
+ end
24
+
25
+ it 'does nothing if no matching buffer' do
26
+ buffer = double('buffer', type: 'not-touch')
27
+ expect(subject.detect([buffer])).to eq []
28
+ end
29
+
30
+ it 'ends gesture if too much time passed' do
31
+ allow(hold_detector).to receive(:detect).and_call_original
32
+ allow(hold_detector).to receive(:tap_hold_threshold).and_return(1)
33
+ allow(subject).to receive(:event_expire_time).and_return(1)
34
+
35
+ t = Time.now
36
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
37
+ Timecop.freeze(t - 4) do
38
+ touch_buffer.buffer(generate_touch_event(record: generate_touch_record(status: 'update'), time: t - 4))
39
+ end
40
+ # 2 seconds - enough to detect hold gesture
41
+ # event is not expired because of there's no event yet
42
+ events = Timecop.freeze(t - 2) do
43
+ # here we detect hold gesture, which makes it the last gesture known to the detector
44
+ subject.detect([touch_buffer])
45
+ end
46
+ expect(events.size).to eq 2
47
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 1]
48
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:hold, 1, :begin]
49
+
50
+ # now the last gesture becomes 1 second old
51
+ events = Timecop.freeze(t) do
52
+ timer_buffer = Plugin::Buffers::TimerBuffer.new
53
+ timer_buffer.buffer(generate_timer_event(time: t))
54
+ # here we waited for too long time without new events
55
+ subject.detect([timer_buffer])
56
+ end
57
+
58
+ expect(events.size).to eq 1
59
+ expect(events.first.record.index.keys.map(&:symbol)).to eq [:hold, 1, :end]
60
+ end
61
+
62
+ it 'ends gesture if end event is found' do
63
+ allow(hold_detector).to receive(:detect).and_call_original
64
+ allow(hold_detector).to receive(:tap_hold_threshold).and_return(1)
65
+ # let's guarantee the event won't be expired
66
+ allow(subject).to receive(:event_expire_time).and_return(10)
67
+
68
+ t = Time.now
69
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
70
+ Timecop.freeze(t - 4) do
71
+ touch_buffer.buffer(generate_touch_event(record: generate_touch_record(status: 'update'), time: t - 4))
72
+ end
73
+ # 2 seconds - enough to detect hold gesture
74
+ # event is not expired because of there's no event yet
75
+ events = Timecop.freeze(t - 2) do
76
+ # here we detect hold gesture, which makes it the last gesture known to the detector
77
+ subject.detect([touch_buffer])
78
+ end
79
+
80
+ # now we send end event
81
+ events = Timecop.freeze(t) do
82
+ touch_buffer.buffer(generate_touch_event(record: generate_touch_record(status: 'end'), time: t))
83
+ subject.detect([touch_buffer])
84
+ end
85
+ expect(events.size).to eq 1
86
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 1, :end]
87
+ end
88
+
89
+ it 'ends gesture if new gesture began' do
90
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 0)
91
+ allow(hold_detector).to receive(:detect).and_return(record)
92
+
93
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
94
+ touch_buffer.buffer(generate_touch_event)
95
+ # let's push the hold gesture as the last known gesture
96
+ events = subject.detect([touch_buffer])
97
+ expect(events.size).to eq 2
98
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0]
99
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:hold, 0, :begin]
100
+
101
+ record = Plugin::Events::Records::TouchRecords::SwipeRecord.new(finger: 0, direction: 'up')
102
+ allow(hold_detector).to receive(:detect).and_return(nil)
103
+ allow(swipe_detector).to receive(:detect).and_return(record)
104
+
105
+ # now let's not issue any more events for the hold gesture, but start a new swipe gesture
106
+ touch_buffer.buffer(generate_touch_event)
107
+ events = subject.detect([touch_buffer])
108
+ expect(events.size).to eq 3
109
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0, :end]
110
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:swipe, 0, :up]
111
+ expect(events[2].record.index.keys.map(&:symbol)).to eq [:swipe, 0, :up, :begin]
112
+ end
113
+
114
+ it 'timer tick does nothing if no touch events' do
115
+ timer_buffer = Plugin::Buffers::TimerBuffer.new
116
+ timer_buffer.buffer(generate_timer_event)
117
+ detectors.each do |detector|
118
+ expect(detector).not_to receive(:detect)
119
+ end
120
+ subject.detect([timer_buffer])
121
+ end
122
+
123
+ it 'calls detectors if touch events' do
124
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
125
+ touch_buffer.buffer(generate_touch_event)
126
+ detectors.each do |detector|
127
+ expect(detector).to receive(:detect)
128
+ end
129
+ subject.detect([touch_buffer])
130
+ end
131
+
132
+ it 'calls detectors if timer tick' do
133
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
134
+ touch_buffer.buffer(generate_touch_event)
135
+ # we call detect which won't detect any gesture yet, but will save the reference to the touch buffer
136
+ subject.detect([touch_buffer])
137
+ timer_buffer = Plugin::Buffers::TimerBuffer.new
138
+ timer_buffer.buffer(generate_timer_event)
139
+ # in fact only the hold detector benefits from timer ticks for now
140
+ expect(hold_detector).to receive(:detect)
141
+ subject.detect([timer_buffer])
142
+ end
143
+
144
+ context 'gesture detected' do
145
+ it 'clears touch buffer' do
146
+ record = Plugin::Events::Records::TouchRecords::TapRecord.new(finger: 0)
147
+ allow(tap_detector).to receive(:detect).and_return(record)
148
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
149
+ touch_buffer.buffer(generate_touch_event)
150
+ expect(touch_buffer).to receive(:clear)
151
+ subject.detect([touch_buffer])
152
+ end
153
+
154
+ context 'repeatable' do
155
+ it 'creates event' do
156
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 0)
157
+ allow(hold_detector).to receive(:detect).and_return(record)
158
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
159
+ touch_buffer.buffer(generate_touch_event)
160
+ events = subject.detect([touch_buffer])
161
+ expect(events.size).to eq 2
162
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0]
163
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:hold, 0, :begin]
164
+ end
165
+
166
+ it 'matches last known gesture' do
167
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 0)
168
+ allow(hold_detector).to receive(:detect).and_return(record)
169
+
170
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
171
+ touch_buffer.buffer(generate_touch_event)
172
+ # make it the last known gesture
173
+ subject.detect([touch_buffer])
174
+
175
+ touch_buffer.buffer(generate_touch_event)
176
+ events = subject.detect([touch_buffer])
177
+
178
+ expect(events.size).to eq 1
179
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0, :update]
180
+ end
181
+
182
+ it 'does not match last known gesture' do
183
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 0)
184
+ allow(hold_detector).to receive(:detect).and_return(record)
185
+
186
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
187
+ touch_buffer.buffer(generate_touch_event)
188
+ # make it the last known gesture
189
+ subject.detect([touch_buffer])
190
+
191
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 1)
192
+ allow(hold_detector).to receive(:detect).and_return(record)
193
+ touch_buffer.buffer(generate_touch_event)
194
+ events = subject.detect([touch_buffer])
195
+
196
+ expect(events.size).to eq 3
197
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0, :end]
198
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:hold, 1]
199
+ expect(events[2].record.index.keys.map(&:symbol)).to eq [:hold, 1, :begin]
200
+ end
201
+
202
+ it 'no last known gesture' do
203
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 0)
204
+ allow(hold_detector).to receive(:detect).and_return(record)
205
+
206
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
207
+ touch_buffer.buffer(generate_touch_event)
208
+ events = subject.detect([touch_buffer])
209
+
210
+ expect(events.size).to eq 2
211
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0]
212
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:hold, 0, :begin]
213
+ end
214
+ end
215
+
216
+ context 'non-repeatable' do
217
+ it 'ends last known repeatable gesture if any' do
218
+ record = Plugin::Events::Records::TouchRecords::HoldRecord.new(finger: 0)
219
+ allow(hold_detector).to receive(:detect).and_return(record)
220
+
221
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
222
+ touch_buffer.buffer(generate_touch_event)
223
+ # make it the last known gesture
224
+ subject.detect([touch_buffer])
225
+
226
+ allow(hold_detector).to receive(:detect).and_return(nil)
227
+ record = Plugin::Events::Records::TouchRecords::TapRecord.new(finger: 0)
228
+ allow(tap_detector).to receive(:detect).and_return(record)
229
+ touch_buffer.buffer(generate_touch_event)
230
+ events = subject.detect([touch_buffer])
231
+
232
+ expect(events.size).to eq 2
233
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:hold, 0, :end]
234
+ expect(events[1].record.index.keys.map(&:symbol)).to eq [:tap, 0]
235
+ end
236
+
237
+ it 'creates event' do
238
+ record = Plugin::Events::Records::TouchRecords::TapRecord.new(finger: 0)
239
+ allow(tap_detector).to receive(:detect).and_return(record)
240
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
241
+ touch_buffer.buffer(generate_touch_event)
242
+ events = subject.detect([touch_buffer])
243
+ expect(events.size).to eq 1
244
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:tap, 0]
245
+ end
246
+
247
+ it 'does not repeat' do
248
+ record = Plugin::Events::Records::TouchRecords::TapRecord.new(finger: 0)
249
+ allow(tap_detector).to receive(:detect).and_return(record)
250
+
251
+ touch_buffer = Plugin::Buffers::TouchBuffer.new
252
+ touch_buffer.buffer(generate_touch_event)
253
+ events = subject.detect([touch_buffer])
254
+ expect(events.size).to eq 1
255
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:tap, 0]
256
+
257
+ touch_buffer.buffer(generate_touch_event)
258
+ events = subject.detect([touch_buffer])
259
+ expect(events.size).to eq 1
260
+ expect(events[0].record.index.keys.map(&:symbol)).to eq [:tap, 0]
261
+ end
262
+ end
263
+ end # context 'gesture detected'
264
+
265
+ end # describe Plugin::Detectors::TouchDetector
266
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "spec_helper"
4
- require './lib/fusuma/plugin/devices/touchscreen_device'
4
+ require 'fusuma/plugin/inputs/libinput_command_input'
5
+ require 'fusuma/plugin/devices/touchscreen_device'
5
6
 
6
7
  module Fusuma
7
8
  RSpec.describe Plugin::Touchscreen::DevicePatch do
@@ -21,13 +22,15 @@ module Fusuma
21
22
  # that's the only touchscreen device I have :)
22
23
  context 'Microsoft Surface 3 Pro' do
23
24
  let(:list_devices_output) do
24
- File.open("./spec/fixtures/libinput-list-devices_ms-surface-3-pro.txt")
25
+ File.open("./spec/samples/libinput-list-devices.txt")
25
26
  end
26
27
 
27
- it 'detects Microsoft Surface 3 Pro touchscreen' do
28
+ it 'detects touchscreen' do
28
29
  expect(Device.available.map(&:name)).to include 'NTRG0001:01 1B96:1B05'
29
30
  end
30
31
  end
31
32
 
33
+ # TODO: create a test for a dummy device with touch capability
34
+
32
35
  end
33
36
  end