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
17namespace ash {
18
19namespace {
20
21DisplayManager* 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.
30gfx::Transform
31TouchTransformerController::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
44bool 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.
76gfx::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
122TouchTransformerController::TouchTransformerController() :
123    force_compute_mirror_mode_touch_transformer_ (false) {
124  Shell::GetInstance()->display_controller()->AddObserver(this);
125}
126
127TouchTransformerController::~TouchTransformerController() {
128  Shell::GetInstance()->display_controller()->RemoveObserver(this);
129}
130
131void 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
221void TouchTransformerController::OnDisplaysInitialized() {
222  UpdateTouchTransformer();
223}
224
225void TouchTransformerController::OnDisplayConfigurationChanged() {
226  UpdateTouchTransformer();
227}
228
229}  // namespace ash
230