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 "ash/display/display_controller.h" 8#include "ash/display/mirror_window_controller.h" 9#include "ash/display/shared_display_edge_indicator.h" 10#include "ash/screen_ash.h" 11#include "ash/shell.h" 12#include "ash/wm/coordinate_conversion.h" 13#include "ash/wm/window_util.h" 14#include "ui/aura/env.h" 15#include "ui/aura/root_window.h" 16#include "ui/aura/window.h" 17#include "ui/base/events/event.h" 18#include "ui/base/layout.h" 19#include "ui/compositor/dip_util.h" 20#include "ui/gfx/screen.h" 21 22namespace ash { 23namespace internal { 24namespace { 25 26// Maximum size on the display edge that initiate snapping phantom window, 27// from the corner of the display. 28const int kMaximumSnapHeight = 16; 29 30// Minimum height of an indicator on the display edge that allows 31// dragging a window. If two displays shares the edge smaller than 32// this, entire edge will be used as a draggable space. 33const int kMinimumIndicatorHeight = 200; 34 35const int kIndicatorThickness = 1; 36} 37 38MouseCursorEventFilter::MouseCursorEventFilter() 39 : mouse_warp_mode_(WARP_ALWAYS), 40 drag_source_root_(NULL), 41 shared_display_edge_indicator_(new SharedDisplayEdgeIndicator) { 42} 43 44MouseCursorEventFilter::~MouseCursorEventFilter() { 45 HideSharedEdgeIndicator(); 46} 47 48void MouseCursorEventFilter::ShowSharedEdgeIndicator( 49 const aura::RootWindow* from) { 50 HideSharedEdgeIndicator(); 51 if (Shell::GetScreen()->GetNumDisplays() <= 1 || from == NULL) { 52 src_indicator_bounds_.SetRect(0, 0, 0, 0); 53 dst_indicator_bounds_.SetRect(0, 0, 0, 0); 54 drag_source_root_ = NULL; 55 return; 56 } 57 drag_source_root_ = from; 58 59 DisplayLayout::Position position = Shell::GetInstance()-> 60 display_controller()->GetCurrentDisplayLayout().position; 61 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) 62 UpdateHorizontalIndicatorWindowBounds(); 63 else 64 UpdateVerticalIndicatorWindowBounds(); 65 66 shared_display_edge_indicator_->Show(src_indicator_bounds_, 67 dst_indicator_bounds_); 68} 69 70void MouseCursorEventFilter::HideSharedEdgeIndicator() { 71 shared_display_edge_indicator_->Hide(); 72} 73 74void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) { 75 // Handle both MOVED and DRAGGED events here because when the mouse pointer 76 // enters the other root window while dragging, the underlying window system 77 // (at least X11) stops generating a ui::ET_MOUSE_MOVED event. 78 if (event->type() != ui::ET_MOUSE_MOVED && 79 event->type() != ui::ET_MOUSE_DRAGGED) { 80 return; 81 } 82 Shell::GetInstance()->display_controller()-> 83 mirror_window_controller()->UpdateCursorLocation(); 84 85 gfx::Point point_in_screen(event->location()); 86 aura::Window* target = static_cast<aura::Window*>(event->target()); 87 wm::ConvertPointToScreen(target, &point_in_screen); 88 if (WarpMouseCursorIfNecessary(target->GetRootWindow(), point_in_screen)) 89 event->StopPropagation(); 90} 91 92bool MouseCursorEventFilter::WarpMouseCursorIfNecessary( 93 aura::RootWindow* target_root, 94 const gfx::Point& point_in_screen) { 95 if (Shell::GetScreen()->GetNumDisplays() <= 1 || 96 mouse_warp_mode_ == WARP_NONE) 97 return false; 98 const float scale_at_target = ui::GetDeviceScaleFactor(target_root->layer()); 99 100 aura::RootWindow* root_at_point = wm::GetRootWindowAt(point_in_screen); 101 gfx::Point point_in_root = point_in_screen; 102 wm::ConvertPointFromScreen(root_at_point, &point_in_root); 103 gfx::Rect root_bounds = root_at_point->bounds(); 104 int offset_x = 0; 105 int offset_y = 0; 106 107 const float scale_at_point = ui::GetDeviceScaleFactor(root_at_point->layer()); 108 // If the window is dragged from 2x display to 1x display, the 109 // pointer location is rounded by the source scale factor (2x) so 110 // it will never reach the edge (which is odd). Shrink by scale 111 // factor instead. Only integral scale factor is supported. 112 int shrink = 113 target_root != root_at_point && scale_at_target != scale_at_point ? 114 static_cast<int>(scale_at_target) : 1; 115 // Make the bounds inclusive to detect the edge. 116 root_bounds.Inset(0, 0, shrink, shrink); 117 118 if (point_in_root.x() <= root_bounds.x()) { 119 // Use -2, not -1, to avoid infinite loop of pointer warp. 120 offset_x = -2 * scale_at_target; 121 } else if (point_in_root.x() >= root_bounds.right()) { 122 offset_x = 2 * scale_at_target; 123 } else if (point_in_root.y() <= root_bounds.y()) { 124 offset_y = -2 * scale_at_target; 125 } else if (point_in_root.y() >= root_bounds.bottom()) { 126 offset_y = 2 * scale_at_target; 127 } else { 128 return false; 129 } 130 131 gfx::Point point_in_dst_screen(point_in_screen); 132 point_in_dst_screen.Offset(offset_x, offset_y); 133 aura::RootWindow* dst_root = wm::GetRootWindowAt(point_in_dst_screen); 134 135 // Warp the mouse cursor only if the location is in the indicator bounds 136 // or the mouse pointer is in the destination root. 137 if (mouse_warp_mode_ == WARP_DRAG && 138 dst_root != drag_source_root_ && 139 !src_indicator_bounds_.Contains(point_in_screen)) { 140 return false; 141 } 142 143 wm::ConvertPointFromScreen(dst_root, &point_in_dst_screen); 144 145 if (dst_root->bounds().Contains(point_in_dst_screen)) { 146 DCHECK_NE(dst_root, root_at_point); 147 dst_root->MoveCursorTo(point_in_dst_screen); 148 return true; 149 } 150 return false; 151} 152 153void MouseCursorEventFilter::UpdateHorizontalIndicatorWindowBounds() { 154 bool from_primary = Shell::GetPrimaryRootWindow() == drag_source_root_; 155 // GetPrimaryDisplay returns an object on stack, so copy the bounds 156 // instead of using reference. 157 const gfx::Rect primary_bounds = 158 Shell::GetScreen()->GetPrimaryDisplay().bounds(); 159 const gfx::Rect secondary_bounds = ScreenAsh::GetSecondaryDisplay().bounds(); 160 DisplayLayout::Position position = Shell::GetInstance()-> 161 display_controller()->GetCurrentDisplayLayout().position; 162 163 src_indicator_bounds_.set_x( 164 std::max(primary_bounds.x(), secondary_bounds.x())); 165 src_indicator_bounds_.set_width( 166 std::min(primary_bounds.right(), secondary_bounds.right()) - 167 src_indicator_bounds_.x()); 168 src_indicator_bounds_.set_height(kIndicatorThickness); 169 src_indicator_bounds_.set_y( 170 position == DisplayLayout::TOP ? 171 primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) : 172 primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0)); 173 174 dst_indicator_bounds_ = src_indicator_bounds_; 175 dst_indicator_bounds_.set_height(kIndicatorThickness); 176 dst_indicator_bounds_.set_y( 177 position == DisplayLayout::TOP ? 178 primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) : 179 primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness)); 180} 181 182void MouseCursorEventFilter::UpdateVerticalIndicatorWindowBounds() { 183 bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_; 184 // GetPrimaryDisplay returns an object on stack, so copy the bounds 185 // instead of using reference. 186 const gfx::Rect primary_bounds = 187 Shell::GetScreen()->GetPrimaryDisplay().bounds(); 188 const gfx::Rect secondary_bounds = ScreenAsh::GetSecondaryDisplay().bounds(); 189 DisplayLayout::Position position = Shell::GetInstance()-> 190 display_controller()->GetCurrentDisplayLayout().position; 191 192 int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y()); 193 int lower_shared_y = std::min(primary_bounds.bottom(), 194 secondary_bounds.bottom()); 195 int shared_height = lower_shared_y - upper_shared_y; 196 197 int dst_x = position == DisplayLayout::LEFT ? 198 primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) : 199 primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness); 200 dst_indicator_bounds_.SetRect( 201 dst_x, upper_shared_y, kIndicatorThickness, shared_height); 202 203 // The indicator on the source display. 204 src_indicator_bounds_.set_width(kIndicatorThickness); 205 src_indicator_bounds_.set_x( 206 position == DisplayLayout::LEFT ? 207 primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) : 208 primary_bounds.right() - (in_primary ? kIndicatorThickness : 0)); 209 210 const gfx::Rect& source_bounds = 211 in_primary ? primary_bounds : secondary_bounds; 212 int upper_indicator_y = source_bounds.y() + kMaximumSnapHeight; 213 int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y); 214 215 // This gives a hight that can be used without sacrifying the snap space. 216 int available_space = lower_indicator_y - 217 std::max(upper_shared_y, upper_indicator_y); 218 219 if (shared_height < kMinimumIndicatorHeight) { 220 // If the shared height is smaller than minimum height, use the 221 // entire height. 222 upper_indicator_y = upper_shared_y; 223 } else if (available_space < kMinimumIndicatorHeight) { 224 // Snap to the bottom. 225 upper_indicator_y = 226 std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight); 227 } else { 228 upper_indicator_y = std::max(upper_indicator_y, upper_shared_y); 229 } 230 src_indicator_bounds_.set_y(upper_indicator_y); 231 src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y); 232} 233 234} // namespace internal 235} // namespace ash 236