summaryrefslogtreecommitdiff
path: root/chromium/ash/display/mouse_cursor_event_filter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ash/display/mouse_cursor_event_filter.cc')
-rw-r--r--chromium/ash/display/mouse_cursor_event_filter.cc255
1 files changed, 255 insertions, 0 deletions
diff --git a/chromium/ash/display/mouse_cursor_event_filter.cc b/chromium/ash/display/mouse_cursor_event_filter.cc
new file mode 100644
index 00000000000..ebac900a891
--- /dev/null
+++ b/chromium/ash/display/mouse_cursor_event_filter.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/display/mouse_cursor_event_filter.h"
+
+#include "ash/display/display_controller.h"
+#include "ash/display/display_manager.h"
+#include "ash/display/mirror_window_controller.h"
+#include "ash/display/shared_display_edge_indicator.h"
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/wm/coordinate_conversion.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/base/layout.h"
+#include "ui/compositor/dip_util.h"
+#include "ui/events/event.h"
+#include "ui/gfx/screen.h"
+
+namespace ash {
+namespace internal {
+namespace {
+
+// Maximum size on the display edge that initiate snapping phantom window,
+// from the corner of the display.
+const int kMaximumSnapHeight = 16;
+
+// Minimum height of an indicator on the display edge that allows
+// dragging a window. If two displays shares the edge smaller than
+// this, entire edge will be used as a draggable space.
+const int kMinimumIndicatorHeight = 200;
+
+const int kIndicatorThickness = 1;
+}
+
+MouseCursorEventFilter::MouseCursorEventFilter()
+ : mouse_warp_mode_(WARP_ALWAYS),
+ was_mouse_warped_(false),
+ drag_source_root_(NULL),
+ scale_when_drag_started_(1.0f),
+ shared_display_edge_indicator_(new SharedDisplayEdgeIndicator) {
+}
+
+MouseCursorEventFilter::~MouseCursorEventFilter() {
+ HideSharedEdgeIndicator();
+}
+
+void MouseCursorEventFilter::ShowSharedEdgeIndicator(
+ const aura::Window* from) {
+ HideSharedEdgeIndicator();
+ if (Shell::GetScreen()->GetNumDisplays() <= 1 || from == NULL) {
+ src_indicator_bounds_.SetRect(0, 0, 0, 0);
+ dst_indicator_bounds_.SetRect(0, 0, 0, 0);
+ drag_source_root_ = NULL;
+ return;
+ }
+ drag_source_root_ = from;
+
+ DisplayLayout::Position position = Shell::GetInstance()->
+ display_manager()->GetCurrentDisplayLayout().position;
+ if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM)
+ UpdateHorizontalIndicatorWindowBounds();
+ else
+ UpdateVerticalIndicatorWindowBounds();
+
+ shared_display_edge_indicator_->Show(src_indicator_bounds_,
+ dst_indicator_bounds_);
+}
+
+void MouseCursorEventFilter::HideSharedEdgeIndicator() {
+ shared_display_edge_indicator_->Hide();
+}
+
+void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) {
+ if (event->type() == ui::ET_MOUSE_PRESSED) {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ scale_when_drag_started_ = ui::GetDeviceScaleFactor(target->layer());
+ } else if (event->type() == ui::ET_MOUSE_RELEASED) {
+ scale_when_drag_started_ = 1.0f;
+ }
+
+ // Handle both MOVED and DRAGGED events here because when the mouse pointer
+ // enters the other root window while dragging, the underlying window system
+ // (at least X11) stops generating a ui::ET_MOUSE_MOVED event.
+ if (event->type() != ui::ET_MOUSE_MOVED &&
+ event->type() != ui::ET_MOUSE_DRAGGED) {
+ return;
+ }
+ Shell::GetInstance()->display_controller()->
+ mirror_window_controller()->UpdateCursorLocation();
+
+ gfx::Point point_in_screen(event->location());
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ wm::ConvertPointToScreen(target, &point_in_screen);
+ if (WarpMouseCursorIfNecessary(target->GetRootWindow(), point_in_screen))
+ event->StopPropagation();
+}
+
+bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(
+ aura::Window* target_root,
+ const gfx::Point& point_in_screen) {
+ if (Shell::GetScreen()->GetNumDisplays() <= 1 ||
+ mouse_warp_mode_ == WARP_NONE)
+ return false;
+
+ // Do not warp again right after the cursor was warped. Sometimes the offset
+ // is not long enough and the cursor moves at the edge of the destination
+ // display. See crbug.com/278885
+ // TODO(mukai): simplify the offset calculation below, it would not be
+ // necessary anymore with this flag.
+ if (was_mouse_warped_) {
+ was_mouse_warped_ = false;
+ return false;
+ }
+
+ aura::Window* root_at_point = wm::GetRootWindowAt(point_in_screen);
+ gfx::Point point_in_root = point_in_screen;
+ wm::ConvertPointFromScreen(root_at_point, &point_in_root);
+ gfx::Rect root_bounds = root_at_point->bounds();
+ int offset_x = 0;
+ int offset_y = 0;
+
+ // If the window is dragged between 2x display and 1x display,
+ // staring from 2x display, pointer location is rounded by the
+ // source scale factor (2x) so it will never reach the edge (which
+ // is odd). Shrink by scale factor of the display where the dragging
+ // started instead. Only integral scale factor is supported for now.
+ int shrink = scale_when_drag_started_;
+ // Make the bounds inclusive to detect the edge.
+ root_bounds.Inset(0, 0, shrink, shrink);
+ gfx::Rect src_indicator_bounds = src_indicator_bounds_;
+ src_indicator_bounds.Inset(-shrink, -shrink, -shrink, -shrink);
+
+ if (point_in_root.x() <= root_bounds.x()) {
+ // Use -2, not -1, to avoid infinite loop of pointer warp.
+ offset_x = -2 * scale_when_drag_started_;
+ } else if (point_in_root.x() >= root_bounds.right()) {
+ offset_x = 2 * scale_when_drag_started_;
+ } else if (point_in_root.y() <= root_bounds.y()) {
+ offset_y = -2 * scale_when_drag_started_;
+ } else if (point_in_root.y() >= root_bounds.bottom()) {
+ offset_y = 2 * scale_when_drag_started_;
+ } else {
+ return false;
+ }
+
+ gfx::Point point_in_dst_screen(point_in_screen);
+ point_in_dst_screen.Offset(offset_x, offset_y);
+ aura::Window* dst_root = wm::GetRootWindowAt(point_in_dst_screen);
+
+ // Warp the mouse cursor only if the location is in the indicator bounds
+ // or the mouse pointer is in the destination root.
+ if (mouse_warp_mode_ == WARP_DRAG &&
+ dst_root != drag_source_root_ &&
+ !src_indicator_bounds.Contains(point_in_screen)) {
+ return false;
+ }
+
+ wm::ConvertPointFromScreen(dst_root, &point_in_dst_screen);
+
+ if (dst_root->bounds().Contains(point_in_dst_screen)) {
+ DCHECK_NE(dst_root, root_at_point);
+ was_mouse_warped_ = true;
+ dst_root->MoveCursorTo(point_in_dst_screen);
+ return true;
+ }
+ return false;
+}
+
+void MouseCursorEventFilter::UpdateHorizontalIndicatorWindowBounds() {
+ bool from_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
+ // GetPrimaryDisplay returns an object on stack, so copy the bounds
+ // instead of using reference.
+ const gfx::Rect primary_bounds =
+ Shell::GetScreen()->GetPrimaryDisplay().bounds();
+ const gfx::Rect secondary_bounds = ScreenAsh::GetSecondaryDisplay().bounds();
+ DisplayLayout::Position position = Shell::GetInstance()->
+ display_manager()->GetCurrentDisplayLayout().position;
+
+ src_indicator_bounds_.set_x(
+ std::max(primary_bounds.x(), secondary_bounds.x()));
+ src_indicator_bounds_.set_width(
+ std::min(primary_bounds.right(), secondary_bounds.right()) -
+ src_indicator_bounds_.x());
+ src_indicator_bounds_.set_height(kIndicatorThickness);
+ src_indicator_bounds_.set_y(
+ position == DisplayLayout::TOP ?
+ primary_bounds.y() - (from_primary ? 0 : kIndicatorThickness) :
+ primary_bounds.bottom() - (from_primary ? kIndicatorThickness : 0));
+
+ dst_indicator_bounds_ = src_indicator_bounds_;
+ dst_indicator_bounds_.set_height(kIndicatorThickness);
+ dst_indicator_bounds_.set_y(
+ position == DisplayLayout::TOP ?
+ primary_bounds.y() - (from_primary ? kIndicatorThickness : 0) :
+ primary_bounds.bottom() - (from_primary ? 0 : kIndicatorThickness));
+}
+
+void MouseCursorEventFilter::UpdateVerticalIndicatorWindowBounds() {
+ bool in_primary = Shell::GetPrimaryRootWindow() == drag_source_root_;
+ // GetPrimaryDisplay returns an object on stack, so copy the bounds
+ // instead of using reference.
+ const gfx::Rect primary_bounds =
+ Shell::GetScreen()->GetPrimaryDisplay().bounds();
+ const gfx::Rect secondary_bounds = ScreenAsh::GetSecondaryDisplay().bounds();
+ DisplayLayout::Position position = Shell::GetInstance()->
+ display_manager()->GetCurrentDisplayLayout().position;
+
+ int upper_shared_y = std::max(primary_bounds.y(), secondary_bounds.y());
+ int lower_shared_y = std::min(primary_bounds.bottom(),
+ secondary_bounds.bottom());
+ int shared_height = lower_shared_y - upper_shared_y;
+
+ int dst_x = position == DisplayLayout::LEFT ?
+ primary_bounds.x() - (in_primary ? kIndicatorThickness : 0) :
+ primary_bounds.right() - (in_primary ? 0 : kIndicatorThickness);
+ dst_indicator_bounds_.SetRect(
+ dst_x, upper_shared_y, kIndicatorThickness, shared_height);
+
+ // The indicator on the source display.
+ src_indicator_bounds_.set_width(kIndicatorThickness);
+ src_indicator_bounds_.set_x(
+ position == DisplayLayout::LEFT ?
+ primary_bounds.x() - (in_primary ? 0 : kIndicatorThickness) :
+ primary_bounds.right() - (in_primary ? kIndicatorThickness : 0));
+
+ const gfx::Rect& source_bounds =
+ in_primary ? primary_bounds : secondary_bounds;
+ int upper_indicator_y = source_bounds.y() + kMaximumSnapHeight;
+ int lower_indicator_y = std::min(source_bounds.bottom(), lower_shared_y);
+
+ // This gives a hight that can be used without sacrifying the snap space.
+ int available_space = lower_indicator_y -
+ std::max(upper_shared_y, upper_indicator_y);
+
+ if (shared_height < kMinimumIndicatorHeight) {
+ // If the shared height is smaller than minimum height, use the
+ // entire height.
+ upper_indicator_y = upper_shared_y;
+ } else if (available_space < kMinimumIndicatorHeight) {
+ // Snap to the bottom.
+ upper_indicator_y =
+ std::max(upper_shared_y, lower_indicator_y + kMinimumIndicatorHeight);
+ } else {
+ upper_indicator_y = std::max(upper_indicator_y, upper_shared_y);
+ }
+ src_indicator_bounds_.set_y(upper_indicator_y);
+ src_indicator_bounds_.set_height(lower_indicator_y - upper_indicator_y);
+}
+
+} // namespace internal
+} // namespace ash