Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / ash / display / mouse_cursor_event_filter.cc
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 <cmath>
8
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"
28
29 namespace ash {
30 namespace {
31
32 // Maximum size on the display edge that initiate snapping phantom window,
33 // from the corner of the display.
34 const int kMaximumSnapHeight = 16;
35
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;
40
41 const int kIndicatorThickness = 1;
42
43 void ConvertPointFromScreenToNative(const aura::Window* root_window,
44                                     gfx::Point* point) {
45   ::wm::ConvertPointFromScreen(root_window, point);
46   root_window->GetHost()->ConvertPointToNativeScreen(point);
47 }
48
49 gfx::Rect GetNativeEdgeBounds(const aura::Window* root_window,
50                               gfx::Point start,
51                               gfx::Point end) {
52   gfx::Rect native_bounds = root_window->GetHost()->GetBounds();
53   native_bounds.Inset(
54       GetRootWindowController(root_window)->ash_host()->GetHostInsets());
55
56   ConvertPointFromScreenToNative(root_window, &start);
57   ConvertPointFromScreenToNative(root_window, &end);
58   if (start.x() == end.x()) {
59     // vertical in native
60     int x = std::abs(native_bounds.x() - start.x()) <
61                     std::abs(native_bounds.right() - start.x())
62                 ? native_bounds.x()
63                 : native_bounds.right() - 1;
64     return gfx::Rect(
65         x, std::min(start.y(), end.y()), 1, std::abs(start.y() - end.y()));
66   } else {
67     // horizontal in native
68     int y = std::abs(native_bounds.y() - start.y()) <
69                     std::abs(native_bounds.bottom() - start.y())
70                 ? native_bounds.y()
71                 : native_bounds.bottom() - 1;
72     return gfx::Rect(
73         std::min(start.x(), end.x()), y, std::abs(start.x() - end.x()), 1);
74   }
75 }
76
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);
85 }
86
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);
94 }
95
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());
102
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());
107 }
108
109 }  // namespace
110
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);
118 }
119
120 MouseCursorEventFilter::~MouseCursorEventFilter() {
121   HideSharedEdgeIndicator();
122   Shell::GetInstance()->display_controller()->RemoveObserver(this);
123 }
124
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;
131     return;
132   }
133   drag_source_root_ = from;
134
135   DisplayLayout::Position position = Shell::GetInstance()->
136       display_manager()->GetCurrentDisplayLayout().position;
137   if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
138     UpdateHorizontalEdgeBounds();
139   else
140     UpdateVerticalEdgeBounds();
141
142   shared_display_edge_indicator_->Show(src_indicator_bounds_,
143                                        dst_indicator_bounds_);
144 }
145
146 void MouseCursorEventFilter::HideSharedEdgeIndicator() {
147   shared_display_edge_indicator_->Hide();
148   OnDisplayConfigurationChanged();
149 }
150
151 void MouseCursorEventFilter::OnDisplaysInitialized() {
152   OnDisplayConfigurationChanged();
153 }
154
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);
162     return;
163   }
164
165   drag_source_root_ = NULL;
166   DisplayLayout::Position position = Shell::GetInstance()
167                                          ->display_manager()
168                                          ->GetCurrentDisplayLayout()
169                                          .position;
170
171   if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
172     UpdateHorizontalEdgeBounds();
173   else
174     UpdateVerticalEdgeBounds();
175 }
176
177 void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) {
178   aura::Window* target = static_cast<aura::Window*>(event->target());
179
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;
184   }
185
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) {
191       return;
192   }
193
194   Shell::GetInstance()->display_controller()->
195       cursor_window_controller()->UpdateLocation();
196
197   if (WarpMouseCursorIfNecessary(event))
198     event->StopPropagation();
199 }
200
201 // static
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);
207
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();
211   native_bounds.Inset(
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);
217
218   MovePointInside(native_bounds, &point_in_native);
219   gfx::Point point_in_host = point_in_native;
220
221   point_in_host.Offset(-native_origin.x(), -native_origin.y());
222   root->GetHost()->MoveCursorToHostLocation(point_in_host);
223 }
224
225 bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(ui::MouseEvent* event) {
226   if (!event->HasNativeEvent())
227     return false;
228
229   gfx::Point point_in_native =
230       ui::EventSystemLocationFromNative(event->native_event());
231
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());
240 #endif
241   gfx::Point point_in_screen = event->location();
242   ::wm::ConvertPointToScreen(target, &point_in_screen);
243
244   return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen);
245 }
246
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)
252     return false;
253
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)
257     return false;
258
259   // The mouse must move.
260   aura::Window* src_root = NULL;
261   aura::Window* dst_root = NULL;
262   GetSrcAndDstRootWindows(&src_root, &dst_root);
263
264   if (in_src_edge)
265     MoveCursorTo(dst_root, point_in_screen);
266   else
267     MoveCursorTo(src_root, point_in_screen);
268
269   return true;
270 }
271
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;
281
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));
292
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));
299
300   aura::Window* src_root = NULL;
301   aura::Window* dst_root = NULL;
302   GetSrcAndDstRootWindows(&src_root, &dst_root);
303
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_);
308 }
309
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;
320
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;
325
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);
331
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));
338
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);
343
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);
347
348   if (shared_height < kMinimumIndicatorHeight) {
349     // If the shared height is smaller than minimum height, use the
350     // entire height.
351     upper_indicator_y = upper_shared_y;
352   } else if (available_space < kMinimumIndicatorHeight) {
353     // Snap to the bottom.
354     upper_indicator_y =
355         std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight);
356   } else {
357     upper_indicator_y = std::max(upper_indicator_y, upper_shared_y);
358   }
359   src_indicator_bounds_.set_y(upper_indicator_y);
360   src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y);
361
362   aura::Window* src_root = NULL;
363   aura::Window* dst_root = NULL;
364   GetSrcAndDstRootWindows(&src_root, &dst_root);
365
366   // Native
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_);
371 }
372
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];
379 }
380
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);
388 }
389
390 }  // namespace ash