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