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/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"
35 // The delay to perform configuration after RRNotify. See the comment
37 const int64 kConfigureDelayMs
= 500;
39 double GetDeviceScaleFactor() {
40 float device_scale_factor
= 1.0f
;
41 if (views::LinuxUI::instance())
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
);
78 ////////////////////////////////////////////////////////////////////////////////
79 // DesktopScreenX11, public:
81 DesktopScreenX11::DesktopScreenX11()
82 : xdisplay_(gfx::GetXDisplay()),
83 x_root_window_(DefaultRootWindow(xdisplay_
)),
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;
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_
,
103 RRScreenChangeNotifyMask
|
104 RROutputChangeNotifyMask
|
105 RRCrtcChangeNotifyMask
);
107 displays_
= BuildDisplaysFromXRandRInfo();
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
;
129 XQueryPointer(display
,
130 DefaultRootWindow(display
),
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 {
161 gfx::Display
DesktopScreenX11::GetDisplayNearestWindow(
162 gfx::NativeView window
) const {
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();
177 DesktopWindowTreeHostX11
* rwh
= DesktopWindowTreeHostX11::GetHostForXID(
178 host
->GetAcceleratedWidget());
180 return GetDisplayMatching(rwh
->GetX11RootWindowBounds());
183 return GetPrimaryDisplay();
186 gfx::Display
DesktopScreenX11::GetDisplayNearestPoint(
187 const gfx::Point
& requested_point
) const {
188 const gfx::Point point_in_pixel
= DIPToPixelPoint(requested_point
);
189 for (std::vector
<gfx::Display
>::const_iterator it
= displays_
.begin();
190 it
!= displays_
.end(); ++it
) {
191 if (it
->bounds().Contains(point_in_pixel
))
195 return GetPrimaryDisplay();
198 gfx::Display
DesktopScreenX11::GetDisplayMatching(
199 const gfx::Rect
& match_rect
) const {
201 const gfx::Display
* matching
= NULL
;
202 for (std::vector
<gfx::Display
>::const_iterator it
= displays_
.begin();
203 it
!= displays_
.end(); ++it
) {
204 gfx::Rect intersect
= gfx::IntersectRects(it
->bounds(), match_rect
);
205 int area
= intersect
.width() * intersect
.height();
206 if (area
> max_area
) {
211 // Fallback to the primary display if there is no matching display.
212 return matching
? *matching
: GetPrimaryDisplay();
215 gfx::Display
DesktopScreenX11::GetPrimaryDisplay() const {
216 return displays_
.front();
219 void DesktopScreenX11::AddObserver(gfx::DisplayObserver
* observer
) {
220 change_notifier_
.AddObserver(observer
);
223 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver
* observer
) {
224 change_notifier_
.RemoveObserver(observer
);
227 bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent
& event
) {
228 return event
->type
- xrandr_event_base_
== RRScreenChangeNotify
||
229 event
->type
- xrandr_event_base_
== RRNotify
;
232 uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent
& event
) {
233 if (event
->type
- xrandr_event_base_
== RRScreenChangeNotify
) {
234 // Pass the event through to xlib.
235 XRRUpdateConfiguration(event
);
236 } else if (event
->type
- xrandr_event_base_
== RRNotify
) {
237 // There's some sort of observer dispatch going on here, but I don't think
238 // it's the screen's?
239 if (configure_timer_
.get() && configure_timer_
->IsRunning()) {
240 configure_timer_
->Reset();
242 configure_timer_
.reset(new base::OneShotTimer
<DesktopScreenX11
>());
243 configure_timer_
->Start(
245 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
247 &DesktopScreenX11::ConfigureTimerFired
);
253 return ui::POST_DISPATCH_NONE
;
256 ////////////////////////////////////////////////////////////////////////////////
257 // DesktopScreenX11, private:
259 DesktopScreenX11::DesktopScreenX11(
260 const std::vector
<gfx::Display
>& test_displays
)
261 : xdisplay_(gfx::GetXDisplay()),
262 x_root_window_(DefaultRootWindow(xdisplay_
)),
264 xrandr_event_base_(0),
265 displays_(test_displays
) {
268 std::vector
<gfx::Display
> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
269 std::vector
<gfx::Display
> displays
;
272 gfx::XObjectDeleter
<XRRScreenResources
, void, XRRFreeScreenResources
>>
273 resources(XRRGetScreenResourcesCurrent(xdisplay_
, x_root_window_
));
275 LOG(ERROR
) << "XRandR returned no displays. Falling back to Root Window.";
276 return GetFallbackDisplayList();
279 bool has_work_area
= false;
280 gfx::Rect work_area_in_pixels
;
281 std::vector
<int> value
;
282 if (ui::GetIntArrayProperty(x_root_window_
, "_NET_WORKAREA", &value
) &&
284 work_area_in_pixels
= gfx::Rect(value
[0], value
[1], value
[2], value
[3]);
285 has_work_area
= true;
288 // As per-display scale factor is not supported right now,
289 // the X11 root window's scale factor is always used.
290 const float device_scale_factor
= GetDeviceScaleFactor();
291 for (int i
= 0; i
< resources
->noutput
; ++i
) {
292 RROutput output_id
= resources
->outputs
[i
];
293 gfx::XScopedPtr
<XRROutputInfo
,
294 gfx::XObjectDeleter
<XRROutputInfo
, void, XRRFreeOutputInfo
>>
295 output_info(XRRGetOutputInfo(xdisplay_
, resources
.get(), output_id
));
297 bool is_connected
= (output_info
->connection
== RR_Connected
);
301 if (output_info
->crtc
) {
302 gfx::XScopedPtr
<XRRCrtcInfo
,
303 gfx::XObjectDeleter
<XRRCrtcInfo
, void, XRRFreeCrtcInfo
>>
304 crtc(XRRGetCrtcInfo(xdisplay_
, resources
.get(), output_info
->crtc
));
306 int64 display_id
= -1;
307 if (!ui::GetDisplayId(output_id
, static_cast<uint8
>(i
), &display_id
)) {
308 // It isn't ideal, but if we can't parse the EDID data, fallback on the
313 gfx::Rect
crtc_bounds(crtc
->x
, crtc
->y
, crtc
->width
, crtc
->height
);
314 gfx::Display
display(display_id
, crtc_bounds
);
316 if (!gfx::Display::HasForceDeviceScaleFactor()) {
317 display
.SetScaleAndBounds(device_scale_factor
, crtc_bounds
);
321 gfx::Rect intersection_in_pixels
= crtc_bounds
;
322 intersection_in_pixels
.Intersect(work_area_in_pixels
);
323 // SetScaleAndBounds() above does the conversion from pixels to DIP for
324 // us, but set_work_area does not, so we need to do it here.
325 display
.set_work_area(gfx::Rect(
327 gfx::ScalePoint(intersection_in_pixels
.origin(),
328 1.0f
/ display
.device_scale_factor())),
330 gfx::ScaleSize(intersection_in_pixels
.size(),
331 1.0f
/ display
.device_scale_factor()))));
334 switch (crtc
->rotation
) {
336 display
.set_rotation(gfx::Display::ROTATE_0
);
339 display
.set_rotation(gfx::Display::ROTATE_90
);
342 display
.set_rotation(gfx::Display::ROTATE_180
);
345 display
.set_rotation(gfx::Display::ROTATE_270
);
349 displays
.push_back(display
);
353 if (displays
.empty())
354 return GetFallbackDisplayList();
359 void DesktopScreenX11::ConfigureTimerFired() {
360 std::vector
<gfx::Display
> old_displays
= displays_
;
361 displays_
= BuildDisplaysFromXRandRInfo();
363 change_notifier_
.NotifyDisplaysChanged(old_displays
, displays_
);
366 ////////////////////////////////////////////////////////////////////////////////
368 gfx::Screen
* CreateDesktopScreen() {
369 return new DesktopScreenX11
;