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