• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetDisplayManager()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)
GetTouchResolutionScale(const DisplayInfo & touch_display) const36 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
GetExtendedModeTouchTransformer(const DisplayInfo & touch_display,const gfx::Size & fb_size) const77 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 
ShouldComputeMirrorModeTouchTransformer(const DisplayInfo & touch_display) const90 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 = &current_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.
GetMirrorModeTouchTransformer(const DisplayInfo & touch_display) const124 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 
TouchTransformerController()170 TouchTransformerController::TouchTransformerController() :
171     force_compute_mirror_mode_touch_transformer_ (false) {
172   Shell::GetInstance()->display_controller()->AddObserver(this);
173 }
174 
~TouchTransformerController()175 TouchTransformerController::~TouchTransformerController() {
176   Shell::GetInstance()->display_controller()->RemoveObserver(this);
177 }
178 
UpdateTouchTransformer() const179 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 
OnDisplaysInitialized()294 void TouchTransformerController::OnDisplaysInitialized() {
295   UpdateTouchTransformer();
296 }
297 
OnDisplayConfigurationChanged()298 void TouchTransformerController::OnDisplayConfigurationChanged() {
299   UpdateTouchTransformer();
300 }
301 
302 }  // namespace ash
303