blob: 2492d0dc8fb448387a7b3631d3b7ac0ecb1fda28 [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_input::{
ImeServiceMarker, InputEvent, InputMethodAction, InputMethodEditorClientMarker,
InputMethodEditorClientRequest, InputMethodEditorMarker, KeyboardEventPhase, KeyboardType,
TextAffinity, TextInputState, TextRange, TextSelection,
};
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_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 futures::{FutureExt, TryFutureExt, TryStreamExt};
use std::env;
use std::sync::{Arc, Mutex};
use term_model::ansi::Handler;
use term_model::config::Config;
use term_model::term::{SizeInfo, Term};
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 ime_service = connect_to_service::<ImeServiceMarker>()?;
let (ime_listener, ime_listener_request) = create_endpoints::<InputMethodEditorMarker>()?;
let (ime_client_listener, ime_client_listener_request) =
create_endpoints::<InputMethodEditorClientMarker>()?;
let mut text_input_state = TextInputState {
revision: 0,
text: "".to_string(),
selection: TextSelection { base: 0, extent: 0, affinity: TextAffinity::Upstream },
composing: TextRange { start: 0, end: 0 },
};
ime_service.get_input_method_editor(
KeyboardType::Text,
InputMethodAction::None,
&mut text_input_state,
ime_client_listener,
ime_listener_request,
)?;
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 {
// In order to keep the channel alive, we need to move ime_listener into this block.
// Otherwise it's unused, which closes the channel immediately.
let _dummy = ime_listener;
let mut stream = ime_client_listener_request.into_stream()?;
while let Some(request) = await!(stream.try_next())? {
match request {
InputMethodEditorClientRequest::DidUpdateState {
event: Some(event),
..
} => view_controller.lock().unwrap().handle_input_event(*event),
_ => (),
}
}
Ok(())
}
.unwrap_or_else(|e: failure::Error| eprintln!("input listener error: {:?}", e)),
);
}
{
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(|| {
let mut term = 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);
}
term
});
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();
}
}
fn handle_input_event(&mut self, event: InputEvent) {
if let (Some(term), InputEvent::Keyboard(event)) = (&mut self.term, &event) {
if event.phase == KeyboardEventPhase::Pressed
|| event.phase == KeyboardEventPhase::Repeat
{
if let Some(c) = std::char::from_u32(event.code_point) {
if c != '\0' {
term.input(c);
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(())
}
}
fn main() -> Result<(), Error> {
env::set_var("RUST_BACKTRACE", "full");
let mut executor = fasync::Executor::new().context("Error creating executor")?;
let app_for_v1 = App::new()?;
let app_for_v2 = app_for_v1.clone();
let fut = fuchsia_app::server::ServicesServer::new()
.add_service((ViewProviderMarker::NAME, move |chan| {
App::spawn_v1_view_provider_server(&app_for_v1, chan)
}))
.add_service((viewsv2::ViewProviderMarker::NAME, move |chan| {
App::spawn_v2_view_provider_server(&app_for_v2, chan)
}))
.start()
.context("Error starting view provider server")?;
executor.run_singlethreaded(fut)?;
Ok(())
}