mouse_cursor_event_filter.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/cursor_window_controller.h" 8#include "ash/display/display_controller.h" 9#include "ash/display/display_manager.h" 10#include "ash/display/shared_display_edge_indicator.h" 11#include "ash/root_window_controller.h" 12#include "ash/screen_util.h" 13#include "ash/shell.h" 14#include "ash/wm/coordinate_conversion.h" 15#include "ash/wm/window_util.h" 16#include "ui/aura/env.h" 17#include "ui/aura/window.h" 18#include "ui/aura/window_event_dispatcher.h" 19#include "ui/base/layout.h" 20#include "ui/compositor/dip_util.h" 21#include "ui/events/event.h" 22#include "ui/gfx/screen.h" 23 24namespace ash { 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 was_mouse_warped_(false), 42 drag_source_root_(NULL), 43 scale_when_drag_started_(1.0f), 44 shared_display_edge_indicator_(new SharedDisplayEdgeIndicator) { 45} 46 47MouseCursorEventFilter::~MouseCursorEventFilter() { 48 HideSharedEdgeIndicator(); 49} 50 51void MouseCursorEventFilter::ShowSharedEdgeIndicator( 52 const aura::Window* from) { 53 HideSharedEdgeIndicator(); 54 if (Shell::GetScreen()->GetNumDisplays() <= 1 || from == NULL) { 55 src_indicator_bounds_.SetRect(0, 0, 0, 0); 56 dst_indicator_bounds_.SetRect(0, 0, 0, 0); 57 drag_source_root_ = NULL; 58 return; 59 } 60 drag_source_root_ = from; 61 62 DisplayLayout::Position position = Shell::GetInstance()-> 63 display_manager()->GetCurrentDisplayLayout().position; 64 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) 65 UpdateHorizontalIndicatorWindowBounds(); 66 else 67 UpdateVerticalIndicatorWindowBounds(); 68 69 shared_display_edge_indicator_->Show(src_indicator_bounds_, 70 dst_indicator_bounds_); 71} 72 73void MouseCursorEventFilter::HideSharedEdgeIndicator() { 74 shared_display_edge_indicator_->Hide(); 75} 76 77void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) { 78 aura::Window* target = static_cast<aura::Window*>(event->target()); 79 RootWindowController* rwc = GetRootWindowController(target->GetRootWindow()); 80 // The root window controller is removed during the shutting down 81 // RootWindow, so don't process events futher. 82 if (!rwc) { 83 event->StopPropagation(); 84 return; 85 } 86 87 if (event->type() == ui::ET_MOUSE_PRESSED) { 88 scale_when_drag_started_ = ui::GetDeviceScaleFactor(target->layer()); 89 } else if (event->type() == ui::ET_MOUSE_RELEASED) { 90 scale_when_drag_started_ = 1.0f; 91 } 92 93 // Handle both MOVED and DRAGGED events here because when the mouse pointer 94 // enters the other root window while dragging, the underlying window system 95 // (at least X11) stops generating a ui::ET_MOUSE_MOVED event. 96 if (event->type() != ui::ET_MOUSE_MOVED && 97 event->type() != ui::ET_MOUSE_DRAGGED) { 98 return; 99 } 100 101 if (!(event->flags() & ui::EF_IS_SYNTHESIZED)) { 102 Shell::GetInstance()->display_controller()-> 103 cursor_window_controller()->UpdateLocation(); 104 } 105 106 gfx::Point point_in_screen(event->location()); 107 wm::ConvertPointToScreen(target, &point_in_screen); 108 if (WarpMouseCursorIfNecessary(target->GetRootWindow(), point_in_screen)) 109 event->StopPropagation(); 110} 111 112bool MouseCursorEventFilter::WarpMouseCursorIfNecessary( 113 aura::Window* target_root, 114 const gfx::Point& point_in_screen) { 115 if (Shell::GetScreen()->GetNumDisplays() <= 1 || 116 mouse_warp_mode_ == WARP_NONE) 117 return false; 118 119 // Do not warp again right after the cursor was warped. Sometimes the offset 120 // is not long enough and the cursor moves at the edge of the destination 121 // display. See crbug.com/278885 122 // TODO(mukai): simplify the offset calculation below, it would not be 123 // necessary anymore with this flag. 124 if (was_mouse_warped_) { 125 was_mouse_warped_ = false; 126 return false; 127 } 128 129 aura::Window* root_at_point = wm::GetRootWindowAt(point_in_screen); 130 gfx::Point point_in_root = point_in_screen; 131 wm::ConvertPointFromScreen(root_at_point, &point_in_root); 132 gfx::Rect root_bounds = root_at_point->bounds(); 133 int offset_x = 0; 134 int offset_y = 0; 135 136 // If the window is dragged between 2x display and 1x display, 137 // staring from 2x display, pointer location is rounded by the 138 // source scale factor (2x) so it will never reach the edge (which 139 // is odd). Shrink by scale factor of the display where the dragging 140 // started instead. Only integral scale factor is supported for now. 141 int shrink = scale_when_drag_started_; 142 // Make the bounds inclusive to detect the edge. 143 root_bounds.Inset(0, 0, shrink, shrink); 144 gfx::Rect src_indicator_bounds = src_indicator_bounds_; 145 src_indicator_bounds.Inset(-shrink, -shrink, -shrink, -shrink); 146 147 if (point_in_root.x() <= root_bounds.x()) { 148 // Use -2, not -1, to avoid infinite loop of pointer warp. 149 offset_x = -2 * scale_when_drag_started_; 150 } else if (point_in_root.x() >= root_bounds.right()) { 151 offset_x = 2 * scale_when_drag_started_; 152 } else if (point_in_root.y() <= root_bounds.y()) { 153 offset_y = -2 * scale_when_drag_started_; 154 } else if (point_in_root.y() >= root_bounds.bottom()) { 155 offset_y = 2 * scale_when_drag_started_; 156 } else { 157 return false; 158 } 159 160 gfx::Point point_in_dst_screen(point_in_screen); 161 point_in_dst_screen.Offset(offset_x, offset_y); 162 aura::Window* dst_root = wm::GetRootWindowAt(point_in_dst_screen); 163 164 // Warp the mouse cursor only if the location is in the indicator bounds 165 // or the mouse pointer is in the destination root. 166 if (mouse_warp_mode_ == WARP_DRAG && 167 dst_root != drag_source_root_ && 168 !src_indicator_bounds.Contains(point_in_screen)) { 169 return false; 170 } 171 172 wm::ConvertPointFromScreen(dst_root, &point_in_dst_screen); 173 174 if (dst_root->bounds().Contains(point_in_dst_screen)) { 175 DCHECK_NE(dst_root, root_at_point); 176 was_mouse_warped_ = true; 177 dst_root->MoveCursorTo(point_in_dst_screen); 178 return true; 179 } 180 return false; 181} 182 183void MouseCursorEventFilter::UpdateHorizontalIndicatorWindowBounds() { 184 bool from_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 = ScreenUtil::GetSecondaryDisplay().bounds(); 190 DisplayLayout::Position position = Shell::GetInstance()-> 191 display_manager()->GetCurrentDisplayLayout().position; 192 193 src_indicator_bounds_.set_x( 194 std::max(primary_bounds.x(), secondary_bounds.x())); 195 src_indicator_bounds_.set_width( 196 std::min(primary_bounds.right(), secondary_bounds.right()) - 197 src_indicator_bounds_.x()); 198 src_indicator_bounds_.set_height(kIndicatorThickness); 199 src_indicator_bounds_.set_y( 200 position == DisplayLayout::TOP ? 201 primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) : 202 primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0)); 203 204 dst_indicator_bounds_ = src_indicator_bounds_; 205 dst_indicator_bounds_.set_height(kIndicatorThickness); 206 dst_indicator_bounds_.set_y( 207 position == DisplayLayout::TOP ? 208 primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) : 209 primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness)); 210} 211 212void MouseCursorEventFilter::UpdateVerticalIndicatorWindowBounds() { 213 bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_; 214 // GetPrimaryDisplay returns an object on stack, so copy the bounds 215 // instead of using reference. 216 const gfx::Rect primary_bounds = 217 Shell::GetScreen()->GetPrimaryDisplay().bounds(); 218 const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds(); 219 DisplayLayout::Position position = Shell::GetInstance()-> 220 display_manager()->GetCurrentDisplayLayout().position; 221 222 int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y()); 223 int lower_shared_y = std::min(primary_bounds.bottom(), 224 secondary_bounds.bottom()); 225 int shared_height = lower_shared_y - upper_shared_y; 226 227 int dst_x = position == DisplayLayout::LEFT ? 228 primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) : 229 primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness); 230 dst_indicator_bounds_.SetRect( 231 dst_x, upper_shared_y, kIndicatorThickness, shared_height); 232 233 // The indicator on the source display. 234 src_indicator_bounds_.set_width(kIndicatorThickness); 235 src_indicator_bounds_.set_x( 236 position == DisplayLayout::LEFT ? 237 primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) : 238 primary_bounds.right() - (in_primary ? kIndicatorThickness : 0)); 239 240 const gfx::Rect& source_bounds = 241 in_primary ? primary_bounds : secondary_bounds; 242 int upper_indicator_y = source_bounds.y() + kMaximumSnapHeight; 243 int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y); 244 245 // This gives a hight that can be used without sacrifying the snap space. 246 int available_space = lower_indicator_y - 247 std::max(upper_shared_y, upper_indicator_y); 248 249 if (shared_height < kMinimumIndicatorHeight) { 250 // If the shared height is smaller than minimum height, use the 251 // entire height. 252 upper_indicator_y = upper_shared_y; 253 } else if (available_space < kMinimumIndicatorHeight) { 254 // Snap to the bottom. 255 upper_indicator_y = 256 std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight); 257 } else { 258 upper_indicator_y = std::max(upper_indicator_y, upper_shared_y); 259 } 260 src_indicator_bounds_.set_y(upper_indicator_y); 261 src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y); 262} 263 264} // namespace ash 265