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