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