// Copyright 2015 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/extended_mouse_warp_controller.h" #include #include "ash/display/display_manager.h" #include "ash/display/display_util.h" #include "ash/display/shared_display_edge_indicator.h" #include "ash/root_window_controller.h" #include "ash/screen_util.h" #include "ash/shell.h" #include "ui/aura/window.h" #include "ui/events/event_utils.h" #include "ui/gfx/screen.h" #include "ui/wm/core/coordinate_conversion.h" namespace ash { 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; } // namespace ExtendedMouseWarpController::ExtendedMouseWarpController( aura::Window* drag_source) : drag_source_root_(drag_source), shared_display_edge_indicator_(new SharedDisplayEdgeIndicator), allow_non_native_event_(false) { DisplayLayout::Position position = Shell::GetInstance() ->display_manager() ->GetCurrentDisplayLayout() .position; // TODO(oshima): Use ComputeBondary instead. if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) UpdateHorizontalEdgeBounds(); else UpdateVerticalEdgeBounds(); if (drag_source) { shared_display_edge_indicator_->Show(src_indicator_bounds_, dst_indicator_bounds_); } } ExtendedMouseWarpController::~ExtendedMouseWarpController() { } bool ExtendedMouseWarpController::WarpMouseCursor(ui::MouseEvent* event) { if (Shell::GetScreen()->GetNumDisplays() <= 1 || !enabled_) return false; aura::Window* target = static_cast(event->target()); gfx::Point point_in_screen = event->location(); ::wm::ConvertPointToScreen(target, &point_in_screen); // A native event may not exist in unit test. Generate the native point // from the screen point instead. if (!event->HasNativeEvent()) { if (!allow_non_native_event_) return false; aura::Window* target_root = target->GetRootWindow(); gfx::Point point_in_native = point_in_screen; ::wm::ConvertPointFromScreen(target_root, &point_in_native); target_root->GetHost()->ConvertPointToNativeScreen(&point_in_native); return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen, true); } gfx::Point point_in_native = ui::EventSystemLocationFromNative(event->native_event()); #if defined(USE_OZONE) // TODO(dnicoara): crbug.com/415680 Move cursor warping into Ozone once Ozone // has access to the logical display layout. // Native events in Ozone are in the native window coordinate system. We need // to translate them to get the global position. point_in_native.Offset(target->GetHost()->GetBounds().x(), target->GetHost()->GetBounds().y()); #endif return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen, false); } void ExtendedMouseWarpController::SetEnabled(bool enabled) { enabled_ = enabled; } bool ExtendedMouseWarpController::WarpMouseCursorInNativeCoords( const gfx::Point& point_in_native, const gfx::Point& point_in_screen, bool update_mouse_location_now) { bool in_src_edge = src_edge_bounds_in_native_.Contains(point_in_native); bool in_dst_edge = dst_edge_bounds_in_native_.Contains(point_in_native); if (!in_src_edge && !in_dst_edge) return false; // The mouse must move. aura::Window* src_root = nullptr; aura::Window* dst_root = nullptr; GetSrcAndDstRootWindows(&src_root, &dst_root); AshWindowTreeHost* target_ash_host = GetRootWindowController(in_src_edge ? dst_root : src_root)->ash_host(); MoveCursorTo(target_ash_host, point_in_screen, update_mouse_location_now); return true; } void ExtendedMouseWarpController::UpdateHorizontalEdgeBounds() { 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 = ScreenUtil::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)); aura::Window* src_root = nullptr; aura::Window* dst_root = nullptr; GetSrcAndDstRootWindows(&src_root, &dst_root); src_edge_bounds_in_native_ = GetNativeEdgeBounds( GetRootWindowController(src_root)->ash_host(), src_indicator_bounds_); dst_edge_bounds_in_native_ = GetNativeEdgeBounds( GetRootWindowController(dst_root)->ash_host(), dst_indicator_bounds_); } void ExtendedMouseWarpController::UpdateVerticalEdgeBounds() { int snap_height = drag_source_root_ ? kMaximumSnapHeight : 0; 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 = ScreenUtil::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() + snap_height; 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); aura::Window* src_root = nullptr; aura::Window* dst_root = nullptr; GetSrcAndDstRootWindows(&src_root, &dst_root); // Native src_edge_bounds_in_native_ = GetNativeEdgeBounds( GetRootWindowController(src_root)->ash_host(), src_indicator_bounds_); dst_edge_bounds_in_native_ = GetNativeEdgeBounds( GetRootWindowController(dst_root)->ash_host(), dst_indicator_bounds_); } void ExtendedMouseWarpController::GetSrcAndDstRootWindows( aura::Window** src_root, aura::Window** dst_root) { aura::Window::Windows root_windows = Shell::GetAllRootWindows(); *src_root = drag_source_root_ ? drag_source_root_ : Shell::GetInstance()->GetPrimaryRootWindow(); *dst_root = root_windows[0] == *src_root ? root_windows[1] : root_windows[0]; } } // namespace ash