Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_screen_x11.cc
blobf4c03da817d894d4438eadaec81f3d05f96ad96c
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.
5 #include "ui/views/widget/desktop_aura/desktop_screen_x11.h"
7 #include <X11/extensions/Xrandr.h>
8 #include <X11/Xlib.h>
10 // It clashes with out RootWindow.
11 #undef RootWindow
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/native_widget_types.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/gfx/x/x11_types.h"
26 #include "ui/views/widget/desktop_aura/desktop_screen.h"
27 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
28 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
30 namespace {
32 // The delay to perform configuration after RRNotify. See the comment
33 // in |Dispatch()|.
34 const int64 kConfigureDelayMs = 500;
36 // TODO(oshima): Consider using gtk-xft-dpi instead.
37 float GetDeviceScaleFactor(int screen_pixels, int screen_mm) {
38 const int kCSSDefaultDPI = 96;
39 const float kInchInMm = 25.4f;
41 float screen_inches = screen_mm / kInchInMm;
42 float screen_dpi = screen_pixels / screen_inches;
43 float scale = screen_dpi / kCSSDefaultDPI;
45 return ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale));
48 std::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));
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);
65 return std::vector<gfx::Display>(1, gfx_display);
68 } // namespace
70 namespace views {
72 ////////////////////////////////////////////////////////////////////////////////
73 // DesktopScreenX11, public:
75 DesktopScreenX11::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;
89 if (has_xrandr_) {
90 int error_base_ignored = 0;
91 XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
93 if (ui::PlatformEventSource::GetInstance())
94 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
95 XRRSelectInput(xdisplay_,
96 x_root_window_,
97 RRScreenChangeNotifyMask |
98 RROutputChangeNotifyMask |
99 RRCrtcChangeNotifyMask);
101 displays_ = BuildDisplaysFromXRandRInfo();
102 } else {
103 displays_ = GetFallbackDisplayList();
107 DesktopScreenX11::~DesktopScreenX11() {
108 if (has_xrandr_ && ui::PlatformEventSource::GetInstance())
109 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
112 ////////////////////////////////////////////////////////////////////////////////
113 // DesktopScreenX11, gfx::Screen implementation:
115 bool DesktopScreenX11::IsDIPEnabled() {
116 return true;
119 gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
120 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
122 XDisplay* display = gfx::GetXDisplay();
124 ::Window root, child;
125 int root_x, root_y, win_x, win_y;
126 unsigned int mask;
127 XQueryPointer(display,
128 DefaultRootWindow(display),
129 &root,
130 &child,
131 &root_x,
132 &root_y,
133 &win_x,
134 &win_y,
135 &mask);
137 return gfx::Point(root_x, root_y);
140 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
141 return GetWindowAtScreenPoint(GetCursorScreenPoint());
144 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
145 const gfx::Point& point) {
146 X11TopmostWindowFinder finder;
147 return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>());
150 int DesktopScreenX11::GetNumDisplays() const {
151 return displays_.size();
154 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
155 return displays_;
158 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
159 gfx::NativeView window) const {
160 // Getting screen bounds here safely is hard.
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());
177 return GetPrimaryDisplay();
180 gfx::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;
188 return GetPrimaryDisplay();
191 gfx::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;
204 // Fallback to the primary display if there is no matching display.
205 return matching ? *matching : GetPrimaryDisplay();
208 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
209 return displays_.front();
212 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
213 change_notifier_.AddObserver(observer);
216 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
217 change_notifier_.RemoveObserver(observer);
220 bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) {
221 return event->type - xrandr_event_base_ == RRScreenChangeNotify ||
222 event->type - xrandr_event_base_ == RRNotify;
225 uint32_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);
242 } else {
243 NOTREACHED();
246 return ui::POST_DISPATCH_NONE;
249 ////////////////////////////////////////////////////////////////////////////////
250 // DesktopScreenX11, private:
252 DesktopScreenX11::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) {
261 std::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();
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;
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);
285 bool is_connected = (output_info->connection == RR_Connected);
286 if (!is_connected) {
287 XRRFreeOutputInfo(output_info);
288 continue;
291 if (output_info->crtc) {
292 XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
293 resources,
294 output_info->crtc);
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;
303 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
304 gfx::Display display(display_id, crtc_bounds);
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);
315 display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
318 if (has_work_area) {
319 gfx::Rect intersection = crtc_bounds;
320 intersection.Intersect(work_area);
321 display.set_work_area(intersection);
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;
339 displays.push_back(display);
341 XRRFreeCrtcInfo(crtc);
344 XRRFreeOutputInfo(output_info);
347 XRRFreeScreenResources(resources);
349 if (displays.empty())
350 return GetFallbackDisplayList();
352 return displays;
355 void DesktopScreenX11::ConfigureTimerFired() {
356 std::vector<gfx::Display> old_displays = displays_;
357 displays_ = BuildDisplaysFromXRandRInfo();
359 change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
362 ////////////////////////////////////////////////////////////////////////////////
364 gfx::Screen* CreateDesktopScreen() {
365 return new DesktopScreenX11;
368 } // namespace views