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