<commit_before>use time;
use std::thread;
use glium::{self, glutin, texture, framebuffer, Surface, DisplayBuild};
use image;
use ::{V2, Rect, AverageDuration, color, Rgba};
use super::event::{Event, MouseButton};
use super::{CanvasMagnify};
use super::event_translator::{EventTranslator};

pub struct WindowBuilder {
    title: String,
    size: V2<u32>,
    frame_interval: Option<f64>,
    fullscreen: bool,
    layout_independent_keys: bool,
    magnify: CanvasMagnify,
}

impl WindowBuilder {
    pub fn new() -> WindowBuilder {
        WindowBuilder {
            title: "".to_string(),
            size: V2(640, 360),
            frame_interval: None,
            fullscreen: false,
            layout_independent_keys: true,
            magnify: CanvasMagnify::PixelPerfect,
        }
    }

    /// Set the window title.
    pub fn set_title(mut self, title: &str) -> WindowBuilder {
        self.title = title.to_string();
        self
    }

    /// Set the frame rate.
    pub fn set_frame_interval(mut self, interval_s: f64) -> WindowBuilder {
        assert!(interval_s > 0.00001);
        self.frame_interval = Some(interval_s);
        self
    }

    /// Set the size of the logical canvas.
    pub fn set_size(mut self, width: u32, height: u32) -> WindowBuilder {
        self.size = V2(width, height);
        self
    }

    /// Get the key values from the user's keyboard layout instead of the
    /// hardware keyboard map. Hardware keymap lookup may not work correctly
    /// on all platforms.
    pub fn use_layout_dependent_keys(mut self) -> WindowBuilder {
        self.layout_independent_keys = false;
        self
    }

    /// Set the canvas to start in fullscreen mode.
    /// FIXME: Broken on Linux, https://github.com/tomaka/glutin/issues/148
    pub fn set_fullscreen(mut self, state: bool) -> WindowBuilder {
        self.fullscreen = state;
        self
    }

    pub fn set_magnify(mut self, magnify: CanvasMagnify) -> WindowBuilder {
        self.magnify = magnify;
        self
    }

    /// Build the window object.
    pub fn build(self) -> Window {
        Window::new(self)
    }
}

/// Toplevel application object.
pub struct Window {
    pub display: glium::Display,
    pub clear_color: Rgba,

    resolution: LogicalResolution,

    /// Shader for blitting the canvas texture to screen.
    blit_shader: glium::Program,

    /// Render target texture.
    buffer: texture::Texture2d,
    depth: framebuffer::DepthRenderBuffer,

    translator: EventTranslator,
    frame_duration: AverageDuration,

    frame_interval: Option<f64>,
    previous_frame_t: f64,
}

impl Window {
    fn new(builder: WindowBuilder) -> Window {
        use glium::glutin::{GlRequest, Api};
        let size = builder.size;
        let title = &builder.title[..];

        let mut glutin = glutin::WindowBuilder::new()
            .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
            .with_title(title.to_string());

        if builder.fullscreen {
            // FIXME: Glutin's X11 fullscreen is broken, this is only enabled
            // for Windows.
            if cfg!(windows) {
                glutin = glutin.with_fullscreen(glutin::get_primary_monitor());
            }
        } else {
            // Zoom up the window to the biggest even pixel multiple that fits
            // the user's monitor.
            let window_border_guesstimate = 32;
            let (w, h) = glutin::get_primary_monitor().get_dimensions();
            let window_size = V2(w, h) - V2(window_border_guesstimate, window_border_guesstimate);

            let (mut x, mut y) = (size.0, size.1);
            while x * 2 <= window_size.0 && y * 2 <= window_size.1 {
                x *= 2;
                y *= 2;
            }

            glutin = glutin.with_dimensions(x, y);
        }

        let display = glutin.build_glium().unwrap();

        let (w, h) = display.get_framebuffer_dimensions();

        let resolution = LogicalResolution::new(builder.magnify, size, V2(w, h));

        let blit_shader = glium::Program::from_source(&display,
            include_str!("blit.vert"),
            include_str!("blit.frag"),
            None).unwrap();

        let buffer = texture::Texture2d::empty(
            &display, size.0, size.1).unwrap();
        let depth = framebuffer::DepthRenderBuffer::new(
            &display, texture::DepthFormat::F32, size.0, size.1).unwrap();

        Window {
            display: display,
            clear_color: color::BLACK,
            resolution: resolution,
            blit_shader: blit_shader,
            buffer: buffer,
            depth: depth,
            translator: EventTranslator::new(builder.layout_independent_keys),
            frame_duration: AverageDuration::new(0.1, 0.9),
            frame_interval: builder.frame_interval,
            previous_frame_t: time::precise_time_s(),
        }
    }

    /// Display the screen buffer and do end-of-frame bookkeeping.
    pub fn end_frame(&mut self) {
        self.frame_duration.tick();
        self.show();

        // Stick to a target frame rate if one is set.
        if let Some(target_t) = self.frame_interval {
            let sleepytime = target_t - (time::precise_time_s() - self.previous_frame_t);
            if sleepytime > 0.0 { thread::sleep_ms((sleepytime * 1e3) as u32); }
        }
        self.previous_frame_t = time::precise_time_s();

        // Pull in new input events. This will sleep if there was a suspend
        // event.
        self.translator.pump(&mut self.display, &self.resolution);
    }

    pub fn draw<F>(&self, draw_f: F)
        where F: FnOnce(&mut framebuffer::SimpleFrameBuffer) {
        let mut target = framebuffer::SimpleFrameBuffer::with_depth_buffer(
            &self.display, &self.buffer, &self.depth).unwrap();
        draw_f(&mut target);
    }

    /// Fill the frame with the given pixel buffer. The pixel data dimensions
    /// must match the logical size of the window.
    pub fn set_frame<'a, T: glium::texture::Texture2dDataSource<'a>>(&mut self, pixels: T) {
        let new_texture = texture::Texture2d::new(&self.display, pixels).unwrap();
        assert!(new_texture.get_width() == self.resolution.canvas.0 &&
                new_texture.get_height() == Some(self.resolution.canvas.1),
            "Pixel data dimensions do not match logical window size");
        self.buffer = new_texture;
    }

    /// Return the logical window resolution.
    #[inline(always)]
    pub fn size(&self) -> V2<u32> { self.resolution.canvas }

    pub fn mouse_pos(&self) -> V2<f32> { self.translator.mouse_pos }

    pub fn mouse_pressed(&self, button: MouseButton) -> bool {
        self.translator.mouse_pressed[button as usize].is_some()
    }

    /// Return the current exponential moving average for frame rendering
    /// duration in seconds.
    pub fn frame_duration(&self) -> f64 { self.frame_duration.value }

    //pub fn events<'a>(&'a mut self) -> EventIterator<'a> { EventIterator { window: self } }
    pub fn events(&mut self) -> Vec<Event> {
        // XXX: Not returning an iterator because of locking crap. May want to
        // do mutable stuff on window in response to events.
        EventIterator { window: self }.collect::<Vec<Event>>()
    }

    /// Map screen position (eg. of a mouse cursor) to canvas position.
    pub fn screen_to_canvas(&self, screen_pos: V2<i32>) -> V2<i32> {
        self.resolution.screen_to_canvas(screen_pos)
    }

    pub fn get_screenshot(&self) -> image::ImageBuffer<image::Rgb<u8>, Vec<u8>> {
        use ::rgb::{to_srgb};
        let mut ret = self.buffer.read::<image::DynamicImage>().to_rgb();

        // Convert to sRGB
        // XXX: Probably horribly slow, can we make OpenGL do this?
        for p in ret.pixels_mut() {
            p.data[0] = (to_srgb(p.data[0] as f32 / 255.0) * 255.0).round() as u8;
            p.data[1] = (to_srgb(p.data[1] as f32 / 255.0) * 255.0).round() as u8;
            p.data[2] = (to_srgb(p.data[2] as f32 / 255.0) * 255.0).round() as u8;
        }

        ret
    }

    /// Show the graphics buffer on screen.
    fn show(&mut self) {
        let mut target = self.display.draw();

        // Clear the window.
        target.clear_color(
            self.clear_color.r,
            self.clear_color.g,
            self.clear_color.b,
            self.clear_color.a);
        target.clear_depth(1.0);
        let (w, h) = self.display.get_framebuffer_dimensions();
        // Clip viewport dimensions to even to prevent rounding errors in
        // pixel perfect scaling.
        self.resolution.window = V2(w & !1, h & !1);

        // Build the geometry for the on-screen rectangle.
        let Rect(V2(sx, sy), V2(sw, sh)) = self.resolution.screen_rect();

        // XXX: This could use glium::Surface::blit_whole_color_to instead of
        // the handmade blitting, but that was buggy on Windows around
        // 2015-03.

        let vertices = {
            #[derive(Copy, Clone)]
            struct BlitVertex { pos: [f32; 2], tex_coord: [f32; 2] }
            implement_vertex!(BlitVertex, pos, tex_coord);

            glium::VertexBuffer::new(&self.display,
            &[
                BlitVertex { pos: [sx,    sy   ], tex_coord: [0.0, 0.0] },
                BlitVertex { pos: [sx+sw, sy   ], tex_coord: [1.0, 0.0] },
                BlitVertex { pos: [sx+sw, sy+sh], tex_coord: [1.0, 1.0] },
                BlitVertex { pos: [sx,    sy+sh], tex_coord: [0.0, 1.0] },
            ]).unwrap()
        };

        let indices = glium::IndexBuffer::new(&self.display,
            glium::index::PrimitiveType::TrianglesList, &[0u16, 1, 2, 0, 2, 3]).unwrap();

        // Set up the rest of the draw parameters.
        let mut params: glium::DrawParameters = Default::default();
        // Set an explicit viewport to apply the custom resolution that fixes
        // pixel perfect rounding errors.
        params.viewport = Some(glium::Rect{
            left: 0, bottom: 0,
            width: self.resolution.window.0,
            height: self.resolution.window.1 });

        let mag_filter = match self.resolution.magnify {
            CanvasMagnify::Smooth => glium::uniforms::MagnifySamplerFilter::Linear,
            _ => glium::uniforms::MagnifySamplerFilter::Nearest
        };

        let uniforms = glium::uniforms::UniformsStorage::new("tex",
            glium::uniforms::Sampler(&self.buffer, glium::uniforms::SamplerBehavior {
                magnify_filter: mag_filter,
                minify_filter: glium::uniforms::MinifySamplerFilter::Linear,
                .. Default::default() }));

        // Draw the graphics buffer to the window.
        target.draw(&vertices, &indices, &self.blit_shader, &uniforms, &params).unwrap();
        target.finish().unwrap();
    }
}

pub struct LogicalResolution {
    pub magnify: CanvasMagnify,
    /// Logical size, pixel on virtual canvas.
    pub canvas: V2<u32>,
    /// Physical size, window on screen.
    pub window: V2<u32>,
}

impl LogicalResolution {
    pub fn new(magnify: CanvasMagnify, canvas_size: V2<u32>, resolution: V2<u32>) -> LogicalResolution {
        LogicalResolution {
            magnify: magnify,
            canvas: canvas_size,
            window: resolution,
        }
    }

    pub fn screen_rect(&self) -> Rect<f32> {
        match self.magnify {
            CanvasMagnify::PixelPerfect => self.pixel_perfect(),
            _ => self.preserve_aspect(),
        }
    }

    /// Map screen position (eg. of a mouse cursor) to canvas position.
    pub fn screen_to_canvas(&self, V2(sx, sy): V2<i32>) -> V2<i32> {
        let Rect(V2(rx, ry), V2(rw, rh)) = self.screen_rect();

        // Transform to device coordinates.
        let sx = sx as f32 * 2.0 / self.window.0 as f32 - 1.0;
        let sy = sy as f32 * 2.0 / self.window.1 as f32 - 1.0;

        V2(((sx - rx) * self.canvas.0 as f32 / rw) as i32,
           ((sy - ry) * self.canvas.1 as f32 / rh) as i32)
    }

    /// A pixel perfect centered and scaled rectangle of resolution dim in a
    /// window of size area, mapped to OpenGL device coordinates.
#[inline(always)]
    fn pixel_perfect(&self) -> Rect<f32> {
        // Scale based on whichever of X or Y axis is the tighter fit.
        let mut scale = (self.window.0 as f32 / self.canvas.0 as f32)
            .min(self.window.1 as f32 / self.canvas.1 as f32);

        if scale > 1.0 {
            // Snap to pixel scale if more than 1 window pixel per canvas pixel.
            scale = scale.floor();
        }

        let dim = V2((scale * self.canvas.0 as f32) * 2.0 / self.window.0 as f32,
        (scale * self.canvas.1 as f32) * 2.0 / self.window.1 as f32);
        let offset = -dim / 2.0;
        Rect(offset, dim)
    }

#[inline(always)]
    fn preserve_aspect(&self) -> Rect<f32> {
        // Scale based on whichever of X or Y axis is the tighter fit.
        let scale = (self.window.0 as f32 / self.canvas.0 as f32)
            .min(self.window.1 as f32 / self.canvas.1 as f32);

        let dim = V2((scale * self.canvas.0 as f32) * 2.0 / self.window.0 as f32,
        (scale * self.canvas.1 as f32) * 2.0 / self.window.1 as f32);
        let offset = -dim / 2.0;
        Rect(offset, dim)
    }
}

pub struct EventIterator<'a> {
    window: &'a mut Window
}

impl<'a> Iterator for EventIterator<'a> {
    type Item = Event;
    fn next(&mut self) -> Option<Event> { self.window.translator.next() }
}
<commit_msg>Cleanup<commit_after>use time;
use std::thread;
use glium::{self, glutin, texture, framebuffer, Surface, DisplayBuild};
use image;
use ::{V2, Rect, AverageDuration, color, Rgba};
use super::event::{Event, MouseButton};
use super::{CanvasMagnify};
use super::event_translator::{EventTranslator};

pub struct WindowBuilder {
    title: String,
    size: V2<u32>,
    frame_interval: Option<f64>,
    fullscreen: bool,
    layout_independent_keys: bool,
    magnify: CanvasMagnify,
}

impl WindowBuilder {
    pub fn new() -> WindowBuilder {
        WindowBuilder {
            title: "".to_string(),
            size: V2(640, 360),
            frame_interval: None,
            fullscreen: false,
            layout_independent_keys: true,
            magnify: CanvasMagnify::PixelPerfect,
        }
    }

    /// Set the window title.
    pub fn set_title(mut self, title: &str) -> WindowBuilder {
        self.title = title.to_string();
        self
    }

    /// Set the frame rate.
    pub fn set_frame_interval(mut self, interval_s: f64) -> WindowBuilder {
        assert!(interval_s > 0.00001);
        self.frame_interval = Some(interval_s);
        self
    }

    /// Set the size of the logical canvas.
    pub fn set_size(mut self, width: u32, height: u32) -> WindowBuilder {
        self.size = V2(width, height);
        self
    }

    /// Get the key values from the user's keyboard layout instead of the
    /// hardware keyboard map. Hardware keymap lookup may not work correctly
    /// on all platforms.
    pub fn use_layout_dependent_keys(mut self) -> WindowBuilder {
        self.layout_independent_keys = false;
        self
    }

    /// Set the canvas to start in fullscreen mode.
    /// FIXME: Broken on Linux, https://github.com/tomaka/glutin/issues/148
    pub fn set_fullscreen(mut self, state: bool) -> WindowBuilder {
        self.fullscreen = state;
        self
    }

    pub fn set_magnify(mut self, magnify: CanvasMagnify) -> WindowBuilder {
        self.magnify = magnify;
        self
    }

    /// Build the window object.
    pub fn build(self) -> Window {
        Window::new(self)
    }
}

/// Toplevel application object.
pub struct Window {
    pub display: glium::Display,
    pub clear_color: Rgba,

    resolution: LogicalResolution,

    /// Shader for blitting the canvas texture to screen.
    blit_shader: glium::Program,

    /// Render target texture.
    buffer: texture::Texture2d,
    depth: framebuffer::DepthRenderBuffer,

    translator: EventTranslator,
    frame_duration: AverageDuration,

    frame_interval: Option<f64>,
    previous_frame_t: f64,
}

impl Window {
    fn new(builder: WindowBuilder) -> Window {
        use glium::glutin::{GlRequest, Api};
        let size = builder.size;
        let title = &builder.title[..];

        let mut glutin = glutin::WindowBuilder::new()
            .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
            .with_title(title.to_string());

        if builder.fullscreen {
            // FIXME: Glutin's X11 fullscreen is broken, this is only enabled
            // for Windows.
            if cfg!(windows) {
                glutin = glutin.with_fullscreen(glutin::get_primary_monitor());
            }
        } else {
            // Zoom up the window to the biggest even pixel multiple that fits
            // the user's monitor.
            let window_border_guesstimate = 32;
            let (w, h) = glutin::get_primary_monitor().get_dimensions();
            let window_size = V2(w, h) - V2(window_border_guesstimate, window_border_guesstimate);

            let (mut x, mut y) = (size.0, size.1);
            while x * 2 <= window_size.0 && y * 2 <= window_size.1 {
                x *= 2;
                y *= 2;
            }

            glutin = glutin.with_dimensions(x, y);
        }

        let display = glutin.build_glium().unwrap();

        let (w, h) = display.get_framebuffer_dimensions();

        let resolution = LogicalResolution::new(builder.magnify, size, V2(w, h));

        let blit_shader = glium::Program::from_source(&display,
            include_str!("blit.vert"),
            include_str!("blit.frag"),
            None).unwrap();

        let buffer = texture::Texture2d::empty(
            &display, size.0, size.1).unwrap();
        let depth = framebuffer::DepthRenderBuffer::new(
            &display, texture::DepthFormat::F32, size.0, size.1).unwrap();

        Window {
            display: display,
            clear_color: color::BLACK,
            resolution: resolution,
            blit_shader: blit_shader,
            buffer: buffer,
            depth: depth,
            translator: EventTranslator::new(builder.layout_independent_keys),
            frame_duration: AverageDuration::new(0.1, 0.9),
            frame_interval: builder.frame_interval,
            previous_frame_t: time::precise_time_s(),
        }
    }

    /// Display the screen buffer and do end-of-frame bookkeeping.
    pub fn end_frame(&mut self) {
        self.frame_duration.tick();
        self.show();

        // Stick to a target frame rate if one is set.
        if let Some(target_t) = self.frame_interval {
            let sleepytime = target_t - (time::precise_time_s() - self.previous_frame_t);
            if sleepytime > 0.0 { thread::sleep_ms((sleepytime * 1e3) as u32); }
        }
        self.previous_frame_t = time::precise_time_s();

        // Pull in new input events. This will sleep if there was a suspend
        // event.
        self.translator.pump(&mut self.display, &self.resolution);
    }

    pub fn draw<F>(&self, draw_f: F)
        where F: FnOnce(&mut framebuffer::SimpleFrameBuffer) {
        let mut target = framebuffer::SimpleFrameBuffer::with_depth_buffer(
            &self.display, &self.buffer, &self.depth).unwrap();
        draw_f(&mut target);
    }

    /// Fill the frame with the given pixel buffer. The pixel data dimensions
    /// must match the logical size of the window.
    pub fn set_frame<'a, T: glium::texture::Texture2dDataSource<'a>>(&mut self, pixels: T) {
        let new_texture = texture::Texture2d::new(&self.display, pixels).unwrap();
        assert!(new_texture.get_width() == self.resolution.canvas.0 &&
                new_texture.get_height() == Some(self.resolution.canvas.1),
            "Pixel data dimensions do not match logical window size");
        self.buffer = new_texture;
    }

    /// Return the logical window resolution.
    #[inline(always)]
    pub fn size(&self) -> V2<u32> { self.resolution.canvas }

    pub fn mouse_pos(&self) -> V2<f32> { self.translator.mouse_pos }

    pub fn mouse_pressed(&self, button: MouseButton) -> bool {
        self.translator.mouse_pressed[button as usize].is_some()
    }

    /// Return the current exponential moving average for frame rendering
    /// duration in seconds.
    pub fn frame_duration(&self) -> f64 { self.frame_duration.value }

    pub fn events(&mut self) -> Vec<Event> {
        // XXX: Not returning an iterator because of locking crap. May want to
        // do mutable stuff on window in response to events.
        EventIterator { window: self }.collect::<Vec<Event>>()
    }

    /// Map screen position (eg. of a mouse cursor) to canvas position.
    pub fn screen_to_canvas(&self, screen_pos: V2<i32>) -> V2<i32> {
        self.resolution.screen_to_canvas(screen_pos)
    }

    pub fn get_screenshot(&self) -> image::ImageBuffer<image::Rgb<u8>, Vec<u8>> {
        use ::rgb::{to_srgb};
        let mut ret = self.buffer.read::<image::DynamicImage>().to_rgb();

        // Convert to sRGB
        // XXX: Probably horribly slow, can we make OpenGL do this?
        for p in ret.pixels_mut() {
            p.data[0] = (to_srgb(p.data[0] as f32 / 255.0) * 255.0).round() as u8;
            p.data[1] = (to_srgb(p.data[1] as f32 / 255.0) * 255.0).round() as u8;
            p.data[2] = (to_srgb(p.data[2] as f32 / 255.0) * 255.0).round() as u8;
        }

        ret
    }

    /// Show the graphics buffer on screen.
    fn show(&mut self) {
        let mut target = self.display.draw();

        // Clear the window.
        target.clear_color(
            self.clear_color.r,
            self.clear_color.g,
            self.clear_color.b,
            self.clear_color.a);
        target.clear_depth(1.0);
        let (w, h) = self.display.get_framebuffer_dimensions();
        // Clip viewport dimensions to even to prevent rounding errors in
        // pixel perfect scaling.
        self.resolution.window = V2(w & !1, h & !1);

        // Build the geometry for the on-screen rectangle.
        let Rect(V2(sx, sy), V2(sw, sh)) = self.resolution.screen_rect();

        // XXX: This could use glium::Surface::blit_whole_color_to instead of
        // the handmade blitting, but that was buggy on Windows around
        // 2015-03.

        let vertices = {
            #[derive(Copy, Clone)]
            struct BlitVertex { pos: [f32; 2], tex_coord: [f32; 2] }
            implement_vertex!(BlitVertex, pos, tex_coord);

            glium::VertexBuffer::new(&self.display,
            &[
                BlitVertex { pos: [sx,    sy   ], tex_coord: [0.0, 0.0] },
                BlitVertex { pos: [sx+sw, sy   ], tex_coord: [1.0, 0.0] },
                BlitVertex { pos: [sx+sw, sy+sh], tex_coord: [1.0, 1.0] },
                BlitVertex { pos: [sx,    sy+sh], tex_coord: [0.0, 1.0] },
            ]).unwrap()
        };

        let indices = glium::IndexBuffer::new(&self.display,
            glium::index::PrimitiveType::TrianglesList, &[0u16, 1, 2, 0, 2, 3]).unwrap();

        // Set up the rest of the draw parameters.
        let mut params: glium::DrawParameters = Default::default();
        // Set an explicit viewport to apply the custom resolution that fixes
        // pixel perfect rounding errors.
        params.viewport = Some(glium::Rect{
            left: 0, bottom: 0,
            width: self.resolution.window.0,
            height: self.resolution.window.1 });

        let mag_filter = match self.resolution.magnify {
            CanvasMagnify::Smooth => glium::uniforms::MagnifySamplerFilter::Linear,
            _ => glium::uniforms::MagnifySamplerFilter::Nearest
        };

        let uniforms = glium::uniforms::UniformsStorage::new("tex",
            glium::uniforms::Sampler(&self.buffer, glium::uniforms::SamplerBehavior {
                magnify_filter: mag_filter,
                minify_filter: glium::uniforms::MinifySamplerFilter::Linear,
                .. Default::default() }));

        // Draw the graphics buffer to the window.
        target.draw(&vertices, &indices, &self.blit_shader, &uniforms, &params).unwrap();
        target.finish().unwrap();
    }
}

pub struct LogicalResolution {
    pub magnify: CanvasMagnify,
    /// Logical size, pixel on virtual canvas.
    pub canvas: V2<u32>,
    /// Physical size, window on screen.
    pub window: V2<u32>,
}

impl LogicalResolution {
    pub fn new(magnify: CanvasMagnify, canvas_size: V2<u32>, resolution: V2<u32>) -> LogicalResolution {
        LogicalResolution {
            magnify: magnify,
            canvas: canvas_size,
            window: resolution,
        }
    }

    pub fn screen_rect(&self) -> Rect<f32> {
        match self.magnify {
            CanvasMagnify::PixelPerfect => self.pixel_perfect(),
            _ => self.preserve_aspect(),
        }
    }

    /// Map screen position (eg. of a mouse cursor) to canvas position.
    pub fn screen_to_canvas(&self, V2(sx, sy): V2<i32>) -> V2<i32> {
        let Rect(V2(rx, ry), V2(rw, rh)) = self.screen_rect();

        // Transform to device coordinates.
        let sx = sx as f32 * 2.0 / self.window.0 as f32 - 1.0;
        let sy = sy as f32 * 2.0 / self.window.1 as f32 - 1.0;

        V2(((sx - rx) * self.canvas.0 as f32 / rw) as i32,
           ((sy - ry) * self.canvas.1 as f32 / rh) as i32)
    }

    /// A pixel perfect centered and scaled rectangle of resolution dim in a
    /// window of size area, mapped to OpenGL device coordinates.
#[inline(always)]
    fn pixel_perfect(&self) -> Rect<f32> {
        // Scale based on whichever of X or Y axis is the tighter fit.
        let mut scale = (self.window.0 as f32 / self.canvas.0 as f32)
            .min(self.window.1 as f32 / self.canvas.1 as f32);

        if scale > 1.0 {
            // Snap to pixel scale if more than 1 window pixel per canvas pixel.
            scale = scale.floor();
        }

        let dim = V2((scale * self.canvas.0 as f32) * 2.0 / self.window.0 as f32,
        (scale * self.canvas.1 as f32) * 2.0 / self.window.1 as f32);
        let offset = -dim / 2.0;
        Rect(offset, dim)
    }

#[inline(always)]
    fn preserve_aspect(&self) -> Rect<f32> {
        // Scale based on whichever of X or Y axis is the tighter fit.
        let scale = (self.window.0 as f32 / self.canvas.0 as f32)
            .min(self.window.1 as f32 / self.canvas.1 as f32);

        let dim = V2((scale * self.canvas.0 as f32) * 2.0 / self.window.0 as f32,
        (scale * self.canvas.1 as f32) * 2.0 / self.window.1 as f32);
        let offset = -dim / 2.0;
        Rect(offset, dim)
    }
}

pub struct EventIterator<'a> {
    window: &'a mut Window
}

impl<'a> Iterator for EventIterator<'a> {
    type Item = Event;
    fn next(&mut self) -> Option<Event> { self.window.translator.next() }
}
<|endoftext|>


<commit_before>// A Map struct that combines Tiles and Units for convenience
// This struct contains all the stuff that is saved/loaded

use super::units::*;
use super::animations::*;
use settings::*;

use std::fs::*;
use std::path::*;

mod grid;
mod iter_2d;
mod tiles;
mod vision;
mod walls;

use super::messages::*;
use super::commands::*;

pub use self::walls::*;
pub use self::tiles::*;

use bincode;

const EXTENSION: &str = ".sav";

// The Map struct
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Map {
    pub units: Units,
    pub tiles: Tiles,
    pub turn: u8,
    pub light: f32,
    pub side: Side
}

impl Map {
    // Create a new map
    pub fn new(cols: usize, rows: usize, light: f32) -> Map {
        Map {
            light,
            units: Units::new(),
            tiles: Tiles::new(cols, rows),
            turn: 1,
            side: Side::PlayerA
        }
    }

    pub fn new_from_settings(settings: SkirmishSettings) -> Self {
        let mut map = Self::new(settings.cols, settings.rows, settings.light);

        // Add player units
        for x in 0 .. settings.player_units {
            map.units.add(settings.player_unit_type, Side::PlayerA, x, 0, UnitFacing::Bottom);
        }

        // Add ai units
        for y in settings.cols - settings.ai_units .. settings.cols {
            map.units.add(settings.ai_unit_type, Side::PlayerB, y, settings.rows - 1, UnitFacing::Top);
        }

        // Generate tiles
        map.tiles.generate(&map.units);

        map
    }

    // Work out if a tile is taken or not
    pub fn taken(&self, x: usize, y: usize) -> bool {
        !self.tiles.at(x, y).obstacle.is_empty() || self.units.at(x, y).is_some()
    }

    // Work out how many units of a particular side are visible to the other side
    pub fn visible(&self, side: Side) -> impl Iterator<Item=&Unit> {
        self.units.iter()
            .filter(move |unit| unit.side == side && self.tiles.visibility_at(unit.x, unit.y, side.enemies()).is_visible())
    }

    // Load a skirmish if possible
    pub fn load(path: &Path) -> Option<Map> {
        File::open(path).ok()
            .and_then(|mut file| bincode::deserialize_from(&mut file).ok())
    }

    // Save the skirmish
    pub fn save(&self, mut filename: String, settings: &Settings) -> ServerAnimations {
        filename.push_str(EXTENSION);
        let directory = Path::new(&settings.savegames);
        let mut animations = ServerAnimations::new();

        // todo: error handling
        if filename.starts_with('.') || (!directory.exists() && create_dir_all(&directory).is_err()) {
            return animations;
        }

        let savegame = directory.join(&filename);

        let result = File::create(&savegame)
            .map_err(|error| Box::new(bincode::ErrorKind::Io(error)))
            .and_then(|mut file| bincode::serialize_into(&mut file, self));

        let message = match result {
            Ok(()) => format!("Game saved to '{}'", savegame.display()),
            Err(error) => format!("Error recieved while trying to save to '{}': {}", savegame.display(), error)
        };

        animations.push_both(Animation::Message(message));
        animations
    }

    pub fn handle_message(&mut self, message: ClientMessage, settings: &Settings) -> (Vec<Animation>, Vec<Animation>) {
        match message {
            ClientMessage::EndTurn => self.end_turn(),
            ClientMessage::SaveGame(filename) => self.save(filename, settings),
            ClientMessage::Command {unit, command} => self.perform_command(unit, command)
        }.split()
    }

    pub fn perform_command(&mut self, id: u8, command: Command) -> ServerAnimations {
        let mut animations = ServerAnimations::new();

        match command {
            Command::Walk(path) => move_command(self, id, path, &mut animations),
            Command::Turn(facing) => turn_command(self, id, facing, &mut animations),
            Command::UseItem(item) => use_item_command(self, id, item, &mut animations),
            Command::PickupItem(item) => pickup_item_command(self, id, item, &mut animations),
            Command::DropItem(item) => drop_item_command(self, id, item, &mut animations),
            Command::ThrowItem {item, x, y} => throw_item_command(self, id, item, x, y, &mut animations),
            Command::Fire {x, y} => fire_command(self, id, x, y, &mut animations),
        }

        animations
    }

    pub fn end_turn(&mut self) -> ServerAnimations {
        for unit in self.units.iter_mut() {
            unit.moves = unit.tag.moves();
        }

        match self.side {
            Side::PlayerA => self.side = Side::PlayerB,
            Side::PlayerB => {
                self.turn += 1;
                self.side = Side::PlayerA;
            }
        }

        let mut animations = ServerAnimations::new();
        animations.push_state(self);
        animations
    }

    pub fn clone_visible(&mut self, side: Side) -> Self {
        // Update visibility first
        self.tiles.update_visibility(&self.units);

        Self {
            light: self.light,
            turn: self.turn,
            side: self.side,
            units: self.tiles.visible_units(&self.units, side).cloned().collect(),
            // todo: should only clone the info of visible tiles and not clone the enemy vision
            tiles: self.tiles.clone_visible(side)

        }
    }
}

#[test]
fn load_save() {
    use super::units::*;

    // Test saving and loading a map

    let settings = Settings::default();
    let mut output = PathBuf::from(&settings.savegames);
    output.push("test.sav");

    let mut map = Map::new(20, 20, 0.5);
    map.units.add(UnitType::Squaddie, Side::PlayerA, 0, 0, UnitFacing::Bottom);
    map.tiles.update_visibility(&map.units);

    let (player_a_animations, player_b_animations) = map.save("test".into(), &settings).split();

    assert_eq!(player_a_animations, player_b_animations);
    assert_eq!(player_a_animations, vec![Animation::Message("Game saved to 'savegames/test.sav'".into())]);
    Map::load(&output).unwrap();
}<commit_msg>Fixed windows save load test<commit_after>// A Map struct that combines Tiles and Units for convenience
// This struct contains all the stuff that is saved/loaded

use super::units::*;
use super::animations::*;
use settings::*;

use std::fs::*;
use std::path::*;

mod grid;
mod iter_2d;
mod tiles;
mod vision;
mod walls;

use super::messages::*;
use super::commands::*;

pub use self::walls::*;
pub use self::tiles::*;

use bincode;

const EXTENSION: &str = ".sav";

// The Map struct
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Map {
    pub units: Units,
    pub tiles: Tiles,
    pub turn: u8,
    pub light: f32,
    pub side: Side
}

impl Map {
    // Create a new map
    pub fn new(cols: usize, rows: usize, light: f32) -> Map {
        Map {
            light,
            units: Units::new(),
            tiles: Tiles::new(cols, rows),
            turn: 1,
            side: Side::PlayerA
        }
    }

    pub fn new_from_settings(settings: SkirmishSettings) -> Self {
        let mut map = Self::new(settings.cols, settings.rows, settings.light);

        // Add player units
        for x in 0 .. settings.player_units {
            map.units.add(settings.player_unit_type, Side::PlayerA, x, 0, UnitFacing::Bottom);
        }

        // Add ai units
        for y in settings.cols - settings.ai_units .. settings.cols {
            map.units.add(settings.ai_unit_type, Side::PlayerB, y, settings.rows - 1, UnitFacing::Top);
        }

        // Generate tiles
        map.tiles.generate(&map.units);

        map
    }

    // Work out if a tile is taken or not
    pub fn taken(&self, x: usize, y: usize) -> bool {
        !self.tiles.at(x, y).obstacle.is_empty() || self.units.at(x, y).is_some()
    }

    // Work out how many units of a particular side are visible to the other side
    pub fn visible(&self, side: Side) -> impl Iterator<Item=&Unit> {
        self.units.iter()
            .filter(move |unit| unit.side == side && self.tiles.visibility_at(unit.x, unit.y, side.enemies()).is_visible())
    }

    // Load a skirmish if possible
    pub fn load(path: &Path) -> Option<Map> {
        File::open(path).ok()
            .and_then(|mut file| bincode::deserialize_from(&mut file).ok())
    }

    // Save the skirmish
    pub fn save(&self, mut filename: String, settings: &Settings) -> ServerAnimations {
        filename.push_str(EXTENSION);
        let directory = Path::new(&settings.savegames);
        let mut animations = ServerAnimations::new();

        // todo: error handling
        if filename.starts_with('.') || (!directory.exists() && create_dir_all(&directory).is_err()) {
            return animations;
        }

        let savegame = directory.join(&filename);

        let result = File::create(&savegame)
            .map_err(|error| Box::new(bincode::ErrorKind::Io(error)))
            .and_then(|mut file| bincode::serialize_into(&mut file, self));

        let message = match result {
            Ok(()) => format!("Game saved to '{}'", savegame.display()),
            Err(error) => format!("Error recieved while trying to save to '{}': {}", savegame.display(), error)
        };

        animations.push_both(Animation::Message(message));
        animations
    }

    pub fn handle_message(&mut self, message: ClientMessage, settings: &Settings) -> (Vec<Animation>, Vec<Animation>) {
        match message {
            ClientMessage::EndTurn => self.end_turn(),
            ClientMessage::SaveGame(filename) => self.save(filename, settings),
            ClientMessage::Command {unit, command} => self.perform_command(unit, command)
        }.split()
    }

    pub fn perform_command(&mut self, id: u8, command: Command) -> ServerAnimations {
        let mut animations = ServerAnimations::new();

        match command {
            Command::Walk(path) => move_command(self, id, path, &mut animations),
            Command::Turn(facing) => turn_command(self, id, facing, &mut animations),
            Command::UseItem(item) => use_item_command(self, id, item, &mut animations),
            Command::PickupItem(item) => pickup_item_command(self, id, item, &mut animations),
            Command::DropItem(item) => drop_item_command(self, id, item, &mut animations),
            Command::ThrowItem {item, x, y} => throw_item_command(self, id, item, x, y, &mut animations),
            Command::Fire {x, y} => fire_command(self, id, x, y, &mut animations),
        }

        animations
    }

    pub fn end_turn(&mut self) -> ServerAnimations {
        for unit in self.units.iter_mut() {
            unit.moves = unit.tag.moves();
        }

        match self.side {
            Side::PlayerA => self.side = Side::PlayerB,
            Side::PlayerB => {
                self.turn += 1;
                self.side = Side::PlayerA;
            }
        }

        let mut animations = ServerAnimations::new();
        animations.push_state(self);
        animations
    }

    pub fn clone_visible(&mut self, side: Side) -> Self {
        // Update visibility first
        self.tiles.update_visibility(&self.units);

        Self {
            light: self.light,
            turn: self.turn,
            side: self.side,
            units: self.tiles.visible_units(&self.units, side).cloned().collect(),
            // todo: should only clone the info of visible tiles and not clone the enemy vision
            tiles: self.tiles.clone_visible(side)

        }
    }
}

#[test]
fn load_save() {
    use super::units::*;

    // Test saving and loading a map

    let settings = Settings::default();
    let mut output = PathBuf::from(&settings.savegames);
    output.push("test.sav");

    let mut map = Map::new(20, 20, 0.5);
    map.units.add(UnitType::Squaddie, Side::PlayerA, 0, 0, UnitFacing::Bottom);
    map.tiles.update_visibility(&map.units);

    let (player_a_animations, player_b_animations) = map.save("test".into(), &settings).split();

    assert_eq!(player_a_animations, player_b_animations);
    assert_eq!(player_a_animations, vec![Animation::Message(format!("Game saved to '{}'", output.display()))]);
    Map::load(&output).unwrap();
}<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

extern crate docopt;
extern crate csv;
extern crate rustc_serialize;
extern crate mimir;
#[macro_use]
extern crate log;

use std::path::Path;
use mimir::rubber::Rubber;
use std::fs;
use std::collections::BTreeMap;

pub struct AdminGetter {
    admins: BTreeMap<String, mimir::Admin>,
}

impl AdminGetter {
    pub fn admin_by_insee(&self, insee: &str) -> Option<&mimir::Admin> {
        self.admins.get(&insee.to_string())
    }
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Bano {
    pub id: String,
    pub nb: String,
    pub street: String,
    pub zip: String,
    pub city: String,
    pub src: String,
    pub lat: f64,
    pub lon: f64,
}

impl Bano {
    pub fn insee(&self) -> &str {
        assert!(self.id.len() >= 5);
        self.id[..5].trim_left_matches('0')
    }
    pub fn fantoir(&self) -> &str {
        assert!(self.id.len() >= 10);
        &self.id[..10]
    }
    pub fn into_addr(self, admin_getter: &AdminGetter) -> mimir::Addr {
        let street_name = format!("{}, ({})", self.street, self.city);
        let addr_name = format!("{} {}", self.nb, street_name);
        let street_id = format!("street:{}", self.fantoir().to_string());
        let admin = admin_getter.admin_by_insee(&format!("admin:fr:{}", self.insee()));
        println!("Admin: {:?}", admin);
        let street = mimir::Street {
            id: street_id,
            street_name: self.street,
            name: street_name,
            administrative_region: admin.map(|a| a.clone()),
            weight: 1,
        };
        mimir::Addr {
            id: format!("addr:{};{}", self.lon, self.lat),
            house_number: self.nb,
            street: street,
            name: addr_name,
            coord: mimir::Coord {
                lat: self.lat,
                lon: self.lon,
            },
            weight: 1,
        }
    }
}

fn index_bano<I>(cnx_string: &str, dataset: &str, files: I)
    where I: Iterator<Item = std::path::PathBuf>
{
    let doc_type = "addr";
    let mut rubber = Rubber::new(cnx_string);
    let admin_getter = AdminGetter {
        admins: match rubber.get_admins() {
            Ok(val) => val,
            _ => BTreeMap::new(),
        },
    };
    let addr_index = rubber.make_index(doc_type, dataset).unwrap();
    info!("Add data in elasticsearch db.");
    for f in files {
        info!("importing {:?}...", &f);
        let mut rdr = csv::Reader::from_file(&f).unwrap().has_headers(false);

        let iter = rdr.decode().map(|r| {
            let b: Bano = r.unwrap();
            b.into_addr(&admin_getter)
        });
        match rubber.bulk_index(&addr_index, iter) {
            Err(e) => panic!("failed to bulk insert file {:?} because: {}", &f, e),
            Ok(nb) => info!("importing {:?}: {} addresses added.", &f, nb),
        }
    }
    rubber.publish_index(doc_type, dataset, addr_index).unwrap();
}

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_input: String,
    flag_connection_string: String,
    flag_dataset: String,
}

static USAGE: &'static str = "
Usage:
    bano2mimir --input=<input> [--connection-string=<connection-string>] [--dataset=<dataset>]

    -i, --input=<input>           Bano files. Can be either a directory or a file.
    -c, --connection-string=<connection-string>
                                  Elasticsearch parameters, [default: http://localhost:9200/munin]
    -d, --dataset=<dataset>       Name of the dataset, [default: fr]
";

fn main() {
    mimir::logger_init().unwrap();
    info!("importing bano into Mimir");

    let args: Args = docopt::Docopt::new(USAGE)
                         .and_then(|d| d.decode())
                         .unwrap_or_else(|e| e.exit());

    let file_path = Path::new(&args.flag_input);
    if file_path.is_dir() {
        let paths: std::fs::ReadDir = fs::read_dir(&args.flag_input).unwrap();
        index_bano(&args.flag_connection_string,
                   &args.flag_dataset,
                   paths.map(|p| p.unwrap().path()));
    } else {
        index_bano(&args.flag_connection_string,
                   &args.flag_dataset,
                   std::iter::once(std::path::PathBuf::from(&args.flag_input)));
    }
}
<commit_msg>debug deleted<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

extern crate docopt;
extern crate csv;
extern crate rustc_serialize;
extern crate mimir;
#[macro_use]
extern crate log;

use std::path::Path;
use mimir::rubber::Rubber;
use std::fs;
use std::collections::BTreeMap;

pub struct AdminGetter {
    admins: BTreeMap<String, mimir::Admin>,
}

impl AdminGetter {
    pub fn admin_by_insee(&self, insee: &str) -> Option<&mimir::Admin> {
        self.admins.get(&insee.to_string())
    }
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Bano {
    pub id: String,
    pub nb: String,
    pub street: String,
    pub zip: String,
    pub city: String,
    pub src: String,
    pub lat: f64,
    pub lon: f64,
}

impl Bano {
    pub fn insee(&self) -> &str {
        assert!(self.id.len() >= 5);
        self.id[..5].trim_left_matches('0')
    }
    pub fn fantoir(&self) -> &str {
        assert!(self.id.len() >= 10);
        &self.id[..10]
    }
    pub fn into_addr(self, admin_getter: &AdminGetter) -> mimir::Addr {
        let street_name = format!("{}, ({})", self.street, self.city);
        let addr_name = format!("{} {}", self.nb, street_name);
        let street_id = format!("street:{}", self.fantoir().to_string());
        let admin = admin_getter.admin_by_insee(&format!("admin:fr:{}", self.insee()));
        let street = mimir::Street {
            id: street_id,
            street_name: self.street,
            name: street_name,
            administrative_region: admin.map(|a| a.clone()),
            weight: 1,
        };
        mimir::Addr {
            id: format!("addr:{};{}", self.lon, self.lat),
            house_number: self.nb,
            street: street,
            name: addr_name,
            coord: mimir::Coord {
                lat: self.lat,
                lon: self.lon,
            },
            weight: 1,
        }
    }
}

fn index_bano<I>(cnx_string: &str, dataset: &str, files: I)
    where I: Iterator<Item = std::path::PathBuf>
{
    let doc_type = "addr";
    let mut rubber = Rubber::new(cnx_string);
    let admin_getter = AdminGetter {
        admins: match rubber.get_admins() {
            Ok(val) => val,
            _ => BTreeMap::new(),
        },
    };
    let addr_index = rubber.make_index(doc_type, dataset).unwrap();
    info!("Add data in elasticsearch db.");
    for f in files {
        info!("importing {:?}...", &f);
        let mut rdr = csv::Reader::from_file(&f).unwrap().has_headers(false);

        let iter = rdr.decode().map(|r| {
            let b: Bano = r.unwrap();
            b.into_addr(&admin_getter)
        });
        match rubber.bulk_index(&addr_index, iter) {
            Err(e) => panic!("failed to bulk insert file {:?} because: {}", &f, e),
            Ok(nb) => info!("importing {:?}: {} addresses added.", &f, nb),
        }
    }
    rubber.publish_index(doc_type, dataset, addr_index).unwrap();
}

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_input: String,
    flag_connection_string: String,
    flag_dataset: String,
}

static USAGE: &'static str = "
Usage:
    bano2mimir --input=<input> [--connection-string=<connection-string>] [--dataset=<dataset>]

    -i, --input=<input>           Bano files. Can be either a directory or a file.
    -c, --connection-string=<connection-string>
                                  Elasticsearch parameters, [default: http://localhost:9200/munin]
    -d, --dataset=<dataset>       Name of the dataset, [default: fr]
";

fn main() {
    mimir::logger_init().unwrap();
    info!("importing bano into Mimir");

    let args: Args = docopt::Docopt::new(USAGE)
                         .and_then(|d| d.decode())
                         .unwrap_or_else(|e| e.exit());

    let file_path = Path::new(&args.flag_input);
    if file_path.is_dir() {
        let paths: std::fs::ReadDir = fs::read_dir(&args.flag_input).unwrap();
        index_bano(&args.flag_connection_string,
                   &args.flag_dataset,
                   paths.map(|p| p.unwrap().path()));
    } else {
        index_bano(&args.flag_connection_string,
                   &args.flag_dataset,
                   std::iter::once(std::path::PathBuf::from(&args.flag_input)));
    }
}
<|endoftext|>


<commit_before>//! Awesome compatibility modules

#![cfg_attr(test, deny(bad_style,
       const_err,
       dead_code,
       improper_ctypes,
       legacy_directory_ownership,
       non_shorthand_field_patterns,
       no_mangle_generic_items,
       overflowing_literals,
       path_statements ,
       patterns_in_fns_without_body,
       plugin_as_library,
       private_in_public,
       private_no_mangle_fns,
       private_no_mangle_statics,
       safe_extern_statics,
       unconditional_recursion,
       unions_with_drop_fields,
       unused,
       unused_allocation,
       unused_comparisons,
       unused_parens,
       while_true))]

// Allowed by default
#![cfg_attr(test, deny(missing_docs,
       trivial_numeric_casts,
       unused_extern_crates,
       unused_import_braces,
       unused_qualifications))]

#[macro_use]
extern crate bitflags;
extern crate cairo;
extern crate cairo_sys;
extern crate env_logger;
extern crate exec;
extern crate getopts;
extern crate gdk_pixbuf;
extern crate glib;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate nix;
extern crate libc;
extern crate rlua;
extern crate tempfile;
extern crate xcb;
#[macro_use]
extern crate wayland_client;
extern crate wayland_sys;

// TODO remove
extern crate wlroots;

#[macro_use]
mod macros;
mod objects;
mod common;
mod wayland_obj;
mod awesome;
mod keygrabber;
mod mousegrabber;
mod root;
mod lua;
mod wayland_protocols;

use std::{env, mem, path::PathBuf, process::exit, io::{self, Write}};

use exec::Command;
use rlua::{LightUserData, Lua, Table};
use log::Level;
use nix::sys::signal::{self, SaFlags, SigAction, SigHandler, SigSet};
use xcb::{xkb, Connection};
use wayland_client::{Display, GlobalManager, EventQueue, GlobalError};
use wayland_client::protocol::{wl_compositor, wl_shm, wl_output, wl_display::RequestsTrait};
use wayland_client::sys::client::wl_display;

use self::lua::{LUA, NEXT_LUA};
use wayland_protocols::xdg_shell::xdg_wm_base;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const GIT_VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/git-version.txt"));
pub const GLOBAL_SIGNALS: &'static str = "__awesome_global_signals";
pub const XCB_CONNECTION_HANDLE: &'static str = "__xcb_connection";

/// The state passed into C to store it during the glib loop.
///
/// It's passed back to us when Awesome needs a refresh so we can
/// construct any Wayland objects.
#[repr(C)]
struct WaylandState {
    pub display: Display,
    pub event_queue: EventQueue
}

/// Called from `wayland_glib_interface.c` after every call back into the
/// wayland event loop.
///
/// This restarts the Lua thread if there is a new one pending
#[no_mangle]
pub extern "C" fn awesome_refresh(wayland_state: *mut libc::c_void) {
    // NOTE
    // This is safe because it's way back up the stack where we can't access it.
    //
    // The moment that stack is accessible this pointer will be lost.
    //
    // The only way it's unsafe is if we destructure `WaylandState`,
    // which we can't do because it's borrowed.
    let _wayland_state = unsafe { &mut *(wayland_state as *mut WaylandState) };
    NEXT_LUA.with(|new_lua_check| {
        if new_lua_check.get() {
            new_lua_check.set(false);
            let awesome = env::args().next().unwrap();
            let args: Vec<_> = env::args().skip(1).collect();
            let err = Command::new(awesome)
                .args(args.as_slice())
                .exec();
            error!("error: {:?}", err);
            panic!("Could not restart Awesome");
        }
    });
}

fn main() {
    let mut opts = getopts::Options::new();
    let matches = match opts.optflag("", "version", "show version information")
                            .parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f.to_string());
            exit(1);
        }
    };
    if matches.opt_present("version") {
        if !GIT_VERSION.is_empty() {
            println!("Way Cooler {} @ {}", VERSION, GIT_VERSION);
        } else {
            println!("Way Cooler {}", VERSION);
        }
        return
    }
    init_logs();
    let sig_action = SigAction::new(SigHandler::Handler(sig_handle),
                                    SaFlags::empty(),
                                    SigSet::empty());
    unsafe {
        signal::sigaction(signal::SIGINT, &sig_action)
            .expect("Could not set SIGINT catcher");
    }
    lua::init_awesome_libraries();
    init_wayland();
    lua::run_awesome();
}

fn init_wayland() {
    let (display, mut event_queue) = match Display::connect_to_env() {
        Ok(res) => res,
        Err(err) => {
            error!("Could not connect to Wayland server. Is it running?");
            error!("{:?}", err);
            exit(1);
        }
    };
    let globals = GlobalManager::new_with_cb(
        display.get_registry().unwrap(),
        global_filter!(
            [
                wl_output::WlOutput,
                wayland_obj::WL_OUTPUT_VERSION,
                wayland_obj::Output::new
            ],
            [
                wl_compositor::WlCompositor,
                wayland_obj::WL_COMPOSITOR_VERSION,
                wayland_obj::wl_compositor_init
            ],
            [
                wl_shm::WlShm,
                wayland_obj::WL_SHM_VERSION,
                wayland_obj::wl_shm_init
            ]
        ),
    );
    event_queue.sync_roundtrip().unwrap();
    let xwm_base_proxy = match globals.instantiate_exact::
    <xdg_wm_base::XdgWmBase>(wayland_obj::XDG_WM_BASE_VERSION) {
        Err(GlobalError::Missing) => {
            error!("Missing xdg_wm_base global (version {})",
                   wayland_obj::XDG_WM_BASE_VERSION);
            error!("Your compositor doesn't support the xdg shell protocol");
            error!("This protocol is necessary for Awesome to function");
            exit(1);
        },
        Err(GlobalError::VersionTooLow(version)) => {
            error!("Got xdg_wm_base version {}, expected version {}",
                   version, wayland_obj::XDG_WM_BASE_VERSION);
            error!("Your compositor doesn't support version {} \
                    of the xdg shell protocol", wayland_obj::XDG_WM_BASE_VERSION);
            error!("Ensure your compositor is up to date");
            exit(1);
        },
        Ok(proxy) => Ok(proxy)
    };
    wayland_obj::xdg_shell_init(xwm_base_proxy, ());
    event_queue.sync_roundtrip().unwrap();
    let mut wayland_state = WaylandState { display, event_queue };
    let display_ptr = wayland_state.display.c_ptr() as *mut wl_display;
    unsafe {
        #[link(name = "wayland_glib_interface", kind = "static")]
        extern "C" {
            fn wayland_glib_interface_init(display: *mut wl_display,
                                           wayland_state: *mut libc::c_void);
        }
        wayland_glib_interface_init(display_ptr,
                                    &mut wayland_state as *mut _ as _);
        ::std::mem::forget(wayland_state);
        ::std::mem::forget(globals);
    }
}

fn setup_awesome_path(lua: &Lua) -> rlua::Result<()> {
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let mut path = package.get::<_, String>("path")?;
    let mut cpath = package.get::<_, String>("cpath")?;

    for mut xdg_data_path in
        env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".into())
                                 .split(':')
                                 .map(PathBuf::from)
    {
        xdg_data_path.push("awesome/lib");
        path.push_str(&format!(";{0}/?.lua;{0}/?/init.lua",
                               xdg_data_path.as_os_str().to_string_lossy()));
        cpath.push_str(&format!(";{}/?.so", xdg_data_path.into_os_string().to_string_lossy()));
    }

    for mut xdg_config_path in env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".into())
                                                          .split(':')
                                                          .map(PathBuf::from)
    {
        xdg_config_path.push("awesome");
        cpath.push_str(&format!(";{}/?.so",
                                xdg_config_path.into_os_string().to_string_lossy()));
    }

    package.set("path", path)?;
    package.set("cpath", cpath)?;

    Ok(())
}

/// Set up global signals value
///
/// We need to store this in Lua, because this make it safer to use.
fn setup_global_signals(lua: &Lua) -> rlua::Result<()> {
    lua.set_named_registry_value(GLOBAL_SIGNALS, lua.create_table()?)
}

/// Sets up the xcb connection and stores it in Lua (for us to access it later)
fn setup_xcb_connection(lua: &Lua) -> rlua::Result<()> {
    let con = match Connection::connect(None) {
        Err(err) => {
            error!("Way Cooler requires XWayland in order to function");
            error!("However, xcb could not connect to it. Is it running?");
            error!("{:?}", err);
            panic!("Could not connect to XWayland instance");
        }
        Ok(con) => con.0
    };
    // Tell xcb we are using the xkb extension
    match xkb::use_extension(&con, 1, 0).get_reply() {
        Ok(r) => {
            if !r.supported() {
                panic!("xkb-1.0 is not supported");
            }
        }
        Err(err) => {
            panic!("Could not get xkb extension supported version {:?}", err);
        }
    }
    lua.set_named_registry_value(XCB_CONNECTION_HANDLE,
                                  LightUserData(con.get_raw_conn() as _))?;
    mem::forget(con);
    Ok(())
}

/// Formats the log strings properly
fn log_format(buf: &mut env_logger::fmt::Formatter, record: &log::Record) -> Result<(), io::Error> {
    let color = match record.level() {
        Level::Info => "",
        Level::Trace => "\x1B[37m",
        Level::Debug => "\x1B[44m",
        Level::Warn => "\x1B[33m",
        Level::Error => "\x1B[31m"
    };
    let mut module_path = record.module_path().unwrap_or("?");
    if let Some(index) = module_path.find("way_cooler::") {
        let index = index + "way_cooler::".len();
        module_path = &module_path[index..];
    }
    writeln!(buf, "{} {} [{}] \x1B[37m{}:{}\x1B[0m{0} {} \x1B[0m",
            color,
            record.level(),
            module_path,
            record.file().unwrap_or("?"),
            record.line().unwrap_or(0),
            record.args())
}

fn init_logs() {
    let env = env_logger::Env::default()
        .filter_or("WAY_COOLER_LOG", "trace");
    env_logger::Builder::from_env(env)
        .format(log_format)
        .init();
    info!("Logger initialized");
}

/// Handler for SIGINT signal
extern "C" fn sig_handle(_: nix::libc::c_int) {
    lua::terminate();
    exit(130);
}
<commit_msg>Better wayland connection error reporting for awesome<commit_after>//! Awesome compatibility modules

#![cfg_attr(test, deny(bad_style,
       const_err,
       dead_code,
       improper_ctypes,
       legacy_directory_ownership,
       non_shorthand_field_patterns,
       no_mangle_generic_items,
       overflowing_literals,
       path_statements ,
       patterns_in_fns_without_body,
       plugin_as_library,
       private_in_public,
       private_no_mangle_fns,
       private_no_mangle_statics,
       safe_extern_statics,
       unconditional_recursion,
       unions_with_drop_fields,
       unused,
       unused_allocation,
       unused_comparisons,
       unused_parens,
       while_true))]

// Allowed by default
#![cfg_attr(test, deny(missing_docs,
       trivial_numeric_casts,
       unused_extern_crates,
       unused_import_braces,
       unused_qualifications))]

#[macro_use]
extern crate bitflags;
extern crate cairo;
extern crate cairo_sys;
extern crate env_logger;
extern crate exec;
extern crate getopts;
extern crate gdk_pixbuf;
extern crate glib;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate nix;
extern crate libc;
extern crate rlua;
extern crate tempfile;
extern crate xcb;
#[macro_use]
extern crate wayland_client;
extern crate wayland_sys;

// TODO remove
extern crate wlroots;

#[macro_use]
mod macros;
mod objects;
mod common;
mod wayland_obj;
mod awesome;
mod keygrabber;
mod mousegrabber;
mod root;
mod lua;
mod wayland_protocols;

use std::{env, mem, path::PathBuf, process::exit, io::{self, Write}};

use exec::Command;
use rlua::{LightUserData, Lua, Table};
use log::Level;
use nix::sys::signal::{self, SaFlags, SigAction, SigHandler, SigSet};
use xcb::{xkb, Connection};
use wayland_client::{Display, GlobalManager, EventQueue, GlobalError, ConnectError};
use wayland_client::protocol::{wl_compositor, wl_shm, wl_output, wl_display::RequestsTrait};
use wayland_client::sys::client::wl_display;

use self::lua::{LUA, NEXT_LUA};
use wayland_protocols::xdg_shell::xdg_wm_base;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const GIT_VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/git-version.txt"));
pub const GLOBAL_SIGNALS: &'static str = "__awesome_global_signals";
pub const XCB_CONNECTION_HANDLE: &'static str = "__xcb_connection";

/// The state passed into C to store it during the glib loop.
///
/// It's passed back to us when Awesome needs a refresh so we can
/// construct any Wayland objects.
#[repr(C)]
struct WaylandState {
    pub display: Display,
    pub event_queue: EventQueue
}

/// Called from `wayland_glib_interface.c` after every call back into the
/// wayland event loop.
///
/// This restarts the Lua thread if there is a new one pending
#[no_mangle]
pub extern "C" fn awesome_refresh(wayland_state: *mut libc::c_void) {
    // NOTE
    // This is safe because it's way back up the stack where we can't access it.
    //
    // The moment that stack is accessible this pointer will be lost.
    //
    // The only way it's unsafe is if we destructure `WaylandState`,
    // which we can't do because it's borrowed.
    let _wayland_state = unsafe { &mut *(wayland_state as *mut WaylandState) };
    NEXT_LUA.with(|new_lua_check| {
        if new_lua_check.get() {
            new_lua_check.set(false);
            let awesome = env::args().next().unwrap();
            let args: Vec<_> = env::args().skip(1).collect();
            let err = Command::new(awesome)
                .args(args.as_slice())
                .exec();
            error!("error: {:?}", err);
            panic!("Could not restart Awesome");
        }
    });
}

fn main() {
    let mut opts = getopts::Options::new();
    let matches = match opts.optflag("", "version", "show version information")
                            .parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f.to_string());
            exit(1);
        }
    };
    if matches.opt_present("version") {
        if !GIT_VERSION.is_empty() {
            println!("Way Cooler {} @ {}", VERSION, GIT_VERSION);
        } else {
            println!("Way Cooler {}", VERSION);
        }
        return
    }
    init_logs();
    let sig_action = SigAction::new(SigHandler::Handler(sig_handle),
                                    SaFlags::empty(),
                                    SigSet::empty());
    unsafe {
        signal::sigaction(signal::SIGINT, &sig_action)
            .expect("Could not set SIGINT catcher");
    }
    lua::init_awesome_libraries();
    init_wayland();
    lua::run_awesome();
}

fn init_wayland() {
    let (display, mut event_queue) = match Display::connect_to_env() {
        Ok(res) => res,
        Err(err) => {
            match err {
                ConnectError::NoWaylandLib =>
                    error!("Could not find Wayland library, is it installed and in PATH?"),
                ConnectError::NoCompositorListening => {
                    error!("Could not connect to Wayland server. Is it running?");
                    error!("WAYLAND_DISPLAY={}", env::var("WAYLAND_DISPLAY")
                           .unwrap_or_else(|_| "".into()));
                },
                ConnectError::InvalidName =>
                    error!("Invalid socket name provided")
            }
            exit(1);
        }
    };
    let globals = GlobalManager::new_with_cb(
        display.get_registry().unwrap(),
        global_filter!(
            [
                wl_output::WlOutput,
                wayland_obj::WL_OUTPUT_VERSION,
                wayland_obj::Output::new
            ],
            [
                wl_compositor::WlCompositor,
                wayland_obj::WL_COMPOSITOR_VERSION,
                wayland_obj::wl_compositor_init
            ],
            [
                wl_shm::WlShm,
                wayland_obj::WL_SHM_VERSION,
                wayland_obj::wl_shm_init
            ]
        ),
    );
    event_queue.sync_roundtrip().unwrap();
    let xwm_base_proxy = match globals.instantiate_exact::
    <xdg_wm_base::XdgWmBase>(wayland_obj::XDG_WM_BASE_VERSION) {
        Err(GlobalError::Missing) => {
            error!("Missing xdg_wm_base global (version {})",
                   wayland_obj::XDG_WM_BASE_VERSION);
            error!("Your compositor doesn't support the xdg shell protocol");
            error!("This protocol is necessary for Awesome to function");
            exit(1);
        },
        Err(GlobalError::VersionTooLow(version)) => {
            error!("Got xdg_wm_base version {}, expected version {}",
                   version, wayland_obj::XDG_WM_BASE_VERSION);
            error!("Your compositor doesn't support version {} \
                    of the xdg shell protocol", wayland_obj::XDG_WM_BASE_VERSION);
            error!("Ensure your compositor is up to date");
            exit(1);
        },
        Ok(proxy) => Ok(proxy)
    };
    wayland_obj::xdg_shell_init(xwm_base_proxy, ());
    event_queue.sync_roundtrip().unwrap();
    let mut wayland_state = WaylandState { display, event_queue };
    let display_ptr = wayland_state.display.c_ptr() as *mut wl_display;
    unsafe {
        #[link(name = "wayland_glib_interface", kind = "static")]
        extern "C" {
            fn wayland_glib_interface_init(display: *mut wl_display,
                                           wayland_state: *mut libc::c_void);
        }
        wayland_glib_interface_init(display_ptr,
                                    &mut wayland_state as *mut _ as _);
        ::std::mem::forget(wayland_state);
        ::std::mem::forget(globals);
    }
}

fn setup_awesome_path(lua: &Lua) -> rlua::Result<()> {
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let mut path = package.get::<_, String>("path")?;
    let mut cpath = package.get::<_, String>("cpath")?;

    for mut xdg_data_path in
        env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".into())
                                 .split(':')
                                 .map(PathBuf::from)
    {
        xdg_data_path.push("awesome/lib");
        path.push_str(&format!(";{0}/?.lua;{0}/?/init.lua",
                               xdg_data_path.as_os_str().to_string_lossy()));
        cpath.push_str(&format!(";{}/?.so", xdg_data_path.into_os_string().to_string_lossy()));
    }

    for mut xdg_config_path in env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".into())
                                                          .split(':')
                                                          .map(PathBuf::from)
    {
        xdg_config_path.push("awesome");
        cpath.push_str(&format!(";{}/?.so",
                                xdg_config_path.into_os_string().to_string_lossy()));
    }

    package.set("path", path)?;
    package.set("cpath", cpath)?;

    Ok(())
}

/// Set up global signals value
///
/// We need to store this in Lua, because this make it safer to use.
fn setup_global_signals(lua: &Lua) -> rlua::Result<()> {
    lua.set_named_registry_value(GLOBAL_SIGNALS, lua.create_table()?)
}

/// Sets up the xcb connection and stores it in Lua (for us to access it later)
fn setup_xcb_connection(lua: &Lua) -> rlua::Result<()> {
    let con = match Connection::connect(None) {
        Err(err) => {
            error!("Way Cooler requires XWayland in order to function");
            error!("However, xcb could not connect to it. Is it running?");
            error!("{:?}", err);
            panic!("Could not connect to XWayland instance");
        }
        Ok(con) => con.0
    };
    // Tell xcb we are using the xkb extension
    match xkb::use_extension(&con, 1, 0).get_reply() {
        Ok(r) => {
            if !r.supported() {
                panic!("xkb-1.0 is not supported");
            }
        }
        Err(err) => {
            panic!("Could not get xkb extension supported version {:?}", err);
        }
    }
    lua.set_named_registry_value(XCB_CONNECTION_HANDLE,
                                  LightUserData(con.get_raw_conn() as _))?;
    mem::forget(con);
    Ok(())
}

/// Formats the log strings properly
fn log_format(buf: &mut env_logger::fmt::Formatter, record: &log::Record) -> Result<(), io::Error> {
    let color = match record.level() {
        Level::Info => "",
        Level::Trace => "\x1B[37m",
        Level::Debug => "\x1B[44m",
        Level::Warn => "\x1B[33m",
        Level::Error => "\x1B[31m"
    };
    let mut module_path = record.module_path().unwrap_or("?");
    if let Some(index) = module_path.find("way_cooler::") {
        let index = index + "way_cooler::".len();
        module_path = &module_path[index..];
    }
    writeln!(buf, "{} {} [{}] \x1B[37m{}:{}\x1B[0m{0} {} \x1B[0m",
            color,
            record.level(),
            module_path,
            record.file().unwrap_or("?"),
            record.line().unwrap_or(0),
            record.args())
}

fn init_logs() {
    let env = env_logger::Env::default()
        .filter_or("WAY_COOLER_LOG", "trace");
    env_logger::Builder::from_env(env)
        .format(log_format)
        .init();
    info!("Logger initialized");
}

/// Handler for SIGINT signal
extern "C" fn sig_handle(_: nix::libc::c_int) {
    lua::terminate();
    exit(130);
}
<|endoftext|>


<commit_before>extern crate rgmk;

use rgmk::{ChunkContent, StringTable};
use std::path::Path;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};

fn dump_strings(table: &StringTable) {
    for string in &table.strings {
        println!("{}", string)
    }
}

fn rebuild_from_strings<P: AsRef<Path>>(path: P, table_offset: u32) -> io::Result<StringTable> {
    let file = try!(File::open(path));
    let reader = BufReader::new(file);
    let mut offsets = Vec::new();
    let mut strings = Vec::new();
    for line in reader.lines().map(|l| l.unwrap()) {
        strings.push(line);
    }
    let count = strings.len();
    // number_of_strings_value + offsets
    let offsets_len: u32 = 4 + (4 * count as u32);
    let mut offset: u32 = table_offset;
    for i in 0..count {
        offsets.push(offsets_len + offset);
        // length_value + characters + null byte
        offset += 4 + strings[i].len() as u32 + 1;
    }
    Ok(StringTable {
        offsets: offsets,
        strings: strings,
    })
}

fn main() {
    let mut args = std::env::args_os().skip(1);
    let mode = args.next().expect("Expected dump/rebuild as 1st argument");
    let mode = mode.into_string().unwrap();
    let path = args.next().expect("Expected path as 2nd argument");
    let mut root = rgmk::load_from_file(&path).unwrap();
    let mut offset = rgmk::CHUNK_HEADER_LEN as i32;
    if let ChunkContent::Form(ref mut chunks) = root.content {
        let mut insert_at = 0;
        let mut table_overw = None;
        for (i, chunk) in chunks.iter_mut().enumerate() {
            offset += rgmk::CHUNK_HEADER_LEN as i32;
            if let ChunkContent::StringTable(ref table) = chunk.content {
                match &mode[..] {
                    "dump" => {
                        dump_strings(&table);
                    }
                    "rebuild" => {
                        let strings_path = args.next()
                                               .expect("Expected strings path as 3nd argument");
                        let table = rebuild_from_strings(&strings_path, offset as u32).unwrap();
                        insert_at = i;
                        table_overw = Some(table.clone());
                    }
                    _ => {
                        println!("Invalid mode \"{}\"", mode);
                        return;
                    }
                }
            }
            offset += chunk.size;
        }
        chunks[insert_at].content = ChunkContent::StringTable(table_overw.unwrap());
    }
    root.save_to_file(path).unwrap();
}
<commit_msg>Fix dump tool<commit_after>extern crate rgmk;

use rgmk::{ChunkContent, StringTable};
use std::path::Path;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};

fn dump_strings(table: &StringTable) {
    for string in &table.strings {
        println!("{}", string)
    }
}

fn rebuild_from_strings<P: AsRef<Path>>(path: P, table_offset: u32) -> io::Result<StringTable> {
    let file = try!(File::open(path));
    let reader = BufReader::new(file);
    let mut offsets = Vec::new();
    let mut strings = Vec::new();
    for line in reader.lines().map(|l| l.unwrap()) {
        strings.push(line);
    }
    let count = strings.len();
    // number_of_strings_value + offsets
    let offsets_len: u32 = 4 + (4 * count as u32);
    let mut offset: u32 = table_offset;
    for i in 0..count {
        offsets.push(offsets_len + offset);
        // length_value + characters + null byte
        offset += 4 + strings[i].len() as u32 + 1;
    }
    Ok(StringTable {
        offsets: offsets,
        strings: strings,
    })
}

fn main() {
    let mut args = std::env::args_os().skip(1);
    let mode = args.next().expect("Expected dump/rebuild as 1st argument");
    let mode = mode.into_string().unwrap();
    let path = args.next().expect("Expected path as 2nd argument");
    let mut root = rgmk::load_from_file(&path).unwrap();
    let mut offset = rgmk::CHUNK_HEADER_LEN as i32;
    if let ChunkContent::Form(ref mut chunks) = root.content {
        let mut insert_at = 0;
        let mut table_overw = None;
        for (i, chunk) in chunks.iter_mut().enumerate() {
            offset += rgmk::CHUNK_HEADER_LEN as i32;
            if let ChunkContent::StringTable(ref table) = chunk.content {
                match &mode[..] {
                    "dump" => {
                        dump_strings(&table);
                        return;
                    }
                    "rebuild" => {
                        let strings_path = args.next()
                                               .expect("Expected strings path as 3nd argument");
                        let table = rebuild_from_strings(&strings_path, offset as u32).unwrap();
                        insert_at = i;
                        table_overw = Some(table.clone());
                    }
                    _ => {
                        println!("Invalid mode \"{}\"", mode);
                        return;
                    }
                }
            }
            offset += chunk.size;
        }
        chunks[insert_at].content = ChunkContent::StringTable(table_overw.unwrap());
    }
    root.save_to_file(path).unwrap();
}
<|endoftext|>


<commit_before>use error::*;
use super::Command;

use constant::*;
use lib::io::*;
use lib::setting::*;
use std::collections::BTreeSet;
use std::path::{MAIN_SEPARATOR, Path};

pub struct Ignore {
    pub command: Option<String>,
    pub paths  : Vec<String>,
}

impl Command for Ignore {
    fn validation(&self) -> bool { true }

    fn usage(&self) -> Usage {
        return Usage::new(UsageKind::Ignore);
    }

    fn main(&self) -> Result<(), CliError> {
        match self.command {
            Some(ref c) => match c.as_ref() {
                "add"     => add(self),
                "remove"  => remove(self),
                "current" => ignore_current_files(),
                "clear"   => clear_ignore_file(),
                _         => Err(From::from(self.usage())),
            },
            None => Err(From::from(self.usage())),
        }
    }
}

fn append_prefix_if_need(path: &String) -> String {
    let prefix       = format!(".{}", MAIN_SEPARATOR);
    let prefix: &str = prefix.as_ref();
    format!("{}{}", if path.starts_with(prefix) { "" } else { prefix }, path)
}

fn add(ignore: &Ignore) -> Result<(), CliError> {
    let paths = &ignore.paths;

    if paths.len() == 0 {
        return Err(From::from(ignore.usage()));
    }

    print_with_tag(0, Tag::Execution, format!("Confirm files to be added to \"{}\"", IGNORE_FILE_NAME));

    let ignores_to_be_added = paths
        .iter()
        .map(append_prefix_if_need)
        .filter(|p| Path::new(p).is_file())
        .collect::<BTreeSet<String>>();

    for file in &ignores_to_be_added {
        print_with_tag(1, Tag::Info, format!("\"{}\" will be ignored", file));
    }

    let original_ignores = try!(read_ignore_file());

    let new_ignores = original_ignores
        .union(&ignores_to_be_added)
        .fold(String::new(), |c, ref f| c + f + "\n");

    print_with_tag(0, Tag::Execution, format!("Recreate \"{}\" file", IGNORE_FILE_NAME));

    try!(create_ignore_file(new_ignores));

    Ok(())
}

fn remove(ignore: &Ignore) -> Result<(), CliError> {
    let paths = &ignore.paths;
    if paths.len() == 0 {
        return Err(From::from(ignore.usage()));
    }
    println!("do remove command with {:?}", paths);

    Ok(())
}

fn ignore_current_files() -> Result<(), CliError> {
    println!("do current command");

    Ok(())
}

fn clear_ignore_file() -> Result<(), CliError> {
    println!("do clear command");

    Ok(())
}
<commit_msg>[Feature] Implement "ignore remove"<commit_after>use error::*;
use super::Command;

use constant::*;
use lib::io::*;
use lib::setting::*;
use std::collections::BTreeSet;
use std::path::{MAIN_SEPARATOR, Path};

pub struct Ignore {
    pub command: Option<String>,
    pub paths  : Vec<String>,
}

impl Command for Ignore {
    fn validation(&self) -> bool { true }

    fn usage(&self) -> Usage {
        return Usage::new(UsageKind::Ignore);
    }

    fn main(&self) -> Result<(), CliError> {
        match self.command {
            Some(ref c) => match c.as_ref() {
                "add"     => add(self),
                "remove"  => remove(self),
                "current" => ignore_current_files(),
                "clear"   => clear_ignore_file(),
                _         => Err(From::from(self.usage())),
            },
            None => Err(From::from(self.usage())),
        }
    }
}

fn append_prefix_if_need(path: &String) -> String {
    let prefix       = format!(".{}", MAIN_SEPARATOR);
    let prefix: &str = prefix.as_ref();
    format!("{}{}", if path.starts_with(prefix) { "" } else { prefix }, path)
}

fn add(ignore: &Ignore) -> Result<(), CliError> {
    let paths = &ignore.paths;

    if paths.len() == 0 {
        return Err(From::from(ignore.usage()));
    }

    print_with_tag(0, Tag::Execution, format!("Confirm files to be added to \"{}\"", IGNORE_FILE_NAME));

    let ignores_to_be_added = paths
        .iter()
        .map(append_prefix_if_need)
        .filter(|p| Path::new(p).is_file())
        .collect::<BTreeSet<String>>();

    for file in &ignores_to_be_added {
        print_with_tag(1, Tag::Info, format!("\"{}\" will be ignored", file));
    }

    let original_ignores = try!(read_ignore_file());

    let new_ignores = original_ignores
        .union(&ignores_to_be_added)
        .fold(String::new(), |c, ref f| c + f + "\n");

    print_with_tag(0, Tag::Execution, format!("Recreate \"{}\" file", IGNORE_FILE_NAME));

    try!(create_ignore_file(new_ignores));

    Ok(())
}

fn remove(ignore: &Ignore) -> Result<(), CliError> {
    let paths = &ignore.paths;

    if paths.len() == 0 {
        return Err(From::from(ignore.usage()));
    }

    let ignores_to_be_removed = paths
        .iter()
        .map(append_prefix_if_need)
        .collect::<BTreeSet<String>>();

    for file in &ignores_to_be_removed {
        print_with_tag(1, Tag::Info, format!("\"{}\" will not be ignored", file));
    }

    let original_ignores = try!(read_ignore_file());

    let new_ignores = original_ignores
        .difference(&ignores_to_be_removed)
        .fold(String::new(), |c, ref f| c + f + "\n");

    print_with_tag(0, Tag::Execution, format!("Recreate \"{}\" file", IGNORE_FILE_NAME));

    try!(create_ignore_file(new_ignores));

    Ok(())
}

fn ignore_current_files() -> Result<(), CliError> {
    println!("do current command");

    Ok(())
}

fn clear_ignore_file() -> Result<(), CliError> {
    println!("do clear command");

    Ok(())
}
<|endoftext|>


<commit_before>/// Components are types that associate an entity with a set of properties
/// for each type a list of all its instances is kept so they can be iterated
/// over efficiently by systems.
///
/// Whenever a new component is created it is added to a list.
///
/// component! { physics, body: physics.RigidBody, physics_id: physics.ID }
///

use std::collections::{ HashSet, HashMap };
use std::any::{ Any, TypeId };
use shared_mutex::{ SharedMutex, SharedMutexReadGuard, SharedMutexWriteGuard };

pub mod example;

#[derive(Clone)]
pub struct Component {
	pub name: TypeId,
	pub get_component_list: fn () -> Box<Any + 'static>
}

lazy_static! {
	pub static ref COMPONENTS: SharedMutex<HashMap<TypeId, Component>> = SharedMutex::new(HashMap::new());
}

pub fn register(component : Component) {
	let mut components = COMPONENTS.write().expect("COMPONENTS lock corrupted");
	components.insert(component.name, component);
}

pub fn get_components_lock<'mutex, T>(id : TypeId) -> SharedMutexReadGuard<'mutex, T> where T : 'static {
	let components = COMPONENTS.read().expect("COMPONENTS lock corrupted");
	let component = components.get(&id).expect("Unknown component type requested");
	let component_list_any = (component.get_component_list)();
	let component_list : &'static SharedMutex<T> = *component_list_any.downcast_ref().expect("Invalid type for components list");
	return component_list.read().expect("Component LIST lock corrupted");
}

#[macro_export]
macro_rules! component {
	( $component_name:ident , $( $name:ident : $field:ty ),* ) => (
		pub mod $component_name {
			use shared_mutex::{ SharedMutex, SharedMutexWriteGuard };
			use entity_rust::entities::{ ComponentList, EntityID };

			#[derive(Default)]
			pub struct Component {
				pub $($name : $field),*
			}

			lazy_static! {
				pub static ref LIST: SharedMutex<ComponentList<Component>> = SharedMutex::new(Vec::new());
			}

			pub fn create(mut list: SharedMutexWriteGuard<ComponentList<Component>>, entity: EntityID, $($name : $field),*) {
				let c = Component { $($name : $name),* };
				list.push((entity,c));
			}
		}
	)
}
<commit_msg>it should implement Any instead of Reflect<commit_after>/// Components are types that associate an entity with a set of properties
/// for each type a list of all its instances is kept so they can be iterated
/// over efficiently by systems.
///
/// Whenever a new component is created it is added to a list.
///
/// component! { physics, body: physics.RigidBody, physics_id: physics.ID }
///

use std::collections::{ HashSet, HashMap };
use std::any::{ Any, TypeId };
use shared_mutex::{ SharedMutex, SharedMutexReadGuard, SharedMutexWriteGuard };

pub mod example;

#[derive(Clone)]
pub struct Component {
	pub name: TypeId,
	pub get_component_list: fn () -> Box<Any + 'static>
}

lazy_static! {
	pub static ref COMPONENTS: SharedMutex<HashMap<TypeId, Component>> = SharedMutex::new(HashMap::new());
}

pub fn register(component : Component) {
	let mut components = COMPONENTS.write().expect("COMPONENTS lock corrupted");
	components.insert(component.name, component);
}

pub fn get_components_lock<'mutex, T>(id : TypeId) -> SharedMutexReadGuard<'mutex, T> where T : Any {
	let components = COMPONENTS.read().expect("COMPONENTS lock corrupted");
	let component = components.get(&id).expect("Unknown component type requested");
	let component_list_any = (component.get_component_list)();
	let component_list : &'static SharedMutex<T> = *component_list_any.downcast_ref().expect("Invalid type for components list");
	return component_list.read().expect("Component LIST lock corrupted");
}

#[macro_export]
macro_rules! component {
	( $component_name:ident , $( $name:ident : $field:ty ),* ) => (
		pub mod $component_name {
			use shared_mutex::{ SharedMutex, SharedMutexWriteGuard };
			use entity_rust::entities::{ ComponentList, EntityID };

			#[derive(Default)]
			pub struct Component {
				pub $($name : $field),*
			}

			lazy_static! {
				pub static ref LIST: SharedMutex<ComponentList<Component>> = SharedMutex::new(Vec::new());
			}

			pub fn create(mut list: SharedMutexWriteGuard<ComponentList<Component>>, entity: EntityID, $($name : $field),*) {
				let c = Component { $($name : $name),* };
				list.push((entity,c));
			}
		}
	)
}
<|endoftext|>


<commit_before>use crate::{
    message::BasicReturnMessage, pinky_swear::PinkySwear, returned_messages::ReturnedMessages,
};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

#[derive(Debug)]
pub struct PublisherConfirm {
    inner: Option<PinkySwear<Confirmation>>,
    returned_messages: ReturnedMessages,
    used: bool,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Confirmation {
    Ack,
    Nack(BasicReturnMessage),
    NotRequested,
}

impl PublisherConfirm {
    pub(crate) fn new(
        inner: PinkySwear<Confirmation>,
        returned_messages: ReturnedMessages,
    ) -> Self {
        Self {
            inner: Some(inner),
            returned_messages,
            used: false,
        }
    }

    pub(crate) fn not_requested(returned_messages: ReturnedMessages) -> Self {
        Self {
            inner: Some(PinkySwear::new_with_data(Confirmation::NotRequested)),
            returned_messages,
            used: false,
        }
    }

    pub fn try_wait(&mut self) -> Option<Confirmation> {
        let confirmation = self.inner.as_ref().expect("inner should only be None after Drop").try_wait()?;
        self.used = true;
        Some(confirmation)
    }

    pub fn wait(&mut self) -> Confirmation {
        self.used = true;
        self.inner.as_ref().expect("inner should only be None after Drop").wait()
    }
}

impl Future for PublisherConfirm {
    type Output = Confirmation;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.as_mut();
        let res = Pin::new(&mut this.inner.as_mut().expect("inner should only be None ater Drop")).poll(cx);
        if res.is_ready() {
            this.used = true;
        }
        res
    }
}

impl Drop for PublisherConfirm {
    fn drop(&mut self) {
        if !self.used {
            if let Some(promise) = self.inner.take() {
                self.returned_messages.register_dropped_confirm(promise);
            }
        }
    }
}
<commit_msg>rustfmt<commit_after>use crate::{
    message::BasicReturnMessage, pinky_swear::PinkySwear, returned_messages::ReturnedMessages,
};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

#[derive(Debug)]
pub struct PublisherConfirm {
    inner: Option<PinkySwear<Confirmation>>,
    returned_messages: ReturnedMessages,
    used: bool,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Confirmation {
    Ack,
    Nack(BasicReturnMessage),
    NotRequested,
}

impl PublisherConfirm {
    pub(crate) fn new(
        inner: PinkySwear<Confirmation>,
        returned_messages: ReturnedMessages,
    ) -> Self {
        Self {
            inner: Some(inner),
            returned_messages,
            used: false,
        }
    }

    pub(crate) fn not_requested(returned_messages: ReturnedMessages) -> Self {
        Self {
            inner: Some(PinkySwear::new_with_data(Confirmation::NotRequested)),
            returned_messages,
            used: false,
        }
    }

    pub fn try_wait(&mut self) -> Option<Confirmation> {
        let confirmation = self
            .inner
            .as_ref()
            .expect("inner should only be None after Drop")
            .try_wait()?;
        self.used = true;
        Some(confirmation)
    }

    pub fn wait(&mut self) -> Confirmation {
        self.used = true;
        self.inner
            .as_ref()
            .expect("inner should only be None after Drop")
            .wait()
    }
}

impl Future for PublisherConfirm {
    type Output = Confirmation;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.as_mut();
        let res = Pin::new(
            &mut this
                .inner
                .as_mut()
                .expect("inner should only be None ater Drop"),
        )
        .poll(cx);
        if res.is_ready() {
            this.used = true;
        }
        res
    }
}

impl Drop for PublisherConfirm {
    fn drop(&mut self) {
        if !self.used {
            if let Some(promise) = self.inner.take() {
                self.returned_messages.register_dropped_confirm(promise);
            }
        }
    }
}
<|endoftext|>