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) || defined(USE_OZONE)
     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     // For now Ozone works in a similar manner as X11. Transitioning from one
     94     // display's coordinate system to anothers may cause events in the
     95     // primary's coordinate system which fall in the extended display.
     96 
     97     gfx::Point location_in_native(location_in_root);
     98     root_window->GetHost()->ConvertPointToNativeScreen(&location_in_native);
     99 
    100     aura::Window::Windows root_windows = Shell::GetAllRootWindows();
    101     for (size_t i = 0; i < root_windows.size(); ++i) {
    102       aura::WindowTreeHost* host = root_windows[i]->GetHost();
    103       const gfx::Rect native_bounds = host->GetBounds();
    104       if (native_bounds.Contains(location_in_native)) {
    105         root_window = root_windows[i];
    106         location_in_root = location_in_native;
    107         host->ConvertPointFromNativeScreen(&location_in_root);
    108         break;
    109       }
    110     }
    111   }
    112 #else
    113   // TODO(yusukes): Support non-X11 platforms if necessary.
    114   NOTIMPLEMENTED();
    115 #endif
    116 
    117   return std::make_pair(root_window, location_in_root);
    118 }
    119 
    120 }  // namespace
    121 
    122 void ScreenPositionController::ConvertPointToScreen(
    123     const aura::Window* window,
    124     gfx::Point* point) {
    125   const aura::Window* root = window->GetRootWindow();
    126   aura::Window::ConvertPointToTarget(window, root, point);
    127   const gfx::Point display_origin = Shell::GetScreen()->GetDisplayNearestWindow(
    128       const_cast<aura::Window*>(root)).bounds().origin();
    129   point->Offset(display_origin.x(), display_origin.y());
    130 }
    131 
    132 void ScreenPositionController::ConvertPointFromScreen(
    133     const aura::Window* window,
    134     gfx::Point* point) {
    135   const aura::Window* root = window->GetRootWindow();
    136   const gfx::Point display_origin = Shell::GetScreen()->GetDisplayNearestWindow(
    137       const_cast<aura::Window*>(root)).bounds().origin();
    138   point->Offset(-display_origin.x(), -display_origin.y());
    139   aura::Window::ConvertPointToTarget(root, window, point);
    140 }
    141 
    142 void ScreenPositionController::ConvertHostPointToScreen(
    143     aura::Window* root_window,
    144     gfx::Point* point) {
    145   aura::Window* root = root_window->GetRootWindow();
    146   root->GetHost()->ConvertPointFromHost(point);
    147   std::pair<aura::Window*, gfx::Point> pair =
    148       GetRootWindowRelativeToWindow(root, *point);
    149   *point = pair.second;
    150   ConvertPointToScreen(pair.first, point);
    151 }
    152 
    153 void ScreenPositionController::SetBounds(aura::Window* window,
    154                                          const gfx::Rect& bounds,
    155                                          const gfx::Display& display) {
    156   DCHECK_NE(-1, display.id());
    157   if (!window->parent()->GetProperty(kUsesScreenCoordinatesKey)) {
    158     window->SetBounds(bounds);
    159     return;
    160   }
    161 
    162   // Don't move a window to other root window if:
    163   // a) the window is a transient window. It moves when its
    164   //    transient_parent moves.
    165   // b) if the window or its ancestor has kStayInSameRootWindowkey. It's
    166   //    intentionally kept in the same root window even if the bounds is
    167   //    outside of the display.
    168   if (!::wm::GetTransientParent(window) &&
    169       !ShouldStayInSameRootWindow(window)) {
    170     aura::Window* dst_root =
    171         Shell::GetInstance()->display_controller()->GetRootWindowForDisplayId(
    172             display.id());
    173     DCHECK(dst_root);
    174     aura::Window* dst_container = NULL;
    175     if (dst_root != window->GetRootWindow()) {
    176       int container_id = window->parent()->id();
    177       // All containers that uses screen coordinates must have valid window ids.
    178       DCHECK_GE(container_id, 0);
    179       // Don't move modal background.
    180       if (!SystemModalContainerLayoutManager::IsModalBackground(window))
    181         dst_container = Shell::GetContainer(dst_root, container_id);
    182     }
    183 
    184     if (dst_container && window->parent() != dst_container) {
    185       aura::Window* focused = aura::client::GetFocusClient(window)->
    186           GetFocusedWindow();
    187       aura::client::ActivationClient* activation_client =
    188           aura::client::GetActivationClient(window->GetRootWindow());
    189       aura::Window* active = activation_client->GetActiveWindow();
    190 
    191       aura::WindowTracker tracker;
    192       if (focused)
    193         tracker.Add(focused);
    194       if (active && focused != active)
    195         tracker.Add(active);
    196 
    197       // Set new bounds now so that the container's layout manager
    198       // can adjust the bounds if necessary.
    199       gfx::Point origin = bounds.origin();
    200       const gfx::Point display_origin = display.bounds().origin();
    201       origin.Offset(-display_origin.x(), -display_origin.y());
    202       gfx::Rect new_bounds = gfx::Rect(origin, bounds.size());
    203 
    204       window->SetBounds(new_bounds);
    205 
    206       dst_container->AddChild(window);
    207 
    208       MoveAllTransientChildrenToNewRoot(display, window);
    209 
    210       // Restore focused/active window.
    211       if (tracker.Contains(focused)) {
    212         aura::client::GetFocusClient(window)->FocusWindow(focused);
    213         // TODO(beng): replace with GetRootWindow().
    214         ash::Shell::GetInstance()->set_target_root_window(
    215             focused->GetRootWindow());
    216       } else if (tracker.Contains(active)) {
    217         activation_client->ActivateWindow(active);
    218       }
    219       // TODO(oshima): We should not have to update the bounds again
    220       // below in theory, but we currently do need as there is a code
    221       // that assumes that the bounds will never be overridden by the
    222       // layout mananger. We should have more explicit control how
    223       // constraints are applied by the layout manager.
    224     }
    225   }
    226   gfx::Point origin(bounds.origin());
    227   const gfx::Point display_origin = Shell::GetScreen()->GetDisplayNearestWindow(
    228       window).bounds().origin();
    229   origin.Offset(-display_origin.x(), -display_origin.y());
    230   window->SetBounds(gfx::Rect(origin, bounds.size()));
    231 }
    232 
    233 }  // namespace ash
    234