blob: a31a763b03ba044d902e92e3c9d248e809b5a48b [file] [log] [blame]
// Copyright 2021 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.
use std::sync::{Arc, Weak};
use fidl_fuchsia_ui_app as fuiapp;
use fuchsia_async as fasync;
use fuchsia_component::server::ServiceFs;
use fuchsia_component::server::ServiceObjLocal;
use fuchsia_zircon as zx;
use fuchsia_zircon::AsHandleRef;
use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::StreamExt;
use futures::TryStreamExt;
use wayland_bridge::dispatcher::WaylandDispatcher;
use super::bridge_client::*;
use super::file_creation::*;
use crate::device::wayland::image_file::ImageFile;
use crate::device::wayland::BufferCollectionFile;
use crate::fs::buffers::*;
use crate::fs::socket::*;
use crate::fs::*;
use crate::task::{CurrentTask, Kernel};
use crate::types::*;
/// The services that are exposed in the component's outgoing directory by the wayland server.
enum ExposedServices {
ViewProvider(fuiapp::ViewProviderRequestStream),
}
/// Initializes the wayland server.
///
/// This does a few different things:
/// - Serves a `ViewProvider` protocol on behalf of the component that is running. This view
/// provider connection is proxied to a view provider that is served by the wayland bridge
/// library.
/// - Creates a socket representing the wayland display. Data written to this socket is proxied
/// to the wayland bridge, which then executes the appropriate scenic commands.
/// - Creates a `DMABuf` file that the wayland client can use to allocate memory and share it with
/// the wayland bridge (and scenic).
///
/// # Parameters
/// - `task`: The task that is being run, which is used to create the wayland files.
/// - `display_path`: The path at which the wayland display socket is created.
/// - `device_path`: The path at which the `DMABuf` file is created.
pub fn serve_wayland(
current_task: &CurrentTask,
display_path: FsString,
device_path: FsString,
) -> Result<(), Errno> {
let display_socket = create_display_socket(current_task, display_path)?;
create_device_file(current_task, device_path)?;
let kernel = current_task.thread_group.kernel.clone();
let outgoing_dir_channel = kernel.outgoing_dir.lock().take().ok_or(errno!(EINVAL))?;
// Add `ViewProvider` to the exposed services of the component, and then serve the
// outgoing directory.
let mut outgoing_dir = ServiceFs::new_local();
outgoing_dir.dir("svc").add_fidl_service(ExposedServices::ViewProvider);
outgoing_dir.serve_connection(outgoing_dir_channel).map_err(|_| errno!(EINVAL))?;
let (wayland_sender, wayland_server_receiver) = unbounded();
let (wayland_server_sender, wayland_receiver) = unbounded();
let kernel = Arc::downgrade(&kernel);
// Spawn a thread to accept a connection to the wayland display socket.
let _accept_socket_thread = std::thread::spawn(move || -> Result<(), Errno> {
let socket = loop {
if let Ok(socket) = display_socket.accept(ucred::default()) {
break socket;
}
};
// Spawn a thread that reads data from the socket and sends it to the wayland bridge
// via wayland_sender.
let client_socket = socket.clone();
let _read_socket_thread = std::thread::spawn(move || loop {
handle_client_data(&client_socket, &wayland_sender);
});
// Reuse this thread to read data from the wayland_receiver (i.e., wayland protocol messages
// from the wayland bridge library, meant for the wayland client).
let mut executor = fasync::LocalExecutor::new().map_err(|_| errno!(EINVAL))?;
executor.run_singlethreaded(handle_server_data(kernel, socket, wayland_receiver))?;
Ok(())
});
let (view_provider_sender, view_provider_receiver) = unbounded();
let wayland_server =
Arc::new(parking_lot::Mutex::new(WaylandClient::new(view_provider_sender)));
let dispatcher = WaylandDispatcher::new_local(wayland_server).map_err(|_| errno!(EINVAL))?;
let display = &dispatcher.display;
display.clone().spawn_new_local_client(
wayland_server_sender,
wayland_server_receiver,
cfg!(feature = "wayland_protocol_logging"),
);
fasync::Task::local(serve_view_provider(outgoing_dir, view_provider_receiver, dispatcher))
.detach();
Ok(())
}
/// Serves a `ViewProvider` protocol from `outgoing_dir`. The outgoing directory belongs to the
/// component this starnix instance is running.
/// # Parameters
/// - `outgoing_dir`: The outgoing directory belonging to the component this starnix instance is
/// running.
/// - `view_provider_receiver`: The channel endpoint that is used to wait for the wayland bridge
/// library to create a `ViewProviderProxy`.
/// - `dispatcher`: The wayland bridge dispatcher that is handling the wayland protocol messages.
/// The dispatcher is kept alive until the `outgoing_dir` is closed.
async fn serve_view_provider(
mut outgoing_dir: ServiceFs<ServiceObjLocal<'static, ExposedServices>>,
mut view_provider_receiver: UnboundedReceiver<fuiapp::ViewProviderProxy>,
_dispatcher: WaylandDispatcher,
) {
// The view provider that is received from the wayland bridge. Declared in the outer scope to
// keep the proxy alive even after handling a create view request.
let mut view_provider: Option<fuiapp::ViewProviderProxy>;
while let Some(ExposedServices::ViewProvider(mut request_stream)) = outgoing_dir.next().await {
// Wait for the wayland bridge to create a view provider on behalf of the component before
// serving the view provider request stream.
view_provider = view_provider_receiver.next().await;
while let Ok(Some(event)) = request_stream.try_next().await {
match event {
fuiapp::ViewProviderRequest::CreateViewWithViewRef {
token,
mut view_ref_control,
mut view_ref,
..
} => {
view_provider.as_ref().map(|view_provider| {
match view_provider.create_view_with_view_ref(
token,
&mut view_ref_control,
&mut view_ref,
) {
Ok(_) => {}
Err(e) => {
tracing::error!("Got an error when creating view: {:?}", e);
return;
}
}
});
}
fuiapp::ViewProviderRequest::CreateView2 { args, control_handle: _ } => {
view_provider.as_ref().map(|view_provider| {
match view_provider.create_view2(args) {
Ok(_) => {}
Err(e) => {
tracing::error!("Got an error when creating view: {:?}", e);
return;
}
}
});
}
r => {
tracing::warn!("Got unexpected view provider request: {:?}", r);
}
}
}
}
}
/// Reads wayland protocol data from `display_socket` and proxies it to the wayland bridge library.
///
/// # Parameters
/// - `display_socket`: The socket that the wayland client is connected to.
/// - `wayland_sender`: The sender used to send data to the wayland bridge.
fn handle_client_data(
display_socket: &SocketHandle,
wayland_sender: &UnboundedSender<zx::MessageBuf>,
) {
let messages =
display_socket.blocking_read_kernel().expect("Failed to wait for display socket data.");
for message in messages {
let mut handles = vec![];
for ancillary_data in message.ancillary_data {
match ancillary_data {
AncillaryData::Unix(UnixControlData::Rights(files)) => {
for file in files {
if let Some(buffer_collection_file) =
file.downcast_file::<BufferCollectionFile>()
{
let import_token = buffer_collection_file
.token
.value
.as_handle_ref()
.duplicate(zx::Rights::SAME_RIGHTS)
.expect("Failed to duplicate buffer collection import token.");
handles.push(import_token);
} else if let Some(image_file) = file.downcast_file::<ImageFile>() {
let import_token = image_file
.info
.token
.value
.as_handle_ref()
.duplicate(zx::Rights::SAME_RIGHTS)
.expect("Failed to duplicate buffer collection import token.");
handles.push(import_token);
} else {
tracing::error!(
"Trying to parse buffre collection token from invalid file type."
);
}
}
}
_ => {
tracing::error!("Got unexpected ancillary data.");
return;
}
}
}
let message_buf = zx::MessageBuf::new_with(message.data.bytes().to_vec(), handles);
match wayland_sender.unbounded_send(message_buf) {
Ok(_) => {}
Err(_) => {
tracing::error!("Failed to send data to wayland bridge.");
return;
}
}
}
}
/// Reads data from the wayland bridge and sends it to the wayland client via the display socket.
///
/// # Parameters
/// - `kernel`: The kernel used to allocate files.
/// - `display_socket`: The display socket that the wayland client is connected to.
/// - `wayland_receiver`: The receiver to which wayland bridge sends data.
async fn handle_server_data(
kernel: Weak<Kernel>,
display_socket: SocketHandle,
mut wayland_receiver: UnboundedReceiver<zx::MessageBuf>,
) -> Result<(), Errno> {
while let Some(mut buffer) = wayland_receiver.next().await {
if let Some(kernel) = kernel.upgrade() {
let mut files: Vec<FileHandle> = vec![];
// Create vmo files for each of the provided handles.
while let Some(handle) = buffer.take_handle(0) {
let vmo = zx::Vmo::from(handle);
let file = Anon::new_file(
anon_fs(&kernel),
Box::new(VmoFileObject::new(Arc::new(vmo))),
OpenFlags::RDWR,
);
files.push(file);
}
let ancillary_data = if !files.is_empty() {
vec![AncillaryData::Unix(UnixControlData::Rights(files))]
} else {
vec![]
};
let message = Message::new(buffer.bytes().to_vec().into(), None, ancillary_data);
display_socket.write_kernel(message)?;
} else {
return Ok(());
}
}
Ok(())
}