// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/touch/touch_transformer_controller.h" #include "ash/display/display_controller.h" #include "ash/display/display_manager.h" #include "ash/host/ash_window_tree_host.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ui/aura/window_tree_host.h" #include "ui/display/chromeos/display_configurator.h" #include "ui/display/types/display_snapshot.h" #include "ui/events/devices/device_data_manager.h" namespace ash { namespace { DisplayManager* GetDisplayManager() { return Shell::GetInstance()->display_manager(); } ui::TouchscreenDevice FindTouchscreenById(unsigned int id) { const std::vector& touchscreens = ui::DeviceDataManager::GetInstance()->touchscreen_devices(); for (const auto& touchscreen : touchscreens) { if (touchscreen.id == id) return touchscreen; } return ui::TouchscreenDevice(); } } // namespace // This is to compute the scale ratio for the TouchEvent's radius. The // configured resolution of the display is not always the same as the touch // screen's reporting resolution, e.g. the display could be set as // 1920x1080 while the touchscreen is reporting touch position range at // 32767x32767. Touch radius is reported in the units the same as touch position // so we need to scale the touch radius to be compatible with the display's // resolution. We compute the scale as // sqrt of (display_area / touchscreen_area) double TouchTransformerController::GetTouchResolutionScale( const DisplayInfo& touch_display, const ui::TouchscreenDevice& touch_device) const { if (touch_device.id == ui::InputDevice::kInvalidId || touch_device.size.IsEmpty() || touch_display.bounds_in_native().size().IsEmpty()) return 1.0; double display_area = touch_display.bounds_in_native().size().GetArea(); double touch_area = touch_device.size.GetArea(); double ratio = std::sqrt(display_area / touch_area); VLOG(2) << "Display size: " << touch_display.bounds_in_native().size().ToString() << ", Touchscreen size: " << touch_device.size.ToString() << ", Touch radius scale ratio: " << ratio; return ratio; } gfx::Transform TouchTransformerController::GetTouchTransform( const DisplayInfo& display, const ui::TouchscreenDevice& touchscreen, const gfx::Size& framebuffer_size) const { gfx::SizeF current_size = display.bounds_in_native().size(); gfx::SizeF native_size = display.GetNativeModeSize(); #if defined(USE_OZONE) gfx::SizeF touch_area = touchscreen.size; #elif defined(USE_X11) // On X11 touches are reported in the framebuffer coordinate space. gfx::SizeF touch_area = framebuffer_size; #endif gfx::Transform ctm; if (current_size.IsEmpty() || native_size.IsEmpty() || touch_area.IsEmpty() || touchscreen.id == ui::InputDevice::kInvalidId) return ctm; #if defined(USE_OZONE) // Translate the touch so that it falls within the display bounds. ctm.Translate(display.bounds_in_native().x(), display.bounds_in_native().y()); #endif // Take care of panel fitting only if supported. // If panel fitting is enabled then the aspect ratio is preserved and the // display is scaled acordingly. In this case blank regions would be present // in order to center the displayed area. if (display.is_aspect_preserving_scaling()) { float native_ar = native_size.width() / native_size.height(); float current_ar = current_size.width() / current_size.height(); if (current_ar > native_ar) { // Letterboxing ctm.Translate( 0, (1 - current_ar / native_ar) * 0.5 * current_size.height()); ctm.Scale(1, current_ar / native_ar); } else if (native_ar > current_ar) { // Pillarboxing ctm.Translate( (1 - native_ar / current_ar) * 0.5 * current_size.width(), 0); ctm.Scale(native_ar / current_ar, 1); } } // Take care of scaling between touchscreen area and display resolution. ctm.Scale(current_size.width() / touch_area.width(), current_size.height() / touch_area.height()); return ctm; } TouchTransformerController::TouchTransformerController() { Shell::GetInstance()->display_controller()->AddObserver(this); } TouchTransformerController::~TouchTransformerController() { Shell::GetInstance()->display_controller()->RemoveObserver(this); } void TouchTransformerController::UpdateTouchTransformer() const { ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); device_manager->ClearTouchTransformerRecord(); // Display IDs and DisplayInfo for mirror or extended mode. int64 display1_id = gfx::Display::kInvalidDisplayID; int64 display2_id = gfx::Display::kInvalidDisplayID; DisplayInfo display1; DisplayInfo display2; // Display ID and DisplayInfo for single display mode. int64 single_display_id = gfx::Display::kInvalidDisplayID; DisplayInfo single_display; DisplayController* display_controller = Shell::GetInstance()->display_controller(); ui::MultipleDisplayState display_state = Shell::GetInstance()->display_configurator()->display_state(); if (display_state == ui::MULTIPLE_DISPLAY_STATE_INVALID || display_state == ui::MULTIPLE_DISPLAY_STATE_HEADLESS) { return; } else if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR || display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) { DisplayIdPair id_pair = GetDisplayManager()->GetCurrentDisplayIdPair(); display1_id = id_pair.first; display2_id = id_pair.second; DCHECK(display1_id != gfx::Display::kInvalidDisplayID && display2_id != gfx::Display::kInvalidDisplayID); display1 = GetDisplayManager()->GetDisplayInfo(display1_id); display2 = GetDisplayManager()->GetDisplayInfo(display2_id); device_manager->UpdateTouchRadiusScale( display1.touch_device_id(), GetTouchResolutionScale( display1, FindTouchscreenById(display1.touch_device_id()))); device_manager->UpdateTouchRadiusScale( display2.touch_device_id(), GetTouchResolutionScale( display2, FindTouchscreenById(display2.touch_device_id()))); } else { single_display_id = GetDisplayManager()->first_display_id(); DCHECK(single_display_id != gfx::Display::kInvalidDisplayID); single_display = GetDisplayManager()->GetDisplayInfo(single_display_id); device_manager->UpdateTouchRadiusScale( single_display.touch_device_id(), GetTouchResolutionScale( single_display, FindTouchscreenById(single_display.touch_device_id()))); } gfx::Size fb_size = Shell::GetInstance()->display_configurator()->framebuffer_size(); if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) { // In mirror mode, both displays share the same root window so // both display ids are associated with the root window. aura::Window* root = display_controller->GetPrimaryRootWindow(); RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID( display1_id, display2_id); device_manager->UpdateTouchInfoForDisplay( display1_id, display1.touch_device_id(), GetTouchTransform(display1, FindTouchscreenById(display1.touch_device_id()), fb_size)); device_manager->UpdateTouchInfoForDisplay( display2_id, display2.touch_device_id(), GetTouchTransform(display2, FindTouchscreenById(display2.touch_device_id()), fb_size)); return; } if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) { // In extended but software mirroring mode, ther is only one X root window // that associates with both displays. if (GetDisplayManager()->software_mirroring_enabled()) { aura::Window* root = display_controller->GetPrimaryRootWindow(); RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID( display1_id, display2_id); DisplayInfo source_display = gfx::Display::InternalDisplayId() == display1_id ? display1 : display2; // Mapping from framebuffer size to the source display's native // resolution. device_manager->UpdateTouchInfoForDisplay( display1_id, display1.touch_device_id(), GetTouchTransform(source_display, FindTouchscreenById(display1.touch_device_id()), fb_size)); device_manager->UpdateTouchInfoForDisplay( display2_id, display2.touch_device_id(), GetTouchTransform(source_display, FindTouchscreenById(display2.touch_device_id()), fb_size)); } else { // In actual extended mode, each display is associated with one root // window. aura::Window* root1 = display_controller->GetRootWindowForDisplayId(display1_id); aura::Window* root2 = display_controller->GetRootWindowForDisplayId(display2_id); RootWindowController::ForWindow(root1)->ash_host()->UpdateDisplayID( display1_id, gfx::Display::kInvalidDisplayID); RootWindowController::ForWindow(root2)->ash_host()->UpdateDisplayID( display2_id, gfx::Display::kInvalidDisplayID); // Mapping from framebuffer size to each display's native resolution. device_manager->UpdateTouchInfoForDisplay( display1_id, display1.touch_device_id(), GetTouchTransform(display1, FindTouchscreenById(display1.touch_device_id()), fb_size)); device_manager->UpdateTouchInfoForDisplay( display2_id, display2.touch_device_id(), GetTouchTransform(display2, FindTouchscreenById(display2.touch_device_id()), fb_size)); } return; } // Single display mode. The root window has one associated display id. aura::Window* root = display_controller->GetRootWindowForDisplayId(single_display.id()); RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID( single_display.id(), gfx::Display::kInvalidDisplayID); device_manager->UpdateTouchInfoForDisplay( single_display_id, single_display.touch_device_id(), GetTouchTransform(single_display, FindTouchscreenById(single_display.touch_device_id()), fb_size)); } void TouchTransformerController::OnDisplaysInitialized() { UpdateTouchTransformer(); } void TouchTransformerController::OnDisplayConfigurationChanged() { UpdateTouchTransformer(); } } // namespace ash