1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "include/haptic_button_generator_filter_interpreter.h"
6
7 #include <math.h>
8
9 #include "include/gestures.h"
10 #include "include/interpreter.h"
11 #include "include/logging.h"
12 #include "include/tracer.h"
13 #include "include/util.h"
14
15 namespace gestures {
16
HapticButtonGeneratorFilterInterpreter(PropRegistry * prop_reg,Interpreter * next,Tracer * tracer)17 HapticButtonGeneratorFilterInterpreter::HapticButtonGeneratorFilterInterpreter(
18 PropRegistry* prop_reg, Interpreter* next, Tracer* tracer)
19 : FilterInterpreter(NULL, next, tracer, false),
20 release_suppress_factor_(1.0),
21 active_gesture_(false),
22 active_gesture_timeout_(0.1),
23 active_gesture_deadline_(NO_DEADLINE),
24 button_down_(false),
25 dynamic_down_threshold_(0.0),
26 dynamic_up_threshold_(0.0),
27 sensitivity_(prop_reg, "Haptic Button Sensitivity", 3),
28 use_custom_thresholds_(prop_reg,
29 "Use Custom Haptic Button Force Thresholds",
30 false),
31 custom_down_threshold_(prop_reg,
32 "Custom Haptic Button Force Threshold Down",
33 150.0),
34 custom_up_threshold_(prop_reg,
35 "Custom Haptic Button Force Threshold Up",
36 130.0),
37 enabled_(prop_reg, "Enable Haptic Button Generation", false),
38 force_scale_(prop_reg, "Force Calibration Slope", 1.0),
39 force_translate_(prop_reg, "Force Calibration Offset", 0.0),
40 complete_release_suppress_speed_(
41 prop_reg, "Haptic Complete Release Suppression Speed", 200.0),
42 use_dynamic_thresholds_(prop_reg, "Use Dynamic Haptic Thresholds", false),
43 dynamic_down_ratio_(prop_reg, "Dynamic Haptic Down Ratio", 1.2),
44 dynamic_up_ratio_(prop_reg, "Dynamic Haptic Up Ratio", 0.5),
45 max_dynamic_up_force_(prop_reg, "Max Dynamic Haptic Up Force", 350.0) {
46 InitName();
47 }
48
Initialize(const HardwareProperties * hwprops,Metrics * metrics,MetricsProperties * mprops,GestureConsumer * consumer)49 void HapticButtonGeneratorFilterInterpreter::Initialize(
50 const HardwareProperties* hwprops,
51 Metrics* metrics,
52 MetricsProperties* mprops,
53 GestureConsumer* consumer) {
54 is_haptic_pad_ = hwprops->is_haptic_pad;
55 FilterInterpreter::Initialize(hwprops, NULL, mprops, consumer);
56 }
57
SyncInterpretImpl(HardwareState * hwstate,stime_t * timeout)58 void HapticButtonGeneratorFilterInterpreter::SyncInterpretImpl(
59 HardwareState* hwstate, stime_t* timeout) {
60 HandleHardwareState(hwstate);
61 stime_t next_timeout = NO_DEADLINE;
62 next_->SyncInterpret(hwstate, &next_timeout);
63 UpdatePalmState(hwstate);
64 *timeout = SetNextDeadlineAndReturnTimeoutVal(
65 hwstate->timestamp, active_gesture_deadline_, next_timeout);
66 }
67
HandleHardwareState(HardwareState * hwstate)68 void HapticButtonGeneratorFilterInterpreter::HandleHardwareState(
69 HardwareState* hwstate) {
70 if (!enabled_.val_ || !is_haptic_pad_)
71 return;
72
73 // Ignore the button state generated by the haptic touchpad.
74 hwstate->buttons_down = 0;
75
76 // Determine force thresholds.
77 double down_threshold;
78 double up_threshold;
79 if (use_custom_thresholds_.val_) {
80 down_threshold = custom_down_threshold_.val_;
81 up_threshold = custom_up_threshold_.val_;
82 }
83 else {
84 down_threshold = down_thresholds_[sensitivity_.val_ - 1];
85 up_threshold = up_thresholds_[sensitivity_.val_ - 1];
86 }
87
88 if (use_dynamic_thresholds_.val_) {
89 up_threshold = fmax(up_threshold, dynamic_up_threshold_);
90 down_threshold = fmax(down_threshold, dynamic_down_threshold_);
91 }
92
93 up_threshold *= release_suppress_factor_;
94
95 // Determine maximum force on touchpad in grams
96 double force = 0.0;
97 for (short i = 0; i < hwstate->finger_cnt; i++) {
98 FingerState* fs = &hwstate->fingers[i];
99 if (!SetContainsValue(palms_, fs->tracking_id)) {
100 force = fmax(force, fs->pressure);
101 }
102 }
103 force *= force_scale_.val_;
104 force += force_translate_.val_;
105
106 // Set the button state
107 bool prev_button_down = button_down_;
108 if (button_down_) {
109 if (force < up_threshold)
110 button_down_ = false;
111 else
112 hwstate->buttons_down = GESTURES_BUTTON_LEFT;
113 } else if (force > down_threshold && !active_gesture_) {
114 button_down_ = true;
115 hwstate->buttons_down = GESTURES_BUTTON_LEFT;
116 }
117
118 // When the user presses very hard, we want to increase the force threshold
119 // for releasing the button. We scale the release threshold as a ratio of the
120 // max force applied while the button is down.
121 if (prev_button_down) {
122 if (button_down_) {
123 dynamic_up_threshold_ = fmax(dynamic_up_threshold_,
124 force * dynamic_up_ratio_.val_);
125 dynamic_up_threshold_ = fmin(dynamic_up_threshold_,
126 max_dynamic_up_force_.val_);
127 } else {
128 dynamic_up_threshold_ = 0.0;
129 }
130 }
131
132 // Because we dynamically increase the up_threshold when a user presses very
133 // hard, we also need to increase the down_threshold for the next click.
134 // However, if the user continues to decrease force after the button release,
135 // event, we will keep scaling down the dynamic_down_threshold.
136 if (prev_button_down) {
137 if (!button_down_) {
138 dynamic_down_threshold_ = force * dynamic_down_ratio_.val_;
139 }
140 } else {
141 if (button_down_) {
142 dynamic_down_threshold_ = 0.0;
143 } else {
144 dynamic_down_threshold_ = fmin(dynamic_down_threshold_,
145 force * dynamic_down_ratio_.val_);
146 }
147 }
148 release_suppress_factor_ = 1.0;
149 }
150
UpdatePalmState(HardwareState * hwstate)151 void HapticButtonGeneratorFilterInterpreter::UpdatePalmState(
152 HardwareState* hwstate) {
153 RemoveMissingIdsFromSet(&palms_, *hwstate);
154 for (short i = 0; i < hwstate->finger_cnt; i++) {
155 FingerState* fs = &hwstate->fingers[i];
156 if (fs->flags & GESTURES_FINGER_LARGE_PALM) {
157 palms_.insert(fs->tracking_id);
158 }
159 }
160 }
161
162
HandleTimerImpl(stime_t now,stime_t * timeout)163 void HapticButtonGeneratorFilterInterpreter::HandleTimerImpl(
164 stime_t now, stime_t *timeout) {
165 stime_t next_timeout;
166 if (ShouldCallNextTimer(active_gesture_deadline_)) {
167 next_timeout = NO_DEADLINE;
168 next_->HandleTimer(now, &next_timeout);
169 } else {
170 if (active_gesture_deadline_ > now) {
171 Err("Spurious callback. now: %f, active gesture deadline: %f",
172 now, active_gesture_deadline_);
173 return;
174 }
175 // If enough time has passed without an active gesture event assume that we
176 // missed the gesture ending event, to prevent a state where the button is
177 // stuck down.
178 active_gesture_ = false;
179
180 active_gesture_deadline_ = NO_DEADLINE;
181 next_timeout =
182 next_timer_deadline_ == NO_DEADLINE || next_timer_deadline_ <= now ?
183 NO_DEADLINE : next_timer_deadline_ - now;
184 }
185 *timeout = SetNextDeadlineAndReturnTimeoutVal(now,
186 active_gesture_deadline_,
187 next_timeout);
188 }
189
ConsumeGesture(const Gesture & gesture)190 void HapticButtonGeneratorFilterInterpreter::ConsumeGesture(
191 const Gesture& gesture) {
192 if (!enabled_.val_ || !is_haptic_pad_) {
193 ProduceGesture(gesture);
194 return;
195 }
196
197 // Determine if there is an active non-click multi-finger gesture.
198 switch (gesture.type) {
199 case kGestureTypeScroll:
200 case kGestureTypeSwipe:
201 case kGestureTypeFourFingerSwipe:
202 active_gesture_ = true;
203 break;
204 case kGestureTypeFling:
205 case kGestureTypeSwipeLift:
206 case kGestureTypeFourFingerSwipeLift:
207 active_gesture_ = false;
208 break;
209 case kGestureTypePinch:
210 active_gesture_ = (gesture.details.pinch.zoom_state != GESTURES_ZOOM_END);
211 break;
212 default:
213 break;
214 }
215 if (active_gesture_) {
216 active_gesture_deadline_ = gesture.end_time + active_gesture_timeout_;
217 }
218
219 // When dragging while clicking, users often reduce the force applied, causing
220 // accidental release. So we calculate a scaling factor to reduce the "up"
221 // threshold which starts at 1.0 (normal threshold) for stationary fingers,
222 // and goes down to 0.0 at the complete_release_suppress_speed_.
223 if (gesture.type == kGestureTypeMove) {
224 float distance_sq = gesture.details.move.dx * gesture.details.move.dx +
225 gesture.details.move.dy * gesture.details.move.dy;
226 stime_t time_delta = gesture.end_time - gesture.start_time;
227 float complete_suppress_dist =
228 complete_release_suppress_speed_.val_ * time_delta;
229 float complete_suppress_dist_sq =
230 complete_suppress_dist * complete_suppress_dist;
231
232 release_suppress_factor_ =
233 time_delta <= 0.0 ? 1.0 : 1.0 - distance_sq / complete_suppress_dist_sq;
234
235 // Always allow release at very low force, to prevent a stuck button when
236 // the user lifts their finger while moving quickly.
237 release_suppress_factor_ = fmax(release_suppress_factor_, 0.1);
238 }
239
240 ProduceGesture(gesture);
241 }
242
243 } // namespace gestures
244