Home | History | Annotate | Download | only in touch
      1 // Copyright 2013 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 "ash/touch/touch_hud_debug.h"
      6 
      7 #include "ash/display/display_manager.h"
      8 #include "ash/root_window_controller.h"
      9 #include "ash/shell.h"
     10 #include "base/json/json_string_value_serializer.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "third_party/skia/include/core/SkPath.h"
     15 #include "ui/aura/root_window.h"
     16 #include "ui/events/event.h"
     17 #include "ui/gfx/animation/animation_delegate.h"
     18 #include "ui/gfx/canvas.h"
     19 #include "ui/gfx/display.h"
     20 #include "ui/gfx/size.h"
     21 #include "ui/gfx/transform.h"
     22 #include "ui/views/controls/label.h"
     23 #include "ui/views/layout/box_layout.h"
     24 #include "ui/views/widget/widget.h"
     25 
     26 #if defined(USE_X11)
     27 #include <X11/extensions/XInput2.h>
     28 #include <X11/Xlib.h>
     29 
     30 #include "ui/events/x/device_data_manager.h"
     31 #endif
     32 
     33 namespace ash {
     34 namespace internal {
     35 
     36 const int kPointRadius = 20;
     37 const SkColor kColors[] = {
     38   SK_ColorYELLOW,
     39   SK_ColorGREEN,
     40   SK_ColorRED,
     41   SK_ColorBLUE,
     42   SK_ColorGRAY,
     43   SK_ColorMAGENTA,
     44   SK_ColorCYAN,
     45   SK_ColorWHITE,
     46   SK_ColorBLACK,
     47   SkColorSetRGB(0xFF, 0x8C, 0x00),
     48   SkColorSetRGB(0x8B, 0x45, 0x13),
     49   SkColorSetRGB(0xFF, 0xDE, 0xAD),
     50 };
     51 const int kAlpha = 0x60;
     52 const int kMaxPaths = arraysize(kColors);
     53 const int kReducedScale = 10;
     54 
     55 const char* GetTouchEventLabel(ui::EventType type) {
     56   switch (type) {
     57     case ui::ET_UNKNOWN:
     58       return " ";
     59     case ui::ET_TOUCH_PRESSED:
     60       return "P";
     61     case ui::ET_TOUCH_MOVED:
     62       return "M";
     63     case ui::ET_TOUCH_RELEASED:
     64       return "R";
     65     case ui::ET_TOUCH_CANCELLED:
     66       return "C";
     67     default:
     68       break;
     69   }
     70   return "?";
     71 }
     72 
     73 int GetTrackingId(const ui::TouchEvent& event) {
     74   if (!event.HasNativeEvent())
     75     return 0;
     76 #if defined(USE_XI2_MT)
     77   ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
     78   double tracking_id;
     79   if (manager->GetEventData(*event.native_event(),
     80                             ui::DeviceDataManager::DT_TOUCH_TRACKING_ID,
     81                             &tracking_id)) {
     82     return static_cast<int>(tracking_id);
     83   }
     84 #endif
     85   return 0;
     86 }
     87 
     88 int GetSourceDeviceId(const ui::TouchEvent& event) {
     89   if (!event.HasNativeEvent())
     90     return 0;
     91 #if defined(USE_X11)
     92   XEvent* xev = event.native_event();
     93   return static_cast<XIDeviceEvent*>(xev->xcookie.data)->sourceid;
     94 #endif
     95   return 0;
     96 }
     97 
     98 // A TouchPointLog represents a single touch-event of a touch point.
     99 struct TouchPointLog {
    100  public:
    101   explicit TouchPointLog(const ui::TouchEvent& touch)
    102       : id(touch.touch_id()),
    103         type(touch.type()),
    104         location(touch.root_location()),
    105         timestamp(touch.time_stamp().InMillisecondsF()),
    106         radius_x(touch.radius_x()),
    107         radius_y(touch.radius_y()),
    108         pressure(touch.force()),
    109         tracking_id(GetTrackingId(touch)),
    110         source_device(GetSourceDeviceId(touch)) {
    111   }
    112 
    113   // Populates a dictionary value with all the information about the touch
    114   // point.
    115   scoped_ptr<DictionaryValue> GetAsDictionary() const {
    116     scoped_ptr<DictionaryValue> value(new DictionaryValue());
    117 
    118     value->SetInteger("id", id);
    119     value->SetString("type", std::string(GetTouchEventLabel(type)));
    120     value->SetString("location", location.ToString());
    121     value->SetDouble("timestamp", timestamp);
    122     value->SetDouble("radius_x", radius_x);
    123     value->SetDouble("radius_y", radius_y);
    124     value->SetDouble("pressure", pressure);
    125     value->SetInteger("tracking_id", tracking_id);
    126     value->SetInteger("source_device", source_device);
    127 
    128     return value.Pass();
    129   }
    130 
    131   int id;
    132   ui::EventType type;
    133   gfx::Point location;
    134   double timestamp;
    135   float radius_x;
    136   float radius_y;
    137   float pressure;
    138   int tracking_id;
    139   int source_device;
    140 };
    141 
    142 // A TouchTrace keeps track of all the touch events of a single touch point
    143 // (starting from a touch-press and ending at a touch-release or touch-cancel).
    144 class TouchTrace {
    145  public:
    146   typedef std::vector<TouchPointLog>::iterator iterator;
    147   typedef std::vector<TouchPointLog>::const_iterator const_iterator;
    148   typedef std::vector<TouchPointLog>::reverse_iterator reverse_iterator;
    149   typedef std::vector<TouchPointLog>::const_reverse_iterator
    150       const_reverse_iterator;
    151 
    152   TouchTrace() {
    153   }
    154 
    155   void AddTouchPoint(const ui::TouchEvent& touch) {
    156     log_.push_back(TouchPointLog(touch));
    157   }
    158 
    159   const std::vector<TouchPointLog>& log() const { return log_; }
    160 
    161   bool active() const {
    162     return !log_.empty() && log_.back().type != ui::ET_TOUCH_RELEASED &&
    163         log_.back().type != ui::ET_TOUCH_CANCELLED;
    164   }
    165 
    166   // Returns a list containing data from all events for the touch point.
    167   scoped_ptr<ListValue> GetAsList() const {
    168     scoped_ptr<ListValue> list(new ListValue());
    169     for (const_iterator i = log_.begin(); i != log_.end(); ++i)
    170       list->Append((*i).GetAsDictionary().release());
    171     return list.Pass();
    172   }
    173 
    174   void Reset() {
    175     log_.clear();
    176   }
    177 
    178  private:
    179   std::vector<TouchPointLog> log_;
    180 
    181   DISALLOW_COPY_AND_ASSIGN(TouchTrace);
    182 };
    183 
    184 // A TouchLog keeps track of all touch events of all touch points.
    185 class TouchLog {
    186  public:
    187   TouchLog() : next_trace_index_(0) {
    188   }
    189 
    190   void AddTouchPoint(const ui::TouchEvent& touch) {
    191     if (touch.type() == ui::ET_TOUCH_PRESSED)
    192       StartTrace(touch);
    193     AddToTrace(touch);
    194   }
    195 
    196   void Reset() {
    197     next_trace_index_ = 0;
    198     for (int i = 0; i < kMaxPaths; ++i)
    199       traces_[i].Reset();
    200   }
    201 
    202   scoped_ptr<ListValue> GetAsList() const {
    203     scoped_ptr<ListValue> list(new ListValue());
    204     for (int i = 0; i < kMaxPaths; ++i) {
    205       if (!traces_[i].log().empty())
    206         list->Append(traces_[i].GetAsList().release());
    207     }
    208     return list.Pass();
    209   }
    210 
    211   int GetTraceIndex(int touch_id) const {
    212     return touch_id_to_trace_index_.at(touch_id);
    213   }
    214 
    215   const TouchTrace* traces() const {
    216     return traces_;
    217   }
    218 
    219  private:
    220   void StartTrace(const ui::TouchEvent& touch) {
    221     // Find the first inactive spot; otherwise, overwrite the one
    222     // |next_trace_index_| is pointing to.
    223     int old_trace_index = next_trace_index_;
    224     do {
    225       if (!traces_[next_trace_index_].active())
    226         break;
    227       next_trace_index_ = (next_trace_index_ + 1) % kMaxPaths;
    228     } while (next_trace_index_ != old_trace_index);
    229     int touch_id = touch.touch_id();
    230     traces_[next_trace_index_].Reset();
    231     touch_id_to_trace_index_[touch_id] = next_trace_index_;
    232     next_trace_index_ = (next_trace_index_ + 1) % kMaxPaths;
    233   }
    234 
    235   void AddToTrace(const ui::TouchEvent& touch) {
    236     int touch_id = touch.touch_id();
    237     int trace_index = touch_id_to_trace_index_[touch_id];
    238     traces_[trace_index].AddTouchPoint(touch);
    239   }
    240 
    241   TouchTrace traces_[kMaxPaths];
    242   int next_trace_index_;
    243 
    244   std::map<int, int> touch_id_to_trace_index_;
    245 
    246   DISALLOW_COPY_AND_ASSIGN(TouchLog);
    247 };
    248 
    249 // TouchHudCanvas draws touch traces in |FULLSCREEN| and |REDUCED_SCALE| modes.
    250 class TouchHudCanvas : public views::View {
    251  public:
    252   explicit TouchHudCanvas(const TouchLog& touch_log)
    253       : touch_log_(touch_log),
    254         scale_(1) {
    255     SetPaintToLayer(true);
    256     SetFillsBoundsOpaquely(false);
    257 
    258     paint_.setStyle(SkPaint::kFill_Style);
    259   }
    260 
    261   virtual ~TouchHudCanvas() {}
    262 
    263   void SetScale(int scale) {
    264     if (scale_ == scale)
    265       return;
    266     scale_ = scale;
    267     gfx::Transform transform;
    268     transform.Scale(1. / scale_, 1. / scale_);
    269     layer()->SetTransform(transform);
    270   }
    271 
    272   int scale() const { return scale_; }
    273 
    274   void TouchPointAdded(int touch_id) {
    275     int trace_index = touch_log_.GetTraceIndex(touch_id);
    276     const TouchTrace& trace = touch_log_.traces()[trace_index];
    277     const TouchPointLog& point = trace.log().back();
    278     if (point.type == ui::ET_TOUCH_PRESSED)
    279       StartedTrace(trace_index);
    280     if (point.type != ui::ET_TOUCH_CANCELLED)
    281       AddedPointToTrace(trace_index);
    282   }
    283 
    284   void Clear() {
    285     for (int i = 0; i < kMaxPaths; ++i)
    286       paths_[i].reset();
    287 
    288     SchedulePaint();
    289   }
    290 
    291  private:
    292   void StartedTrace(int trace_index) {
    293     paths_[trace_index].reset();
    294     colors_[trace_index] = SkColorSetA(kColors[trace_index], kAlpha);
    295   }
    296 
    297   void AddedPointToTrace(int trace_index) {
    298     const TouchTrace& trace = touch_log_.traces()[trace_index];
    299     const TouchPointLog& point = trace.log().back();
    300     const gfx::Point& location = point.location;
    301     SkScalar x = SkIntToScalar(location.x());
    302     SkScalar y = SkIntToScalar(location.y());
    303     SkPoint last;
    304     if (!paths_[trace_index].getLastPt(&last) || x != last.x() ||
    305         y != last.y()) {
    306       paths_[trace_index].addCircle(x, y, SkIntToScalar(kPointRadius));
    307       SchedulePaint();
    308     }
    309   }
    310 
    311   // Overridden from views::View.
    312   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
    313     for (int i = 0; i < kMaxPaths; ++i) {
    314       if (paths_[i].countPoints() == 0)
    315         continue;
    316       paint_.setColor(colors_[i]);
    317       canvas->DrawPath(paths_[i], paint_);
    318     }
    319   }
    320 
    321   SkPaint paint_;
    322 
    323   const TouchLog& touch_log_;
    324   SkPath paths_[kMaxPaths];
    325   SkColor colors_[kMaxPaths];
    326 
    327   int scale_;
    328 
    329   DISALLOW_COPY_AND_ASSIGN(TouchHudCanvas);
    330 };
    331 
    332 TouchHudDebug::TouchHudDebug(aura::Window* initial_root)
    333     : TouchObserverHUD(initial_root),
    334       mode_(FULLSCREEN),
    335       touch_log_(new TouchLog()),
    336       canvas_(NULL),
    337       label_container_(NULL) {
    338   const gfx::Display& display =
    339       Shell::GetInstance()->display_manager()->GetDisplayForId(display_id());
    340 
    341   views::View* content = widget()->GetContentsView();
    342 
    343   canvas_ = new TouchHudCanvas(*touch_log_);
    344   content->AddChildView(canvas_);
    345 
    346   const gfx::Size& display_size = display.size();
    347   canvas_->SetSize(display_size);
    348 
    349   label_container_ = new views::View;
    350   label_container_->SetLayoutManager(new views::BoxLayout(
    351       views::BoxLayout::kVertical, 0, 0, 0));
    352 
    353   for (int i = 0; i < kMaxTouchPoints; ++i) {
    354     touch_labels_[i] = new views::Label;
    355     touch_labels_[i]->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
    356     touch_labels_[i]->SetShadowColors(SK_ColorWHITE,
    357                                       SK_ColorWHITE);
    358     touch_labels_[i]->SetShadowOffset(1, 1);
    359     label_container_->AddChildView(touch_labels_[i]);
    360   }
    361   label_container_->SetX(0);
    362   label_container_->SetY(display_size.height() / kReducedScale);
    363   label_container_->SetSize(label_container_->GetPreferredSize());
    364   label_container_->SetVisible(false);
    365   content->AddChildView(label_container_);
    366 }
    367 
    368 TouchHudDebug::~TouchHudDebug() {
    369 }
    370 
    371 // static
    372 scoped_ptr<DictionaryValue> TouchHudDebug::GetAllAsDictionary() {
    373   scoped_ptr<DictionaryValue> value(new DictionaryValue());
    374   aura::Window::Windows roots = Shell::GetInstance()->GetAllRootWindows();
    375   for (aura::Window::Windows::iterator iter = roots.begin();
    376       iter != roots.end(); ++iter) {
    377     internal::RootWindowController* controller = GetRootWindowController(*iter);
    378     internal::TouchHudDebug* hud = controller->touch_hud_debug();
    379     if (hud) {
    380       scoped_ptr<ListValue> list = hud->GetLogAsList();
    381       if (!list->empty())
    382         value->Set(base::Int64ToString(hud->display_id()), list.release());
    383     }
    384   }
    385   return value.Pass();
    386 }
    387 
    388 void TouchHudDebug::ChangeToNextMode() {
    389   switch (mode_) {
    390     case FULLSCREEN:
    391       SetMode(REDUCED_SCALE);
    392       break;
    393     case REDUCED_SCALE:
    394       SetMode(INVISIBLE);
    395       break;
    396     case INVISIBLE:
    397       SetMode(FULLSCREEN);
    398       break;
    399   }
    400 }
    401 
    402 scoped_ptr<ListValue> TouchHudDebug::GetLogAsList() const {
    403   return touch_log_->GetAsList();
    404 }
    405 
    406 void TouchHudDebug::Clear() {
    407   if (widget()->IsVisible()) {
    408     canvas_->Clear();
    409     for (int i = 0; i < kMaxTouchPoints; ++i)
    410       touch_labels_[i]->SetText(string16());
    411     label_container_->SetSize(label_container_->GetPreferredSize());
    412   }
    413 }
    414 
    415 void TouchHudDebug::SetMode(Mode mode) {
    416   if (mode_ == mode)
    417     return;
    418   mode_ = mode;
    419   switch (mode) {
    420     case FULLSCREEN:
    421       label_container_->SetVisible(false);
    422       canvas_->SetVisible(true);
    423       canvas_->SetScale(1);
    424       canvas_->SchedulePaint();
    425       widget()->Show();
    426       break;
    427     case REDUCED_SCALE:
    428       label_container_->SetVisible(true);
    429       canvas_->SetVisible(true);
    430       canvas_->SetScale(kReducedScale);
    431       canvas_->SchedulePaint();
    432       widget()->Show();
    433       break;
    434     case INVISIBLE:
    435       widget()->Hide();
    436       break;
    437   }
    438 }
    439 
    440 void TouchHudDebug::UpdateTouchPointLabel(int index) {
    441   int trace_index = touch_log_->GetTraceIndex(index);
    442   const TouchTrace& trace = touch_log_->traces()[trace_index];
    443   TouchTrace::const_reverse_iterator point = trace.log().rbegin();
    444   ui::EventType touch_status = point->type;
    445   float touch_radius = std::max(point->radius_x, point->radius_y);
    446   while (point != trace.log().rend() && point->type == ui::ET_TOUCH_CANCELLED)
    447     point++;
    448   DCHECK(point != trace.log().rend());
    449   gfx::Point touch_position = point->location;
    450 
    451   std::string string = base::StringPrintf("%2d: %s %s (%.4f)",
    452                                           index,
    453                                           GetTouchEventLabel(touch_status),
    454                                           touch_position.ToString().c_str(),
    455                                           touch_radius);
    456   touch_labels_[index]->SetText(UTF8ToUTF16(string));
    457 }
    458 
    459 void TouchHudDebug::OnTouchEvent(ui::TouchEvent* event) {
    460   if (event->touch_id() >= kMaxTouchPoints)
    461     return;
    462 
    463   touch_log_->AddTouchPoint(*event);
    464   canvas_->TouchPointAdded(event->touch_id());
    465   UpdateTouchPointLabel(event->touch_id());
    466   label_container_->SetSize(label_container_->GetPreferredSize());
    467 }
    468 
    469 void TouchHudDebug::OnDisplayBoundsChanged(const gfx::Display& display) {
    470   TouchObserverHUD::OnDisplayBoundsChanged(display);
    471 
    472   if (display.id() != display_id())
    473     return;
    474   const gfx::Size& size = display.size();
    475   canvas_->SetSize(size);
    476   label_container_->SetY(size.height() / kReducedScale);
    477 }
    478 
    479 void TouchHudDebug::SetHudForRootWindowController(
    480     RootWindowController* controller) {
    481   controller->set_touch_hud_debug(this);
    482 }
    483 
    484 void TouchHudDebug::UnsetHudForRootWindowController(
    485     RootWindowController* controller) {
    486   controller->set_touch_hud_debug(NULL);
    487 }
    488 
    489 }  // namespace internal
    490 }  // namespace ash
    491