<commit_before>pub mod display_list;
pub mod export;

use glutin::dpi::PhysicalSize;
use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D, LineCap, LineJoin, FillRule};
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_renderer::{
    concurrent::rayon::RayonExecutor,
    concurrent::scene_proxy::SceneProxy,
    options::BuildOptions,
    gpu::{
        renderer::Renderer as PathfinderRenderer,
        options::{DestFramebuffer, RendererOptions},
    },
};

use crate::{Point, Color};

use super::coords::ScreenPoint;
use super::state::{DrawingState, TurtleState};

use display_list::{DisplayList, DrawPrim, Line, Polygon};

/// Converts a color from the representation in this crate to the one used in the renderer
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
fn convert_color(color: Color) -> ColorU {
    let Color {red, green, blue, alpha} = color;
    ColorU {
        r: red.round() as u8,
        g: green.round() as u8,
        b: blue.round() as u8,
        a: (alpha * 255.0).round() as u8,
    }
}

/// A renderer that draws on the current OpenGL context
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
pub struct Renderer {
    renderer: PathfinderRenderer<GLDevice>,
    font_context: CanvasFontContext,
    scene: SceneProxy,
    /// Information about DPI scaling: https://docs.rs/glutin/0.24.0/glutin/dpi/index.html
    dpi_scale: f64,
}

#[cfg_attr(any(feature = "test", test), allow(dead_code))]
impl Renderer {
    /// Creates a new renderer with the given physical size in pixels
    pub fn new(draw_size: PhysicalSize<u32>, dpi_scale: f64) -> Self {
        let renderer = PathfinderRenderer::new(
            GLDevice::new(GLVersion::GL3, 0),
            &EmbeddedResourceLoader::new(),
            DestFramebuffer::full_window(vec2i(draw_size.width as i32, draw_size.height as i32)),
            // This background color will be overwritten during the first render
            RendererOptions {
                background_color: Some(ColorU::white().to_f32()),
                ..RendererOptions::default()
            },
        );

        Self {
            renderer,
            font_context: CanvasFontContext::from_system_source(),
            scene: SceneProxy::new(RayonExecutor),
            dpi_scale,
        }
    }

    /// Updates the scale factor used during rendering
    pub fn scale_factor(&self) -> f64 {
        self.dpi_scale
    }

    /// Updates the scale factor used during rendering
    pub fn set_scale_factor(&mut self, dpi_scale: f64) {
        self.dpi_scale = dpi_scale;
    }

    /// Draw the given primitives onto a canvas of the given size
    ///
    /// Size is passed in to ensure that it is up-to-date
    pub fn render<'a>(
        &mut self,
        draw_size: PhysicalSize<u32>,
        display_list: &DisplayList,
        drawing: &DrawingState,
        turtles: impl Iterator<Item=&'a TurtleState>
    ) {
        // Set the current draw size
        self.renderer.replace_dest_framebuffer(
            DestFramebuffer::full_window(vec2i(draw_size.width as i32, draw_size.height as i32))
        );

        // Clear to background color
        self.renderer.set_options(RendererOptions {
            background_color: Some(convert_color(drawing.background).to_f32()),
            ..RendererOptions::default()
        });

        // The size of the framebuffer
        let fb_size = vec2f(draw_size.width as f32, draw_size.height as f32);
        let mut canvas = Canvas::new(fb_size)
            .get_context_2d(self.font_context.clone());

        // Set default options for all operations
        canvas.set_line_cap(LineCap::Round);
        canvas.set_line_join(LineJoin::Round);

        //TODO: Remove this line once servo/pathfinder#318 is fixed.
        //  Link: https://github.com/servo/pathfinder/issues/318
        // Need to render *something* every time to get pathfinder to even render a background
        canvas.stroke_rect(pathfinder_geometry::rect::RectF::new(vec2f(0.0, 0.0), vec2f(1.0, 1.0)));

        // Draw each primitive
        let dpi_scale = self.dpi_scale;
        let center = drawing.center;
        let fb_center = fb_size / 2.0;
        for prim in display_list.iter() {
            match prim {
                &DrawPrim::Line(Line {start, end, thickness, color}) => {
                    let mut path = Path2D::new();

                    path.move_to(ScreenPoint::from_logical(start, dpi_scale, center, fb_center.into()).into());
                    path.line_to(ScreenPoint::from_logical(end, dpi_scale, center, fb_center.into()).into());

                    canvas.set_line_width((thickness * dpi_scale) as f32);
                    canvas.set_stroke_style(convert_color(color));
                    canvas.stroke_path(path);
                },

                &DrawPrim::Polygon(Polygon {ref points, fill_color}) => {
                    // Skip obviously degenerate polygons
                    if points.len() <= 2 {
                        continue;
                    }

                    let mut path = Path2D::new();

                    path.move_to(ScreenPoint::from_logical(points[0], dpi_scale, center, fb_center.into()).into());
                    for &point in &points[1..] {
                        path.line_to(ScreenPoint::from_logical(point, dpi_scale, center, fb_center.into()).into());
                    }

                    path.close_path();

                    canvas.set_fill_style(convert_color(fill_color));
                    canvas.fill_path(path, FillRule::Winding);
                },
            }
        }

        // The turtle shell specified in logical coordinates relative to the turtle position
        let shell = &[Point {x: 0.0, y: 15.0}, Point {x: 10.0, y: 0.0}, Point {x: 0.0, y: -15.0}];
        for turtle in turtles {
            let &TurtleState {position, heading, is_visible, ..} = turtle;
            if !is_visible {
                continue;
            }

            let Point {x: turtle_x, y: turtle_y} = position;
            let cos = heading.cos();
            let sin = heading.sin();
            let shell_screen_coord = |Point {x, y}| {
                // Rotate each point by the heading and add the current turtle position
                let point = Point {
                    x: cos * x - sin * y + turtle_x,
                    y: sin * x + cos * y + turtle_y,
                };
                ScreenPoint::from_logical(point, dpi_scale, center, fb_center.into()).into()
            };

            let mut path = Path2D::new();
            path.move_to(shell_screen_coord(shell[0]));
            for &point in &shell[1..] {
                path.line_to(shell_screen_coord(point));
            }
            path.close_path();
            canvas.set_fill_style(ColorU::white());
            canvas.fill_path(path.clone(), FillRule::Winding);
            canvas.set_line_width((1.0 * dpi_scale) as f32);
            canvas.set_stroke_style(ColorU::black());
            canvas.stroke_path(path);
        }

        // Build and render scene
        self.scene.replace_scene(canvas.into_canvas().into_scene());
        self.scene.build_and_render(&mut self.renderer, BuildOptions::default());
    }
}
<commit_msg>Only converting fb_center into ScreenPoint once<commit_after>pub mod display_list;
pub mod export;

use glutin::dpi::PhysicalSize;
use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D, LineCap, LineJoin, FillRule};
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_renderer::{
    concurrent::rayon::RayonExecutor,
    concurrent::scene_proxy::SceneProxy,
    options::BuildOptions,
    gpu::{
        renderer::Renderer as PathfinderRenderer,
        options::{DestFramebuffer, RendererOptions},
    },
};

use crate::{Point, Color};

use super::coords::ScreenPoint;
use super::state::{DrawingState, TurtleState};

use display_list::{DisplayList, DrawPrim, Line, Polygon};

/// Converts a color from the representation in this crate to the one used in the renderer
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
fn convert_color(color: Color) -> ColorU {
    let Color {red, green, blue, alpha} = color;
    ColorU {
        r: red.round() as u8,
        g: green.round() as u8,
        b: blue.round() as u8,
        a: (alpha * 255.0).round() as u8,
    }
}

/// A renderer that draws on the current OpenGL context
#[cfg_attr(any(feature = "test", test), allow(dead_code))]
pub struct Renderer {
    renderer: PathfinderRenderer<GLDevice>,
    font_context: CanvasFontContext,
    scene: SceneProxy,
    /// Information about DPI scaling: https://docs.rs/glutin/0.24.0/glutin/dpi/index.html
    dpi_scale: f64,
}

#[cfg_attr(any(feature = "test", test), allow(dead_code))]
impl Renderer {
    /// Creates a new renderer with the given physical size in pixels
    pub fn new(draw_size: PhysicalSize<u32>, dpi_scale: f64) -> Self {
        let renderer = PathfinderRenderer::new(
            GLDevice::new(GLVersion::GL3, 0),
            &EmbeddedResourceLoader::new(),
            DestFramebuffer::full_window(vec2i(draw_size.width as i32, draw_size.height as i32)),
            // This background color will be overwritten during the first render
            RendererOptions {
                background_color: Some(ColorU::white().to_f32()),
                ..RendererOptions::default()
            },
        );

        Self {
            renderer,
            font_context: CanvasFontContext::from_system_source(),
            scene: SceneProxy::new(RayonExecutor),
            dpi_scale,
        }
    }

    /// Updates the scale factor used during rendering
    pub fn scale_factor(&self) -> f64 {
        self.dpi_scale
    }

    /// Updates the scale factor used during rendering
    pub fn set_scale_factor(&mut self, dpi_scale: f64) {
        self.dpi_scale = dpi_scale;
    }

    /// Draw the given primitives onto a canvas of the given size
    ///
    /// Size is passed in to ensure that it is up-to-date
    pub fn render<'a>(
        &mut self,
        draw_size: PhysicalSize<u32>,
        display_list: &DisplayList,
        drawing: &DrawingState,
        turtles: impl Iterator<Item=&'a TurtleState>
    ) {
        // Set the current draw size
        self.renderer.replace_dest_framebuffer(
            DestFramebuffer::full_window(vec2i(draw_size.width as i32, draw_size.height as i32))
        );

        // Clear to background color
        self.renderer.set_options(RendererOptions {
            background_color: Some(convert_color(drawing.background).to_f32()),
            ..RendererOptions::default()
        });

        // The size of the framebuffer
        let fb_size = vec2f(draw_size.width as f32, draw_size.height as f32);
        let mut canvas = Canvas::new(fb_size)
            .get_context_2d(self.font_context.clone());

        // Set default options for all operations
        canvas.set_line_cap(LineCap::Round);
        canvas.set_line_join(LineJoin::Round);

        //TODO: Remove this line once servo/pathfinder#318 is fixed.
        //  Link: https://github.com/servo/pathfinder/issues/318
        // Need to render *something* every time to get pathfinder to even render a background
        canvas.stroke_rect(pathfinder_geometry::rect::RectF::new(vec2f(0.0, 0.0), vec2f(1.0, 1.0)));

        // Draw each primitive
        let dpi_scale = self.dpi_scale;
        let center = drawing.center;
        let fb_center = (fb_size / 2.0).into();
        for prim in display_list.iter() {
            match prim {
                &DrawPrim::Line(Line {start, end, thickness, color}) => {
                    let mut path = Path2D::new();

                    path.move_to(ScreenPoint::from_logical(start, dpi_scale, center, fb_center).into());
                    path.line_to(ScreenPoint::from_logical(end, dpi_scale, center, fb_center).into());

                    canvas.set_line_width((thickness * dpi_scale) as f32);
                    canvas.set_stroke_style(convert_color(color));
                    canvas.stroke_path(path);
                },

                &DrawPrim::Polygon(Polygon {ref points, fill_color}) => {
                    // Skip obviously degenerate polygons
                    if points.len() <= 2 {
                        continue;
                    }

                    let mut path = Path2D::new();

                    path.move_to(ScreenPoint::from_logical(points[0], dpi_scale, center, fb_center).into());
                    for &point in &points[1..] {
                        path.line_to(ScreenPoint::from_logical(point, dpi_scale, center, fb_center).into());
                    }

                    path.close_path();

                    canvas.set_fill_style(convert_color(fill_color));
                    canvas.fill_path(path, FillRule::Winding);
                },
            }
        }

        // The turtle shell specified in logical coordinates relative to the turtle position
        let shell = &[Point {x: 0.0, y: 15.0}, Point {x: 10.0, y: 0.0}, Point {x: 0.0, y: -15.0}];
        for turtle in turtles {
            let &TurtleState {position, heading, is_visible, ..} = turtle;
            if !is_visible {
                continue;
            }

            let Point {x: turtle_x, y: turtle_y} = position;
            let cos = heading.cos();
            let sin = heading.sin();
            let shell_screen_coord = |Point {x, y}| {
                // Rotate each point by the heading and add the current turtle position
                let point = Point {
                    x: cos * x - sin * y + turtle_x,
                    y: sin * x + cos * y + turtle_y,
                };
                ScreenPoint::from_logical(point, dpi_scale, center, fb_center).into()
            };

            let mut path = Path2D::new();
            path.move_to(shell_screen_coord(shell[0]));
            for &point in &shell[1..] {
                path.line_to(shell_screen_coord(point));
            }
            path.close_path();
            canvas.set_fill_style(ColorU::white());
            canvas.fill_path(path.clone(), FillRule::Winding);
            canvas.set_line_width((1.0 * dpi_scale) as f32);
            canvas.set_stroke_style(ColorU::black());
            canvas.stroke_path(path);
        }

        // Build and render scene
        self.scene.replace_scene(canvas.into_canvas().into_scene());
        self.scene.build_and_render(&mut self.renderer, BuildOptions::default());
    }
}
<|endoftext|>


<commit_before>/**
    The layout task. Performs layout on the DOM, builds display lists and sends them to be
    rendered.
*/

use au = gfx::geometry;
use au::au;
use content::content_task;
use core::dvec::DVec;
use css::resolve::apply::apply_style;
use css::values::Stylesheet;
use dl = gfx::display_list;
use dom::event::{Event, ReflowEvent};
use dom::node::{Node, LayoutData};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::render_task;
use gfx::render_layers::RenderLayer;
use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use opt = core::option;
use render_task::RenderTask;
use resource::image_cache_task::ImageCacheTask;
use servo_text::font_cache::FontCache;
use std::arc::ARC;
use std::net::url::Url;

use layout::traverse::*;
use comm::*;
use task::*;

pub type LayoutTask = comm::Chan<Msg>;

pub enum LayoutQuery {
    ContentBox(Node)
}

pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;

enum LayoutQueryResponse_ {
    ContentSize(Size2D<int>)
}

pub enum Msg {
    BuildMsg(Node, ARC<Stylesheet>, Url, comm::Chan<Event>, Size2D<uint>),
    PingMsg(comm::Chan<content_task::PingMsg>),
    QueryMsg(LayoutQuery, comm::Chan<LayoutQueryResponse>),
    ExitMsg
}

fn LayoutTask(render_task: RenderTask,
              img_cache_task: ImageCacheTask) -> LayoutTask {
    do spawn_listener::<Msg> |from_content| {
        Layout(render_task, img_cache_task, from_content).start();
    }
}

struct Layout {
    render_task: RenderTask,
    image_cache_task: ImageCacheTask,
    from_content: comm::Port<Msg>,

    font_cache: @FontCache,
    // This is used to root auxilliary RCU reader data
    layout_refs: DVec<@LayoutData>
}

fn Layout(render_task: RenderTask,
         image_cache_task: ImageCacheTask,
         from_content: comm::Port<Msg>) -> Layout {

    Layout {
        render_task: render_task,
        image_cache_task: image_cache_task,
        from_content: from_content,
        font_cache: FontCache(),
        layout_refs: DVec()
    }
}

impl Layout {

    fn handle_query(query: LayoutQuery,
                    reply_chan: comm::Chan<LayoutQueryResponse>) {
        match query {
            ContentBox(node) => {
                // TODO: extract me to a method when I get sibling arms
                let response = match node.aux(|a| copy *a).flow {
                    None => Err(()),
                    Some(flow) => {
                        let start_val : Option<Rect<au>> = None;
                        let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
                            match acc {
                                Some(acc) => Some(acc.union(&box.content_box())),
                                None => Some(box.content_box())
                            }
                        };

                        match rect {
                            None => Err(()),
                            Some(rect) => {
                                let size = Size2D(au::to_px(rect.size.width),
                                                  au::to_px(rect.size.height));
                                Ok(ContentSize(move size))
                            }
                        }
                    }
                };

                reply_chan.send(response)
            }
        }
    }

    fn start() {
        while self.handle_request(self.from_content) {
            // loop indefinitely
        }
    }

    fn handle_request(request: comm::Port<Msg>) -> bool {
        match request.recv() {
            PingMsg(ping_channel) => ping_channel.send(content_task::PongMsg),
            QueryMsg(query, chan) => self.handle_query(query, chan),
            ExitMsg => {
                debug!("layout: ExitMsg received");
                return false
            },
            BuildMsg(node, styles, doc_url, to_content, window_size) => {
                debug!("layout: received layout request for: %s", doc_url.to_str());
                debug!("layout: parsed Node tree");
                node.dump();

                let screen_size = Size2D(au::from_px(window_size.width as int),
                                         au::from_px(window_size.height as int));

                let layout_ctx = LayoutContext {
                    image_cache: self.image_cache_task,
                    font_cache: self.font_cache,
                    doc_url: doc_url,
                    reflow_cb: || to_content.send(ReflowEvent),
                    screen_size: Rect(Point2D(au(0), au(0)), screen_size)
                };

                do util::time::time(~"layout") {
                    // TODO: this is dumb. we don't need 3 separate traversals.
                    node.initialize_style_for_subtree(&layout_ctx, &self.layout_refs);
                    node.recompute_style_for_subtree(&layout_ctx, &styles);
                    /* resolve styles (convert relative values) down the node tree */
                    apply_style(&layout_ctx, node, copy layout_ctx.reflow_cb);

                    let builder = LayoutTreeBuilder();
                    let layout_root: @FlowContext = match builder.construct_trees(&layout_ctx,
                                                                                node) {
                        Ok(root) => root,
                        Err(*) => fail ~"Root flow should always exist"
                    };

                    debug!("layout: constructed Flow tree");
                    layout_root.dump();

                    /* perform layout passes over the flow tree */
                    do layout_root.traverse_postorder |f| { f.bubble_widths(&layout_ctx) }
                    do layout_root.traverse_preorder  |f| { f.assign_widths(&layout_ctx) }
                    do layout_root.traverse_postorder |f| { f.assign_height(&layout_ctx) }

                    let dlist = DVec();
                    let builder = dl::DisplayListBuilder {
                        ctx: &layout_ctx,
                    };
                    let render_layer = RenderLayer {
                        display_list: move dlist,
                        size: Size2D(au::to_px(screen_size.width) as uint,
                                     au::to_px(screen_size.height) as uint)
                    };

                    // TODO: set options on the builder before building
                    // TODO: be smarter about what needs painting
                    layout_root.build_display_list(&builder, &copy layout_root.d().position,
                                                   &render_layer.display_list);
                    self.render_task.send(render_task::RenderMsg(move render_layer));
                } // time(layout)
            } // BuildMsg
        } // match

        true
    }
}

<commit_msg>layout: Remove some more debug stuff<commit_after>/**
    The layout task. Performs layout on the DOM, builds display lists and sends them to be
    rendered.
*/

use au = gfx::geometry;
use au::au;
use content::content_task;
use core::dvec::DVec;
use css::resolve::apply::apply_style;
use css::values::Stylesheet;
use dl = gfx::display_list;
use dom::event::{Event, ReflowEvent};
use dom::node::{Node, LayoutData};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::render_task;
use gfx::render_layers::RenderLayer;
use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use opt = core::option;
use render_task::RenderTask;
use resource::image_cache_task::ImageCacheTask;
use servo_text::font_cache::FontCache;
use std::arc::ARC;
use std::net::url::Url;

use layout::traverse::*;
use comm::*;
use task::*;

pub type LayoutTask = comm::Chan<Msg>;

pub enum LayoutQuery {
    ContentBox(Node)
}

pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;

enum LayoutQueryResponse_ {
    ContentSize(Size2D<int>)
}

pub enum Msg {
    BuildMsg(Node, ARC<Stylesheet>, Url, comm::Chan<Event>, Size2D<uint>),
    PingMsg(comm::Chan<content_task::PingMsg>),
    QueryMsg(LayoutQuery, comm::Chan<LayoutQueryResponse>),
    ExitMsg
}

fn LayoutTask(render_task: RenderTask,
              img_cache_task: ImageCacheTask) -> LayoutTask {
    do spawn_listener::<Msg> |from_content| {
        Layout(render_task, img_cache_task, from_content).start();
    }
}

struct Layout {
    render_task: RenderTask,
    image_cache_task: ImageCacheTask,
    from_content: comm::Port<Msg>,

    font_cache: @FontCache,
    // This is used to root auxilliary RCU reader data
    layout_refs: DVec<@LayoutData>
}

fn Layout(render_task: RenderTask,
         image_cache_task: ImageCacheTask,
         from_content: comm::Port<Msg>) -> Layout {

    Layout {
        render_task: render_task,
        image_cache_task: image_cache_task,
        from_content: from_content,
        font_cache: FontCache(),
        layout_refs: DVec()
    }
}

impl Layout {

    fn handle_query(query: LayoutQuery,
                    reply_chan: comm::Chan<LayoutQueryResponse>) {
        match query {
            ContentBox(node) => {
                // TODO: extract me to a method when I get sibling arms
                let response = match node.aux(|a| copy *a).flow {
                    None => Err(()),
                    Some(flow) => {
                        let start_val : Option<Rect<au>> = None;
                        let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
                            match acc {
                                Some(acc) => Some(acc.union(&box.content_box())),
                                None => Some(box.content_box())
                            }
                        };

                        match rect {
                            None => Err(()),
                            Some(rect) => {
                                let size = Size2D(au::to_px(rect.size.width),
                                                  au::to_px(rect.size.height));
                                Ok(ContentSize(move size))
                            }
                        }
                    }
                };

                reply_chan.send(response)
            }
        }
    }

    fn start() {
        while self.handle_request(self.from_content) {
            // loop indefinitely
        }
    }

    fn handle_request(request: comm::Port<Msg>) -> bool {
        match request.recv() {
            PingMsg(ping_channel) => ping_channel.send(content_task::PongMsg),
            QueryMsg(query, chan) => self.handle_query(query, chan),
            ExitMsg => {
                debug!("layout: ExitMsg received");
                return false
            },
            BuildMsg(node, styles, doc_url, to_content, window_size) => {
                debug!("layout: received layout request for: %s", doc_url.to_str());
                debug!("layout: parsed Node tree");
                //node.dump();

                let screen_size = Size2D(au::from_px(window_size.width as int),
                                         au::from_px(window_size.height as int));

                let layout_ctx = LayoutContext {
                    image_cache: self.image_cache_task,
                    font_cache: self.font_cache,
                    doc_url: doc_url,
                    reflow_cb: || to_content.send(ReflowEvent),
                    screen_size: Rect(Point2D(au(0), au(0)), screen_size)
                };

                do util::time::time(~"layout") {
                    // TODO: this is dumb. we don't need 3 separate traversals.
                    node.initialize_style_for_subtree(&layout_ctx, &self.layout_refs);
                    node.recompute_style_for_subtree(&layout_ctx, &styles);
                    /* resolve styles (convert relative values) down the node tree */
                    apply_style(&layout_ctx, node, copy layout_ctx.reflow_cb);

                    let builder = LayoutTreeBuilder();
                    let layout_root: @FlowContext = match builder.construct_trees(&layout_ctx,
                                                                                node) {
                        Ok(root) => root,
                        Err(*) => fail ~"Root flow should always exist"
                    };

                    debug!("layout: constructed Flow tree");
                    layout_root.dump();

                    /* perform layout passes over the flow tree */
                    do layout_root.traverse_postorder |f| { f.bubble_widths(&layout_ctx) }
                    do layout_root.traverse_preorder  |f| { f.assign_widths(&layout_ctx) }
                    do layout_root.traverse_postorder |f| { f.assign_height(&layout_ctx) }

                    let dlist = DVec();
                    let builder = dl::DisplayListBuilder {
                        ctx: &layout_ctx,
                    };
                    let render_layer = RenderLayer {
                        display_list: move dlist,
                        size: Size2D(au::to_px(screen_size.width) as uint,
                                     au::to_px(screen_size.height) as uint)
                    };

                    // TODO: set options on the builder before building
                    // TODO: be smarter about what needs painting
                    layout_root.build_display_list(&builder, &copy layout_root.d().position,
                                                   &render_layer.display_list);
                    self.render_task.send(render_task::RenderMsg(move render_layer));
                } // time(layout)
            } // BuildMsg
        } // match

        true
    }
}

<|endoftext|>


<commit_before>use jeebie::core::cpu::CPU;
use jeebie::memory::MMU;
use jeebie::core::registers::Register8::*;
use jeebie::core::registers::Register16::*;
use jeebie::core::registers::Flags::*;

#[test]
fn add_8_bit() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);
    // simple add, 2 + 2
    cpu.set8(A, 2u8);
    cpu.compute_add(A, A);
    assert_eq!(cpu.get8(A), 4u8);
    // no flags
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    // overflow, 255 + 1
    cpu.set8(A, 255u8);
    cpu.set8(B, 1u8);
    cpu.compute_add(A, B);
    assert_eq!(cpu.get8(A), 0u8);
    // all except sub
    assert!(cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 127u8);
    cpu.set8(B, 1u8);
    cpu.compute_add(A, B);
    assert_eq!(cpu.get8(A), 128u8);
    // HC only
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));
}

#[test]
fn sub_8_bit() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);
    // simple sub
    cpu.set8(A, 8u8);
    cpu.set8(B, 2u8);
    cpu.compute_sub(B);
    assert_eq!(cpu.get8(A), 6u8);
    // sub only
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));

    // underflow
    cpu.set8(A, 0u8);
    cpu.set8(B, 1u8);
    cpu.compute_sub(B);
    assert_eq!(cpu.get8(A), 255u8);
    // all flags except zero
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 128u8);
    cpu.set8(B, 2u8);
    cpu.compute_sub(B);
    assert_eq!(cpu.get8(A), 126u8);
    // HC and sub
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));
}

#[test]
fn cp_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    // simple sub
    cpu.set8(A, 10);
    cpu.set8(B, 2);
    cpu.compute_cp(B);

    assert!(cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));

    // underflow
    cpu.set8(A, 100);
    cpu.set8(B, 100);
    cpu.compute_cp(B);

    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));
}

#[test]
fn inc_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    // NOTE: Carry flag is unmodified
    // simple inc
    cpu.set8(A, 0x9);
    cpu.compute_inc(A);

    assert_eq!(0xA, cpu.get8(A));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(HalfCarry));

    // zero
    cpu.set8(A, 0xFF);
    cpu.compute_inc(A);

    assert_eq!(0, cpu.get8(A));
    assert!(cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 0x0F);
    cpu.compute_inc(A);

    assert_eq!(0x10, cpu.get8(A));
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));
}

#[test]
fn bit_check_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(B, 0b1000_0001u8);
    cpu.bit_check(0, B);
    // Sub is always 0, HC always 1
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    cpu.bit_check(1, B);
    // Sub is always 0, HC always 1
    assert!(cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));
}

#[test]
fn swap_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(B, 0xAB);
    cpu.compute_swap(B);

    assert_eq!(0xBA, cpu.get8(B));
    // all reset except zero
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));

    // internal RAM, will be initialized to 0
    cpu.set16(HL, 0xC000);
    cpu.compute_swap(RegisterAddress(HL));

    assert_eq!(0, cpu.get8(RegisterAddress(HL)));
    // Zero should be set
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));
}<commit_msg>Added more cpu tests<commit_after>use jeebie::core::cpu::CPU;
use jeebie::memory::MMU;
use jeebie::core::registers::Register8::*;
use jeebie::core::registers::Register16::*;
use jeebie::core::registers::Flags::*;

#[test]
fn add_8_bit() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);
    // simple add, 2 + 2
    cpu.set8(A, 2u8);
    cpu.compute_add(A, A);
    assert_eq!(cpu.get8(A), 4u8);
    // no flags
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    // overflow, 255 + 1
    cpu.set8(A, 255u8);
    cpu.set8(B, 1u8);
    cpu.compute_add(A, B);
    assert_eq!(cpu.get8(A), 0u8);
    // all except sub
    assert!(cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 127u8);
    cpu.set8(B, 1u8);
    cpu.compute_add(A, B);
    assert_eq!(cpu.get8(A), 128u8);
    // HC only
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));
}

#[test]
fn sub_8_bit() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);
    // simple sub
    cpu.set8(A, 8u8);
    cpu.set8(B, 2u8);
    cpu.compute_sub(B);
    assert_eq!(cpu.get8(A), 6u8);
    // sub only
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));

    // underflow
    cpu.set8(A, 0u8);
    cpu.set8(B, 1u8);
    cpu.compute_sub(B);
    assert_eq!(cpu.get8(A), 255u8);
    // all flags except zero
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 128u8);
    cpu.set8(B, 2u8);
    cpu.compute_sub(B);
    assert_eq!(cpu.get8(A), 126u8);
    // HC and sub
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));
}

#[test]
fn cp_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    // simple sub
    cpu.set8(A, 10);
    cpu.set8(B, 2);
    cpu.compute_cp(B);

    assert!(cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));

    // underflow
    cpu.set8(A, 100);
    cpu.set8(B, 100);
    cpu.compute_cp(B);

    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Carry));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));
}

#[test]
fn inc_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    // NOTE: Carry flag is unmodified
    // simple inc
    cpu.set8(A, 0x9);
    cpu.compute_inc(A);

    assert_eq!(0xA, cpu.get8(A));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(HalfCarry));

    // zero
    cpu.set8(A, 0xFF);
    cpu.compute_inc(A);

    assert_eq!(0, cpu.get8(A));
    assert!(cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 0x0F);
    cpu.compute_inc(A);

    assert_eq!(0x10, cpu.get8(A));
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));
}

#[test]
fn dec_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    // NOTE: Carry flag is unmodified
    // simple dec
    cpu.set8(A, 0x9);
    cpu.compute_dec(A);

    assert_eq!(0x8, cpu.get8(A));
    assert!(cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(HalfCarry));

    // zero
    cpu.set8(A, 1);
    cpu.compute_dec(A);

    assert_eq!(0, cpu.get8(A));
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));

    // halfcarry
    cpu.set8(A, 0x10);
    cpu.compute_dec(A);

    assert_eq!(0x0F, cpu.get8(A));
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(cpu.reg.is_set(Sub));

    // internal RAM, will be initialized to 0
    cpu.set16(HL, 0xC000);
    cpu.compute_dec(RegisterAddress(HL));

    assert_eq!(0xFF, cpu.get8(RegisterAddress(HL)));
    // HC should be set
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(Sub));
    assert!(cpu.reg.is_set(HalfCarry));
}

#[test]
fn bit_check_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(B, 0b1000_0001u8);
    cpu.bit_check(0, B);
    // Sub is always 0, HC always 1
    assert!(!cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));

    cpu.bit_check(1, B);
    // Sub is always 0, HC always 1
    assert!(cpu.reg.is_set(Zero));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Sub));
}

#[test]
fn swap_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(B, 0xAB);
    cpu.compute_swap(B);

    assert_eq!(0xBA, cpu.get8(B));
    // all reset except zero
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));

    // internal RAM, will be initialized to 0
    cpu.set16(HL, 0xC000);
    cpu.compute_swap(RegisterAddress(HL));

    assert_eq!(0, cpu.get8(RegisterAddress(HL)));
    // Zero should be set
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));
}

#[test]
fn and_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(A, 0x0F);
    cpu.set8(B, 0xAB);
    cpu.compute_and(B);

    assert_eq!(0x0B, cpu.get8(A));
    // Z 0 1 0
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));

    // internal RAM, will be initialized to 0
    cpu.set16(HL, 0xC000);
    cpu.compute_and(RegisterAddress(HL));

    assert_eq!(0, cpu.get8(A));
    // Zero should be set
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));
}

#[test]
fn or_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(A, 0x0B);
    cpu.set8(B, 0xA0);
    cpu.compute_or(B);

    assert_eq!(0xAB, cpu.get8(A));
    // Z 0 0 0
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));

    // internal RAM, will be initialized to 0
    cpu.set8(A, 0);
    cpu.set16(HL, 0xC000);
    cpu.compute_or(RegisterAddress(HL));

    assert_eq!(0, cpu.get8(A));
    // Zero should be set
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));
}

#[test]
fn xor_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set8(A, 0x21);
    cpu.set8(B, 0x41);
    cpu.compute_xor(B);

    assert_eq!(0x60, cpu.get8(A));
    // Z 0 0 0
    assert!(!cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));

    cpu.set8(A, 0xFF);
    cpu.set8(B, 0xFF);
    cpu.compute_xor(B);

    assert_eq!(0, cpu.get8(A));
    // Z 0 0 0
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));

    // internal RAM, will be initialized to 0
    cpu.set8(A, 0);
    cpu.set16(HL, 0xC000);
    cpu.compute_xor(RegisterAddress(HL));

    assert_eq!(0, cpu.get8(A));
    // Zero should be set
    assert!(cpu.reg.is_set(Zero));
    assert!(!cpu.reg.is_set(Sub));
    assert!(!cpu.reg.is_set(HalfCarry));
    assert!(!cpu.reg.is_set(Carry));
}

#[test]
fn cpu_stack_test() {
    let mut mmu = MMU::new();
    let mut cpu = CPU::new(&mut mmu);

    cpu.set16(SP, 0xFFFE);
    cpu.push_stack(Value16(0xCAFE));
    cpu.push_stack(Value16(0xBABE));
    cpu.push_stack(Value16(0xFADE));

    assert_eq!(0xFFF8, cpu.get16(SP));

    cpu.pop_stack(HL);
    assert_eq!(0xFFFA, cpu.get16(SP));
    assert_eq!(0xFADE, cpu.get16(HL));

    cpu.pop_stack(DE);
    assert_eq!(0xFFFC, cpu.get16(SP));
    assert_eq!(0xBABE, cpu.get16(DE));

    cpu.pop_stack(BC);
    assert_eq!(0xFFFE, cpu.get16(SP));
    assert_eq!(0xCAFE, cpu.get16(BC));
}<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Jordi Boggiano <j.boggiano@seld.be>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

/* last synced with: printenv (GNU coreutils) 8.13 */

use clap::{crate_version, App, Arg};
use std::env;
use uucore::error::UResult;

static ABOUT: &str = "Display the values of the specified environment VARIABLE(s), or (with no VARIABLE) display name and value pairs for them all.";

static OPT_NULL: &str = "null";

static ARG_VARIABLES: &str = "variables";

fn usage() -> String {
    format!("{0} [VARIABLE]... [OPTION]...", uucore::execution_phrase())
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let variables: Vec<String> = matches
        .values_of(ARG_VARIABLES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let separator = if matches.is_present(OPT_NULL) {
        "\x00"
    } else {
        "\n"
    };

    if variables.is_empty() {
        for (env_var, value) in env::vars() {
            print!("{}={}{}", env_var, value, separator);
        }
        return Ok(());
    }

    for env_var in variables {
        if let Ok(var) = env::var(env_var) {
            print!("{}{}", var, separator);
        }
    }

    Ok(())
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(OPT_NULL)
                .short("0")
                .long(OPT_NULL)
                .help("end each output line with 0 byte rather than newline"),
        )
        .arg(
            Arg::with_name(ARG_VARIABLES)
                .multiple(true)
                .takes_value(true)
                .min_values(1),
        )
}
<commit_msg>printenv: change exit code when variable not found<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Jordi Boggiano <j.boggiano@seld.be>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

/* last synced with: printenv (GNU coreutils) 8.13 */

use clap::{crate_version, App, Arg};
use std::env;
use uucore::error::UResult;

static ABOUT: &str = "Display the values of the specified environment VARIABLE(s), or (with no VARIABLE) display name and value pairs for them all.";

static OPT_NULL: &str = "null";

static ARG_VARIABLES: &str = "variables";

fn usage() -> String {
    format!("{0} [VARIABLE]... [OPTION]...", uucore::execution_phrase())
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let variables: Vec<String> = matches
        .values_of(ARG_VARIABLES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let separator = if matches.is_present(OPT_NULL) {
        "\x00"
    } else {
        "\n"
    };

    if variables.is_empty() {
        for (env_var, value) in env::vars() {
            print!("{}={}{}", env_var, value, separator);
        }
        return Ok(());
    }

    let mut not_found = false;
    for env_var in variables {
        if let Ok(var) = env::var(env_var) {
            print!("{}{}", var, separator);
        } else {
            not_found = true;
        }
    }

    if not_found {
        Err(1.into())
    } else {
        Ok(())
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(OPT_NULL)
                .short("0")
                .long(OPT_NULL)
                .help("end each output line with 0 byte rather than newline"),
        )
        .arg(
            Arg::with_name(ARG_VARIABLES)
                .multiple(true)
                .takes_value(true)
                .min_values(1),
        )
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Prover task runner that runs multiple instances of the prover task and returns
//! as soon as the fastest instance finishes.

use crate::cli::Options;
use async_trait::async_trait;
use futures::{future::FutureExt, pin_mut, select};
use rand::Rng;
use std::{
    process::Output,
    sync::mpsc::{channel, Sender},
};
use tokio::{
    process::Command,
    runtime::Runtime,
    sync::{broadcast, broadcast::Receiver},
};

#[derive(Debug, Clone)]
enum BroadcastMsg {
    Stop,
}

#[async_trait]
pub trait ProverTask {
    type TaskResult: Send + 'static;
    type TaskId: Send + Copy + 'static;

    /// Initialize the task runner given the number of instances.
    fn init(&mut self, num_instances: usize) -> Vec<Self::TaskId>;

    /// Run the task with task_id. This function will be called from one of the worker threads.
    async fn run(&mut self, task_id: Self::TaskId) -> Self::TaskResult;
}

pub struct ProverTaskRunner();

impl ProverTaskRunner {
    /// Run `num_instances` instances of the prover `task` and returns the task id
    /// as well as the result of the fastest running instance.
    pub fn run_tasks<T>(mut task: T, num_instances: usize) -> (T::TaskId, T::TaskResult)
    where
        T: ProverTask + Clone + Send + 'static,
    {
        let rt = Runtime::new().unwrap();

        // Create channels for communication.
        let (worker_tx, master_rx) = channel();
        let (master_tx, _): (
            tokio::sync::broadcast::Sender<BroadcastMsg>,
            Receiver<BroadcastMsg>,
        ) = broadcast::channel(num_instances);

        // Initialize the prover tasks.
        let task_ids = task.init(num_instances);
        for task_id in task_ids {
            let send_n = worker_tx.clone();
            let worker_rx = master_tx.subscribe();
            let cloned_task = task.clone();
            // Spawn a task worker for each task_id.
            rt.spawn(async move {
                Self::run_task_until_cancelled(cloned_task, task_id, send_n, worker_rx).await;
            });
        }
        // Listens until one of the workers finishes.
        loop {
            let res = master_rx.recv();
            if let Ok((task_id, result)) = res {
                // Result received from one worker. Broadcast to other workers
                // so they can stop working.
                if num_instances > 1 {
                    let _ = master_tx.send(BroadcastMsg::Stop);
                }
                return (task_id, result);
            }
        }
    }

    // Run two async tasks, listening on broadcast channel and running boogie, until
    // either boogie finishes running, or a stop message is received.
    async fn run_task_until_cancelled<T>(
        mut task: T,
        task_id: T::TaskId,
        tx: Sender<(T::TaskId, T::TaskResult)>,
        rx: Receiver<BroadcastMsg>,
    ) where
        T: ProverTask,
    {
        let boogie_fut = task.run(task_id).fuse();
        let watchdog_fut = Self::watchdog(rx).fuse();
        pin_mut!(boogie_fut, watchdog_fut);
        select! {
            _ = watchdog_fut => {
                // A stop message is received.
            }
            res = boogie_fut => {
                // Boogie finishes running, send the result to parent thread.
                let _ = tx.send((task_id, res));
            },
        }
    }

    /// Waits for a stop message from the parent thread.
    async fn watchdog(mut rx: Receiver<BroadcastMsg>) {
        let _ = rx.recv().await;
    }
}

#[derive(Debug, Clone)]
pub struct RunBoogieWithSeeds {
    pub options: Options,
    pub boogie_file: String,
}

#[async_trait]
impl ProverTask for RunBoogieWithSeeds {
    type TaskResult = Output;
    type TaskId = usize;

    fn init(&mut self, num_instances: usize) -> Vec<Self::TaskId> {
        // If we are running only one Boogie instance, use the default random seed.
        if num_instances == 1 {
            return vec![self.options.backend.random_seed];
        }
        let mut rng = rand::thread_rng();
        // Otherwise generate a list of random numbers to use as seeds.
        (0..num_instances)
            .map(|_| rng.gen::<u8>() as usize)
            .collect()
    }

    async fn run(&mut self, task_id: Self::TaskId) -> Self::TaskResult {
        let args = self.get_boogie_command(task_id);
        Command::new(&args[0])
            .args(&args[1..])
            .kill_on_drop(true)
            .output()
            .await
            .unwrap()
    }
}

impl RunBoogieWithSeeds {
    /// Returns command line to call boogie.
    pub fn get_boogie_command(&mut self, seed: usize) -> Vec<String> {
        self.options
            .backend
            .boogie_flags
            .push(format!("-proverOpt:O:smt.random_seed={}", seed));
        self.options.get_boogie_command(&self.boogie_file)
    }
}
<commit_msg>[move prover] limit concurrency<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Prover task runner that runs multiple instances of the prover task and returns
//! as soon as the fastest instance finishes.

use crate::cli::Options;
use async_trait::async_trait;
use futures::{future::FutureExt, pin_mut, select};
use rand::Rng;
use std::{
    process::Output,
    sync::Arc,
    sync::mpsc::{channel, Sender},
};
use tokio::{
    process::Command,
    runtime::Runtime,
    sync::{broadcast, broadcast::Receiver, Semaphore},
};

#[derive(Debug, Clone)]
enum BroadcastMsg {
    Stop,
}

#[async_trait]
pub trait ProverTask {
    type TaskResult: Send + 'static;
    type TaskId: Send + Copy + 'static;

    /// Initialize the task runner given the number of instances.
    fn init(&mut self, num_instances: usize) -> Vec<Self::TaskId>;

    /// Run the task with task_id. This function will be called from one of the worker threads.
    async fn run(&mut self, task_id: Self::TaskId, sem: Arc<Semaphore>) -> Self::TaskResult;
}

pub struct ProverTaskRunner();

impl ProverTaskRunner {
    /// Run `num_instances` instances of the prover `task` and returns the task id
    /// as well as the result of the fastest running instance.
    pub fn run_tasks<T>(mut task: T, num_instances: usize) -> (T::TaskId, T::TaskResult)
    where
        T: ProverTask + Clone + Send + 'static,
    {
        // let rt = Runtime::new().unwrap();
        let mut rt = tokio::runtime::Builder::new()
            .core_threads(1)
            .threaded_scheduler()
            .enable_all()
            .build()
            .unwrap();
        let sem = Arc::new(Semaphore::new(3));
        // Create channels for communication.
        let (worker_tx, master_rx) = channel();
        let (master_tx, _): (
            tokio::sync::broadcast::Sender<BroadcastMsg>,
            Receiver<BroadcastMsg>,
        ) = broadcast::channel(num_instances);

        // Initialize the prover tasks.
        let task_ids = task.init(num_instances);
        for task_id in task_ids {
            let s = sem.clone();
            let send_n = worker_tx.clone();
            let worker_rx = master_tx.subscribe();
            let cloned_task = task.clone();
            // Spawn a task worker for each task_id.
            println!("spawning tasks now");
            rt.spawn(async move {
                println!("task spawned");
                Self::run_task_until_cancelled(cloned_task, task_id, send_n, worker_rx, s).await;
            });
        }
        // Listens until one of the workers finishes.
        loop {
            let res = master_rx.recv();
            if let Ok((task_id, result)) = res {
                // Result received from one worker. Broadcast to other workers
                // so they can stop working.
                if num_instances > 1 {
                    let _ = master_tx.send(BroadcastMsg::Stop);
                }
                return (task_id, result);
            }
        }
    }

    // Run two async tasks, listening on broadcast channel and running boogie, until
    // either boogie finishes running, or a stop message is received.
    async fn run_task_until_cancelled<T>(
        mut task: T,
        task_id: T::TaskId,
        tx: Sender<(T::TaskId, T::TaskResult)>,
        rx: Receiver<BroadcastMsg>,
        sem: Arc<Semaphore>,
    ) where
        T: ProverTask,
    {
        let boogie_fut = task.run(task_id, sem).fuse();
        let watchdog_fut = Self::watchdog(rx).fuse();
        pin_mut!(boogie_fut, watchdog_fut);
        select! {
            _ = watchdog_fut => {
                // A stop message is received.
            }
            res = boogie_fut => {
                // Boogie finishes running, send the result to parent thread.
                let _ = tx.send((task_id, res));
            },
        }
    }

    /// Waits for a stop message from the parent thread.
    async fn watchdog(mut rx: Receiver<BroadcastMsg>) {
        let _ = rx.recv().await;
    }
}

#[derive(Debug, Clone)]
pub struct RunBoogieWithSeeds {
    pub options: Options,
    pub boogie_file: String,
}

#[async_trait]
impl ProverTask for RunBoogieWithSeeds {
    type TaskResult = Output;
    type TaskId = usize;

    fn init(&mut self, num_instances: usize) -> Vec<Self::TaskId> {
        // If we are running only one Boogie instance, use the default random seed.
        if num_instances == 1 {
            return vec![self.options.backend.random_seed];
        }
        let mut rng = rand::thread_rng();
        // Otherwise generate a list of random numbers to use as seeds.
        (0..num_instances)
            .map(|_| rng.gen::<u8>() as usize)
            .collect()
    }

    async fn run(&mut self, task_id: Self::TaskId, sem: Arc<Semaphore>) -> Self::TaskResult {
        let _guard = sem.acquire().await;
        let args = self.get_boogie_command(task_id);
        println!("ruunning command with seed {}", task_id);
        Command::new(&args[0])
            .args(&args[1..])
            .kill_on_drop(true)
            .output()
            .await
            .unwrap()
    }
}

impl RunBoogieWithSeeds {
    /// Returns command line to call boogie.
    pub fn get_boogie_command(&mut self, seed: usize) -> Vec<String> {
        self.options
            .backend
            .boogie_flags
            .push(format!("-proverOpt:O:smt.random_seed={}", seed));
        self.options.get_boogie_command(&self.boogie_file)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Cartographer Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::opengl;
use cgmath::{
    Decomposed, Deg, InnerSpace, Matrix4, One, PerspectiveFov, Quaternion, Rad, Rotation,
    Rotation3, Transform, Vector3, Zero,
};
use serde_derive::{Deserialize, Serialize};
use std::f32;
use time;

#[derive(Debug)]
pub struct Camera {
    pub moving_backward: bool,
    pub moving_forward: bool,
    pub moving_left: bool,
    pub moving_right: bool,
    pub moving_down: bool,
    pub moving_up: bool,
    pub turning_left: bool,
    pub turning_right: bool,
    pub turning_down: bool,
    pub turning_up: bool,
    pub width: i32,
    pub height: i32,

    movement_speed: f32,
    theta: Rad<f32>,
    phi: Rad<f32>,
    pan: Vector3<f32>,
    delta_theta: Rad<f32>,
    delta_phi: Rad<f32>,

    moved: bool,
    transform: Decomposed<Vector3<f32>, Quaternion<f32>>,

    projection_matrix: Matrix4<f32>,
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub struct State {
    transform: Decomposed<Vector3<f32>, Quaternion<f32>>,
    phi: Rad<f32>,
    theta: Rad<f32>,
}

impl Camera {
    pub fn new(gl: &opengl::Gl, width: i32, height: i32) -> Self {
        let mut camera = Camera {
            movement_speed: 10.,
            moving_backward: false,
            moving_forward: false,
            moving_left: false,
            moving_right: false,
            moving_down: false,
            moving_up: false,
            turning_left: false,
            turning_right: false,
            turning_down: false,
            turning_up: false,
            moved: true,
            theta: Rad::zero(),
            phi: Rad::zero(),
            pan: Vector3::zero(),
            delta_theta: Rad::zero(),
            delta_phi: Rad::zero(),
            transform: Decomposed {
                scale: 1.,
                rot: Quaternion::one(),
                disp: Vector3::new(0., 0., 150.),
            },

            // These will be set by set_size().
            projection_matrix: One::one(),
            width: 0,
            height: 0,
        };
        camera.set_size(gl, width, height);
        camera
    }

    pub fn state(&self) -> State {
        State {
            transform: self.transform,
            phi: self.phi,
            theta: self.theta,
        }
    }

    pub fn set_state(&mut self, state: State) {
        self.transform = state.transform;
        self.phi = state.phi;
        self.theta = state.theta;
        self.moved = true;
    }

    pub fn set_size(&mut self, gl: &opengl::Gl, width: i32, height: i32) {
        self.width = width;
        self.height = height;
        self.projection_matrix = Matrix4::from(PerspectiveFov {
            fovy: Rad::from(Deg(45.)),
            aspect: width as f32 / height as f32,
            near: 0.1,
            far: 10000.0,
        });
        unsafe {
            gl.Viewport(0, 0, width, height);
        }
        self.moved = true;
    }

    pub fn get_world_to_gl(&self) -> Matrix4<f32> {
        let world_to_camera: Matrix4<f32> = self.transform.inverse_transform().unwrap().into();
        self.projection_matrix * world_to_camera
    }

    /// Update the camera position for the current frame. Returns true if the camera moved in this
    /// step.
    pub fn update(&mut self, elapsed: time::Duration) -> bool {
        let mut moved = self.moved;
        self.moved = false;

        // Handle keyboard input
        let mut pan = Vector3::zero();
        if self.moving_right {
            pan.x += 1.;
        }
        if self.moving_left {
            pan.x -= 1.;
        }
        if self.moving_backward {
            pan.z += 1.;
        }
        if self.moving_forward {
            pan.z -= 1.;
        }
        if self.moving_up {
            pan.y += 1.;
        }
        if self.moving_down {
            pan.y -= 1.;
        }
        if pan.magnitude2() > 0. {
            self.pan += pan.normalize();
        }

        let elapsed_seconds = elapsed.num_milliseconds() as f32 / 1000.;

        const TURNING_SPEED: Rad<f32> = Rad(0.15);
        if self.turning_left {
            self.delta_theta += TURNING_SPEED;
        }
        if self.turning_right {
            self.delta_theta -= TURNING_SPEED;
        }
        if self.turning_up {
            self.delta_phi += TURNING_SPEED;
        }
        if self.turning_down {
            self.delta_phi -= TURNING_SPEED;
        }

        // Apply changes
        if self.pan.magnitude2() > 0. {
            moved = true;
            let translation = self
                .transform
                .rot
                .rotate_vector(self.pan * self.movement_speed * elapsed_seconds);
            self.transform.disp += translation;
        }

        if !self.delta_theta.is_zero() || !self.delta_phi.is_zero() {
            moved = true;
            self.theta += self.delta_theta * elapsed_seconds;
            self.phi += self.delta_phi * elapsed_seconds;
            let rotation_z = Quaternion::from_angle_z(self.theta);
            let rotation_x = Quaternion::from_angle_x(self.phi);
            self.transform.rot = rotation_z * rotation_x;
        }

        self.pan = Vector3::zero();
        self.delta_theta = Rad::zero();
        self.delta_phi = Rad::zero();
        moved
    }

    pub fn mouse_drag_pan(&mut self, delta_x: i32, delta_y: i32) {
        self.pan.x -= 100. * delta_x as f32 / self.width as f32;
        self.pan.y += 100. * delta_y as f32 / self.height as f32;
    }

    pub fn mouse_drag_rotate(&mut self, delta_x: i32, delta_y: i32) {
        self.delta_theta -= Rad(2. * f32::consts::PI * delta_x as f32 / self.width as f32);
        self.delta_phi -= Rad(2. * f32::consts::PI * delta_y as f32 / self.height as f32);
    }

    pub fn mouse_wheel(&mut self, delta: i32) {
        let sign = delta.signum() as f32;
        self.movement_speed += sign * 0.1 * self.movement_speed;
        self.movement_speed = self.movement_speed.max(0.01);
    }

    pub fn pan(&mut self, x: f32, y: f32, z: f32) {
        self.pan.x += x;
        self.pan.y += y;
        self.pan.z += z;
    }

    pub fn rotate(&mut self, up: f32, around: f32) {
        self.delta_phi += Rad(up);
        self.delta_theta += Rad(around);
    }
}
<commit_msg>Fix rotating with the mouse. (#208)<commit_after>// Copyright 2016 The Cartographer Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::opengl;
use cgmath::{
    Decomposed, Deg, InnerSpace, Matrix4, One, PerspectiveFov, Quaternion, Rad, Rotation,
    Rotation3, Transform, Vector3, Zero,
};
use serde_derive::{Deserialize, Serialize};
use std::f32;
use time;

#[derive(Debug)]
struct RotationAngle {
    theta: Rad<f32>,
    phi: Rad<f32>,
}

impl RotationAngle {
    pub fn zero() -> Self {
        RotationAngle {
            theta: Rad::zero(),
            phi: Rad::zero(),
        }
    }
}

#[derive(Debug)]
pub struct Camera {
    pub moving_backward: bool,
    pub moving_forward: bool,
    pub moving_left: bool,
    pub moving_right: bool,
    pub moving_down: bool,
    pub moving_up: bool,
    pub turning_left: bool,
    pub turning_right: bool,
    pub turning_down: bool,
    pub turning_up: bool,
    pub width: i32,
    pub height: i32,

    movement_speed: f32,
    theta: Rad<f32>,
    phi: Rad<f32>,
    pan: Vector3<f32>,

    // The speed we currently want to rotate at. This is multiplied with the seconds since the last
    // frame to get to an absolute rotation.
    rotation_speed: RotationAngle,

    // An absolute value that we should rotate around. This is used when the user is clicking and
    // dragging with the mouse, at which point we want to follow the mouse and ignore rotation
    // speed from the Joystick.
    delta_rotation: RotationAngle,

    moved: bool,
    transform: Decomposed<Vector3<f32>, Quaternion<f32>>,

    projection_matrix: Matrix4<f32>,
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub struct State {
    transform: Decomposed<Vector3<f32>, Quaternion<f32>>,
    phi: Rad<f32>,
    theta: Rad<f32>,
}

impl Camera {
    pub fn new(gl: &opengl::Gl, width: i32, height: i32) -> Self {
        let mut camera = Camera {
            movement_speed: 10.,
            moving_backward: false,
            moving_forward: false,
            moving_left: false,
            moving_right: false,
            moving_down: false,
            moving_up: false,
            turning_left: false,
            turning_right: false,
            turning_down: false,
            turning_up: false,
            moved: true,
            theta: Rad::zero(),
            phi: Rad::zero(),
            pan: Vector3::zero(),
            rotation_speed: RotationAngle::zero(),
            delta_rotation: RotationAngle::zero(),
            transform: Decomposed {
                scale: 1.,
                rot: Quaternion::one(),
                disp: Vector3::new(0., 0., 150.),
            },

            // These will be set by set_size().
            projection_matrix: One::one(),
            width: 0,
            height: 0,
        };
        camera.set_size(gl, width, height);
        camera
    }

    pub fn state(&self) -> State {
        State {
            transform: self.transform,
            phi: self.phi,
            theta: self.theta,
        }
    }

    pub fn set_state(&mut self, state: State) {
        self.transform = state.transform;
        self.phi = state.phi;
        self.theta = state.theta;
        self.moved = true;
    }

    pub fn set_size(&mut self, gl: &opengl::Gl, width: i32, height: i32) {
        self.width = width;
        self.height = height;
        self.projection_matrix = Matrix4::from(PerspectiveFov {
            fovy: Rad::from(Deg(45.)),
            aspect: width as f32 / height as f32,
            near: 0.1,
            far: 10000.0,
        });
        unsafe {
            gl.Viewport(0, 0, width, height);
        }
        self.moved = true;
    }

    pub fn get_world_to_gl(&self) -> Matrix4<f32> {
        let world_to_camera: Matrix4<f32> = self.transform.inverse_transform().unwrap().into();
        self.projection_matrix * world_to_camera
    }

    /// Update the camera position for the current frame. Returns true if the camera moved in this
    /// step.
    pub fn update(&mut self, elapsed: time::Duration) -> bool {
        let mut moved = self.moved;
        self.moved = false;

        // Handle keyboard input
        let mut pan = Vector3::zero();
        if self.moving_right {
            pan.x += 1.;
        }
        if self.moving_left {
            pan.x -= 1.;
        }
        if self.moving_backward {
            pan.z += 1.;
        }
        if self.moving_forward {
            pan.z -= 1.;
        }
        if self.moving_up {
            pan.y += 1.;
        }
        if self.moving_down {
            pan.y -= 1.;
        }
        if pan.magnitude2() > 0. {
            self.pan += pan.normalize();
        }

        let elapsed_seconds = elapsed.num_milliseconds() as f32 / 1000.;

        const TURNING_SPEED: Rad<f32> = Rad(0.15);
        if self.turning_left {
            self.rotation_speed.theta += TURNING_SPEED;
        }
        if self.turning_right {
            self.rotation_speed.theta -= TURNING_SPEED;
        }
        if self.turning_up {
            self.rotation_speed.phi += TURNING_SPEED;
        }
        if self.turning_down {
            self.rotation_speed.phi -= TURNING_SPEED;
        }

        // Apply changes
        if self.pan.magnitude2() > 0. {
            moved = true;
            let translation = self
                .transform
                .rot
                .rotate_vector(self.pan * self.movement_speed * elapsed_seconds);
            self.transform.disp += translation;
        }

        if !self.rotation_speed.theta.is_zero()
            || !self.rotation_speed.phi.is_zero()
            || !self.delta_rotation.theta.is_zero()
            || !self.delta_rotation.phi.is_zero()
        {
            moved = true;
            if !self.delta_rotation.theta.is_zero() || !self.delta_rotation.phi.is_zero() {
                self.theta += self.delta_rotation.theta;
                self.phi += self.delta_rotation.phi;
            } else {
                self.theta += self.rotation_speed.theta * elapsed_seconds;
                self.phi += self.rotation_speed.phi * elapsed_seconds;
            }
            let rotation_z = Quaternion::from_angle_z(self.theta);
            let rotation_x = Quaternion::from_angle_x(self.phi);
            self.transform.rot = rotation_z * rotation_x;
        }

        self.pan = Vector3::zero();
        self.rotation_speed.theta = Rad::zero();
        self.rotation_speed.phi = Rad::zero();
        self.delta_rotation.theta = Rad::zero();
        self.delta_rotation.phi = Rad::zero();
        moved
    }

    pub fn mouse_drag_pan(&mut self, delta_x: i32, delta_y: i32) {
        self.pan.x -= 100. * delta_x as f32 / self.width as f32;
        self.pan.y += 100. * delta_y as f32 / self.height as f32;
    }

    pub fn mouse_drag_rotate(&mut self, delta_x: i32, delta_y: i32) {
        self.delta_rotation.theta -= Rad(2. * f32::consts::PI * delta_x as f32 / self.width as f32);
        self.delta_rotation.phi -= Rad(2. * f32::consts::PI * delta_y as f32 / self.height as f32);
    }

    pub fn mouse_wheel(&mut self, delta: i32) {
        let sign = delta.signum() as f32;
        self.movement_speed += sign * 0.1 * self.movement_speed;
        self.movement_speed = self.movement_speed.max(0.01);
    }

    pub fn pan(&mut self, x: f32, y: f32, z: f32) {
        self.pan.x += x;
        self.pan.y += y;
        self.pan.z += z;
    }

    pub fn rotate(&mut self, up: f32, around: f32) {
        self.rotation_speed.phi += Rad(up);
        self.rotation_speed.theta += Rad(around);
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::f32::consts::{
  PI,
  FRAC_PI_2,
};
use std::num::{
  sqrt,
  pow,
  sin,
  cos,
};
use glfw;
use gl;
use std;
use gl::types::{
  GLfloat,
  GLint,
  GLuint,
};
use cgmath::matrix::Matrix;
use cgmath::vector::{
  Vec3,
  Vec2,
  Vector,
};
use glh = gl_helpers;
use camera::Camera;
use glfw_events::EventHandlers;
use map::TileIterator;
use color::Color3;

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

static PICK_VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  in vec3 color;
  out vec3 pass_color;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
    pass_color = color;
  }
";

static PICK_FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  in vec3 pass_color;
  out vec4 out_color;
  void main() {
    out_color = vec4(pass_color, 1.0);
  }
";

struct TilePicker {
  program: GLuint,
  color_buffer_obj: GLuint,
  mat_id: GLint,
  vertex_buffer_obj: GLuint,
  vertex_data: ~[Vec3<GLfloat>],
  color_data: ~[Color3],
}

impl TilePicker {
  fn new(geom: &Geom) -> TilePicker {
    let mut picker = TilePicker {
      color_buffer_obj: 0,
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      vertex_data: ~[],
      color_data: ~[],
    };
    picker.init(geom);
    picker
  }

  fn init_opengl(&mut self) {
    self.program = glh::compile_program(
      PICK_VERTEX_SHADER_SRC,
      PICK_FRAGMENT_SHADER_SRC);
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteBuffers(1, &self.color_buffer_obj);
    }
  }

  fn build_hex_mesh_for_picking(&mut self, geom: &Geom) {
    for tile_pos in TileIterator::new() {
      let pos3d = geom.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = geom.index_to_hex_vertex(num);
        let next_vertex = geom.index_to_hex_vertex(num + 1);
        let col_x = tile_pos.x as f32 / 255.0;
        let col_y = tile_pos.y as f32 / 255.0;
        let c_data = &mut self.color_data;
        let v_data = &mut self.vertex_data;
        let color = Color3{r: col_x, g: col_y, b: 1.0};
        v_data.push(pos3d + vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + next_vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + Vec3::zero());
        c_data.push(color);
      }
    }
  }

  fn init(&mut self, geom: &Geom) {
    self.build_hex_mesh_for_picking(geom);
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      gl::GenBuffers(1, &mut self.color_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer_obj);
      glh::fill_current_color_vbo(self.color_data);
      let color_attr = glh::get_attr(self.program, "color");
      gl::EnableVertexAttribArray(color_attr);
      glh::define_array_of_generic_attr_data(color_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn _pick_tile(
    &self,
    win_size: (i32, i32),
    mouse_pos: Vec2<i32>
  ) -> Option<Vec2<i32>> {
    let (_, height) = win_size;
    let reverted_y = height - mouse_pos.y;
    let data: [u8, ..4] = [0, 0, 0, 0]; // mut
    unsafe {
      let data_ptr = std::cast::transmute(&data[0]);
      gl::ReadPixels(
        mouse_pos.x, reverted_y, 1, 1,
        gl::RGBA,
        gl::UNSIGNED_BYTE,
        data_ptr
      );
    }
    if data[2] != 0 {
      Some(Vec2{x: data[0] as i32, y: data[1] as i32})
    } else {
      None
    }
  }

  pub fn pick_tile(
    &mut self,
    win_size: (i32, i32),
    camera: &Camera,
    mouse_pos: Vec2<i32>
  ) -> Option<Vec2<i32>> {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &camera.mat());
    gl::ClearColor(0.0, 0.0, 0.0, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    glh::draw_mesh(self.vertex_data);
    self._pick_tile(win_size, mouse_pos)
  }
}

impl Drop for TilePicker {
  fn drop(&mut self) {
    self.cleanup_opengl();
  }
}

struct Geom {
  hex_ex_radius: GLfloat,
  hex_in_radius: GLfloat,
}

impl Geom {
  pub fn new() -> Geom {
    let hex_ex_radius: GLfloat = 1.0 / 2.0;
    let hex_in_radius = sqrt(
        pow(hex_ex_radius, 2) - pow(hex_ex_radius / 2.0, 2));
    Geom {
      hex_ex_radius: hex_ex_radius,
      hex_in_radius: hex_in_radius,
    }
  }

  pub fn v2i_to_v2f(&self, i: Vec2<i32>) -> Vec2<f32> {
    let v = Vec2 {
      x: (i.x as f32) * self.hex_in_radius * 2.0,
      y: (i.y as f32) * self.hex_ex_radius * 1.5
    };
    if i.y % 2 == 0 {
      Vec2{x: v.x + self.hex_in_radius, y: v.y}
    } else {
      v
    }
  }

  pub fn index_to_circle_vertex(&self, count: int, i: int) -> Vec2<f32> {
    let n = FRAC_PI_2 + 2.0 * PI * (i as f32) / (count as f32);
    Vec2{x: cos(n), y: sin(n)}.mul_s(self.hex_ex_radius)
  }

  pub fn index_to_hex_vertex(&self, i: int) -> Vec2<f32> {
    self.index_to_circle_vertex(6, i)
  }
}

pub struct Visualizer {
  glfw_event_handlers: EventHandlers,
  program: GLuint,
  vertex_buffer_obj: GLuint,
  mat_id: GLint,
  win: Option<glfw::Window>,
  vertex_data: ~[Vec3<GLfloat>],
  mouse_pos: Vec2<f32>,
  camera: Camera,
  picker: TilePicker,
  selected_tile_pos: Option<Vec2<i32>>,
  geom: Geom,
}

fn init_win(win_size: Vec2<int>) -> glfw::Window {
  glfw::set_error_callback(~glfw::LogErrorHandler);
  let init_status = glfw::init();
  if !init_status.is_ok() {
    fail!("Failed to initialize GLFW");
  }
  let win = glfw::Window::create(
    win_size.x as u32,
    win_size.y as u32,
    "OpenGL",
    glfw::Windowed,
  ).unwrap();
  win.make_context_current();
  win
}

impl Visualizer {
  pub fn new() -> ~Visualizer {
    let win_size = Vec2::<int>{x: 640, y: 480};
    let win = init_win(win_size);
    let geom = Geom::new();
    let mut vis = ~Visualizer {
      glfw_event_handlers: EventHandlers::new(&win),
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      win: Some(win),
      vertex_data: ~[],
      mouse_pos: Vec2::zero(),
      camera: Camera::new(),
      picker: TilePicker::new(&geom),
      selected_tile_pos: None,
      geom: geom,
    };
    vis.init_opengl();
    vis.init_model();
    vis
  }

  fn win<'a>(&'a self) -> &'a glfw::Window {
    self.win.get_ref()
  }

  fn build_hex_mesh(&mut self) {
    for tile_pos in TileIterator::new() {
      let pos3d = self.geom.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = self.geom.index_to_hex_vertex(num);
        let next_vertex = self.geom.index_to_hex_vertex(num + 1);
        let data = &mut self.vertex_data;
        data.push(pos3d + vertex.extend(0.0));
        data.push(pos3d + next_vertex.extend(0.0));
        data.push(pos3d + Vec3::zero());
      }
    }
  }

  fn init_model(&mut self) {
    self.build_hex_mesh();
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn init_opengl(&mut self) {
    gl::load_with(glfw::get_proc_address);
    self.program = glh::compile_program(
      VERTEX_SHADER_SRC,
      FRAGMENT_SHADER_SRC,
    );
    self.picker.init_opengl();
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
    }
  }

  fn draw_map(&self) {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &self.camera.mat());
    glh::draw_mesh(self.vertex_data);
  }

  pub fn draw(&self) {
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    self.draw_map();
    self.win().swap_buffers();
  }

  pub fn is_running(&self) -> bool {
    return !self.win().should_close()
  }

  pub fn handle_key_events(&mut self) {
    self.glfw_event_handlers.key_handler.handle(|event| {
      if event.action != glfw::Press {
        return;
      }
      match event.key {
        glfw::KeyEscape | glfw::KeyQ
                       => self.win().set_should_close(true),
        glfw::KeySpace => println!("space"),
        glfw::KeyUp    => self.camera.move(270.0),
        glfw::KeyDown  => self.camera.move(90.0),
        glfw::KeyRight => self.camera.move(0.0),
        glfw::KeyLeft  => self.camera.move(180.0),
        _ => {},
      }
    });
  }

  pub fn handle_cursor_pos_events(&mut self) {
    self.glfw_event_handlers.cursor_pos_handler.handle(|event| {
      let button = self.win().get_mouse_button(glfw::MouseButtonRight);
      if button == glfw::Press {
        self.camera.z_angle += self.mouse_pos.x - event.x;
        self.camera.x_angle += self.mouse_pos.y - event.y;
      }
      self.mouse_pos = Vec2{x: event.x, y: event.y};
    });
  }

  pub fn handle_events(&mut self) {
    glfw::poll_events();
    self.handle_key_events();
    self.handle_cursor_pos_events();
  }

  fn close_window(&mut self) {
    // destroy glfw::Window before terminating glfw
    self.win = None;
  }

  pub fn pick_tile(&mut self) {
    let mouse_pos = Vec2 {
      x: self.mouse_pos.x as i32,
      y: self.mouse_pos.y as i32,
    };
    let win_size = self.win().get_size();
    self.selected_tile_pos = self.picker.pick_tile(
      win_size, &self.camera, mouse_pos);
    println!("selected: {:?}", self.selected_tile_pos);
  }
}

impl Drop for Visualizer {
  fn drop(&mut self) {
    self.cleanup_opengl();
    self.close_window();
    glfw::terminate();
  }
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>visualizer.rs: Fixed TilePicker initialization :(<commit_after>// See LICENSE file for copyright and license details.

use std::f32::consts::{
  PI,
  FRAC_PI_2,
};
use std::num::{
  sqrt,
  pow,
  sin,
  cos,
};
use glfw;
use gl;
use std;
use gl::types::{
  GLfloat,
  GLint,
  GLuint,
};
use cgmath::matrix::Matrix;
use cgmath::vector::{
  Vec3,
  Vec2,
  Vector,
};
use glh = gl_helpers;
use camera::Camera;
use glfw_events::EventHandlers;
use map::TileIterator;
use color::Color3;

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

static PICK_VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  in vec3 color;
  out vec3 pass_color;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
    pass_color = color;
  }
";

static PICK_FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  in vec3 pass_color;
  out vec4 out_color;
  void main() {
    out_color = vec4(pass_color, 1.0);
  }
";

struct TilePicker {
  program: GLuint,
  color_buffer_obj: GLuint,
  mat_id: GLint,
  vertex_buffer_obj: GLuint,
  vertex_data: ~[Vec3<GLfloat>],
  color_data: ~[Color3],
}

impl TilePicker {
  fn new() -> TilePicker {
    let picker = TilePicker {
      color_buffer_obj: 0,
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      vertex_data: ~[],
      color_data: ~[],
    };
    picker
  }

  fn init_opengl(&mut self) {
    self.program = glh::compile_program(
      PICK_VERTEX_SHADER_SRC,
      PICK_FRAGMENT_SHADER_SRC);
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteBuffers(1, &self.color_buffer_obj);
    }
  }

  fn build_hex_mesh_for_picking(&mut self, geom: &Geom) {
    for tile_pos in TileIterator::new() {
      let pos3d = geom.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = geom.index_to_hex_vertex(num);
        let next_vertex = geom.index_to_hex_vertex(num + 1);
        let col_x = tile_pos.x as f32 / 255.0;
        let col_y = tile_pos.y as f32 / 255.0;
        let c_data = &mut self.color_data;
        let v_data = &mut self.vertex_data;
        let color = Color3{r: col_x, g: col_y, b: 1.0};
        v_data.push(pos3d + vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + next_vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + Vec3::zero());
        c_data.push(color);
      }
    }
  }

  fn init(&mut self, geom: &Geom) {
    self.build_hex_mesh_for_picking(geom);
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      gl::GenBuffers(1, &mut self.color_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer_obj);
      glh::fill_current_color_vbo(self.color_data);
      let color_attr = glh::get_attr(self.program, "color");
      gl::EnableVertexAttribArray(color_attr);
      glh::define_array_of_generic_attr_data(color_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn _pick_tile(
    &self,
    win_size: (i32, i32),
    mouse_pos: Vec2<i32>
  ) -> Option<Vec2<i32>> {
    let (_, height) = win_size;
    let reverted_y = height - mouse_pos.y;
    let data: [u8, ..4] = [0, 0, 0, 0]; // mut
    unsafe {
      let data_ptr = std::cast::transmute(&data[0]);
      gl::ReadPixels(
        mouse_pos.x, reverted_y, 1, 1,
        gl::RGBA,
        gl::UNSIGNED_BYTE,
        data_ptr
      );
    }
    if data[2] != 0 {
      Some(Vec2{x: data[0] as i32, y: data[1] as i32})
    } else {
      None
    }
  }

  pub fn pick_tile(
    &mut self,
    win_size: (i32, i32),
    camera: &Camera,
    mouse_pos: Vec2<i32>
  ) -> Option<Vec2<i32>> {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &camera.mat());
    gl::ClearColor(0.0, 0.0, 0.0, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    glh::draw_mesh(self.vertex_data);
    self._pick_tile(win_size, mouse_pos)
  }
}

impl Drop for TilePicker {
  fn drop(&mut self) {
    self.cleanup_opengl();
  }
}

struct Geom {
  hex_ex_radius: GLfloat,
  hex_in_radius: GLfloat,
}

impl Geom {
  pub fn new() -> Geom {
    let hex_ex_radius: GLfloat = 1.0 / 2.0;
    let hex_in_radius = sqrt(
        pow(hex_ex_radius, 2) - pow(hex_ex_radius / 2.0, 2));
    Geom {
      hex_ex_radius: hex_ex_radius,
      hex_in_radius: hex_in_radius,
    }
  }

  pub fn v2i_to_v2f(&self, i: Vec2<i32>) -> Vec2<f32> {
    let v = Vec2 {
      x: (i.x as f32) * self.hex_in_radius * 2.0,
      y: (i.y as f32) * self.hex_ex_radius * 1.5
    };
    if i.y % 2 == 0 {
      Vec2{x: v.x + self.hex_in_radius, y: v.y}
    } else {
      v
    }
  }

  pub fn index_to_circle_vertex(&self, count: int, i: int) -> Vec2<f32> {
    let n = FRAC_PI_2 + 2.0 * PI * (i as f32) / (count as f32);
    Vec2{x: cos(n), y: sin(n)}.mul_s(self.hex_ex_radius)
  }

  pub fn index_to_hex_vertex(&self, i: int) -> Vec2<f32> {
    self.index_to_circle_vertex(6, i)
  }
}

pub struct Visualizer {
  glfw_event_handlers: EventHandlers,
  program: GLuint,
  vertex_buffer_obj: GLuint,
  mat_id: GLint,
  win: Option<glfw::Window>,
  vertex_data: ~[Vec3<GLfloat>],
  mouse_pos: Vec2<f32>,
  camera: Camera,
  picker: TilePicker,
  selected_tile_pos: Option<Vec2<i32>>,
  geom: Geom,
}

fn init_win(win_size: Vec2<int>) -> glfw::Window {
  glfw::set_error_callback(~glfw::LogErrorHandler);
  let init_status = glfw::init();
  if !init_status.is_ok() {
    fail!("Failed to initialize GLFW");
  }
  let win = glfw::Window::create(
    win_size.x as u32,
    win_size.y as u32,
    "OpenGL",
    glfw::Windowed,
  ).unwrap();
  win.make_context_current();
  win
}

impl Visualizer {
  pub fn new() -> ~Visualizer {
    let win_size = Vec2::<int>{x: 640, y: 480};
    let win = init_win(win_size);
    let geom = Geom::new();
    let mut vis = ~Visualizer {
      glfw_event_handlers: EventHandlers::new(&win),
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      win: Some(win),
      vertex_data: ~[],
      mouse_pos: Vec2::zero(),
      camera: Camera::new(),
      picker: TilePicker::new(),
      selected_tile_pos: None,
      geom: geom,
    };
    vis.init_opengl();
    vis.picker.init(&geom);
    vis.init_model();
    vis
  }

  fn win<'a>(&'a self) -> &'a glfw::Window {
    self.win.get_ref()
  }

  fn build_hex_mesh(&mut self) {
    for tile_pos in TileIterator::new() {
      let pos3d = self.geom.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = self.geom.index_to_hex_vertex(num);
        let next_vertex = self.geom.index_to_hex_vertex(num + 1);
        let data = &mut self.vertex_data;
        data.push(pos3d + vertex.extend(0.0));
        data.push(pos3d + next_vertex.extend(0.0));
        data.push(pos3d + Vec3::zero());
      }
    }
  }

  fn init_model(&mut self) {
    self.build_hex_mesh();
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn init_opengl(&mut self) {
    gl::load_with(glfw::get_proc_address);
    self.program = glh::compile_program(
      VERTEX_SHADER_SRC,
      FRAGMENT_SHADER_SRC,
    );
    self.picker.init_opengl();
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
    }
  }

  fn draw_map(&self) {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &self.camera.mat());
    glh::draw_mesh(self.vertex_data);
  }

  pub fn draw(&self) {
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    self.draw_map();
    self.win().swap_buffers();
  }

  pub fn is_running(&self) -> bool {
    return !self.win().should_close()
  }

  pub fn handle_key_events(&mut self) {
    self.glfw_event_handlers.key_handler.handle(|event| {
      if event.action != glfw::Press {
        return;
      }
      match event.key {
        glfw::KeyEscape | glfw::KeyQ
                       => self.win().set_should_close(true),
        glfw::KeySpace => println!("space"),
        glfw::KeyUp    => self.camera.move(270.0),
        glfw::KeyDown  => self.camera.move(90.0),
        glfw::KeyRight => self.camera.move(0.0),
        glfw::KeyLeft  => self.camera.move(180.0),
        _ => {},
      }
    });
  }

  pub fn handle_cursor_pos_events(&mut self) {
    self.glfw_event_handlers.cursor_pos_handler.handle(|event| {
      let button = self.win().get_mouse_button(glfw::MouseButtonRight);
      if button == glfw::Press {
        self.camera.z_angle += self.mouse_pos.x - event.x;
        self.camera.x_angle += self.mouse_pos.y - event.y;
      }
      self.mouse_pos = Vec2{x: event.x, y: event.y};
    });
  }

  pub fn handle_events(&mut self) {
    glfw::poll_events();
    self.handle_key_events();
    self.handle_cursor_pos_events();
  }

  fn close_window(&mut self) {
    // destroy glfw::Window before terminating glfw
    self.win = None;
  }

  pub fn pick_tile(&mut self) {
    let mouse_pos = Vec2 {
      x: self.mouse_pos.x as i32,
      y: self.mouse_pos.y as i32,
    };
    let win_size = self.win().get_size();
    self.selected_tile_pos = self.picker.pick_tile(
      win_size, &self.camera, mouse_pos);
    println!("selected: {:?}", self.selected_tile_pos);
  }
}

impl Drop for Visualizer {
  fn drop(&mut self) {
    self.cleanup_opengl();
    self.close_window();
    glfw::terminate();
  }
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<|endoftext|>