mouse_cursor_event_filter.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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/coordinate_conversion.h" 18#include "ash/wm/window_util.h" 19#include "ui/aura/env.h" 20#include "ui/aura/window.h" 21#include "ui/aura/window_event_dispatcher.h" 22#include "ui/aura/window_tree_host.h" 23#include "ui/base/layout.h" 24#include "ui/compositor/dip_util.h" 25#include "ui/events/event.h" 26#include "ui/events/event_utils.h" 27#include "ui/gfx/screen.h" 28 29namespace ash { 30namespace { 31 32// Maximum size on the display edge that initiate snapping phantom window, 33// from the corner of the display. 34const 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. 39const int kMinimumIndicatorHeight = 200; 40 41const int kIndicatorThickness = 1; 42 43void ConvertPointFromScreenToNative(const aura::Window* root_window, 44 gfx::Point* point) { 45 wm::ConvertPointFromScreen(root_window, point); 46 root_window->GetHost()->ConvertPointToNativeScreen(point); 47} 48 49gfx::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|. 79gfx::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 87gfx::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 96void 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 111MouseCursorEventFilter::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 120MouseCursorEventFilter::~MouseCursorEventFilter() { 121 HideSharedEdgeIndicator(); 122 Shell::GetInstance()->display_controller()->RemoveObserver(this); 123} 124 125void 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 146void MouseCursorEventFilter::HideSharedEdgeIndicator() { 147 shared_display_edge_indicator_->Hide(); 148 OnDisplayConfigurationChanged(); 149} 150 151void MouseCursorEventFilter::OnDisplaysInitialized() { 152 OnDisplayConfigurationChanged(); 153} 154 155#if !defined(USE_OZONE) 156void MouseCursorEventFilter::OnDisplayConfigurationChanged() { 157 // Extra check for |num_connected_displays()| is for SystemDisplayApiTest 158 // that injects MockScreen. 159 if (Shell::GetScreen()->GetNumDisplays() <= 1 || 160 Shell::GetInstance()->display_manager()->num_connected_displays() <= 1) { 161 src_edge_bounds_in_native_.SetRect(0, 0, 0, 0); 162 dst_edge_bounds_in_native_.SetRect(0, 0, 0, 0); 163 return; 164 } 165 166 drag_source_root_ = NULL; 167 DisplayLayout::Position position = Shell::GetInstance() 168 ->display_manager() 169 ->GetCurrentDisplayLayout() 170 .position; 171 172 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) 173 UpdateHorizontalEdgeBounds(); 174 else 175 UpdateVerticalEdgeBounds(); 176} 177#endif 178 179void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) { 180 aura::Window* target = static_cast<aura::Window*>(event->target()); 181 182 if (event->type() == ui::ET_MOUSE_PRESSED) { 183 scale_when_drag_started_ = ui::GetDeviceScaleFactor(target->layer()); 184 } else if (event->type() == ui::ET_MOUSE_RELEASED) { 185 scale_when_drag_started_ = 1.0f; 186 } 187 188 // Handle both MOVED and DRAGGED events here because when the mouse pointer 189 // enters the other root window while dragging, the underlying window system 190 // (at least X11) stops generating a ui::ET_MOUSE_MOVED event. 191 if (event->type() != ui::ET_MOUSE_MOVED && 192 event->type() != ui::ET_MOUSE_DRAGGED) { 193 return; 194 } 195 196 Shell::GetInstance()->display_controller()-> 197 cursor_window_controller()->UpdateLocation(); 198 199 if (WarpMouseCursorIfNecessary(event)) 200 event->StopPropagation(); 201} 202 203// static 204void MouseCursorEventFilter::MoveCursorTo(aura::Window* root, 205 const gfx::Point& point_in_screen) { 206 gfx::Point point_in_native = point_in_screen; 207 wm::ConvertPointFromScreen(root, &point_in_native); 208 root->GetHost()->ConvertPointToNativeScreen(&point_in_native); 209 210 // now fit the point inside the native bounds. 211 gfx::Rect native_bounds = root->GetHost()->GetBounds(); 212 gfx::Point native_origin = native_bounds.origin(); 213 native_bounds.Inset( 214 GetRootWindowController(root)->ash_host()->GetHostInsets()); 215 // Shrink further so that the mouse doesn't warp on the 216 // edge. The right/bottom needs to be shrink by 2 to subtract 217 // the 1 px from width/height value. 218 native_bounds.Inset(1, 1, 2, 2); 219 220 MovePointInside(native_bounds, &point_in_native); 221 gfx::Point point_in_host = point_in_native; 222 223 point_in_host.Offset(-native_origin.x(), -native_origin.y()); 224 root->GetHost()->MoveCursorToHostLocation(point_in_host); 225} 226 227#if !defined(USE_OZONE) 228bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(ui::MouseEvent* event) { 229 if (!event->HasNativeEvent()) 230 return false; 231 232 gfx::Point point_in_native = 233 ui::EventSystemLocationFromNative(event->native_event()); 234 235 gfx::Point point_in_screen = event->location(); 236 aura::Window* target = static_cast<aura::Window*>(event->target()); 237 wm::ConvertPointToScreen(target, &point_in_screen); 238 239 return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen); 240} 241 242bool MouseCursorEventFilter::WarpMouseCursorInNativeCoords( 243 const gfx::Point& point_in_native, 244 const gfx::Point& point_in_screen) { 245 if (Shell::GetScreen()->GetNumDisplays() <= 1 || 246 mouse_warp_mode_ == WARP_NONE) 247 return false; 248 249 bool in_src_edge = src_edge_bounds_in_native_.Contains(point_in_native); 250 bool in_dst_edge = dst_edge_bounds_in_native_.Contains(point_in_native); 251 if (!in_src_edge && !in_dst_edge) 252 return false; 253 254 // The mouse must move. 255 aura::Window* src_root = NULL; 256 aura::Window* dst_root = NULL; 257 GetSrcAndDstRootWindows(&src_root, &dst_root); 258 259 if (in_src_edge) 260 MoveCursorTo(dst_root, point_in_screen); 261 else 262 MoveCursorTo(src_root, point_in_screen); 263 264 return true; 265} 266#endif 267 268void MouseCursorEventFilter::UpdateHorizontalEdgeBounds() { 269 bool from_primary = Shell::GetPrimaryRootWindow() == drag_source_root_; 270 // GetPrimaryDisplay returns an object on stack, so copy the bounds 271 // instead of using reference. 272 const gfx::Rect primary_bounds = 273 Shell::GetScreen()->GetPrimaryDisplay().bounds(); 274 const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds(); 275 DisplayLayout::Position position = Shell::GetInstance()-> 276 display_manager()->GetCurrentDisplayLayout().position; 277 278 src_indicator_bounds_.set_x( 279 std::max(primary_bounds.x(), secondary_bounds.x())); 280 src_indicator_bounds_.set_width( 281 std::min(primary_bounds.right(), secondary_bounds.right()) - 282 src_indicator_bounds_.x()); 283 src_indicator_bounds_.set_height(kIndicatorThickness); 284 src_indicator_bounds_.set_y( 285 position == DisplayLayout::TOP ? 286 primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) : 287 primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0)); 288 289 dst_indicator_bounds_ = src_indicator_bounds_; 290 dst_indicator_bounds_.set_height(kIndicatorThickness); 291 dst_indicator_bounds_.set_y( 292 position == DisplayLayout::TOP ? 293 primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) : 294 primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness)); 295 296 aura::Window* src_root = NULL; 297 aura::Window* dst_root = NULL; 298 GetSrcAndDstRootWindows(&src_root, &dst_root); 299 300 src_edge_bounds_in_native_ = 301 CreateHorizontalEdgeBoundsInNative(src_root, src_indicator_bounds_); 302 dst_edge_bounds_in_native_ = 303 CreateHorizontalEdgeBoundsInNative(dst_root, dst_indicator_bounds_); 304} 305 306void MouseCursorEventFilter::UpdateVerticalEdgeBounds() { 307 int snap_height = drag_source_root_ ? kMaximumSnapHeight : 0; 308 bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_; 309 // GetPrimaryDisplay returns an object on stack, so copy the bounds 310 // instead of using reference. 311 const gfx::Rect primary_bounds = 312 Shell::GetScreen()->GetPrimaryDisplay().bounds(); 313 const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds(); 314 DisplayLayout::Position position = Shell::GetInstance()-> 315 display_manager()->GetCurrentDisplayLayout().position; 316 317 int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y()); 318 int lower_shared_y = std::min(primary_bounds.bottom(), 319 secondary_bounds.bottom()); 320 int shared_height = lower_shared_y - upper_shared_y; 321 322 int dst_x = position == DisplayLayout::LEFT ? 323 primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) : 324 primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness); 325 dst_indicator_bounds_.SetRect( 326 dst_x, upper_shared_y, kIndicatorThickness, shared_height); 327 328 // The indicator on the source display. 329 src_indicator_bounds_.set_width(kIndicatorThickness); 330 src_indicator_bounds_.set_x( 331 position == DisplayLayout::LEFT ? 332 primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) : 333 primary_bounds.right() - (in_primary ? kIndicatorThickness : 0)); 334 335 const gfx::Rect& source_bounds = 336 in_primary ? primary_bounds : secondary_bounds; 337 int upper_indicator_y = source_bounds.y() + snap_height; 338 int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y); 339 340 // This gives a hight that can be used without sacrifying the snap space. 341 int available_space = lower_indicator_y - 342 std::max(upper_shared_y, upper_indicator_y); 343 344 if (shared_height < kMinimumIndicatorHeight) { 345 // If the shared height is smaller than minimum height, use the 346 // entire height. 347 upper_indicator_y = upper_shared_y; 348 } else if (available_space < kMinimumIndicatorHeight) { 349 // Snap to the bottom. 350 upper_indicator_y = 351 std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight); 352 } else { 353 upper_indicator_y = std::max(upper_indicator_y, upper_shared_y); 354 } 355 src_indicator_bounds_.set_y(upper_indicator_y); 356 src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y); 357 358 aura::Window* src_root = NULL; 359 aura::Window* dst_root = NULL; 360 GetSrcAndDstRootWindows(&src_root, &dst_root); 361 362 // Native 363 src_edge_bounds_in_native_ = 364 CreateVerticalEdgeBoundsInNative(src_root, src_indicator_bounds_); 365 dst_edge_bounds_in_native_ = 366 CreateVerticalEdgeBoundsInNative(dst_root, dst_indicator_bounds_); 367} 368 369void MouseCursorEventFilter::GetSrcAndDstRootWindows(aura::Window** src_root, 370 aura::Window** dst_root) { 371 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 372 *src_root = drag_source_root_ ? drag_source_root_ 373 : Shell::GetInstance()->GetPrimaryRootWindow(); 374 *dst_root = root_windows[0] == *src_root ? root_windows[1] : root_windows[0]; 375} 376 377#if !defined(USE_OZONE) 378bool MouseCursorEventFilter::WarpMouseCursorIfNecessaryForTest( 379 aura::Window* target_root, 380 const gfx::Point& point_in_screen) { 381 gfx::Point native = point_in_screen; 382 wm::ConvertPointFromScreen(target_root, &native); 383 target_root->GetHost()->ConvertPointToNativeScreen(&native); 384 return WarpMouseCursorInNativeCoords(native, point_in_screen); 385} 386#endif 387 388} // namespace ash 389