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
18namespace ash {
19
20namespace {
21
22DisplayManager* 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)
36double 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.
76gfx::Transform
77TouchTransformerController::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
90bool 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.
124gfx::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
170TouchTransformerController::TouchTransformerController() :
171    force_compute_mirror_mode_touch_transformer_ (false) {
172  Shell::GetInstance()->display_controller()->AddObserver(this);
173}
174
175TouchTransformerController::~TouchTransformerController() {
176  Shell::GetInstance()->display_controller()->RemoveObserver(this);
177}
178
179void 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
294void TouchTransformerController::OnDisplaysInitialized() {
295  UpdateTouchTransformer();
296}
297
298void TouchTransformerController::OnDisplayConfigurationChanged() {
299  UpdateTouchTransformer();
300}
301
302}  // namespace ash
303