blob: e5778071ec203334415c35a320206279a5b1a6f7 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/scenic/lib/display/display_manager2.h"
#include <lib/fidl/cpp/clone.h>
#include <iterator>
#include <string>
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/fxl/logging.h"
#include "src/ui/scenic/lib/display/display_controller.h"
namespace scenic_impl {
namespace display {
static fuchsia::ui::display::DisplayRef NewDisplayRef() {
fuchsia::ui::display::DisplayRef display_ref;
// Safe and valid event, by construction.
zx_status_t status = zx::event::create(/*flags*/ 0u, &display_ref.reference);
FX_DCHECK(status == ZX_OK);
// Reduce rights to only what's necessary.
status = display_ref.reference.replace(ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER | ZX_RIGHT_INSPECT,
&display_ref.reference);
FX_DCHECK(status == ZX_OK);
return display_ref;
}
static fuchsia::ui::display::DisplayRef DuplicateDisplayRef(
const fuchsia::ui::display::DisplayRef& original_ref) {
fuchsia::ui::display::DisplayRef display_ref;
// Safe and valid event, by construction. Don't allow further duplication.
zx_status_t status = original_ref.reference.duplicate(ZX_RIGHT_TRANSFER | ZX_RIGHT_INSPECT,
&display_ref.reference);
FX_DCHECK(status == ZX_OK);
return display_ref;
}
DisplayManager2::DisplayManager2() : weak_factory_(this) {}
void DisplayManager2::AddDisplayController(
std::shared_ptr<fuchsia::hardware::display::ControllerSyncPtr> controller,
std::unique_ptr<DisplayControllerListener> controller_listener) {
auto display_controller_private = DisplayControllerPrivateUniquePtr(
new DisplayControllerPrivate{.controller = std::move(controller),
.listener = std::move(controller_listener),
.displays = std::vector<DisplayInfoPrivate>(),
.has_ownership = false},
[](DisplayControllerPrivate* dc) {
if (dc->listener) {
dc->listener->ClearCallbacks();
}
delete dc;
});
DisplayControllerPrivate* dc = display_controller_private.get();
display_controllers_private_.push_back(std::move(display_controller_private));
auto on_invalid_cb = [this, dc]() { RemoveOnInvalid(dc); };
auto displays_changed_cb = [this, dc](std::vector<fuchsia::hardware::display::Info> added,
std::vector<uint64_t> removed) {
OnDisplaysChanged(dc, std::move(added), std::move(removed));
};
auto display_ownership_changed_cb = [this, dc](bool has_ownership) {
OnDisplayOwnershipChanged(dc, has_ownership);
};
// Note: These callbacks are all cleared in the destructor for |dc|.
dc->listener->InitializeCallbacks(std::move(on_invalid_cb), std::move(displays_changed_cb),
std::move(display_ownership_changed_cb));
}
void DisplayManager2::InvokeDisplayAddedForListener(
const fidl::InterfacePtr<fuchsia::ui::display::DisplayListener>& listener,
const DisplayInfoPrivate& display_info_private) {
auto display_info_clone = fidl::Clone(display_info_private.info);
display_info_clone.set_display_ref(DuplicateDisplayRef(display_info_clone.display_ref()));
listener->OnDisplayAdded(std::move(display_info_clone));
}
bool DisplayManager2::HasDisplayWithId(
const std::vector<DisplayManager2::DisplayInfoPrivate>& displays, uint64_t display_id) {
auto display_it = std::find_if(displays.begin(), displays.end(),
[display_id](const DisplayInfoPrivate& display_info) {
return display_info.id == display_id;
});
return display_it != displays.end();
}
void DisplayManager2::InvokeDisplayOwnershipChangedForListener(
const fidl::InterfacePtr<fuchsia::ui::display::DisplayListener>& listener,
DisplayControllerPrivate* dc, bool has_ownership) {
if (dc->displays.empty()) {
return;
}
std::vector<fuchsia::ui::display::DisplayRef> display_refs;
for (auto& display_info_private : dc->displays) {
display_refs.push_back(DuplicateDisplayRef(display_info_private.info.display_ref()));
}
listener->OnDisplayOwnershipChanged(std::move(display_refs), has_ownership);
}
void DisplayManager2::AddDisplayListener(
fidl::InterfaceHandle<fuchsia::ui::display::DisplayListener>
display_listener_interface_handle) {
fuchsia::ui::display::DisplayListenerPtr display_listener =
display_listener_interface_handle.Bind();
for (auto& display_controller : display_controllers_private_) {
// Deliver OnDisplayAdded events for all the displays that currently exist.
for (auto& display : display_controller->displays) {
InvokeDisplayAddedForListener(display_listener, display);
}
// Notify the client if we have ownership of the display controller.
if (display_controller->has_ownership) {
InvokeDisplayOwnershipChangedForListener(display_listener, display_controller.get(),
display_controller->has_ownership);
}
}
display_listeners_.AddInterfacePtr(std::move(display_listener));
}
DisplayControllerUniquePtr DisplayManager2::ClaimFirstDisplayDeprecated() {
for (auto& display_controller_private : display_controllers_private_) {
if (display_controller_private->displays.size() > 0) {
return ClaimDisplay(display_controller_private->displays[0].display_ref_koid);
}
}
return nullptr;
}
DisplayControllerUniquePtr DisplayManager2::ClaimDisplay(zx_koid_t display_ref_koid) {
DisplayControllerPrivate* dc_private = nullptr;
DisplayInfoPrivate* display_info_private = nullptr;
std::tie(dc_private, display_info_private) = FindDisplay(display_ref_koid);
if (dc_private == nullptr) {
return nullptr;
}
if (dc_private->claimed_dc) {
return nullptr;
}
// Create a list of displays.
std::vector<Display2> displays;
for (auto& display_info_private : dc_private->displays) {
displays.emplace_back(display_info_private.id, display_info_private.info.modes(),
display_info_private.pixel_formats);
}
auto custom_deleter = [weak = GetWeakPtr()](DisplayController* dc) {
if (weak) {
DisplayControllerPrivate* dc_private = weak->FindDisplayControllerPrivate(dc);
if (dc_private) {
dc_private->claimed_dc = nullptr;
dc_private->listener->SetOnVsyncCallback(nullptr);
}
}
delete dc;
};
DisplayControllerUniquePtr display_controller =
DisplayControllerUniquePtr(new DisplayController(std::move(displays), dc_private->controller),
std::move(custom_deleter));
// This raw pointer is cleared in the custom deleter above.
dc_private->claimed_dc = display_controller.get();
// This callback is cleared in the custom deleter above.
dc_private->listener->SetOnVsyncCallback([dc_private](uint64_t display_id, uint64_t timestamp,
std::vector<uint64_t> images,
uint64_t cookie) {
if (!dc_private->claimed_dc) {
FX_LOGS(WARNING)
<< "DisplayManager: Couldn't find display controller matching to vsync callback.";
FX_DCHECK(false);
return;
}
// Since the number of displays will be very low (and often only == 1), performance is
// usually better iterating instead of using a map.
for (auto& display : *(dc_private->claimed_dc->displays())) {
if (display.display_id() == display_id) {
display.OnVsync(zx::time(timestamp), std::move(images));
return;
}
}
FX_LOGS(WARNING) << "DisplayManager: Couldn't find display matching to vsync callback.";
FX_DCHECK(false);
});
return display_controller;
}
std::optional<DisplayManager2::DisplayInfoPrivate> DisplayManager2::RemoveDisplayWithId(
std::vector<DisplayInfoPrivate>* displays, uint64_t display_id) {
auto display_it = std::find_if(
displays->begin(), displays->end(),
[display_id](DisplayInfoPrivate& display_info) { return display_info.id == display_id; });
if (display_it != displays->end()) {
auto removed_display = std::make_optional<DisplayInfoPrivate>(std::move(*display_it));
displays->erase(display_it);
return removed_display;
} else {
return std::optional<DisplayInfoPrivate>();
}
}
DisplayManager2::DisplayControllerPrivate* DisplayManager2::FindDisplayControllerPrivate(
DisplayController* dc) {
for (auto& display_controller_private : display_controllers_private_) {
if (display_controller_private->claimed_dc == dc) {
return display_controller_private.get();
}
}
return nullptr;
}
std::tuple<DisplayManager2::DisplayControllerPrivate*, DisplayManager2::DisplayInfoPrivate*>
DisplayManager2::FindDisplay(zx_koid_t display_ref_koid) {
for (auto& display_controller_private : display_controllers_private_) {
for (auto& display_private : display_controller_private->displays) {
if (display_private.display_ref_koid == display_ref_koid) {
return {display_controller_private.get(), &display_private};
}
}
}
return {nullptr, nullptr};
}
DisplayManager2::DisplayInfoPrivate DisplayManager2::NewDisplayInfoPrivate(
fuchsia::hardware::display::Info hardware_display_info,
std::shared_ptr<fuchsia::hardware::display::ControllerSyncPtr> controller) {
fuchsia::ui::display::Info display_info;
display_info.set_display_ref(NewDisplayRef());
display_info.set_modes(std::move(hardware_display_info.modes));
display_info.set_manufacturer_name(hardware_display_info.manufacturer_name);
display_info.set_monitor_name(hardware_display_info.monitor_name);
return DisplayInfoPrivate{
hardware_display_info.id, fsl::GetKoid(display_info.display_ref().reference.get()),
hardware_display_info.pixel_format, std::move(controller), std::move(display_info)};
}
void DisplayManager2::OnDisplaysChanged(
DisplayControllerPrivate* dc_private,
std::vector<fuchsia::hardware::display::Info> displays_added,
std::vector<uint64_t> displays_removed) {
for (fuchsia::hardware::display::Info& display_info : displays_added) {
if (HasDisplayWithId(dc_private->displays, display_info.id)) {
last_error_ = "DisplayManager: Display added, but a display already exists with same id=" +
std::to_string(display_info.id);
FX_LOGS(WARNING) << last_error_;
continue;
}
if (dc_private->claimed_dc) {
dc_private->claimed_dc->AddDisplay(
Display2(display_info.id, display_info.modes, display_info.pixel_format));
}
dc_private->displays.push_back(NewDisplayInfoPrivate(display_info, dc_private->controller));
const DisplayInfoPrivate& display_info_private = dc_private->displays.back();
for (auto& listener : display_listeners_.ptrs()) {
InvokeDisplayAddedForListener(*listener, display_info_private);
}
}
for (uint64_t display_id : displays_removed) {
std::optional<DisplayInfoPrivate> display_info_private =
RemoveDisplayWithId(&dc_private->displays, display_id);
if (!display_info_private) {
last_error_ = "DisplayManager: Got a display removed event for invalid display=" +
std::to_string(display_id);
FX_LOGS(WARNING) << last_error_;
continue;
}
if (dc_private->claimed_dc) {
bool deleted = dc_private->claimed_dc->RemoveDisplay(display_id);
if (!deleted) {
last_error_ = "DisplayManager: Unable to remove display for display controller, id=" +
std::to_string(display_id);
FX_LOGS(WARNING) << last_error_;
continue;
}
}
fuchsia::ui::display::DisplayRef display_ref =
fidl::Clone(display_info_private->info.display_ref());
for (auto& listener : display_listeners_.ptrs()) {
(*listener)->OnDisplayRemoved(DuplicateDisplayRef(display_ref));
}
}
}
void DisplayManager2::OnDisplayOwnershipChanged(DisplayControllerPrivate* dc, bool has_ownership) {
dc->has_ownership = has_ownership;
if (dc->displays.empty()) {
return;
}
for (auto& listener : display_listeners_.ptrs()) {
InvokeDisplayOwnershipChangedForListener(*listener, dc, has_ownership);
}
}
void DisplayManager2::RemoveOnInvalid(DisplayControllerPrivate* dc) {
auto it = std::find_if(display_controllers_private_.begin(), display_controllers_private_.end(),
[dc](DisplayControllerPrivateUniquePtr& p) { return p.get() == dc; });
FX_DCHECK(it != display_controllers_private_.end());
auto display_controller = std::move(*it);
display_controllers_private_.erase(it);
for (auto& display : display_controller->displays) {
for (auto& listener : display_listeners_.ptrs()) {
(*listener)->OnDisplayRemoved(DuplicateDisplayRef(display.info.display_ref()));
}
}
}
} // namespace display
} // namespace scenic_impl