Home | History | Annotate | Download | only in display
      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/screen_position_controller.h"
      6 
      7 #include "ash/display/display_controller.h"
      8 #include "ash/root_window_controller.h"
      9 #include "ash/shell.h"
     10 #include "ash/shell_window_ids.h"
     11 #include "ash/wm/coordinate_conversion.h"
     12 #include "ash/wm/system_modal_container_layout_manager.h"
     13 #include "ash/wm/window_properties.h"
     14 #include "ui/aura/client/activation_client.h"
     15 #include "ui/aura/client/capture_client.h"
     16 #include "ui/aura/client/focus_client.h"
     17 #include "ui/aura/root_window.h"
     18 #include "ui/aura/window_tracker.h"
     19 #include "ui/compositor/dip_util.h"
     20 #include "ui/gfx/display.h"
     21 #include "ui/gfx/screen.h"
     22 
     23 namespace ash {
     24 namespace {
     25 
     26 // Return true if the window or its ancestor has |kStayInSameRootWindowkey|
     27 // property.
     28 bool ShouldStayInSameRootWindow(const aura::Window* window) {
     29   return window &&
     30       (window->GetProperty(internal::kStayInSameRootWindowKey) ||
     31        ShouldStayInSameRootWindow(window->parent()));
     32 }
     33 
     34 // Move all transient children to |dst_root|, including the ones in
     35 // the child windows and transient children of the transient children.
     36 void MoveAllTransientChildrenToNewRoot(const gfx::Display& display,
     37                                        aura::Window* window) {
     38   aura::Window* dst_root = Shell::GetInstance()->display_controller()->
     39       GetRootWindowForDisplayId(display.id());
     40   aura::Window::Windows transient_children = window->transient_children();
     41   for (aura::Window::Windows::iterator iter = transient_children.begin();
     42        iter != transient_children.end(); ++iter) {
     43     aura::Window* transient_child = *iter;
     44     int container_id = transient_child->parent()->id();
     45     DCHECK_GE(container_id, 0);
     46     aura::Window* container = Shell::GetContainer(dst_root, container_id);
     47     gfx::Rect parent_bounds_in_screen = transient_child->GetBoundsInScreen();
     48     container->AddChild(transient_child);
     49     transient_child->SetBoundsInScreen(parent_bounds_in_screen, display);
     50 
     51     // Transient children may have transient children.
     52     MoveAllTransientChildrenToNewRoot(display, transient_child);
     53   }
     54   // Move transient children of the child windows if any.
     55   aura::Window::Windows children = window->children();
     56   for (aura::Window::Windows::iterator iter = children.begin();
     57        iter != children.end(); ++iter)
     58     MoveAllTransientChildrenToNewRoot(display, *iter);
     59 }
     60 
     61 // Finds the root window at |location| in |window|'s coordinates and returns a
     62 // pair of root window and location in that root window's coordinates. The
     63 // function usually returns |window->GetRootWindow()|, but if the mouse pointer
     64 // is moved outside the |window|'s root while the mouse is captured, it returns
     65 // the other root window.
     66 std::pair<aura::Window*, gfx::Point> GetRootWindowRelativeToWindow(
     67     aura::Window* window,
     68     const gfx::Point& location) {
     69   aura::Window* root_window = window->GetRootWindow();
     70   gfx::Point location_in_root(location);
     71   aura::Window::ConvertPointToTarget(window, root_window, &location_in_root);
     72 
     73 #if defined(USE_X11)
     74   if (!root_window->ContainsPointInRoot(location_in_root)) {
     75     // This conversion is necessary to deal with X's passive input
     76     // grab while dragging window. For example, if we have two
     77     // displays, say 1000x1000 (primary) and 500x500 (extended one
     78     // on the right), and start dragging a window at (999, 123), and
     79     // then move the pointer to the right, the pointer suddenly
     80     // warps to the extended display. The destination is (0, 123) in
     81     // the secondary root window's coordinates, or (1000, 123) in
     82     // the screen coordinates. However, since the mouse is captured
     83     // by X during drag, a weird LocatedEvent, something like (0, 1123)
     84     // in the *primary* root window's coordinates, is sent to Chrome
     85     // (Remember that in the native X11 world, the two root windows
     86     // are always stacked vertically regardless of the display
     87     // layout in Ash). We need to figure out that (0, 1123) in the
     88     // primary root window's coordinates is actually (0, 123) in the
     89     // extended root window's coordinates.
     90 
     91     gfx::Point location_in_native(location_in_root);
     92     root_window->GetDispatcher()->host()->ConvertPointToNativeScreen(
     93         &location_in_native);
     94 
     95     aura::Window::Windows root_windows = Shell::GetAllRootWindows();
     96     for (size_t i = 0; i < root_windows.size(); ++i) {
     97       aura::WindowEventDispatcher* dispatcher =
     98           root_windows[i]->GetDispatcher();
     99       const gfx::Rect native_bounds = dispatcher->host()->GetBounds();
    100       if (native_bounds.Contains(location_in_native)) {
    101         root_window = root_windows[i];
    102         location_in_root = location_in_native;
    103         dispatcher->host()->ConvertPointFromNativeScreen(&location_in_root);
    104         break;
    105       }
    106     }
    107   }
    108 #else
    109   // TODO(yusukes): Support non-X11 platforms if necessary.
    110 #endif
    111 
    112   return std::make_pair(root_window, location_in_root);
    113 }
    114 
    115 }  // namespace
    116 
    117 namespace internal {
    118 
    119 void ScreenPositionController::ConvertPointToScreen(
    120     const aura::Window* window,
    121     gfx::Point* point) {
    122   const aura::Window* root = window->GetRootWindow();
    123   aura::Window::ConvertPointToTarget(window, root, point);
    124   const gfx::Point display_origin = Shell::GetScreen()->GetDisplayNearestWindow(
    125       const_cast<aura::Window*>(root)).bounds().origin();
    126   point->Offset(display_origin.x(), display_origin.y());
    127 }
    128 
    129 void ScreenPositionController::ConvertPointFromScreen(
    130     const aura::Window* window,
    131     gfx::Point* point) {
    132   const aura::Window* root = window->GetRootWindow();
    133   const gfx::Point display_origin = Shell::GetScreen()->GetDisplayNearestWindow(
    134       const_cast<aura::Window*>(root)).bounds().origin();
    135   point->Offset(-display_origin.x(), -display_origin.y());
    136   aura::Window::ConvertPointToTarget(root, window, point);
    137 }
    138 
    139 void ScreenPositionController::ConvertHostPointToScreen(
    140     aura::Window* root_window,
    141     gfx::Point* point) {
    142   aura::Window* root = root_window->GetRootWindow();
    143   root->GetDispatcher()->ConvertPointFromHost(point);
    144   std::pair<aura::Window*, gfx::Point> pair =
    145       GetRootWindowRelativeToWindow(root, *point);
    146   *point = pair.second;
    147   ConvertPointToScreen(pair.first, point);
    148 }
    149 
    150 void ScreenPositionController::SetBounds(aura::Window* window,
    151                                          const gfx::Rect& bounds,
    152                                          const gfx::Display& display) {
    153   DCHECK_NE(-1, display.id());
    154   if (!window->parent()->GetProperty(internal::kUsesScreenCoordinatesKey)) {
    155     window->SetBounds(bounds);
    156     return;
    157   }
    158 
    159   // Don't move a window to other root window if:
    160   // a) the window is a transient window. It moves when its
    161   //    transient_parent moves.
    162   // b) if the window or its ancestor has kStayInSameRootWindowkey. It's
    163   //    intentionally kept in the same root window even if the bounds is
    164   //    outside of the display.
    165   if (!window->transient_parent() &&
    166       !ShouldStayInSameRootWindow(window)) {
    167     aura::Window* dst_root =
    168         Shell::GetInstance()->display_controller()->GetRootWindowForDisplayId(
    169             display.id());
    170     DCHECK(dst_root);
    171     aura::Window* dst_container = NULL;
    172     if (dst_root != window->GetRootWindow()) {
    173       int container_id = window->parent()->id();
    174       // All containers that uses screen coordinates must have valid window ids.
    175       DCHECK_GE(container_id, 0);
    176       // Don't move modal background.
    177       if (!SystemModalContainerLayoutManager::IsModalBackground(window))
    178         dst_container = Shell::GetContainer(dst_root, container_id);
    179     }
    180 
    181     if (dst_container && window->parent() != dst_container) {
    182       aura::Window* focused = aura::client::GetFocusClient(window)->
    183           GetFocusedWindow();
    184       aura::client::ActivationClient* activation_client =
    185           aura::client::GetActivationClient(window->GetRootWindow());
    186       aura::Window* active = activation_client->GetActiveWindow();
    187 
    188       aura::WindowTracker tracker;
    189       if (focused)
    190         tracker.Add(focused);
    191       if (active && focused != active)
    192         tracker.Add(active);
    193 
    194       dst_container->AddChild(window);
    195 
    196       MoveAllTransientChildrenToNewRoot(display, window);
    197 
    198       // Restore focused/active window.
    199       if (tracker.Contains(focused)) {
    200         aura::client::GetFocusClient(window)->FocusWindow(focused);
    201         // TODO(beng): replace with GetRootWindow().
    202         ash::Shell::GetInstance()->set_target_root_window(
    203             focused->GetRootWindow());
    204       } else if (tracker.Contains(active)) {
    205         activation_client->ActivateWindow(active);
    206       }
    207     }
    208   }
    209 
    210   gfx::Point origin(bounds.origin());
    211   const gfx::Point display_origin = Shell::GetScreen()->GetDisplayNearestWindow(
    212       window).bounds().origin();
    213   origin.Offset(-display_origin.x(), -display_origin.y());
    214   window->SetBounds(gfx::Rect(origin, bounds.size()));
    215 }
    216 
    217 }  // internal
    218 }  // ash
    219