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/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"
32 // The delay to perform configuration after RRNotify. See the comment
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
);
72 ////////////////////////////////////////////////////////////////////////////////
73 // DesktopScreenX11, public:
75 DesktopScreenX11::DesktopScreenX11()
76 : xdisplay_(gfx::GetXDisplay()),
77 x_root_window_(DefaultRootWindow(xdisplay_
)),
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;
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_
,
97 RRScreenChangeNotifyMask
|
98 RROutputChangeNotifyMask
|
99 RRCrtcChangeNotifyMask
);
101 displays_
= BuildDisplaysFromXRandRInfo();
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() {
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
;
127 XQueryPointer(display
,
128 DefaultRootWindow(display
),
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 {
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();
171 DesktopWindowTreeHostX11
* rwh
= DesktopWindowTreeHostX11::GetHostForXID(
172 host
->GetAcceleratedWidget());
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
))
188 return GetPrimaryDisplay();
191 gfx::Display
DesktopScreenX11::GetDisplayMatching(
192 const gfx::Rect
& match_rect
) const {
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
) {
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();
235 configure_timer_
.reset(new base::OneShotTimer
<DesktopScreenX11
>());
236 configure_timer_
->Start(
238 base::TimeDelta::FromMilliseconds(kConfigureDelayMs
),
240 &DesktopScreenX11::ConfigureTimerFired
);
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_
)),
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_
);
266 LOG(ERROR
) << "XRandR returned no displays. Falling back to Root Window.";
267 return GetFallbackDisplayList();
270 bool has_work_area
= false;
272 std::vector
<int> value
;
273 if (ui::GetIntArrayProperty(x_root_window_
, "_NET_WORKAREA", &value
) &&
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
);
287 XRRFreeOutputInfo(output_info
);
291 if (output_info
->crtc
) {
292 XRRCrtcInfo
*crtc
= XRRGetCrtcInfo(xdisplay_
,
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
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
);
319 gfx::Rect intersection
= crtc_bounds
;
320 intersection
.Intersect(work_area
);
321 display
.set_work_area(intersection
);
324 switch (crtc
->rotation
) {
326 display
.set_rotation(gfx::Display::ROTATE_0
);
329 display
.set_rotation(gfx::Display::ROTATE_90
);
332 display
.set_rotation(gfx::Display::ROTATE_180
);
335 display
.set_rotation(gfx::Display::ROTATE_270
);
339 displays
.push_back(display
);
341 XRRFreeCrtcInfo(crtc
);
344 XRRFreeOutputInfo(output_info
);
347 XRRFreeScreenResources(resources
);
349 if (displays
.empty())
350 return GetFallbackDisplayList();
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
;