Upstream version 7.36.149.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 "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
24 namespace ash {
25 namespace {
26
27 // Maximum size on the display edge that initiate snapping phantom window,
28 // from the corner of the display.
29 const 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.
34 const int kMinimumIndicatorHeight = 200;
35
36 const int kIndicatorThickness = 1;
37 }
38
39 MouseCursorEventFilter::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
47 MouseCursorEventFilter::~MouseCursorEventFilter() {
48   HideSharedEdgeIndicator();
49 }
50
51 void 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
73 void MouseCursorEventFilter::HideSharedEdgeIndicator() {
74   shared_display_edge_indicator_->Hide();
75 }
76
77 void 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   Shell::GetInstance()->display_controller()->
102       cursor_window_controller()->UpdateLocation();
103
104   gfx::Point point_in_screen(event->location());
105   wm::ConvertPointToScreen(target, &point_in_screen);
106   if (WarpMouseCursorIfNecessary(target->GetRootWindow(), point_in_screen))
107     event->StopPropagation();
108 }
109
110 bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(
111     aura::Window* target_root,
112     const gfx::Point& point_in_screen) {
113   if (Shell::GetScreen()->GetNumDisplays() <= 1 ||
114       mouse_warp_mode_ == WARP_NONE)
115     return false;
116
117   // Do not warp again right after the cursor was warped. Sometimes the offset
118   // is not long enough and the cursor moves at the edge of the destination
119   // display. See crbug.com/278885
120   // TODO(mukai): simplify the offset calculation below, it would not be
121   // necessary anymore with this flag.
122   if (was_mouse_warped_) {
123     was_mouse_warped_ = false;
124     return false;
125   }
126
127   aura::Window* root_at_point = wm::GetRootWindowAt(point_in_screen);
128   gfx::Point point_in_root = point_in_screen;
129   wm::ConvertPointFromScreen(root_at_point, &point_in_root);
130   gfx::Rect root_bounds = root_at_point->bounds();
131   int offset_x = 0;
132   int offset_y = 0;
133
134   // If the window is dragged between 2x display and 1x display,
135   // staring from 2x display, pointer location is rounded by the
136   // source scale factor (2x) so it will never reach the edge (which
137   // is odd). Shrink by scale factor of the display where the dragging
138   // started instead.  Only integral scale factor is supported for now.
139   int shrink = scale_when_drag_started_;
140   // Make the bounds inclusive to detect the edge.
141   root_bounds.Inset(0, 0, shrink, shrink);
142   gfx::Rect src_indicator_bounds = src_indicator_bounds_;
143   src_indicator_bounds.Inset(-shrink, -shrink, -shrink, -shrink);
144
145   if (point_in_root.x() <= root_bounds.x()) {
146     // Use -2, not -1, to avoid infinite loop of pointer warp.
147     offset_x = -2 * scale_when_drag_started_;
148   } else if (point_in_root.x() >= root_bounds.right()) {
149     offset_x = 2 * scale_when_drag_started_;
150   } else if (point_in_root.y() <= root_bounds.y()) {
151     offset_y = -2 * scale_when_drag_started_;
152   } else if (point_in_root.y() >= root_bounds.bottom()) {
153     offset_y = 2 * scale_when_drag_started_;
154   } else {
155     return false;
156   }
157
158   gfx::Point point_in_dst_screen(point_in_screen);
159   point_in_dst_screen.Offset(offset_x, offset_y);
160   aura::Window* dst_root = wm::GetRootWindowAt(point_in_dst_screen);
161
162   // Warp the mouse cursor only if the location is in the indicator bounds
163   // or the mouse pointer is in the destination root.
164   if (mouse_warp_mode_ == WARP_DRAG &&
165       dst_root != drag_source_root_ &&
166       !src_indicator_bounds.Contains(point_in_screen)) {
167     return false;
168   }
169
170   wm::ConvertPointFromScreen(dst_root, &point_in_dst_screen);
171
172   if (dst_root->bounds().Contains(point_in_dst_screen)) {
173     DCHECK_NE(dst_root, root_at_point);
174     was_mouse_warped_ = true;
175     dst_root->MoveCursorTo(point_in_dst_screen);
176     return true;
177   }
178   return false;
179 }
180
181 void MouseCursorEventFilter::UpdateHorizontalIndicatorWindowBounds() {
182   bool from_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
183   // GetPrimaryDisplay returns an object on stack, so copy the bounds
184   // instead of using reference.
185   const gfx::Rect primary_bounds =
186       Shell::GetScreen()->GetPrimaryDisplay().bounds();
187   const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds();
188   DisplayLayout::Position position = Shell::GetInstance()->
189       display_manager()->GetCurrentDisplayLayout().position;
190
191   src_indicator_bounds_.set_x(
192       std::max(primary_bounds.x(), secondary_bounds.x()));
193   src_indicator_bounds_.set_width(
194       std::min(primary_bounds.right(), secondary_bounds.right()) -
195       src_indicator_bounds_.x());
196   src_indicator_bounds_.set_height(kIndicatorThickness);
197   src_indicator_bounds_.set_y(
198       position == DisplayLayout::TOP ?
199       primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) :
200       primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0));
201
202   dst_indicator_bounds_ = src_indicator_bounds_;
203   dst_indicator_bounds_.set_height(kIndicatorThickness);
204   dst_indicator_bounds_.set_y(
205       position == DisplayLayout::TOP ?
206       primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) :
207       primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness));
208 }
209
210 void MouseCursorEventFilter::UpdateVerticalIndicatorWindowBounds() {
211   bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
212   // GetPrimaryDisplay returns an object on stack, so copy the bounds
213   // instead of using reference.
214   const gfx::Rect primary_bounds =
215       Shell::GetScreen()->GetPrimaryDisplay().bounds();
216   const gfx::Rect secondary_bounds = ScreenUtil::GetSecondaryDisplay().bounds();
217   DisplayLayout::Position position = Shell::GetInstance()->
218       display_manager()->GetCurrentDisplayLayout().position;
219
220   int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y());
221   int lower_shared_y = std::min(primary_bounds.bottom(),
222                                 secondary_bounds.bottom());
223   int shared_height = lower_shared_y - upper_shared_y;
224
225   int dst_x = position == DisplayLayout::LEFT ?
226       primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) :
227       primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness);
228   dst_indicator_bounds_.SetRect(
229       dst_x, upper_shared_y, kIndicatorThickness, shared_height);
230
231   // The indicator on the source display.
232   src_indicator_bounds_.set_width(kIndicatorThickness);
233   src_indicator_bounds_.set_x(
234       position == DisplayLayout::LEFT ?
235       primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) :
236       primary_bounds.right() - (in_primary ? kIndicatorThickness : 0));
237
238   const gfx::Rect& source_bounds =
239       in_primary ? primary_bounds : secondary_bounds;
240   int upper_indicator_y = source_bounds.y() + kMaximumSnapHeight;
241   int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y);
242
243   // This gives a hight that can be used without sacrifying the snap space.
244   int available_space = lower_indicator_y -
245       std::max(upper_shared_y, upper_indicator_y);
246
247   if (shared_height < kMinimumIndicatorHeight) {
248     // If the shared height is smaller than minimum height, use the
249     // entire height.
250     upper_indicator_y = upper_shared_y;
251   } else if (available_space < kMinimumIndicatorHeight) {
252     // Snap to the bottom.
253     upper_indicator_y =
254         std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight);
255   } else {
256     upper_indicator_y = std::max(upper_indicator_y, upper_shared_y);
257   }
258   src_indicator_bounds_.set_y(upper_indicator_y);
259   src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y);
260 }
261
262 }  // namespace ash