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