1 // Copyright 2015 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/extended_mouse_warp_controller.h"
9 #include "ash/display/display_manager.h"
10 #include "ash/display/display_util.h"
11 #include "ash/display/shared_display_edge_indicator.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/screen_util.h"
14 #include "ash/shell.h"
15 #include "ui/aura/window.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/gfx/screen.h"
18 #include "ui/wm/core/coordinate_conversion.h"
24 // Maximum size on the display edge that initiate snapping phantom window,
25 // from the corner of the display.
26 const int kMaximumSnapHeight
= 16;
28 // Minimum height of an indicator on the display edge that allows
29 // dragging a window. If two displays shares the edge smaller than
30 // this, entire edge will be used as a draggable space.
31 const int kMinimumIndicatorHeight
= 200;
33 const int kIndicatorThickness
= 1;
37 ExtendedMouseWarpController::ExtendedMouseWarpController(
38 aura::Window
* drag_source
)
39 : drag_source_root_(drag_source
),
40 shared_display_edge_indicator_(new SharedDisplayEdgeIndicator
),
41 allow_non_native_event_(false) {
42 DisplayLayout::Position position
= Shell::GetInstance()
44 ->GetCurrentDisplayLayout()
46 // TODO(oshima): Use ComputeBondary instead.
47 if (position
== DisplayLayout::TOP
|| position
== DisplayLayout::BOTTOM
)
48 UpdateHorizontalEdgeBounds();
50 UpdateVerticalEdgeBounds();
52 shared_display_edge_indicator_
->Show(src_indicator_bounds_
,
53 dst_indicator_bounds_
);
57 ExtendedMouseWarpController::~ExtendedMouseWarpController() {
60 bool ExtendedMouseWarpController::WarpMouseCursor(ui::MouseEvent
* event
) {
61 if (Shell::GetScreen()->GetNumDisplays() <= 1 || !enabled_
)
64 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
65 gfx::Point point_in_screen
= event
->location();
66 ::wm::ConvertPointToScreen(target
, &point_in_screen
);
68 // A native event may not exist in unit test. Generate the native point
69 // from the screen point instead.
70 if (!event
->HasNativeEvent()) {
71 if (!allow_non_native_event_
)
73 aura::Window
* target_root
= target
->GetRootWindow();
74 gfx::Point point_in_native
= point_in_screen
;
75 ::wm::ConvertPointFromScreen(target_root
, &point_in_native
);
76 target_root
->GetHost()->ConvertPointToNativeScreen(&point_in_native
);
77 return WarpMouseCursorInNativeCoords(point_in_native
, point_in_screen
,
81 gfx::Point point_in_native
=
82 ui::EventSystemLocationFromNative(event
->native_event());
84 #if defined(USE_OZONE)
85 // TODO(dnicoara): crbug.com/415680 Move cursor warping into Ozone once Ozone
86 // has access to the logical display layout.
87 // Native events in Ozone are in the native window coordinate system. We need
88 // to translate them to get the global position.
89 point_in_native
.Offset(target
->GetHost()->GetBounds().x(),
90 target
->GetHost()->GetBounds().y());
93 return WarpMouseCursorInNativeCoords(point_in_native
, point_in_screen
, false);
96 void ExtendedMouseWarpController::SetEnabled(bool enabled
) {
100 bool ExtendedMouseWarpController::WarpMouseCursorInNativeCoords(
101 const gfx::Point
& point_in_native
,
102 const gfx::Point
& point_in_screen
,
103 bool update_mouse_location_now
) {
104 bool in_src_edge
= src_edge_bounds_in_native_
.Contains(point_in_native
);
105 bool in_dst_edge
= dst_edge_bounds_in_native_
.Contains(point_in_native
);
106 if (!in_src_edge
&& !in_dst_edge
)
109 // The mouse must move.
110 aura::Window
* src_root
= nullptr;
111 aura::Window
* dst_root
= nullptr;
112 GetSrcAndDstRootWindows(&src_root
, &dst_root
);
113 AshWindowTreeHost
* target_ash_host
=
114 GetRootWindowController(in_src_edge
? dst_root
: src_root
)->ash_host();
116 MoveCursorTo(target_ash_host
, point_in_screen
, update_mouse_location_now
);
120 void ExtendedMouseWarpController::UpdateHorizontalEdgeBounds() {
121 bool from_primary
= Shell::GetPrimaryRootWindow() == drag_source_root_
;
122 // GetPrimaryDisplay returns an object on stack, so copy the bounds
123 // instead of using reference.
124 const gfx::Rect primary_bounds
=
125 Shell::GetScreen()->GetPrimaryDisplay().bounds();
126 const gfx::Rect secondary_bounds
= ScreenUtil::GetSecondaryDisplay().bounds();
127 DisplayLayout::Position position
= Shell::GetInstance()
129 ->GetCurrentDisplayLayout()
132 src_indicator_bounds_
.set_x(
133 std::max(primary_bounds
.x(), secondary_bounds
.x()));
134 src_indicator_bounds_
.set_width(
135 std::min(primary_bounds
.right(), secondary_bounds
.right()) -
136 src_indicator_bounds_
.x());
137 src_indicator_bounds_
.set_height(kIndicatorThickness
);
138 src_indicator_bounds_
.set_y(
139 position
== DisplayLayout::TOP
140 ? primary_bounds
.y() - (from_primary
? 0 : kIndicatorThickness
)
141 : primary_bounds
.bottom() - (from_primary
? kIndicatorThickness
: 0));
143 dst_indicator_bounds_
= src_indicator_bounds_
;
144 dst_indicator_bounds_
.set_height(kIndicatorThickness
);
145 dst_indicator_bounds_
.set_y(
146 position
== DisplayLayout::TOP
147 ? primary_bounds
.y() - (from_primary
? kIndicatorThickness
: 0)
148 : primary_bounds
.bottom() - (from_primary
? 0 : kIndicatorThickness
));
150 aura::Window
* src_root
= nullptr;
151 aura::Window
* dst_root
= nullptr;
152 GetSrcAndDstRootWindows(&src_root
, &dst_root
);
154 src_edge_bounds_in_native_
= GetNativeEdgeBounds(
155 GetRootWindowController(src_root
)->ash_host(), src_indicator_bounds_
);
156 dst_edge_bounds_in_native_
= GetNativeEdgeBounds(
157 GetRootWindowController(dst_root
)->ash_host(), dst_indicator_bounds_
);
160 void ExtendedMouseWarpController::UpdateVerticalEdgeBounds() {
161 int snap_height
= drag_source_root_
? kMaximumSnapHeight
: 0;
162 bool in_primary
= Shell::GetPrimaryRootWindow() == drag_source_root_
;
163 // GetPrimaryDisplay returns an object on stack, so copy the bounds
164 // instead of using reference.
165 const gfx::Rect primary_bounds
=
166 Shell::GetScreen()->GetPrimaryDisplay().bounds();
167 const gfx::Rect secondary_bounds
= ScreenUtil::GetSecondaryDisplay().bounds();
168 DisplayLayout::Position position
= Shell::GetInstance()
170 ->GetCurrentDisplayLayout()
173 int upper_shared_y
= std::max(primary_bounds
.y(), secondary_bounds
.y());
175 std::min(primary_bounds
.bottom(), secondary_bounds
.bottom());
176 int shared_height
= lower_shared_y
- upper_shared_y
;
179 position
== DisplayLayout::LEFT
180 ? primary_bounds
.x() - (in_primary
? kIndicatorThickness
: 0)
181 : primary_bounds
.right() - (in_primary
? 0 : kIndicatorThickness
);
182 dst_indicator_bounds_
.SetRect(dst_x
, upper_shared_y
, kIndicatorThickness
,
185 // The indicator on the source display.
186 src_indicator_bounds_
.set_width(kIndicatorThickness
);
187 src_indicator_bounds_
.set_x(
188 position
== DisplayLayout::LEFT
189 ? primary_bounds
.x() - (in_primary
? 0 : kIndicatorThickness
)
190 : primary_bounds
.right() - (in_primary
? kIndicatorThickness
: 0));
192 const gfx::Rect
& source_bounds
=
193 in_primary
? primary_bounds
: secondary_bounds
;
194 int upper_indicator_y
= source_bounds
.y() + snap_height
;
195 int lower_indicator_y
= std::min(source_bounds
.bottom(), lower_shared_y
);
197 // This gives a hight that can be used without sacrifying the snap space.
198 int available_space
=
199 lower_indicator_y
- std::max(upper_shared_y
, upper_indicator_y
);
201 if (shared_height
< kMinimumIndicatorHeight
) {
202 // If the shared height is smaller than minimum height, use the
204 upper_indicator_y
= upper_shared_y
;
205 } else if (available_space
< kMinimumIndicatorHeight
) {
206 // Snap to the bottom.
208 std::max(upper_shared_y
, lower_indicator_y
+ kMinimumIndicatorHeight
);
210 upper_indicator_y
= std::max(upper_indicator_y
, upper_shared_y
);
212 src_indicator_bounds_
.set_y(upper_indicator_y
);
213 src_indicator_bounds_
.set_height(lower_indicator_y
- upper_indicator_y
);
215 aura::Window
* src_root
= nullptr;
216 aura::Window
* dst_root
= nullptr;
217 GetSrcAndDstRootWindows(&src_root
, &dst_root
);
220 src_edge_bounds_in_native_
= GetNativeEdgeBounds(
221 GetRootWindowController(src_root
)->ash_host(), src_indicator_bounds_
);
222 dst_edge_bounds_in_native_
= GetNativeEdgeBounds(
223 GetRootWindowController(dst_root
)->ash_host(), dst_indicator_bounds_
);
226 void ExtendedMouseWarpController::GetSrcAndDstRootWindows(
227 aura::Window
** src_root
,
228 aura::Window
** dst_root
) {
229 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
230 *src_root
= drag_source_root_
? drag_source_root_
231 : Shell::GetInstance()->GetPrimaryRootWindow();
232 *dst_root
= root_windows
[0] == *src_root
? root_windows
[1] : root_windows
[0];