1 // Copyright 2014 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_transformer_controller.h" 6 7 #include "ash/display/display_controller.h" 8 #include "ash/display/display_manager.h" 9 #include "ash/host/ash_window_tree_host.h" 10 #include "ash/root_window_controller.h" 11 #include "ash/shell.h" 12 #include "ui/aura/window_tree_host.h" 13 #include "ui/display/chromeos/display_configurator.h" 14 #include "ui/display/types/display_snapshot.h" 15 #include "ui/events/device_data_manager.h" 16 #include "ui/events/x/device_data_manager_x11.h" 17 18 namespace ash { 19 20 namespace { 21 22 DisplayManager* GetDisplayManager() { 23 return Shell::GetInstance()->display_manager(); 24 } 25 26 } // namespace 27 28 // This is to compute the scale ratio for the TouchEvent's radius. The 29 // configured resolution of the display is not always the same as the touch 30 // screen's reporting resolution, e.g. the display could be set as 31 // 1920x1080 while the touchscreen is reporting touch position range at 32 // 32767x32767. Touch radius is reported in the units the same as touch position 33 // so we need to scale the touch radius to be compatible with the display's 34 // resolution. We compute the scale as 35 // sqrt of (display_area / touchscreen_area) 36 double TouchTransformerController::GetTouchResolutionScale( 37 const DisplayInfo& touch_display) const { 38 if (touch_display.touch_device_id() == 0) 39 return 1.0; 40 41 double min_x, max_x; 42 double min_y, max_y; 43 if (!ui::DeviceDataManagerX11::GetInstance()->GetDataRange( 44 touch_display.touch_device_id(), 45 ui::DeviceDataManagerX11::DT_TOUCH_POSITION_X, 46 &min_x, &max_x) || 47 !ui::DeviceDataManagerX11::GetInstance()->GetDataRange( 48 touch_display.touch_device_id(), 49 ui::DeviceDataManagerX11::DT_TOUCH_POSITION_Y, 50 &min_y, &max_y)) { 51 return 1.0; 52 } 53 54 double width = touch_display.bounds_in_native().width(); 55 double height = touch_display.bounds_in_native().height(); 56 57 if (max_x == 0.0 || max_y == 0.0 || width == 0.0 || height == 0.0) 58 return 1.0; 59 60 // [0, max_x] -> touchscreen width = max_x + 1 61 // [0, max_y] -> touchscreen height = max_y + 1 62 max_x += 1.0; 63 max_y += 1.0; 64 65 double ratio = std::sqrt((width * height) / (max_x * max_y)); 66 67 VLOG(2) << "Screen width/height: " << width << "/" << height 68 << ", Touchscreen width/height: " << max_x << "/" << max_y 69 << ", Touch radius scale ratio: " << ratio; 70 return ratio; 71 } 72 73 // This function computes the extended mode TouchTransformer for 74 // |touch_display|. The TouchTransformer maps the touch event position 75 // from framebuffer size to the display size. 76 gfx::Transform 77 TouchTransformerController::GetExtendedModeTouchTransformer( 78 const DisplayInfo& touch_display, const gfx::Size& fb_size) const { 79 gfx::Transform ctm; 80 if (touch_display.touch_device_id() == 0 || 81 fb_size.width() == 0.0 || 82 fb_size.height() == 0.0) 83 return ctm; 84 float width = touch_display.bounds_in_native().width(); 85 float height = touch_display.bounds_in_native().height(); 86 ctm.Scale(width / fb_size.width(), height / fb_size.height()); 87 return ctm; 88 } 89 90 bool TouchTransformerController::ShouldComputeMirrorModeTouchTransformer( 91 const DisplayInfo& touch_display) const { 92 if (force_compute_mirror_mode_touch_transformer_) 93 return true; 94 95 if (touch_display.touch_device_id() == 0) 96 return false; 97 98 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); 99 const std::vector<gfx::Display>& displays = display_manager->displays(); 100 const DisplayInfo* info = NULL; 101 for (size_t i = 0; i < displays.size(); i++) { 102 const DisplayInfo& current_info = 103 display_manager->GetDisplayInfo(displays[i].id()); 104 if (current_info.touch_device_id() == touch_display.touch_device_id()) { 105 info = ¤t_info; 106 break; 107 } 108 } 109 110 if (!info || info->size_in_pixel() == info->GetNativeModeSize() || 111 !info->is_aspect_preserving_scaling()) { 112 return false; 113 } 114 return true; 115 } 116 117 // This function computes the mirror mode TouchTransformer for |touch_display|. 118 // When internal monitor is applied a resolution that does not have 119 // the same aspect ratio as its native resolution, there would be 120 // blank regions in the letterboxing/pillarboxing mode. 121 // The TouchTransformer will make sure the touch events on the blank region 122 // have negative coordinates and touch events within the chrome region 123 // have the correct positive coordinates. 124 gfx::Transform TouchTransformerController::GetMirrorModeTouchTransformer( 125 const DisplayInfo& touch_display) const { 126 gfx::Transform ctm; 127 if (!ShouldComputeMirrorModeTouchTransformer(touch_display)) 128 return ctm; 129 130 float mirror_width = touch_display.bounds_in_native().width(); 131 float mirror_height = touch_display.bounds_in_native().height(); 132 float native_width = 0; 133 float native_height = 0; 134 135 std::vector<DisplayMode> modes = touch_display.display_modes(); 136 for (size_t i = 0; i < modes.size(); i++) { 137 if (modes[i].native) { 138 native_width = modes[i].size.width(); 139 native_height = modes[i].size.height(); 140 break; 141 } 142 } 143 144 if (native_height == 0.0 || mirror_height == 0.0 || 145 native_width == 0.0 || mirror_width == 0.0) 146 return ctm; 147 148 float native_ar = static_cast<float>(native_width) / 149 static_cast<float>(native_height); 150 float mirror_ar = static_cast<float>(mirror_width) / 151 static_cast<float>(mirror_height); 152 153 if (mirror_ar > native_ar) { // Letterboxing 154 // Translate before scale. 155 ctm.Translate(0.0, (1.0 - mirror_ar / native_ar) * 0.5 * mirror_height); 156 ctm.Scale(1.0, mirror_ar / native_ar); 157 return ctm; 158 } 159 160 if (native_ar > mirror_ar) { // Pillarboxing 161 // Translate before scale. 162 ctm.Translate((1.0 - native_ar / mirror_ar) * 0.5 * mirror_width, 0.0); 163 ctm.Scale(native_ar / mirror_ar, 1.0); 164 return ctm; 165 } 166 167 return ctm; // Same aspect ratio - return identity 168 } 169 170 TouchTransformerController::TouchTransformerController() : 171 force_compute_mirror_mode_touch_transformer_ (false) { 172 Shell::GetInstance()->display_controller()->AddObserver(this); 173 } 174 175 TouchTransformerController::~TouchTransformerController() { 176 Shell::GetInstance()->display_controller()->RemoveObserver(this); 177 } 178 179 void TouchTransformerController::UpdateTouchTransformer() const { 180 ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); 181 device_manager->ClearTouchTransformerRecord(); 182 183 // Display IDs and DisplayInfo for mirror or extended mode. 184 int64 display1_id = gfx::Display::kInvalidDisplayID; 185 int64 display2_id = gfx::Display::kInvalidDisplayID; 186 DisplayInfo display1; 187 DisplayInfo display2; 188 // Display ID and DisplayInfo for single display mode. 189 int64 single_display_id = gfx::Display::kInvalidDisplayID; 190 DisplayInfo single_display; 191 192 DisplayController* display_controller = 193 Shell::GetInstance()->display_controller(); 194 ui::MultipleDisplayState display_state = 195 Shell::GetInstance()->display_configurator()->display_state(); 196 if (display_state == ui::MULTIPLE_DISPLAY_STATE_INVALID || 197 display_state == ui::MULTIPLE_DISPLAY_STATE_HEADLESS) { 198 return; 199 } else if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR || 200 display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) { 201 DisplayIdPair id_pair = GetDisplayManager()->GetCurrentDisplayIdPair(); 202 display1_id = id_pair.first; 203 display2_id = id_pair.second; 204 DCHECK(display1_id != gfx::Display::kInvalidDisplayID && 205 display2_id != gfx::Display::kInvalidDisplayID); 206 display1 = GetDisplayManager()->GetDisplayInfo(display1_id); 207 display2 = GetDisplayManager()->GetDisplayInfo(display2_id); 208 device_manager->UpdateTouchRadiusScale(display1.touch_device_id(), 209 GetTouchResolutionScale(display1)); 210 device_manager->UpdateTouchRadiusScale(display2.touch_device_id(), 211 GetTouchResolutionScale(display2)); 212 } else { 213 single_display_id = GetDisplayManager()->first_display_id(); 214 DCHECK(single_display_id != gfx::Display::kInvalidDisplayID); 215 single_display = GetDisplayManager()->GetDisplayInfo(single_display_id); 216 device_manager->UpdateTouchRadiusScale( 217 single_display.touch_device_id(), 218 GetTouchResolutionScale(single_display)); 219 } 220 221 if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) { 222 // In mirror mode, both displays share the same root window so 223 // both display ids are associated with the root window. 224 aura::Window* root = display_controller->GetPrimaryRootWindow(); 225 RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID( 226 display1_id, display2_id); 227 device_manager->UpdateTouchInfoForDisplay( 228 display1_id, 229 display1.touch_device_id(), 230 GetMirrorModeTouchTransformer(display1)); 231 device_manager->UpdateTouchInfoForDisplay( 232 display2_id, 233 display2.touch_device_id(), 234 GetMirrorModeTouchTransformer(display2)); 235 return; 236 } 237 238 if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) { 239 gfx::Size fb_size = 240 Shell::GetInstance()->display_configurator()->framebuffer_size(); 241 // In extended but software mirroring mode, ther is only one X root window 242 // that associates with both displays. 243 if (GetDisplayManager()->software_mirroring_enabled()) { 244 aura::Window* root = display_controller->GetPrimaryRootWindow(); 245 RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID( 246 display1_id, display2_id); 247 DisplayInfo source_display = 248 gfx::Display::InternalDisplayId() == display1_id ? 249 display1 : display2; 250 // Mapping from framebuffer size to the source display's native 251 // resolution. 252 device_manager->UpdateTouchInfoForDisplay( 253 display1_id, 254 display1.touch_device_id(), 255 GetExtendedModeTouchTransformer(source_display, fb_size)); 256 device_manager->UpdateTouchInfoForDisplay( 257 display2_id, 258 display2.touch_device_id(), 259 GetExtendedModeTouchTransformer(source_display, fb_size)); 260 } else { 261 // In actual extended mode, each display is associated with one root 262 // window. 263 aura::Window* root1 = 264 display_controller->GetRootWindowForDisplayId(display1_id); 265 aura::Window* root2 = 266 display_controller->GetRootWindowForDisplayId(display2_id); 267 RootWindowController::ForWindow(root1)->ash_host()->UpdateDisplayID( 268 display1_id, gfx::Display::kInvalidDisplayID); 269 RootWindowController::ForWindow(root2)->ash_host()->UpdateDisplayID( 270 display2_id, gfx::Display::kInvalidDisplayID); 271 // Mapping from framebuffer size to each display's native resolution. 272 device_manager->UpdateTouchInfoForDisplay( 273 display1_id, 274 display1.touch_device_id(), 275 GetExtendedModeTouchTransformer(display1, fb_size)); 276 device_manager->UpdateTouchInfoForDisplay( 277 display2_id, 278 display2.touch_device_id(), 279 GetExtendedModeTouchTransformer(display2, fb_size)); 280 } 281 return; 282 } 283 284 // Single display mode. The root window has one associated display id. 285 aura::Window* root = 286 display_controller->GetRootWindowForDisplayId(single_display.id()); 287 RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID( 288 single_display.id(), gfx::Display::kInvalidDisplayID); 289 device_manager->UpdateTouchInfoForDisplay(single_display_id, 290 single_display.touch_device_id(), 291 gfx::Transform()); 292 } 293 294 void TouchTransformerController::OnDisplaysInitialized() { 295 UpdateTouchTransformer(); 296 } 297 298 void TouchTransformerController::OnDisplayConfigurationChanged() { 299 UpdateTouchTransformer(); 300 } 301 302 } // namespace ash 303