desktop_screen_x11.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/views/widget/desktop_aura/desktop_screen_x11.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <X11/extensions/Xrandr.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <X11/Xlib.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// It clashes with out RootWindow.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef RootWindow
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
1468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "base/x11/edid_parser_x11.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/root_window.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/root_window_host.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/x/x11_util.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/display.h"
1968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/gfx/display_observer.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/native_widget_types.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
2268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/gfx/x/x11_types.h"
2368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/views/widget/desktop_aura/desktop_screen.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// The delay to perform configuration after RRNotify.  See the comment
2868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// in |Dispatch()|.
2968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)const int64 kConfigureDelayMs = 500;
3068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
3168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)std::vector<gfx::Display> GetFallbackDisplayList() {
3268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  ::XDisplay* display = gfx::GetXDisplay();
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::Screen* screen = DefaultScreenOfDisplay(display);
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width = WidthOfScreen(screen);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int height = HeightOfScreen(screen);
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return std::vector<gfx::Display>(
3868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      1, gfx::Display(0, gfx::Rect(0, 0, width, height)));
3968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
4068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
4168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}  // namespace
4268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
4368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)namespace views {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// DesktopScreenX11, public:
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)DesktopScreenX11::DesktopScreenX11()
4968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
5068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      x_root_window_(DefaultRootWindow(xdisplay_)),
5168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      has_xrandr_(false),
5268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      xrandr_event_base_(0) {
5368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // We only support 1.3+. There were library changes before this and we should
5468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // use the new interface instead of the 1.2 one.
5568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  int randr_version_major = 0;
5668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  int randr_version_minor = 0;
5768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  has_xrandr_ = XRRQueryVersion(
5868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        xdisplay_, &randr_version_major, &randr_version_minor) &&
5968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      randr_version_major == 1 &&
6068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      randr_version_minor >= 3;
6168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
6268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (has_xrandr_) {
6368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    int error_base_ignored = 0;
6468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
6568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
6668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
6768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    XRRSelectInput(xdisplay_,
6868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                   x_root_window_,
6968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                   RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
7068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
7168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    displays_ = BuildDisplaysFromXRandRInfo();
7268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  } else {
7368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    displays_ = GetFallbackDisplayList();
7468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DesktopScreenX11::~DesktopScreenX11() {
7868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (has_xrandr_)
7968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
8068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
8168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
8268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void DesktopScreenX11::ProcessDisplayChange(
8368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const std::vector<gfx::Display>& incoming) {
8468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  std::vector<gfx::Display>::const_iterator cur_it = displays_.begin();
8568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  for (; cur_it != displays_.end(); ++cur_it) {
8668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    bool found = false;
8768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    for (std::vector<gfx::Display>::const_iterator incoming_it =
8868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)             incoming.begin(); incoming_it != incoming.end(); ++incoming_it) {
8968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      if (cur_it->id() == incoming_it->id()) {
9068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        found = true;
9168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        break;
9268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      }
9368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
9468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
9568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!found) {
9668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
9768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                        OnDisplayRemoved(*cur_it));
9868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
9968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
10068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
10168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  std::vector<gfx::Display>::const_iterator incoming_it = incoming.begin();
10268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  for (; incoming_it != incoming.end(); ++incoming_it) {
10368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    bool found = false;
10468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    for (std::vector<gfx::Display>::const_iterator cur_it = displays_.begin();
10568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)         cur_it != displays_.end(); ++cur_it) {
10668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      if (incoming_it->id() == cur_it->id()) {
10768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        if (incoming_it->bounds() != cur_it->bounds()) {
10868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
10968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                            OnDisplayBoundsChanged(*incoming_it));
11068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
11168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
11268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        found = true;
11368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        break;
11468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      }
11568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
11668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
11768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!found) {
11868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
11968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                        OnDisplayAdded(*incoming_it));
12068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
12168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
12268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
12368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  displays_ = incoming;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// DesktopScreenX11, gfx::Screen implementation:
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool DesktopScreenX11::IsDIPEnabled() {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
13468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  XDisplay* display = gfx::GetXDisplay();
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::Window root, child;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int root_x, root_y, win_x, win_y;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned int mask;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  XQueryPointer(display,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                DefaultRootWindow(display),
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &root,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &child,
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &root_x,
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &root_y,
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &win_x,
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &win_y,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &mask);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Point(root_x, root_y);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
152424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
15368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return GetWindowAtScreenPoint(GetCursorScreenPoint());
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
156424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
157424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const gfx::Point& point) {
15868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // TODO(erg): Implement using the discussion at
15968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // http://codereview.chromium.org/10279005/
160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  NOTIMPLEMENTED();
161424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  return NULL;
162424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
163424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)int DesktopScreenX11::GetNumDisplays() const {
16568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return displays_.size();
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
168424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
16968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return displays_;
170424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
171424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::NativeView window) const {
17468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // TODO(erg): This should theoretically be easy, but it isn't. At the time we
17568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // get called here, our aura::Window has not been Init()ed, because this
17668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // method is called to get the device scale factor as part of
17768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // RootWindow::Init(), before Window::Init(). This seems very confused; we're
17868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // trying to get a display nearest window even before we've allocated the
17968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // root window. Once fixed, the correct implementation should probably be:
18068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  //
18168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  //   return GetDisplayMatching(window->GetBoundsInScreen());
18268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  //
18368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // But at least for now, we'll just fallback:
18468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return GetPrimaryDisplay();
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point) const {
18968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
19068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)       it != displays_.end(); ++it) {
19168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (it->bounds().Contains(point))
19268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      return *it;
19368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
19468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
19568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return GetPrimaryDisplay();
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Display DesktopScreenX11::GetDisplayMatching(
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& match_rect) const {
20068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  int max_area = 0;
20168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  const gfx::Display* matching = NULL;
20268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
20368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)       it != displays_.end(); ++it) {
20468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
20568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    int area = intersect.width() * intersect.height();
20668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (area > max_area) {
20768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      max_area = area;
20868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      matching = &*it;
20968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
21068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
21168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // Fallback to the primary display if there is no matching display.
21268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return matching ? *matching : GetPrimaryDisplay();
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
21668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return displays_.front();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
22068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  observer_list_.AddObserver(observer);
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
22268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
22468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  observer_list_.RemoveObserver(observer);
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
22768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)bool DesktopScreenX11::Dispatch(const base::NativeEvent& event) {
22868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
22968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // Pass the event through to xlib.
23068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    XRRUpdateConfiguration(event);
23168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  } else if (event->type - xrandr_event_base_ == RRNotify) {
23268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // There's some sort of observer dispatch going on here, but I don't think
23368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // it's the screen's?
23468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    DLOG(ERROR) << "DesktopScreenX11::Dispatch() -> RRNotify";
23568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
23668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (configure_timer_.get()) {
23768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      configure_timer_->Reset();
23868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    } else {
23968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
24068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      configure_timer_->Start(
24168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          FROM_HERE,
24268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
24368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          this,
24468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          &DesktopScreenX11::ConfigureTimerFired);
24568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
24668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
24768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
24868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return true;
24968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
25268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// DesktopScreenX11, private:
25368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
25468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)DesktopScreenX11::DesktopScreenX11(
25568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const std::vector<gfx::Display>& test_displays)
25668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
25768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      x_root_window_(DefaultRootWindow(xdisplay_)),
25868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      has_xrandr_(false),
25968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      xrandr_event_base_(0),
26068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      displays_(test_displays) {
26168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
26468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  std::vector<gfx::Display> displays;
26568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  XRRScreenResources* resources =
26668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
26768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (!resources) {
26868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
26968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    return GetFallbackDisplayList();
27068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
27168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
27268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  bool has_work_area = false;
27368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Rect work_area;
27468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  std::vector<int> value;
27568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
27668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      value.size() >= 4) {
27768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
27868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    has_work_area = true;
27968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
28068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
28168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  for (int i = 0; i < resources->noutput; ++i) {
28268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    RROutput output_id = resources->outputs[i];
28368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    XRROutputInfo* output_info =
28468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        XRRGetOutputInfo(xdisplay_, resources, output_id);
28568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
28668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    bool is_connected = (output_info->connection == RR_Connected);
28768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!is_connected) {
28868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      XRRFreeOutputInfo(output_info);
28968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      continue;
29068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
29168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
29268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (output_info->crtc) {
29368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
29468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                                         resources,
29568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                                         output_info->crtc);
29668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
29768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      int64 display_id = -1;
29868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      if (!base::GetDisplayId(output_id, i, &display_id)) {
29968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        // It isn't ideal, but if we can't parse the EDID data, fallback on the
30068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        // display number.
30168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        display_id = i;
30268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      }
30368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
30468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
30568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      gfx::Display display(display_id, crtc_bounds);
30668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      if (has_work_area) {
30768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        gfx::Rect intersection = crtc_bounds;
30868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        intersection.Intersect(work_area);
30968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        display.set_work_area(intersection);
31068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      }
31168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
31268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      displays.push_back(display);
31368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
31468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      XRRFreeCrtcInfo(crtc);
31568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
31668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
31768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    XRRFreeOutputInfo(output_info);
31868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
31968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
32068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  XRRFreeScreenResources(resources);
32168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
32268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (displays.empty())
32368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    return GetFallbackDisplayList();
32468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
32568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return displays;
32668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
32768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
32868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void DesktopScreenX11::ConfigureTimerFired() {
32968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo();
33068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  ProcessDisplayChange(new_displays);
33168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
33268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
33368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Screen* CreateDesktopScreen() {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new DesktopScreenX11;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace views
340