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