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/devices/device_data_manager.h"
21 DisplayManager
* GetDisplayManager() {
22 return Shell::GetInstance()->display_manager();
25 ui::TouchscreenDevice
FindTouchscreenById(int id
) {
26 const std::vector
<ui::TouchscreenDevice
>& touchscreens
=
27 ui::DeviceDataManager::GetInstance()->touchscreen_devices();
28 for (const auto& touchscreen
: touchscreens
) {
29 if (touchscreen
.id
== id
)
33 return ui::TouchscreenDevice();
38 // This is to compute the scale ratio for the TouchEvent's radius. The
39 // configured resolution of the display is not always the same as the touch
40 // screen's reporting resolution, e.g. the display could be set as
41 // 1920x1080 while the touchscreen is reporting touch position range at
42 // 32767x32767. Touch radius is reported in the units the same as touch position
43 // so we need to scale the touch radius to be compatible with the display's
44 // resolution. We compute the scale as
45 // sqrt of (display_area / touchscreen_area)
46 double TouchTransformerController::GetTouchResolutionScale(
47 const DisplayInfo
& touch_display
,
48 const ui::TouchscreenDevice
& touch_device
) const {
49 if (touch_device
.id
== ui::InputDevice::kInvalidId
||
50 touch_device
.size
.IsEmpty() ||
51 touch_display
.bounds_in_native().size().IsEmpty())
54 double display_area
= touch_display
.bounds_in_native().size().GetArea();
55 double touch_area
= touch_device
.size
.GetArea();
56 double ratio
= std::sqrt(display_area
/ touch_area
);
58 VLOG(2) << "Display size: "
59 << touch_display
.bounds_in_native().size().ToString()
60 << ", Touchscreen size: " << touch_device
.size
.ToString()
61 << ", Touch radius scale ratio: " << ratio
;
65 gfx::Transform
TouchTransformerController::GetTouchTransform(
66 const DisplayInfo
& display
,
67 const DisplayInfo
& touch_display
,
68 const ui::TouchscreenDevice
& touchscreen
,
69 const gfx::Size
& framebuffer_size
) const {
70 gfx::SizeF current_size
= display
.bounds_in_native().size();
71 gfx::SizeF touch_native_size
= touch_display
.GetNativeModeSize();
72 #if defined(USE_OZONE)
73 gfx::SizeF touch_area
= touchscreen
.size
;
74 #elif defined(USE_X11)
75 // On X11 touches are reported in the framebuffer coordinate space.
76 gfx::SizeF touch_area
= framebuffer_size
;
81 if (current_size
.IsEmpty() || touch_native_size
.IsEmpty() ||
82 touch_area
.IsEmpty() || touchscreen
.id
== ui::InputDevice::kInvalidId
)
85 #if defined(USE_OZONE)
86 // Translate the touch so that it falls within the display bounds.
87 ctm
.Translate(display
.bounds_in_native().x(),
88 display
.bounds_in_native().y());
91 // Take care of panel fitting only if supported. Panel fitting is emulated in
92 // software mirroring mode (display != touch_display).
93 // If panel fitting is enabled then the aspect ratio is preserved and the
94 // display is scaled acordingly. In this case blank regions would be present
95 // in order to center the displayed area.
96 if (display
.is_aspect_preserving_scaling() ||
97 display
.id() != touch_display
.id()) {
98 float touch_native_ar
=
99 touch_native_size
.width() / touch_native_size
.height();
100 float current_ar
= current_size
.width() / current_size
.height();
102 if (current_ar
> touch_native_ar
) { // Letterboxing
104 0, (1 - current_ar
/ touch_native_ar
) * 0.5 * current_size
.height());
105 ctm
.Scale(1, current_ar
/ touch_native_ar
);
106 } else if (touch_native_ar
> current_ar
) { // Pillarboxing
108 (1 - touch_native_ar
/ current_ar
) * 0.5 * current_size
.width(), 0);
109 ctm
.Scale(touch_native_ar
/ current_ar
, 1);
113 // Take care of scaling between touchscreen area and display resolution.
114 ctm
.Scale(current_size
.width() / touch_area
.width(),
115 current_size
.height() / touch_area
.height());
119 TouchTransformerController::TouchTransformerController() {
120 Shell::GetInstance()->display_controller()->AddObserver(this);
123 TouchTransformerController::~TouchTransformerController() {
124 Shell::GetInstance()->display_controller()->RemoveObserver(this);
127 void TouchTransformerController::UpdateTouchTransformer() const {
128 ui::DeviceDataManager
* device_manager
= ui::DeviceDataManager::GetInstance();
129 device_manager
->ClearTouchDeviceAssociations();
131 // Display IDs and DisplayInfo for mirror or extended mode.
132 int64 display1_id
= gfx::Display::kInvalidDisplayID
;
133 int64 display2_id
= gfx::Display::kInvalidDisplayID
;
134 DisplayInfo display1
;
135 DisplayInfo display2
;
136 // Display ID and DisplayInfo for single display mode.
137 int64 single_display_id
= gfx::Display::kInvalidDisplayID
;
138 DisplayInfo single_display
;
140 DisplayController
* display_controller
=
141 Shell::GetInstance()->display_controller();
142 DisplayManager
* display_manager
= GetDisplayManager();
143 if (display_manager
->num_connected_displays() == 0) {
145 } else if (display_manager
->num_connected_displays() == 1 ||
146 display_manager
->IsInUnifiedMode()) {
147 single_display_id
= display_manager
->first_display_id();
148 DCHECK(single_display_id
!= gfx::Display::kInvalidDisplayID
);
149 single_display
= display_manager
->GetDisplayInfo(single_display_id
);
150 device_manager
->UpdateTouchRadiusScale(
151 single_display
.touch_device_id(),
152 GetTouchResolutionScale(
154 FindTouchscreenById(single_display
.touch_device_id())));
156 DisplayIdPair id_pair
= display_manager
->GetCurrentDisplayIdPair();
157 display1_id
= id_pair
.first
;
158 display2_id
= id_pair
.second
;
159 DCHECK(display1_id
!= gfx::Display::kInvalidDisplayID
&&
160 display2_id
!= gfx::Display::kInvalidDisplayID
);
161 display1
= display_manager
->GetDisplayInfo(display1_id
);
162 display2
= display_manager
->GetDisplayInfo(display2_id
);
163 device_manager
->UpdateTouchRadiusScale(
164 display1
.touch_device_id(),
165 GetTouchResolutionScale(
167 FindTouchscreenById(display1
.touch_device_id())));
168 device_manager
->UpdateTouchRadiusScale(
169 display2
.touch_device_id(),
170 GetTouchResolutionScale(
172 FindTouchscreenById(display2
.touch_device_id())));
176 Shell::GetInstance()->display_configurator()->framebuffer_size();
178 if (display_manager
->IsInMirrorMode()) {
179 if (GetDisplayManager()->SoftwareMirroringEnabled()) {
180 // In extended but software mirroring mode, there is a WindowTreeHost for
181 // each display, but all touches are forwarded to the primary root
182 // window's WindowTreeHost.
183 DisplayInfo target_display
=
184 display_controller
->GetPrimaryDisplayId() == display1_id
? display1
186 device_manager
->UpdateTouchInfoForDisplay(
187 target_display
.id(), display1
.touch_device_id(),
188 GetTouchTransform(target_display
, display1
,
189 FindTouchscreenById(display1
.touch_device_id()),
191 device_manager
->UpdateTouchInfoForDisplay(
192 target_display
.id(), display2
.touch_device_id(),
193 GetTouchTransform(target_display
, display2
,
194 FindTouchscreenById(display2
.touch_device_id()),
197 // In mirror mode, there is just one WindowTreeHost and two displays. Make
198 // the WindowTreeHost accept touch events from both displays.
199 int64 primary_display_id
= display_controller
->GetPrimaryDisplayId();
200 device_manager
->UpdateTouchInfoForDisplay(
201 primary_display_id
, display1
.touch_device_id(),
202 GetTouchTransform(display1
, display1
,
203 FindTouchscreenById(display1
.touch_device_id()),
205 device_manager
->UpdateTouchInfoForDisplay(
206 primary_display_id
, display2
.touch_device_id(),
207 GetTouchTransform(display2
, display2
,
208 FindTouchscreenById(display2
.touch_device_id()),
214 if (display_manager
->num_connected_displays() > 1) {
215 // In actual extended mode, each display is associated with one
217 device_manager
->UpdateTouchInfoForDisplay(
218 display1_id
, display1
.touch_device_id(),
219 GetTouchTransform(display1
, display1
,
220 FindTouchscreenById(display1
.touch_device_id()),
222 device_manager
->UpdateTouchInfoForDisplay(
223 display2_id
, display2
.touch_device_id(),
224 GetTouchTransform(display2
, display2
,
225 FindTouchscreenById(display2
.touch_device_id()),
230 // Single display mode. The WindowTreeHost has one associated display id.
231 device_manager
->UpdateTouchInfoForDisplay(
232 single_display_id
, single_display
.touch_device_id(),
233 GetTouchTransform(single_display
, single_display
,
234 FindTouchscreenById(single_display
.touch_device_id()),
238 void TouchTransformerController::OnDisplaysInitialized() {
239 UpdateTouchTransformer();
242 void TouchTransformerController::OnDisplayConfigurationChanged() {
243 UpdateTouchTransformer();