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