fusuma-plugin-touchscreen 0.0.1.alpha → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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