Home | History | Annotate | Download | only in display
      1 // Copyright (c) 2012 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/display/mouse_cursor_event_filter.h"
      6 
      7 #include <cmath>
      8 
      9 #include "ash/display/cursor_window_controller.h"
     10 #include "ash/display/display_controller.h"
     11 #include "ash/display/display_manager.h"
     12 #include "ash/display/shared_display_edge_indicator.h"
     13 #include "ash/host/ash_window_tree_host.h"
     14 #include "ash/root_window_controller.h"
     15 #include "ash/screen_util.h"
     16 #include "ash/shell.h"
     17 #include "ash/wm/window_util.h"
     18 #include "ui/aura/env.h"
     19 #include "ui/aura/window.h"
     20 #include "ui/aura/window_event_dispatcher.h"
     21 #include "ui/aura/window_tree_host.h"
     22 #include "ui/base/layout.h"
     23 #include "ui/compositor/dip_util.h"
     24 #include "ui/events/event.h"
     25 #include "ui/events/event_utils.h"
     26 #include "ui/gfx/screen.h"
     27 #include "ui/wm/core/coordinate_conversion.h"
     28 
     29 namespace ash {
     30 namespace {
     31 
     32 // Maximum size on the display edge that initiate snapping phantom window,
     33 // from the corner of the display.
     34 const int kMaximumSnapHeight = 16;
     35 
     36 // Minimum height of an indicator on the display edge that allows
     37 // dragging a window.  If two displays shares the edge smaller than
     38 // this, entire edge will be used as a draggable space.
     39 const int kMinimumIndicatorHeight = 200;
     40 
     41 const int kIndicatorThickness = 1;
     42 
     43 void ConvertPointFromScreenToNative(const aura::Window* root_window,
     44                                     gfx::Point* point) {
     45   ::wm::ConvertPointFromScreen(root_window, point);
     46   root_window->GetHost()->ConvertPointToNativeScreen(point);
     47 }
     48 
     49 gfx::Rect GetNativeEdgeBounds(const aura::Window* root_window,
     50                               gfx::Point start,
     51                               gfx::Point end) {
     52   gfx::Rect native_bounds = root_window->GetHost()->GetBounds();
     53   native_bounds.Inset(
     54       GetRootWindowController(root_window)->ash_host()->GetHostInsets());
     55 
     56   ConvertPointFromScreenToNative(root_window, &start);
     57   ConvertPointFromScreenToNative(root_window, &end);
     58   if (start.x() == end.x()) {
     59     // vertical in native
     60     int x = std::abs(native_bounds.x() - start.x()) <
     61                     std::abs(native_bounds.right() - start.x())
     62                 ? native_bounds.x()
     63                 : native_bounds.right() - 1;
     64     return gfx::Rect(
     65         x, std::min(start.y(), end.y()), 1, std::abs(start.y() - end.y()));
     66   } else {
     67     // horizontal in native
     68     int y = std::abs(native_bounds.y() - start.y()) <
     69                     std::abs(native_bounds.bottom() - start.y())
     70                 ? native_bounds.y()
     71                 : native_bounds.bottom() - 1;
     72     return gfx::Rect(
     73         std::min(start.x(), end.x()), y, std::abs(start.x() - end.x()), 1);
     74   }
     75 }
     76 
     77 // Creates edge bounds from indicator bounds that fits the edge
     78 // of the native window for |root_window|.
     79 gfx::Rect CreateVerticalEdgeBoundsInNative(const aura::Window* root_window,
     80                                            const gfx::Rect& indicator_bounds) {
     81   gfx::Point start = indicator_bounds.origin();
     82   gfx::Point end = start;
     83   end.set_y(indicator_bounds.bottom());
     84   return GetNativeEdgeBounds(root_window, start, end);
     85 }
     86 
     87 gfx::Rect CreateHorizontalEdgeBoundsInNative(
     88     const aura::Window* root_window,
     89     const gfx::Rect& indicator_bounds) {
     90   gfx::Point start = indicator_bounds.origin();
     91   gfx::Point end = start;
     92   end.set_x(indicator_bounds.right());
     93   return GetNativeEdgeBounds(root_window, start, end);
     94 }
     95 
     96 void MovePointInside(const gfx::Rect& native_bounds,
     97                      gfx::Point* point_in_native) {
     98   if (native_bounds.x() > point_in_native->x())
     99     point_in_native->set_x(native_bounds.x());
    100   if (native_bounds.right() < point_in_native->x())
    101     point_in_native->set_x(native_bounds.right());
    102 
    103   if (native_bounds.y() > point_in_native->y())
    104     point_in_native->set_y(native_bounds.y());
    105   if (native_bounds.bottom() < point_in_native->y())
    106     point_in_native->set_y(native_bounds.bottom());
    107 }
    108 
    109 }  // namespace
    110 
    111 MouseCursorEventFilter::MouseCursorEventFilter()
    112     : mouse_warp_mode_(WARP_ALWAYS),
    113       was_mouse_warped_(false),
    114       drag_source_root_(NULL),
    115       scale_when_drag_started_(1.0f),
    116       shared_display_edge_indicator_(new SharedDisplayEdgeIndicator) {
    117   Shell::GetInstance()->display_controller()->AddObserver(this);
    118 }
    119 
    120 MouseCursorEventFilter::~MouseCursorEventFilter() {
    121   HideSharedEdgeIndicator();
    122   Shell::GetInstance()->display_controller()->RemoveObserver(this);
    123 }
    124 
    125 void MouseCursorEventFilter::ShowSharedEdgeIndicator(aura::Window* from) {
    126   HideSharedEdgeIndicator();
    127   if (Shell::GetScreen()->GetNumDisplays() <= 1 || from == NULL) {
    128     src_indicator_bounds_.SetRect(0, 0, 0, 0);
    129     dst_indicator_bounds_.SetRect(0, 0, 0, 0);
    130     drag_source_root_ = NULL;
    131     return;
    132   }
    133   drag_source_root_ = from;
    134 
    135   DisplayLayout::Position position = Shell::GetInstance()->
    136       display_manager()->GetCurrentDisplayLayout().position;
    137   if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
    138     UpdateHorizontalEdgeBounds();
    139   else
    140     UpdateVerticalEdgeBounds();
    141 
    142   shared_display_edge_indicator_->Show(src_indicator_bounds_,
    143                                        dst_indicator_bounds_);
    144 }
    145 
    146 void MouseCursorEventFilter::HideSharedEdgeIndicator() {
    147   shared_display_edge_indicator_->Hide();
    148   OnDisplayConfigurationChanged();
    149 }
    150 
    151 void MouseCursorEventFilter::OnDisplaysInitialized() {
    152   OnDisplayConfigurationChanged();
    153 }
    154 
    155 void MouseCursorEventFilter::OnDisplayConfigurationChanged() {
    156   // Extra check for |num_connected_displays()| is for SystemDisplayApiTest
    157   // that injects MockScreen.
    158   if (Shell::GetScreen()->GetNumDisplays() <= 1 ||
    159       Shell::GetInstance()->display_manager()->num_connected_displays() <= 1) {
    160     src_edge_bounds_in_native_.SetRect(0, 0, 0, 0);
    161     dst_edge_bounds_in_native_.SetRect(0, 0, 0, 0);
    162     return;
    163   }
    164 
    165   drag_source_root_ = NULL;
    166   DisplayLayout::Position position = Shell::GetInstance()
    167                                          ->display_manager()
    168                                          ->GetCurrentDisplayLayout()
    169                                          .position;
    170 
    171   if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
    172     UpdateHorizontalEdgeBounds();
    173   else
    174     UpdateVerticalEdgeBounds();
    175 }
    176 
    177 void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) {
    178   aura::Window* target = static_cast<aura::Window*>(event->target());
    179 
    180   if (event->type() == ui::ET_MOUSE_PRESSED) {
    181     scale_when_drag_started_ = ui::GetDeviceScaleFactor(target->layer());
    182   } else if (event->type() == ui::ET_MOUSE_RELEASED) {
    183     scale_when_drag_started_ = 1.0f;
    184   }
    185 
    186   // Handle both MOVED and DRAGGED events here because when the mouse pointer
    187   // enters the other root window while dragging, the underlying window system
    188   // (at least X11) stops generating a ui::ET_MOUSE_MOVED event.
    189   if (event->type() != ui::ET_MOUSE_MOVED &&
    190       event->type() != ui::ET_MOUSE_DRAGGED) {
    191       return;
    192   }
    193 
    194   Shell::GetInstance()->display_controller()->
    195       cursor_window_controller()->UpdateLocation();
    196 
    197   if (WarpMouseCursorIfNecessary(event))
    198     event->StopPropagation();
    199 }
    200 
    201 // static
    202 void MouseCursorEventFilter::MoveCursorTo(aura::Window* root,
    203                                           const gfx::Point& point_in_screen) {
    204   gfx::Point point_in_native = point_in_screen;
    205   ::wm::ConvertPointFromScreen(root, &point_in_native);
    206   root->GetHost()->ConvertPointToNativeScreen(&point_in_native);
    207 
    208   // now fit the point inside the native bounds.
    209   gfx::Rect native_bounds = root->GetHost()->GetBounds();
    210   gfx::Point native_origin = native_bounds.origin();
    211   native_bounds.Inset(
    212       GetRootWindowController(root)->ash_host()->GetHostInsets());
    213   // Shrink further so that the mouse doesn't warp on the
    214   // edge. The right/bottom needs to be shrink by 2 to subtract
    215   // the 1 px from width/height value.
    216   native_bounds.Inset(1, 1, 2, 2);
    217 
    218   MovePointInside(native_bounds, &point_in_native);
    219   gfx::Point point_in_host = point_in_native;
    220 
    221   point_in_host.Offset(-native_origin.x(), -native_origin.y());
    222   root->GetHost()->MoveCursorToHostLocation(point_in_host);
    223 }
    224 
    225 bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(ui::MouseEvent* event) {
    226   if (!event->HasNativeEvent())
    227     return false;
    228 
    229   gfx::Point point_in_native =
    230       ui::EventSystemLocationFromNative(event->native_event());
    231 
    232   aura::Window* target = static_cast<aura::Window*>(event->target());
    233 #if defined(USE_OZONE)
    234   // TODO(dnicoara): crbug.com/415680 Move cursor warping into Ozone once Ozone
    235   // has access to the logical display layout.
    236   // Native events in Ozone are in the native window coordinate system. We need
    237   // to translate them to get the global position.
    238   point_in_native.Offset(target->GetHost()->GetBounds().x(),
    239                          target->GetHost()->GetBounds().y());
    240 #endif
    241   gfx::Point point_in_screen = event->location();
    242   ::wm::ConvertPointToScreen(target, &point_in_screen);
    243 
    244   return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen);
    245 }
    246 
    247 bool MouseCursorEventFilter::WarpMouseCursorInNativeCoords(
    248     const gfx::Point& point_in_native,
    249     const gfx::Point& point_in_screen) {
    250   if (Shell::GetScreen()->GetNumDisplays() <= 1 ||
    251       mouse_warp_mode_ == WARP_NONE)
    252     return false;
    253 
    254   bool in_src_edge = src_edge_bounds_in_native_.Contains(point_in_native);
    255   bool in_dst_edge = dst_edge_bounds_in_native_.Contains(point_in_native);
    256   if (!in_src_edge && !in_dst_edge)
    257     return false;
    258 
    259   // The mouse must move.
    260   aura::Window* src_root = NULL;
    261   aura::Window* dst_root = NULL;
    262   GetSrcAndDstRootWindows(&src_root, &dst_root);
    263 
    264   if (in_src_edge)
    265     MoveCursorTo(dst_root, point_in_screen);
    266   else
    267     MoveCursorTo(src_root, point_in_screen);
    268 
    269   return true;
    270 }
    271 
    272 void MouseCursorEventFilter::UpdateHorizontalEdgeBounds() {
    273   bool from_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
    274   // GetPrimaryDisplay returns an object on stack, so copy the bounds
    275   // instead of using reference.
    276   const gfx::Rect primary_bounds =
    277       Shell::GetScreen()->GetPrimaryDisplay().bounds();
    278   const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds();
    279   DisplayLayout::Position position = Shell::GetInstance()->
    280       display_manager()->GetCurrentDisplayLayout().position;
    281 
    282   src_indicator_bounds_.set_x(
    283       std::max(primary_bounds.x(), secondary_bounds.x()));
    284   src_indicator_bounds_.set_width(
    285       std::min(primary_bounds.right(), secondary_bounds.right()) -
    286       src_indicator_bounds_.x());
    287   src_indicator_bounds_.set_height(kIndicatorThickness);
    288   src_indicator_bounds_.set_y(
    289       position == DisplayLayout::TOP ?
    290       primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) :
    291       primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0));
    292 
    293   dst_indicator_bounds_ = src_indicator_bounds_;
    294   dst_indicator_bounds_.set_height(kIndicatorThickness);
    295   dst_indicator_bounds_.set_y(
    296       position == DisplayLayout::TOP ?
    297       primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) :
    298       primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness));
    299 
    300   aura::Window* src_root = NULL;
    301   aura::Window* dst_root = NULL;
    302   GetSrcAndDstRootWindows(&src_root, &dst_root);
    303 
    304   src_edge_bounds_in_native_ =
    305       CreateHorizontalEdgeBoundsInNative(src_root, src_indicator_bounds_);
    306   dst_edge_bounds_in_native_ =
    307       CreateHorizontalEdgeBoundsInNative(dst_root, dst_indicator_bounds_);
    308 }
    309 
    310 void MouseCursorEventFilter::UpdateVerticalEdgeBounds() {
    311   int snap_height = drag_source_root_ ? kMaximumSnapHeight : 0;
    312   bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
    313   // GetPrimaryDisplay returns an object on stack, so copy the bounds
    314   // instead of using reference.
    315   const gfx::Rect primary_bounds =
    316       Shell::GetScreen()->GetPrimaryDisplay().bounds();
    317   const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds();
    318   DisplayLayout::Position position = Shell::GetInstance()->
    319       display_manager()->GetCurrentDisplayLayout().position;
    320 
    321   int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y());
    322   int lower_shared_y = std::min(primary_bounds.bottom(),
    323                                 secondary_bounds.bottom());
    324   int shared_height = lower_shared_y - upper_shared_y;
    325 
    326   int dst_x = position == DisplayLayout::LEFT ?
    327       primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) :
    328       primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness);
    329   dst_indicator_bounds_.SetRect(
    330       dst_x, upper_shared_y, kIndicatorThickness, shared_height);
    331 
    332   // The indicator on the source display.
    333   src_indicator_bounds_.set_width(kIndicatorThickness);
    334   src_indicator_bounds_.set_x(
    335       position == DisplayLayout::LEFT ?
    336       primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) :
    337       primary_bounds.right() - (in_primary ? kIndicatorThickness : 0));
    338 
    339   const gfx::Rect& source_bounds =
    340       in_primary ? primary_bounds : secondary_bounds;
    341   int upper_indicator_y = source_bounds.y() + snap_height;
    342   int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y);
    343 
    344   // This gives a hight that can be used without sacrifying the snap space.
    345   int available_space = lower_indicator_y -
    346       std::max(upper_shared_y, upper_indicator_y);
    347 
    348   if (shared_height < kMinimumIndicatorHeight) {
    349     // If the shared height is smaller than minimum height, use the
    350     // entire height.
    351     upper_indicator_y = upper_shared_y;
    352   } else if (available_space < kMinimumIndicatorHeight) {
    353     // Snap to the bottom.
    354     upper_indicator_y =
    355         std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight);
    356   } else {
    357     upper_indicator_y = std::max(upper_indicator_y, upper_shared_y);
    358   }
    359   src_indicator_bounds_.set_y(upper_indicator_y);
    360   src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y);
    361 
    362   aura::Window* src_root = NULL;
    363   aura::Window* dst_root = NULL;
    364   GetSrcAndDstRootWindows(&src_root, &dst_root);
    365 
    366   // Native
    367   src_edge_bounds_in_native_ =
    368       CreateVerticalEdgeBoundsInNative(src_root, src_indicator_bounds_);
    369   dst_edge_bounds_in_native_ =
    370       CreateVerticalEdgeBoundsInNative(dst_root, dst_indicator_bounds_);
    371 }
    372 
    373 void MouseCursorEventFilter::GetSrcAndDstRootWindows(aura::Window** src_root,
    374                                                      aura::Window** dst_root) {
    375   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
    376   *src_root = drag_source_root_ ? drag_source_root_
    377                                 : Shell::GetInstance()->GetPrimaryRootWindow();
    378   *dst_root = root_windows[0] == *src_root ? root_windows[1] : root_windows[0];
    379 }
    380 
    381 bool MouseCursorEventFilter::WarpMouseCursorIfNecessaryForTest(
    382     aura::Window* target_root,
    383     const gfx::Point& point_in_screen) {
    384   gfx::Point native = point_in_screen;
    385   ::wm::ConvertPointFromScreen(target_root, &native);
    386   target_root->GetHost()->ConvertPointToNativeScreen(&native);
    387   return WarpMouseCursorInNativeCoords(native, point_in_screen);
    388 }
    389 
    390 }  // namespace ash
    391