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