<commit_before>//! Types for the Global Descriptor Table and segment selectors.

use crate::structures::tss::TaskStateSegment;
use crate::PrivilegeLevel;
use bit_field::BitField;
use bitflags::bitflags;
use core::fmt;

/// Specifies which element to load into a segment from
/// descriptor tables (i.e., is a index to LDT or GDT table
/// with some additional flags).
///
/// See Intel 3a, Section 3.4.2 "Segment Selectors"
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct SegmentSelector(pub u16);

impl SegmentSelector {
    /// Creates a new SegmentSelector
    ///
    /// # Arguments
    ///  * `index`: index in GDT or LDT array (not the offset)
    ///  * `rpl`: the requested privilege level
    pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector {
        SegmentSelector(index << 3 | (rpl as u16))
    }

    /// Returns the GDT index.
    pub fn index(&self) -> u16 {
        self.0 >> 3
    }

    /// Returns the requested privilege level.
    pub fn rpl(&self) -> PrivilegeLevel {
        PrivilegeLevel::from_u16(self.0.get_bits(0..2))
    }
}

impl fmt::Debug for SegmentSelector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut s = f.debug_struct("SegmentSelector");
        s.field("index", &self.index());
        s.field("rpl", &self.rpl());
        s.finish()
    }
}

/// A 64-bit mode global descriptor table (GDT).
///
/// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for
/// switching between user and kernel mode or for loading a TSS.
///
/// The GDT has a fixed size of 8 entries, trying to add more entries will panic.
#[derive(Debug, Clone)]
pub struct GlobalDescriptorTable {
    table: [u64; 8],
    next_free: usize,
}

impl GlobalDescriptorTable {
    /// Creates an empty GDT.
    pub fn new() -> GlobalDescriptorTable {
        GlobalDescriptorTable {
            table: [0; 8],
            next_free: 1,
        }
    }

    /// Adds the given segment descriptor to the GDT, returning the segment selector.
    ///
    /// Panics if the GDT has no free entries left.
    pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
        let index = match entry {
            Descriptor::UserSegment(value) => self.push(value),
            Descriptor::SystemSegment(value_low, value_high) => {
                let index = self.push(value_low);
                self.push(value_high);
                index
            }
        };
        SegmentSelector::new(index as u16, PrivilegeLevel::Ring0)
    }

    /// Loads the GDT in the CPU using the `lgdt` instruction.
    #[cfg(target_arch = "x86_64")]
    pub fn load(&'static self) {
        use crate::instructions::tables::{lgdt, DescriptorTablePointer};
        use core::mem::size_of;

        let ptr = DescriptorTablePointer {
            base: self.table.as_ptr() as u64,
            limit: (self.table.len() * size_of::<u64>() - 1) as u16,
        };

        unsafe { lgdt(&ptr) };
    }

    fn push(&mut self, value: u64) -> usize {
        if self.next_free < self.table.len() {
            let index = self.next_free;
            self.table[index] = value;
            self.next_free += 1;
            index
        } else {
            panic!("GDT full");
        }
    }
}

/// A 64-bit mode segment descriptor.
///
/// Segmentation is no longer supported in 64-bit mode, so most of the descriptor
/// contents are ignored.
#[derive(Debug, Clone)]
pub enum Descriptor {
    /// Descriptor for a code or data segment.
    ///
    /// Since segmentation is no longer supported in 64-bit mode, almost all of
    /// code and data descriptors is ignored. Only some flags are still used.
    UserSegment(u64),
    /// A system segment descriptor such as a LDT or TSS descriptor.
    SystemSegment(u64, u64),
}

bitflags! {
    /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types.
    pub struct DescriptorFlags: u64 {
        /// Marks a code segment as “conforming”. This influences the privilege checks that
        /// occur on control transfers.
        const CONFORMING        = 1 << 42;
        /// This flag must be set for code segments.
        const EXECUTABLE        = 1 << 43;
        /// This flag must be set for user segments (in contrast to system segments).
        const USER_SEGMENT      = 1 << 44;
        /// Must be set for any segment, causes a segment not present exception if not set.
        const PRESENT           = 1 << 47;
        /// Must be set for long mode code segments.
        const LONG_MODE         = 1 << 53;

        /// The DPL for this descriptor is Ring 3
        const DPL_RING_3        = 3 << 45;
    }
}

impl Descriptor {
    /// Creates a segment descriptor for a long mode kernel code segment.
    pub fn kernel_code_segment() -> Descriptor {
        use self::DescriptorFlags as Flags;

        let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::LONG_MODE;
        Descriptor::UserSegment(flags.bits())
    }

    /// Creates a segment descriptor for a long mode ring-3 code segment.
    pub fn user_code_segment() -> Descriptor {
        use self::DescriptorFlags as Flags;

        let flags = Flags::USER_SEGMENT
            | Flags::PRESENT
            | Flags::EXECUTABLE
            | Flags::LONG_MODE
            | Flags::DPL_RING_3;
        Descriptor::UserSegment(flags.bits())
    }

    /// Creates a TSS system descriptor for the given TSS.
    pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
        use self::DescriptorFlags as Flags;
        use core::mem::size_of;

        let ptr = tss as *const _ as u64;

        let mut low = Flags::PRESENT.bits();
        // base
        low.set_bits(16..40, ptr.get_bits(0..24));
        low.set_bits(56..64, ptr.get_bits(24..32));
        // limit (the `-1` in needed since the bound is inclusive)
        low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
        // type (0b1001 = available 64-bit tss)
        low.set_bits(40..44, 0b1001);

        let mut high = 0;
        high.set_bits(0..32, ptr.get_bits(32..64));

        Descriptor::SystemSegment(low, high)
    }
}
<commit_msg>Update GDT docs, add kernel_data_segment function and READ_WRITE flag<commit_after>//! Types for the Global Descriptor Table and segment selectors.

use crate::structures::tss::TaskStateSegment;
use crate::PrivilegeLevel;
use bit_field::BitField;
use bitflags::bitflags;
use core::fmt;

/// Specifies which element to load into a segment from
/// descriptor tables (i.e., is a index to LDT or GDT table
/// with some additional flags).
///
/// See Intel 3a, Section 3.4.2 "Segment Selectors"
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct SegmentSelector(pub u16);

impl SegmentSelector {
    /// Creates a new SegmentSelector
    ///
    /// # Arguments
    ///  * `index`: index in GDT or LDT array (not the offset)
    ///  * `rpl`: the requested privilege level
    pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector {
        SegmentSelector(index << 3 | (rpl as u16))
    }

    /// Returns the GDT index.
    pub fn index(&self) -> u16 {
        self.0 >> 3
    }

    /// Returns the requested privilege level.
    pub fn rpl(&self) -> PrivilegeLevel {
        PrivilegeLevel::from_u16(self.0.get_bits(0..2))
    }
}

impl fmt::Debug for SegmentSelector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut s = f.debug_struct("SegmentSelector");
        s.field("index", &self.index());
        s.field("rpl", &self.rpl());
        s.finish()
    }
}

/// A 64-bit mode global descriptor table (GDT).
///
/// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for
/// switching between user and kernel mode or for loading a TSS.
///
/// The GDT has a fixed size of 8 entries, trying to add more entries will panic.
///
/// You do **not** need to add a null segment descriptor yourself - this is already done
/// internally.
#[derive(Debug, Clone)]
pub struct GlobalDescriptorTable {
    table: [u64; 8],
    next_free: usize,
}

impl GlobalDescriptorTable {
    /// Creates an empty GDT.
    pub fn new() -> GlobalDescriptorTable {
        GlobalDescriptorTable {
            table: [0; 8],
            next_free: 1,
        }
    }

    /// Adds the given segment descriptor to the GDT, returning the segment selector.
    ///
    /// Panics if the GDT has no free entries left.
    pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
        let index = match entry {
            Descriptor::UserSegment(value) => self.push(value),
            Descriptor::SystemSegment(value_low, value_high) => {
                let index = self.push(value_low);
                self.push(value_high);
                index
            }
        };
        SegmentSelector::new(index as u16, PrivilegeLevel::Ring0)
    }

    /// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the
    /// segment registers; you **must** (re)load them yourself using [the appropriate
    /// functions](crate::instructions::segmentation):
    /// [load_ds](crate::instructions::segmentation::load_ds),
    /// [set_cs](crate::instructions::segmentation::set_cs) etc.
    #[cfg(target_arch = "x86_64")]
    pub fn load(&'static self) {
        use crate::instructions::tables::{lgdt, DescriptorTablePointer};
        use core::mem::size_of;

        let ptr = DescriptorTablePointer {
            base: self.table.as_ptr() as u64,
            limit: (self.table.len() * size_of::<u64>() - 1) as u16,
        };

        unsafe { lgdt(&ptr) };
    }

    fn push(&mut self, value: u64) -> usize {
        if self.next_free < self.table.len() {
            let index = self.next_free;
            self.table[index] = value;
            self.next_free += 1;
            index
        } else {
            panic!("GDT full");
        }
    }
}

/// A 64-bit mode segment descriptor.
///
/// Segmentation is no longer supported in 64-bit mode, so most of the descriptor
/// contents are ignored.
#[derive(Debug, Clone)]
pub enum Descriptor {
    /// Descriptor for a code or data segment.
    ///
    /// Since segmentation is no longer supported in 64-bit mode, almost all of
    /// code and data descriptors is ignored. Only some flags are still used.
    UserSegment(u64),
    /// A system segment descriptor such as a LDT or TSS descriptor.
    SystemSegment(u64, u64),
}

bitflags! {
    /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types.
    pub struct DescriptorFlags: u64 {
        /// For code segments, this flag sets the segment as readable. For data segments, this flag
        /// sets the segment as writable.
        const READ_WRITE        = 1 << 41;
        /// Marks a code segment as “conforming”. This influences the privilege checks that
        /// occur on control transfers.
        const CONFORMING        = 1 << 42;
        /// This flag must be set for code segments.
        const EXECUTABLE        = 1 << 43;
        /// This flag must be set for user segments (in contrast to system segments).
        const USER_SEGMENT      = 1 << 44;
        /// Must be set for any segment, causes a segment not present exception if not set.
        const PRESENT           = 1 << 47;
        /// Must be set for long mode code segments.
        const LONG_MODE         = 1 << 53;

        /// The DPL for this descriptor is Ring 3
        const DPL_RING_3        = 3 << 45;
    }
}

impl Descriptor {
    /// Creates a segment descriptor for a long mode kernel code segment.
    pub fn kernel_code_segment() -> Descriptor {
        use self::DescriptorFlags as Flags;

        let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::LONG_MODE;
        Descriptor::UserSegment(flags.bits())
    }

    /// Creates a segment descriptor for a long mode kernel data segment.
    pub fn kernel_data_segment() -> Descriptor {
        use self::DescriptorFlags as Flags;

        let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READ_WRITE;
        Descriptor::UserSegment(flags.bits())
    }

    /// Creates a segment descriptor for a long mode ring-3 code segment.
    pub fn user_code_segment() -> Descriptor {
        use self::DescriptorFlags as Flags;

        let flags = Flags::USER_SEGMENT
            | Flags::PRESENT
            | Flags::EXECUTABLE
            | Flags::LONG_MODE
            | Flags::DPL_RING_3;
        Descriptor::UserSegment(flags.bits())
    }

    /// Creates a TSS system descriptor for the given TSS.
    pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
        use self::DescriptorFlags as Flags;
        use core::mem::size_of;

        let ptr = tss as *const _ as u64;

        let mut low = Flags::PRESENT.bits();
        // base
        low.set_bits(16..40, ptr.get_bits(0..24));
        low.set_bits(56..64, ptr.get_bits(24..32));
        // limit (the `-1` in needed since the bound is inclusive)
        low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
        // type (0b1001 = available 64-bit tss)
        low.set_bits(40..44, 0b1001);

        let mut high = 0;
        high.set_bits(0..32, ptr.get_bits(32..64));

        Descriptor::SystemSegment(low, high)
    }
}
<|endoftext|>


<commit_before>// Extern crates.
use graphics::{ Context, Graphics };
use graphics;
use piston::input::keyboard;
use current::{ Current, CurrentGuard };
use start_piston;
use glium_graphics::GliumGraphics;

// Local crate.
use action;
use settings;
use object;
use object::Object;
use text as text_mod;
use game_state::GameState;
use player::{ Player };
use colors;
use snake::Snake;
use air_bottle::AirBottle;
use bar::Bar;

pub fn current_cam() -> Current<Cam> { unsafe { Current::new() } }
pub fn current_game_state() -> Current<GameState> { unsafe { Current::new() } }
pub fn current_objects() -> Current<Vec<Object>> { unsafe { Current::new() } }
pub fn current_index() -> Current<Index> { unsafe { Current::new() } }
pub fn current_settings() -> Current<Settings> { unsafe { Current::new() } }
pub fn current_player() -> Current<Player> { unsafe { Current::new() } }
pub fn current_snakes() -> Current<Vec<Snake>> { unsafe { Current::new() } }
pub fn current_air_bottles() -> Current<Vec<AirBottle>> { unsafe { Current::new() } }
pub fn current_bars() -> Current<Vec<Bar>> { unsafe { Current::new() } }

pub fn app() {
    let mut cam = Cam([0.0, 0.0]);
    let mut game_state = GameState::Play;
    let mut objects: Vec<Object> = Vec::new();
    let mut index = Index::new();
    let mut settings = Settings::new();
    let mut player = Player::new();
    let mut snakes: Vec<Snake> = Vec::new();
    let mut air_bottles: Vec<AirBottle> = Vec::new();
    let mut bars: Vec<Bar> = Vec::new();

    let cam_guard = CurrentGuard::new(&mut cam);
    let game_state_guard = CurrentGuard::new(&mut game_state);
    let objects_guard = CurrentGuard::new(&mut objects);
    let index_guard = CurrentGuard::new(&mut index);
    let settings_guard = CurrentGuard::new(&mut settings);
    let player_guard = CurrentGuard::new(&mut player);
    let snakes_guard = CurrentGuard::new(&mut snakes);
    let air_bottles_guard = CurrentGuard::new(&mut air_bottles);
    let bars_guard = CurrentGuard::new(&mut bars);

    start();

    drop(cam_guard);
    drop(game_state_guard);
    drop(objects_guard);
    drop(index_guard);
    drop(settings_guard);
    drop(player_guard);
    drop(snakes_guard);
    drop(air_bottles_guard);
    drop(bars_guard);
}

/// The graphics back-end to use for rendering.
#[derive(Debug)]
pub enum GraphicsBackEnd {
    /// Use Gfx to render
    Gfx,
    /// Use OpenGL to render
    OpenGL,
    /// Use Glium to render
    Glium,
}

fn start() {
    use piston::event::{ RenderEvent, UpdateEvent, PressEvent, ReleaseEvent };
    use piston::window::Window;

    let mut back_end = GraphicsBackEnd::Gfx;
    println!("Running with graphics backend {:?}", back_end);
    println!("Use 'S' to swap back-end");

    load();
    for e in start_piston::events().max_fps(120).swap_buffers(false) {
        use piston::input::Key;
        use piston::input::Button::Keyboard;

        if let Some(Keyboard(Key::S)) = e.press_args() {
            back_end = match back_end {
                GraphicsBackEnd::Gfx => {
                    println!("Swapped to OpenGL");
                    GraphicsBackEnd::OpenGL
                }
                GraphicsBackEnd::OpenGL => {
                    println!("Swapped to Glum");
                    GraphicsBackEnd::Glium
                }
                GraphicsBackEnd::Glium => {
                    println!("Swapped to Gfx");
                    GraphicsBackEnd::Gfx
                }
            };
        };
        e.render(|args| {
            match back_end {
                GraphicsBackEnd::Gfx => {
                    {
                        start_piston::render_2d_gfx(Some(settings::WATER_COLOR), |c, g| {
                            render(&c, g)
                        });
                    }
                    {
                        let window = start_piston::current_window();
                        let mut window = window.borrow_mut();
                        window.swap_buffers();
                    }
                }
                GraphicsBackEnd::OpenGL => {
                    {
                        start_piston::render_2d_opengl(Some(settings::WATER_COLOR), |c, g| {
                            render(&c, g)
                        });
                    }
                    {
                        let window = start_piston::current_window();
                        let mut window = window.borrow_mut();
                        window.swap_buffers();
                    }
                }
                GraphicsBackEnd::Glium => {
                    let glium_window = start_piston::current_glium_window();
                    let glium_window = glium_window.borrow();
                    let glium_2d = start_piston::current_glium_2d();
                    let mut glium_2d = glium_2d.borrow_mut();
                    let mut target = glium_window.draw();
                    {
                        let mut g = GliumGraphics::new(&mut *glium_2d, &mut target);
                        graphics::clear(settings::WATER_COLOR, &mut g);
                        let c = Context::new_viewport(args.viewport());
                        render(&c, &mut g);
                    }
                    target.finish();
                }
            };
            start_piston::set_title(start_piston::fps_tick().to_string());
        });
        e.update(|args| {
            update(args.dt);
        });
        if let Some(Keyboard(key)) = e.press_args() {
            key_press(key);
        }
        if let Some(Keyboard(key)) = e.release_args() {
            key_release(key);
        }
    }
}

pub struct Cam(pub [f64; 2]);

impl Cam {
    fn pos(&self) -> [f64; 2] {
        let Cam(pos) = *self;
        pos
    }

    fn set(&mut self, val: [f64; 2]) {
        *self = Cam(val);
    }

    /// Make camera follow position.
    fn follow_pos(&mut self, dt: f64, follow_percentage: f64, pos: [f64; 2]) {
        let camera_pos = self.pos();
        let (dx, dy) = (pos[0] - camera_pos[0], pos[1] - camera_pos[1]);
        let dx = follow_percentage * dt * dx;
        let dy = follow_percentage * dt * dy;
        self.set([camera_pos[0] + dx, camera_pos[1] + dy]);
    }
}

pub struct Index {
    pub player: Option<usize>,
    pub blood_bar: Option<usize>,
    pub air_bar: Option<usize>,
}

impl Index {
    pub fn new() -> Index {
        Index {
            player: None,
            blood_bar: None,
            air_bar: None
        }
    }
}

pub struct Settings {
    // Tells where the surface is.
    pub surface_y: Option<f64>,
    pub camera_follow_percentage: Option<f64>,
}

impl Settings {
    pub fn new() -> Settings {
        Settings {
            surface_y: None,
            camera_follow_percentage: None,
        }
    }
}

pub fn render<G: Graphics>(c: &Context, gl: &mut G) {
    use graphics::*;

    let c = &c.reset();

    // Get camera coordinates.
    let cam_pos = current_cam().pos();
    let cam_x = cam_pos[0];
    let mut cam_y = cam_pos[1];
    cam_y += 0.4;

    // Render surface.
    let surface_y = current_settings().surface_y.unwrap();
    graphics::Rectangle::new(colors::BLUE)
        .draw([-1.0, surface_y - cam_y, 2.0, 0.05],
            &c.draw_state, c.transform, gl);

    // Render objects in layers.
    let cam = &c.trans(-cam_x, -cam_y);
    for i in 0usize..settings::NUMBER_OF_LAYERS {
        for obj in current_objects().iter() {
            if obj.layer == i { obj.render(cam, c, gl); }
        }
    }

    let text_c = c.flip_v();
    let text_c = text_c.zoom(0.0025);
    match *current_game_state() {
        GameState::Win => {
            let pos = settings::YOU_WIN_POS;
            text_mod::text(settings::YOU_WIN_TEXT,
                &graphics::Polygon::new(settings::YOU_WIN_TEXT_COLOR),
                &text_c
                .trans(pos[0], pos[1])
            , gl);
        },
        GameState::Lose => {
            let pos = settings::YOU_LOSE_POS;
            text_mod::text(settings::YOU_LOSE_TEXT,
                &graphics::Polygon::new(settings::YOU_LOSE_TEXT_COLOR),
                &text_c
                .trans(pos[0], pos[1])
            , gl);
        },
        GameState::Play => {

        },
    }
}

pub fn update(dt: f64) {

    fn player_pos() -> [f64; 2] {
        let player_index = current_index().player.unwrap();
        current_objects()[player_index].pos
    }

    fn update_objects(dt: f64) {
        if *current_game_state() != GameState::Play { return; }

        current_player().update(dt);

        // Update states of objects.
        let player_pos = player_pos();
        let mut attack_damage: f64 = 0.0;
        for obj in current_objects().iter_mut() {
            match obj.update(dt, player_pos) {
                action::Action::Passive => {},
                action::Action::Attack(attack) => { attack_damage += attack; },
            }
        }
        // Bite player.
        current_player().bite(attack_damage);

        // Decrease the player's air with time.
        let air = current_player().air;
        current_player().air = air - dt * settings::PLAYER_LOSE_AIR_SPEED;
    }

    update_objects(dt);

    fn fill_air() {
        let player_pos = player_pos();
        let mut air = current_player().air;
        for obj in current_objects().iter_mut() {
            let pos = obj.pos;
            match obj.data {
                object::Data::AirBottle(i) => {
                    let dx = pos[0] - player_pos[0];
                    let dy = pos[1] - player_pos[1];
                    let d = dx * dx + dy * dy;
                    if d <= settings::AIR_BOTTLE_RADIUS * settings::AIR_BOTTLE_RADIUS {
                        let air_bottle = &mut current_air_bottles()[i];
                        air += air_bottle.fill_up;
                        air_bottle.fill_up = 0.0;
                    }
                },
                _ => {}
            }
        }

        air = if air > 1.0 { 1.0 } else { air };
        current_player().air = air;
    }

    fill_air();

    fn win() {
        let player_pos = player_pos();
        // When player reaches surface, win.
        if player_pos[1] >= current_settings().surface_y.unwrap() {
            *current_game_state() = GameState::Win;
            return;
        }
    }

    win();

    fn lose() {
        let player = &*current_player();
        let blood = player.blood;
        let air = player.air;
        if blood < 0.0 || air < 0.0 {
            *current_game_state() = GameState::Lose;
            return;
        }
    }

    lose();

    fn follow_player(dt: f64) {
        // Make camera follow player.
        let follow_percentage = current_settings().camera_follow_percentage.unwrap();
        current_cam().follow_pos(dt, follow_percentage, player_pos());
    }

    follow_player(dt);
}

pub fn load() {
    current_cam().set(settings::INITIAL_CAMERA_POS);
    current_settings().camera_follow_percentage = Some(settings::CAMERA_FOLLOW_PERCENTAGE);
    *current_game_state() = settings::INITIAL_GAME_STATE;

    // Add player.
    current_objects().clear();
    current_objects().push(Object::bar_background());
    current_index().player = Some(0);

    fn add_bars() {
        fn air() -> f64 { current_player().air }
        fn blood() -> f64 { current_player().blood }

        let objects = &mut *current_objects();
        objects.push(Object::bar(
            settings::AIR_BAR_POS,
            "air",
            settings::AIR_BAR_TEXT_COLOR,
            settings::AIR_BAR_BACKGROUND_COLOR,
            settings::AIR_BAR_BAR_COLOR,
            air
        ));
        current_index().air_bar = Some(objects.len() - 1);
        objects.push(Object::bar(
            settings::BLOOD_BAR_POS,
            "blood",
            settings::BLOOD_BAR_TEXT_COLOR,
            settings::BLOOD_BAR_BACKGROUND_COLOR,
            settings::BLOOD_BAR_BAR_COLOR,
            blood
        ));
        current_index().blood_bar = Some(objects.len() - 1);
    }

    // Add blood and air bar.
    add_bars();

    settings::level_1();
}

fn restart() {
    load();
}

pub fn key_press(key: keyboard::Key) {
    use piston::input::Key;

    if *current_game_state() != GameState::Play { return; }

    match (key, current_index().player) {
        (Key::Right, Some(player_index)) => {
            current_objects()[player_index].move_right();
        },
        (Key::Up, Some(player_index)) => {
            current_objects()[player_index].move_up();
        },
        (Key::Left, Some(player_index)) => {
            current_objects()[player_index].move_left();
        },
        (Key::Down, Some(player_index)) => {
            current_objects()[player_index].move_down();
        },
        _ => {},
    }
}

pub fn key_release(key: keyboard::Key) {
    use piston::input::Key;

    if key == Key::Return || key == Key::Space {
        match *current_game_state() {
            GameState::Win | GameState::Lose => restart(),
            _ => {},
        }
    }
}
<commit_msg>Fixed spelling error<commit_after>// Extern crates.
use graphics::{ Context, Graphics };
use graphics;
use piston::input::keyboard;
use current::{ Current, CurrentGuard };
use start_piston;
use glium_graphics::GliumGraphics;

// Local crate.
use action;
use settings;
use object;
use object::Object;
use text as text_mod;
use game_state::GameState;
use player::{ Player };
use colors;
use snake::Snake;
use air_bottle::AirBottle;
use bar::Bar;

pub fn current_cam() -> Current<Cam> { unsafe { Current::new() } }
pub fn current_game_state() -> Current<GameState> { unsafe { Current::new() } }
pub fn current_objects() -> Current<Vec<Object>> { unsafe { Current::new() } }
pub fn current_index() -> Current<Index> { unsafe { Current::new() } }
pub fn current_settings() -> Current<Settings> { unsafe { Current::new() } }
pub fn current_player() -> Current<Player> { unsafe { Current::new() } }
pub fn current_snakes() -> Current<Vec<Snake>> { unsafe { Current::new() } }
pub fn current_air_bottles() -> Current<Vec<AirBottle>> { unsafe { Current::new() } }
pub fn current_bars() -> Current<Vec<Bar>> { unsafe { Current::new() } }

pub fn app() {
    let mut cam = Cam([0.0, 0.0]);
    let mut game_state = GameState::Play;
    let mut objects: Vec<Object> = Vec::new();
    let mut index = Index::new();
    let mut settings = Settings::new();
    let mut player = Player::new();
    let mut snakes: Vec<Snake> = Vec::new();
    let mut air_bottles: Vec<AirBottle> = Vec::new();
    let mut bars: Vec<Bar> = Vec::new();

    let cam_guard = CurrentGuard::new(&mut cam);
    let game_state_guard = CurrentGuard::new(&mut game_state);
    let objects_guard = CurrentGuard::new(&mut objects);
    let index_guard = CurrentGuard::new(&mut index);
    let settings_guard = CurrentGuard::new(&mut settings);
    let player_guard = CurrentGuard::new(&mut player);
    let snakes_guard = CurrentGuard::new(&mut snakes);
    let air_bottles_guard = CurrentGuard::new(&mut air_bottles);
    let bars_guard = CurrentGuard::new(&mut bars);

    start();

    drop(cam_guard);
    drop(game_state_guard);
    drop(objects_guard);
    drop(index_guard);
    drop(settings_guard);
    drop(player_guard);
    drop(snakes_guard);
    drop(air_bottles_guard);
    drop(bars_guard);
}

/// The graphics back-end to use for rendering.
#[derive(Debug)]
pub enum GraphicsBackEnd {
    /// Use Gfx to render
    Gfx,
    /// Use OpenGL to render
    OpenGL,
    /// Use Glium to render
    Glium,
}

fn start() {
    use piston::event::{ RenderEvent, UpdateEvent, PressEvent, ReleaseEvent };
    use piston::window::Window;

    let mut back_end = GraphicsBackEnd::Gfx;
    println!("Running with graphics backend {:?}", back_end);
    println!("Use 'S' to swap back-end");

    load();
    for e in start_piston::events().max_fps(120).swap_buffers(false) {
        use piston::input::Key;
        use piston::input::Button::Keyboard;

        if let Some(Keyboard(Key::S)) = e.press_args() {
            back_end = match back_end {
                GraphicsBackEnd::Gfx => {
                    println!("Swapped to OpenGL");
                    GraphicsBackEnd::OpenGL
                }
                GraphicsBackEnd::OpenGL => {
                    println!("Swapped to Glium");
                    GraphicsBackEnd::Glium
                }
                GraphicsBackEnd::Glium => {
                    println!("Swapped to Gfx");
                    GraphicsBackEnd::Gfx
                }
            };
        };
        e.render(|args| {
            match back_end {
                GraphicsBackEnd::Gfx => {
                    {
                        start_piston::render_2d_gfx(Some(settings::WATER_COLOR), |c, g| {
                            render(&c, g)
                        });
                    }
                    {
                        let window = start_piston::current_window();
                        let mut window = window.borrow_mut();
                        window.swap_buffers();
                    }
                }
                GraphicsBackEnd::OpenGL => {
                    {
                        start_piston::render_2d_opengl(Some(settings::WATER_COLOR), |c, g| {
                            render(&c, g)
                        });
                    }
                    {
                        let window = start_piston::current_window();
                        let mut window = window.borrow_mut();
                        window.swap_buffers();
                    }
                }
                GraphicsBackEnd::Glium => {
                    let glium_window = start_piston::current_glium_window();
                    let glium_window = glium_window.borrow();
                    let glium_2d = start_piston::current_glium_2d();
                    let mut glium_2d = glium_2d.borrow_mut();
                    let mut target = glium_window.draw();
                    {
                        let mut g = GliumGraphics::new(&mut *glium_2d, &mut target);
                        graphics::clear(settings::WATER_COLOR, &mut g);
                        let c = Context::new_viewport(args.viewport());
                        render(&c, &mut g);
                    }
                    target.finish();
                }
            };
            start_piston::set_title(start_piston::fps_tick().to_string());
        });
        e.update(|args| {
            update(args.dt);
        });
        if let Some(Keyboard(key)) = e.press_args() {
            key_press(key);
        }
        if let Some(Keyboard(key)) = e.release_args() {
            key_release(key);
        }
    }
}

pub struct Cam(pub [f64; 2]);

impl Cam {
    fn pos(&self) -> [f64; 2] {
        let Cam(pos) = *self;
        pos
    }

    fn set(&mut self, val: [f64; 2]) {
        *self = Cam(val);
    }

    /// Make camera follow position.
    fn follow_pos(&mut self, dt: f64, follow_percentage: f64, pos: [f64; 2]) {
        let camera_pos = self.pos();
        let (dx, dy) = (pos[0] - camera_pos[0], pos[1] - camera_pos[1]);
        let dx = follow_percentage * dt * dx;
        let dy = follow_percentage * dt * dy;
        self.set([camera_pos[0] + dx, camera_pos[1] + dy]);
    }
}

pub struct Index {
    pub player: Option<usize>,
    pub blood_bar: Option<usize>,
    pub air_bar: Option<usize>,
}

impl Index {
    pub fn new() -> Index {
        Index {
            player: None,
            blood_bar: None,
            air_bar: None
        }
    }
}

pub struct Settings {
    // Tells where the surface is.
    pub surface_y: Option<f64>,
    pub camera_follow_percentage: Option<f64>,
}

impl Settings {
    pub fn new() -> Settings {
        Settings {
            surface_y: None,
            camera_follow_percentage: None,
        }
    }
}

pub fn render<G: Graphics>(c: &Context, gl: &mut G) {
    use graphics::*;

    let c = &c.reset();

    // Get camera coordinates.
    let cam_pos = current_cam().pos();
    let cam_x = cam_pos[0];
    let mut cam_y = cam_pos[1];
    cam_y += 0.4;

    // Render surface.
    let surface_y = current_settings().surface_y.unwrap();
    graphics::Rectangle::new(colors::BLUE)
        .draw([-1.0, surface_y - cam_y, 2.0, 0.05],
            &c.draw_state, c.transform, gl);

    // Render objects in layers.
    let cam = &c.trans(-cam_x, -cam_y);
    for i in 0usize..settings::NUMBER_OF_LAYERS {
        for obj in current_objects().iter() {
            if obj.layer == i { obj.render(cam, c, gl); }
        }
    }

    let text_c = c.flip_v();
    let text_c = text_c.zoom(0.0025);
    match *current_game_state() {
        GameState::Win => {
            let pos = settings::YOU_WIN_POS;
            text_mod::text(settings::YOU_WIN_TEXT,
                &graphics::Polygon::new(settings::YOU_WIN_TEXT_COLOR),
                &text_c
                .trans(pos[0], pos[1])
            , gl);
        },
        GameState::Lose => {
            let pos = settings::YOU_LOSE_POS;
            text_mod::text(settings::YOU_LOSE_TEXT,
                &graphics::Polygon::new(settings::YOU_LOSE_TEXT_COLOR),
                &text_c
                .trans(pos[0], pos[1])
            , gl);
        },
        GameState::Play => {

        },
    }
}

pub fn update(dt: f64) {

    fn player_pos() -> [f64; 2] {
        let player_index = current_index().player.unwrap();
        current_objects()[player_index].pos
    }

    fn update_objects(dt: f64) {
        if *current_game_state() != GameState::Play { return; }

        current_player().update(dt);

        // Update states of objects.
        let player_pos = player_pos();
        let mut attack_damage: f64 = 0.0;
        for obj in current_objects().iter_mut() {
            match obj.update(dt, player_pos) {
                action::Action::Passive => {},
                action::Action::Attack(attack) => { attack_damage += attack; },
            }
        }
        // Bite player.
        current_player().bite(attack_damage);

        // Decrease the player's air with time.
        let air = current_player().air;
        current_player().air = air - dt * settings::PLAYER_LOSE_AIR_SPEED;
    }

    update_objects(dt);

    fn fill_air() {
        let player_pos = player_pos();
        let mut air = current_player().air;
        for obj in current_objects().iter_mut() {
            let pos = obj.pos;
            match obj.data {
                object::Data::AirBottle(i) => {
                    let dx = pos[0] - player_pos[0];
                    let dy = pos[1] - player_pos[1];
                    let d = dx * dx + dy * dy;
                    if d <= settings::AIR_BOTTLE_RADIUS * settings::AIR_BOTTLE_RADIUS {
                        let air_bottle = &mut current_air_bottles()[i];
                        air += air_bottle.fill_up;
                        air_bottle.fill_up = 0.0;
                    }
                },
                _ => {}
            }
        }

        air = if air > 1.0 { 1.0 } else { air };
        current_player().air = air;
    }

    fill_air();

    fn win() {
        let player_pos = player_pos();
        // When player reaches surface, win.
        if player_pos[1] >= current_settings().surface_y.unwrap() {
            *current_game_state() = GameState::Win;
            return;
        }
    }

    win();

    fn lose() {
        let player = &*current_player();
        let blood = player.blood;
        let air = player.air;
        if blood < 0.0 || air < 0.0 {
            *current_game_state() = GameState::Lose;
            return;
        }
    }

    lose();

    fn follow_player(dt: f64) {
        // Make camera follow player.
        let follow_percentage = current_settings().camera_follow_percentage.unwrap();
        current_cam().follow_pos(dt, follow_percentage, player_pos());
    }

    follow_player(dt);
}

pub fn load() {
    current_cam().set(settings::INITIAL_CAMERA_POS);
    current_settings().camera_follow_percentage = Some(settings::CAMERA_FOLLOW_PERCENTAGE);
    *current_game_state() = settings::INITIAL_GAME_STATE;

    // Add player.
    current_objects().clear();
    current_objects().push(Object::bar_background());
    current_index().player = Some(0);

    fn add_bars() {
        fn air() -> f64 { current_player().air }
        fn blood() -> f64 { current_player().blood }

        let objects = &mut *current_objects();
        objects.push(Object::bar(
            settings::AIR_BAR_POS,
            "air",
            settings::AIR_BAR_TEXT_COLOR,
            settings::AIR_BAR_BACKGROUND_COLOR,
            settings::AIR_BAR_BAR_COLOR,
            air
        ));
        current_index().air_bar = Some(objects.len() - 1);
        objects.push(Object::bar(
            settings::BLOOD_BAR_POS,
            "blood",
            settings::BLOOD_BAR_TEXT_COLOR,
            settings::BLOOD_BAR_BACKGROUND_COLOR,
            settings::BLOOD_BAR_BAR_COLOR,
            blood
        ));
        current_index().blood_bar = Some(objects.len() - 1);
    }

    // Add blood and air bar.
    add_bars();

    settings::level_1();
}

fn restart() {
    load();
}

pub fn key_press(key: keyboard::Key) {
    use piston::input::Key;

    if *current_game_state() != GameState::Play { return; }

    match (key, current_index().player) {
        (Key::Right, Some(player_index)) => {
            current_objects()[player_index].move_right();
        },
        (Key::Up, Some(player_index)) => {
            current_objects()[player_index].move_up();
        },
        (Key::Left, Some(player_index)) => {
            current_objects()[player_index].move_left();
        },
        (Key::Down, Some(player_index)) => {
            current_objects()[player_index].move_down();
        },
        _ => {},
    }
}

pub fn key_release(key: keyboard::Key) {
    use piston::input::Key;

    if key == Key::Return || key == Key::Space {
        match *current_game_state() {
            GameState::Win | GameState::Lose => restart(),
            _ => {},
        }
    }
}
<|endoftext|>


<commit_before>use super::scheduler_thread::*;
use super::job_queue::*;
use super::queue_state::*;
use super::wake_queue::*;

use std::sync::*;
use std::collections::vec_deque::*;

use futures::task;
use futures::task::{Context};

///
/// The scheduler core contains the internal data used by the scheduler
///
pub (super) struct SchedulerCore {
    /// The queues that are active in the scheduler
    pub (super) schedule: Arc<Mutex<VecDeque<Arc<JobQueue>>>>,

    /// Active threads and whether or not they're busy
    pub (super) threads: Mutex<Vec<(Arc<Mutex<bool>>, SchedulerThread)>>,

    /// The maximum number of threads permitted in this scheduler
    pub (super) max_threads: Mutex<usize>
}

impl SchedulerCore {
    ///
    /// Wakes a thread to run a dormant queue. Returns true if a thread was woken up
    ///
    pub (super) fn schedule_thread(&self, core: Arc<SchedulerCore>) -> bool {
        // Find a dormant thread and activate it
        let schedule = self.schedule.clone();

        // Schedule work on this dormant thread
        let work_core   = Arc::clone(&core);
        let do_work     = move |work: Arc<JobQueue>| {
            let waker       = Arc::new(WakeQueue(Arc::clone(&work), Arc::clone(&work_core)));
            let waker       = task::waker_ref(&waker);
            let mut context = Context::from_waker(&waker);

            work.drain(&mut context)
        };

        if !self.schedule_dormant(move || Self::next_to_run(&schedule), do_work) {
            // Try to create a new thread
            if self.spawn_thread_if_less_than_maximum() {
                // Try harder to schedule this task if a thread was created
                self.schedule_thread(core)
            } else {
                // Couldn't schedule on an existing thread or create a new one
                false
            }
        } else {
            // Successfully scheduled
            true
        }
    }

    ///
    /// If a queue is idle and has pending jobs, places it in the schedule
    ///
    pub (super) fn reschedule_queue(&self, queue: &Arc<JobQueue>, core: Arc<SchedulerCore>) {
        let reschedule = {
            let mut core = queue.core.lock().expect("JobQueue core lock");

            match core.state {
                QueueState::Idle => {
                    // Schedule a thread to restart the queue if more things were queued
                    if core.queue.len() > 0 {
                        // Need to schedule the queue after this event
                        core.state = QueueState::Pending;
                        true
                    } else {
                        // Queue is empty and can go back to idle
                        core.state = QueueState::Idle;
                        false
                    }
                },

                QueueState::WaitingForPoll(_) => {
                    // If the target thread gets stuck and stops draining the queue, race it to reschedule it on one of our threads if we can
                    true
                },

                _ => {
                    // Not scheduled
                    false
                }
            }
        };

        if reschedule {
            self.schedule.lock().expect("Schedule lock").push_back(queue.clone());
            self.schedule_thread(core);
        }
    }

    ///
    /// Finds the next queue that should be run. If this returns successfully, the queue will
    /// be marked as running.
    ///
    pub (super) fn next_to_run(schedule: &Arc<Mutex<VecDeque<Arc<JobQueue>>>>) -> Option<Arc<JobQueue>> {
        // Search the queues...
        let mut schedule = schedule.lock().expect("Schedule lock");

        // Find a queue where the state is pending
        while let Some(q) = schedule.pop_front() {
            let mut core = q.core.lock().expect("JobQueue core lock");

            match core.state {
                QueueState::Pending |
                QueueState::WaitingForPoll(_) => {
                    // Queue is ready to run. Mark it as running and return it
                    core.state = QueueState::Running;
                    return Some(q.clone());
                }

                _ => {
                    // Move to the next queue in the schedule
                }
            }
        }

        None
    }

    ///
    /// If any of the scheduler threads have finished (which generally means they panicked), despawn them
    ///
    pub (super) fn remove_finished_threads(&self) {
        let mut dead_threads = vec![];

        // Collate the dead threads into a vec
        {
            // Find busy threads
            let mut threads = self.threads.lock().expect("Scheduler threads lock");

            // TODO: retain doesn't return the removed elements. Really want drain_filter but it's been in nightly for 5 years so I guess it's never getting released?
            for thread_num in 0..threads.len() {
                let (_, thread) = &threads[thread_num];

                if thread.is_finished() {
                    let (is_busy, dead_thread) = threads.remove(thread_num);
                    dead_threads.push((is_busy, dead_thread));
                }
            }
        }

        // Despawn the dead threads (which might panic a bit)
        for (is_busy, dead_thread) in dead_threads {
            // Join with the thread in case it's mid-panic
            dead_thread.despawn().join().unwrap();

            // The busy flag should always be unset after a thread is despawned
            let busy = is_busy.lock().unwrap();
            if *busy {
                panic!("Thread despawned while busy");
            }
        }
    }

    ///
    /// Attempts to schedule a task on a dormant thread
    ///
    pub (super) fn schedule_dormant<NextJob, RunJob, JobData>(&self, next_job: NextJob, job: RunJob) -> bool
    where
        RunJob:     'static + Send + Fn(JobData) -> (),
        NextJob:    'static + Send + Fn() -> Option<JobData>,
    {
        // Try to despawn any threads that have finished since the last time we were called
        self.remove_finished_threads();

        // Find busy threads
        let threads = self.threads.lock().expect("Scheduler threads lock");

        // Find the first thread that is not marked as busy and schedule this task on it
        for &(ref busy_rc, ref thread) in threads.iter() {
            if let Ok(mut busy) = busy_rc.try_lock() {
                // If the busy lock is held, then we consider the thread to be busy
                if !*busy {
                    // Clone the busy mutex so we can return this thread to readiness
                    let also_busy =  busy_rc.clone();

                    // This thread is busy
                    *busy = true;
                    thread.run(move || {
                        let mut done = false;

                        while !done {
                            // Obtain the next job. The thread is not busy once there are no longer any jobs
                            // We hold the mutex while this is going on to avoid a race condition when a thread is going dormant
                            let job_data = {
                                let mut busy = also_busy.lock().expect("Thread busy lock");
                                let job_data = next_job();

                                // If there's no next job, then this thread is no longer busy
                                if job_data.is_none() {
                                    *busy = false;
                                }

                                job_data
                            };

                            // Run the job if there is one, stop the thread if there is not
                            if let Some(job_data) = job_data {
                                job(job_data);
                            } else {
                                done = true;
                            }
                        }
                    });

                    return true;
                }
            }
        }

        // No dormant threads were found
        false
    }

    ///
    /// If we're running fewer than the maximum number of threads, try to spawn a new one
    ///
    pub (super) fn spawn_thread_if_less_than_maximum(&self) -> bool {
        let max_threads = { *self.max_threads.lock().expect("Max threads lock") };
        let mut threads = self.threads.lock().expect("Scheduler threads lock");

        if threads.len() < max_threads {
            // Create a new thread
            let is_busy     = Arc::new(Mutex::new(false));
            let new_thread  = SchedulerThread::new();
            threads.push((is_busy, new_thread));

            true
        } else {
            // Can't spawn a new thread
            false
        }
    }
}
<commit_msg>Oops, implemented the 'drain the finished threads' routine wrong (mutating the vec while iterating it is confusing)<commit_after>use super::scheduler_thread::*;
use super::job_queue::*;
use super::queue_state::*;
use super::wake_queue::*;

use std::sync::*;
use std::collections::vec_deque::*;

use futures::task;
use futures::task::{Context};

///
/// The scheduler core contains the internal data used by the scheduler
///
pub (super) struct SchedulerCore {
    /// The queues that are active in the scheduler
    pub (super) schedule: Arc<Mutex<VecDeque<Arc<JobQueue>>>>,

    /// Active threads and whether or not they're busy
    pub (super) threads: Mutex<Vec<(Arc<Mutex<bool>>, SchedulerThread)>>,

    /// The maximum number of threads permitted in this scheduler
    pub (super) max_threads: Mutex<usize>
}

impl SchedulerCore {
    ///
    /// Wakes a thread to run a dormant queue. Returns true if a thread was woken up
    ///
    pub (super) fn schedule_thread(&self, core: Arc<SchedulerCore>) -> bool {
        // Find a dormant thread and activate it
        let schedule = self.schedule.clone();

        // Schedule work on this dormant thread
        let work_core   = Arc::clone(&core);
        let do_work     = move |work: Arc<JobQueue>| {
            let waker       = Arc::new(WakeQueue(Arc::clone(&work), Arc::clone(&work_core)));
            let waker       = task::waker_ref(&waker);
            let mut context = Context::from_waker(&waker);

            work.drain(&mut context)
        };

        if !self.schedule_dormant(move || Self::next_to_run(&schedule), do_work) {
            // Try to create a new thread
            if self.spawn_thread_if_less_than_maximum() {
                // Try harder to schedule this task if a thread was created
                self.schedule_thread(core)
            } else {
                // Couldn't schedule on an existing thread or create a new one
                false
            }
        } else {
            // Successfully scheduled
            true
        }
    }

    ///
    /// If a queue is idle and has pending jobs, places it in the schedule
    ///
    pub (super) fn reschedule_queue(&self, queue: &Arc<JobQueue>, core: Arc<SchedulerCore>) {
        let reschedule = {
            let mut core = queue.core.lock().expect("JobQueue core lock");

            match core.state {
                QueueState::Idle => {
                    // Schedule a thread to restart the queue if more things were queued
                    if core.queue.len() > 0 {
                        // Need to schedule the queue after this event
                        core.state = QueueState::Pending;
                        true
                    } else {
                        // Queue is empty and can go back to idle
                        core.state = QueueState::Idle;
                        false
                    }
                },

                QueueState::WaitingForPoll(_) => {
                    // If the target thread gets stuck and stops draining the queue, race it to reschedule it on one of our threads if we can
                    true
                },

                _ => {
                    // Not scheduled
                    false
                }
            }
        };

        if reschedule {
            self.schedule.lock().expect("Schedule lock").push_back(queue.clone());
            self.schedule_thread(core);
        }
    }

    ///
    /// Finds the next queue that should be run. If this returns successfully, the queue will
    /// be marked as running.
    ///
    pub (super) fn next_to_run(schedule: &Arc<Mutex<VecDeque<Arc<JobQueue>>>>) -> Option<Arc<JobQueue>> {
        // Search the queues...
        let mut schedule = schedule.lock().expect("Schedule lock");

        // Find a queue where the state is pending
        while let Some(q) = schedule.pop_front() {
            let mut core = q.core.lock().expect("JobQueue core lock");

            match core.state {
                QueueState::Pending |
                QueueState::WaitingForPoll(_) => {
                    // Queue is ready to run. Mark it as running and return it
                    core.state = QueueState::Running;
                    return Some(q.clone());
                }

                _ => {
                    // Move to the next queue in the schedule
                }
            }
        }

        None
    }

    ///
    /// If any of the scheduler threads have finished (which generally means they panicked), despawn them
    ///
    pub (super) fn remove_finished_threads(&self) {
        let mut dead_threads = vec![];

        // Collate the dead threads into a vec
        {
            // Find busy threads
            let mut threads = self.threads.lock().expect("Scheduler threads lock");

            // TODO: retain doesn't return the removed elements. Really want drain_filter but it's been in nightly for 5 years so I guess it's never getting released?
            let mut thread_num = 0;
            while thread_num < threads.len() {
                let (_, thread) = &threads[thread_num];

                if thread.is_finished() {
                    // Remove the thread and queue it for later despawning
                    let (is_busy, dead_thread) = threads.remove(thread_num);
                    dead_threads.push((is_busy, dead_thread));
                } else {
                    // Thread still running
                    thread_num += 1;
                }
            }
        }

        // Despawn the dead threads (which might panic a bit)
        for (is_busy, dead_thread) in dead_threads {
            // Join with the thread in case it's mid-panic
            dead_thread.despawn().join().unwrap();

            // The busy flag should always be unset after a thread is despawned
            let busy = is_busy.lock().unwrap();
            if *busy {
                panic!("Thread despawned while busy");
            }
        }
    }

    ///
    /// Attempts to schedule a task on a dormant thread
    ///
    pub (super) fn schedule_dormant<NextJob, RunJob, JobData>(&self, next_job: NextJob, job: RunJob) -> bool
    where
        RunJob:     'static + Send + Fn(JobData) -> (),
        NextJob:    'static + Send + Fn() -> Option<JobData>,
    {
        // Try to despawn any threads that have finished since the last time we were called
        self.remove_finished_threads();

        // Find busy threads
        let threads = self.threads.lock().expect("Scheduler threads lock");

        // Find the first thread that is not marked as busy and schedule this task on it
        for &(ref busy_rc, ref thread) in threads.iter() {
            if let Ok(mut busy) = busy_rc.try_lock() {
                // If the busy lock is held, then we consider the thread to be busy
                if !*busy {
                    // Clone the busy mutex so we can return this thread to readiness
                    let also_busy =  busy_rc.clone();

                    // This thread is busy
                    *busy = true;
                    thread.run(move || {
                        let mut done = false;

                        while !done {
                            // Obtain the next job. The thread is not busy once there are no longer any jobs
                            // We hold the mutex while this is going on to avoid a race condition when a thread is going dormant
                            let job_data = {
                                let mut busy = also_busy.lock().expect("Thread busy lock");
                                let job_data = next_job();

                                // If there's no next job, then this thread is no longer busy
                                if job_data.is_none() {
                                    *busy = false;
                                }

                                job_data
                            };

                            // Run the job if there is one, stop the thread if there is not
                            if let Some(job_data) = job_data {
                                job(job_data);
                            } else {
                                done = true;
                            }
                        }
                    });

                    return true;
                }
            }
        }

        // No dormant threads were found
        false
    }

    ///
    /// If we're running fewer than the maximum number of threads, try to spawn a new one
    ///
    pub (super) fn spawn_thread_if_less_than_maximum(&self) -> bool {
        let max_threads = { *self.max_threads.lock().expect("Max threads lock") };
        let mut threads = self.threads.lock().expect("Scheduler threads lock");

        if threads.len() < max_threads {
            // Create a new thread
            let is_busy     = Arc::new(Mutex::new(false));
            let new_thread  = SchedulerThread::new();
            threads.push((is_busy, new_thread));

            true
        } else {
            // Can't spawn a new thread
            false
        }
    }
}
<|endoftext|>


<commit_before>use super::super::{Coord, Grid, X, Y};
use super::*;

use rand::distributions::Distribution;
use rand::{distributions, Rng};
use std::cmp;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ClueConstraint {
    Any,
    Forced,
    Prohibited,
}

pub struct GeneratorOption {
    pub clue_constraint: Grid<ClueConstraint>,
    pub max_clue: Option<i32>,
    pub symmetry: bool,
    pub use_trial_and_error: bool,
}

enum HasClueHistory {
    Update(Coord, bool),
    Checkpoint,
}
struct HasClue {
    has_clue: Grid<bool>,
    n_clues: i32,
    history: Vec<HasClueHistory>,
}

impl HasClue {
    fn new(height: i32, width: i32) -> HasClue {
        HasClue {
            has_clue: Grid::new(height, width, false),
            n_clues: 0,
            history: vec![],
        }
    }

    fn update(&mut self, loc: Coord, val: bool) {
        if self.has_clue[loc] == val {
            return;
        }
        if val {
            self.n_clues += 1;
        } else {
            self.n_clues -= 1;
        }
        self.history.push(HasClueHistory::Update(loc, !val));
        self.has_clue[loc] = val;
    }
    fn get(&self, loc: Coord) -> bool {
        self.has_clue[loc]
    }
    fn get_checked(&self, loc: Coord) -> bool {
        self.has_clue.is_valid_coord(loc) && self.has_clue[loc]
    }
    fn add_checkpoint(&mut self) {
        self.history.push(HasClueHistory::Checkpoint);
    }
    fn rollback(&mut self) {
        loop {
            match self.history.pop() {
                Some(HasClueHistory::Update(cd, v)) => {
                    self.has_clue[cd] = v;
                    if v {
                        self.n_clues += 1;
                    } else {
                        self.n_clues -= 1;
                    }
                }
                _ => break,
            }
        }
    }
    fn forget_history(&mut self) {
        self.history.clear();
    }
}

pub fn generate<R: Rng>(
    opts: &GeneratorOption,
    dic: &Dictionary,
    consecutive_dic: &ConsecutiveRegionDictionary,
    rng: &mut R,
) -> Option<Grid<Clue>> {
    let height = opts.clue_constraint.height();
    let width = opts.clue_constraint.width();
    let mut has_clue = HasClue::new(height, width);

    let mut problem = Grid::new(height, width, NO_CLUE);
    let mut field = Field::new(height, width, dic, consecutive_dic);
    let mut current_energy = 0i32;

    for y in 0..height {
        for x in 0..width {
            if opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                has_clue.update((Y(y), X(x)), true);
            }
        }
    }

    let n_step = height * width * 10;
    let mut temperature = 20f64;

    for s in 0..n_step {
        let mut update_cand = vec![];
        for y in 0..height {
            for x in 0..width {
                if field.cell((Y(y), X(x))) == Cell::Black
                    || opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Prohibited
                {
                    continue;
                }
                if opts.symmetry {
                    let y2 = height - 1 - y;
                    let x2 = width - 1 - x;
                    if -1 <= y - y2 && y - y2 <= 1 && -1 <= x - x2 && x - x2 <= 1
                        && (y != y2 || x != x2)
                    {
                        continue;
                    }
                }
                let mut isok = true;
                for dy in -1..2 {
                    for dx in -1..2 {
                        if (dy != 0 || dx != 0) && has_clue.get_checked((Y(y + dy), X(x + dx))) {
                            isok = false;
                        }
                    }
                }
                let mut isok2 = false;
                for dy in -2..3 {
                    for dx in -2..3 {
                        let loc = (Y(y + dy), X(x + dx));
                        if field.cell_checked(loc) == Cell::Undecided {
                            isok2 = true;
                        }
                    }
                }
                if (has_clue.get((Y(y), X(x))) && problem[(Y(y), X(x))] == NO_CLUE)
                    || (isok && isok2
                        && (problem[(Y(y), X(x))] != NO_CLUE || rng.gen::<f64>() < 1.0))
                {
                    for v in (-1)..(CLUE_TYPES as i32) {
                        if v == -1 && opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                            continue;
                        }
                        if v == 0 || v == 21 || v == 22 {
                            continue;
                        }
                        let next_clue = Clue(v);
                        if problem[(Y(y), X(x))] != next_clue {
                            update_cand.push(((Y(y), X(x)), next_clue));
                        }
                    }
                }
            }
        }

        rng.shuffle(&mut update_cand);

        let mut updated = false;

        for &(loc, clue) in &update_cand {
            let previous_clue = problem[loc];
            let (Y(y), X(x)) = loc;

            if opts.symmetry {
                let loc2 = (Y(height - 1 - y), X(width - 1 - x));
                if clue == NO_CLUE {
                    if problem[loc2] == NO_CLUE {
                        has_clue.update(loc, false);
                        has_clue.update(loc2, false);
                    }
                } else {
                    has_clue.update(loc, true);
                    has_clue.update(loc2, true);
                }
            } else {
                has_clue.update(loc, clue != NO_CLUE);
            }
            if let Some(max_clue) = opts.max_clue {
                if max_clue < has_clue.n_clues {
                    continue;
                }
            }

            problem[loc] = clue;
            let next_field = if previous_clue == NO_CLUE {
                let mut f = field.clone();
                f.add_clue(loc, clue);
                f.solve();
                if opts.use_trial_and_error {
                    f.trial_and_error();
                }
                f
            } else {
                solve_test(
                    &problem,
                    &has_clue,
                    opts.use_trial_and_error,
                    dic,
                    consecutive_dic,
                )
            };
            let energy = next_field.decided_cells() - 4 * has_clue.n_clues;

            let update = !next_field.inconsistent()
                && (current_energy < energy
                    || rng.gen::<f64>() < ((energy - current_energy) as f64 / temperature).exp());

            if update {
                current_energy = energy;
                field = next_field;

                let mut clue_filled = true;
                for y in 0..height {
                    for x in 0..width {
                        if has_clue.get((Y(y), X(x))) && problem[(Y(y), X(x))] == NO_CLUE {
                            clue_filled = false;
                        }
                    }
                }
                if field.fully_solved() && clue_filled {
                    return Some(problem);
                }
                updated = true;
                has_clue.forget_history();
                break;
            }
            problem[loc] = previous_clue;
            has_clue.rollback();
        }

        if !updated {
            break;
        }

        temperature *= 0.995f64;
    }

    None
}

fn solve_test<'a, 'b>(
    problem: &Grid<Clue>,
    has_clue: &HasClue,
    use_trial_and_error: bool,
    dic: &'a Dictionary,
    consecutive_dic: &'b ConsecutiveRegionDictionary,
) -> Field<'a, 'b> {
    let height = problem.height();
    let width = problem.width();
    let mut ret = Field::new(height, width, dic, consecutive_dic);

    for y in 0..height {
        for x in 0..width {
            let clue = problem[(Y(y), X(x))];
            if clue != NO_CLUE {
                ret.add_clue((Y(y), X(x)), clue);
            } else if has_clue.get((Y(y), X(x))) {
                ret.decide((Y(y), X(x)), Cell::White);
            }

            if ret.inconsistent() {
                return ret;
            }
        }
    }

    ret.solve();
    if use_trial_and_error {
        ret.trial_and_error();
    }

    ret
}
<commit_msg>Tapa: bug fix in generator<commit_after>use super::super::{Coord, Grid, X, Y};
use super::*;

use rand::distributions::Distribution;
use rand::{distributions, Rng};
use std::cmp;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ClueConstraint {
    Any,
    Forced,
    Prohibited,
}

pub struct GeneratorOption {
    pub clue_constraint: Grid<ClueConstraint>,
    pub max_clue: Option<i32>,
    pub symmetry: bool,
    pub use_trial_and_error: bool,
}

enum HasClueHistory {
    Update(Coord, bool),
    Checkpoint,
}
struct HasClue {
    has_clue: Grid<bool>,
    n_clues: i32,
    history: Vec<HasClueHistory>,
}

impl HasClue {
    fn new(height: i32, width: i32) -> HasClue {
        HasClue {
            has_clue: Grid::new(height, width, false),
            n_clues: 0,
            history: vec![],
        }
    }

    fn update(&mut self, loc: Coord, val: bool) {
        if self.has_clue[loc] == val {
            return;
        }
        if val {
            self.n_clues += 1;
        } else {
            self.n_clues -= 1;
        }
        self.history.push(HasClueHistory::Update(loc, !val));
        self.has_clue[loc] = val;
    }
    fn get(&self, loc: Coord) -> bool {
        self.has_clue[loc]
    }
    fn get_checked(&self, loc: Coord) -> bool {
        self.has_clue.is_valid_coord(loc) && self.has_clue[loc]
    }
    fn add_checkpoint(&mut self) {
        self.history.push(HasClueHistory::Checkpoint);
    }
    fn rollback(&mut self) {
        loop {
            match self.history.pop() {
                Some(HasClueHistory::Update(cd, v)) => {
                    self.has_clue[cd] = v;
                    if v {
                        self.n_clues += 1;
                    } else {
                        self.n_clues -= 1;
                    }
                }
                _ => break,
            }
        }
    }
    fn forget_history(&mut self) {
        self.history.clear();
    }
}

pub fn generate<R: Rng>(
    opts: &GeneratorOption,
    dic: &Dictionary,
    consecutive_dic: &ConsecutiveRegionDictionary,
    rng: &mut R,
) -> Option<Grid<Clue>> {
    let height = opts.clue_constraint.height();
    let width = opts.clue_constraint.width();
    let mut has_clue = HasClue::new(height, width);

    let mut problem = Grid::new(height, width, NO_CLUE);
    let mut field = Field::new(height, width, dic, consecutive_dic);
    let mut current_energy = 0i32;

    for y in 0..height {
        for x in 0..width {
            if opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                has_clue.update((Y(y), X(x)), true);
            }
        }
    }

    let n_step = height * width * 10;
    let mut temperature = 20f64;

    for s in 0..n_step {
        let mut update_cand = vec![];
        for y in 0..height {
            for x in 0..width {
                if field.cell((Y(y), X(x))) == Cell::Black
                    || opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Prohibited
                {
                    continue;
                }
                if opts.symmetry {
                    let y2 = height - 1 - y;
                    let x2 = width - 1 - x;
                    if -1 <= y - y2 && y - y2 <= 1 && -1 <= x - x2 && x - x2 <= 1
                        && (y != y2 || x != x2)
                    {
                        continue;
                    }
                }
                let mut isok = true;
                for dy in -1..2 {
                    for dx in -1..2 {
                        if (dy != 0 || dx != 0) && has_clue.get_checked((Y(y + dy), X(x + dx))) {
                            isok = false;
                        }
                    }
                }
                let mut isok2 = false;
                for dy in -2..3 {
                    for dx in -2..3 {
                        let loc = (Y(y + dy), X(x + dx));
                        if field.cell_checked(loc) == Cell::Undecided {
                            isok2 = true;
                        }
                    }
                }
                if (has_clue.get((Y(y), X(x))) && problem[(Y(y), X(x))] == NO_CLUE)
                    || (isok && isok2
                        && (problem[(Y(y), X(x))] != NO_CLUE || rng.gen::<f64>() < 1.0))
                {
                    for v in (-1)..(CLUE_TYPES as i32) {
                        if v == -1 && opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                            continue;
                        }
                        if v == 0 || v == 21 || v == 22 {
                            continue;
                        }
                        let next_clue = Clue(v);
                        if problem[(Y(y), X(x))] != next_clue {
                            update_cand.push(((Y(y), X(x)), next_clue));
                        }
                    }
                }
            }
        }

        rng.shuffle(&mut update_cand);

        let mut updated = false;

        for &(loc, clue) in &update_cand {
            let previous_clue = problem[loc];
            let (Y(y), X(x)) = loc;

            has_clue.add_checkpoint();
            if opts.symmetry {
                let loc2 = (Y(height - 1 - y), X(width - 1 - x));
                if clue == NO_CLUE {
                    if problem[loc2] == NO_CLUE {
                        has_clue.update(loc, false);
                        has_clue.update(loc2, false);
                    }
                } else {
                    has_clue.update(loc, true);
                    has_clue.update(loc2, true);
                }
            } else {
                has_clue.update(loc, clue != NO_CLUE);
            }
            if let Some(max_clue) = opts.max_clue {
                if max_clue < has_clue.n_clues {
                    has_clue.rollback();
                    continue;
                }
            }

            problem[loc] = clue;
            let next_field = if previous_clue == NO_CLUE {
                let mut f = field.clone();
                f.add_clue(loc, clue);
                f.solve();
                if opts.use_trial_and_error {
                    f.trial_and_error();
                }
                f
            } else {
                solve_test(
                    &problem,
                    &has_clue,
                    opts.use_trial_and_error,
                    dic,
                    consecutive_dic,
                )
            };
            let energy = next_field.decided_cells() - 4 * has_clue.n_clues;

            let update = !next_field.inconsistent()
                && (current_energy < energy
                    || rng.gen::<f64>() < ((energy - current_energy) as f64 / temperature).exp());

            if update {
                current_energy = energy;
                field = next_field;

                let mut clue_filled = true;
                for y in 0..height {
                    for x in 0..width {
                        if has_clue.get((Y(y), X(x))) && problem[(Y(y), X(x))] == NO_CLUE {
                            clue_filled = false;
                        }
                    }
                }
                if field.fully_solved() && clue_filled {
                    return Some(problem);
                }
                updated = true;
                has_clue.forget_history();
                break;
            }
            problem[loc] = previous_clue;
            has_clue.rollback();
        }

        if !updated {
            break;
        }

        temperature *= 0.995f64;
    }

    None
}

fn solve_test<'a, 'b>(
    problem: &Grid<Clue>,
    has_clue: &HasClue,
    use_trial_and_error: bool,
    dic: &'a Dictionary,
    consecutive_dic: &'b ConsecutiveRegionDictionary,
) -> Field<'a, 'b> {
    let height = problem.height();
    let width = problem.width();
    let mut ret = Field::new(height, width, dic, consecutive_dic);

    for y in 0..height {
        for x in 0..width {
            let clue = problem[(Y(y), X(x))];
            if clue != NO_CLUE {
                ret.add_clue((Y(y), X(x)), clue);
            } else if has_clue.get((Y(y), X(x))) {
                ret.decide((Y(y), X(x)), Cell::White);
            }

            if ret.inconsistent() {
                return ret;
            }
        }
    }

    ret.solve();
    if use_trial_and_error {
        ret.trial_and_error();
    }

    ret
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

#![crate_name="sdl2_image"]
#![crate_type = "lib"]

extern crate sdl2;
extern crate libc;
extern crate "sdl2-sys" as sys;

#[macro_use]
extern crate bitflags;

use libc::{c_int, c_char};
use std::ptr;
use std::ffi::CString;
use std::path::Path;
use sdl2::surface::Surface;
use sdl2::render::Texture;
use sdl2::render::Renderer;
use sdl2::rwops::RWops;
use sdl2::version::Version;
use sdl2::get_error;
use sdl2::SdlResult;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(kind="framework", name="SDL2_image")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL2_image")]
    extern {}
}

#[cfg(any(target_os="windows", target_os="linux", target_os="freebsd"))]
mod others {
    #[link(name="SDL2_image")]
    extern {}
}

#[allow(non_camel_case_types, dead_code)]
mod ffi;

/// InitFlags are passed to init() to control which subsystem
/// functionality to load.
bitflags! {
    flags InitFlag : u32 {
        const INIT_JPG  = ffi::IMG_INIT_JPG as u32,
        const INIT_PNG  = ffi::IMG_INIT_PNG as u32,
        const INIT_TIF  = ffi::IMG_INIT_TIF as u32,
        const INIT_WEBP = ffi::IMG_INIT_WEBP as u32
    }
}

/// Static method extensions for creating Surfaces
pub trait LoadSurface {
    // Self is only returned here to type hint to the compiler.
    // The syntax for type hinting in this case is not yet defined.
    // The intended return value is SdlResult<~Surface>.
    fn from_file(filename: &Path) -> SdlResult<Self>;
    fn from_xpm_array(xpm: *const *const i8) -> SdlResult<Self>;
}

/// Method extensions to Surface for saving to disk
pub trait SaveSurface {
    fn save(&self, filename: &Path) -> SdlResult<()>;
    fn save_rw(&self, dst: &mut RWops) -> SdlResult<()>;
}

impl LoadSurface for Surface {
    fn from_file(filename: &Path) -> SdlResult<Surface> {
        //! Loads an SDL Surface from a file
        unsafe {
            let raw = ffi::IMG_Load(CString::new(filename.to_str().unwrap()).unwrap().as_ptr());
            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw, true))
            }
        }
    }

    fn from_xpm_array(xpm: *const *const i8) -> SdlResult<Surface> {
        //! Loads an SDL Surface from XPM data
        unsafe {
            let raw = ffi::IMG_ReadXPMFromArray(xpm as *const *const c_char);
            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw, true))
            }
        }
    }
}

impl SaveSurface for Surface {
    fn save(&self, filename: &Path) -> SdlResult<()> {
        //! Saves an SDL Surface to a file
        unsafe {
            let status = ffi::IMG_SavePNG(self.raw(), CString::new(filename.to_str().unwrap()).unwrap().as_ptr());
            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    fn save_rw(&self, dst: &mut RWops) -> SdlResult<()> {
        //! Saves an SDL Surface to an RWops
        unsafe {
            let status = ffi::IMG_SavePNG_RW(self.raw(), dst.raw(), 0);

            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Method extensions for creating Textures from a Renderer
pub trait LoadTexture {
    fn load_texture(&self, filename: &Path) -> SdlResult<Texture>;
}

impl LoadTexture for Renderer {
    fn load_texture(&self, filename: &Path) -> SdlResult<Texture> {
        //! Loads an SDL Texture from a file
        unsafe {
            let raw = ffi::IMG_LoadTexture(self.raw(), CString::new(filename.to_str().unwrap()).unwrap().as_ptr());
            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Texture{ raw: raw, owned: true })
            }
        }
    }
}

pub fn init(flags: InitFlag) -> InitFlag {
    //! Initializes SDL2_image with InitFlags and returns which
    //! InitFlags were actually used.
    unsafe {
        let used = ffi::IMG_Init(flags.bits() as c_int);
        InitFlag::from_bits_truncate(used as u32)
    }
}

pub fn quit() {
    //! Teardown the SDL2_Image subsystem
    unsafe { ffi::IMG_Quit(); }
}

pub fn get_linked_version() -> Version {
    //! Returns the version of the dynamically linked SDL_image library
    unsafe {
        Version::from_ll(ffi::IMG_Linked_Version())
    }
}

#[inline]
fn to_surface_result(raw: *const sys::surface::SDL_Surface) -> SdlResult<Surface> {
    if raw == ptr::null() {
        Err(get_error())
    } else {
        unsafe { Ok(Surface::from_ll(raw, true)) }
    }
}

pub trait ImageRWops {
    /// load as a surface. except TGA
    fn load(&self) -> SdlResult<Surface>;
    /// load as a surface. This can load all supported image formats.
    fn load_typed(&self, _type: &str) -> SdlResult<Surface>;

    fn load_cur(&self) -> SdlResult<Surface>;
    fn load_ico(&self) -> SdlResult<Surface>;
    fn load_bmp(&self) -> SdlResult<Surface>;
    fn load_pnm(&self) -> SdlResult<Surface>;
    fn load_xpm(&self) -> SdlResult<Surface>;
    fn load_xcf(&self) -> SdlResult<Surface>;
    fn load_pcx(&self) -> SdlResult<Surface>;
    fn load_gif(&self) -> SdlResult<Surface>;
    fn load_jpg(&self) -> SdlResult<Surface>;
    fn load_tif(&self) -> SdlResult<Surface>;
    fn load_png(&self) -> SdlResult<Surface>;
    fn load_tga(&self) -> SdlResult<Surface>;
    fn load_lbm(&self) -> SdlResult<Surface>;
    fn load_xv(&self)  -> SdlResult<Surface>;
    fn load_webp(&self) -> SdlResult<Surface>;

    fn is_cur(&self) -> bool;
    fn is_ico(&self) -> bool;
    fn is_bmp(&self) -> bool;
    fn is_pnm(&self) -> bool;
    fn is_xpm(&self) -> bool;
    fn is_xcf(&self) -> bool;
    fn is_pcx(&self) -> bool;
    fn is_gif(&self) -> bool;
    fn is_jpg(&self) -> bool;
    fn is_tif(&self) -> bool;
    fn is_png(&self) -> bool;
    fn is_lbm(&self) -> bool;
    fn is_xv(&self)  -> bool;
    fn is_webp(&self) -> bool;
}

impl ImageRWops for RWops {
    fn load(&self) -> SdlResult<Surface> {
        let raw = unsafe {
            ffi::IMG_Load_RW(self.raw(), 0)
        };
        to_surface_result(raw)
    }
    fn load_typed(&self, _type: &str) -> SdlResult<Surface> {
        let raw = unsafe {
            ffi::IMG_LoadTyped_RW(self.raw(), 0, CString::from_slice(_type.as_bytes()).as_ptr())
        };
        to_surface_result(raw)
    }

    fn load_cur(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadCUR_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_ico(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadICO_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_bmp(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadBMP_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pnm(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadPNM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xpm(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadXPM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xcf(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadXCF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pcx(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadPCX_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_gif(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadGIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_jpg(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadJPG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tif(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadTIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_png(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadPNG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tga(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadTGA_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_lbm(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadLBM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xv(&self)  -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadXV_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_webp(&self)  -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadWEBP_RW(self.raw()) };
        to_surface_result(raw)
    }

    fn is_cur(&self) -> bool {
        unsafe { ffi::IMG_isCUR(self.raw()) == 1 }
    }
    fn is_ico(&self) -> bool {
        unsafe { ffi::IMG_isICO(self.raw()) == 1 }
    }
    fn is_bmp(&self) -> bool {
        unsafe { ffi::IMG_isBMP(self.raw()) == 1 }
    }
    fn is_pnm(&self) -> bool {
        unsafe { ffi::IMG_isPNM(self.raw()) == 1 }
    }
    fn is_xpm(&self) -> bool {
        unsafe { ffi::IMG_isXPM(self.raw()) == 1 }
    }
    fn is_xcf(&self) -> bool {
        unsafe { ffi::IMG_isXCF(self.raw()) == 1 }
    }
    fn is_pcx(&self) -> bool {
        unsafe { ffi::IMG_isPCX(self.raw()) == 1 }
    }
    fn is_gif(&self) -> bool {
        unsafe { ffi::IMG_isGIF(self.raw()) == 1 }
    }
    fn is_jpg(&self) -> bool {
        unsafe { ffi::IMG_isJPG(self.raw()) == 1 }
    }
    fn is_tif(&self) -> bool {
        unsafe { ffi::IMG_isTIF(self.raw()) == 1 }
    }
    fn is_png(&self) -> bool {
        unsafe { ffi::IMG_isPNG(self.raw()) == 1 }
    }
    fn is_lbm(&self) -> bool {
        unsafe { ffi::IMG_isLBM(self.raw()) == 1 }
    }
    fn is_xv(&self)  -> bool {
        unsafe { ffi::IMG_isXV(self.raw())  == 1 }
    }
    fn is_webp(&self) -> bool {
        unsafe { ffi::IMG_isWEBP(self.raw())  == 1 }
    }
}
<commit_msg>Fix Texture creation.<commit_after>#![feature(macro_rules)]

#![crate_name="sdl2_image"]
#![crate_type = "lib"]

extern crate sdl2;
extern crate libc;
extern crate "sdl2-sys" as sys;

#[macro_use]
extern crate bitflags;

use libc::{c_int, c_char};
use std::ptr;
use std::ffi::CString;
use std::path::Path;
use sdl2::surface::Surface;
use sdl2::render::Texture;
use sdl2::render::Renderer;
use sdl2::rwops::RWops;
use sdl2::version::Version;
use sdl2::get_error;
use sdl2::SdlResult;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(kind="framework", name="SDL2_image")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL2_image")]
    extern {}
}

#[cfg(any(target_os="windows", target_os="linux", target_os="freebsd"))]
mod others {
    #[link(name="SDL2_image")]
    extern {}
}

#[allow(non_camel_case_types, dead_code)]
mod ffi;

/// InitFlags are passed to init() to control which subsystem
/// functionality to load.
bitflags! {
    flags InitFlag : u32 {
        const INIT_JPG  = ffi::IMG_INIT_JPG as u32,
        const INIT_PNG  = ffi::IMG_INIT_PNG as u32,
        const INIT_TIF  = ffi::IMG_INIT_TIF as u32,
        const INIT_WEBP = ffi::IMG_INIT_WEBP as u32
    }
}

/// Static method extensions for creating Surfaces
pub trait LoadSurface {
    // Self is only returned here to type hint to the compiler.
    // The syntax for type hinting in this case is not yet defined.
    // The intended return value is SdlResult<~Surface>.
    fn from_file(filename: &Path) -> SdlResult<Self>;
    fn from_xpm_array(xpm: *const *const i8) -> SdlResult<Self>;
}

/// Method extensions to Surface for saving to disk
pub trait SaveSurface {
    fn save(&self, filename: &Path) -> SdlResult<()>;
    fn save_rw(&self, dst: &mut RWops) -> SdlResult<()>;
}

impl LoadSurface for Surface {
    fn from_file(filename: &Path) -> SdlResult<Surface> {
        //! Loads an SDL Surface from a file
        unsafe {
            let raw = ffi::IMG_Load(CString::new(filename.to_str().unwrap()).unwrap().as_ptr());
            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw, true))
            }
        }
    }

    fn from_xpm_array(xpm: *const *const i8) -> SdlResult<Surface> {
        //! Loads an SDL Surface from XPM data
        unsafe {
            let raw = ffi::IMG_ReadXPMFromArray(xpm as *const *const c_char);
            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw, true))
            }
        }
    }
}

impl SaveSurface for Surface {
    fn save(&self, filename: &Path) -> SdlResult<()> {
        //! Saves an SDL Surface to a file
        unsafe {
            let status = ffi::IMG_SavePNG(self.raw(), CString::new(filename.to_str().unwrap()).unwrap().as_ptr());
            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    fn save_rw(&self, dst: &mut RWops) -> SdlResult<()> {
        //! Saves an SDL Surface to an RWops
        unsafe {
            let status = ffi::IMG_SavePNG_RW(self.raw(), dst.raw(), 0);

            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Method extensions for creating Textures from a Renderer
pub trait LoadTexture {
    fn load_texture(&self, filename: &Path) -> SdlResult<Texture>;
}

impl LoadTexture for Renderer {
    fn load_texture(&self, filename: &Path) -> SdlResult<Texture> {
        //! Loads an SDL Texture from a file
        unsafe {
            let raw = ffi::IMG_LoadTexture(self.raw(), CString::new(filename.to_str().unwrap()).unwrap().as_ptr());
            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Texture::from_ll(raw))
            }
        }
    }
}

pub fn init(flags: InitFlag) -> InitFlag {
    //! Initializes SDL2_image with InitFlags and returns which
    //! InitFlags were actually used.
    unsafe {
        let used = ffi::IMG_Init(flags.bits() as c_int);
        InitFlag::from_bits_truncate(used as u32)
    }
}

pub fn quit() {
    //! Teardown the SDL2_Image subsystem
    unsafe { ffi::IMG_Quit(); }
}

pub fn get_linked_version() -> Version {
    //! Returns the version of the dynamically linked SDL_image library
    unsafe {
        Version::from_ll(ffi::IMG_Linked_Version())
    }
}

#[inline]
fn to_surface_result(raw: *const sys::surface::SDL_Surface) -> SdlResult<Surface> {
    if raw == ptr::null() {
        Err(get_error())
    } else {
        unsafe { Ok(Surface::from_ll(raw, true)) }
    }
}

pub trait ImageRWops {
    /// load as a surface. except TGA
    fn load(&self) -> SdlResult<Surface>;
    /// load as a surface. This can load all supported image formats.
    fn load_typed(&self, _type: &str) -> SdlResult<Surface>;

    fn load_cur(&self) -> SdlResult<Surface>;
    fn load_ico(&self) -> SdlResult<Surface>;
    fn load_bmp(&self) -> SdlResult<Surface>;
    fn load_pnm(&self) -> SdlResult<Surface>;
    fn load_xpm(&self) -> SdlResult<Surface>;
    fn load_xcf(&self) -> SdlResult<Surface>;
    fn load_pcx(&self) -> SdlResult<Surface>;
    fn load_gif(&self) -> SdlResult<Surface>;
    fn load_jpg(&self) -> SdlResult<Surface>;
    fn load_tif(&self) -> SdlResult<Surface>;
    fn load_png(&self) -> SdlResult<Surface>;
    fn load_tga(&self) -> SdlResult<Surface>;
    fn load_lbm(&self) -> SdlResult<Surface>;
    fn load_xv(&self)  -> SdlResult<Surface>;
    fn load_webp(&self) -> SdlResult<Surface>;

    fn is_cur(&self) -> bool;
    fn is_ico(&self) -> bool;
    fn is_bmp(&self) -> bool;
    fn is_pnm(&self) -> bool;
    fn is_xpm(&self) -> bool;
    fn is_xcf(&self) -> bool;
    fn is_pcx(&self) -> bool;
    fn is_gif(&self) -> bool;
    fn is_jpg(&self) -> bool;
    fn is_tif(&self) -> bool;
    fn is_png(&self) -> bool;
    fn is_lbm(&self) -> bool;
    fn is_xv(&self)  -> bool;
    fn is_webp(&self) -> bool;
}

impl ImageRWops for RWops {
    fn load(&self) -> SdlResult<Surface> {
        let raw = unsafe {
            ffi::IMG_Load_RW(self.raw(), 0)
        };
        to_surface_result(raw)
    }
    fn load_typed(&self, _type: &str) -> SdlResult<Surface> {
        let raw = unsafe {
            ffi::IMG_LoadTyped_RW(self.raw(), 0, CString::from_slice(_type.as_bytes()).as_ptr())
        };
        to_surface_result(raw)
    }

    fn load_cur(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadCUR_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_ico(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadICO_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_bmp(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadBMP_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pnm(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadPNM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xpm(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadXPM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xcf(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadXCF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pcx(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadPCX_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_gif(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadGIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_jpg(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadJPG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tif(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadTIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_png(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadPNG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tga(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadTGA_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_lbm(&self) -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadLBM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xv(&self)  -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadXV_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_webp(&self)  -> SdlResult<Surface> {
        let raw = unsafe { ffi::IMG_LoadWEBP_RW(self.raw()) };
        to_surface_result(raw)
    }

    fn is_cur(&self) -> bool {
        unsafe { ffi::IMG_isCUR(self.raw()) == 1 }
    }
    fn is_ico(&self) -> bool {
        unsafe { ffi::IMG_isICO(self.raw()) == 1 }
    }
    fn is_bmp(&self) -> bool {
        unsafe { ffi::IMG_isBMP(self.raw()) == 1 }
    }
    fn is_pnm(&self) -> bool {
        unsafe { ffi::IMG_isPNM(self.raw()) == 1 }
    }
    fn is_xpm(&self) -> bool {
        unsafe { ffi::IMG_isXPM(self.raw()) == 1 }
    }
    fn is_xcf(&self) -> bool {
        unsafe { ffi::IMG_isXCF(self.raw()) == 1 }
    }
    fn is_pcx(&self) -> bool {
        unsafe { ffi::IMG_isPCX(self.raw()) == 1 }
    }
    fn is_gif(&self) -> bool {
        unsafe { ffi::IMG_isGIF(self.raw()) == 1 }
    }
    fn is_jpg(&self) -> bool {
        unsafe { ffi::IMG_isJPG(self.raw()) == 1 }
    }
    fn is_tif(&self) -> bool {
        unsafe { ffi::IMG_isTIF(self.raw()) == 1 }
    }
    fn is_png(&self) -> bool {
        unsafe { ffi::IMG_isPNG(self.raw()) == 1 }
    }
    fn is_lbm(&self) -> bool {
        unsafe { ffi::IMG_isLBM(self.raw()) == 1 }
    }
    fn is_xv(&self)  -> bool {
        unsafe { ffi::IMG_isXV(self.raw())  == 1 }
    }
    fn is_webp(&self) -> bool {
        unsafe { ffi::IMG_isWEBP(self.raw())  == 1 }
    }
}
<|endoftext|>


<commit_before>//! Utility functionality for integrating git hooks in the store
//!
//! Contains primitives to create a repository within the store path

use git2::Repository;
use git2::RepositoryInitOptions;

use libimagstore::store::Store;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::result::Result;

pub fn mkrepo(store: &Store) -> Result<()> {
    let mut opts = RepositoryInitOptions::new();
    opts.bare(false);
    opts.no_reinit(true);
    opts.mkdir(false);
    opts.external_template(false);
    Repository::init_opts(store.path(), &opts)
        .map(|_| ())
        .map_err_into(GHEK::MkRepo)
}

<commit_msg>Remove unused fn: mkrepo()<commit_after>//! Utility functionality for integrating git hooks in the store
//!
//! Contains primitives to create a repository within the store path

use git2::Repository;
use git2::RepositoryInitOptions;

use libimagstore::store::Store;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::result::Result;

<|endoftext|>


<commit_before>use super::*;
use crate::mem::{self, MaybeUninit};
use core::array::FixedSizeArray;

// Verify that the bytes of initialized RWLock are the same as in
// libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to
// be changed too.
#[test]
fn test_c_rwlock_initializer() {
    #[rustfmt::skip]
    const RWLOCK_INIT: &[u8] = &[
        /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    ];

    #[inline(never)]
    fn zero_stack() {
        test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
    }

    #[inline(never)]
    unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
        init.write(RWLock::new());
    }

    unsafe {
        // try hard to make sure that the padding/unused bytes in RWLock
        // get initialized as 0. If the assertion below fails, that might
        // just be an issue with the test code and not with the value of
        // RWLOCK_INIT.
        zero_stack();
        let mut init = MaybeUninit::<RWLock>::zeroed();
        rwlock_new(&mut init);
        assert_eq!(mem::transmute::<_, [u8; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT)
    };
}
<commit_msg>Improve SGX RWLock initializer test<commit_after>use super::*;

// Verify that the byte pattern libunwind uses to initialize an RWLock is
// equivalent to the value of RWLock::new(). If the value changes,
// `src/UnwindRustSgx.h` in libunwind needs to be changed too.
#[test]
fn test_c_rwlock_initializer() {
    #[rustfmt::skip]
    const C_RWLOCK_INIT: &[u8] = &[
        /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    ];

    // For the test to work, we need the padding/unused bytes in RWLock to be
    // initialized as 0. In practice, this is the case with statics.
    static RUST_RWLOCK_INIT: RWLock = RWLock::new();

    unsafe {
        // If the assertion fails, that not necessarily an issue with the value
        // of C_RWLOCK_INIT. It might just be an issue with the way padding
        // bytes are initialized in the test code.
        assert_eq!(&crate::mem::transmute_copy::<_, [u8; 144]>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT);
    };
}
<|endoftext|>