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