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.
5 #include "ash/touch/touch_hud_debug.h"
7 #include "ash/display/display_manager.h"
8 #include "ash/root_window_controller.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"
27 #include <X11/extensions/XInput2.h>
30 #include "ui/events/x/device_data_manager_x11.h"
35 const int kPointRadius
= 20;
36 const SkColor kColors
[] = {
46 SkColorSetRGB(0xFF, 0x8C, 0x00),
47 SkColorSetRGB(0x8B, 0x45, 0x13),
48 SkColorSetRGB(0xFF, 0xDE, 0xAD),
50 const int kAlpha
= 0x60;
51 const int kMaxPaths
= arraysize(kColors
);
52 const int kReducedScale
= 10;
54 const char* GetTouchEventLabel(ui::EventType type
) {
58 case ui::ET_TOUCH_PRESSED
:
60 case ui::ET_TOUCH_MOVED
:
62 case ui::ET_TOUCH_RELEASED
:
64 case ui::ET_TOUCH_CANCELLED
:
72 int GetTrackingId(const ui::TouchEvent
& event
) {
73 if (!event
.HasNativeEvent())
75 #if defined(USE_XI2_MT)
76 ui::DeviceDataManagerX11
* manager
= ui::DeviceDataManagerX11::GetInstance();
78 if (manager
->GetEventData(*event
.native_event(),
79 ui::DeviceDataManagerX11::DT_TOUCH_TRACKING_ID
,
81 return static_cast<int>(tracking_id
);
87 int GetSourceDeviceId(const ui::TouchEvent
& event
) {
88 if (!event
.HasNativeEvent())
91 XEvent
* xev
= event
.native_event();
92 return static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
)->sourceid
;
97 // A TouchPointLog represents a single touch-event of a touch point.
98 struct TouchPointLog
{
100 explicit TouchPointLog(const ui::TouchEvent
& touch
)
101 : id(touch
.touch_id()),
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
)) {
112 // Populates a dictionary value with all the information about the touch
114 scoped_ptr
<base::DictionaryValue
> GetAsDictionary() const {
115 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue());
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
);
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).
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
;
154 void AddTouchPoint(const ui::TouchEvent
& touch
) {
155 log_
.push_back(TouchPointLog(touch
));
158 const std::vector
<TouchPointLog
>& log() const { return log_
; }
160 bool active() const {
161 return !log_
.empty() && log_
.back().type
!= ui::ET_TOUCH_RELEASED
&&
162 log_
.back().type
!= ui::ET_TOUCH_CANCELLED
;
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());
178 std::vector
<TouchPointLog
> log_
;
180 DISALLOW_COPY_AND_ASSIGN(TouchTrace
);
183 // A TouchLog keeps track of all touch events of all touch points.
186 TouchLog() : next_trace_index_(0) {
189 void AddTouchPoint(const ui::TouchEvent
& touch
) {
190 if (touch
.type() == ui::ET_TOUCH_PRESSED
)
196 next_trace_index_
= 0;
197 for (int i
= 0; i
< kMaxPaths
; ++i
)
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());
210 int GetTraceIndex(int touch_id
) const {
211 return touch_id_to_trace_index_
.at(touch_id
);
214 const TouchTrace
* traces() const {
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_
;
224 if (!traces_
[next_trace_index_
].active())
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
;
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
);
240 TouchTrace traces_
[kMaxPaths
];
241 int next_trace_index_
;
243 std::map
<int, int> touch_id_to_trace_index_
;
245 DISALLOW_COPY_AND_ASSIGN(TouchLog
);
248 // TouchHudCanvas draws touch traces in |FULLSCREEN| and |REDUCED_SCALE| modes.
249 class TouchHudCanvas
: public views::View
{
251 explicit TouchHudCanvas(const TouchLog
& touch_log
)
252 : touch_log_(touch_log
),
254 SetPaintToLayer(true);
255 SetFillsBoundsOpaquely(false);
257 paint_
.setStyle(SkPaint::kFill_Style
);
260 virtual ~TouchHudCanvas() {}
262 void SetScale(int scale
) {
266 gfx::Transform transform
;
267 transform
.Scale(1. / scale_
, 1. / scale_
);
268 layer()->SetTransform(transform
);
271 int scale() const { return scale_
; }
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
);
284 for (int i
= 0; i
< kMaxPaths
; ++i
)
291 void StartedTrace(int trace_index
) {
292 paths_
[trace_index
].reset();
293 colors_
[trace_index
] = SkColorSetA(kColors
[trace_index
], kAlpha
);
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());
303 if (!paths_
[trace_index
].getLastPt(&last
) || x
!= last
.x() ||
305 paths_
[trace_index
].addCircle(x
, y
, SkIntToScalar(kPointRadius
));
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)
315 paint_
.setColor(colors_
[i
]);
316 canvas
->DrawPath(paths_
[i
], paint_
);
322 const TouchLog
& touch_log_
;
323 SkPath paths_
[kMaxPaths
];
324 SkColor colors_
[kMaxPaths
];
328 DISALLOW_COPY_AND_ASSIGN(TouchHudCanvas
);
331 TouchHudDebug::TouchHudDebug(aura::Window
* initial_root
)
332 : TouchObserverHUD(initial_root
),
334 touch_log_(new TouchLog()),
336 label_container_(NULL
) {
337 const gfx::Display
& display
=
338 Shell::GetInstance()->display_manager()->GetDisplayForId(display_id());
340 views::View
* content
= widget()->GetContentsView();
342 canvas_
= new TouchHudCanvas(*touch_log_
);
343 content
->AddChildView(canvas_
);
345 const gfx::Size
& display_size
= display
.size();
346 canvas_
->SetSize(display_size
);
348 label_container_
= new views::View
;
349 label_container_
->SetLayoutManager(new views::BoxLayout(
350 views::BoxLayout::kVertical
, 0, 0, 0));
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
]);
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_
);
366 TouchHudDebug::~TouchHudDebug() {
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();
378 scoped_ptr
<base::ListValue
> list
= hud
->GetLogAsList();
380 value
->Set(base::Int64ToString(hud
->display_id()), list
.release());
386 void TouchHudDebug::ChangeToNextMode() {
389 SetMode(REDUCED_SCALE
);
400 scoped_ptr
<base::ListValue
> TouchHudDebug::GetLogAsList() const {
401 return touch_log_
->GetAsList();
404 void TouchHudDebug::Clear() {
405 if (widget()->IsVisible()) {
407 for (int i
= 0; i
< kMaxTouchPoints
; ++i
)
408 touch_labels_
[i
]->SetText(base::string16());
409 label_container_
->SetSize(label_container_
->GetPreferredSize());
413 void TouchHudDebug::SetMode(Mode mode
) {
419 label_container_
->SetVisible(false);
420 canvas_
->SetVisible(true);
421 canvas_
->SetScale(1);
422 canvas_
->SchedulePaint();
426 label_container_
->SetVisible(true);
427 canvas_
->SetVisible(true);
428 canvas_
->SetScale(kReducedScale
);
429 canvas_
->SchedulePaint();
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
)
446 DCHECK(point
!= trace
.log().rend());
447 gfx::Point touch_position
= point
->location
;
449 std::string string
= base::StringPrintf("%2d: %s %s (%.4f)",
451 GetTouchEventLabel(touch_status
),
452 touch_position
.ToString().c_str(),
454 touch_labels_
[index
]->SetText(base::UTF8ToUTF16(string
));
457 void TouchHudDebug::OnTouchEvent(ui::TouchEvent
* event
) {
458 if (event
->touch_id() >= kMaxTouchPoints
)
461 touch_log_
->AddTouchPoint(*event
);
462 canvas_
->TouchPointAdded(event
->touch_id());
463 UpdateTouchPointLabel(event
->touch_id());
464 label_container_
->SetSize(label_container_
->GetPreferredSize());
467 void TouchHudDebug::OnDisplayMetricsChanged(const gfx::Display
& display
,
469 TouchObserverHUD::OnDisplayMetricsChanged(display
, metrics
);
471 if (display
.id() != display_id() || !(metrics
& DISPLAY_METRIC_BOUNDS
))
473 const gfx::Size
& size
= display
.size();
474 canvas_
->SetSize(size
);
475 label_container_
->SetY(size
.height() / kReducedScale
);
478 void TouchHudDebug::SetHudForRootWindowController(
479 RootWindowController
* controller
) {
480 controller
->set_touch_hud_debug(this);
483 void TouchHudDebug::UnsetHudForRootWindowController(
484 RootWindowController
* controller
) {
485 controller
->set_touch_hud_debug(NULL
);