desktop_screen_x11.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/widget/desktop_aura/desktop_screen_x11.h"
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <X11/extensions/Xrandr.h>
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <X11/Xlib.h>
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// It clashes with out RootWindow.
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#undef RootWindow
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/debug/trace_event.h"
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/logging.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window_event_dispatcher.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window_tree_host.h"
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/base/layout.h"
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/display/util/display_util.h"
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/display/util/x11/edid_parser_x11.h"
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/events/platform/platform_event_source.h"
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/display.h"
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/native_widget_types.h"
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/screen.h"
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/x/x11_types.h"
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/widget/desktop_aura/desktop_screen.h"
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace {
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// The delay to perform configuration after RRNotify.  See the comment
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// in |Dispatch()|.
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int64 kConfigureDelayMs = 500;
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// TODO(oshima): Consider using gtk-xft-dpi instead.
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)float GetDeviceScaleFactor(int screen_pixels, int screen_mm) {
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const int kCSSDefaultDPI = 96;
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const float kInchInMm = 25.4f;
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  float screen_inches = screen_mm / kInchInMm;
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  float screen_dpi = screen_pixels / screen_inches;
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  float scale = screen_dpi / kCSSDefaultDPI;
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale));
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::vector<gfx::Display> GetFallbackDisplayList() {
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ::XDisplay* display = gfx::GetXDisplay();
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ::Screen* screen = DefaultScreenOfDisplay(display);
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int width = WidthOfScreen(screen);
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int height = HeightOfScreen(screen);
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Size physical_size(WidthMMOfScreen(screen), HeightMMOfScreen(screen));
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Rect bounds_in_pixels(0, 0, width, height);
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Display gfx_display(0, bounds_in_pixels);
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!gfx::Display::HasForceDeviceScaleFactor() &&
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      !ui::IsDisplaySizeBlackListed(physical_size)) {
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    float device_scale_factor = GetDeviceScaleFactor(
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        width, physical_size.width());
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DCHECK_LE(1.0f, device_scale_factor);
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels);
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return std::vector<gfx::Display>(1, gfx_display);
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace views {
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// DesktopScreenX11, public:
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)DesktopScreenX11::DesktopScreenX11()
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : xdisplay_(gfx::GetXDisplay()),
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      x_root_window_(DefaultRootWindow(xdisplay_)),
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      has_xrandr_(false),
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      xrandr_event_base_(0) {
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // We only support 1.3+. There were library changes before this and we should
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // use the new interface instead of the 1.2 one.
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int randr_version_major = 0;
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int randr_version_minor = 0;
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  has_xrandr_ = XRRQueryVersion(
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        xdisplay_, &randr_version_major, &randr_version_minor) &&
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      randr_version_major == 1 &&
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      randr_version_minor >= 3;
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (has_xrandr_) {
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int error_base_ignored = 0;
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (ui::PlatformEventSource::GetInstance())
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    XRRSelectInput(xdisplay_,
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                   x_root_window_,
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                   RRScreenChangeNotifyMask |
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                   RROutputChangeNotifyMask |
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                   RRCrtcChangeNotifyMask);
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    displays_ = BuildDisplaysFromXRandRInfo();
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    displays_ = GetFallbackDisplayList();
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)DesktopScreenX11::~DesktopScreenX11() {
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (has_xrandr_ && ui::PlatformEventSource::GetInstance())
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// DesktopScreenX11, gfx::Screen implementation:
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool DesktopScreenX11::IsDIPEnabled() {
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return true;
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  XDisplay* display = gfx::GetXDisplay();
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ::Window root, child;
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int root_x, root_y, win_x, win_y;
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  unsigned int mask;
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  XQueryPointer(display,
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                DefaultRootWindow(display),
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &root,
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &child,
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &root_x,
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &root_y,
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &win_x,
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &win_y,
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                &mask);
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return gfx::Point(root_x, root_y);
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return GetWindowAtScreenPoint(GetCursorScreenPoint());
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const gfx::Point& point) {
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  X11TopmostWindowFinder finder;
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>());
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)int DesktopScreenX11::GetNumDisplays() const {
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return displays_.size();
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
155  return displays_;
156}
157
158gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
159    gfx::NativeView window) const {
160  // Getting screen bounds here safely is hard.
161  //
162  // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
163  // can't because |window| (and the associated WindowEventDispatcher*) can be
164  // partially initialized at this point; WindowEventDispatcher initializations
165  // call through into GetDisplayNearestWindow(). But the X11 resources are
166  // created before we create the aura::WindowEventDispatcher. So we ask what
167  // the DRWHX11 believes the window bounds are instead of going through the
168  // aura::Window's screen bounds.
169  aura::WindowTreeHost* host = window->GetHost();
170  if (host) {
171    DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
172        host->GetAcceleratedWidget());
173    if (rwh)
174      return GetDisplayMatching(rwh->GetX11RootWindowBounds());
175  }
176
177  return GetPrimaryDisplay();
178}
179
180gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
181    const gfx::Point& point) const {
182  for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
183       it != displays_.end(); ++it) {
184    if (it->bounds().Contains(point))
185      return *it;
186  }
187
188  return GetPrimaryDisplay();
189}
190
191gfx::Display DesktopScreenX11::GetDisplayMatching(
192    const gfx::Rect& match_rect) const {
193  int max_area = 0;
194  const gfx::Display* matching = NULL;
195  for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
196       it != displays_.end(); ++it) {
197    gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
198    int area = intersect.width() * intersect.height();
199    if (area > max_area) {
200      max_area = area;
201      matching = &*it;
202    }
203  }
204  // Fallback to the primary display if there is no matching display.
205  return matching ? *matching : GetPrimaryDisplay();
206}
207
208gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
209  return displays_.front();
210}
211
212void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
213  change_notifier_.AddObserver(observer);
214}
215
216void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
217  change_notifier_.RemoveObserver(observer);
218}
219
220bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) {
221  return event->type - xrandr_event_base_ == RRScreenChangeNotify ||
222         event->type - xrandr_event_base_ == RRNotify;
223}
224
225uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) {
226  if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
227    // Pass the event through to xlib.
228    XRRUpdateConfiguration(event);
229  } else if (event->type - xrandr_event_base_ == RRNotify) {
230    // There's some sort of observer dispatch going on here, but I don't think
231    // it's the screen's?
232    if (configure_timer_.get() && configure_timer_->IsRunning()) {
233      configure_timer_->Reset();
234    } else {
235      configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
236      configure_timer_->Start(
237          FROM_HERE,
238          base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
239          this,
240          &DesktopScreenX11::ConfigureTimerFired);
241    }
242  } else {
243    NOTREACHED();
244  }
245
246  return ui::POST_DISPATCH_NONE;
247}
248
249////////////////////////////////////////////////////////////////////////////////
250// DesktopScreenX11, private:
251
252DesktopScreenX11::DesktopScreenX11(
253    const std::vector<gfx::Display>& test_displays)
254    : xdisplay_(gfx::GetXDisplay()),
255      x_root_window_(DefaultRootWindow(xdisplay_)),
256      has_xrandr_(false),
257      xrandr_event_base_(0),
258      displays_(test_displays) {
259}
260
261std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
262  std::vector<gfx::Display> displays;
263  XRRScreenResources* resources =
264      XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
265  if (!resources) {
266    LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
267    return GetFallbackDisplayList();
268  }
269
270  bool has_work_area = false;
271  gfx::Rect work_area;
272  std::vector<int> value;
273  if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
274      value.size() >= 4) {
275    work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
276    has_work_area = true;
277  }
278
279  float device_scale_factor = 1.0f;
280  for (int i = 0; i < resources->noutput; ++i) {
281    RROutput output_id = resources->outputs[i];
282    XRROutputInfo* output_info =
283        XRRGetOutputInfo(xdisplay_, resources, output_id);
284
285    bool is_connected = (output_info->connection == RR_Connected);
286    if (!is_connected) {
287      XRRFreeOutputInfo(output_info);
288      continue;
289    }
290
291    if (output_info->crtc) {
292      XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
293                                         resources,
294                                         output_info->crtc);
295
296      int64 display_id = -1;
297      if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) {
298        // It isn't ideal, but if we can't parse the EDID data, fallback on the
299        // display number.
300        display_id = i;
301      }
302
303      gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
304      gfx::Display display(display_id, crtc_bounds);
305
306      if (!gfx::Display::HasForceDeviceScaleFactor()) {
307        if (i == 0 && !ui::IsDisplaySizeBlackListed(
308            gfx::Size(output_info->mm_width, output_info->mm_height))) {
309          // As per display scale factor is not supported right now,
310          // the primary display's scale factor is always used.
311          device_scale_factor = GetDeviceScaleFactor(crtc->width,
312                                                     output_info->mm_width);
313          DCHECK_LE(1.0f, device_scale_factor);
314        }
315        display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
316      }
317
318      if (has_work_area) {
319        gfx::Rect intersection = crtc_bounds;
320        intersection.Intersect(work_area);
321        display.set_work_area(intersection);
322      }
323
324      switch (crtc->rotation) {
325        case RR_Rotate_0:
326          display.set_rotation(gfx::Display::ROTATE_0);
327          break;
328        case RR_Rotate_90:
329          display.set_rotation(gfx::Display::ROTATE_90);
330          break;
331        case RR_Rotate_180:
332          display.set_rotation(gfx::Display::ROTATE_180);
333          break;
334        case RR_Rotate_270:
335          display.set_rotation(gfx::Display::ROTATE_270);
336          break;
337      }
338
339      displays.push_back(display);
340
341      XRRFreeCrtcInfo(crtc);
342    }
343
344    XRRFreeOutputInfo(output_info);
345  }
346
347  XRRFreeScreenResources(resources);
348
349  if (displays.empty())
350    return GetFallbackDisplayList();
351
352  return displays;
353}
354
355void DesktopScreenX11::ConfigureTimerFired() {
356  std::vector<gfx::Display> old_displays = displays_;
357  displays_ = BuildDisplaysFromXRandRInfo();
358
359  change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
360}
361
362////////////////////////////////////////////////////////////////////////////////
363
364gfx::Screen* CreateDesktopScreen() {
365  return new DesktopScreenX11;
366}
367
368}  // namespace views
369