1 // Copyright (c) 2012 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_observer_hud.h"
7 #include "ash/display/display_controller.h"
8 #include "ash/display/display_manager.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/property_util.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/stringprintf.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/utf_string_conversions.h"
16 #include "third_party/skia/include/core/SkPath.h"
17 #include "third_party/skia/include/core/SkXfermode.h"
18 #include "ui/aura/root_window.h"
19 #include "ui/aura/window.h"
20 #include "ui/base/events/event.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/display.h"
23 #include "ui/gfx/rect.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/gfx/size.h"
26 #include "ui/gfx/transform.h"
27 #include "ui/views/background.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/widget/widget.h"
33 #include <X11/extensions/XInput2.h>
36 #include "ui/base/x/valuators.h"
42 const int kPointRadius
= 20;
43 const int kColors
[] = {
44 static_cast<int>(SK_ColorYELLOW
),
45 static_cast<int>(SK_ColorGREEN
),
46 static_cast<int>(SK_ColorRED
),
47 static_cast<int>(SK_ColorBLUE
),
48 static_cast<int>(SK_ColorGRAY
),
49 static_cast<int>(SK_ColorMAGENTA
),
50 static_cast<int>(SK_ColorCYAN
),
51 static_cast<int>(SK_ColorWHITE
),
52 static_cast<int>(SK_ColorBLACK
)
54 const int kAlpha
= 0x60;
55 const int kMaxPaths
= arraysize(kColors
);
56 const int kReducedScale
= 10;
58 const char* GetTouchEventLabel(ui::EventType type
) {
62 case ui::ET_TOUCH_PRESSED
:
64 case ui::ET_TOUCH_MOVED
:
66 case ui::ET_TOUCH_RELEASED
:
68 case ui::ET_TOUCH_CANCELLED
:
76 int GetTrackingId(const ui::TouchEvent
& event
) {
77 if (!event
.HasNativeEvent())
79 #if defined(USE_XI2_MT)
80 ui::ValuatorTracker
* valuators
= ui::ValuatorTracker::GetInstance();
82 if (valuators
->ExtractValuator(*event
.native_event(),
83 ui::ValuatorTracker::VAL_TRACKING_ID
,
85 return static_cast<int>(tracking_id
);
91 int GetSourceDeviceId(const ui::TouchEvent
& event
) {
92 if (!event
.HasNativeEvent())
95 XEvent
* xev
= event
.native_event();
96 return static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
)->sourceid
;
101 // A TouchPointLog represents a single touch-event of a touch point.
102 struct TouchPointLog
{
104 explicit TouchPointLog(const ui::TouchEvent
& touch
)
105 : id(touch
.touch_id()),
107 location(touch
.root_location()),
108 timestamp(touch
.time_stamp().InMillisecondsF()),
109 radius_x(touch
.radius_x()),
110 radius_y(touch
.radius_y()),
111 pressure(touch
.force()),
112 tracking_id(GetTrackingId(touch
)),
113 source_device(GetSourceDeviceId(touch
)) {
116 // Populates a dictionary value with all the information about the touch
118 scoped_ptr
<DictionaryValue
> GetAsDictionary() const {
119 scoped_ptr
<DictionaryValue
> value(new DictionaryValue());
121 value
->SetInteger("id", id
);
122 value
->SetString("type", std::string(GetTouchEventLabel(type
)));
123 value
->SetString("location", location
.ToString());
124 value
->SetDouble("timestamp", timestamp
);
125 value
->SetDouble("radius_x", radius_x
);
126 value
->SetDouble("radius_y", radius_y
);
127 value
->SetDouble("pressure", pressure
);
128 value
->SetInteger("tracking_id", tracking_id
);
129 value
->SetInteger("source_device", source_device
);
145 // A TouchTrace keeps track of all the touch events of a single touch point
146 // (starting from a touch-press and ending at touch-release).
152 void AddTouchPoint(const ui::TouchEvent
& touch
) {
153 log_
.push_back(TouchPointLog(touch
));
156 bool empty() const { return log_
.empty(); }
158 // Returns a list containing data from all events for the touch point.
159 scoped_ptr
<ListValue
> GetAsList() const {
160 scoped_ptr
<ListValue
> list(new ListValue());
161 for (std::vector
<TouchPointLog
>::const_iterator i
= log_
.begin();
162 i
!= log_
.end(); ++i
) {
163 list
->Append((*i
).GetAsDictionary().release());
174 std::vector
<TouchPointLog
> log_
;
176 DISALLOW_COPY_AND_ASSIGN(TouchTrace
);
179 class TouchHudCanvas
: public views::View
{
181 explicit TouchHudCanvas(TouchObserverHUD
* owner
)
186 SetPaintToLayer(true);
187 SetFillsBoundsOpaquely(false);
190 virtual ~TouchHudCanvas() {}
192 void SetScale(int scale
) {
196 gfx::Transform transform
;
197 transform
.Scale(1. / scale_
, 1. / scale_
);
198 layer()->SetTransform(transform
);
201 int scale() const { return scale_
; }
203 void Start(const ui::TouchEvent
& touch
) {
204 int id
= touch
.touch_id();
205 paths_
[path_index_
].reset();
206 traces_
[path_index_
].Reset();
207 colors_
[path_index_
] = SkColorSetA(kColors
[color_index_
], kAlpha
);
208 color_index_
= (color_index_
+ 1) % arraysize(kColors
);
209 touch_id_to_path_
[id
] = path_index_
;
210 path_index_
= (path_index_
+ 1) % kMaxPaths
;
215 void AddPoint(const ui::TouchEvent
& touch
) {
216 int id
= touch
.touch_id();
217 const gfx::Point
& point
= touch
.root_location();
218 int path_id
= touch_id_to_path_
[id
];
219 SkScalar x
= SkIntToScalar(point
.x());
220 SkScalar y
= SkIntToScalar(point
.y());
222 if (!paths_
[path_id
].getLastPt(&last
) || x
!= last
.x() || y
!= last
.y()) {
223 paths_
[path_id
].addCircle(x
, y
, SkIntToScalar(kPointRadius
));
224 traces_
[path_id
].AddTouchPoint(touch
);
232 for (size_t i
= 0; i
< arraysize(paths_
); ++i
) {
240 scoped_ptr
<ListValue
> GetAsList() const {
241 scoped_ptr
<ListValue
> list(new ListValue());
242 for (size_t i
= 0; i
< arraysize(traces_
); ++i
) {
243 if (!traces_
[i
].empty())
244 list
->Append(traces_
[i
].GetAsList().release());
250 // Overridden from views::View.
251 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
{
252 canvas
->DrawColor(SK_ColorBLACK
, SkXfermode::kClear_Mode
);
253 canvas
->DrawColor(SkColorSetARGB(0, 0, 0, 0));
256 paint
.setStyle(SkPaint::kFill_Style
);
257 for (size_t i
= 0; i
< arraysize(paths_
); ++i
) {
258 if (paths_
[i
].countPoints() == 0)
260 paint
.setColor(colors_
[i
]);
261 canvas
->DrawPath(paths_
[i
], paint
);
265 TouchObserverHUD
* owner_
;
266 SkPath paths_
[kMaxPaths
];
267 SkColor colors_
[kMaxPaths
];
268 TouchTrace traces_
[kMaxPaths
];
274 std::map
<int, int> touch_id_to_path_
;
276 DISALLOW_COPY_AND_ASSIGN(TouchHudCanvas
);
279 TouchObserverHUD::TouchObserverHUD(aura::RootWindow
* initial_root
)
280 : display_id_(initial_root
->GetProperty(kDisplayIdKey
)),
281 root_window_(initial_root
) {
282 const gfx::Display
& display
=
283 Shell::GetInstance()->display_manager()->GetDisplayForId(display_id_
);
285 views::View
* content
= new views::View
;
287 canvas_
= new TouchHudCanvas(this);
288 content
->AddChildView(canvas_
);
290 const gfx::Size
& display_size
= display
.size();
291 canvas_
->SetSize(display_size
);
292 content
->SetSize(display_size
);
294 label_container_
= new views::View
;
295 label_container_
->SetLayoutManager(new views::BoxLayout(
296 views::BoxLayout::kVertical
, 0, 0, 0));
298 for (int i
= 0; i
< kMaxTouchPoints
; ++i
) {
299 touch_status_
[i
] = ui::ET_UNKNOWN
;
300 touch_labels_
[i
] = new views::Label
;
301 touch_labels_
[i
]->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
302 touch_labels_
[i
]->SetShadowColors(SK_ColorWHITE
,
304 touch_labels_
[i
]->SetShadowOffset(1, 1);
305 label_container_
->AddChildView(touch_labels_
[i
]);
307 label_container_
->SetX(0);
308 label_container_
->SetY(display_size
.height() / kReducedScale
);
309 label_container_
->SetSize(label_container_
->GetPreferredSize());
310 label_container_
->SetVisible(false);
311 content
->AddChildView(label_container_
);
313 widget_
= new views::Widget();
314 views::Widget::InitParams
315 params(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
316 params
.transparent
= true;
317 params
.can_activate
= false;
318 params
.accept_events
= false;
319 params
.bounds
= gfx::Rect(display_size
);
320 params
.parent
= Shell::GetContainer(
322 internal::kShellWindowId_OverlayContainer
);
323 widget_
->Init(params
);
324 widget_
->SetContentsView(content
);
325 widget_
->StackAtTop();
328 widget_
->AddObserver(this);
330 // Observe changes in display size and mode to update touch HUD.
331 Shell::GetScreen()->AddObserver(this);
332 #if defined(OS_CHROMEOS)
333 Shell::GetInstance()->output_configurator()->AddObserver(this);
334 #endif // defined(OS_CHROMEOS)
336 Shell::GetInstance()->display_controller()->AddObserver(this);
337 root_window_
->AddPreTargetHandler(this);
340 TouchObserverHUD::~TouchObserverHUD() {
341 Shell::GetInstance()->display_controller()->RemoveObserver(this);
343 #if defined(OS_CHROMEOS)
344 Shell::GetInstance()->output_configurator()->RemoveObserver(this);
345 #endif // defined(OS_CHROMEOS)
346 Shell::GetScreen()->RemoveObserver(this);
348 widget_
->RemoveObserver(this);
352 scoped_ptr
<DictionaryValue
> TouchObserverHUD::GetAllAsDictionary() {
353 scoped_ptr
<DictionaryValue
> value(new DictionaryValue());
354 Shell::RootWindowList roots
= Shell::GetInstance()->GetAllRootWindows();
355 for (Shell::RootWindowList::iterator iter
= roots
.begin();
356 iter
!= roots
.end(); ++iter
) {
357 internal::RootWindowController
* controller
= GetRootWindowController(*iter
);
358 if (controller
->touch_observer_hud()) {
359 int64 display_id
= (*iter
)->GetProperty(kDisplayIdKey
);
360 scoped_ptr
<ListValue
> list
=
361 controller
->touch_observer_hud()->GetLogAsList();
363 value
->Set(base::Int64ToString(display_id
), list
.release());
369 void TouchObserverHUD::ChangeToNextMode() {
370 if (widget_
->IsVisible()) {
371 if (canvas_
->scale() == kReducedScale
) {
374 label_container_
->SetVisible(true);
375 canvas_
->SetScale(kReducedScale
);
378 canvas_
->SetScale(1);
379 label_container_
->SetVisible(false);
384 void TouchObserverHUD::Clear() {
385 if (widget_
->IsVisible())
389 scoped_ptr
<ListValue
> TouchObserverHUD::GetLogAsList() const {
390 return canvas_
->GetAsList();
393 void TouchObserverHUD::UpdateTouchPointLabel(int index
) {
394 std::string string
= base::StringPrintf("%2d: %s %s (%.4f)",
395 index
, GetTouchEventLabel(touch_status_
[index
]),
396 touch_positions_
[index
].ToString().c_str(),
397 touch_radius_
[index
]);
398 touch_labels_
[index
]->SetText(UTF8ToUTF16(string
));
401 void TouchObserverHUD::OnTouchEvent(ui::TouchEvent
* event
) {
402 if (event
->touch_id() >= kMaxTouchPoints
)
405 if (event
->type() != ui::ET_TOUCH_CANCELLED
)
406 touch_positions_
[event
->touch_id()] = event
->root_location();
408 touch_radius_
[event
->touch_id()] = std::max(event
->radius_x(),
411 if (event
->type() == ui::ET_TOUCH_PRESSED
)
412 canvas_
->Start(*event
);
413 else if (event
->type() != ui::ET_TOUCH_CANCELLED
)
414 canvas_
->AddPoint(*event
);
415 touch_status_
[event
->touch_id()] = event
->type();
417 UpdateTouchPointLabel(event
->touch_id());
418 label_container_
->SetSize(label_container_
->GetPreferredSize());
421 void TouchObserverHUD::OnWidgetDestroying(views::Widget
* widget
) {
422 DCHECK_EQ(widget
, widget_
);
426 void TouchObserverHUD::OnDisplayBoundsChanged(const gfx::Display
& display
) {
427 if (display
.id() != display_id_
)
429 const gfx::Size
& size
= display
.size();
430 widget_
->SetSize(size
);
431 canvas_
->SetSize(size
);
432 label_container_
->SetY(size
.height() / kReducedScale
);
435 void TouchObserverHUD::OnDisplayAdded(const gfx::Display
& new_display
) {}
437 void TouchObserverHUD::OnDisplayRemoved(const gfx::Display
& old_display
) {
438 if (old_display
.id() != display_id_
)
443 #if defined(OS_CHROMEOS)
444 void TouchObserverHUD::OnDisplayModeChanged() {
445 // Clear touch HUD for any change in display mode (single, dual extended, dual
449 #endif // defined(OS_CHROMEOS)
451 void TouchObserverHUD::OnDisplayConfigurationChanging() {
455 root_window_
->RemovePreTargetHandler(this);
457 RootWindowController
* controller
= GetRootWindowController(root_window_
);
458 controller
->set_touch_observer_hud(NULL
);
460 views::Widget::ReparentNativeView(
461 widget_
->GetNativeView(),
462 Shell::GetContainer(root_window_
,
463 internal::kShellWindowId_UnparentedControlContainer
));
468 void TouchObserverHUD::OnDisplayConfigurationChanged() {
472 root_window_
= Shell::GetInstance()->display_controller()->
473 GetRootWindowForDisplayId(display_id_
);
475 views::Widget::ReparentNativeView(
476 widget_
->GetNativeView(),
477 Shell::GetContainer(root_window_
,
478 internal::kShellWindowId_OverlayContainer
));
480 RootWindowController
* controller
= GetRootWindowController(root_window_
);
481 controller
->set_touch_observer_hud(this);
483 root_window_
->AddPreTargetHandler(this);
486 } // namespace internal