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>
10 // It clashes with out 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/base/x/x11_util.h"
20 #include "ui/display/x11/edid_parser_x11.h"
21 #include "ui/gfx/display.h"
22 #include "ui/gfx/display_observer.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"
31 // The delay to perform configuration after RRNotify. See the comment
33 const int64 kConfigureDelayMs = 500;
35 float GetDeviceScaleFactor(int screen_pixels, int screen_mm) {
36 const int kCSSDefaultDPI = 96;
37 const float kInchInMm = 25.4f;
39 float screen_inches = screen_mm / kInchInMm;
40 float screen_dpi = screen_pixels / screen_inches;
41 float scale = screen_dpi / kCSSDefaultDPI;
43 return ui::GetImageScale(ui::GetSupportedScaleFactor(scale));
46 std::vector<gfx::Display> GetFallbackDisplayList() {
47 ::XDisplay* display = gfx::GetXDisplay();
48 ::Screen* screen = DefaultScreenOfDisplay(display);
49 int width = WidthOfScreen(screen);
50 int height = HeightOfScreen(screen);
51 int mm_width = WidthMMOfScreen(screen);
52 int mm_height = HeightMMOfScreen(screen);
54 gfx::Rect bounds_in_pixels(0, 0, width, height);
55 gfx::Display gfx_display(0, bounds_in_pixels);
56 if (!gfx::Display::HasForceDeviceScaleFactor() &&
57 !ui::IsXDisplaySizeBlackListed(mm_width, mm_height)) {
58 float device_scale_factor = GetDeviceScaleFactor(width, mm_width);
59 DCHECK_LE(1.0f, device_scale_factor);
60 gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels);
63 return std::vector<gfx::Display>(1, gfx_display);
66 // Helper class to GetWindowAtScreenPoint() which returns the topmost window at
67 // the location passed to FindAt(). NULL is returned if a window which does not
68 // belong to Chromium is topmost at the passed in location.
69 class ToplevelWindowFinder : public ui::EnumerateWindowsDelegate {
71 ToplevelWindowFinder() : toplevel_(NULL) {
74 virtual ~ToplevelWindowFinder() {
77 aura::Window* FindAt(const gfx::Point& screen_loc) {
78 screen_loc_ = screen_loc;
79 ui::EnumerateTopLevelWindows(this);
84 virtual bool ShouldStopIterating(XID xid) OVERRIDE {
85 if (!ui::IsWindowVisible(xid))
88 aura::Window* window =
89 views::DesktopWindowTreeHostX11::GetContentWindowForXID(xid);
91 // Currently |window|->IsVisible() always returns true.
92 // TODO(pkotwicz): Fix this. crbug.com/353038
93 if (window->IsVisible() &&
94 window->GetBoundsInScreen().Contains(screen_loc_)) {
101 if (ui::WindowContainsPoint(xid, screen_loc_)) {
108 gfx::Point screen_loc_;
109 aura::Window* toplevel_;
111 DISALLOW_COPY_AND_ASSIGN(ToplevelWindowFinder);
118 ////////////////////////////////////////////////////////////////////////////////
119 // DesktopScreenX11, public:
121 DesktopScreenX11::DesktopScreenX11()
122 : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
123 x_root_window_(DefaultRootWindow(xdisplay_)),
125 xrandr_event_base_(0) {
126 // We only support 1.3+. There were library changes before this and we should
127 // use the new interface instead of the 1.2 one.
128 int randr_version_major = 0;
129 int randr_version_minor = 0;
130 has_xrandr_ = XRRQueryVersion(
131 xdisplay_, &randr_version_major, &randr_version_minor) &&
132 randr_version_major == 1 &&
133 randr_version_minor >= 3;
136 int error_base_ignored = 0;
137 XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
139 base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
140 XRRSelectInput(xdisplay_,
142 RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
144 displays_ = BuildDisplaysFromXRandRInfo();
146 displays_ = GetFallbackDisplayList();
150 DesktopScreenX11::~DesktopScreenX11() {
152 base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
155 void DesktopScreenX11::ProcessDisplayChange(
156 const std::vector<gfx::Display>& incoming) {
157 std::vector<gfx::Display> old_displays = displays_;
158 displays_ = incoming;
160 typedef std::vector<gfx::Display>::const_iterator DisplayIt;
161 std::vector<gfx::Display>::const_iterator old_it = old_displays.begin();
162 for (; old_it != old_displays.end(); ++old_it) {
164 for (std::vector<gfx::Display>::const_iterator new_it =
165 displays_.begin(); new_it != displays_.end(); ++new_it) {
166 if (old_it->id() == new_it->id()) {
173 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
174 OnDisplayRemoved(*old_it));
178 std::vector<gfx::Display>::const_iterator new_it = displays_.begin();
179 for (; new_it != displays_.end(); ++new_it) {
181 for (std::vector<gfx::Display>::const_iterator old_it =
182 old_displays.begin(); old_it != old_displays.end(); ++old_it) {
183 if (new_it->id() == old_it->id()) {
184 if (new_it->bounds() != old_it->bounds()) {
185 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
186 OnDisplayBoundsChanged(*new_it));
195 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
196 OnDisplayAdded(*new_it));
201 ////////////////////////////////////////////////////////////////////////////////
202 // DesktopScreenX11, gfx::Screen implementation:
204 bool DesktopScreenX11::IsDIPEnabled() {
208 gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
209 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
211 XDisplay* display = gfx::GetXDisplay();
213 ::Window root, child;
214 int root_x, root_y, win_x, win_y;
216 XQueryPointer(display,
217 DefaultRootWindow(display),
226 return gfx::Point(root_x, root_y);
229 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
230 return GetWindowAtScreenPoint(GetCursorScreenPoint());
233 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
234 const gfx::Point& point) {
235 ToplevelWindowFinder finder;
236 return finder.FindAt(point);
239 int DesktopScreenX11::GetNumDisplays() const {
240 return displays_.size();
243 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
247 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
248 gfx::NativeView window) const {
249 // Getting screen bounds here safely is hard.
251 // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
252 // can't because |window| (and the associated WindowEventDispatcher*) can be
253 // partially initialized at this point; WindowEventDispatcher initializations
254 // call through into GetDisplayNearestWindow(). But the X11 resources are
255 // created before we create the aura::WindowEventDispatcher. So we ask what
256 // the DRWHX11 believes the window bounds are instead of going through the
257 // aura::Window's screen bounds.
258 aura::WindowTreeHost* host = window->GetHost();
260 DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
261 host->GetAcceleratedWidget());
263 return GetDisplayMatching(rwh->GetX11RootWindowBounds());
266 return GetPrimaryDisplay();
269 gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
270 const gfx::Point& point) const {
271 for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
272 it != displays_.end(); ++it) {
273 if (it->bounds().Contains(point))
277 return GetPrimaryDisplay();
280 gfx::Display DesktopScreenX11::GetDisplayMatching(
281 const gfx::Rect& match_rect) const {
283 const gfx::Display* matching = NULL;
284 for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
285 it != displays_.end(); ++it) {
286 gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
287 int area = intersect.width() * intersect.height();
288 if (area > max_area) {
293 // Fallback to the primary display if there is no matching display.
294 return matching ? *matching : GetPrimaryDisplay();
297 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
298 return displays_.front();
301 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
302 observer_list_.AddObserver(observer);
305 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
306 observer_list_.RemoveObserver(observer);
309 uint32_t DesktopScreenX11::Dispatch(const base::NativeEvent& event) {
310 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
311 // Pass the event through to xlib.
312 XRRUpdateConfiguration(event);
313 } else if (event->type - xrandr_event_base_ == RRNotify) {
314 // There's some sort of observer dispatch going on here, but I don't think
315 // it's the screen's?
316 if (configure_timer_.get() && configure_timer_->IsRunning()) {
317 configure_timer_->Reset();
319 configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
320 configure_timer_->Start(
322 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
324 &DesktopScreenX11::ConfigureTimerFired);
328 return POST_DISPATCH_NONE;
331 ////////////////////////////////////////////////////////////////////////////////
332 // DesktopScreenX11, private:
334 DesktopScreenX11::DesktopScreenX11(
335 const std::vector<gfx::Display>& test_displays)
336 : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
337 x_root_window_(DefaultRootWindow(xdisplay_)),
339 xrandr_event_base_(0),
340 displays_(test_displays) {
343 std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
344 std::vector<gfx::Display> displays;
345 XRRScreenResources* resources =
346 XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
348 LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
349 return GetFallbackDisplayList();
352 bool has_work_area = false;
354 std::vector<int> value;
355 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
357 work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
358 has_work_area = true;
361 float device_scale_factor = 1.0f;
362 for (int i = 0; i < resources->noutput; ++i) {
363 RROutput output_id = resources->outputs[i];
364 XRROutputInfo* output_info =
365 XRRGetOutputInfo(xdisplay_, resources, output_id);
367 bool is_connected = (output_info->connection == RR_Connected);
369 XRRFreeOutputInfo(output_info);
373 if (output_info->crtc) {
374 XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
378 int64 display_id = -1;
379 if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) {
380 // It isn't ideal, but if we can't parse the EDID data, fallback on the
385 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
386 gfx::Display display(display_id, crtc_bounds);
388 if (!gfx::Display::HasForceDeviceScaleFactor()) {
389 if (i == 0 && !ui::IsXDisplaySizeBlackListed(output_info->mm_width,
390 output_info->mm_height)) {
391 // As per display scale factor is not supported right now,
392 // the primary display's scale factor is always used.
393 device_scale_factor = GetDeviceScaleFactor(crtc->width,
394 output_info->mm_width);
395 DCHECK_LE(1.0f, device_scale_factor);
397 display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
401 gfx::Rect intersection = crtc_bounds;
402 intersection.Intersect(work_area);
403 display.set_work_area(intersection);
406 displays.push_back(display);
408 XRRFreeCrtcInfo(crtc);
411 XRRFreeOutputInfo(output_info);
414 XRRFreeScreenResources(resources);
416 if (displays.empty())
417 return GetFallbackDisplayList();
422 void DesktopScreenX11::ConfigureTimerFired() {
423 std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo();
424 ProcessDisplayChange(new_displays);
427 ////////////////////////////////////////////////////////////////////////////////
429 gfx::Screen* CreateDesktopScreen() {
430 return new DesktopScreenX11;