blob: 8802ddcf21d8306fafc35a58ac930cc068d99903 [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::Error;
use carnelian::{
make_app_assistant, make_message, AnimationMode, App, AppAssistant, Canvas, Color, Coord,
MappingPixelSink, Point, Rect, Size, ViewAssistant, ViewAssistantContext, ViewAssistantPtr,
ViewKey, ViewMessages, ViewMode,
};
use euclid::rect;
use fidl_fuchsia_ui_input::{KeyboardEvent, KeyboardEventPhase};
use fuchsia_zircon::{AsHandleRef, ClockId, Signals, Time};
use std::{
collections::BTreeMap,
env,
f32::consts::PI,
io::{self, Read},
thread,
};
/// Convenience function that can be called from main and causes the Fuchsia process being
/// run over ssh to be terminated when the user hits control-C.
pub fn wait_for_close() {
if let Some(argument) = env::args().next() {
if !argument.starts_with("/tmp") {
return;
}
}
thread::spawn(move || loop {
let mut input = [0; 1];
match io::stdin().read_exact(&mut input) {
Ok(()) => {}
Err(_) => {
std::process::exit(0);
}
}
});
}
#[derive(Default)]
struct DrawingAppAssistant;
impl AppAssistant for DrawingAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant_canvas(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
let r = rect(0.0, 0.0, 0.0, 0.0);
Ok(Box::new(DrawingViewAssistant::new(r)?))
}
fn get_mode(&self) -> ViewMode {
ViewMode::Canvas
}
fn signals_wait_event(&self) -> bool {
return true;
}
}
struct DrawingViewAssistant {
bounds: Rect,
start: Time,
bg_color: Color,
color1: Color,
color2: Color,
color3: Color,
color4: Color,
color5: Color,
last_radius_map: BTreeMap<u64, Coord>,
}
impl DrawingViewAssistant {
pub fn new(bounds: Rect) -> Result<DrawingViewAssistant, Error> {
let bg_color = Color::from_hash_code("#EBD5B3")?;
let color1 = Color::from_hash_code("#B7410E")?;
let color2 = Color::from_hash_code("#008080")?;
let color3 = Color::from_hash_code("#FF00FF")?;
let color4 = Color::from_hash_code("#00008B")?;
let color5 = Color::from_hash_code("#7B68EE")?;
Ok(DrawingViewAssistant {
bounds,
start: Time::get(ClockId::Monotonic),
bg_color,
color1,
color2,
color3,
color4,
color5,
last_radius_map: BTreeMap::new(),
})
}
pub fn inval_circle(pt: &Point, radius: Coord, canvas: &mut Canvas<MappingPixelSink>) {
let diameter = radius * 2.0;
let top_left = *pt - Point::new(radius, radius);
let circle_bounds = Rect::new(top_left.to_point(), Size::new(diameter, diameter));
canvas.add_to_update_area(&circle_bounds);
}
}
impl ViewAssistant for DrawingViewAssistant {
fn setup(&mut self, _: &ViewAssistantContext<'_>) -> Result<(), Error> {
Ok(())
}
fn update(&mut self, context: &ViewAssistantContext<'_>) -> Result<(), Error> {
const SPEED: f32 = 0.25;
const SECONDS_PER_NANOSECOND: f32 = 1e-9;
let canvas = &mut context.canvas.as_ref().unwrap().borrow_mut();
let new_bounds = Rect::new(Point::zero(), context.size);
self.bounds = new_bounds;
let min_dimension = self.bounds.size.width.min(self.bounds.size.height);
let grid_size = min_dimension / 8.0;
let v = grid_size * 3.0;
let pt = Point::new(v, v);
if let Some(last_radius) = self.last_radius_map.get(&canvas.id) {
Self::inval_circle(&pt, *last_radius, canvas);
}
canvas.fill_rect(&self.bounds, self.bg_color);
let r = Rect::new(Point::new(grid_size, grid_size), Size::new(grid_size, grid_size));
canvas.fill_rect(&r, self.color1);
let time_now = Time::get(ClockId::Monotonic);
let t = ((time_now.into_nanos() - self.start.into_nanos()) as f32
* SECONDS_PER_NANOSECOND
* SPEED)
% 1.0;
let angle = t * PI * 2.0;
let circle_size = grid_size;
let radius = angle.cos() * circle_size + circle_size;
self.last_radius_map.insert(canvas.id, radius);
Self::inval_circle(&pt, radius, canvas);
canvas.fill_circle(&pt, radius, self.color2);
let rr = Rect::new(
Point::new(3.0 * grid_size, grid_size),
Size::new(grid_size * 2.0, grid_size),
);
canvas.fill_roundrect(&rr, grid_size / 8.0, self.color3);
let r_clipped = Rect::new(
Point::new(-grid_size, grid_size * 3.0),
Size::new(grid_size * 2.0, grid_size),
);
canvas.fill_roundrect(&r_clipped, grid_size / 6.0, self.color4);
let r_clipped2 = Rect::new(
Point::new(
self.bounds.size.width - grid_size,
self.bounds.size.height - grid_size / 3.0,
),
Size::new(grid_size * 4.0, grid_size * 2.0),
);
canvas.fill_roundrect(&r_clipped2, grid_size / 5.0, self.color5);
canvas.reset_update_area();
if let Some(wait_event) = context.wait_event.as_ref() {
wait_event.as_handle_ref().signal(Signals::NONE, Signals::EVENT_SIGNALED)?;
}
Ok(())
}
fn handle_keyboard_event(
&mut self,
context: &mut ViewAssistantContext<'_>,
keyboard_event: &KeyboardEvent,
) -> Result<(), Error> {
if keyboard_event.code_point == ' ' as u32
&& keyboard_event.phase == KeyboardEventPhase::Pressed
{
self.color3 = Color::from_hash_code("#FFFFFF")?;
}
context.queue_message(make_message(ViewMessages::Update));
Ok(())
}
fn initial_animation_mode(&mut self) -> AnimationMode {
return AnimationMode::EveryFrame;
}
}
fn main() -> Result<(), Error> {
println!("drawing: started");
wait_for_close();
App::run(make_app_assistant::<DrawingAppAssistant>())
}