| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/ozone/platform/wayland/host/wayland_output.h" |
| |
| #include <xdg-output-unstable-v1-client-protocol.h> |
| |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "ui/display/display.h" |
| #include "ui/gfx/color_space.h" |
| #include "ui/ozone/platform/wayland/host/dump_util.h" |
| #include "ui/ozone/platform/wayland/host/wayland_connection.h" |
| #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" |
| #include "ui/ozone/platform/wayland/host/wayland_wp_color_management_output.h" |
| #include "ui/ozone/platform/wayland/host/wayland_wp_color_manager.h" |
| #include "ui/ozone/platform/wayland/host/xdg_output.h" |
| |
| namespace ui { |
| |
| namespace { |
| constexpr uint32_t kMinVersion = 2; |
| constexpr uint32_t kMaxVersion = 4; |
| } // namespace |
| |
| using Metrics = WaylandOutput::Metrics; |
| |
| // static |
| constexpr char WaylandOutput::kInterfaceName[]; |
| |
| // static |
| void WaylandOutput::Instantiate(WaylandConnection* connection, |
| wl_registry* registry, |
| uint32_t name, |
| const std::string& interface, |
| uint32_t version) { |
| CHECK_EQ(interface, kInterfaceName) << "Expected \"" << kInterfaceName |
| << "\" but got \"" << interface << "\""; |
| |
| if (!wl::CanBind(interface, version, kMinVersion, kMaxVersion)) { |
| return; |
| } |
| |
| auto output = |
| wl::Bind<wl_output>(registry, name, std::min(version, kMaxVersion)); |
| if (!output) { |
| LOG(ERROR) << "Failed to bind to wl_output global"; |
| return; |
| } |
| |
| if (!connection->output_manager_) { |
| connection->output_manager_ = |
| std::make_unique<WaylandOutputManager>(connection); |
| } |
| connection->output_manager_->AddWaylandOutput(name, output.release()); |
| } |
| |
| Metrics::Metrics() = default; |
| Metrics::Metrics(Id output_id, |
| int64_t display_id, |
| gfx::Point origin, |
| gfx::Size logical_size, |
| gfx::Size physical_size, |
| gfx::Insets insets, |
| gfx::Insets physical_overscan_insets, |
| float scale_factor, |
| int32_t panel_transform, |
| int32_t logical_transform, |
| const std::string& description) |
| : output_id(output_id), |
| display_id(display_id), |
| origin(origin), |
| logical_size(logical_size), |
| physical_size(physical_size), |
| insets(insets), |
| physical_overscan_insets(physical_overscan_insets), |
| scale_factor(scale_factor), |
| panel_transform(panel_transform), |
| logical_transform(logical_transform), |
| description(description) {} |
| Metrics::Metrics(const Metrics&) = default; |
| Metrics& Metrics::operator=(const Metrics&) = default; |
| Metrics::Metrics(Metrics&&) = default; |
| Metrics& Metrics::operator=(Metrics&&) = default; |
| Metrics::~Metrics() = default; |
| |
| void Metrics::DumpState(std::ostream& out) const { |
| constexpr auto kTransformMap = base::MakeFixedFlatMap<int32_t, const char*>({ |
| {WL_OUTPUT_TRANSFORM_NORMAL, "normal"}, |
| {WL_OUTPUT_TRANSFORM_90, "90"}, |
| {WL_OUTPUT_TRANSFORM_180, "180"}, |
| {WL_OUTPUT_TRANSFORM_270, "270"}, |
| {WL_OUTPUT_TRANSFORM_FLIPPED, "flipped"}, |
| {WL_OUTPUT_TRANSFORM_FLIPPED_90, "flipped 90"}, |
| {WL_OUTPUT_TRANSFORM_FLIPPED_180, "flipped 180"}, |
| {WL_OUTPUT_TRANSFORM_FLIPPED_270, "flipped 270"}, |
| }); |
| |
| out << "output_id=" << output_id << ", display_id=" << display_id |
| << ", description=" << description << ", origin=" << origin.ToString() |
| << ", logical_size=" << logical_size.ToString() |
| << ", physicacl_size=" << physical_size.ToString() |
| << ", scale_factor=" << scale_factor |
| << ", work_area_insets=" << insets.ToString() |
| << ", overscacn_insets=" << physical_overscan_insets.ToString() |
| << ", panel_transform=" |
| << GetMapValueOrDefault(kTransformMap, panel_transform) |
| << ", logical_transform=" |
| << GetMapValueOrDefault(kTransformMap, logical_transform); |
| } |
| |
| WaylandOutput::WaylandOutput(Id output_id, |
| wl_output* output, |
| WaylandConnection* connection) |
| : output_id_(output_id), output_(output), connection_(connection) { |
| wl_output_set_user_data(output_.get(), this); |
| } |
| |
| WaylandOutput::~WaylandOutput() { |
| wl_output_set_user_data(output_.get(), nullptr); |
| } |
| |
| void WaylandOutput::InitializeXdgOutput( |
| zxdg_output_manager_v1* xdg_output_manager) { |
| DCHECK(!xdg_output_); |
| xdg_output_ = std::make_unique<XDGOutput>( |
| zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output_.get())); |
| } |
| |
| void WaylandOutput::InitializeWpColorManagementOutput( |
| WaylandWpColorManager* wp_color_manager) { |
| DCHECK(!wp_color_management_output_); |
| wp_color_management_output_ = |
| std::make_unique<WaylandWpColorManagementOutput>( |
| wp_color_manager->CreateColorManagementOutput(output_.get()) |
| .release(), |
| this, connection_); |
| } |
| |
| void WaylandOutput::Initialize(Delegate* delegate) { |
| DCHECK(!delegate_); |
| delegate_ = delegate; |
| static constexpr wl_output_listener kOutputListener = { |
| .geometry = &OnGeometry, |
| .mode = &OnMode, |
| .done = &OnDone, |
| .scale = &OnScale, |
| .name = &OnName, |
| .description = &OnDescription, |
| }; |
| wl_output_add_listener(output_.get(), &kOutputListener, this); |
| } |
| |
| const Metrics& WaylandOutput::GetMetrics() const { |
| return metrics_; |
| } |
| |
| void WaylandOutput::SetMetrics(const Metrics& metrics) { |
| metrics_ = metrics; |
| } |
| |
| float WaylandOutput::scale_factor() const { |
| return metrics_.scale_factor; |
| } |
| |
| bool WaylandOutput::IsReady() const { |
| return is_ready_; |
| } |
| |
| void WaylandOutput::SetScaleFactorForTesting(float scale_factor) { |
| metrics_.scale_factor = scale_factor; |
| } |
| |
| void WaylandOutput::TriggerDelegateNotifications() { |
| // Wait until the all outputs receives enough information to generate display |
| // information. |
| if (!IsReady()) |
| return; |
| |
| delegate_->OnOutputHandleMetrics(GetMetrics()); |
| } |
| |
| void WaylandOutput::DumpState(std::ostream& out) const { |
| metrics_.DumpState(out); |
| } |
| |
| void WaylandOutput::UpdateMetrics() { |
| metrics_.output_id = output_id_; |
| // For the non-aura case we map the global output "name" to the display_id. |
| metrics_.display_id = output_id_; |
| metrics_.origin = origin_; |
| metrics_.physical_size = physical_size_; |
| metrics_.scale_factor = scale_factor_; |
| metrics_.panel_transform = panel_transform_; |
| metrics_.logical_transform = panel_transform_; |
| metrics_.name = name_; |
| metrics_.description = description_; |
| |
| if (xdg_output_) { |
| xdg_output_->UpdateMetrics( |
| connection_->supports_viewporter_surface_scaling(), metrics_); |
| } |
| } |
| |
| // static |
| void WaylandOutput::OnGeometry(void* data, |
| wl_output* output, |
| int32_t x, |
| int32_t y, |
| int32_t physical_width, |
| int32_t physical_height, |
| int32_t subpixel, |
| const char* make, |
| const char* model, |
| int32_t output_transform) { |
| if (auto* self = static_cast<WaylandOutput*>(data)) { |
| // It looks like there is a bug in libffi - only the 8th arg is affected. |
| // Possibly it is not following the calling convention of the ABI? Eg. the |
| // lib has some off-by-1-error where it's supposed to pass 8 args in regs |
| // and the rest on the stack but instead it's passing 7 in regs. This is |
| // out of our control. Given the output_transform is always correct, |
| // unpoison the value to make MSAN happy. |
| MSAN_UNPOISON(&output_transform, sizeof(int32_t)); |
| self->origin_ = gfx::Point(x, y); |
| self->panel_transform_ = output_transform; |
| } |
| } |
| |
| // static |
| void WaylandOutput::OnMode(void* data, |
| wl_output* wl_output, |
| uint32_t flags, |
| int32_t width, |
| int32_t height, |
| int32_t refresh) { |
| auto* self = static_cast<WaylandOutput*>(data); |
| if (self && (flags & WL_OUTPUT_MODE_CURRENT)) { |
| self->physical_size_ = gfx::Size(width, height); |
| } |
| } |
| |
| // static |
| void WaylandOutput::OnDone(void* data, wl_output* output) { |
| auto* self = static_cast<WaylandOutput*>(data); |
| if (!self) { |
| return; |
| } |
| |
| self->is_ready_ = true; |
| |
| if (auto& xdg_output = self->xdg_output_) { |
| xdg_output->HandleDone(); |
| } |
| |
| // Once all metrics have been received perform an atomic update on this |
| // output's `metrics_`. |
| self->UpdateMetrics(); |
| |
| self->TriggerDelegateNotifications(); |
| } |
| |
| // static |
| void WaylandOutput::OnScale(void* data, wl_output* output, int32_t factor) { |
| if (auto* self = static_cast<WaylandOutput*>(data)) { |
| self->scale_factor_ = factor; |
| } |
| } |
| |
| // static |
| void WaylandOutput::OnName(void* data, wl_output* output, const char* name) { |
| if (auto* self = static_cast<WaylandOutput*>(data)) { |
| self->name_ = name ? std::string(name) : std::string{}; |
| } |
| } |
| |
| // static |
| void WaylandOutput::OnDescription(void* data, |
| wl_output* output, |
| const char* description) { |
| if (auto* self = static_cast<WaylandOutput*>(data)) { |
| self->description_ = description ? std::string(description) : std::string{}; |
| } |
| } |
| |
| } // namespace ui |