blob: fbe22f58b1cc87f9bbccdf04435cf8966821528d [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_controller_listener.h"
#include <lib/async/default.h>
#include <zircon/types.h>
#include "src/lib/fxl/logging.h"
namespace scenic_impl {
namespace display {
DisplayControllerListener::DisplayControllerListener(
zx::channel device_channel,
std::shared_ptr<fuchsia::hardware::display::ControllerSyncPtr> controller,
zx_handle_t controller_channel)
: controller_(std::move(controller)),
controller_channel_handle_(controller_channel),
device_channel_(std::move(device_channel)) {
valid_ = device_channel_.is_valid() && controller_channel_handle_ != 0 && controller_->is_bound();
if (valid_) {
// Listen for when the device channel closes.
wait_device_closed_.set_object(device_channel_.get());
wait_device_closed_.set_trigger(ZX_CHANNEL_PEER_CLOSED);
wait_device_closed_.Begin(async_get_default_dispatcher());
// Listen for when the controller channel closes.
wait_controller_closed_.set_object(controller_channel_handle_);
wait_controller_closed_.set_trigger(ZX_CHANNEL_PEER_CLOSED);
wait_controller_closed_.Begin(async_get_default_dispatcher());
// Listen for events
// TODO(FIDL-183): Resolve this hack when synchronous interfaces support events.
wait_event_msg_.set_object(controller_channel_handle_);
wait_event_msg_.set_trigger(ZX_CHANNEL_READABLE);
wait_event_msg_.Begin(async_get_default_dispatcher());
}
}
DisplayControllerListener::~DisplayControllerListener() {
ClearCallbacks();
if (wait_event_msg_.object() != ZX_HANDLE_INVALID) {
wait_event_msg_.Cancel();
}
if (wait_device_closed_.object() != ZX_HANDLE_INVALID) {
wait_device_closed_.Cancel();
}
if (wait_controller_closed_.object() != ZX_HANDLE_INVALID) {
wait_controller_closed_.Cancel();
}
}
void DisplayControllerListener::InitializeCallbacks(
fit::closure on_invalid, OnDisplaysChangedCallback on_displays_changed_cb,
OnClientOwnershipChangeCallback on_client_ownership_change_cb) {
FX_CHECK(!initialized_callbacks_);
initialized_callbacks_ = true;
on_invalid_cb_ = std::move(on_invalid);
// TODO(FIDL-183): Resolve this hack when synchronous interfaces support events.
auto event_dispatcher =
static_cast<fuchsia::hardware::display::Controller::Proxy_*>(event_dispatcher_.get());
event_dispatcher->OnDisplaysChanged = std::move(on_displays_changed_cb);
event_dispatcher->OnClientOwnershipChange = std::move(on_client_ownership_change_cb);
}
void DisplayControllerListener::ClearCallbacks() {
auto event_dispatcher =
static_cast<fuchsia::hardware::display::Controller::Proxy_*>(event_dispatcher_.get());
event_dispatcher->OnDisplaysChanged = nullptr;
event_dispatcher->OnClientOwnershipChange = nullptr;
event_dispatcher->OnVsync = nullptr;
on_invalid_cb_ = nullptr;
}
void DisplayControllerListener::SetOnVsyncCallback(OnVsyncCallback on_vsync_cb) {
// TODO(FIDL-183): Resolve this hack when synchronous interfaces support events.
auto event_dispatcher =
static_cast<fuchsia::hardware::display::Controller::Proxy_*>(event_dispatcher_.get());
event_dispatcher->OnVsync = std::move(on_vsync_cb);
}
void DisplayControllerListener::OnPeerClosedAsync(async_dispatcher_t* dispatcher,
async::WaitBase* self, zx_status_t status,
const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
FX_LOGS(WARNING) << "scenic_impl::gfx::DisplayControllerImpl: Error while waiting on "
"ZX_CHANNEL_PEER_CLOSED: "
<< status;
return;
}
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
valid_ = false;
// We don't want to get another callback, and we don't know which channel closed, so just cancel
// both waits.
wait_device_closed_.Cancel();
wait_controller_closed_.Cancel();
if (on_invalid_cb_) {
// We want |on_invalid_cb_| to be cleared when we're done, so move it out.
auto callback = std::move(on_invalid_cb_);
callback();
// See warning below.
}
// Warning!
// Don't do anything else after callback() is invoked, since |this| could be destroyed.
return;
}
FX_NOTREACHED();
}
void DisplayControllerListener::OnEventMsgAsync(async_dispatcher_t* dispatcher,
async::WaitBase* self, zx_status_t status,
const zx_packet_signal_t* signal) {
// TODO(FIDL-183): Resolve this hack when synchronous interfaces support events.
if (status != ZX_OK) {
FX_LOGS(WARNING)
<< "scenic_impl::gfx::DisplayControllerImpl: Error while waiting on ZX_CHANNEL_READABLE: "
<< status;
return;
}
if (signal->observed & ZX_CHANNEL_READABLE) {
uint8_t byte_buffer[ZX_CHANNEL_MAX_MSG_BYTES];
fidl::Message msg(fidl::BytePart(byte_buffer, ZX_CHANNEL_MAX_MSG_BYTES), fidl::HandlePart());
if (msg.Read(controller_channel_handle_, 0) != ZX_OK) {
FX_LOGS(WARNING) << "Display controller callback read failed";
return;
}
// Re-arm the wait.
wait_event_msg_.Begin(async_get_default_dispatcher());
static_cast<fuchsia::hardware::display::Controller::Proxy_*>(event_dispatcher_.get())
->Dispatch_(std::move(msg));
return;
}
FX_NOTREACHED();
}
} // namespace display
} // namespace scenic_impl