xref: /aosp_15_r20/external/libchrome-gestures/src/lookahead_filter_interpreter.cc (revision aed3e5085e770be5b69ce25295ecf6ddf906af95)
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