1 // Copyright 2018 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.
4
5 #include "ui/base/x/x11_display_util.h"
6
7 #include <dlfcn.h>
8
9 #include <bitset>
10
11 #include "base/bits.h"
12 #include "base/compiler_specific.h"
13 #include "base/logging.h"
14 #include "ui/base/x/x11_util.h"
15 #include "ui/display/util/display_util.h"
16 #include "ui/display/util/edid_parser.h"
17 #include "ui/gfx/color_space.h"
18 #include "ui/gfx/geometry/matrix3_f.h"
19 #include "ui/gfx/geometry/rect.h"
20 #include "ui/gfx/geometry/vector3d_f.h"
21 #include "ui/gfx/x/x11.h"
22 #include "ui/gfx/x/x11_atom_cache.h"
23
24 namespace ui {
25
26 namespace {
27
28 constexpr int kMinVersionXrandr = 103; // Need at least xrandr version 1.3.
29
30 typedef XRRMonitorInfo* (*XRRGetMonitors)(::Display*, Window, bool, int*);
31 typedef void (*XRRFreeMonitors)(XRRMonitorInfo*);
32
33 NO_SANITIZE("cfi-icall")
GetMonitors(int version,XDisplay * xdisplay,GLXWindow window)34 std::map<RROutput, int> GetMonitors(int version,
35 XDisplay* xdisplay,
36 GLXWindow window) {
37 std::map<RROutput, int> output_to_monitor;
38 if (version >= 105) {
39 void* xrandr_lib = dlopen(nullptr, RTLD_NOW);
40 if (xrandr_lib) {
41 static XRRGetMonitors XRRGetMonitors_ptr =
42 reinterpret_cast<XRRGetMonitors>(dlsym(xrandr_lib, "XRRGetMonitors"));
43 static XRRFreeMonitors XRRFreeMonitors_ptr =
44 reinterpret_cast<XRRFreeMonitors>(
45 dlsym(xrandr_lib, "XRRFreeMonitors"));
46 if (XRRGetMonitors_ptr && XRRFreeMonitors_ptr) {
47 int nmonitors = 0;
48 XRRMonitorInfo* monitors =
49 XRRGetMonitors_ptr(xdisplay, window, false, &nmonitors);
50 for (int monitor = 0; monitor < nmonitors; monitor++) {
51 for (int j = 0; j < monitors[monitor].noutput; j++) {
52 output_to_monitor[monitors[monitor].outputs[j]] = monitor;
53 }
54 }
55 XRRFreeMonitors_ptr(monitors);
56 }
57 }
58 }
59 return output_to_monitor;
60 }
61
62 // Sets the work area on a list of displays. The work area for each display
63 // must already be initialized to the display bounds. At most one display out
64 // of |displays| will be affected.
ClipWorkArea(std::vector<display::Display> * displays,int64_t primary_display_index,float scale)65 void ClipWorkArea(std::vector<display::Display>* displays,
66 int64_t primary_display_index,
67 float scale) {
68 XDisplay* xdisplay = gfx::GetXDisplay();
69 GLXWindow x_root_window = DefaultRootWindow(xdisplay);
70
71 std::vector<int> value;
72 if (!ui::GetIntArrayProperty(x_root_window, "_NET_WORKAREA", &value) ||
73 value.size() < 4) {
74 return;
75 }
76 gfx::Rect work_area = gfx::ScaleToEnclosingRect(
77 gfx::Rect(value[0], value[1], value[2], value[3]), 1.0f / scale);
78
79 // If the work area entirely contains exactly one display, assume it's meant
80 // for that display (and so do nothing).
81 if (std::count_if(displays->begin(), displays->end(),
82 [&](const display::Display& display) {
83 return work_area.Contains(display.bounds());
84 }) == 1) {
85 return;
86 }
87
88 // If the work area is entirely contained within exactly one display, assume
89 // it's meant for that display and intersect the work area with only that
90 // display.
91 auto found = std::find_if(displays->begin(), displays->end(),
92 [&](const display::Display& display) {
93 return display.bounds().Contains(work_area);
94 });
95
96 // If the work area spans multiple displays, intersect the work area with the
97 // primary display, like GTK does.
98 display::Display& primary =
99 found == displays->end() ? (*displays)[primary_display_index] : *found;
100
101 work_area.Intersect(primary.work_area());
102 if (!work_area.IsEmpty())
103 primary.set_work_area(work_area);
104 }
105
GetRefreshRateFromXRRModeInfo(XRRModeInfo * modes,int num_of_mode,RRMode current_mode_id)106 float GetRefreshRateFromXRRModeInfo(XRRModeInfo* modes,
107 int num_of_mode,
108 RRMode current_mode_id) {
109 for (int i = 0; i < num_of_mode; i++) {
110 XRRModeInfo mode_info = modes[i];
111 if (mode_info.id != current_mode_id)
112 continue;
113 if (!mode_info.hTotal || !mode_info.vTotal)
114 return 0;
115
116 // Refresh Rate = Pixel Clock / (Horizontal Total * Vertical Total)
117 return mode_info.dotClock /
118 static_cast<float>(mode_info.hTotal * mode_info.vTotal);
119 }
120 return 0;
121 }
122
DefaultScreenDepth(XDisplay * xdisplay)123 int DefaultScreenDepth(XDisplay* xdisplay) {
124 return DefaultDepth(xdisplay, DefaultScreen(xdisplay));
125 }
126
DefaultBitsPerComponent(XDisplay * xdisplay)127 int DefaultBitsPerComponent(XDisplay* xdisplay) {
128 Visual* visual = DefaultVisual(xdisplay, DefaultScreen(xdisplay));
129
130 // The mask fields are only valid for DirectColor and TrueColor classes.
131 if (visual->c_class == DirectColor || visual->c_class == TrueColor) {
132 // RGB components are packed into fixed size integers for each visual. The
133 // layout of bits in the packing is given by
134 // |visual->{red,green,blue}_mask|. Count the number of bits to get the
135 // number of bits per component.
136 auto bits = [](auto mask) {
137 return std::bitset<sizeof(mask) * 8>{mask}.count();
138 };
139 size_t red_bits = bits(visual->red_mask);
140 size_t green_bits = bits(visual->green_mask);
141 size_t blue_bits = bits(visual->blue_mask);
142 if (red_bits == green_bits && red_bits == blue_bits)
143 return red_bits;
144 }
145
146 // Next, try getting the number of colormap entries per subfield. If it's a
147 // power of 2, log2 is a possible guess for the number of bits per component.
148 if (base::bits::IsPowerOfTwo(visual->map_entries))
149 return base::bits::Log2Ceiling(visual->map_entries);
150
151 // |bits_per_rgb| can sometimes be unreliable (may be 11 for 30bpp visuals),
152 // so only use it as a last resort.
153 return visual->bits_per_rgb;
154 }
155
IsRandRAvailable()156 bool IsRandRAvailable() {
157 int randr_version_major = 0;
158 int randr_version_minor = 0;
159 static bool is_randr_available = XRRQueryVersion(
160 gfx::GetXDisplay(), &randr_version_major, &randr_version_minor);
161 return is_randr_available;
162 }
163
164 // Get the EDID data from the |output| and stores to |edid|.
GetEDIDProperty(XID output,std::vector<uint8_t> * edid)165 void GetEDIDProperty(XID output, std::vector<uint8_t>* edid) {
166 Display* display = gfx::GetXDisplay();
167 if (!display)
168 return;
169
170 if (!IsRandRAvailable())
171 return;
172
173 Atom edid_property = gfx::GetAtom(RR_PROPERTY_RANDR_EDID);
174
175 bool has_edid_property = false;
176 int num_properties = 0;
177 gfx::XScopedPtr<Atom[]> properties(
178 XRRListOutputProperties(display, output, &num_properties));
179 for (int i = 0; i < num_properties; ++i) {
180 if (properties[i] == edid_property) {
181 has_edid_property = true;
182 break;
183 }
184 }
185 if (!has_edid_property)
186 return;
187
188 Atom actual_type;
189 int actual_format;
190 unsigned long bytes_after;
191 unsigned long nitems = 0;
192 unsigned char* prop = nullptr;
193 XRRGetOutputProperty(display, output, edid_property,
194 0, // offset
195 128, // length
196 false, // _delete
197 false, // pending
198 AnyPropertyType, // req_type
199 &actual_type, &actual_format, &nitems, &bytes_after,
200 &prop);
201 DCHECK_EQ(XA_INTEGER, actual_type);
202 DCHECK_EQ(8, actual_format);
203 edid->assign(prop, prop + nitems);
204 XFree(prop);
205 }
206
207 } // namespace
208
GetXrandrVersion(XDisplay * xdisplay)209 int GetXrandrVersion(XDisplay* xdisplay) {
210 int xrandr_version = 0;
211 // We only support 1.3+. There were library changes before this and we should
212 // use the new interface instead of the 1.2 one.
213 int randr_version_major = 0;
214 int randr_version_minor = 0;
215 if (XRRQueryVersion(xdisplay, &randr_version_major, &randr_version_minor)) {
216 xrandr_version = randr_version_major * 100 + randr_version_minor;
217 }
218 return xrandr_version;
219 }
220
GetFallbackDisplayList(float scale)221 std::vector<display::Display> GetFallbackDisplayList(float scale) {
222 XDisplay* display = gfx::GetXDisplay();
223 ::Screen* screen = DefaultScreenOfDisplay(display);
224 gfx::Size physical_size(WidthMMOfScreen(screen), HeightMMOfScreen(screen));
225
226 int width = WidthOfScreen(screen);
227 int height = HeightOfScreen(screen);
228 gfx::Rect bounds_in_pixels(0, 0, width, height);
229 display::Display gfx_display(0, bounds_in_pixels);
230
231 if (!display::Display::HasForceDeviceScaleFactor() &&
232 !display::IsDisplaySizeBlackListed(physical_size)) {
233 DCHECK_LE(1.0f, scale);
234 gfx_display.SetScaleAndBounds(scale, bounds_in_pixels);
235 gfx_display.set_work_area(
236 gfx::ScaleToEnclosingRect(bounds_in_pixels, 1.0f / scale));
237 } else {
238 scale = 1;
239 }
240
241 gfx_display.set_color_depth(DefaultScreenDepth(display));
242 gfx_display.set_depth_per_component(DefaultBitsPerComponent(display));
243
244 std::vector<display::Display> displays{gfx_display};
245 ClipWorkArea(&displays, 0, scale);
246 return displays;
247 }
248
BuildDisplaysFromXRandRInfo(int version,float scale,int64_t * primary_display_index_out)249 std::vector<display::Display> BuildDisplaysFromXRandRInfo(
250 int version,
251 float scale,
252 int64_t* primary_display_index_out) {
253 DCHECK(primary_display_index_out);
254 DCHECK_GE(version, kMinVersionXrandr);
255 XDisplay* xdisplay = gfx::GetXDisplay();
256 GLXWindow x_root_window = DefaultRootWindow(xdisplay);
257 std::vector<display::Display> displays;
258 gfx::XScopedPtr<
259 XRRScreenResources,
260 gfx::XObjectDeleter<XRRScreenResources, void, XRRFreeScreenResources>>
261 resources(XRRGetScreenResourcesCurrent(xdisplay, x_root_window));
262 if (!resources) {
263 LOG(ERROR) << "XRandR returned no displays; falling back to root window";
264 return GetFallbackDisplayList(scale);
265 }
266
267 const int depth = DefaultScreenDepth(xdisplay);
268 const int bits_per_component = DefaultBitsPerComponent(xdisplay);
269
270 std::map<RROutput, int> output_to_monitor =
271 GetMonitors(version, xdisplay, x_root_window);
272 *primary_display_index_out = 0;
273 RROutput primary_display_id = XRRGetOutputPrimary(xdisplay, x_root_window);
274
275 int explicit_primary_display_index = -1;
276 int monitor_order_primary_display_index = -1;
277
278 // As per-display scale factor is not supported right now,
279 // the X11 root window's scale factor is always used.
280 for (int i = 0; i < resources->noutput; ++i) {
281 RROutput output_id = resources->outputs[i];
282 gfx::XScopedPtr<XRROutputInfo,
283 gfx::XObjectDeleter<XRROutputInfo, void, XRRFreeOutputInfo>>
284 output_info(XRRGetOutputInfo(xdisplay, resources.get(), output_id));
285
286 // XRRGetOutputInfo returns null in some cases: https://crbug.com/921490
287 if (!output_info)
288 continue;
289
290 bool is_connected = (output_info->connection == RR_Connected);
291 if (!is_connected)
292 continue;
293
294 bool is_primary_display = (output_id == primary_display_id);
295
296 if (output_info->crtc) {
297 gfx::XScopedPtr<XRRCrtcInfo,
298 gfx::XObjectDeleter<XRRCrtcInfo, void, XRRFreeCrtcInfo>>
299 crtc(XRRGetCrtcInfo(xdisplay, resources.get(), output_info->crtc));
300
301 std::vector<uint8_t> edid_bytes;
302 GetEDIDProperty(output_id, &edid_bytes);
303 display::EdidParser edid_parser(edid_bytes);
304 int64_t display_id = edid_parser.GetDisplayId(output_id);
305 // It isn't ideal, but if we can't parse the EDID data, fall back on the
306 // display number.
307 if (!display_id)
308 display_id = i;
309
310 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
311 display::Display display(display_id, crtc_bounds);
312
313 if (!display::Display::HasForceDeviceScaleFactor()) {
314 display.SetScaleAndBounds(scale, crtc_bounds);
315 display.set_work_area(
316 gfx::ScaleToEnclosingRect(crtc_bounds, 1.0f / scale));
317 }
318
319 switch (crtc->rotation) {
320 case RR_Rotate_0:
321 display.set_rotation(display::Display::ROTATE_0);
322 break;
323 case RR_Rotate_90:
324 display.set_rotation(display::Display::ROTATE_90);
325 break;
326 case RR_Rotate_180:
327 display.set_rotation(display::Display::ROTATE_180);
328 break;
329 case RR_Rotate_270:
330 display.set_rotation(display::Display::ROTATE_270);
331 break;
332 }
333
334 if (is_primary_display)
335 explicit_primary_display_index = displays.size();
336
337 auto monitor_iter = output_to_monitor.find(output_id);
338 if (monitor_iter != output_to_monitor.end() && monitor_iter->second == 0)
339 monitor_order_primary_display_index = displays.size();
340
341 if (!display::Display::HasForceDisplayColorProfile()) {
342 gfx::ICCProfile icc_profile = ui::GetICCProfileForMonitor(
343 monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second);
344 gfx::ColorSpace color_space = icc_profile.GetPrimariesOnlyColorSpace();
345
346 // Most folks do not have an ICC profile set up, but we still want to
347 // detect if a display has a wide color gamut so that HDR videos can be
348 // enabled. Only do this if |bits_per_component| > 8 or else SDR
349 // screens may have washed out colors.
350 if (bits_per_component > 8 && !color_space.IsValid())
351 color_space = display::GetColorSpaceFromEdid(edid_parser);
352
353 display.set_color_spaces(
354 gfx::DisplayColorSpaces(color_space, gfx::BufferFormat::BGRA_8888));
355 }
356
357 display.set_color_depth(depth);
358 display.set_depth_per_component(bits_per_component);
359
360 // Set monitor refresh rate
361 int refresh_rate = static_cast<int>(GetRefreshRateFromXRRModeInfo(
362 resources->modes, resources->nmode, crtc->mode));
363 display.set_display_frequency(refresh_rate);
364
365 displays.push_back(display);
366 }
367 }
368
369 if (explicit_primary_display_index != -1) {
370 *primary_display_index_out = explicit_primary_display_index;
371 } else if (monitor_order_primary_display_index != -1) {
372 *primary_display_index_out = monitor_order_primary_display_index;
373 }
374
375 if (displays.empty())
376 return GetFallbackDisplayList(scale);
377
378 ClipWorkArea(&displays, *primary_display_index_out, scale);
379 return displays;
380 }
381
GetPrimaryDisplayRefreshIntervalFromXrandr(Display * display)382 base::TimeDelta GetPrimaryDisplayRefreshIntervalFromXrandr(Display* display) {
383 constexpr base::TimeDelta kDefaultInterval =
384 base::TimeDelta::FromSecondsD(1. / 60);
385 GLXWindow root = DefaultRootWindow(display);
386 gfx::XScopedPtr<
387 XRRScreenResources,
388 gfx::XObjectDeleter<XRRScreenResources, void, XRRFreeScreenResources>>
389 resources(XRRGetScreenResourcesCurrent(display, root));
390 if (!resources)
391 return kDefaultInterval;
392 // TODO(crbug.com/726842): It might make sense here to pick the output that
393 // the window is on. On the other hand, if compositing is enabled, all drawing
394 // might be synced to the primary output anyway. Needs investigation.
395 RROutput primary_output = XRRGetOutputPrimary(display, root);
396 bool disconnected_primary = false;
397 for (int i = 0; i < resources->noutput; i++) {
398 if (!disconnected_primary && resources->outputs[i] != primary_output)
399 continue;
400
401 gfx::XScopedPtr<XRROutputInfo,
402 gfx::XObjectDeleter<XRROutputInfo, void, XRRFreeOutputInfo>>
403 output_info(XRRGetOutputInfo(display, resources.get(), primary_output));
404 if (!output_info)
405 continue;
406
407 if (output_info->connection != RR_Connected) {
408 // If the primary monitor is disconnected, then start over and choose the
409 // first connected monitor instead.
410 if (!disconnected_primary) {
411 disconnected_primary = true;
412 i = -1;
413 }
414 continue;
415 }
416 gfx::XScopedPtr<XRRCrtcInfo,
417 gfx::XObjectDeleter<XRRCrtcInfo, void, XRRFreeCrtcInfo>>
418 crtc(XRRGetCrtcInfo(display, resources.get(), output_info->crtc));
419 if (!crtc)
420 continue;
421 float refresh_rate = GetRefreshRateFromXRRModeInfo(
422 resources->modes, resources->nmode, crtc->mode);
423 if (refresh_rate == 0)
424 continue;
425
426 return base::TimeDelta::FromSecondsD(1. / refresh_rate);
427 }
428 return kDefaultInterval;
429 }
430
431 } // namespace ui
432