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/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/widget/desktop_aura/desktop_screen.h"
29 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
30 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
34 // The delay to perform configuration after RRNotify. See the comment
36 const int64 kConfigureDelayMs
= 500;
38 // TODO(oshima): Consider using gtk-xft-dpi instead.
39 float GetDeviceScaleFactor(int screen_pixels
, int screen_mm
) {
40 const int kCSSDefaultDPI
= 96;
41 const float kInchInMm
= 25.4f
;
43 float screen_inches
= screen_mm
/ kInchInMm
;
44 float screen_dpi
= screen_pixels
/ screen_inches
;
45 float scale
= screen_dpi
/ kCSSDefaultDPI
;
47 return ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale
));
50 float GetDeviceScaleFactor() {
51 gfx::Display display
= gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
52 return display
.device_scale_factor();
55 gfx::Point
PixelToDIPPoint(const gfx::Point
& pixel_point
) {
56 return ToFlooredPoint(ScalePoint(pixel_point
, 1.0f
/ GetDeviceScaleFactor()));
59 gfx::Point
DIPToPixelPoint(const gfx::Point
& dip_point
) {
60 return ToFlooredPoint(gfx::ScalePoint(dip_point
, GetDeviceScaleFactor()));
63 std::vector
<gfx::Display
> GetFallbackDisplayList() {
64 ::XDisplay
* display
= gfx::GetXDisplay();
65 ::Screen
* screen
= DefaultScreenOfDisplay(display
);
66 int width
= WidthOfScreen(screen
);
67 int height
= HeightOfScreen(screen
);
68 gfx::Size
physical_size(WidthMMOfScreen(screen
), HeightMMOfScreen(screen
));
70 gfx::Rect
bounds_in_pixels(0, 0, width
, height
);
71 gfx::Display
gfx_display(0, bounds_in_pixels
);
72 if (!gfx::Display::HasForceDeviceScaleFactor() &&
73 !ui::IsDisplaySizeBlackListed(physical_size
)) {
74 float device_scale_factor
= GetDeviceScaleFactor(
75 width
, physical_size
.width());
76 DCHECK_LE(1.0f
, device_scale_factor
);
77 gfx_display
.SetScaleAndBounds(device_scale_factor
, bounds_in_pixels
);
80 return std::vector
<gfx::Display
>(1, gfx_display
);
87 ////////////////////////////////////////////////////////////////////////////////
88 // DesktopScreenX11, public:
90 DesktopScreenX11::DesktopScreenX11()
91 : xdisplay_(gfx::GetXDisplay()),
92 x_root_window_(DefaultRootWindow(xdisplay_
)),
94 xrandr_event_base_(0) {
95 // We only support 1.3+. There were library changes before this and we should
96 // use the new interface instead of the 1.2 one.
97 int randr_version_major
= 0;
98 int randr_version_minor
= 0;
99 has_xrandr_
= XRRQueryVersion(
100 xdisplay_
, &randr_version_major
, &randr_version_minor
) &&
101 randr_version_major
== 1 &&
102 randr_version_minor
>= 3;
105 int error_base_ignored
= 0;
106 XRRQueryExtension(xdisplay_
, &xrandr_event_base_
, &error_base_ignored
);
108 if (ui::PlatformEventSource::GetInstance())
109 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
110 XRRSelectInput(xdisplay_
,
112 RRScreenChangeNotifyMask
|
113 RROutputChangeNotifyMask
|
114 RRCrtcChangeNotifyMask
);
116 displays_
= BuildDisplaysFromXRandRInfo();
118 displays_
= GetFallbackDisplayList();
122 DesktopScreenX11::~DesktopScreenX11() {
123 if (has_xrandr_
&& ui::PlatformEventSource::GetInstance())
124 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
127 ////////////////////////////////////////////////////////////////////////////////
128 // DesktopScreenX11, gfx::Screen implementation:
130 gfx::Point
DesktopScreenX11::GetCursorScreenPoint() {
131 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
133 XDisplay
* display
= gfx::GetXDisplay();
135 ::Window root
, child
;
136 int root_x
, root_y
, win_x
, win_y
;
138 XQueryPointer(display
,
139 DefaultRootWindow(display
),
148 return PixelToDIPPoint(gfx::Point(root_x
, root_y
));
151 gfx::NativeWindow
DesktopScreenX11::GetWindowUnderCursor() {
152 return GetWindowAtScreenPoint(GetCursorScreenPoint());
155 gfx::NativeWindow
DesktopScreenX11::GetWindowAtScreenPoint(
156 const gfx::Point
& point
) {
157 X11TopmostWindowFinder finder
;
158 return finder
.FindLocalProcessWindowAt(
159 DIPToPixelPoint(point
), std::set
<aura::Window
*>());
162 int DesktopScreenX11::GetNumDisplays() const {
163 return displays_
.size();
166 std::vector
<gfx::Display
> DesktopScreenX11::GetAllDisplays() const {
170 gfx::Display
DesktopScreenX11::GetDisplayNearestWindow(
171 gfx::NativeView window
) const {
173 return GetPrimaryDisplay();
175 // Getting screen bounds here safely is hard.
177 // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
178 // can't because |window| (and the associated WindowEventDispatcher*) can be
179 // partially initialized at this point; WindowEventDispatcher initializations
180 // call through into GetDisplayNearestWindow(). But the X11 resources are
181 // created before we create the aura::WindowEventDispatcher. So we ask what
182 // the DRWHX11 believes the window bounds are instead of going through the
183 // aura::Window's screen bounds.
184 aura::WindowTreeHost
* host
= window
->GetHost();
186 DesktopWindowTreeHostX11
* rwh
= DesktopWindowTreeHostX11::GetHostForXID(
187 host
->GetAcceleratedWidget());
189 return GetDisplayMatching(rwh
->GetX11RootWindowBounds());
192 return GetPrimaryDisplay();
195 gfx::Display
DesktopScreenX11::GetDisplayNearestPoint(
196 const gfx::Point
& requested_point
) const {
197 const gfx::Point point_in_pixel
= DIPToPixelPoint(requested_point
);
198 for (std::vector
<gfx::Display
>::const_iterator it
= displays_
.begin();
199 it
!= displays_
.end(); ++it
) {
200 if (it
->bounds().Contains(point_in_pixel
))
204 return GetPrimaryDisplay();
207 gfx::Display
DesktopScreenX11::GetDisplayMatching(
208 const gfx::Rect
& match_rect
) const {
210 const gfx::Display
* matching
= NULL
;
211 for (std::vector
<gfx::Display
>::const_iterator it
= displays_
.begin();
212 it
!= displays_
.end(); ++it
) {
213 gfx::Rect intersect
= gfx::IntersectRects(it
->bounds(), match_rect
);
214 int area
= intersect
.width() * intersect
.height();
215 if (area
> max_area
) {
220 // Fallback to the primary display if there is no matching display.
221 return matching
? *matching
: GetPrimaryDisplay();
224 gfx::Display
DesktopScreenX11::GetPrimaryDisplay() const {
225 return displays_
.front();
228 void DesktopScreenX11::AddObserver(gfx::DisplayObserver
* observer
) {
229 change_notifier_
.AddObserver(observer
);
232 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver
* observer
) {
233 change_notifier_
.RemoveObserver(observer
);
236 bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent
& event
) {
237 return event
->type
- xrandr_event_base_
== RRScreenChangeNotify
||
238 event
->type
- xrandr_event_base_
== RRNotify
;
241 uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent
& event
) {
242 if (event
->type
- xrandr_event_base_
== RRScreenChangeNotify
) {
243 // Pass the event through to xlib.
244 XRRUpdateConfiguration(event
);
245 } else if (event
->type
- xrandr_event_base_
== RRNotify
) {
246 // There's some sort of observer dispatch going on here, but I don't think
247 // it's the screen's?
248 if (configure_timer_
.get() && configure_timer_
->IsRunning()) {
249 configure_timer_
->Reset();
251 configure_timer_
.reset(new base::OneShotTimer
<DesktopScreenX11
>());
252 configure_timer_
->Start(
254 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
256 &DesktopScreenX11::ConfigureTimerFired
);
262 return ui::POST_DISPATCH_NONE
;
265 ////////////////////////////////////////////////////////////////////////////////
266 // DesktopScreenX11, private:
268 DesktopScreenX11::DesktopScreenX11(
269 const std::vector
<gfx::Display
>& test_displays
)
270 : xdisplay_(gfx::GetXDisplay()),
271 x_root_window_(DefaultRootWindow(xdisplay_
)),
273 xrandr_event_base_(0),
274 displays_(test_displays
) {
277 std::vector
<gfx::Display
> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
278 std::vector
<gfx::Display
> displays
;
281 gfx::XObjectDeleter
<XRRScreenResources
, void, XRRFreeScreenResources
>>
282 resources(XRRGetScreenResourcesCurrent(xdisplay_
, x_root_window_
));
284 LOG(ERROR
) << "XRandR returned no displays. Falling back to Root Window.";
285 return GetFallbackDisplayList();
288 bool has_work_area
= false;
289 gfx::Rect work_area_in_pixels
;
290 std::vector
<int> value
;
291 if (ui::GetIntArrayProperty(x_root_window_
, "_NET_WORKAREA", &value
) &&
293 work_area_in_pixels
= gfx::Rect(value
[0], value
[1], value
[2], value
[3]);
294 has_work_area
= true;
297 float device_scale_factor
= 1.0f
;
298 for (int i
= 0; i
< resources
->noutput
; ++i
) {
299 RROutput output_id
= resources
->outputs
[i
];
300 gfx::XScopedPtr
<XRROutputInfo
,
301 gfx::XObjectDeleter
<XRROutputInfo
, void, XRRFreeOutputInfo
>>
302 output_info(XRRGetOutputInfo(xdisplay_
, resources
.get(), output_id
));
304 bool is_connected
= (output_info
->connection
== RR_Connected
);
308 if (output_info
->crtc
) {
309 gfx::XScopedPtr
<XRRCrtcInfo
,
310 gfx::XObjectDeleter
<XRRCrtcInfo
, void, XRRFreeCrtcInfo
>>
311 crtc(XRRGetCrtcInfo(xdisplay_
, resources
.get(), output_info
->crtc
));
313 int64 display_id
= -1;
314 if (!ui::GetDisplayId(output_id
, static_cast<uint8
>(i
), &display_id
)) {
315 // It isn't ideal, but if we can't parse the EDID data, fallback on the
320 gfx::Rect
crtc_bounds(crtc
->x
, crtc
->y
, crtc
->width
, crtc
->height
);
321 gfx::Display
display(display_id
, crtc_bounds
);
323 if (!gfx::Display::HasForceDeviceScaleFactor()) {
324 if (i
== 0 && !ui::IsDisplaySizeBlackListed(
325 gfx::Size(output_info
->mm_width
, output_info
->mm_height
))) {
326 // As per display scale factor is not supported right now,
327 // the primary display's scale factor is always used.
328 device_scale_factor
= GetDeviceScaleFactor(crtc
->width
,
329 output_info
->mm_width
);
330 DCHECK_LE(1.0f
, device_scale_factor
);
332 display
.SetScaleAndBounds(device_scale_factor
, crtc_bounds
);
336 gfx::Rect intersection_in_pixels
= crtc_bounds
;
337 intersection_in_pixels
.Intersect(work_area_in_pixels
);
338 // SetScaleAndBounds() above does the conversion from pixels to DIP for
339 // us, but set_work_area does not, so we need to do it here.
340 display
.set_work_area(gfx::Rect(
342 gfx::ScalePoint(intersection_in_pixels
.origin(),
343 1.0f
/ display
.device_scale_factor())),
345 gfx::ScaleSize(intersection_in_pixels
.size(),
346 1.0f
/ display
.device_scale_factor()))));
349 switch (crtc
->rotation
) {
351 display
.set_rotation(gfx::Display::ROTATE_0
);
354 display
.set_rotation(gfx::Display::ROTATE_90
);
357 display
.set_rotation(gfx::Display::ROTATE_180
);
360 display
.set_rotation(gfx::Display::ROTATE_270
);
364 displays
.push_back(display
);
368 if (displays
.empty())
369 return GetFallbackDisplayList();
374 void DesktopScreenX11::ConfigureTimerFired() {
375 std::vector
<gfx::Display
> old_displays
= displays_
;
376 displays_
= BuildDisplaysFromXRandRInfo();
378 change_notifier_
.NotifyDisplaysChanged(old_displays
, displays_
);
381 ////////////////////////////////////////////////////////////////////////////////
383 gfx::Screen
* CreateDesktopScreen() {
384 return new DesktopScreenX11
;