blob: 495dde8b1a12118ac09e8960f562f9436eb2788a [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 failure::{bail, Error, Fail, ResultExt};
use fidl_fuchsia_amber as amber;
use fuchsia_app as app;
use fuchsia_async::{self as fasync, futures::TryFutureExt};
use fuchsia_framebuffer::{Config, Frame, FrameBuffer, PixelFormat};
use fuchsia_ui::{Canvas, Color, FontDescription, FontFace, Paint, PixelSink, Point, Rect};
use futures::future;
use std::{cell::RefCell, rc::Rc};
static FONT_DATA: &'static [u8] =
include_bytes!("../../../fonts/third_party/robotoslab/RobotoSlab-Regular.ttf");
struct RecoveryUI<'a> {
face: FontFace<'a>,
canvas: Canvas<FramePixelSink>,
config: Config,
text_size: u32,
}
impl<'a> RecoveryUI<'a> {
fn draw(&mut self, url: &str, user_code: &str) {
let r = Rect {
top: 0,
left: 0,
bottom: self.config.height,
right: self.config.width,
};
let bg = Color {
r: 255,
g: 0,
b: 255,
a: 255,
};
self.canvas.fill_rect(&r, bg);
let mut font_description = FontDescription {
baseline: 0,
face: &mut self.face,
size: self.text_size,
};
let (width, height) = self.canvas.measure_text(url, &mut font_description);
let paint = Paint {
fg: Color {
r: 255,
g: 255,
b: 255,
a: 255,
},
bg: bg,
};
self.canvas.fill_text(
url,
Point {
x: ((self.config.width / 2) as i32 - width / 2) as u32,
y: ((self.config.height / 4) as i32 - height / 2) as u32,
},
&mut font_description,
&paint,
);
let (width, height) = self.canvas.measure_text(user_code, &mut font_description);
self.canvas.fill_text(
user_code,
Point {
x: ((self.config.width / 2) as i32 - width / 2) as u32,
y: ((self.config.height / 2 + self.config.height / 4) as i32 - height / 2) as u32,
},
&mut font_description,
&paint,
);
}
}
const BYTES_PER_PIXEL: u32 = 4;
struct FramePixelSink {
frame: Frame,
}
impl PixelSink for FramePixelSink {
fn write_pixel_at_location(&mut self, x: u32, y: u32, value: &[u8]) {
let stride = self.frame.linear_stride_bytes() as u32;
let offset = (y * stride + x * BYTES_PER_PIXEL) as usize;
self.frame.write_pixel_at_offset(offset, &value);
}
fn write_pixel_at_offset(&mut self, offset: usize, value: &[u8]) {
self.frame.write_pixel_at_offset(offset, &value);
}
}
fn main() -> Result<(), Error> {
println!("Recovery UI");
let face = FontFace::new(FONT_DATA).unwrap();
let mut executor = fasync::Executor::new().context("Failed to create executor")?;
let fb = FrameBuffer::new(None, &mut executor).context("Failed to create framebuffer")?;
let config = fb.get_config();
if config.format != PixelFormat::Argb8888 {
bail!("Unsupported pixel format {:#?}", config.format);
}
let frame = fb.new_frame(&mut executor)?;
frame.present(&fb)?;
let stride = frame.linear_stride_bytes() as u32;
let sink = FramePixelSink { frame };
let canvas = Canvas::new_with_sink(sink, stride);
let mut ui = RecoveryUI {
face: face,
canvas,
config,
text_size: config.height / 12,
};
ui.draw("Verification URL", "User Code");
let ui_login = Rc::new(RefCell::new(ui));
let ui_fail = ui_login.clone();
let amber_control = app::client::connect_to_service::<amber::ControlMarker>()?;
let list_srcs = amber_control
.list_srcs()
.map_err(|e| e.context("listlist_srcs failed"))
.map_ok(move |src_list| {
if src_list.len() > 0 {
let login = amber_control
.login(&src_list[0].id)
.map_err(|e| e.context("login failed"))
.map_ok(move |device_code| println!("device_code = {:#?}", device_code));
fasync::spawn_local(login.unwrap_or_else(move |err| {
println!("in login recover {:#?}", err);
let mut ui_local = ui_login.borrow_mut();
ui_local.draw(&src_list[0].id, "Login failed")
}));
} else {
ui_fail
.borrow_mut()
.draw("Could not get", "source list from Amber")
}
});
fasync::spawn_local(
list_srcs.unwrap_or_else(|err| println!("in list_srcs recover {:#?}", err)),
);
executor.run_singlethreaded(future::empty::<()>());
unreachable!();
}