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