1 // Copyright 2012 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/lookahead_filter_interpreter.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <math.h>
10
11 #include "include/tracer.h"
12 #include "include/util.h"
13
14 using std::max;
15 using std::min;
16
17 namespace gestures {
18
19 namespace {
20 static const stime_t kMaxDelay = 0.09; // 90ms
21 }
22
LookaheadFilterInterpreter(PropRegistry * prop_reg,Interpreter * next,Tracer * tracer)23 LookaheadFilterInterpreter::LookaheadFilterInterpreter(
24 PropRegistry* prop_reg, Interpreter* next, Tracer* tracer)
25 : FilterInterpreter(nullptr, next, tracer, false),
26 last_id_(0), max_fingers_per_hwstate_(0),
27 interpreter_due_deadline_(-1.0), last_interpreted_time_(-1.0),
28 min_nonsuppress_speed_(prop_reg, "Input Queue Min Nonsuppression Speed",
29 200.0),
30 min_delay_(prop_reg, "Input Queue Delay", 0.0),
31 max_delay_(prop_reg, "Input Queue Max Delay", 0.017),
32 split_min_period_(prop_reg, "Min Interpolate Period", 0.021),
33 drumroll_suppression_enable_(prop_reg, "Drumroll Suppression Enable",
34 true),
35 drumroll_speed_thresh_(prop_reg, "Drumroll Speed Thresh", 400.0),
36 drumroll_max_speed_ratio_(prop_reg,
37 "Drumroll Max Speed Change Factor",
38 15.0),
39 quick_move_thresh_(prop_reg, "Quick Move Distance Thresh", 3.0),
40 co_move_ratio_(prop_reg, "Drumroll Co Move Ratio", 1.2),
41 suppress_immediate_tapdown_(prop_reg, "Suppress Immediate Tapdown", true),
42 delay_on_possible_liftoff_(prop_reg, "Delay On Possible Liftoff", false),
43 liftoff_speed_increase_threshold_(prop_reg, "Liftoff Speed Factor", 5.0) {
44 InitName();
45 }
46
SyncInterpretImpl(HardwareState & hwstate,stime_t * timeout)47 void LookaheadFilterInterpreter::SyncInterpretImpl(HardwareState& hwstate,
48 stime_t* timeout) {
49 const char name[] = "LookaheadFilterInterpreter::SyncInterpretImpl";
50 LogHardwareStatePre(name, hwstate);
51
52 // Keep track of where the last node is in the current queue_
53 auto const queue_was_not_empty = !queue_.empty();
54 QState* old_back_node = queue_was_not_empty ? &queue_.back() : nullptr;
55
56 // Allocate and initialize a new node on the end of the queue_
57 auto& new_node = queue_.emplace_back(hwprops_->max_finger_cnt);
58 new_node.state_.DeepCopy(hwstate, hwprops_->max_finger_cnt);
59 double delay = max(0.0, min<stime_t>(kMaxDelay, min_delay_.val_));
60 new_node.due_ = hwstate.timestamp + delay;
61
62 if (queue_was_not_empty) {
63 new_node.output_ids_ = old_back_node->output_ids_;
64
65 // At this point, if ExtraVariableDelay() > 0, old_back_node.due_ may have
66 // ExtraVariableDelay() applied, but new_node.due_ does not, yet.
67 if (old_back_node->due_ - new_node.due_ > ExtraVariableDelay()) {
68 Err("Clock changed backwards. Flushing queue.");
69 stime_t next_timeout = NO_DEADLINE;
70 auto q_node_iter = queue_.begin();
71 do {
72 if (!q_node_iter->completed_)
73 next_->SyncInterpret(q_node_iter->state_, &next_timeout);
74 ++q_node_iter;
75 queue_.pop_front();
76 } while (queue_.size() > 1);
77 interpreter_due_deadline_ = -1.0;
78 last_interpreted_time_ = -1.0;
79 }
80 }
81
82 AssignTrackingIds();
83 AttemptInterpolation();
84
85 // Update the timeout and interpreter_due_deadline_ based on above processing
86 UpdateInterpreterDue(interpreter_due_deadline_, hwstate.timestamp, timeout);
87
88 // Make sure to handle any state expiration processing that is needed
89 HandleTimerImpl(hwstate.timestamp, timeout);
90
91 // Copy finger flags for upstream filters.
92 QState& q_node = queue_.front();
93 if (q_node.state_.SameFingersAs(hwstate)) {
94 for (size_t i = 0; i < hwstate.finger_cnt; i++) {
95 hwstate.fingers[i].flags = q_node.state_.fingers[i].flags;
96 }
97 }
98
99 LogHardwareStatePost(name, hwstate);
100 }
101
102 // Interpolates the two hardware states into out.
103 // out must have finger states allocated and pointed to already.
Interpolate(const HardwareState & first,const HardwareState & second,HardwareState * out)104 void LookaheadFilterInterpreter::Interpolate(const HardwareState& first,
105 const HardwareState& second,
106 HardwareState* out) {
107 out->timestamp = (first.timestamp + second.timestamp) / 2.0;
108 out->buttons_down = first.buttons_down;
109 out->touch_cnt = first.touch_cnt;
110 out->finger_cnt = first.finger_cnt;
111 for (size_t i = 0; i < first.finger_cnt; i++) {
112 const FingerState& older = first.fingers[i];
113 const FingerState& newer = second.fingers[i];
114 FingerState* mid = &out->fingers[i];
115 mid->touch_major = (older.touch_major + newer.touch_major) / 2.0;
116 mid->touch_minor = (older.touch_minor + newer.touch_minor) / 2.0;
117 mid->width_major = (older.width_major + newer.width_major) / 2.0;
118 mid->width_minor = (older.width_minor + newer.width_minor) / 2.0;
119 mid->pressure = (older.pressure + newer.pressure) / 2.0;
120 mid->orientation = (older.orientation + newer.orientation) / 2.0;
121 mid->position_x = (older.position_x + newer.position_x) / 2.0;
122 mid->position_y = (older.position_y + newer.position_y) / 2.0;
123 mid->tracking_id = older.tracking_id;
124 mid->flags = newer.flags;
125 }
126 // We are not interested in interpolating relative movement values.
127 out->rel_x = 0;
128 out->rel_y = 0;
129 out->rel_wheel = 0;
130 out->rel_wheel_hi_res = 0;
131 out->rel_hwheel = 0;
132 }
133
AssignTrackingIds()134 void LookaheadFilterInterpreter::AssignTrackingIds() {
135 // For semi-mt devices, drumrolls and quick moves are handled in
136 // SemiMtCorrectingFilterInterpreter already. We need to bypass the detection
137 // and tracking id reassignment here to make fast-scroll working correctly.
138 // For haptic touchpads, we need to bypass tracking id reassignment so the
139 // haptic button filter can have the same tracking ids.
140 if (hwprops_->support_semi_mt ||
141 hwprops_->is_haptic_pad ||
142 !drumroll_suppression_enable_.val_) {
143 return;
144 }
145 if (queue_.size() < 2) {
146 // Always reassign trackingID on the very first hwstate so that
147 // the next hwstate can inherit the trackingID mapping.
148 if (queue_.size() == 1) {
149 QState& tail = queue_.back();
150 HardwareState* hs = &tail.state_;
151 for (size_t i = 0; i < hs->finger_cnt; i++) {
152 FingerState* fs = &hs->fingers[i];
153 tail.output_ids_[fs->tracking_id] = NextTrackingId();
154 fs->tracking_id = tail.output_ids_[fs->tracking_id];
155 }
156 if (hs->finger_cnt > 0)
157 tail.due_ += ExtraVariableDelay();
158 }
159 return;
160 }
161
162 auto& tail = queue_.at(-1);
163 HardwareState* hs = &tail.state_;
164 QState* prev_qs = queue_.size() < 2 ? nullptr : &(queue_.at(-2));
165 HardwareState* prev_hs = prev_qs ? &prev_qs->state_ : nullptr;
166 QState* prev2_qs = queue_.size() < 3 ? nullptr : &(queue_.at(-3));
167 HardwareState* prev2_hs = prev2_qs ? &prev2_qs->state_ : nullptr;
168
169 RemoveMissingIdsFromMap(&tail.output_ids_, *hs);
170 float dt = prev_hs ? hs->timestamp - prev_hs->timestamp : 1.0;
171 float prev_dt =
172 prev_hs && prev2_hs ? prev_hs->timestamp - prev2_hs->timestamp : 1.0;
173
174 float dist_sq_thresh =
175 dt * dt * drumroll_speed_thresh_.val_ * drumroll_speed_thresh_.val_;
176
177 const float multiplier_per_time_ratio_sq = dt * dt *
178 drumroll_max_speed_ratio_.val_ *
179 drumroll_max_speed_ratio_.val_;
180 const float prev_dt_sq = prev_dt * prev_dt;
181
182 std::set<short> separated_fingers; // input ids
183 float max_dist_sq = 0.0; // largest non-drumroll dist squared.
184 // If there is only a single finger drumrolling, this is the distance
185 // it travelled squared.
186 float drum_dist_sq = INFINITY;
187 bool new_finger_present = false;
188 for (size_t i = 0; i < hs->finger_cnt; i++) {
189 FingerState* fs = &hs->fingers[i];
190 const short old_id = fs->tracking_id;
191 bool new_finger = false;
192 if (!MapContainsKey(tail.output_ids_, fs->tracking_id)) {
193 tail.output_ids_[fs->tracking_id] = NextTrackingId();
194 new_finger_present = new_finger = true;
195 }
196 fs->tracking_id = tail.output_ids_[fs->tracking_id];
197 if (new_finger)
198 continue;
199 if (!prev_hs) {
200 Err("How is prev_hs null?");
201 continue;
202 }
203 // Consider breaking the connection between this frame and the previous
204 // by assigning this finger a new ID
205 if (!MapContainsKey(prev_qs->output_ids_, old_id)) {
206 Err("How is old id missing from old output_ids?");
207 continue;
208 }
209 FingerState* prev_fs =
210 prev_hs->GetFingerState(prev_qs->output_ids_[old_id]);
211 if (!prev_fs) {
212 Err("How is prev_fs null?");
213 continue;
214 }
215
216 float dx = fs->position_x - prev_fs->position_x;
217 float dy = fs->position_y - prev_fs->position_y;
218 float dist_sq = dx * dx + dy * dy;
219 float prev_max_dist_sq = max_dist_sq;
220 if (dist_sq > max_dist_sq)
221 max_dist_sq = dist_sq;
222
223 FingerState* prev2_fs = nullptr;
224
225 if (prev2_hs && MapContainsKey(prev2_qs->output_ids_, old_id))
226 prev2_fs = prev2_hs->GetFingerState(prev2_qs->output_ids_[old_id]);
227
228 // Quick movement detection.
229 if (prev2_fs) {
230 float prev_dx = prev_fs->position_x - prev2_fs->position_x;
231 float prev_dy = prev_fs->position_y - prev2_fs->position_y;
232
233 // Along either x or y axis, the movement between (prev2, prev) and
234 // (prev, current) should be on the same direction, and the distance
235 // travelled should be larger than quick_move_thresh_.
236 if ((prev_dx * dx >= 0.0 && fabs(prev_dx) >= quick_move_thresh_.val_ &&
237 fabs(dx) >= quick_move_thresh_.val_) ||
238 (prev_dy * dy >= 0.0 && fabs(prev_dy) >= quick_move_thresh_.val_ &&
239 fabs(dy) >= quick_move_thresh_.val_)) {
240 // Quick movement detected. Correct the tracking ID if the previous
241 // finger state has a reassigned trackingID due to drumroll detection.
242 if (prev_qs->output_ids_[old_id] != prev2_qs->output_ids_[old_id]) {
243 prev_qs->output_ids_[old_id] = prev2_qs->output_ids_[old_id];
244 prev_fs->tracking_id = prev_qs->output_ids_[old_id];
245 tail.output_ids_[old_id] = prev2_qs->output_ids_[old_id];
246 fs->tracking_id = tail.output_ids_[old_id];
247 continue;
248 }
249 }
250 }
251
252 // Drumroll detection.
253 if (dist_sq > dist_sq_thresh) {
254 if (prev2_fs) {
255 float prev_dx = prev_fs->position_x - prev2_fs->position_x;
256 float prev_dy = prev_fs->position_y - prev2_fs->position_y;
257 // If the finger is switching direction rapidly, it's drumroll.
258 if (prev_dx * dx >= 0.0 || prev_dy * dy >= 0.0) {
259 // Finger not switching direction rapidly. Now, test if large
260 // speed change.
261 float prev_dist_sq = prev_dx * prev_dx + prev_dy * prev_dy;
262 if (dist_sq * prev_dt_sq <=
263 multiplier_per_time_ratio_sq * prev_dist_sq)
264 continue;
265 }
266 }
267 if (fs->flags & (GESTURES_FINGER_WARP_X | GESTURES_FINGER_WARP_Y)) {
268 // Finger is warping. Don't reassign tracking ID.
269 // Because we would have reassigned the ID, we make sure we're warping
270 // both axes
271 fs->flags |= (GESTURES_FINGER_WARP_X | GESTURES_FINGER_WARP_Y);
272 continue;
273 }
274 separated_fingers.insert(old_id);
275 SeparateFinger(&tail, fs, old_id);
276 // Separating fingers shouldn't tap.
277 fs->flags |= GESTURES_FINGER_NO_TAP;
278 // Try to also flag the previous frame, if we didn't execute it yet
279 if (!prev_qs->completed_)
280 prev_fs->flags |= GESTURES_FINGER_NO_TAP;
281 // Since this is drumroll, don't count it toward the max dist sq.
282 max_dist_sq = prev_max_dist_sq;
283 // Instead, store this distance as the drumroll distance
284 drum_dist_sq = dist_sq;
285 }
286 }
287 // There are some times where we abort drumrolls. If two fingers are both
288 // drumrolling, that's unlikely (they are probably quickly swiping). Also,
289 // if a single finger is moving enough to trigger drumroll, but another
290 // finger is moving about as much, don't drumroll-suppress the one finger.
291 if (separated_fingers.size() > 1 ||
292 (separated_fingers.size() == 1 &&
293 drum_dist_sq <
294 max_dist_sq * co_move_ratio_.val_ * co_move_ratio_.val_)) {
295 // Two fingers drumrolling at the exact same time. More likely this is
296 // a fast multi-finger swipe. Abort the drumroll detection.
297 for (std::set<short>::const_iterator it = separated_fingers.begin(),
298 e = separated_fingers.end(); it != e; ++it) {
299 short input_id = *it;
300 if (!MapContainsKey(prev_qs->output_ids_, input_id)) {
301 Err("How is input ID missing from prev state? %d", input_id);
302 continue;
303 }
304 short new_bad_output_id = tail.output_ids_[input_id];
305 short prev_output_id = prev_qs->output_ids_[input_id];
306 tail.output_ids_[input_id] = prev_output_id;
307 FingerState* fs = tail.state_.GetFingerState(new_bad_output_id);
308 if (!fs) {
309 Err("Can't find finger state.");
310 continue;
311 }
312 fs->tracking_id = prev_output_id;
313 }
314 separated_fingers.clear();
315 }
316
317 if (!separated_fingers.empty() || new_finger_present ||
318 (delay_on_possible_liftoff_.val_ && hs && prev_hs && prev2_hs &&
319 LiftoffJumpStarting(*hs, *prev_hs, *prev2_hs))) {
320 // Possibly add some extra delay to correct, incase this separation
321 // shouldn't have occurred or if the finger may be lifting from the pad.
322 tail.due_ += ExtraVariableDelay();
323 }
324 }
325
LiftoffJumpStarting(const HardwareState & hs,const HardwareState & prev_hs,const HardwareState & prev2_hs) const326 bool LookaheadFilterInterpreter::LiftoffJumpStarting(
327 const HardwareState& hs,
328 const HardwareState& prev_hs,
329 const HardwareState& prev2_hs) const {
330 for (size_t i = 0; i < hs.finger_cnt; i++) {
331 const FingerState* fs = &hs.fingers[i];
332 const FingerState* prev_fs = prev_hs.GetFingerState(fs->tracking_id);
333 if (!prev_fs)
334 continue;
335 if (fs->pressure > prev_fs->pressure) {
336 // Pressure increasing. Likely not liftoff.
337 continue;
338 }
339 const FingerState* prev2_fs = prev2_hs.GetFingerState(fs->tracking_id);
340 if (!prev2_fs)
341 continue;
342
343 float dist_sq_new = DistSq(*fs, *prev_fs);
344 float dist_sq_old = DistSq(*prev_fs, *prev2_fs);
345 float dt_new = hs.timestamp - prev_hs.timestamp;
346 float dt_old = prev_hs.timestamp - prev2_hs.timestamp;
347
348 if (dt_old * dt_old * dist_sq_new >
349 dt_new * dt_new * dist_sq_old *
350 liftoff_speed_increase_threshold_.val_ *
351 liftoff_speed_increase_threshold_.val_)
352 return true;
353 }
354 return false;
355 }
356
TapDownOccurringGesture(stime_t now)357 void LookaheadFilterInterpreter::TapDownOccurringGesture(stime_t now) {
358 if (suppress_immediate_tapdown_.val_)
359 return;
360 if (queue_.size() < 2)
361 return; // Not enough data to know
362
363 const char name[] = "LookaheadFilterInterpreter::TapDownOccurringGesture";
364
365 HardwareState& hs = queue_.back().state_;
366 if (queue_.back().state_.timestamp != now)
367 return; // We didn't push a new hardware state now
368 // See if latest hwstate has finger that previous doesn't.
369 // Get the state_ of back->prev
370 HardwareState& prev_hs = queue_.at(-2).state_;
371 if (hs.finger_cnt > prev_hs.finger_cnt) {
372 // Finger was added.
373 auto fling_tap_down = Gesture(kGestureFling,
374 prev_hs.timestamp, hs.timestamp,
375 0, 0, GESTURES_FLING_TAP_DOWN);
376 LogGestureProduce(name, fling_tap_down);
377 ProduceGesture(fling_tap_down);
378 return;
379 }
380 // Go finger by finger for a final check
381 for (size_t i = 0; i < hs.finger_cnt; i++)
382 if (!prev_hs.GetFingerState(hs.fingers[i].tracking_id)) {
383 auto fling_tap_down = Gesture(kGestureFling,
384 prev_hs.timestamp, hs.timestamp,
385 0, 0, GESTURES_FLING_TAP_DOWN);
386 LogGestureProduce(name, fling_tap_down);
387 ProduceGesture(fling_tap_down);
388 return;
389 }
390 }
391
SeparateFinger(QState * node,FingerState * fs,short input_id)392 void LookaheadFilterInterpreter::SeparateFinger(QState* node,
393 FingerState* fs,
394 short input_id) {
395 short output_id = NextTrackingId();
396 if (!MapContainsKey(node->output_ids_, input_id)) {
397 Err("How is this possible?");
398 return;
399 }
400 node->output_ids_[input_id] = output_id;
401 fs->tracking_id = output_id;
402 }
403
NextTrackingId()404 short LookaheadFilterInterpreter::NextTrackingId() {
405 short out = ++last_id_ & 0x7fff; // keep it non-negative
406 return out;
407 }
408
AttemptInterpolation()409 void LookaheadFilterInterpreter::AttemptInterpolation() {
410 if (queue_.size() < 2)
411 return;
412 QState& new_node = queue_.at(-1);
413 QState& prev = queue_.at(-2);
414 if (new_node.state_.timestamp - prev.state_.timestamp <
415 split_min_period_.val_) {
416 return; // Nodes came in too quickly to need interpolation
417 }
418 if (!prev.state_.SameFingersAs(new_node.state_))
419 return;
420 auto node = QState(hwprops_->max_finger_cnt);
421 Interpolate(prev.state_, new_node.state_, &node.state_);
422
423 double delay = max(0.0, min<stime_t>(kMaxDelay, min_delay_.val_));
424 node.due_ = node.state_.timestamp + delay;
425
426 // Make sure time seems monotonically increasing w/ this new event
427 if (node.state_.timestamp > last_interpreted_time_) {
428 queue_.insert(--queue_.end(), std::move(node));
429 }
430 }
431
HandleTimerImpl(stime_t now,stime_t * timeout)432 void LookaheadFilterInterpreter::HandleTimerImpl(stime_t now,
433 stime_t* timeout) {
434 const char name[] = "LookaheadFilterInterpreter::HandleTimerImpl";
435 LogHandleTimerPre(name, now, timeout);
436
437 stime_t next_timeout = NO_DEADLINE;
438
439 // Determine if a FlingTapDown gesture needs to be produced
440 TapDownOccurringGesture(now);
441
442 // The queue_ can have multiple nodes that are due_, so look for all
443 while (true) {
444 if (interpreter_due_deadline_ > 0.0) {
445 // Previously determined we have an expired node
446 if (interpreter_due_deadline_ > now) {
447 next_timeout = interpreter_due_deadline_ - now;
448 break; // Spurious callback
449 }
450
451 // Mark that we interpreted and propagate the next_ HandleTimer
452 last_interpreted_time_ = now;
453 next_timeout = NO_DEADLINE;
454 next_->HandleTimer(now, &next_timeout);
455 } else {
456 // No previous detection of an expired node
457 if (queue_.empty())
458 break;
459
460 // Get next uncompleted and overdue node
461 auto node = &queue_.back();
462 for (auto& elem : queue_) {
463 if (!elem.completed_) {
464 node = &elem;
465 break;
466 }
467 }
468 if (node->completed_ || node->due_ > now)
469 break;
470
471 // node has not completed and is due. Mark that we interpreted,
472 // copy the hwstate back out of the node and propagate the next_
473 // SyncInterpret
474 last_interpreted_time_ = node->state_.timestamp;
475 const size_t finger_cnt = node->state_.finger_cnt;
476 auto fs_copy =
477 std::make_unique<FingerState[]>(std::max(finger_cnt, (size_t)1));
478 std::copy(&node->state_.fingers[0],
479 &node->state_.fingers[finger_cnt],
480 &fs_copy[0]);
481 HardwareState hs_copy = {
482 node->state_.timestamp,
483 node->state_.buttons_down,
484 node->state_.finger_cnt,
485 node->state_.touch_cnt,
486 fs_copy.get(),
487 node->state_.rel_x,
488 node->state_.rel_y,
489 node->state_.rel_wheel,
490 node->state_.rel_wheel_hi_res,
491 node->state_.rel_hwheel,
492 node->state_.msc_timestamp,
493 };
494 next_timeout = NO_DEADLINE;
495 next_->SyncInterpret(hs_copy, &next_timeout);
496
497 // Clear previously completed nodes, but keep at least two nodes.
498 while (queue_.size() > 2 && queue_.front().completed_) {
499 queue_.pop_front();
500 }
501
502 // Mark current node completed. This should be the only completed
503 // node in the queue.
504 node->completed_ = true;
505
506 // Copy finger flags for upstream filters.
507 for (size_t i = 0; i < node->state_.finger_cnt; i++) {
508 node->state_.fingers[i].flags = hs_copy.fingers[i].flags;
509 }
510 }
511 UpdateInterpreterDue(next_timeout, now, timeout);
512 }
513 UpdateInterpreterDue(next_timeout, now, timeout);
514 LogHandleTimerPost(name, now, timeout);
515 }
516
ConsumeGesture(const Gesture & gesture)517 void LookaheadFilterInterpreter::ConsumeGesture(const Gesture& gesture) {
518 const char name[] = "LookaheadFilterInterpreter::ConsumeGesture";
519 LogGestureConsume(name, gesture);
520
521 QState& node = queue_.front();
522
523 float distance_sq = 0.0;
524 // Slow movements should potentially be suppressed
525 switch (gesture.type) {
526 case kGestureTypeMove:
527 distance_sq = gesture.details.move.dx * gesture.details.move.dx +
528 gesture.details.move.dy * gesture.details.move.dy;
529 break;
530 case kGestureTypeScroll:
531 distance_sq = gesture.details.scroll.dx * gesture.details.scroll.dx +
532 gesture.details.scroll.dy * gesture.details.scroll.dy;
533 break;
534 default:
535 // Non-movement: just allow it.
536 LogGestureProduce(name, gesture);
537 ProduceGesture(gesture);
538 return;
539 }
540 stime_t time_delta = gesture.end_time - gesture.start_time;
541 float min_nonsuppress_dist_sq =
542 min_nonsuppress_speed_.val_ * min_nonsuppress_speed_.val_ *
543 time_delta * time_delta;
544 if (distance_sq >= min_nonsuppress_dist_sq) {
545 LogGestureProduce(name, gesture);
546 ProduceGesture(gesture);
547 return;
548 }
549 // Speed is slow. Suppress if fingers have changed.
550 for (auto iter = ++queue_.begin(); iter != queue_.end(); ++iter) {
551 if (!node.state_.SameFingersAs((*iter).state_) ||
552 (node.state_.buttons_down != (*iter).state_.buttons_down))
553 return; // suppress
554 }
555
556 LogGestureProduce(name, gesture);
557 ProduceGesture(gesture);
558 }
559
UpdateInterpreterDue(stime_t new_interpreter_timeout,stime_t now,stime_t * timeout)560 void LookaheadFilterInterpreter::UpdateInterpreterDue(
561 stime_t new_interpreter_timeout,
562 stime_t now,
563 stime_t* timeout) {
564 // The next hardware state may already be over due, thus having a negative
565 // timeout, so we use -DBL_MAX as the invalid value.
566 stime_t next_hwstate_timeout = -DBL_MAX;
567 // Scan queue_ to find when next hwstate is due.
568 for (auto& elem : queue_) {
569 if (elem.completed_)
570 continue;
571 next_hwstate_timeout = elem.due_ - now;
572 break;
573 }
574
575 interpreter_due_deadline_ = -1.0;
576 if (new_interpreter_timeout >= 0.0 &&
577 (new_interpreter_timeout < next_hwstate_timeout ||
578 next_hwstate_timeout == -DBL_MAX)) {
579 interpreter_due_deadline_ = new_interpreter_timeout + now;
580 *timeout = new_interpreter_timeout;
581 } else if (next_hwstate_timeout > -DBL_MAX) {
582 *timeout = next_hwstate_timeout <= 0.0 ? NO_DEADLINE : next_hwstate_timeout;
583 }
584 }
585
Initialize(const HardwareProperties * hwprops,Metrics * metrics,MetricsProperties * mprops,GestureConsumer * consumer)586 void LookaheadFilterInterpreter::Initialize(
587 const HardwareProperties* hwprops,
588 Metrics* metrics,
589 MetricsProperties* mprops,
590 GestureConsumer* consumer) {
591 FilterInterpreter::Initialize(hwprops, nullptr, mprops, consumer);
592 queue_.clear();
593 }
594
ExtraVariableDelay() const595 stime_t LookaheadFilterInterpreter::ExtraVariableDelay() const {
596 return std::max<stime_t>(0.0, max_delay_.val_ - min_delay_.val_);
597 }
598
QState()599 LookaheadFilterInterpreter::QState::QState()
600 : max_fingers_(0) {
601 fs_.reset();
602 state_.fingers = nullptr;
603 }
604
QState(unsigned short max_fingers)605 LookaheadFilterInterpreter::QState::QState(unsigned short max_fingers)
606 : max_fingers_(max_fingers) {
607 fs_.reset(new FingerState[max_fingers]);
608 state_.fingers = fs_.get();
609 }
610
611 } // namespace gestures
612