Cleanup for new conventions

This commit is contained in:
Brian Anderson 2012-06-22 01:39:56 -07:00
parent b1b0434bc4
commit b754510d53
8 changed files with 167 additions and 131 deletions

View file

@ -3,21 +3,31 @@
tasks. tasks.
"] "]
export ControlMsg, PingMsg; export Content, ControlMsg, PingMsg;
export content;
import comm::{port, chan, listen};
import task::{spawn, spawn_listener};
import io::{read_whole_file, println};
import result::{ok, err};
import dom::base::NodeScope; import dom::base::NodeScope;
import dom::rcu::WriterMethods; import dom::rcu::WriterMethods;
import dom::style; import dom::style;
import style::print_sheet;
import parser::lexer::{spawn_css_lexer_task, spawn_html_parser_task}; import parser::lexer::{spawn_css_lexer_task, spawn_html_parser_task};
import parser::css_builder::build_stylesheet; import parser::css_builder::build_stylesheet;
import parser::html_builder::build_dom; import parser::html_builder::build_dom;
import layout::layout_task; import layout::layout_task;
import layout_task::{Layout, BuildMsg};
import jsrt = js::rust::rt;
import js::rust::methods; import js::rust::methods;
import js::global::{global_class, debug_fns};
import result::extensions; import result::extensions;
type Content = chan<ControlMsg>;
enum ControlMsg { enum ControlMsg {
ParseMsg(~str), ParseMsg(~str),
ExecuteMsg(~str), ExecuteMsg(~str),
@ -30,24 +40,23 @@ enum PingMsg {
#[doc="Sends a ping to layout and waits for the response."] #[doc="Sends a ping to layout and waits for the response."]
#[warn(no_non_implicitly_copyable_typarams)] #[warn(no_non_implicitly_copyable_typarams)]
fn join_layout(scope: NodeScope, to_layout: chan<layout_task::Msg>) { fn join_layout(scope: NodeScope, layout: Layout) {
if scope.is_reader_forked() { if scope.is_reader_forked() {
comm::listen { listen { |response_from_layout|
|from_layout| layout.send(layout_task::PingMsg(response_from_layout));
to_layout.send(layout_task::PingMsg(from_layout)); response_from_layout.recv();
from_layout.recv();
} }
scope.reader_joined(); scope.reader_joined();
} }
} }
#[warn(no_non_implicitly_copyable_typarams)] #[warn(no_non_implicitly_copyable_typarams)]
fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> { fn Content(layout: Layout) -> Content {
task::spawn_listener::<ControlMsg> { spawn_listener::<ControlMsg> {
|from_master| |from_master|
let scope = NodeScope(); let scope = NodeScope();
let rt = js::rust::rt(); let rt = jsrt();
loop { loop {
alt from_master.recv() { alt from_master.recv() {
ParseMsg(filename) { ParseMsg(filename) {
@ -59,9 +68,9 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
let new_file = (*filename).substr(0u, (*filename).len() - 5u) + ".css"; let new_file = (*filename).substr(0u, (*filename).len() - 5u) + ".css";
// Send off a task to parse the stylesheet // Send off a task to parse the stylesheet
let css_port = comm::port(); let css_port = port();
let css_chan = comm::chan(css_port); let css_chan = chan(css_port);
task::spawn {|| spawn { ||
let new_file = copy new_file; let new_file = copy new_file;
let css_stream = spawn_css_lexer_task(~new_file); let css_stream = spawn_css_lexer_task(~new_file);
let css_rules = build_stylesheet(css_stream); let css_rules = build_stylesheet(css_stream);
@ -74,19 +83,19 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
let root = build_dom(scope, stream); let root = build_dom(scope, stream);
// Collect the css stylesheet // Collect the css stylesheet
let css_rules = comm::recv(css_port); let css_rules = css_port.recv();
// Apply the css rules to the dom tree: // Apply the css rules to the dom tree:
// TODO // TODO
#debug["%s",style::print_sheet(css_rules)]; #debug["%s", print_sheet(css_rules)];
// Now, join the layout so that they will see the latest // Now, join the layout so that they will see the latest
// changes we have made. // changes we have made.
join_layout(scope, to_layout); join_layout(scope, layout);
// Send new document to layout. // Send new document to layout.
to_layout.send(layout_task::BuildMsg(root, css_rules)); layout.send(BuildMsg(root, css_rules));
// Indicate that reader was forked so any further // Indicate that reader was forked so any further
// changes will be isolated. // changes will be isolated.
@ -96,17 +105,17 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
ExecuteMsg(filename) { ExecuteMsg(filename) {
#debug["content: Received filename `%s` to execute", *filename]; #debug["content: Received filename `%s` to execute", *filename];
alt io::read_whole_file(*filename) { alt read_whole_file(*filename) {
result::err(msg) { err(msg) {
io::println(#fmt["Error opening %s: %s", *filename, msg]); println(#fmt["Error opening %s: %s", *filename, msg]);
} }
result::ok(bytes) { ok(bytes) {
let cx = rt.cx(); let cx = rt.cx();
cx.set_default_options_and_version(); cx.set_default_options_and_version();
cx.set_logging_error_reporter(); cx.set_logging_error_reporter();
cx.new_compartment(js::global::global_class).chain { cx.new_compartment(global_class).chain {
|compartment| |compartment|
compartment.define_functions(js::global::debug_fns); compartment.define_functions(debug_fns);
cx.evaluate_script(compartment.global_obj, bytes, *filename, 1u) cx.evaluate_script(compartment.global_obj, bytes, *filename, 1u)
}; };
} }
@ -114,7 +123,7 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
} }
ExitMsg { ExitMsg {
to_layout.send(layout_task::ExitMsg); layout.send(layout_task::ExitMsg);
break; break;
} }
} }

View file

@ -1,36 +1,42 @@
import gfx::renderer::{Renderer, Sink};
import task::spawn_listener;
import comm::chan; import comm::chan;
import gfx::renderer; import layout::layout_task;
import layout_task::Layout;
import content::{Content, ExecuteMsg, ParseMsg};
type Engine = chan<Msg>;
enum Msg { enum Msg {
LoadURLMsg(~str), LoadURLMsg(~str),
ExitMsg(comm::chan<()>) ExitMsg(chan<()>)
} }
fn engine<S:renderer::sink send copy>(sink: S) -> chan<Msg> { fn Engine<S: Sink send copy>(sink: S) -> Engine {
task::spawn_listener::<Msg> {|self_ch| spawn_listener::<Msg> { |request|
// The renderer // The renderer
let renderer = renderer::renderer(sink); let renderer = Renderer(sink);
// The layout task // The layout task
let layout = layout::layout_task::layout(renderer); let layout = Layout(renderer);
// The content task // The content task
let content = content::content(layout); let content = Content(layout);
loop { loop {
alt self_ch.recv() { alt request.recv() {
LoadURLMsg(url) { LoadURLMsg(url) {
let url = copy url; let url = copy url;
if (*url).ends_with(".js") { if (*url).ends_with(".js") {
content.send(content::ExecuteMsg(url)) content.send(ExecuteMsg(url))
} else { } else {
content.send(content::ParseMsg(url)) content.send(ParseMsg(url))
} }
} }
ExitMsg(sender) { ExitMsg(sender) {
content.send(content::ExitMsg); content.send(content::ExitMsg);
layout.send(layout::layout_task::ExitMsg); layout.send(layout_task::ExitMsg);
listen { listen {
|response_channel| |response_channel|
renderer.send(renderer::ExitMsg(response_channel)); renderer.send(renderer::ExitMsg(response_channel));

View file

@ -5,64 +5,76 @@ Each time the renderer renders a frame the bufsink will output a
`[u8]` containing the frame in PNG format. `[u8]` containing the frame in PNG format.
"]; "];
export msg, pngsink; export PngSink, Msg;
import azure::cairo; import libc::{c_int, c_uint, c_void, c_uchar};
import azure_bg = azure::bindgen;
import azure::AzDrawTargetRef; import azure::AzDrawTargetRef;
import azure_bg = azure::bindgen;
import azure_bg::{AzCreateDrawTargetForCairoSurface, AzReleaseDrawTarget};
import azure::cairo;
import cairo::{CAIRO_FORMAT_ARGB32, cairo_surface_t, cairo_status_t,
CAIRO_STATUS_SUCCESS};
import cairo_bg = cairo::bindgen; import cairo_bg = cairo::bindgen;
import cairo_bg::{cairo_image_surface_create, cairo_surface_destroy,
cairo_surface_write_to_png_stream};
import renderer::{Renderer, Sink, RenderMsg};
import task::spawn_listener;
import comm::chan;
import unsafe::reinterpret_cast;
import vec_from_buf = vec::unsafe::from_buf;
import ptr::addr_of;
enum msg { type PngSink = chan<Msg>;
begin_drawing(chan<AzDrawTargetRef>),
draw(chan<AzDrawTargetRef>, AzDrawTargetRef), enum Msg {
exit BeginDrawing(chan<AzDrawTargetRef>),
Draw(chan<AzDrawTargetRef>, AzDrawTargetRef),
Exit
} }
impl pngsink of renderer::sink for chan<msg> { impl PngSink of Sink for chan<Msg> {
fn begin_drawing(next_dt: chan<AzDrawTargetRef>) { fn begin_drawing(next_dt: chan<AzDrawTargetRef>) {
self.send(begin_drawing(next_dt)) self.send(BeginDrawing(next_dt))
} }
fn draw(next_dt: chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef) { fn draw(next_dt: chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef) {
self.send(draw(next_dt, draw_me)) self.send(Draw(next_dt, draw_me))
} }
} }
fn pngsink(output: chan<[u8]>) -> chan<msg> { fn PngSink(output: chan<[u8]>) -> PngSink {
task::spawn_listener::<msg> { |po| spawn_listener::<Msg> { |po|
let cairo_surf = cairo_bg::cairo_image_surface_create( let cairo_surf = cairo_image_surface_create(
cairo::CAIRO_FORMAT_ARGB32, 800 as libc::c_int, 600 as libc::c_int CAIRO_FORMAT_ARGB32, 800 as c_int, 600 as c_int
); );
assert cairo_surf.is_not_null(); assert cairo_surf.is_not_null();
let draw_target = azure_bg::AzCreateDrawTargetForCairoSurface(cairo_surf); let draw_target = AzCreateDrawTargetForCairoSurface(cairo_surf);
assert draw_target.is_not_null(); assert draw_target.is_not_null();
loop { loop {
alt po.recv() { alt po.recv() {
begin_drawing(sender) { BeginDrawing(sender) {
#debug("pngsink: begin_drawing"); #debug("pngsink: begin_drawing");
sender.send(draw_target); sender.send(draw_target);
} }
draw(sender, dt) { Draw(sender, dt) {
#debug("pngsink: draw"); #debug("pngsink: draw");
do_draw(sender, dt, output, cairo_surf); do_draw(sender, dt, output, cairo_surf);
} }
exit { break } Exit { break }
} }
} }
azure_bg::AzReleaseDrawTarget(draw_target); AzReleaseDrawTarget(draw_target);
cairo_bg::cairo_surface_destroy(cairo_surf); cairo_surface_destroy(cairo_surf);
} }
} }
fn do_draw(sender: chan<AzDrawTargetRef>, fn do_draw(sender: chan<AzDrawTargetRef>,
dt: AzDrawTargetRef, dt: AzDrawTargetRef,
output: comm::chan<[u8]>, output: chan<[u8]>,
cairo_surf: *cairo::cairo_surface_t) { cairo_surf: *cairo_surface_t) {
import libc::*;
listen {|data_ch| listen {|data_ch|
@ -70,26 +82,26 @@ fn do_draw(sender: chan<AzDrawTargetRef>,
data: *c_uchar, data: *c_uchar,
len: c_uint) len: c_uint)
-> cairo::cairo_status_t unsafe { -> cairo_status_t unsafe {
let p: *chan<[u8]> = unsafe::reinterpret_cast(closure); let p: *chan<[u8]> = reinterpret_cast(closure);
let data_ch = *p; let data_ch = *p;
// Convert from *c_uchar to *u8 // Convert from *c_uchar to *u8
let data = unsafe::reinterpret_cast(data); let data = reinterpret_cast(data);
let len = len as uint; let len = len as uint;
// Copy to a vector // Copy to a vector
let data = vec::unsafe::from_buf(data, len); let data = vec_from_buf(data, len);
data_ch.send(data); data_ch.send(data);
ret cairo::CAIRO_STATUS_SUCCESS; ret CAIRO_STATUS_SUCCESS;
} }
let closure = ptr::addr_of(data_ch); let closure = addr_of(data_ch);
unsafe { unsafe {
cairo_bg::cairo_surface_write_to_png_stream( cairo_surface_write_to_png_stream(
cairo_surf, write_fn, unsafe::reinterpret_cast(closure)); cairo_surf, write_fn, reinterpret_cast(closure));
} }
// Collect the entire image into a single vector // Collect the entire image into a single vector
@ -110,17 +122,17 @@ fn sanity_check() {
listen { listen {
|self_channel| |self_channel|
let sink = pngsink(self_channel); let sink = PngSink(self_channel);
let renderer = renderer::renderer(sink); let renderer = Renderer(sink);
let dlist = []; let dlist = [];
renderer.send(renderer::RenderMsg(dlist)); renderer.send(RenderMsg(dlist));
listen { listen {
|from_renderer| |from_renderer|
renderer.send(renderer::ExitMsg(from_renderer)); renderer.send(renderer::ExitMsg(from_renderer));
from_renderer.recv(); from_renderer.recv();
} }
sink.send(exit) sink.send(Exit)
} }
} }

View file

@ -8,6 +8,8 @@ import azure::bindgen::*;
import libc::size_t; import libc::size_t;
import text::text_run::TextRun; import text::text_run::TextRun;
type Renderer = chan<Msg>;
enum Msg { enum Msg {
RenderMsg(dl::display_list), RenderMsg(dl::display_list),
ExitMsg(comm::chan<()>) ExitMsg(comm::chan<()>)
@ -17,12 +19,12 @@ enum Msg {
The interface used to by the renderer to aquire draw targets for The interface used to by the renderer to aquire draw targets for
each rendered frame and submit them to be drawn to the display each rendered frame and submit them to be drawn to the display
"] "]
iface sink { iface Sink {
fn begin_drawing(next_dt: chan<AzDrawTargetRef>); fn begin_drawing(next_dt: chan<AzDrawTargetRef>);
fn draw(next_dt: chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef); fn draw(next_dt: chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef);
} }
fn renderer<S: sink send copy>(sink: S) -> chan<Msg> { fn Renderer<S: Sink send copy>(sink: S) -> chan<Msg> {
task::spawn_listener::<Msg> {|po| task::spawn_listener::<Msg> {|po|
listen { listen {
|draw_target_ch| |draw_target_ch|

View file

@ -9,6 +9,7 @@ import display_list_builder::build_display_list;
import dom::base::{Node}; import dom::base::{Node};
import dom::style::stylesheet; import dom::style::stylesheet;
import gfx::geometry::px_to_au; import gfx::geometry::px_to_au;
import gfx::renderer::Renderer;
import base::{NodeMethods, layout_methods}; import base::{NodeMethods, layout_methods};
import layout::style::style::style_methods; import layout::style::style::style_methods;
import box_builder::box_builder_methods; import box_builder::box_builder_methods;
@ -17,16 +18,18 @@ import layout::style::apply::ApplyStyleBoxMethods;
import task::*; import task::*;
import comm::*; import comm::*;
type Layout = chan<Msg>;
enum Msg { enum Msg {
BuildMsg(Node, stylesheet), BuildMsg(Node, stylesheet),
PingMsg(chan<content::PingMsg>), PingMsg(chan<content::PingMsg>),
ExitMsg ExitMsg
} }
fn layout(to_renderer: chan<renderer::Msg>) -> chan<Msg> { fn Layout(renderer: Renderer) -> Layout {
spawn_listener::<Msg> { |po| spawn_listener::<Msg> { |request|
loop { loop {
alt po.recv() { alt request.recv() {
PingMsg(ping_channel) { PingMsg(ping_channel) {
ping_channel.send(content::PongMsg); ping_channel.send(content::PongMsg);
} }
@ -47,7 +50,7 @@ fn layout(to_renderer: chan<renderer::Msg>) -> chan<Msg> {
this_box.reflow(px_to_au(800)); this_box.reflow(px_to_au(800));
let dlist = build_display_list(this_box); let dlist = build_display_list(this_box);
to_renderer.send(renderer::RenderMsg(dlist)); renderer.send(renderer::RenderMsg(dlist));
} }
} }
} }

View file

@ -5,18 +5,18 @@ from command line arguments.
"]; "];
type opts = { type Opts = {
urls: [str], urls: [str],
render_mode: render_mode render_mode: RenderMode
}; };
enum render_mode { enum RenderMode {
screen, Screen,
png(str) Png(str)
} }
#[warn(no_non_implicitly_copyable_typarams)] #[warn(no_non_implicitly_copyable_typarams)]
fn from_cmdline_args(args: [str]) -> opts { fn from_cmdline_args(args: [str]) -> Opts {
import std::getopts; import std::getopts;
let args = args.tail(); let args = args.tail();
@ -37,8 +37,8 @@ fn from_cmdline_args(args: [str]) -> opts {
}; };
let render_mode = alt getopts::opt_maybe_str(match, "o") { let render_mode = alt getopts::opt_maybe_str(match, "o") {
some(output_file) { png(copy output_file) } some(output_file) { Png(copy output_file) }
none { screen } none { Screen }
}; };
{ {

View file

@ -1,4 +1,4 @@
export msg, osmain, gfxsink; export OSMain, Msg;
import azure::*; import azure::*;
import azure::bindgen::*; import azure::bindgen::*;
@ -6,17 +6,19 @@ import azure::cairo;
import azure::cairo::bindgen::*; import azure::cairo::bindgen::*;
import comm::*; import comm::*;
import azure::cairo::cairo_surface_t; import azure::cairo::cairo_surface_t;
import gfx::renderer; import gfx::renderer::{Sink};
enum msg { type OSMain = chan<Msg>;
begin_drawing(chan<AzDrawTargetRef>),
draw(chan<AzDrawTargetRef>, AzDrawTargetRef), enum Msg {
add_key_handler(chan<()>), BeginDrawing(chan<AzDrawTargetRef>),
exit Draw(chan<AzDrawTargetRef>, AzDrawTargetRef),
AddKeyHandler(chan<()>),
Exit
} }
fn osmain() -> chan<msg> { fn OSMain() -> OSMain {
on_osmain::<msg> {|po| on_osmain::<Msg> {|po|
platform::runmain {|| platform::runmain {||
#debug("preparing to enter main loop"); #debug("preparing to enter main loop");
mainloop(po); mainloop(po);
@ -24,7 +26,7 @@ fn osmain() -> chan<msg> {
} }
} }
fn mainloop(po: port<msg>) { fn mainloop(po: port<Msg>) {
let mut key_handlers: [chan<()>] = []; let mut key_handlers: [chan<()>] = [];
@ -42,6 +44,7 @@ fn mainloop(po: port<msg>) {
loop { loop {
sdl::event::poll_event {|event| sdl::event::poll_event {|event|
alt event { alt event {
sdl::event::keydown_event(_) { sdl::event::keydown_event(_) {
key_handlers.iter {|key_ch| key_handlers.iter {|key_ch|
@ -55,13 +58,13 @@ fn mainloop(po: port<msg>) {
// Handle messages // Handle messages
if po.peek() { if po.peek() {
alt check po.recv() { alt check po.recv() {
add_key_handler(key_ch) { AddKeyHandler(key_ch) {
key_handlers += [key_ch]; key_handlers += [key_ch];
} }
begin_drawing(sender) { BeginDrawing(sender) {
lend_surface(surfaces, sender); lend_surface(surfaces, sender);
} }
draw(sender, dt) { Draw(sender, dt) {
return_surface(surfaces, dt); return_surface(surfaces, dt);
lend_surface(surfaces, sender); lend_surface(surfaces, sender);
@ -89,12 +92,12 @@ fn mainloop(po: port<msg>) {
Implementation to allow the osmain channel to be used as a graphics Implementation to allow the osmain channel to be used as a graphics
sink for the renderer sink for the renderer
"] "]
impl gfxsink of renderer::sink for chan<msg> { impl OSMain of Sink for OSMain {
fn begin_drawing(next_dt: chan<AzDrawTargetRef>) { fn begin_drawing(next_dt: chan<AzDrawTargetRef>) {
self.send(begin_drawing(next_dt)) self.send(BeginDrawing(next_dt))
} }
fn draw(next_dt: chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef) { fn draw(next_dt: chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef) {
self.send(draw(next_dt, draw_me)) self.send(Draw(next_dt, draw_me))
} }
} }

View file

@ -3,18 +3,21 @@ import parser::lexer;
import result::extensions; import result::extensions;
import gfx::renderer; import gfx::renderer;
import platform::osmain; import platform::osmain;
import osmain::{OSMain, AddKeyHandler};
import opts::{Opts, Screen, Png};
import engine::{Engine, LoadURLMsg};
fn main(args: [str]) { fn main(args: [str]) {
run(opts::from_cmdline_args(args)) run(opts::from_cmdline_args(args))
} }
#[warn(no_non_implicitly_copyable_typarams)] #[warn(no_non_implicitly_copyable_typarams)]
fn run(opts: opts::opts) { fn run(opts: Opts) {
alt opts.render_mode { alt opts.render_mode {
opts::screen { Screen {
run_pipeline_screen(opts.urls) run_pipeline_screen(opts.urls)
} }
opts::png(outfile) { Png(outfile) {
assert opts.urls.is_not_empty(); assert opts.urls.is_not_empty();
if opts.urls.len() > 1u { if opts.urls.len() > 1u {
fail "servo asks that you stick to a single URL in PNG output mode" fail "servo asks that you stick to a single URL in PNG output mode"
@ -26,58 +29,56 @@ fn run(opts: opts::opts) {
fn run_pipeline_screen(urls: [str]) { fn run_pipeline_screen(urls: [str]) {
// Use the platform thread as the renderer sink
import osmain::gfxsink;
// The platform event handler thread // The platform event handler thread
let osmain = osmain::osmain(); let osmain = OSMain();
// Create a serve instance // Create a serve instance
let engine = engine::engine(osmain); let engine = Engine(osmain);
// Send each file to render then wait for keypress // Send each file to render then wait for keypress
listen {|key_ch| listen { |keypress_from_osmain|
osmain.send(platform::osmain::add_key_handler(key_ch)); osmain.send(AddKeyHandler(keypress_from_osmain));
for urls.each { |filename| for urls.each { |filename|
#debug["master: Sending filename `%s`", filename]; #debug["master: Sending filename `%s`", filename];
engine.send(engine::LoadURLMsg(~copy filename)); engine.send(LoadURLMsg(~copy filename));
#debug["master: Waiting for keypress"]; #debug["master: Waiting for keypress"];
key_ch.recv(); keypress_from_osmain.recv();
} }
} }
// Shut everything down // Shut everything down
#debug["master: Shut down"]; #debug["master: Shut down"];
listen {|resp_ch| listen { |exit_response_from_engine|
engine.send(engine::ExitMsg(resp_ch)); engine.send(engine::ExitMsg(exit_response_from_engine));
resp_ch.recv(); exit_response_from_engine.recv();
} }
osmain.send(platform::osmain::exit); osmain.send(osmain::Exit);
} }
fn run_pipeline_png(-url: str, outfile: str) { fn run_pipeline_png(-url: str, outfile: str) {
// Use a PNG encoder as the graphics sink // Use a PNG encoder as the graphics sink
import gfx::pngsink; import gfx::pngsink;
import pngsink::pngsink; import pngsink::PngSink;
import result::{ok, err};
import io::{writer, buffered_file_writer};
listen {|pngdata| listen { |pngdata_from_sink|
let sink = pngsink::pngsink(pngdata); let sink = PngSink(pngdata_from_sink);
let engine = engine::engine(sink); let engine = Engine(sink);
let url = copy url; let url = copy url;
engine.send(engine::LoadURLMsg(~url)); engine.send(LoadURLMsg(~url));
alt io::buffered_file_writer(outfile) { alt buffered_file_writer(outfile) {
result::ok(writer) { ok(writer) {
import io::writer; writer.write(pngdata_from_sink.recv())
writer.write(pngdata.recv())
} }
result::err(e) { fail e } err(e) { fail e }
} }
listen {|response_channel| listen { |exit_response_from_engine|
engine.send(engine::ExitMsg(response_channel)); engine.send(engine::ExitMsg(exit_response_from_engine));
response_channel.recv(); exit_response_from_engine.recv();
} }
sink.send(pngsink::exit); sink.send(pngsink::Exit);
} }
} }