blob: c60bf30df80dc5aba27af02f01deb114e88acbdb [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.
use anyhow::{Context as _, Error};
use carnelian::{
color::Color,
drawing::{path_for_rectangle, path_for_rounded_rectangle},
facet::{Facet, FacetId, LayerGroup, Scene, SceneBuilder},
input::{self},
make_app_assistant,
render::{BlendMode, Context as RenderContext, Fill, FillRule, Layer, Path, Style},
App, AppAssistant, Coord, Rect, Size, ViewAssistant, ViewAssistantContext, ViewAssistantPtr,
ViewKey,
};
use euclid::{point2, size2, vec2, Angle, Transform2D};
use fidl::endpoints::{RequestStream, ServiceMarker};
use fidl_test_placeholders::{EchoMarker, EchoRequest, EchoRequestStream};
use fuchsia_async as fasync;
use fuchsia_zircon::{Event, Time};
use futures::prelude::*;
use std::{any::Any, f32::consts::PI};
#[derive(Default)]
struct SpinningSquareAppAssistant;
impl AppAssistant for SpinningSquareAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
SpinningSquareViewAssistant::new()
}
/// Return the list of names of services this app wants to provide
fn outgoing_services_names(&self) -> Vec<&'static str> {
[EchoMarker::NAME].to_vec()
}
/// Handle a request to connect to a service provided by this app
fn handle_service_connection_request(
&mut self,
_service_name: &str,
channel: fasync::Channel,
) -> Result<(), Error> {
Self::create_echo_server(channel, false);
Ok(())
}
}
impl SpinningSquareAppAssistant {
fn create_echo_server(channel: fasync::Channel, quiet: bool) {
fasync::Task::local(
async move {
let mut stream = EchoRequestStream::from_channel(channel);
while let Some(EchoRequest::EchoString { value, responder }) =
stream.try_next().await.context("error running echo server")?
{
if !quiet {
println!("Spinning Square received echo request for string {:?}", value);
}
responder
.send(value.as_ref().map(|s| &**s))
.context("error sending response")?;
if !quiet {
println!("echo response sent successfully");
}
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| eprintln!("{:?}", e)),
)
.detach();
}
}
struct SceneDetails {
scene: Scene,
square: FacetId,
}
#[derive(Debug)]
pub struct ToggleRoundedMessage {}
struct SpinningSquareFacet {
square_color: Color,
rounded: bool,
start: Time,
square_path: Option<Path>,
}
impl SpinningSquareFacet {
fn new(square_color: Color, start: Time) -> Self {
Self { square_color, rounded: false, start, square_path: None }
}
fn clone_square_path(&self) -> Path {
self.square_path.as_ref().expect("square_path").clone()
}
}
impl Facet for SpinningSquareFacet {
fn update_layers(
&mut self,
size: Size,
layer_group: &mut LayerGroup,
render_context: &mut RenderContext,
) -> Result<(), Error> {
const SPEED: f32 = 0.25;
const SECONDS_PER_NANOSECOND: f32 = 1e-9;
const SQUARE_PATH_SIZE: Coord = 1.0;
const SQUARE_PATH_SIZE_2: Coord = SQUARE_PATH_SIZE / 2.0;
const CORNER_RADIUS: Coord = SQUARE_PATH_SIZE / 4.0;
let center_x = size.width * 0.5;
let center_y = size.height * 0.5;
let square_size = size.width.min(size.height) * 0.6;
let presentation_time = Time::get_monotonic();
let t = ((presentation_time.into_nanos() - self.start.into_nanos()) as f32
* SECONDS_PER_NANOSECOND
* SPEED)
% 1.0;
let angle = t * PI * 2.0;
if self.square_path.is_none() {
let top_left = point2(-SQUARE_PATH_SIZE_2, -SQUARE_PATH_SIZE_2);
let square = Rect::new(top_left, size2(SQUARE_PATH_SIZE, SQUARE_PATH_SIZE));
let square_path = if self.rounded {
path_for_rounded_rectangle(&square, CORNER_RADIUS, render_context)
} else {
path_for_rectangle(&square, render_context)
};
self.square_path.replace(square_path);
}
let transformation = Transform2D::rotation(Angle::radians(angle))
.then_scale(square_size, square_size)
.then_translate(vec2(center_x, center_y));
let mut raster_builder = render_context.raster_builder().expect("raster_builder");
raster_builder.add(&self.clone_square_path(), Some(&transformation));
let square_raster = raster_builder.build();
layer_group.replace_all(std::iter::once(Layer {
raster: square_raster,
style: Style {
fill_rule: FillRule::NonZero,
fill: Fill::Solid(self.square_color),
blend_mode: BlendMode::Over,
},
}));
Ok(())
}
fn handle_message(&mut self, msg: Box<dyn Any>) {
if let Some(_) = msg.downcast_ref::<ToggleRoundedMessage>() {
self.rounded = !self.rounded;
self.square_path = None;
}
}
}
struct SpinningSquareViewAssistant {
background_color: Color,
square_color: Color,
start: Time,
scene_details: Option<SceneDetails>,
}
impl SpinningSquareViewAssistant {
fn new() -> Result<ViewAssistantPtr, Error> {
let square_color = Color { r: 0xff, g: 0x00, b: 0xff, a: 0xff };
let background_color = Color { r: 0xb7, g: 0x41, b: 0x0e, a: 0xff };
let start = Time::get_monotonic();
Ok(Box::new(SpinningSquareViewAssistant {
background_color,
square_color,
start,
scene_details: None,
}))
}
fn toggle_rounded(&mut self) {
if let Some(scene_details) = self.scene_details.as_mut() {
scene_details
.scene
.send_message(&scene_details.square, Box::new(ToggleRoundedMessage {}));
}
}
fn move_backward(&mut self) {
if let Some(scene_details) = self.scene_details.as_mut() {
scene_details
.scene
.move_facet_backward(scene_details.square)
.unwrap_or_else(|e| println!("error in move_facet_backward: {}", e));
}
}
fn move_forward(&mut self) {
if let Some(scene_details) = self.scene_details.as_mut() {
scene_details
.scene
.move_facet_forward(scene_details.square)
.unwrap_or_else(|e| println!("error in move_facet_forward: {}", e));
}
}
}
impl ViewAssistant for SpinningSquareViewAssistant {
fn resize(&mut self, _new_size: &Size) -> Result<(), Error> {
self.scene_details = None;
Ok(())
}
fn render(
&mut self,
render_context: &mut RenderContext,
ready_event: Event,
context: &ViewAssistantContext,
) -> Result<(), Error> {
let mut scene_details = self.scene_details.take().unwrap_or_else(|| {
let mut builder = SceneBuilder::new(self.background_color);
let square_facet = SpinningSquareFacet::new(self.square_color, self.start);
let square = builder.facet(Box::new(square_facet));
const STRIPE_COUNT: usize = 5;
let stripe_height = context.size.height / (STRIPE_COUNT * 2 + 1) as f32;
let stripe_size = size2(context.size.width, stripe_height);
let mut y = stripe_height;
for _ in 0..STRIPE_COUNT {
let stripe_bounds = Rect::new(point2(0.0, y), stripe_size);
builder.rectangle(stripe_bounds, Color::white());
y += stripe_height * 2.0;
}
let scene = builder.build();
SceneDetails { scene, square }
});
scene_details.scene.render(render_context, ready_event, context)?;
self.scene_details = Some(scene_details);
context.request_render();
Ok(())
}
fn handle_keyboard_event(
&mut self,
_context: &mut ViewAssistantContext,
_event: &input::Event,
keyboard_event: &input::keyboard::Event,
) -> Result<(), Error> {
const SPACE: u32 = ' ' as u32;
const B: u32 = 'b' as u32;
const F: u32 = 'f' as u32;
if let Some(code_point) = keyboard_event.code_point {
if keyboard_event.phase == input::keyboard::Phase::Pressed {
match code_point {
SPACE => self.toggle_rounded(),
B => self.move_backward(),
F => self.move_forward(),
_ => println!("code_point = {}", code_point),
}
}
}
Ok(())
}
}
fn main() -> Result<(), Error> {
fuchsia_trace_provider::trace_provider_create_with_fdio();
App::run(make_app_assistant::<SpinningSquareAppAssistant>())
}