• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetTouchEventLabel(ui::EventType type)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 
GetTrackingId(const ui::TouchEvent & event)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 
GetSourceDeviceId(const ui::TouchEvent & event)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:
TouchPointLogash::internal::TouchPointLog101   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.
GetAsDictionaryash::internal::TouchPointLog115   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 
TouchTrace()152   TouchTrace() {
153   }
154 
AddTouchPoint(const ui::TouchEvent & touch)155   void AddTouchPoint(const ui::TouchEvent& touch) {
156     log_.push_back(TouchPointLog(touch));
157   }
158 
log() const159   const std::vector<TouchPointLog>& log() const { return log_; }
160 
active() const161   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.
GetAsList() const167   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 
Reset()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:
TouchLog()187   TouchLog() : next_trace_index_(0) {
188   }
189 
AddTouchPoint(const ui::TouchEvent & touch)190   void AddTouchPoint(const ui::TouchEvent& touch) {
191     if (touch.type() == ui::ET_TOUCH_PRESSED)
192       StartTrace(touch);
193     AddToTrace(touch);
194   }
195 
Reset()196   void Reset() {
197     next_trace_index_ = 0;
198     for (int i = 0; i < kMaxPaths; ++i)
199       traces_[i].Reset();
200   }
201 
GetAsList() const202   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 
GetTraceIndex(int touch_id) const211   int GetTraceIndex(int touch_id) const {
212     return touch_id_to_trace_index_.at(touch_id);
213   }
214 
traces() const215   const TouchTrace* traces() const {
216     return traces_;
217   }
218 
219  private:
StartTrace(const ui::TouchEvent & touch)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 
AddToTrace(const ui::TouchEvent & touch)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:
TouchHudCanvas(const TouchLog & touch_log)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 
~TouchHudCanvas()261   virtual ~TouchHudCanvas() {}
262 
SetScale(int scale)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 
scale() const272   int scale() const { return scale_; }
273 
TouchPointAdded(int touch_id)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 
Clear()284   void Clear() {
285     for (int i = 0; i < kMaxPaths; ++i)
286       paths_[i].reset();
287 
288     SchedulePaint();
289   }
290 
291  private:
StartedTrace(int trace_index)292   void StartedTrace(int trace_index) {
293     paths_[trace_index].reset();
294     colors_[trace_index] = SkColorSetA(kColors[trace_index], kAlpha);
295   }
296 
AddedPointToTrace(int trace_index)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.
OnPaint(gfx::Canvas * canvas)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 
TouchHudDebug(aura::Window * initial_root)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 
~TouchHudDebug()368 TouchHudDebug::~TouchHudDebug() {
369 }
370 
371 // static
GetAllAsDictionary()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 
ChangeToNextMode()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 
GetLogAsList() const402 scoped_ptr<ListValue> TouchHudDebug::GetLogAsList() const {
403   return touch_log_->GetAsList();
404 }
405 
Clear()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 
SetMode(Mode mode)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 
UpdateTouchPointLabel(int index)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 
OnTouchEvent(ui::TouchEvent * event)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 
OnDisplayBoundsChanged(const gfx::Display & display)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 
SetHudForRootWindowController(RootWindowController * controller)479 void TouchHudDebug::SetHudForRootWindowController(
480     RootWindowController* controller) {
481   controller->set_touch_hud_debug(this);
482 }
483 
UnsetHudForRootWindowController(RootWindowController * controller)484 void TouchHudDebug::UnsetHudForRootWindowController(
485     RootWindowController* controller) {
486   controller->set_touch_hud_debug(NULL);
487 }
488 
489 }  // namespace internal
490 }  // namespace ash
491