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/chromeos/display_snapshot.h"
15 #include "ui/events/x/device_data_manager.h"
16
17 namespace ash {
18
19 namespace {
20
GetDisplayManager()21 DisplayManager* GetDisplayManager() {
22 return Shell::GetInstance()->display_manager();
23 }
24
25 } // namespace
26
27 // This function computes the extended mode TouchTransformer for
28 // |touch_display|. The TouchTransformer maps the touch event position
29 // from framebuffer size to the display size.
30 gfx::Transform
GetExtendedModeTouchTransformer(const DisplayInfo & touch_display,const gfx::Size & fb_size) const31 TouchTransformerController::GetExtendedModeTouchTransformer(
32 const DisplayInfo& touch_display, const gfx::Size& fb_size) const {
33 gfx::Transform ctm;
34 if (touch_display.touch_device_id() == 0 ||
35 fb_size.width() == 0.0 ||
36 fb_size.height() == 0.0)
37 return ctm;
38 float width = touch_display.bounds_in_native().width();
39 float height = touch_display.bounds_in_native().height();
40 ctm.Scale(width / fb_size.width(), height / fb_size.height());
41 return ctm;
42 }
43
ShouldComputeMirrorModeTouchTransformer(const DisplayInfo & touch_display) const44 bool TouchTransformerController::ShouldComputeMirrorModeTouchTransformer(
45 const DisplayInfo& touch_display) const {
46 if (force_compute_mirror_mode_touch_transformer_)
47 return true;
48
49 if (touch_display.touch_device_id() == 0)
50 return false;
51
52 const ui::DisplayConfigurator::DisplayState* state = NULL;
53 const std::vector<ui::DisplayConfigurator::DisplayState>& cached_displays =
54 Shell::GetInstance()->display_configurator()->cached_displays();
55 for (size_t i = 0; i < cached_displays.size(); i++) {
56 if (cached_displays[i].touch_device_id == touch_display.touch_device_id()) {
57 state = &cached_displays[i];
58 break;
59 }
60 }
61
62 if (!state || state->mirror_mode == state->display->native_mode() ||
63 !state->display->is_aspect_preserving_scaling()) {
64 return false;
65 }
66 return true;
67 }
68
69 // This function computes the mirror mode TouchTransformer for |touch_display|.
70 // When internal monitor is applied a resolution that does not have
71 // the same aspect ratio as its native resolution, there would be
72 // blank regions in the letterboxing/pillarboxing mode.
73 // The TouchTransformer will make sure the touch events on the blank region
74 // have negative coordinates and touch events within the chrome region
75 // have the correct positive coordinates.
GetMirrorModeTouchTransformer(const DisplayInfo & touch_display) const76 gfx::Transform TouchTransformerController::GetMirrorModeTouchTransformer(
77 const DisplayInfo& touch_display) const {
78 gfx::Transform ctm;
79 if (!ShouldComputeMirrorModeTouchTransformer(touch_display))
80 return ctm;
81
82 float mirror_width = touch_display.bounds_in_native().width();
83 float mirror_height = touch_display.bounds_in_native().height();
84 float native_width = 0;
85 float native_height = 0;
86
87 std::vector<DisplayMode> modes = touch_display.display_modes();
88 for (size_t i = 0; i < modes.size(); i++) {
89 if (modes[i].native) {
90 native_width = modes[i].size.width();
91 native_height = modes[i].size.height();
92 break;
93 }
94 }
95
96 if (native_height == 0.0 || mirror_height == 0.0 ||
97 native_width == 0.0 || mirror_width == 0.0)
98 return ctm;
99
100 float native_ar = static_cast<float>(native_width) /
101 static_cast<float>(native_height);
102 float mirror_ar = static_cast<float>(mirror_width) /
103 static_cast<float>(mirror_height);
104
105 if (mirror_ar > native_ar) { // Letterboxing
106 // Translate before scale.
107 ctm.Translate(0.0, (1.0 - mirror_ar / native_ar) * 0.5 * mirror_height);
108 ctm.Scale(1.0, mirror_ar / native_ar);
109 return ctm;
110 }
111
112 if (native_ar > mirror_ar) { // Pillarboxing
113 // Translate before scale.
114 ctm.Translate((1.0 - native_ar / mirror_ar) * 0.5 * mirror_width, 0.0);
115 ctm.Scale(native_ar / mirror_ar, 1.0);
116 return ctm;
117 }
118
119 return ctm; // Same aspect ratio - return identity
120 }
121
TouchTransformerController()122 TouchTransformerController::TouchTransformerController() :
123 force_compute_mirror_mode_touch_transformer_ (false) {
124 Shell::GetInstance()->display_controller()->AddObserver(this);
125 }
126
~TouchTransformerController()127 TouchTransformerController::~TouchTransformerController() {
128 Shell::GetInstance()->display_controller()->RemoveObserver(this);
129 }
130
UpdateTouchTransformer() const131 void TouchTransformerController::UpdateTouchTransformer() const {
132 ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
133 device_manager->ClearTouchTransformerRecord();
134
135 // Display IDs and DisplayInfo for mirror or extended mode.
136 int64 display1_id = gfx::Display::kInvalidDisplayID;
137 int64 display2_id = gfx::Display::kInvalidDisplayID;
138 DisplayInfo display1;
139 DisplayInfo display2;
140 // Display ID and DisplayInfo for single display mode.
141 int64 single_display_id = gfx::Display::kInvalidDisplayID;
142 DisplayInfo single_display;
143
144 DisplayController* display_controller =
145 Shell::GetInstance()->display_controller();
146 ui::MultipleDisplayState display_state =
147 Shell::GetInstance()->display_configurator()->display_state();
148 if (display_state == ui::MULTIPLE_DISPLAY_STATE_INVALID ||
149 display_state == ui::MULTIPLE_DISPLAY_STATE_HEADLESS) {
150 return;
151 } else if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
152 display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) {
153 // TODO(miletus) : Handle DUAL_EXTENDED with software mirroring.
154 DisplayIdPair id_pair = GetDisplayManager()->GetCurrentDisplayIdPair();
155 display1_id = id_pair.first;
156 display2_id = id_pair.second;
157 DCHECK(display1_id != gfx::Display::kInvalidDisplayID &&
158 display2_id != gfx::Display::kInvalidDisplayID);
159 display1 = GetDisplayManager()->GetDisplayInfo(display1_id);
160 display2 = GetDisplayManager()->GetDisplayInfo(display2_id);
161 } else {
162 single_display_id = GetDisplayManager()->first_display_id();
163 DCHECK(single_display_id != gfx::Display::kInvalidDisplayID);
164 single_display = GetDisplayManager()->GetDisplayInfo(single_display_id);
165 }
166
167 if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
168 // In mirror mode, both displays share the same root window so
169 // both display ids are associated with the root window.
170 aura::Window* root = display_controller->GetPrimaryRootWindow();
171 RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID(
172 display1_id, display2_id);
173 device_manager->UpdateTouchInfoForDisplay(
174 display1_id,
175 display1.touch_device_id(),
176 GetMirrorModeTouchTransformer(display1));
177 device_manager->UpdateTouchInfoForDisplay(
178 display2_id,
179 display2.touch_device_id(),
180 GetMirrorModeTouchTransformer(display2));
181 return;
182 }
183
184 if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) {
185 // TODO(miletus) : Handle the case the state is DUAL_EXTENDED but it
186 // is actually doing software mirroring.
187 if (GetDisplayManager()->software_mirroring_enabled())
188 return;
189 // In extended mode, each display is associated with one root window.
190 aura::Window* root1 =
191 display_controller->GetRootWindowForDisplayId(display1_id);
192 aura::Window* root2 =
193 display_controller->GetRootWindowForDisplayId(display2_id);
194 RootWindowController::ForWindow(root1)->ash_host()->UpdateDisplayID(
195 display1_id, gfx::Display::kInvalidDisplayID);
196 RootWindowController::ForWindow(root2)->ash_host()->UpdateDisplayID(
197 display2_id, gfx::Display::kInvalidDisplayID);
198 gfx::Size fb_size =
199 Shell::GetInstance()->display_configurator()->framebuffer_size();
200 device_manager->UpdateTouchInfoForDisplay(
201 display1_id,
202 display1.touch_device_id(),
203 GetExtendedModeTouchTransformer(display1, fb_size));
204 device_manager->UpdateTouchInfoForDisplay(
205 display2_id,
206 display2.touch_device_id(),
207 GetExtendedModeTouchTransformer(display2, fb_size));
208 return;
209 }
210
211 // Single display mode. The root window has one associated display id.
212 aura::Window* root =
213 display_controller->GetRootWindowForDisplayId(single_display.id());
214 RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID(
215 single_display.id(), gfx::Display::kInvalidDisplayID);
216 device_manager->UpdateTouchInfoForDisplay(single_display_id,
217 single_display.touch_device_id(),
218 gfx::Transform());
219 }
220
OnDisplaysInitialized()221 void TouchTransformerController::OnDisplaysInitialized() {
222 UpdateTouchTransformer();
223 }
224
OnDisplayConfigurationChanged()225 void TouchTransformerController::OnDisplayConfigurationChanged() {
226 UpdateTouchTransformer();
227 }
228
229 } // namespace ash
230