Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_screen_x11.cc
blobb6e147e957725e1b2cd80e24fd24877d97d72f42
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/logging.h"
14 #include "base/trace_event/trace_event.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/geometry/point_conversions.h"
24 #include "ui/gfx/geometry/size_conversions.h"
25 #include "ui/gfx/native_widget_types.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/gfx/x/x11_types.h"
28 #include "ui/views/linux_ui/linux_ui.h"
29 #include "ui/views/widget/desktop_aura/desktop_screen.h"
30 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
31 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
33 namespace {
35 // The delay to perform configuration after RRNotify. See the comment
36 // in |Dispatch()|.
37 const int64 kConfigureDelayMs = 500;
39 double GetDeviceScaleFactor() {
40 float device_scale_factor = 1.0f;
41 if (views::LinuxUI::instance())
42 device_scale_factor =
43 views::LinuxUI::instance()->GetDeviceScaleFactor();
44 return device_scale_factor;
47 gfx::Point PixelToDIPPoint(const gfx::Point& pixel_point) {
48 return ToFlooredPoint(ScalePoint(pixel_point, 1.0f / GetDeviceScaleFactor()));
51 gfx::Point DIPToPixelPoint(const gfx::Point& dip_point) {
52 return ToFlooredPoint(gfx::ScalePoint(dip_point, GetDeviceScaleFactor()));
55 std::vector<gfx::Display> GetFallbackDisplayList() {
56 ::XDisplay* display = gfx::GetXDisplay();
57 ::Screen* screen = DefaultScreenOfDisplay(display);
58 int width = WidthOfScreen(screen);
59 int height = HeightOfScreen(screen);
60 gfx::Size physical_size(WidthMMOfScreen(screen), HeightMMOfScreen(screen));
62 gfx::Rect bounds_in_pixels(0, 0, width, height);
63 gfx::Display gfx_display(0, bounds_in_pixels);
64 if (!gfx::Display::HasForceDeviceScaleFactor() &&
65 !ui::IsDisplaySizeBlackListed(physical_size)) {
66 const float device_scale_factor = GetDeviceScaleFactor();
67 DCHECK_LE(1.0f, device_scale_factor);
68 gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels);
71 return std::vector<gfx::Display>(1, gfx_display);
74 } // namespace
76 namespace views {
78 ////////////////////////////////////////////////////////////////////////////////
79 // DesktopScreenX11, public:
81 DesktopScreenX11::DesktopScreenX11()
82 : xdisplay_(gfx::GetXDisplay()),
83 x_root_window_(DefaultRootWindow(xdisplay_)),
84 has_xrandr_(false),
85 xrandr_event_base_(0) {
86 // We only support 1.3+. There were library changes before this and we should
87 // use the new interface instead of the 1.2 one.
88 int randr_version_major = 0;
89 int randr_version_minor = 0;
90 has_xrandr_ = XRRQueryVersion(
91 xdisplay_, &randr_version_major, &randr_version_minor) &&
92 randr_version_major == 1 &&
93 randr_version_minor >= 3;
95 if (has_xrandr_) {
96 int error_base_ignored = 0;
97 XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
99 if (ui::PlatformEventSource::GetInstance())
100 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
101 XRRSelectInput(xdisplay_,
102 x_root_window_,
103 RRScreenChangeNotifyMask |
104 RROutputChangeNotifyMask |
105 RRCrtcChangeNotifyMask);
107 displays_ = BuildDisplaysFromXRandRInfo();
108 } else {
109 displays_ = GetFallbackDisplayList();
113 DesktopScreenX11::~DesktopScreenX11() {
114 if (has_xrandr_ && ui::PlatformEventSource::GetInstance())
115 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
118 ////////////////////////////////////////////////////////////////////////////////
119 // DesktopScreenX11, gfx::Screen implementation:
121 gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
122 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
124 XDisplay* display = gfx::GetXDisplay();
126 ::Window root, child;
127 int root_x, root_y, win_x, win_y;
128 unsigned int mask;
129 XQueryPointer(display,
130 DefaultRootWindow(display),
131 &root,
132 &child,
133 &root_x,
134 &root_y,
135 &win_x,
136 &win_y,
137 &mask);
139 return PixelToDIPPoint(gfx::Point(root_x, root_y));
142 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
143 return GetWindowAtScreenPoint(GetCursorScreenPoint());
146 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
147 const gfx::Point& point) {
148 X11TopmostWindowFinder finder;
149 return finder.FindLocalProcessWindowAt(
150 DIPToPixelPoint(point), std::set<aura::Window*>());
153 int DesktopScreenX11::GetNumDisplays() const {
154 return displays_.size();
157 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
158 return displays_;
161 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
162 gfx::NativeView window) const {
163 if (!window)
164 return GetPrimaryDisplay();
166 // Getting screen bounds here safely is hard.
168 // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
169 // can't because |window| (and the associated WindowEventDispatcher*) can be
170 // partially initialized at this point; WindowEventDispatcher initializations
171 // call through into GetDisplayNearestWindow(). But the X11 resources are
172 // created before we create the aura::WindowEventDispatcher. So we ask what
173 // the DRWHX11 believes the window bounds are instead of going through the
174 // aura::Window's screen bounds.
175 aura::WindowTreeHost* host = window->GetHost();
176 if (host) {
177 DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
178 host->GetAcceleratedWidget());
179 if (rwh)
180 return GetDisplayMatching(rwh->GetX11RootWindowBounds());
183 return GetPrimaryDisplay();
186 gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
187 const gfx::Point& point) const {
188 for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
189 it != displays_.end(); ++it) {
190 if (it->bounds().Contains(point))
191 return *it;
194 return GetPrimaryDisplay();
197 gfx::Display DesktopScreenX11::GetDisplayMatching(
198 const gfx::Rect& match_rect) const {
199 int max_area = 0;
200 const gfx::Display* matching = NULL;
201 for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
202 it != displays_.end(); ++it) {
203 gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
204 int area = intersect.width() * intersect.height();
205 if (area > max_area) {
206 max_area = area;
207 matching = &*it;
210 // Fallback to the primary display if there is no matching display.
211 return matching ? *matching : GetPrimaryDisplay();
214 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
215 return displays_.front();
218 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
219 change_notifier_.AddObserver(observer);
222 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
223 change_notifier_.RemoveObserver(observer);
226 bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) {
227 return event->type - xrandr_event_base_ == RRScreenChangeNotify ||
228 event->type - xrandr_event_base_ == RRNotify;
231 uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) {
232 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
233 // Pass the event through to xlib.
234 XRRUpdateConfiguration(event);
235 } else if (event->type - xrandr_event_base_ == RRNotify) {
236 // There's some sort of observer dispatch going on here, but I don't think
237 // it's the screen's?
238 if (configure_timer_.get() && configure_timer_->IsRunning()) {
239 configure_timer_->Reset();
240 } else {
241 configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
242 configure_timer_->Start(
243 FROM_HERE,
244 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
245 this,
246 &DesktopScreenX11::ConfigureTimerFired);
248 } else {
249 NOTREACHED();
252 return ui::POST_DISPATCH_NONE;
255 ////////////////////////////////////////////////////////////////////////////////
256 // DesktopScreenX11, private:
258 DesktopScreenX11::DesktopScreenX11(
259 const std::vector<gfx::Display>& test_displays)
260 : xdisplay_(gfx::GetXDisplay()),
261 x_root_window_(DefaultRootWindow(xdisplay_)),
262 has_xrandr_(false),
263 xrandr_event_base_(0),
264 displays_(test_displays) {
267 std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
268 std::vector<gfx::Display> displays;
269 gfx::XScopedPtr<
270 XRRScreenResources,
271 gfx::XObjectDeleter<XRRScreenResources, void, XRRFreeScreenResources>>
272 resources(XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_));
273 if (!resources) {
274 LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
275 return GetFallbackDisplayList();
278 bool has_work_area = false;
279 gfx::Rect work_area_in_pixels;
280 std::vector<int> value;
281 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
282 value.size() >= 4) {
283 work_area_in_pixels = gfx::Rect(value[0], value[1], value[2], value[3]);
284 has_work_area = true;
287 // As per-display scale factor is not supported right now,
288 // the X11 root window's scale factor is always used.
289 const float device_scale_factor = GetDeviceScaleFactor();
290 for (int i = 0; i < resources->noutput; ++i) {
291 RROutput output_id = resources->outputs[i];
292 gfx::XScopedPtr<XRROutputInfo,
293 gfx::XObjectDeleter<XRROutputInfo, void, XRRFreeOutputInfo>>
294 output_info(XRRGetOutputInfo(xdisplay_, resources.get(), output_id));
296 bool is_connected = (output_info->connection == RR_Connected);
297 if (!is_connected)
298 continue;
300 if (output_info->crtc) {
301 gfx::XScopedPtr<XRRCrtcInfo,
302 gfx::XObjectDeleter<XRRCrtcInfo, void, XRRFreeCrtcInfo>>
303 crtc(XRRGetCrtcInfo(xdisplay_, resources.get(), output_info->crtc));
305 int64 display_id = -1;
306 if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) {
307 // It isn't ideal, but if we can't parse the EDID data, fallback on the
308 // display number.
309 display_id = i;
312 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
313 gfx::Display display(display_id, crtc_bounds);
315 if (!gfx::Display::HasForceDeviceScaleFactor()) {
316 display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
319 if (has_work_area) {
320 gfx::Rect intersection_in_pixels = crtc_bounds;
321 intersection_in_pixels.Intersect(work_area_in_pixels);
322 // SetScaleAndBounds() above does the conversion from pixels to DIP for
323 // us, but set_work_area does not, so we need to do it here.
324 display.set_work_area(gfx::Rect(
325 gfx::ToFlooredPoint(
326 gfx::ScalePoint(intersection_in_pixels.origin(),
327 1.0f / display.device_scale_factor())),
328 gfx::ToFlooredSize(
329 gfx::ScaleSize(intersection_in_pixels.size(),
330 1.0f / display.device_scale_factor()))));
333 switch (crtc->rotation) {
334 case RR_Rotate_0:
335 display.set_rotation(gfx::Display::ROTATE_0);
336 break;
337 case RR_Rotate_90:
338 display.set_rotation(gfx::Display::ROTATE_90);
339 break;
340 case RR_Rotate_180:
341 display.set_rotation(gfx::Display::ROTATE_180);
342 break;
343 case RR_Rotate_270:
344 display.set_rotation(gfx::Display::ROTATE_270);
345 break;
348 displays.push_back(display);
352 if (displays.empty())
353 return GetFallbackDisplayList();
355 return displays;
358 void DesktopScreenX11::ConfigureTimerFired() {
359 std::vector<gfx::Display> old_displays = displays_;
360 displays_ = BuildDisplaysFromXRandRInfo();
362 change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
365 ////////////////////////////////////////////////////////////////////////////////
367 gfx::Screen* CreateDesktopScreen() {
368 return new DesktopScreenX11;
371 } // namespace views