--- ui/base/x/x11_display_util.cc.orig 2024-04-19 13:02:56 UTC +++ ui/base/x/x11_display_util.cc @@ -14,7 +14,6 @@ #include "base/bits.h" #include "base/command_line.h" -#include "base/containers/flat_map.h" #include "base/logging.h" #include "base/numerics/clamped_math.h" #include "base/ranges/algorithm.h" @@ -28,7 +27,6 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_f.h" -#include "ui/gfx/switches.h" #include "ui/gfx/x/atom_cache.h" #include "ui/gfx/x/connection.h" #include "ui/gfx/x/randr.h" @@ -43,89 +41,42 @@ constexpr std::pair kMinVersionXra constexpr const char kRandrEdidProperty[] = "EDID"; -std::map GetMonitors( - const x11::Response& reply) { - std::map output_to_monitor; - if (!reply) { - return output_to_monitor; - } - for (size_t monitor = 0; monitor < reply->monitors.size(); monitor++) { - for (x11::RandR::Output output : reply->monitors[monitor].outputs) { - output_to_monitor[output] = monitor; +std::map GetMonitors( + std::pair version, + x11::RandR* randr, + x11::Window window) { + std::map output_to_monitor; + if (version >= std::pair{1, 5}) { + if (auto reply = randr->GetMonitors({window}).Sync()) { + for (size_t monitor = 0; monitor < reply->monitors.size(); monitor++) { + for (x11::RandR::Output output : reply->monitors[monitor].outputs) { + output_to_monitor[output] = monitor; + } + } } } return output_to_monitor; } -x11::Future GetWorkAreaFuture( - x11::Connection* connection) { - return connection->GetProperty({ - .window = connection->default_root(), - .property = connection->GetAtom("_NET_WORKAREA"), - .long_length = 4, - }); -} - -gfx::Rect GetWorkAreaSync(x11::Future future) { - auto response = future.Sync(); - if (!response || response->format != 32 || response->value_len != 4) { - return gfx::Rect(); - } - const uint32_t* value = response->value->front_as(); - return gfx::Rect(value[0], value[1], value[2], value[3]); -} - -x11::Future GetIccProfileFuture( - x11::Connection* connection, - size_t monitor) { - std::string atom_name = monitor == 0 - ? "_ICC_PROFILE" - : base::StringPrintf("_ICC_PROFILE_%zu", monitor); - auto future = connection->GetProperty({ - .window = connection->default_root(), - .property = x11::GetAtom(atom_name.c_str()), - .long_length = std::numeric_limits::max(), - }); - future.IgnoreError(); - return future; -} - -gfx::ICCProfile GetIccProfileSync(x11::Future future) { - auto response = future.Sync(); - if (!response || !response->value->size()) { - return gfx::ICCProfile(); - } - return gfx::ICCProfile::FromData(response->value->data(), - response->value->size()); -} - -x11::Future GetEdidFuture( - x11::Connection* connection, - x11::RandR::Output output) { - auto future = connection->randr().GetOutputProperty({ - .output = output, - .property = x11::GetAtom(kRandrEdidProperty), - .long_length = 128, - }); - future.IgnoreError(); - return future; -} - // Sets the work area on a list of displays. The work area for each display // must already be initialized to the display bounds. At most one display out // of |displays| will be affected. void ClipWorkArea(std::vector* displays, - size_t primary_display_index, - const gfx::Rect& net_workarea) { - if (net_workarea.IsEmpty()) { + size_t primary_display_index) { + x11::Window x_root_window = ui::GetX11RootWindow(); + + std::vector value; + if (!x11::Connection::Get()->GetArrayProperty( + x_root_window, x11::GetAtom("_NET_WORKAREA"), &value) || + value.size() < 4) { return; } - auto get_work_area = [&](const display::Display& display) { float scale = display::Display::HasForceDeviceScaleFactor() ? display::Display::GetForcedDeviceScaleFactor() : display.device_scale_factor(); - return gfx::ScaleToEnclosingRect(net_workarea, 1.0f / scale); + return gfx::ScaleToEnclosingRect( + gfx::Rect(value[0], value[1], value[2], value[3]), 1.0f / scale); }; // If the work area entirely contains exactly one display, assume it's meant @@ -207,9 +158,14 @@ int DefaultBitsPerComponent() { return visual.bits_per_rgb_value; } -// Get the EDID data from the `output` and stores to `edid`. -std::vector GetEdidProperty( - x11::Response response) { +// Get the EDID data from the |output| and stores to |edid|. +std::vector GetEDIDProperty(x11::RandR* randr, + x11::RandR::Output output) { + auto future = randr->GetOutputProperty(x11::RandR::GetOutputPropertyRequest{ + .output = output, + .property = x11::GetAtom(kRandrEdidProperty), + .long_length = 128}); + auto response = future.Sync(); std::vector edid; if (response && response->format == 8 && response->type != x11::Atom::None) { edid = std::move(response->data); @@ -261,8 +217,7 @@ gfx::PointF DisplayOriginPxToDip(const display::Displa std::vector GetFallbackDisplayList( float scale, size_t* primary_display_index_out) { - auto* connection = x11::Connection::Get(); - const auto& screen = connection->default_screen(); + const auto& screen = x11::Connection::Get()->default_screen(); gfx::Size physical_size(screen.width_in_millimeters, screen.height_in_millimeters); @@ -289,9 +244,7 @@ std::vector GetFallbackDisplayList( std::vector displays{gfx_display}; *primary_display_index_out = 0; - ClipWorkArea(&displays, *primary_display_index_out, - GetWorkAreaSync(GetWorkAreaFuture(connection))); - + ClipWorkArea(&displays, *primary_display_index_out); return displays; } @@ -307,16 +260,7 @@ std::vector BuildDisplaysFromXRandRI auto x_root_window = ui::GetX11RootWindow(); std::vector displays; - auto resources_future = randr.GetScreenResourcesCurrent({x_root_window}); - auto output_primary_future = randr.GetOutputPrimary({x_root_window}); - x11::Future monitors_future; - if (connection->randr_version() >= std::pair{1, 5}) { - monitors_future = randr.GetMonitors(x_root_window); - } - auto work_area_future = GetWorkAreaFuture(connection); - connection->Flush(); - - auto resources = resources_future.Sync(); + auto resources = randr.GetScreenResourcesCurrent({x_root_window}).Sync(); if (!resources) { LOG(ERROR) << "XRandR returned no displays; falling back to root window"; return GetFallbackDisplayList(primary_scale, primary_display_index_out); @@ -325,65 +269,21 @@ std::vector BuildDisplaysFromXRandRI const int depth = connection->default_screen().root_depth; const int bits_per_component = DefaultBitsPerComponent(); - auto output_primary = output_primary_future.Sync(); + std::map output_to_monitor = + GetMonitors(connection->randr_version(), &randr, x_root_window); + auto output_primary = randr.GetOutputPrimary({x_root_window}).Sync(); if (!output_primary) { return GetFallbackDisplayList(primary_scale, primary_display_index_out); } x11::RandR::Output primary_display_id = output_primary->output; - const auto monitors_reply = monitors_future.Sync(); - const auto output_to_monitor = GetMonitors(monitors_reply); - const size_t n_iccs = - monitors_reply ? std::max(1, monitors_reply->monitors.size()) : 1; - int explicit_primary_display_index = -1; int monitor_order_primary_display_index = -1; - std::vector> crtc_futures{}; - crtc_futures.reserve(resources->crtcs.size()); - for (auto crtc : resources->crtcs) { - crtc_futures.push_back( - randr.GetCrtcInfo({crtc, resources->config_timestamp})); - } - connection->Flush(); - - std::vector> icc_futures{n_iccs}; - if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless)) { - for (size_t monitor = 0; monitor < n_iccs; ++monitor) { - icc_futures[monitor] = GetIccProfileFuture(connection, monitor); - } - connection->Flush(); - } - - std::vector> output_futures{}; - output_futures.reserve(resources->outputs.size()); - for (auto output : resources->outputs) { - output_futures.push_back( - randr.GetOutputInfo({output, resources->config_timestamp})); - } - connection->Flush(); - - std::vector> edid_futures{}; - edid_futures.reserve(resources->outputs.size()); - for (auto output : resources->outputs) { - edid_futures.push_back(GetEdidFuture(connection, output)); - } - connection->Flush(); - - base::flat_map crtcs; - for (size_t i = 0; i < resources->crtcs.size(); ++i) { - crtcs.emplace(resources->crtcs[i], crtc_futures[i].Sync()); - } - - std::vector iccs; - iccs.reserve(n_iccs); - for (auto& future : icc_futures) { - iccs.push_back(GetIccProfileSync(std::move(future))); - } - for (size_t i = 0; i < resources->outputs.size(); i++) { x11::RandR::Output output_id = resources->outputs[i]; - auto output_info = output_futures[i].Sync(); + auto output_info = + randr.GetOutputInfo({output_id, resources->config_timestamp}).Sync(); if (!output_info) { continue; } @@ -398,16 +298,15 @@ std::vector BuildDisplaysFromXRandRI continue; } - auto crtc_it = crtcs.find(output_info->crtc); - if (crtc_it == crtcs.end()) { - continue; - } - const auto& crtc = crtc_it->second; + auto crtc = + randr.GetCrtcInfo({output_info->crtc, resources->config_timestamp}) + .Sync(); if (!crtc) { continue; } - display::EdidParser edid_parser(GetEdidProperty(edid_futures[i].Sync())); + display::EdidParser edid_parser( + GetEDIDProperty(&randr, static_cast(output_id))); auto output_32 = static_cast(output_id); int64_t display_id = output_32 > 0xff ? 0 : edid_parser.GetIndexBasedDisplayId(output_32); @@ -462,9 +361,8 @@ std::vector BuildDisplaysFromXRandRI } if (!display::HasForceDisplayColorProfile()) { - const size_t monitor = - monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second; - const auto& icc_profile = iccs[monitor < iccs.size() ? monitor : 0]; + gfx::ICCProfile icc_profile = ui::GetICCProfileForMonitor( + monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second); gfx::ColorSpace color_space = icc_profile.GetPrimariesOnlyColorSpace(); // Most folks do not have an ICC profile set up, but we still want to @@ -509,24 +407,63 @@ std::vector BuildDisplaysFromXRandRI ConvertDisplayBoundsToDips(&displays, *primary_display_index_out); } - ClipWorkArea(&displays, *primary_display_index_out, - GetWorkAreaSync(std::move(work_area_future))); + ClipWorkArea(&displays, *primary_display_index_out); return displays; } base::TimeDelta GetPrimaryDisplayRefreshIntervalFromXrandr() { constexpr base::TimeDelta kDefaultInterval = base::Seconds(1. / 60); - - size_t primary_display_index = 0; - auto displays = BuildDisplaysFromXRandRInfo(display::DisplayConfig(), - &primary_display_index); - CHECK_LT(primary_display_index, displays.size()); - + x11::RandR randr = x11::Connection::Get()->randr(); + auto root = ui::GetX11RootWindow(); + auto resources = randr.GetScreenResourcesCurrent({root}).Sync(); + if (!resources) { + return kDefaultInterval; + } // TODO(crbug.com/726842): It might make sense here to pick the output that // the window is on. On the other hand, if compositing is enabled, all drawing // might be synced to the primary output anyway. Needs investigation. - auto frequency = displays[primary_display_index].display_frequency(); - return frequency > 0 ? base::Seconds(1. / frequency) : kDefaultInterval; + auto output_primary = randr.GetOutputPrimary({root}).Sync(); + if (!output_primary) { + return kDefaultInterval; + } + x11::RandR::Output primary_output = output_primary->output; + bool disconnected_primary = false; + for (size_t i = 0; i < resources->outputs.size(); i++) { + if (!disconnected_primary && resources->outputs[i] != primary_output) { + continue; + } + + auto output_info = + randr.GetOutputInfo({primary_output, resources->config_timestamp}) + .Sync(); + if (!output_info) { + continue; + } + + if (output_info->connection != x11::RandR::RandRConnection::Connected) { + // If the primary monitor is disconnected, then start over and choose the + // first connected monitor instead. + if (!disconnected_primary) { + disconnected_primary = true; + i = -1; + } + continue; + } + auto crtc = + randr.GetCrtcInfo({output_info->crtc, resources->config_timestamp}) + .Sync(); + if (!crtc) { + continue; + } + float refresh_rate = + GetRefreshRateFromXRRModeInfo(resources->modes, crtc->mode); + if (refresh_rate == 0) { + continue; + } + + return base::Seconds(1. / refresh_rate); + } + return kDefaultInterval; } int RangeDistance(int min1, int max1, int min2, int max2) {