Home | History | Annotate | Download | only in gesture_detection
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      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 "ui/events/gesture_detection/motion_event_buffer.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "ui/events/gesture_detection/motion_event.h"
      9 #include "ui/events/gesture_detection/motion_event_generic.h"
     10 
     11 namespace ui {
     12 namespace {
     13 
     14 // Latency added during resampling. A few milliseconds doesn't hurt much but
     15 // reduces the impact of mispredicted touch positions.
     16 const int kResampleLatencyMs = 5;
     17 
     18 // Minimum time difference between consecutive samples before attempting to
     19 // resample.
     20 const int kResampleMinDeltaMs = 2;
     21 
     22 // Maximum time to predict forward from the last known state, to avoid
     23 // predicting too far into the future.  This time is further bounded by 50% of
     24 // the last time delta.
     25 const int kResampleMaxPredictionMs = 8;
     26 
     27 typedef ScopedVector<MotionEvent> MotionEventVector;
     28 
     29 float Lerp(float a, float b, float alpha) {
     30   return a + alpha * (b - a);
     31 }
     32 
     33 bool CanAddSample(const MotionEvent& event0, const MotionEvent& event1) {
     34   DCHECK_EQ(event0.GetAction(), MotionEvent::ACTION_MOVE);
     35   if (event1.GetAction() != MotionEvent::ACTION_MOVE)
     36     return false;
     37 
     38   const size_t pointer_count = event0.GetPointerCount();
     39   if (pointer_count != event1.GetPointerCount())
     40     return false;
     41 
     42   for (size_t event0_i = 0; event0_i < pointer_count; ++event0_i) {
     43     const int id = event0.GetPointerId(event0_i);
     44     const int event1_i = event1.FindPointerIndexOfId(id);
     45     if (event1_i == -1)
     46       return false;
     47     if (event0.GetToolType(event0_i) != event1.GetToolType(event1_i))
     48       return false;
     49   }
     50 
     51   return true;
     52 }
     53 
     54 bool ShouldResampleTool(MotionEvent::ToolType tool) {
     55   return tool == MotionEvent::TOOL_TYPE_UNKNOWN ||
     56          tool == MotionEvent::TOOL_TYPE_FINGER;
     57 }
     58 
     59 size_t CountSamplesNoLaterThan(const MotionEventVector& batch,
     60                                base::TimeTicks time) {
     61   size_t count = 0;
     62   while (count < batch.size() && batch[count]->GetEventTime() <= time)
     63     ++count;
     64   return count;
     65 }
     66 
     67 MotionEventVector ConsumeSamplesNoLaterThan(MotionEventVector* batch,
     68                                             base::TimeTicks time) {
     69   DCHECK(batch);
     70   size_t count = CountSamplesNoLaterThan(*batch, time);
     71   DCHECK_GE(batch->size(), count);
     72   if (count == 0)
     73     return MotionEventVector();
     74 
     75   if (count == batch->size())
     76     return batch->Pass();
     77 
     78   // TODO(jdduke): Use a ScopedDeque to work around this mess.
     79   MotionEventVector unconsumed_batch;
     80   unconsumed_batch.insert(
     81       unconsumed_batch.begin(), batch->begin() + count, batch->end());
     82   batch->weak_erase(batch->begin() + count, batch->end());
     83 
     84   unconsumed_batch.swap(*batch);
     85   DCHECK_GE(unconsumed_batch.size(), 1U);
     86   return unconsumed_batch.Pass();
     87 }
     88 
     89 PointerProperties PointerFromMotionEvent(const MotionEvent& event,
     90                                          size_t pointer_index) {
     91   PointerProperties result;
     92   result.id = event.GetPointerId(pointer_index);
     93   result.tool_type = event.GetToolType(pointer_index);
     94   result.x = event.GetX(pointer_index);
     95   result.y = event.GetY(pointer_index);
     96   result.raw_x = event.GetRawX(pointer_index);
     97   result.raw_y = event.GetRawY(pointer_index);
     98   result.pressure = event.GetPressure(pointer_index);
     99   result.touch_major = event.GetTouchMajor(pointer_index);
    100   result.touch_minor = event.GetTouchMinor(pointer_index);
    101   result.orientation = event.GetOrientation(pointer_index);
    102   return result;
    103 }
    104 
    105 PointerProperties ResamplePointer(const MotionEvent& event0,
    106                                   const MotionEvent& event1,
    107                                   size_t event0_pointer_index,
    108                                   size_t event1_pointer_index,
    109                                   float alpha) {
    110   DCHECK_EQ(event0.GetPointerId(event0_pointer_index),
    111             event1.GetPointerId(event1_pointer_index));
    112   // If the tool should not be resampled, use the latest event in the valid
    113   // horizon (i.e., the event no later than the time interpolated by alpha).
    114   if (!ShouldResampleTool(event0.GetToolType(event0_pointer_index))) {
    115     if (alpha > 1)
    116       return PointerFromMotionEvent(event1, event1_pointer_index);
    117     else
    118       return PointerFromMotionEvent(event0, event0_pointer_index);
    119   }
    120 
    121   PointerProperties p(PointerFromMotionEvent(event0, event0_pointer_index));
    122   p.x = Lerp(p.x, event1.GetX(event1_pointer_index), alpha);
    123   p.y = Lerp(p.y, event1.GetY(event1_pointer_index), alpha);
    124   p.raw_x = Lerp(p.raw_x, event1.GetRawX(event1_pointer_index), alpha);
    125   p.raw_y = Lerp(p.raw_y, event1.GetRawY(event1_pointer_index), alpha);
    126   return p;
    127 }
    128 
    129 scoped_ptr<MotionEvent> ResampleMotionEvent(const MotionEvent& event0,
    130                                             const MotionEvent& event1,
    131                                             base::TimeTicks resample_time) {
    132   DCHECK_EQ(MotionEvent::ACTION_MOVE, event0.GetAction());
    133   DCHECK_EQ(event0.GetPointerCount(), event1.GetPointerCount());
    134 
    135   const base::TimeTicks time0 = event0.GetEventTime();
    136   const base::TimeTicks time1 = event1.GetEventTime();
    137   DCHECK(time0 < time1);
    138   DCHECK(time0 <= resample_time);
    139 
    140   const float alpha = (resample_time - time0).InMillisecondsF() /
    141                       (time1 - time0).InMillisecondsF();
    142 
    143   scoped_ptr<MotionEventGeneric> event;
    144   const size_t pointer_count = event0.GetPointerCount();
    145   DCHECK_EQ(pointer_count, event1.GetPointerCount());
    146   for (size_t event0_i = 0; event0_i < pointer_count; ++event0_i) {
    147     int event1_i = event1.FindPointerIndexOfId(event0.GetPointerId(event0_i));
    148     DCHECK_NE(event1_i, -1);
    149     PointerProperties pointer = ResamplePointer(
    150         event0, event1, event0_i, static_cast<size_t>(event1_i), alpha);
    151 
    152     if (event0_i == 0) {
    153       event.reset(new MotionEventGeneric(
    154           MotionEvent::ACTION_MOVE, resample_time, pointer));
    155     } else {
    156       event->PushPointer(pointer);
    157     }
    158   }
    159 
    160   DCHECK(event);
    161   event->set_id(event0.GetId());
    162   event->set_action_index(event0.GetActionIndex());
    163   event->set_button_state(event0.GetButtonState());
    164 
    165   return event.PassAs<MotionEvent>();
    166 }
    167 
    168 // MotionEvent implementation for storing multiple events, with the most
    169 // recent event used as the base event, and prior events used as the history.
    170 class CompoundMotionEvent : public ui::MotionEvent {
    171  public:
    172   explicit CompoundMotionEvent(MotionEventVector events)
    173       : events_(events.Pass()) {
    174     DCHECK_GE(events_.size(), 1U);
    175   }
    176   virtual ~CompoundMotionEvent() {}
    177 
    178   virtual int GetId() const OVERRIDE { return latest().GetId(); }
    179 
    180   virtual Action GetAction() const OVERRIDE { return latest().GetAction(); }
    181 
    182   virtual int GetActionIndex() const OVERRIDE {
    183     return latest().GetActionIndex();
    184   }
    185 
    186   virtual size_t GetPointerCount() const OVERRIDE {
    187     return latest().GetPointerCount();
    188   }
    189 
    190   virtual int GetPointerId(size_t pointer_index) const OVERRIDE {
    191     return latest().GetPointerId(pointer_index);
    192   }
    193 
    194   virtual float GetX(size_t pointer_index) const OVERRIDE {
    195     return latest().GetX(pointer_index);
    196   }
    197 
    198   virtual float GetY(size_t pointer_index) const OVERRIDE {
    199     return latest().GetY(pointer_index);
    200   }
    201 
    202   virtual float GetRawX(size_t pointer_index) const OVERRIDE {
    203     return latest().GetRawX(pointer_index);
    204   }
    205 
    206   virtual float GetRawY(size_t pointer_index) const OVERRIDE {
    207     return latest().GetRawY(pointer_index);
    208   }
    209 
    210   virtual float GetTouchMajor(size_t pointer_index) const OVERRIDE {
    211     return latest().GetTouchMajor(pointer_index);
    212   }
    213 
    214   virtual float GetTouchMinor(size_t pointer_index) const OVERRIDE {
    215     return latest().GetTouchMinor(pointer_index);
    216   }
    217 
    218   virtual float GetOrientation(size_t pointer_index) const OVERRIDE {
    219     return latest().GetOrientation(pointer_index);
    220   }
    221 
    222   virtual float GetPressure(size_t pointer_index) const OVERRIDE {
    223     return latest().GetPressure(pointer_index);
    224   }
    225 
    226   virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE {
    227     return latest().GetToolType(pointer_index);
    228   }
    229 
    230   virtual int GetButtonState() const OVERRIDE {
    231     return latest().GetButtonState();
    232   }
    233 
    234   virtual int GetFlags() const OVERRIDE { return latest().GetFlags(); }
    235 
    236   virtual base::TimeTicks GetEventTime() const OVERRIDE {
    237     return latest().GetEventTime();
    238   }
    239 
    240   virtual size_t GetHistorySize() const OVERRIDE { return events_.size() - 1; }
    241 
    242   virtual base::TimeTicks GetHistoricalEventTime(
    243       size_t historical_index) const OVERRIDE {
    244     DCHECK_LT(historical_index, GetHistorySize());
    245     return events_[historical_index]->GetEventTime();
    246   }
    247 
    248   virtual float GetHistoricalTouchMajor(
    249       size_t pointer_index,
    250       size_t historical_index) const OVERRIDE {
    251     DCHECK_LT(historical_index, GetHistorySize());
    252     return events_[historical_index]->GetTouchMajor();
    253   }
    254 
    255   virtual float GetHistoricalX(size_t pointer_index,
    256                                size_t historical_index) const OVERRIDE {
    257     DCHECK_LT(historical_index, GetHistorySize());
    258     return events_[historical_index]->GetX(pointer_index);
    259   }
    260 
    261   virtual float GetHistoricalY(size_t pointer_index,
    262                                size_t historical_index) const OVERRIDE {
    263     DCHECK_LT(historical_index, GetHistorySize());
    264     return events_[historical_index]->GetY(pointer_index);
    265   }
    266 
    267   virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE {
    268     MotionEventVector cloned_events;
    269     cloned_events.reserve(events_.size());
    270     for (size_t i = 0; i < events_.size(); ++i)
    271       cloned_events.push_back(events_[i]->Clone().release());
    272     return scoped_ptr<MotionEvent>(
    273         new CompoundMotionEvent(cloned_events.Pass()));
    274   }
    275 
    276   virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE {
    277     return latest().Cancel();
    278   }
    279 
    280   // Returns the new, resampled event, or NULL if none was created.
    281   // TODO(jdduke): Revisit resampling to handle cases where alternating frames
    282   // are resampled or resampling is otherwise inconsistent, e.g., a 90hz input
    283   // and 60hz frame signal could phase-align such that even frames yield an
    284   // extrapolated event and odd frames are not resampled, crbug.com/399381.
    285   const MotionEvent* TryResample(base::TimeTicks resample_time,
    286                                  const ui::MotionEvent* next) {
    287     DCHECK_EQ(GetAction(), ACTION_MOVE);
    288     const ui::MotionEvent* event0 = NULL;
    289     const ui::MotionEvent* event1 = NULL;
    290     if (next) {
    291       DCHECK(resample_time < next->GetEventTime());
    292       // Interpolate between current sample and future sample.
    293       event0 = events_.back();
    294       event1 = next;
    295     } else if (events_.size() >= 2) {
    296       // Extrapolate future sample using current sample and past sample.
    297       event0 = events_[events_.size() - 2];
    298       event1 = events_[events_.size() - 1];
    299 
    300       const base::TimeTicks time1 = event1->GetEventTime();
    301       base::TimeTicks max_predict =
    302           time1 +
    303           std::min((event1->GetEventTime() - event0->GetEventTime()) / 2,
    304                    base::TimeDelta::FromMilliseconds(kResampleMaxPredictionMs));
    305       if (resample_time > max_predict) {
    306         TRACE_EVENT_INSTANT2("input",
    307                              "MotionEventBuffer::TryResample prediction adjust",
    308                              TRACE_EVENT_SCOPE_THREAD,
    309                              "original(ms)",
    310                              (resample_time - time1).InMilliseconds(),
    311                              "adjusted(ms)",
    312                              (max_predict - time1).InMilliseconds());
    313         resample_time = max_predict;
    314       }
    315     } else {
    316       TRACE_EVENT_INSTANT0("input",
    317                            "MotionEventBuffer::TryResample insufficient data",
    318                            TRACE_EVENT_SCOPE_THREAD);
    319       return NULL;
    320     }
    321 
    322     DCHECK(event0);
    323     DCHECK(event1);
    324     const base::TimeTicks time0 = event0->GetEventTime();
    325     const base::TimeTicks time1 = event1->GetEventTime();
    326     base::TimeDelta delta = time1 - time0;
    327     if (delta < base::TimeDelta::FromMilliseconds(kResampleMinDeltaMs)) {
    328       TRACE_EVENT_INSTANT1("input",
    329                            "MotionEventBuffer::TryResample failure",
    330                            TRACE_EVENT_SCOPE_THREAD,
    331                            "event_delta_too_small(ms)",
    332                            delta.InMilliseconds());
    333       return NULL;
    334     }
    335 
    336     events_.push_back(
    337         ResampleMotionEvent(*event0, *event1, resample_time).release());
    338     return events_.back();
    339   }
    340 
    341   size_t samples() const { return events_.size(); }
    342 
    343  private:
    344   const MotionEvent& latest() const { return *events_.back(); }
    345 
    346   // Events are in order from oldest to newest.
    347   MotionEventVector events_;
    348 
    349   DISALLOW_COPY_AND_ASSIGN(CompoundMotionEvent);
    350 };
    351 
    352 }  // namespace
    353 
    354 MotionEventBuffer::MotionEventBuffer(MotionEventBufferClient* client,
    355                                      bool enable_resampling)
    356     : client_(client), resample_(enable_resampling) {
    357 }
    358 
    359 MotionEventBuffer::~MotionEventBuffer() {
    360 }
    361 
    362 void MotionEventBuffer::OnMotionEvent(const MotionEvent& event) {
    363   if (event.GetAction() != MotionEvent::ACTION_MOVE) {
    364     last_extrapolated_event_time_ = base::TimeTicks();
    365     if (!buffered_events_.empty())
    366       FlushWithoutResampling(buffered_events_.Pass());
    367     client_->ForwardMotionEvent(event);
    368     return;
    369   }
    370 
    371   // Guard against events that are *older* than the last one that may have been
    372   // artificially synthesized.
    373   if (!last_extrapolated_event_time_.is_null()) {
    374     DCHECK(buffered_events_.empty());
    375     if (event.GetEventTime() < last_extrapolated_event_time_)
    376       return;
    377     last_extrapolated_event_time_ = base::TimeTicks();
    378   }
    379 
    380   scoped_ptr<MotionEvent> clone = event.Clone();
    381   if (buffered_events_.empty()) {
    382     buffered_events_.push_back(clone.release());
    383     client_->SetNeedsFlush();
    384     return;
    385   }
    386 
    387   if (CanAddSample(*buffered_events_.front(), *clone)) {
    388     DCHECK(buffered_events_.back()->GetEventTime() <= clone->GetEventTime());
    389   } else {
    390     FlushWithoutResampling(buffered_events_.Pass());
    391   }
    392 
    393   buffered_events_.push_back(clone.release());
    394   // No need to request another flush as the first event will have requested it.
    395 }
    396 
    397 void MotionEventBuffer::Flush(base::TimeTicks frame_time) {
    398   if (buffered_events_.empty())
    399     return;
    400 
    401   // Shifting the sample time back slightly minimizes the potential for
    402   // misprediction when extrapolating events.
    403   if (resample_)
    404     frame_time -= base::TimeDelta::FromMilliseconds(kResampleLatencyMs);
    405 
    406   // TODO(jdduke): Use a persistent MotionEventVector vector for temporary
    407   // storage.
    408   MotionEventVector events(
    409       ConsumeSamplesNoLaterThan(&buffered_events_, frame_time));
    410   if (events.empty()) {
    411     DCHECK(!buffered_events_.empty());
    412     client_->SetNeedsFlush();
    413     return;
    414   }
    415 
    416   if (!resample_ || (events.size() == 1 && buffered_events_.empty())) {
    417     FlushWithoutResampling(events.Pass());
    418     if (!buffered_events_.empty())
    419       client_->SetNeedsFlush();
    420     return;
    421   }
    422 
    423   CompoundMotionEvent resampled_event(events.Pass());
    424   base::TimeTicks original_event_time = resampled_event.GetEventTime();
    425   const MotionEvent* next_event =
    426       !buffered_events_.empty() ? buffered_events_.front() : NULL;
    427 
    428   // Try to interpolate/extrapolate a new event at |frame_time|. Note that
    429   // |new_event|, if non-NULL, is owned by |resampled_event_|.
    430   const MotionEvent* new_event =
    431       resampled_event.TryResample(frame_time, next_event);
    432 
    433   // Log the extrapolated event time, guarding against subsequently queued
    434   // events that might have an earlier timestamp.
    435   if (!next_event && new_event &&
    436       new_event->GetEventTime() > original_event_time) {
    437     last_extrapolated_event_time_ = new_event->GetEventTime();
    438   } else {
    439     last_extrapolated_event_time_ = base::TimeTicks();
    440   }
    441 
    442   client_->ForwardMotionEvent(resampled_event);
    443   if (!buffered_events_.empty())
    444     client_->SetNeedsFlush();
    445 }
    446 
    447 void MotionEventBuffer::FlushWithoutResampling(MotionEventVector events) {
    448   last_extrapolated_event_time_ = base::TimeTicks();
    449   if (events.empty())
    450     return;
    451 
    452   if (events.size() == 1) {
    453     // Avoid CompoundEvent creation to prevent unnecessary allocations.
    454     scoped_ptr<MotionEvent> event(events.front());
    455     events.weak_clear();
    456     client_->ForwardMotionEvent(*event);
    457     return;
    458   }
    459 
    460   CompoundMotionEvent compound_event(events.Pass());
    461   client_->ForwardMotionEvent(compound_event);
    462 }
    463 
    464 }  // namespace ui
    465