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.
5 #include "ash/display/mouse_cursor_event_filter.h"
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/window_util.h"
18 #include "ui/aura/env.h"
19 #include "ui/aura/window.h"
20 #include "ui/aura/window_event_dispatcher.h"
21 #include "ui/aura/window_tree_host.h"
22 #include "ui/base/layout.h"
23 #include "ui/compositor/dip_util.h"
24 #include "ui/events/event.h"
25 #include "ui/events/event_utils.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/wm/core/coordinate_conversion.h"
32 // Maximum size on the display edge that initiate snapping phantom window,
33 // from the corner of the display.
34 const int kMaximumSnapHeight = 16;
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.
39 const int kMinimumIndicatorHeight = 200;
41 const int kIndicatorThickness = 1;
43 void ConvertPointFromScreenToNative(const aura::Window* root_window,
45 ::wm::ConvertPointFromScreen(root_window, point);
46 root_window->GetHost()->ConvertPointToNativeScreen(point);
49 gfx::Rect GetNativeEdgeBounds(const aura::Window* root_window,
52 gfx::Rect native_bounds = root_window->GetHost()->GetBounds();
54 GetRootWindowController(root_window)->ash_host()->GetHostInsets());
56 ConvertPointFromScreenToNative(root_window, &start);
57 ConvertPointFromScreenToNative(root_window, &end);
58 if (start.x() == end.x()) {
60 int x = std::abs(native_bounds.x() - start.x()) <
61 std::abs(native_bounds.right() - start.x())
63 : native_bounds.right() - 1;
65 x, std::min(start.y(), end.y()), 1, std::abs(start.y() - end.y()));
67 // horizontal in native
68 int y = std::abs(native_bounds.y() - start.y()) <
69 std::abs(native_bounds.bottom() - start.y())
71 : native_bounds.bottom() - 1;
73 std::min(start.x(), end.x()), y, std::abs(start.x() - end.x()), 1);
77 // Creates edge bounds from indicator bounds that fits the edge
78 // of the native window for |root_window|.
79 gfx::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);
87 gfx::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);
96 void 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());
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());
111 MouseCursorEventFilter::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);
120 MouseCursorEventFilter::~MouseCursorEventFilter() {
121 HideSharedEdgeIndicator();
122 Shell::GetInstance()->display_controller()->RemoveObserver(this);
125 void 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;
133 drag_source_root_ = from;
135 DisplayLayout::Position position = Shell::GetInstance()->
136 display_manager()->GetCurrentDisplayLayout().position;
137 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
138 UpdateHorizontalEdgeBounds();
140 UpdateVerticalEdgeBounds();
142 shared_display_edge_indicator_->Show(src_indicator_bounds_,
143 dst_indicator_bounds_);
146 void MouseCursorEventFilter::HideSharedEdgeIndicator() {
147 shared_display_edge_indicator_->Hide();
148 OnDisplayConfigurationChanged();
151 void MouseCursorEventFilter::OnDisplaysInitialized() {
152 OnDisplayConfigurationChanged();
155 void MouseCursorEventFilter::OnDisplayConfigurationChanged() {
156 // Extra check for |num_connected_displays()| is for SystemDisplayApiTest
157 // that injects MockScreen.
158 if (Shell::GetScreen()->GetNumDisplays() <= 1 ||
159 Shell::GetInstance()->display_manager()->num_connected_displays() <= 1) {
160 src_edge_bounds_in_native_.SetRect(0, 0, 0, 0);
161 dst_edge_bounds_in_native_.SetRect(0, 0, 0, 0);
165 drag_source_root_ = NULL;
166 DisplayLayout::Position position = Shell::GetInstance()
168 ->GetCurrentDisplayLayout()
171 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
172 UpdateHorizontalEdgeBounds();
174 UpdateVerticalEdgeBounds();
177 void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) {
178 aura::Window* target = static_cast<aura::Window*>(event->target());
180 if (event->type() == ui::ET_MOUSE_PRESSED) {
181 scale_when_drag_started_ = ui::GetDeviceScaleFactor(target->layer());
182 } else if (event->type() == ui::ET_MOUSE_RELEASED) {
183 scale_when_drag_started_ = 1.0f;
186 // Handle both MOVED and DRAGGED events here because when the mouse pointer
187 // enters the other root window while dragging, the underlying window system
188 // (at least X11) stops generating a ui::ET_MOUSE_MOVED event.
189 if (event->type() != ui::ET_MOUSE_MOVED &&
190 event->type() != ui::ET_MOUSE_DRAGGED) {
194 Shell::GetInstance()->display_controller()->
195 cursor_window_controller()->UpdateLocation();
197 if (WarpMouseCursorIfNecessary(event))
198 event->StopPropagation();
202 void MouseCursorEventFilter::MoveCursorTo(aura::Window* root,
203 const gfx::Point& point_in_screen) {
204 gfx::Point point_in_native = point_in_screen;
205 ::wm::ConvertPointFromScreen(root, &point_in_native);
206 root->GetHost()->ConvertPointToNativeScreen(&point_in_native);
208 // now fit the point inside the native bounds.
209 gfx::Rect native_bounds = root->GetHost()->GetBounds();
210 gfx::Point native_origin = native_bounds.origin();
212 GetRootWindowController(root)->ash_host()->GetHostInsets());
213 // Shrink further so that the mouse doesn't warp on the
214 // edge. The right/bottom needs to be shrink by 2 to subtract
215 // the 1 px from width/height value.
216 native_bounds.Inset(1, 1, 2, 2);
218 MovePointInside(native_bounds, &point_in_native);
219 gfx::Point point_in_host = point_in_native;
221 point_in_host.Offset(-native_origin.x(), -native_origin.y());
222 root->GetHost()->MoveCursorToHostLocation(point_in_host);
225 bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(ui::MouseEvent* event) {
226 if (!event->HasNativeEvent())
229 gfx::Point point_in_native =
230 ui::EventSystemLocationFromNative(event->native_event());
232 aura::Window* target = static_cast<aura::Window*>(event->target());
233 #if defined(USE_OZONE)
234 // TODO(dnicoara): crbug.com/415680 Move cursor warping into Ozone once Ozone
235 // has access to the logical display layout.
236 // Native events in Ozone are in the native window coordinate system. We need
237 // to translate them to get the global position.
238 point_in_native.Offset(target->GetHost()->GetBounds().x(),
239 target->GetHost()->GetBounds().y());
241 gfx::Point point_in_screen = event->location();
242 ::wm::ConvertPointToScreen(target, &point_in_screen);
244 return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen);
247 bool MouseCursorEventFilter::WarpMouseCursorInNativeCoords(
248 const gfx::Point& point_in_native,
249 const gfx::Point& point_in_screen) {
250 if (Shell::GetScreen()->GetNumDisplays() <= 1 ||
251 mouse_warp_mode_ == WARP_NONE)
254 bool in_src_edge = src_edge_bounds_in_native_.Contains(point_in_native);
255 bool in_dst_edge = dst_edge_bounds_in_native_.Contains(point_in_native);
256 if (!in_src_edge && !in_dst_edge)
259 // The mouse must move.
260 aura::Window* src_root = NULL;
261 aura::Window* dst_root = NULL;
262 GetSrcAndDstRootWindows(&src_root, &dst_root);
265 MoveCursorTo(dst_root, point_in_screen);
267 MoveCursorTo(src_root, point_in_screen);
272 void MouseCursorEventFilter::UpdateHorizontalEdgeBounds() {
273 bool from_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
274 // GetPrimaryDisplay returns an object on stack, so copy the bounds
275 // instead of using reference.
276 const gfx::Rect primary_bounds =
277 Shell::GetScreen()->GetPrimaryDisplay().bounds();
278 const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds();
279 DisplayLayout::Position position = Shell::GetInstance()->
280 display_manager()->GetCurrentDisplayLayout().position;
282 src_indicator_bounds_.set_x(
283 std::max(primary_bounds.x(), secondary_bounds.x()));
284 src_indicator_bounds_.set_width(
285 std::min(primary_bounds.right(), secondary_bounds.right()) -
286 src_indicator_bounds_.x());
287 src_indicator_bounds_.set_height(kIndicatorThickness);
288 src_indicator_bounds_.set_y(
289 position == DisplayLayout::TOP ?
290 primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) :
291 primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0));
293 dst_indicator_bounds_ = src_indicator_bounds_;
294 dst_indicator_bounds_.set_height(kIndicatorThickness);
295 dst_indicator_bounds_.set_y(
296 position == DisplayLayout::TOP ?
297 primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) :
298 primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness));
300 aura::Window* src_root = NULL;
301 aura::Window* dst_root = NULL;
302 GetSrcAndDstRootWindows(&src_root, &dst_root);
304 src_edge_bounds_in_native_ =
305 CreateHorizontalEdgeBoundsInNative(src_root, src_indicator_bounds_);
306 dst_edge_bounds_in_native_ =
307 CreateHorizontalEdgeBoundsInNative(dst_root, dst_indicator_bounds_);
310 void MouseCursorEventFilter::UpdateVerticalEdgeBounds() {
311 int snap_height = drag_source_root_ ? kMaximumSnapHeight : 0;
312 bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
313 // GetPrimaryDisplay returns an object on stack, so copy the bounds
314 // instead of using reference.
315 const gfx::Rect primary_bounds =
316 Shell::GetScreen()->GetPrimaryDisplay().bounds();
317 const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds();
318 DisplayLayout::Position position = Shell::GetInstance()->
319 display_manager()->GetCurrentDisplayLayout().position;
321 int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y());
322 int lower_shared_y = std::min(primary_bounds.bottom(),
323 secondary_bounds.bottom());
324 int shared_height = lower_shared_y - upper_shared_y;
326 int dst_x = position == DisplayLayout::LEFT ?
327 primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) :
328 primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness);
329 dst_indicator_bounds_.SetRect(
330 dst_x, upper_shared_y, kIndicatorThickness, shared_height);
332 // The indicator on the source display.
333 src_indicator_bounds_.set_width(kIndicatorThickness);
334 src_indicator_bounds_.set_x(
335 position == DisplayLayout::LEFT ?
336 primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) :
337 primary_bounds.right() - (in_primary ? kIndicatorThickness : 0));
339 const gfx::Rect& source_bounds =
340 in_primary ? primary_bounds : secondary_bounds;
341 int upper_indicator_y = source_bounds.y() + snap_height;
342 int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y);
344 // This gives a hight that can be used without sacrifying the snap space.
345 int available_space = lower_indicator_y -
346 std::max(upper_shared_y, upper_indicator_y);
348 if (shared_height < kMinimumIndicatorHeight) {
349 // If the shared height is smaller than minimum height, use the
351 upper_indicator_y = upper_shared_y;
352 } else if (available_space < kMinimumIndicatorHeight) {
353 // Snap to the bottom.
355 std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight);
357 upper_indicator_y = std::max(upper_indicator_y, upper_shared_y);
359 src_indicator_bounds_.set_y(upper_indicator_y);
360 src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y);
362 aura::Window* src_root = NULL;
363 aura::Window* dst_root = NULL;
364 GetSrcAndDstRootWindows(&src_root, &dst_root);
367 src_edge_bounds_in_native_ =
368 CreateVerticalEdgeBoundsInNative(src_root, src_indicator_bounds_);
369 dst_edge_bounds_in_native_ =
370 CreateVerticalEdgeBoundsInNative(dst_root, dst_indicator_bounds_);
373 void MouseCursorEventFilter::GetSrcAndDstRootWindows(aura::Window** src_root,
374 aura::Window** dst_root) {
375 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
376 *src_root = drag_source_root_ ? drag_source_root_
377 : Shell::GetInstance()->GetPrimaryRootWindow();
378 *dst_root = root_windows[0] == *src_root ? root_windows[1] : root_windows[0];
381 bool MouseCursorEventFilter::WarpMouseCursorIfNecessaryForTest(
382 aura::Window* target_root,
383 const gfx::Point& point_in_screen) {
384 gfx::Point native = point_in_screen;
385 ::wm::ConvertPointFromScreen(target_root, &native);
386 target_root->GetHost()->ConvertPointToNativeScreen(&native);
387 return WarpMouseCursorInNativeCoords(native, point_in_screen);