• 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/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_x11.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 
GetTouchEventLabel(ui::EventType type)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 
GetTrackingId(const ui::TouchEvent & event)72 int GetTrackingId(const ui::TouchEvent& event) {
73   if (!event.HasNativeEvent())
74     return 0;
75 #if defined(USE_XI2_MT)
76   ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance();
77   double tracking_id;
78   if (manager->GetEventData(*event.native_event(),
79                             ui::DeviceDataManagerX11::DT_TOUCH_TRACKING_ID,
80                             &tracking_id)) {
81     return static_cast<int>(tracking_id);
82   }
83 #endif
84   return 0;
85 }
86 
GetSourceDeviceId(const ui::TouchEvent & event)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:
TouchPointLogash::TouchPointLog100   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.
GetAsDictionaryash::TouchPointLog114   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 
TouchTrace()151   TouchTrace() {
152   }
153 
AddTouchPoint(const ui::TouchEvent & touch)154   void AddTouchPoint(const ui::TouchEvent& touch) {
155     log_.push_back(TouchPointLog(touch));
156   }
157 
log() const158   const std::vector<TouchPointLog>& log() const { return log_; }
159 
active() const160   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.
GetAsList() const166   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 
Reset()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:
TouchLog()186   TouchLog() : next_trace_index_(0) {
187   }
188 
AddTouchPoint(const ui::TouchEvent & touch)189   void AddTouchPoint(const ui::TouchEvent& touch) {
190     if (touch.type() == ui::ET_TOUCH_PRESSED)
191       StartTrace(touch);
192     AddToTrace(touch);
193   }
194 
Reset()195   void Reset() {
196     next_trace_index_ = 0;
197     for (int i = 0; i < kMaxPaths; ++i)
198       traces_[i].Reset();
199   }
200 
GetAsList() const201   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 
GetTraceIndex(int touch_id) const210   int GetTraceIndex(int touch_id) const {
211     return touch_id_to_trace_index_.at(touch_id);
212   }
213 
traces() const214   const TouchTrace* traces() const {
215     return traces_;
216   }
217 
218  private:
StartTrace(const ui::TouchEvent & touch)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 
AddToTrace(const ui::TouchEvent & touch)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:
TouchHudCanvas(const TouchLog & touch_log)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 
~TouchHudCanvas()260   virtual ~TouchHudCanvas() {}
261 
SetScale(int scale)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 
scale() const271   int scale() const { return scale_; }
272 
TouchPointAdded(int touch_id)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 
Clear()283   void Clear() {
284     for (int i = 0; i < kMaxPaths; ++i)
285       paths_[i].reset();
286 
287     SchedulePaint();
288   }
289 
290  private:
StartedTrace(int trace_index)291   void StartedTrace(int trace_index) {
292     paths_[trace_index].reset();
293     colors_[trace_index] = SkColorSetA(kColors[trace_index], kAlpha);
294   }
295 
AddedPointToTrace(int trace_index)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.
OnPaint(gfx::Canvas * canvas)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 
TouchHudDebug(aura::Window * initial_root)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]->SetShadows(gfx::ShadowValues(
356         1, 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 
~TouchHudDebug()366 TouchHudDebug::~TouchHudDebug() {
367 }
368 
369 // static
GetAllAsDictionary()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 
ChangeToNextMode()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 
GetLogAsList() const400 scoped_ptr<base::ListValue> TouchHudDebug::GetLogAsList() const {
401   return touch_log_->GetAsList();
402 }
403 
Clear()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 
SetMode(Mode mode)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 
UpdateTouchPointLabel(int index)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 
OnTouchEvent(ui::TouchEvent * event)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 
OnDisplayMetricsChanged(const gfx::Display & display,uint32_t metrics)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 
SetHudForRootWindowController(RootWindowController * controller)478 void TouchHudDebug::SetHudForRootWindowController(
479     RootWindowController* controller) {
480   controller->set_touch_hud_debug(this);
481 }
482 
UnsetHudForRootWindowController(RootWindowController * controller)483 void TouchHudDebug::UnsetHudForRootWindowController(
484     RootWindowController* controller) {
485   controller->set_touch_hud_debug(NULL);
486 }
487 
488 }  // namespace ash
489