Skip to content

Commit

Permalink
FRP GUI proof of concept (bkase#2)
Browse files Browse the repository at this point in the history
Machinery in place to use future-signals to drive a GUI
  • Loading branch information
bkase authored Apr 2, 2019
1 parent 170935b commit f114b12
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ cfg-if = "0.1.2"
wasm-bindgen = "0.2"
packed_struct = "0.3"
packed_struct_codegen = "0.3"
js-sys = "0.3.17"
virtual-dom-rs = "0.6.7"
css-rs-macro = "0.1"
futures-preview = "0.3.0-alpha.13"
futures-util-preview = "0.3.0-alpha.13"
futures-signals = "0.3.4"

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
Expand All @@ -32,6 +38,8 @@ wee_alloc = { version = "0.4.2", optional = true }
version = "0.3.4"
features = [
'Document',
'MouseEvent',
'console',
'Element',
'HtmlElement',
'HtmlCanvasElement',
Expand Down
24 changes: 24 additions & 0 deletions src/debug_gui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use futures::{Future, FutureExt};
use futures_signals::signal::{Signal, SignalExt};
use virtual_dom_rs::prelude::*;
use web_utils;

fn sample_view<In: Signal<Item = u32>>(model: In) -> impl Signal<Item = VirtualNode> {
model.map(|m| {
let greetings = format!("Hello, World! {}", m);
html! { <div> { greetings } </div> }
})
}

pub fn run<In: Signal<Item = u32>>(rx: In) -> impl Future<Output = ()> {
let start_view = html! { <div> Hello </div> };

let body = web_utils::document().body().unwrap();

let mut dom_updater = DomUpdater::new_append_to_mount(start_view, &body);

sample_view(rx)
.map(move |vdom| dom_updater.update(vdom))
.to_future()
.map(|_| ())
}
11 changes: 11 additions & 0 deletions src/future_driver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use futures::task::Poll;
use futures::Future;
use std::cell::RefCell;
use std::rc::Rc;

pub fn tick<F: Future + Unpin>(f: Rc<RefCell<F>>) -> Poll<F::Output> {
let waker = futures_util::task::noop_waker();
// TODO: Is this safe?
let mut pin = unsafe { std::pin::Pin::new_unchecked(f.borrow_mut()) };
pin.as_mut().poll(&waker)
}
38 changes: 34 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
// hello world

#![feature(proc_macro_hygiene)]
#![feature(futures_api)]

extern crate console_error_panic_hook;
extern crate css_rs_macro;
extern crate futures;
extern crate futures_signals;
extern crate futures_util;
extern crate js_sys;
extern crate virtual_dom_rs;
extern crate wasm_bindgen;
extern crate web_sys;

extern crate packed_struct;
#[macro_use]
Expand All @@ -12,6 +24,8 @@ pub mod test {

mod alu;
mod cpu;
mod debug_gui;
mod future_driver;
mod instr;
mod mem;
mod ppu;
Expand All @@ -21,12 +35,12 @@ mod tile_debug;
mod utils;
mod web_utils;

use futures::task::Poll;
use futures_signals::signal::Mutable;
use std::cell::RefCell;
use std::rc::Rc;

use wasm_bindgen::prelude::*;
use wasm_bindgen::{Clamped, JsCast};

use web_utils::*;

fn draw_frame(data: &mut Vec<u8>, width: u32, height: u32, i: u32) {
Expand Down Expand Up @@ -73,8 +87,17 @@ pub fn run() -> Result<(), JsValue> {
let mut i = 0;
let mut data = vec![0; (height * width * 4) as usize];

let state: Mutable<u32> = Mutable::new(0);
let signal_future = Rc::new(RefCell::new(debug_gui::run(state.signal())));

let mut last = performance_now();
*g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
let closure = move || {
{
// Change the state
let mut lock = state.lock_mut();
*lock = i;
}

// Stop after 500 frames
if i > 500 {
// Drop our handle to this closure so that it will get cleaned
Expand Down Expand Up @@ -104,9 +127,16 @@ pub fn run() -> Result<(), JsValue> {
// Increment once per call
i += 1;

// Drive our GUI
match future_driver::tick(signal_future.clone()) {
Poll::Pending => (),
Poll::Ready(()) => panic!("The signal should never end!"),
};

// Schedule ourself for another requestAnimationFrame callback.
request_animation_frame(f.borrow().as_ref().unwrap());
}) as Box<FnMut()>));
};
*g.borrow_mut() = Some(Closure::wrap(Box::new(closure) as Box<FnMut()>));

request_animation_frame(g.borrow().as_ref().unwrap());
Ok(())
Expand Down

0 comments on commit f114b12

Please sign in to comment.