blob: 10d343071eafce37ce15c662a111128961323d36 [file] [log] [blame]
// Copyright 2018 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.
#![feature(async_await, await_macro)]
#![deny(warnings)]
use failure::{Error, ResultExt};
use fidl::endpoints::{create_endpoints, create_proxy, RequestStream, ServerEnd, ServiceMarker};
use fidl_fuchsia_images as images;
use fidl_fuchsia_math::SizeF;
use fidl_fuchsia_ui_app as viewsv2;
use fidl_fuchsia_ui_gfx as gfx;
use fidl_fuchsia_ui_scenic::{self as scenic, ScenicProxy, SessionListenerRequest};
use fidl_fuchsia_ui_viewsv1::ViewProviderRequest::CreateView;
use fidl_fuchsia_ui_viewsv1::{
ViewListenerMarker, ViewListenerRequest, ViewManagerMarker, ViewManagerProxy, ViewProperties,
ViewProviderMarker, ViewProviderRequestStream, ViewProxy,
};
use fuchsia_app::client::connect_to_service;
use fuchsia_app::server::ServiceFactory;
use fuchsia_async as fasync;
use fuchsia_scenic::{HostImageCycler, ImportNode, Session, SessionPtr};
use fuchsia_ui::{Canvas, Color, FontDescription, FontFace, Paint, Point, SharedBufferPixelSink, Size};
use fuchsia_zircon::{EventPair, Handle};
use term_model::term::{SizeInfo, Term};
use term_model::config::{Config};
use term_model::ansi::{Handler};
use futures::{FutureExt, TryFutureExt, TryStreamExt};
use std::env;
use std::sync::{Arc, Mutex};
static FONT_DATA: &'static [u8] =
include_bytes!("../../fonts/third_party/robotomono/RobotoMono-Regular.ttf");
type FontFacePtr = Arc<Mutex<FontFace<'static>>>;
struct ViewController {
face: FontFacePtr,
_view: ViewProxy,
session: SessionPtr,
import_node: ImportNode,
image_cycler: HostImageCycler,
metrics: Option<gfx::Metrics>,
logical_size: Option<SizeF>,
term: Option<Term>,
}
type ViewControllerPtr = Arc<Mutex<ViewController>>;
impl ViewController {
pub fn new(
face: FontFacePtr, view_listener_request: ServerEnd<ViewListenerMarker>, view: ViewProxy,
mine: EventPair, scenic: ScenicProxy,
) -> Result<ViewControllerPtr, Error> {
let (session_listener, session_listener_request) = create_endpoints()?;
let (session_proxy, session_request) = create_proxy()?;
scenic.create_session(session_request, Some(session_listener))?;
let session = Session::new(session_proxy);
let view_controller = ViewController {
face,
_view: view,
session: session.clone(),
import_node: ImportNode::new(session.clone(), mine),
image_cycler: HostImageCycler::new(session.clone()),
metrics: None,
logical_size: None,
term: None,
};
view_controller.setup_scene();
view_controller.present();
let view_controller = Arc::new(Mutex::new(view_controller));
{
let view_controller = view_controller.clone();
fasync::spawn(
async move {
let mut stream = session_listener_request.into_stream()?;
while let Some(request) = await!(stream.try_next())? {
match request {
SessionListenerRequest::OnScenicEvent {
events,
control_handle: _,
} => view_controller
.lock()
.unwrap()
.handle_session_events(events),
_ => (),
}
}
Ok(())
}
.unwrap_or_else(|e: failure::Error| eprintln!("view listener error: {:?}", e)),
);
}
{
let view_controller = view_controller.clone();
fasync::spawn(
async move {
let mut stream = view_listener_request.into_stream()?;
while let Some(req) = await!(stream.try_next())? {
let ViewListenerRequest::OnPropertiesChanged {
properties,
responder,
} = req;
view_controller
.lock()
.unwrap()
.handle_properties_changed(properties);
responder
.send()
.unwrap_or_else(|e| eprintln!("view listener error: {:?}", e))
}
Ok(())
}
.unwrap_or_else(|e: failure::Error| eprintln!("view listener error: {:?}", e)),
);
}
Ok(view_controller)
}
fn setup_scene(&self) {
self.import_node
.resource()
.set_event_mask(gfx::METRICS_EVENT_MASK);
self.import_node.add_child(self.image_cycler.node());
}
fn invalidate(&mut self) {
self.begin_frame();
}
fn begin_frame(&mut self) {
let (metrics, logical_size) = match (self.metrics.as_ref(), self.logical_size.as_ref()) {
(Some(metrics), Some(logical_size)) => (metrics, logical_size),
_ => return,
};
let physical_width = (logical_size.width * metrics.scale_x) as u32;
let physical_height = (logical_size.height * metrics.scale_y) as u32;
let stride = physical_width * 4;
let info = images::ImageInfo {
transform: images::Transform::Normal,
width: physical_width,
height: physical_height,
stride,
pixel_format: images::PixelFormat::Bgra8,
color_space: images::ColorSpace::Srgb,
tiling: images::Tiling::Linear,
alpha_format: images::AlphaFormat::Opaque,
};
{
let guard = self
.image_cycler
.acquire(info)
.expect("failed to allocate buffer");
let mut face = self.face.lock().unwrap();
let mut canvas = Canvas::<SharedBufferPixelSink>::new(guard.image().buffer(), stride);
let size = Size {
width: 14,
height: 22,
};
let mut font = FontDescription {
face: &mut face,
size: 20,
baseline: 18,
};
let term = self.term.get_or_insert_with(||
Term::new(&Config::default(), SizeInfo {
width: physical_width as f32,
height: physical_height as f32,
cell_width: size.width as f32,
cell_height: size.height as f32,
padding_x: 0.,
padding_y: 0.,
})
);
for c in "$ echo \"hello, world!\"".chars() {
term.input(c);
}
for cell in term.renderable_cells(&Config::default(), None, true) {
let mut buffer: [u8; 4] = [0, 0, 0, 0];
canvas.fill_text_cells(
cell.c.encode_utf8(&mut buffer),
Point { x: size.width * cell.column.0 as u32, y: size.height * cell.line.0 as u32 },
size, &mut font,
&Paint {
fg: Color { r: cell.fg.r, g: cell.fg.g, b: cell.fg.b, a: 0xFF },
bg: Color { r: cell.bg.r, g: cell.bg.g, b: cell.bg.b, a: 0xFF },
}
)
}
}
let node = self.image_cycler.node();
node.set_scale(1.0 / metrics.scale_x, 1.0 / metrics.scale_y, 1.0);
node.set_translation(logical_size.width / 2.0, logical_size.height / 2.0, 0.0);
self.present();
}
fn present(&self) {
fasync::spawn(self.session.lock().present(0).map(|_| ()));
}
fn handle_session_events(&mut self, events: Vec<scenic::Event>) {
events.iter().for_each(|event| match event {
scenic::Event::Gfx(gfx::Event::Metrics(event)) => {
self.metrics = Some(gfx::Metrics { ..event.metrics });
self.invalidate();
}
_ => (),
});
}
fn handle_properties_changed(&mut self, properties: ViewProperties) {
if let Some(view_properties) = properties.view_layout {
self.logical_size = Some(view_properties.size);
self.invalidate();
}
}
}
struct App {
face: FontFacePtr,
view_manager: ViewManagerProxy,
view_controllers: Vec<ViewControllerPtr>,
}
type AppPtr = Arc<Mutex<App>>;
impl App {
pub fn new() -> Result<AppPtr, Error> {
let view_manager = connect_to_service::<ViewManagerMarker>()?;
Ok(Arc::new(Mutex::new(App {
face: Arc::new(Mutex::new(FontFace::new(FONT_DATA).unwrap())),
view_manager,
view_controllers: vec![],
})))
}
pub fn spawn_v1_view_provider_server(app: &AppPtr, chan: fasync::Channel) {
let app = app.clone();
fasync::spawn(
ViewProviderRequestStream::from_channel(chan)
.try_for_each(move |req| {
let CreateView { view_owner, .. } = req;
let token: EventPair = EventPair::from(Handle::from(view_owner.into_channel()));
App::app_create_view(app.clone(), token).unwrap();
futures::future::ready(Ok(()))
})
.unwrap_or_else(|e| eprintln!("error running V1 view_provider server: {:?}", e)),
)
}
pub fn spawn_v2_view_provider_server(app: &AppPtr, chan: fasync::Channel) {
let app = app.clone();
fasync::spawn(
viewsv2::ViewProviderRequestStream::from_channel(chan)
.try_for_each(move |req| {
let viewsv2::ViewProviderRequest::CreateView { token, .. } = req;
App::app_create_view(app.clone(), token).unwrap();
futures::future::ready(Ok(()))
})
.unwrap_or_else(|e| eprintln!("error running V2 view_provider server: {:?}", e)),
)
}
pub fn app_create_view(app: AppPtr, view_token: EventPair) -> Result<(), Error> {
app.lock().unwrap().create_view(view_token)
}
pub fn create_view(&mut self, view_token: EventPair) -> Result<(), Error> {
let (view, view_request) = create_proxy()?;
let (view_listener, view_listener_request) = create_endpoints()?;
let (mine, theirs) = EventPair::create()?;
self.view_manager.create_view2(
view_request,
view_token,
view_listener,
theirs,
Some("Terminal"),
)?;
let (scenic, scenic_request) = create_proxy()?;
self.view_manager.get_scenic(scenic_request)?;
self.view_controllers.push(ViewController::new(
self.face.clone(),
view_listener_request,
view,
mine,
scenic,
)?);
Ok(())
}
}
struct V1ViewProvider {
app: AppPtr,
}
impl ServiceFactory for V1ViewProvider {
fn service_name(&self) -> &str {
ViewProviderMarker::NAME
}
fn spawn_service(&mut self, channel: fasync::Channel) {
App::spawn_v1_view_provider_server(&self.app, channel);
}
}
struct V2ViewProvider {
app: AppPtr,
}
impl ServiceFactory for V2ViewProvider {
fn service_name(&self) -> &str {
viewsv2::ViewProviderMarker::NAME
}
fn spawn_service(&mut self, channel: fasync::Channel) {
App::spawn_v2_view_provider_server(&self.app, channel);
}
}
fn main() -> Result<(), Error> {
env::set_var("RUST_BACKTRACE", "1");
let mut executor = fasync::Executor::new().context("Error creating executor")?;
let app = App::new()?;
let fut = fuchsia_app::server::ServicesServer::new()
.add_service(V1ViewProvider { app: app.clone() })
.add_service(V2ViewProvider { app: app.clone() })
.start()
.context("Error starting view provider server")?;
executor.run_singlethreaded(fut)?;
Ok(())
}