blob: 8654657491867894aaec02e504d118b4fd3b09b4 [file] [log] [blame]
// 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 <aura-shell-client-protocol.h>
#include <chrome-color-management-client-protocol.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/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_zaura_output.h"
#include "ui/ozone/platform/wayland/host/wayland_zcr_color_management_output.h"
#include "ui/ozone/platform/wayland/host/wayland_zcr_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
// 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->wayland_output_manager_) {
connection->wayland_output_manager_ =
std::make_unique<WaylandOutputManager>(connection);
}
connection->wayland_output_manager_->AddWaylandOutput(name, output.release());
}
WaylandOutput::Metrics::Metrics() = default;
WaylandOutput::Metrics::Metrics(Id output_id,
int64_t display_id,
gfx::Point origin,
gfx::Size logical_size,
gfx::Size physical_size,
gfx::Insets 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),
scale_factor(scale_factor),
panel_transform(panel_transform),
logical_transform(logical_transform),
description(description) {}
WaylandOutput::Metrics::Metrics(const Metrics& copy) = default;
WaylandOutput::Metrics::~Metrics() = default;
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::InitializeZAuraOutput(zaura_shell* aura_shell) {
DCHECK(!aura_output_);
aura_output_ = std::make_unique<WaylandZAuraOutput>(
zaura_shell_get_aura_output(aura_shell, output_.get()));
}
void WaylandOutput::InitializeColorManagementOutput(
WaylandZcrColorManager* zcr_color_manager) {
DCHECK(!color_management_output_);
color_management_output_ = std::make_unique<WaylandZcrColorManagementOutput>(
this,
zcr_color_manager->CreateColorManagementOutput(output_.get()).release());
}
void WaylandOutput::Initialize(Delegate* delegate) {
DCHECK(!delegate_);
delegate_ = delegate;
static constexpr wl_output_listener output_listener = {
&OutputHandleGeometry, &OutputHandleMode, &OutputHandleDone,
&OutputHandleScale, &OutputHandleName, &OutputHandleDescription,
};
wl_output_add_listener(output_.get(), &output_listener, this);
}
float WaylandOutput::GetUIScaleFactor() const {
return display::Display::HasForceDeviceScaleFactor()
? display::Display::GetForcedDeviceScaleFactor()
: scale_factor();
}
WaylandOutput::Metrics WaylandOutput::GetMetrics() const {
// TODO(aluh): Change to designated initializers once C++20 is supported.
return {output_id(), display_id(), origin(), logical_size(),
physical_size(), insets(), scale_factor(), panel_transform(),
logical_transform(), description()};
}
int32_t WaylandOutput::logical_transform() const {
if (aura_output_ && aura_output_->logical_transform()) {
return *aura_output_->logical_transform();
}
return panel_transform();
}
gfx::Point WaylandOutput::origin() const {
if (xdg_output_ && xdg_output_->logical_position()) {
return *xdg_output_->logical_position();
}
return origin_;
}
gfx::Size WaylandOutput::logical_size() const {
return xdg_output_ ? xdg_output_->logical_size() : gfx::Size();
}
gfx::Insets WaylandOutput::insets() const {
return aura_output_ ? aura_output_->insets() : gfx::Insets();
}
const std::string& WaylandOutput::description() const {
return xdg_output_ ? xdg_output_->description() : description_;
}
int64_t WaylandOutput::display_id() const {
return aura_output_ && aura_output_->display_id().has_value()
? aura_output_->display_id().value()
: output_id_;
}
const std::string& WaylandOutput::name() const {
return xdg_output_ ? xdg_output_->name() : name_;
}
bool WaylandOutput::IsReady() const {
// The aura output requires both the logical size and the display ID
// to become ready. If a client that uses xdg_output but not aura_output
// needs different condition for readiness, this needs to be updated.
return !physical_size_.IsEmpty() &&
(!aura_output_ ||
(xdg_output_ && xdg_output_->IsReady() && aura_output_->IsReady()));
}
zaura_output* WaylandOutput::get_zaura_output() {
return aura_output_ ? aura_output_->wl_object() : nullptr;
}
void WaylandOutput::SetScaleFactorForTesting(float scale_factor) {
scale_factor_ = scale_factor;
}
void WaylandOutput::TriggerDelegateNotifications() {
if (xdg_output_ && connection_->surface_submission_in_pixel_coordinates()) {
DCHECK(!physical_size_.IsEmpty());
const gfx::Size logical_size = xdg_output_->logical_size();
if (!logical_size.IsEmpty()) {
// We calculate the fractional scale factor from the long sides of the
// physical and logical sizes, since their orientations may be different.
const float max_physical_side =
std::max(physical_size_.width(), physical_size_.height());
const float max_logical_side =
std::max(logical_size.width(), logical_size.height());
scale_factor_ = max_physical_side / max_logical_side;
}
}
// Wait until the all outputs receives enough information to generate display
// information.
if (!IsReady())
return;
delegate_->OnOutputHandleMetrics(GetMetrics());
}
// static
void WaylandOutput::OutputHandleGeometry(void* data,
wl_output* obj,
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* output = 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));
output->origin_ = gfx::Point(x, y);
output->panel_transform_ = output_transform;
}
}
// static
void WaylandOutput::OutputHandleMode(void* data,
wl_output* wl_output,
uint32_t flags,
int32_t width,
int32_t height,
int32_t refresh) {
auto* output = static_cast<WaylandOutput*>(data);
if (output && (flags & WL_OUTPUT_MODE_CURRENT))
output->physical_size_ = gfx::Size(width, height);
}
// static
void WaylandOutput::OutputHandleDone(void* data, struct wl_output* wl_output) {
if (auto* output = static_cast<WaylandOutput*>(data))
output->TriggerDelegateNotifications();
}
// static
void WaylandOutput::OutputHandleScale(void* data,
struct wl_output* wl_output,
int32_t factor) {
if (auto* output = static_cast<WaylandOutput*>(data))
output->scale_factor_ = factor;
}
// static
void WaylandOutput::OutputHandleName(void* data,
struct wl_output* wl_output,
const char* name) {
if (auto* output = static_cast<WaylandOutput*>(data))
output->name_ = name ? std::string(name) : std::string{};
}
// static
void WaylandOutput::OutputHandleDescription(void* data,
struct wl_output* wl_output,
const char* description) {
if (auto* output = static_cast<WaylandOutput*>(data)) {
output->description_ =
description ? std::string(description) : std::string{};
}
}
} // namespace ui