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.
5 #include "ash/touch/touch_transformer_controller.h"
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"
22 DisplayManager
* GetDisplayManager() {
23 return Shell::GetInstance()->display_manager();
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)
43 if (!ui::DeviceDataManagerX11::GetInstance()->GetDataRange(
44 touch_display
.touch_device_id(),
45 ui::DeviceDataManagerX11::DT_TOUCH_POSITION_X
,
47 !ui::DeviceDataManagerX11::GetInstance()->GetDataRange(
48 touch_display
.touch_device_id(),
49 ui::DeviceDataManagerX11::DT_TOUCH_POSITION_Y
,
54 double width
= touch_display
.bounds_in_native().width();
55 double height
= touch_display
.bounds_in_native().height();
57 if (max_x
== 0.0 || max_y
== 0.0 || width
== 0.0 || height
== 0.0)
60 // [0, max_x] -> touchscreen width = max_x + 1
61 // [0, max_y] -> touchscreen height = max_y + 1
65 double ratio
= std::sqrt((width
* height
) / (max_x
* max_y
));
67 VLOG(2) << "Screen width/height: " << width
<< "/" << height
68 << ", Touchscreen width/height: " << max_x
<< "/" << max_y
69 << ", Touch radius scale ratio: " << ratio
;
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.
77 TouchTransformerController::GetExtendedModeTouchTransformer(
78 const DisplayInfo
& touch_display
, const gfx::Size
& fb_size
) const {
80 if (touch_display
.touch_device_id() == 0 ||
81 fb_size
.width() == 0.0 ||
82 fb_size
.height() == 0.0)
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());
90 bool TouchTransformerController::ShouldComputeMirrorModeTouchTransformer(
91 const DisplayInfo
& touch_display
) const {
92 if (force_compute_mirror_mode_touch_transformer_
)
95 if (touch_display
.touch_device_id() == 0)
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
;
110 if (!info
|| info
->size_in_pixel() == info
->GetNativeModeSize() ||
111 !info
->is_aspect_preserving_scaling()) {
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 {
127 if (!ShouldComputeMirrorModeTouchTransformer(touch_display
))
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;
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();
144 if (native_height
== 0.0 || mirror_height
== 0.0 ||
145 native_width
== 0.0 || mirror_width
== 0.0)
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
);
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
);
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);
167 return ctm
; // Same aspect ratio - return identity
170 TouchTransformerController::TouchTransformerController() :
171 force_compute_mirror_mode_touch_transformer_ (false) {
172 Shell::GetInstance()->display_controller()->AddObserver(this);
175 TouchTransformerController::~TouchTransformerController() {
176 Shell::GetInstance()->display_controller()->RemoveObserver(this);
179 void TouchTransformerController::UpdateTouchTransformer() const {
180 ui::DeviceDataManager
* device_manager
= ui::DeviceDataManager::GetInstance();
181 device_manager
->ClearTouchTransformerRecord();
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
;
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
) {
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
));
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
));
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(
229 display1
.touch_device_id(),
230 GetMirrorModeTouchTransformer(display1
));
231 device_manager
->UpdateTouchInfoForDisplay(
233 display2
.touch_device_id(),
234 GetMirrorModeTouchTransformer(display2
));
238 if (display_state
== ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED
) {
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
?
250 // Mapping from framebuffer size to the source display's native
252 device_manager
->UpdateTouchInfoForDisplay(
254 display1
.touch_device_id(),
255 GetExtendedModeTouchTransformer(source_display
, fb_size
));
256 device_manager
->UpdateTouchInfoForDisplay(
258 display2
.touch_device_id(),
259 GetExtendedModeTouchTransformer(source_display
, fb_size
));
261 // In actual extended mode, each display is associated with one root
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(
274 display1
.touch_device_id(),
275 GetExtendedModeTouchTransformer(display1
, fb_size
));
276 device_manager
->UpdateTouchInfoForDisplay(
278 display2
.touch_device_id(),
279 GetExtendedModeTouchTransformer(display2
, fb_size
));
284 // Single display mode. The root window has one associated display id.
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(),
294 void TouchTransformerController::OnDisplaysInitialized() {
295 UpdateTouchTransformer();
298 void TouchTransformerController::OnDisplayConfigurationChanged() {
299 UpdateTouchTransformer();