chromium/components/exo/wayland/zaura_output_manager_v2.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/exo/wayland/zaura_output_manager_v2.h"

#include <aura-output-management-server-protocol.h>
#include <wayland-server-core.h>

#include <memory>

#include "base/bit_cast.h"
#include "components/exo/wayland/output_metrics.h"
#include "components/exo/wayland/server_util.h"
#include "components/exo/wayland/wayland_display_observer.h"
#include "components/exo/wayland/wayland_display_output.h"
#include "ui/display/display.h"
#include "ui/display/display_observer.h"

namespace exo::wayland {

void bind_aura_output_manager_v2(wl_client* client,
                                 void* data,
                                 uint32_t version,
                                 uint32_t id) {
  wl_resource* outout_manager_resouce =
      wl_resource_create(client, &zaura_output_manager_v2_interface,
                         std::min(version, kZAuraOutputManagerV2Version), id);

  auto user_data = std::make_unique<AuraOutputManagerV2::UserData>(
      static_cast<AuraOutputManagerV2*>(data), outout_manager_resouce);
  SetImplementation(outout_manager_resouce, nullptr, std::move(user_data));
}

AuraOutputManagerV2::UserData::UserData(AuraOutputManagerV2* output_manager,
                                        wl_resource* outout_manager_resouce)
    : output_manager_(output_manager->GetWeakPtr()),
      outout_manager_resouce_(outout_manager_resouce) {
  output_manager_->Register(outout_manager_resouce);
}

AuraOutputManagerV2::UserData::~UserData() {
  if (output_manager_) {
    output_manager_->Unregister(outout_manager_resouce_.get());
  }
}

AuraOutputManagerV2::AuraOutputManagerV2(
    ActiveOutputGetter active_output_getter)
    : active_output_getter_(std::move(active_output_getter)) {}

AuraOutputManagerV2::~AuraOutputManagerV2() = default;

bool AuraOutputManagerV2::OnDidProcessDisplayChanges(
    const OutputConfigurationChange& configuration_change) {
  // A done event is required if any outputs have been added or removed.
  bool needs_done = !configuration_change.added_outputs.empty() ||
                    !configuration_change.removed_outputs.empty();

  // Send metrics for any newly-added displays.
  for (const WaylandDisplayOutput* added_output :
       configuration_change.added_outputs) {
    static constexpr uint32_t kAllDisplayChanges = 0xFFFFFFFF;
    SendOutputMetrics(*added_output, kAllDisplayChanges);
  }

  // Send metrics for any existing updated displays.
  for (const auto& change_pair : configuration_change.changed_outputs) {
    needs_done |= SendOutputMetrics(*change_pair.first, change_pair.second);
  }

  return needs_done;
}

void AuraOutputManagerV2::SendOutputActivated(
    const WaylandDisplayOutput& output) {
  for (wl_resource* manager_resource : manager_resources_) {
    const uint32_t output_name = wl_global_get_name(
        output.global(), wl_resource_get_client(manager_resource));
    zaura_output_manager_v2_send_activated(manager_resource, output_name);
  }
}

void AuraOutputManagerV2::SendDone() {
  for (wl_resource* manager_resource : manager_resources_) {
    zaura_output_manager_v2_send_done(manager_resource);
  }
}

void AuraOutputManagerV2::Register(wl_resource* manager_resource) {
  // When a client first binds to the global the current active global outputs
  // and their metrics make up the first configuration change.
  manager_resources_.insert(manager_resource);
  for (const WaylandDisplayOutput* output : active_output_getter_.Run()) {
    SendOutputMetricsForClient(*output, manager_resource);
  }
  SendDone();
}

void AuraOutputManagerV2::Unregister(wl_resource* manager_resource) {
  manager_resources_.erase(manager_resource);
}

base::WeakPtr<AuraOutputManagerV2> AuraOutputManagerV2::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

bool AuraOutputManagerV2::SendOutputMetrics(const WaylandDisplayOutput& output,
                                            uint32_t changed_metrics) {
  if (!(changed_metrics &
        (display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
         display::DisplayObserver::DISPLAY_METRIC_WORK_AREA |
         display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
         display::DisplayObserver::DISPLAY_METRIC_ROTATION))) {
    return false;
  }

  for (wl_resource* manager_resource : manager_resources_) {
    SendOutputMetricsForClient(output, manager_resource);
  }
  return true;
}

void AuraOutputManagerV2::SendOutputMetricsForClient(
    const WaylandDisplayOutput& output,
    wl_resource* manager_resource) {
  const uint32_t output_name = wl_global_get_name(
      output.global(), wl_resource_get_client(manager_resource));
  const OutputMetrics& output_metrics = output.metrics();

  const ui::wayland::WaylandDisplayIdPair& display_id =
      output_metrics.display_id;
  zaura_output_manager_v2_send_display_id(manager_resource, output_name,
                                          display_id.high, display_id.low);

  const auto& logical_origin = output_metrics.logical_origin;
  zaura_output_manager_v2_send_logical_position(
      manager_resource, output_name, logical_origin.x(), logical_origin.y());

  const auto& logical_size = output_metrics.logical_size;
  zaura_output_manager_v2_send_logical_size(manager_resource, output_name,
                                            logical_size.width(),
                                            logical_size.height());

  const auto& physical_size = output_metrics.physical_size_px;
  if (output_metrics.mode_flags & WL_OUTPUT_MODE_CURRENT) {
    zaura_output_manager_v2_send_physical_size(manager_resource, output_name,
                                               physical_size.width(),
                                               physical_size.height());
  }

  const auto& insets = output_metrics.logical_insets;
  zaura_output_manager_v2_send_work_area_insets(
      manager_resource, output_name, insets.top(), insets.left(),
      insets.bottom(), insets.right());

  const auto& overscan = output_metrics.physical_overscan_insets;
  zaura_output_manager_v2_send_overscan_insets(
      manager_resource, output_name, overscan.top(), overscan.left(),
      overscan.bottom(), overscan.right());

  // The float value is bit_cast<> into a uint32_t. It must later be cast back
  // into a float. This is because wayland does not support native transport of
  // floats. As different CPU architectures may use different endian
  // representations for IEEE 754 floats, this implicitly assumes that the
  // caller and receiver are the same machine.
  zaura_output_manager_v2_send_device_scale_factor(
      manager_resource, output_name,
      base::bit_cast<uint32_t>(output_metrics.device_scale_factor));

  zaura_output_manager_v2_send_logical_transform(
      manager_resource, output_name, output_metrics.logical_transform);

  zaura_output_manager_v2_send_panel_transform(manager_resource, output_name,
                                               output_metrics.panel_transform);

  zaura_output_manager_v2_send_description(manager_resource, output_name,
                                           output_metrics.description.c_str());
}

}  // namespace exo::wayland