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