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 "ash/display/display_change_observer_x11.h"
12 #include <X11/extensions/Xrandr.h>
14 #include "ash/display/display_manager.h"
15 #include "ash/shell.h"
16 #include "base/message_pump_aurax11.h"
17 #include "ui/base/x/x11_util.h"
18 #include "ui/compositor/dip_util.h"
19 #include "ui/gfx/display.h"
26 // The DPI threshold to detect high density screen.
27 // Higher DPI than this will use device_scale_factor=2.
28 // Note: This value has to be kept in sync with the mouse/touchpad driver
29 // which controls mouse pointer acceleration. If you need to update this value,
30 // please update the bug (crosbug.com/31628) first and make sure that the
31 // driver will use the same value.
32 // This value also has to be kept in sync with the value in
33 // chromeos/display/output_configurator.cc. See crbug.com/130188
34 const unsigned int kHighDensityDIPThreshold
= 160;
37 const float kInchInMm
= 25.4f
;
39 XRRModeInfo
* FindMode(XRRScreenResources
* screen_resources
, XID current_mode
) {
40 for (int m
= 0; m
< screen_resources
->nmode
; m
++) {
41 XRRModeInfo
*mode
= &screen_resources
->modes
[m
];
42 if (mode
->id
== current_mode
)
48 bool CompareDisplayY(const gfx::Display
& lhs
, const gfx::Display
& rhs
) {
49 return lhs
.bounds_in_pixel().y() < rhs
.bounds_in_pixel().y();
52 // A list of bogus sizes in mm that X detects and should be ignored.
53 // See crbug.com/136533.
54 const unsigned long kInvalidDisplaySizeList
[][2] = {
61 // Returns true if the size nifo in the output_info isn't valid
62 // and should be ignored.
63 bool ShouldIgnoreSize(XRROutputInfo
*output_info
) {
64 if (output_info
->mm_width
== 0 || output_info
->mm_height
== 0) {
65 LOG(WARNING
) << "No display size available";
68 for (unsigned long i
= 0 ; i
< arraysize(kInvalidDisplaySizeList
); ++i
) {
69 const unsigned long* size
= kInvalidDisplaySizeList
[i
];
70 if (output_info
->mm_width
== size
[0] && output_info
->mm_height
== size
[1]) {
71 LOG(WARNING
) << "Black listed display size detected:"
72 << size
[0] << "x" << size
[1];
81 DisplayChangeObserverX11::DisplayChangeObserverX11()
82 : xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
83 x_root_window_(DefaultRootWindow(xdisplay_
)),
84 xrandr_event_base_(0) {
85 int error_base_ignored
;
86 XRRQueryExtension(xdisplay_
, &xrandr_event_base_
, &error_base_ignored
);
87 base::MessagePumpAuraX11::Current()->AddDispatcherForRootWindow(this);
90 DisplayChangeObserverX11::~DisplayChangeObserverX11() {
91 base::MessagePumpAuraX11::Current()->RemoveDispatcherForRootWindow(this);
94 bool DisplayChangeObserverX11::Dispatch(const base::NativeEvent
& event
) {
95 if (event
->type
- xrandr_event_base_
== RRScreenChangeNotify
) {
96 NotifyDisplayChange();
101 void DisplayChangeObserverX11::NotifyDisplayChange() {
102 XRRScreenResources
* screen_resources
=
103 XRRGetScreenResources(xdisplay_
, x_root_window_
);
104 std::map
<XID
, XRRCrtcInfo
*> crtc_info_map
;
106 for (int c
= 0; c
< screen_resources
->ncrtc
; c
++) {
107 XID crtc_id
= screen_resources
->crtcs
[c
];
108 XRRCrtcInfo
*crtc_info
=
109 XRRGetCrtcInfo(xdisplay_
, screen_resources
, crtc_id
);
110 crtc_info_map
[crtc_id
] = crtc_info
;
113 std::vector
<gfx::Display
> displays
;
114 std::set
<int> y_coords
;
116 for (int o
= 0; o
< screen_resources
->noutput
; o
++) {
117 XRROutputInfo
*output_info
=
118 XRRGetOutputInfo(xdisplay_
,
120 screen_resources
->outputs
[o
]);
121 if (output_info
->connection
!= RR_Connected
) {
122 XRRFreeOutputInfo(output_info
);
125 XRRCrtcInfo
* crtc_info
= crtc_info_map
[output_info
->crtc
];
127 LOG(WARNING
) << "Crtc not found for output: output=" << o
;
130 XRRModeInfo
* mode
= FindMode(screen_resources
, crtc_info
->mode
);
132 LOG(WARNING
) << "Could not find a mode for the output: output=" << o
;
135 // Mirrored monitors have the same y coordinates.
136 if (y_coords
.find(crtc_info
->y
) != y_coords
.end())
138 displays
.push_back(gfx::Display());
140 float device_scale_factor
= 1.0f
;
141 if (!ShouldIgnoreSize(output_info
) &&
142 (kInchInMm
* mode
->width
/ output_info
->mm_width
) >
143 kHighDensityDIPThreshold
) {
144 device_scale_factor
= 2.0f
;
146 displays
.back().SetScaleAndBounds(
148 gfx::Rect(crtc_info
->x
, crtc_info
->y
, mode
->width
, mode
->height
));
150 uint16 manufacturer_id
= 0;
151 uint32 serial_number
= 0;
152 if (ui::GetOutputDeviceData(screen_resources
->outputs
[o
], &manufacturer_id
,
153 &serial_number
, NULL
) && manufacturer_id
!= 0) {
154 // An ID based on display's index will be assigned later if this call
156 int64 new_id
= gfx::Display::GetID(manufacturer_id
, serial_number
);
157 if (ids
.find(new_id
) == ids
.end()) {
158 displays
.back().set_id(new_id
);
163 y_coords
.insert(crtc_info
->y
);
164 XRRFreeOutputInfo(output_info
);
167 // Free all allocated resources.
168 for (std::map
<XID
, XRRCrtcInfo
*>::const_iterator iter
= crtc_info_map
.begin();
169 iter
!= crtc_info_map
.end(); ++iter
) {
170 XRRFreeCrtcInfo(iter
->second
);
172 XRRFreeScreenResources(screen_resources
);
174 // PowerManager lays out the outputs vertically. Sort them by Y
176 std::sort(displays
.begin(), displays
.end(), CompareDisplayY
);
178 for (std::vector
<gfx::Display
>::iterator iter
= displays
.begin();
179 iter
!= displays
.end(); ++iter
) {
180 if (iter
->id() == gfx::Display::kInvalidDisplayID
) {
185 // DisplayManager can be null during the boot.
186 Shell::GetInstance()->display_manager()->OnNativeDisplaysChanged(displays
);
189 } // namespace internal