// 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