// Copyright 2023 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.h"
#include <wayland-server-core.h>
#include <memory>
#include "components/exo/wayland/output_metrics.h"
#include "components/exo/wayland/server_util.h"
#include "components/exo/wayland/wayland_display_observer.h"
#include "ui/display/display.h"
#include "ui/display/display_observer.h"
#include "ui/display/screen.h"
namespace exo::wayland {
AuraOutputManager::AuraOutputManager(wl_resource* manager_resource)
: client_(wl_resource_get_client(manager_resource)),
manager_resource_(manager_resource) {}
// static.
AuraOutputManager* AuraOutputManager::Get(wl_client* client) {
// Avoid querying client resources if it has already begun destruction.
if (IsClientDestroyed(client)) {
return nullptr;
}
AuraOutputManager* output_manager = nullptr;
wl_client_for_each_resource(
client,
[](wl_resource* resource, void* user_data) {
constexpr char kAuraOutputManagerClass[] = "zaura_output_manager";
const char* class_name = wl_resource_get_class(resource);
if (std::strcmp(kAuraOutputManagerClass, class_name) != 0) {
return WL_ITERATOR_CONTINUE;
}
DCHECK_NE(nullptr, user_data);
AuraOutputManager** output_manager_ref =
static_cast<AuraOutputManager**>(user_data);
DCHECK_EQ(nullptr, *output_manager_ref);
*output_manager_ref = GetUserDataAs<AuraOutputManager>(resource);
return WL_ITERATOR_STOP;
},
&output_manager);
return output_manager;
}
// static
int64_t AuraOutputManager::GetDisplayIdForOutput(wl_resource* output_resource) {
if (!output_resource) {
return display::kInvalidDisplayId;
}
struct UserData {
raw_ptr<wl_resource> output_resource = nullptr;
int64_t display_id = display::kInvalidDisplayId;
};
auto user_data_iterator = [](wl_resource* resource, void* user_data) {
constexpr char kWlOutputClass[] = "wl_output";
const char* class_name = wl_resource_get_class(resource);
if (std::strcmp(kWlOutputClass, class_name) != 0) {
return WL_ITERATOR_CONTINUE;
}
UserData* data_ref = static_cast<UserData*>(user_data);
auto* display_handler_tmp = GetUserDataAs<WaylandDisplayHandler>(resource);
if (display_handler_tmp->output_resource() != data_ref->output_resource) {
return WL_ITERATOR_CONTINUE;
}
data_ref->display_id = display_handler_tmp->id();
return WL_ITERATOR_STOP;
};
auto* client = wl_resource_get_client(output_resource);
CHECK(client);
CHECK(!IsClientDestroyed(client));
UserData data{.output_resource = output_resource};
wl_client_for_each_resource(client, user_data_iterator, &data);
return data.display_id;
}
bool AuraOutputManager::SendOutputMetrics(wl_resource* output_resource,
const display::Display& display,
uint32_t changed_metrics) {
DCHECK_EQ(client_, wl_resource_get_client(output_resource));
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;
}
const OutputMetrics output_metrics(display);
const ui::wayland::WaylandDisplayIdPair& display_id =
output_metrics.display_id;
zaura_output_manager_send_display_id(manager_resource_, output_resource,
display_id.high, display_id.low);
const auto& logical_origin = output_metrics.logical_origin;
zaura_output_manager_send_logical_position(manager_resource_, output_resource,
logical_origin.x(),
logical_origin.y());
const auto& logical_size = output_metrics.logical_size;
zaura_output_manager_send_logical_size(manager_resource_, output_resource,
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_send_physical_size(manager_resource_, output_resource,
physical_size.width(),
physical_size.height());
}
const auto& insets = output_metrics.logical_insets;
zaura_output_manager_send_insets(manager_resource_, output_resource,
insets.top(), insets.left(), insets.bottom(),
insets.right());
if (wl_resource_get_version(manager_resource_) >=
ZAURA_OUTPUT_MANAGER_OVERSCAN_INSETS_SINCE_VERSION) {
const auto& overscan = output_metrics.physical_overscan_insets;
zaura_output_manager_send_overscan_insets(
manager_resource_, output_resource, 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_send_device_scale_factor(
manager_resource_, output_resource,
base::bit_cast<uint32_t>(output_metrics.device_scale_factor));
zaura_output_manager_send_logical_transform(
manager_resource_, output_resource, output_metrics.logical_transform);
zaura_output_manager_send_panel_transform(manager_resource_, output_resource,
output_metrics.panel_transform);
zaura_output_manager_send_description(manager_resource_, output_resource,
output_metrics.description.c_str());
zaura_output_manager_send_done(manager_resource_, output_resource);
return true;
}
void AuraOutputManager::SendOutputActivated(wl_resource* output_resource) {
if (wl_resource_get_version(manager_resource_) >=
ZAURA_OUTPUT_MANAGER_ACTIVATED_SINCE_VERSION) {
CHECK_EQ(client_, wl_resource_get_client(output_resource));
zaura_output_manager_send_activated(manager_resource_, output_resource);
}
}
void bind_aura_output_manager(wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
wl_resource* manager_resource =
wl_resource_create(client, &zaura_output_manager_interface,
std::min(version, kZAuraOutputManagerVersion), id);
auto handler = std::make_unique<AuraOutputManager>(manager_resource);
SetImplementation(manager_resource, nullptr, std::move(handler));
}
} // namespace exo::wayland