<commit_before>
extern mod core;
extern mod cortex;
extern mod rustusb = "usb";

use core::fail::abort;
use core::option::{Option, Some, None};
use cortex::regs::{store, load, set, clear, wait_for};
use sim::{enable_clock, USBOTG};
use sim::{select_usb_source};
use rustusb::usb::{Usb_Peripheral, Usb_Data};
use rustusb::usb::{Endpoint_Type};
use rustusb::stream::Stream_Handler;

mod sim;

static mut USB_PERIPHERAL: Option<Freescale_Usb> = None;

static BASE_USB: u32        = 0x4007_2000;
static USB_USBTRC0: u32     = BASE_USB + 0x010C;
static USB_USBCTRL: u32     = BASE_USB + 0x0100;
static USB_CTL: u32         = BASE_USB + 0x0094;
static USB_ADDR: u32        = BASE_USB + 0x0098;
static USB_CONTROL: u32     = BASE_USB + 0x0108;
static USB_INTEN: u32       = BASE_USB + 0x0084;

static USB_STAT: u32        = BASE_USB + 0x0090;
static USB_ISTAT: *mut u8   = (BASE_USB + 0x0080) as *mut u8;
static USB_ERRSTAT: u32     = BASE_USB + 0x0088;
static USB_OTGISTAT: u32    = BASE_USB + 0x0010;

static USB_BDTPAGE1: u32    = BASE_USB + 0x009C;
static USB_BDTPAGE2: u32    = BASE_USB + 0x00B0;
static USB_BDTPAGE3: u32    = BASE_USB + 0x00B4;

static USB_ENDPT0: u32      = BASE_USB + 0x00C0;

pub enum Usb_Int {
    USBRSTEN = 0x01,
    ERROREN  = 0x02,
    SOFTOKEN = 0x04,
    TOKDNEEN = 0x08,
    SLEEPEN  = 0x10,
    RESUMEEN = 0x20,
    ATTACHEN = 0x40,
    STALLEN  = 0x80
}

fn zero_bdt() {
}

fn set_bdt() {
    // Need a 512 byte aligned memory
    let addr: u32 = 0x00; // This should be a pointer to the BDT
    unsafe {
        store(USB_BDTPAGE1 as *mut u8, (addr << 8) as u8);
        store(USB_BDTPAGE2 as *mut u8, (addr << 16) as u8);
        store(USB_BDTPAGE3 as *mut u8, (addr << 24) as u8);
    }
}

fn usb_reset_hard() {
    unsafe {
        let addr: *mut u8 = (BASE_USB + 0x010C) as *mut u8;
        store(addr, 0x80);
        wait_for(addr as *u8, 0x80, 0x00);
    }
}

/// Read PID from BDT
pub fn get_pid(ep: u8) {

}

/// Sets the STALL flag for ENDPTx register
pub fn stall_ep(ep: u32) {
    if ep <= 15 {
        let addr = USB_ENDPT0 + ep*4;
        unsafe {
            set(addr as *mut u8, 0x02);
        }
    }
}

/// Clears the STALL flag for ENDPTx register
pub fn unstall_ep(ep: u32) {
    if ep <= 15 {
        let addr = USB_ENDPT0 + ep*4;
        unsafe {
            clear(addr as *mut u8, 0x02);
        }
    }
}

/// Enables the endpoint (also unstalls)
pub fn enable_ep(ep: u32, control: bool, handshake: bool) {
    if ep <= 15 {
        let addr = USB_ENDPT0 + ep*4;
        let val = 0x0c; // |
        //    (if !control 0x10 else 0) |
        //    (if handshake 0x01 else 0)
        unsafe {
            store(addr as *mut u8, val as u8);
        }
    }
}

// TODO: Need to handle BDT pingponging in here.

/// Resume token processing
pub fn resume() {
    unsafe {
        clear(USB_CTL as *mut u8, 0x20);
    }
}

pub fn set_interrupt(val: Usb_Int) {
    unsafe {
        set(USB_INTEN as *mut u8, val as u8);
    }
}

pub fn set_interrupts(val: u8) {
    unsafe {
        store(USB_INTEN as *mut u8, val as u8);
    }
}

pub struct Freescale_Usb {
    bdt: *mut u32,
    max_ep: uint,
    ping: [bool, ..32],
}

impl Freescale_Usb {
    /// Create a new instance
    /// Specify number of endpoints including EP0
    pub fn new(max_endpoint: uint) -> &'static mut Freescale_Usb {
        let size = if max_endpoint > 15 {
            512
        } else {
            max_endpoint * 32
        };

        // Need 512byte aligned memory for BDT
        let ptr = unsafe { core::heap::aligned_alloc_raw(512, size) };
        let this =Freescale_Usb {
            bdt: ptr as *mut u32,
            max_ep: max_endpoint,
            ping: [false, ..32],
        };
        unsafe { USB_PERIPHERAL = Some(this); }
        Freescale_Usb::get()
    }

    pub fn get() -> &'static mut Freescale_Usb {
        unsafe {
            match USB_PERIPHERAL {
                Some(ref mut module) => module,
                None => abort()
            }
        }
    }

    fn get_bdt_offset(ep: uint, is_tx: bool, is_odd: bool) -> uint {
        32*ep + (if is_tx { 16 } else { 0 } ) +
            (if is_odd { 8 } else { 0 } )
    }

    pub fn get_bdt_setting(&self, ep: uint, is_tx: bool, is_odd: bool) -> u32 {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd);
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            load(addr)
        }
    }

    pub fn set_bdt_setting(&self, ep: uint, is_tx: bool, is_odd: bool, val: u32) {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd);
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            store(addr, val);
        }
    }

    pub fn get_bdt_address(&self, ep: uint, is_tx: bool, is_odd: bool) -> u32 {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd) + 4;
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            load(addr)
        }
    }

    pub fn set_bdt_address(&self, ep: uint, is_tx: bool, is_odd: bool, val: u32) {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd) + 4;
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            store(addr, val);
        }
    }
}

// TODO: Implement Drop trait to free the bdt

impl Usb_Peripheral for Freescale_Usb {
    /// Enables the USB peripheral
    /// Selects clock source, resets peripheral hardware,
    /// allocates needed memory, runs software usb reset
    fn init(&self) {
        // Enable the SIM clock for USB
        enable_clock(USBOTG);

        // Set the USB clock source to internal
        select_usb_source(true);

        // Cycle the USB module through a hard reset
        usb_reset_hard();

        // Load the address of the BDT
        unsafe {
            let addr = self.bdt as u32;
            store(USB_BDTPAGE1 as *mut u8, (addr << 8) as u8);
            store(USB_BDTPAGE2 as *mut u8, (addr << 16) as u8);
            store(USB_BDTPAGE3 as *mut u8, (addr << 24) as u8);
        }

        // Enable the transceiver and disable pulldowns
        unsafe { store(USB_USBCTRL as *mut u8, 0x00); }

        // Run the software USB reset
        self.reset();

        // TODO: Move into usb crate
        // Enable interrupts for USBRST, TOKDNE and STALL
        set_interrupts(STALLEN as u8 |
                      TOKDNEEN as u8 |
                      USBRSTEN as u8);
    }

    /// Attaches the device to the bus
    /// Enables relevant pullup
    fn attach(&self) {
        // Enable the DP pullup
        unsafe { set(USB_CONTROL as *mut u8, 0x10); }
    }

    /// Moves peripheral to default state
    /// Reset address, reset all endpoints,
    /// clear any status flags
    fn reset(&self) {
        unsafe {
            // Disable and suspend USB module
            set(USB_CTL as *mut u8, 0x22);

            // TODO: Reset pingpong register

            // Clear any remaining status flags
            store(USB_ISTAT as *mut u8, 0xFF);
            store(USB_ERRSTAT as *mut u8, 0xFF);
            store(USB_OTGISTAT as *mut u8, 0xFF);

            // Zero out BDT
            core::ptr::set_memory(self.bdt as *mut u8, 0, self.max_ep*32);

            // Reset address
            self.set_address(0x00);

            // Enable USB module
            store(USB_CTL as *mut u8, 0x01);
        }
    }

    fn poll(&self) {
        USB0_Handler();
    }

    fn max_endpoints(&self) -> uint {
        16
    }

    fn queue_rx(&mut self, ep: uint, stream: &Stream_Handler) {
        // Get pingpong status
        let odd = self.ping[ep*2];

        // Check BDT is free
        let stat = self.get_bdt_setting(ep, false, odd);
        if stat & 0x80 > 0 {
            abort();
        }

        // Transfer Stream_Handler into BDT
        let addr = stream.address() as u32;
        self.set_bdt_address(ep, false, odd, addr);

        let len = stream.len();
        let data1 = if stream.data1() { 1 } else { 0 };

        // Activate BDT
        let val = ((len & 0x3FF) << 16) |
            0x88 | (data1 << 6);
        let stat = self.set_bdt_setting(ep, false, odd, val as u32);

        // Swap pingpong
        self.ping[ep*2] = !odd;
    }

    fn set_address(&self, addr: u8) {
        unsafe {
            store(USB_ADDR as *mut u8, addr);
        }
    }

    fn ep_enable(&self, ep: uint, typ: Endpoint_Type) {
        // Make sure ep is in range
        if ep > 15 {
            abort();
        }

        // Work out flags required for type
        let val = match typ {
            rustusb::usb::Control         => 0x0D,
            rustusb::usb::Tx_Only         => 0x15,
            rustusb::usb::Rx_Only         => 0x19,
            rustusb::usb::Tx_Rx           => 0x1D,
            rustusb::usb::Isochronous_Tx  => 0x14,
            rustusb::usb::Isochronous_Rx  => 0x18
        };

        // Set enpoint
        let addr = USB_ENDPT0 + (ep as u32)*4;
        unsafe {
            store(addr as *mut u8, val as u8);
        }
    }

    fn ep_stall(&self, ep: uint) {

    }

    fn ep_unstall(&self, ep: uint) {

    }
}

#[no_mangle]
pub extern "C" fn USB0_Handler() {
    // Check module has been initialised (needed?)
    if !Usb_Data::is_ready() {
        return;
    }

    // Get interrupt status
    let istat = unsafe { load(USB_ISTAT) };

    // Get module
    let module = Usb_Data::get();

    // On usbrst call reset and return
    if istat & 1 > 0 {
        module.on_reset();
        return;
    }

    // On stall call stall
    if istat & 0x80 > 0 {
        module.on_stall();
    }

    // On tokdne call out to handle_transaction
    if istat & 0x08 > 0 {
        // Get token info
        let stat = unsafe { load(USB_STAT as *mut u8) as uint };
        let ep = stat >> 4;
        let tx = (stat & 0x08) > 0;
        let odd = (stat & 0x04) > 0;

        let this = Freescale_Usb::get();
        let bdt_info = this.get_bdt_setting(ep, tx, odd);
        let pid = (bdt_info >> 2) & 0x0F;
        let len = (bdt_info >> 16) & 0x3FF;

        module.on_token(ep, tx, pid as uint, len as uint);

        // Update CTL after processing a SETUP token (will have been paused)
        if pid == 0x0D {
            unsafe { store(USB_CTL as *mut u8, 0x01); }
        }
    }

    // Clear flags
    // TODO: Does this need to be more complicated?
    unsafe { store(USB_ISTAT, istat); }
}
<commit_msg>Begin clearing out the usb peripheral code<commit_after>
extern mod core;
extern mod cortex;
extern mod rustusb = "usb";

use core::fail::abort;
use core::option::{Option, Some, None};
use cortex::regs::{store, load, set, wait_for};
use sim::{enable_clock, USBOTG};
use sim::{select_usb_source};
use rustusb::usb::{Usb_Peripheral, Usb_Data};
use rustusb::usb::{Endpoint_Type};
use rustusb::stream::Stream_Handler;

mod sim;

static mut USB_PERIPHERAL: Option<Freescale_Usb> = None;

static BASE_USB: u32        = 0x4007_2000;
static USB_USBTRC0: u32     = BASE_USB + 0x010C;
static USB_USBCTRL: u32     = BASE_USB + 0x0100;
static USB_CTL: u32         = BASE_USB + 0x0094;
static USB_ADDR: u32        = BASE_USB + 0x0098;
static USB_CONTROL: u32     = BASE_USB + 0x0108;
static USB_INTEN: u32       = BASE_USB + 0x0084;

static USB_STAT: u32        = BASE_USB + 0x0090;
static USB_ISTAT: *mut u8   = (BASE_USB + 0x0080) as *mut u8;
static USB_ERRSTAT: u32     = BASE_USB + 0x0088;
static USB_OTGISTAT: u32    = BASE_USB + 0x0010;

static USB_BDTPAGE1: u32    = BASE_USB + 0x009C;
static USB_BDTPAGE2: u32    = BASE_USB + 0x00B0;
static USB_BDTPAGE3: u32    = BASE_USB + 0x00B4;

static USB_ENDPT0: u32      = BASE_USB + 0x00C0;

pub enum Usb_Int {
    USBRSTEN = 0x01,
    ERROREN  = 0x02,
    SOFTOKEN = 0x04,
    TOKDNEEN = 0x08,
    SLEEPEN  = 0x10,
    RESUMEEN = 0x20,
    ATTACHEN = 0x40,
    STALLEN  = 0x80
}

// fn zero_bdt() {
// }
//
// fn set_bdt() {
//     // Need a 512 byte aligned memory
//     let addr: u32 = 0x00; // This should be a pointer to the BDT
//     unsafe {
//         store(USB_BDTPAGE1 as *mut u8, (addr << 8) as u8);
//         store(USB_BDTPAGE2 as *mut u8, (addr << 16) as u8);
//         store(USB_BDTPAGE3 as *mut u8, (addr << 24) as u8);
//     }
// }
//
//
// /// Read PID from BDT
// pub fn get_pid(ep: u8) {
//
// }
//
// /// Sets the STALL flag for ENDPTx register
// pub fn stall_ep(ep: u32) {
//     if ep <= 15 {
//         let addr = USB_ENDPT0 + ep*4;
//         unsafe {
//             set(addr as *mut u8, 0x02);
//         }
//     }
// }
//
// /// Clears the STALL flag for ENDPTx register
// pub fn unstall_ep(ep: u32) {
//     if ep <= 15 {
//         let addr = USB_ENDPT0 + ep*4;
//         unsafe {
//             clear(addr as *mut u8, 0x02);
//         }
//     }
// }
//
// /// Enables the endpoint (also unstalls)
// pub fn enable_ep(ep: u32, control: bool, handshake: bool) {
//     if ep <= 15 {
//         let addr = USB_ENDPT0 + ep*4;
//         let val = 0x0c; // |
//         //    (if !control 0x10 else 0) |
//         //    (if handshake 0x01 else 0)
//         unsafe {
//             store(addr as *mut u8, val as u8);
//         }
//     }
// }
//
// /// Resume token processing
// pub fn resume() {
//     unsafe {
//         clear(USB_CTL as *mut u8, 0x20);
//     }
// }
//
// pub fn set_interrupt(val: Usb_Int) {
//     unsafe {
//         set(USB_INTEN as *mut u8, val as u8);
//     }
// }
//

pub struct Freescale_Usb {
    bdt: *mut u32,
    max_ep: uint,
    ping: [bool, ..32],
}

impl Freescale_Usb {
    /// Create a new instance
    /// Specify number of endpoints including EP0
    pub fn new(max_endpoint: uint) -> &'static mut Freescale_Usb {
        let size = if max_endpoint > 15 {
            512
        } else {
            max_endpoint * 32
        };

        // Need 512byte aligned memory for BDT
        let ptr = unsafe { core::heap::aligned_alloc_raw(512, size) };
        let this =Freescale_Usb {
            bdt: ptr as *mut u32,
            max_ep: max_endpoint,
            ping: [false, ..32],
        };
        unsafe { USB_PERIPHERAL = Some(this); }
        Freescale_Usb::get()
    }

    pub fn get() -> &'static mut Freescale_Usb {
        unsafe {
            match USB_PERIPHERAL {
                Some(ref mut module) => module,
                None => abort()
            }
        }
    }

    fn get_bdt_offset(ep: uint, is_tx: bool, is_odd: bool) -> uint {
        32*ep + (if is_tx { 16 } else { 0 } ) +
            (if is_odd { 8 } else { 0 } )
    }

    pub fn get_bdt_setting(&self, ep: uint, is_tx: bool, is_odd: bool) -> u32 {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd);
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            load(addr)
        }
    }

    pub fn set_bdt_setting(&self, ep: uint, is_tx: bool, is_odd: bool, val: u32) {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd);
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            store(addr, val);
        }
    }

    pub fn get_bdt_address(&self, ep: uint, is_tx: bool, is_odd: bool) -> u32 {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd) + 4;
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            load(addr)
        }
    }

    pub fn set_bdt_address(&self, ep: uint, is_tx: bool, is_odd: bool, val: u32) {
        let offset = Freescale_Usb::get_bdt_offset(ep, is_tx, is_odd) + 4;
        let addr = ((self.bdt as u32) + offset as u32) as *mut u32;
        unsafe {
            store(addr, val);
        }
    }

    fn usb_reset_hard(&self) {
        unsafe {
            let addr: *mut u8 = (BASE_USB + 0x010C) as *mut u8;
            store(addr, 0x80);
            wait_for(addr as *u8, 0x80, 0x00);
        }
    }

    fn set_interrupts(&self, val: u8) {
        unsafe {
            store(USB_INTEN as *mut u8, val as u8);
        }
    }
}

// TODO: Implement Drop trait to free the bdt

impl Usb_Peripheral for Freescale_Usb {
    /// Enables the USB peripheral
    /// Selects clock source, resets peripheral hardware,
    /// allocates needed memory, runs software usb reset
    fn init(&self) {
        // Enable the SIM clock for USB
        enable_clock(USBOTG);

        // Set the USB clock source to internal
        select_usb_source(true);

        // Cycle the USB module through a hard reset
        self.usb_reset_hard();

        // Load the address of the BDT
        unsafe {
            let addr = self.bdt as u32;
            store(USB_BDTPAGE1 as *mut u8, (addr << 8) as u8);
            store(USB_BDTPAGE2 as *mut u8, (addr << 16) as u8);
            store(USB_BDTPAGE3 as *mut u8, (addr << 24) as u8);
        }

        // Enable the transceiver and disable pulldowns
        unsafe { store(USB_USBCTRL as *mut u8, 0x00); }

        // Run the software USB reset
        self.reset();

        // TODO: Move into usb crate
        // Enable interrupts for USBRST, TOKDNE and STALL
        self.set_interrupts(STALLEN as u8 |
                      TOKDNEEN as u8 |
                      USBRSTEN as u8);
    }

    /// Attaches the device to the bus
    /// Enables relevant pullup
    fn attach(&self) {
        // Enable the DP pullup
        unsafe { set(USB_CONTROL as *mut u8, 0x10); }
    }

    /// Moves peripheral to default state
    /// Reset address, reset all endpoints,
    /// clear any status flags
    fn reset(&self) {
        unsafe {
            // Disable and suspend USB module
            set(USB_CTL as *mut u8, 0x22);

            // TODO: Reset pingpong register

            // Clear any remaining status flags
            store(USB_ISTAT as *mut u8, 0xFF);
            store(USB_ERRSTAT as *mut u8, 0xFF);
            store(USB_OTGISTAT as *mut u8, 0xFF);

            // Zero out BDT
            core::ptr::set_memory(self.bdt as *mut u8, 0, self.max_ep*32);

            // Reset address
            self.set_address(0x00);

            // Enable USB module
            store(USB_CTL as *mut u8, 0x01);
        }
    }

    fn poll(&self) {
        USB0_Handler();
    }

    fn max_endpoints(&self) -> uint {
        16
    }

    fn queue_rx(&mut self, ep: uint, stream: &Stream_Handler) {
        // Get pingpong status
        let odd = self.ping[ep*2];

        // Check BDT is free
        let stat = self.get_bdt_setting(ep, false, odd);
        if stat & 0x80 > 0 {
            abort();
        }

        // Transfer Stream_Handler into BDT
        let addr = stream.address() as u32;
        self.set_bdt_address(ep, false, odd, addr);

        let len = stream.len();
        let data1 = if stream.data1() { 1 } else { 0 };

        // Activate BDT
        let val = ((len & 0x3FF) << 16) |
            0x88 | (data1 << 6);
        let stat = self.set_bdt_setting(ep, false, odd, val as u32);

        // Swap pingpong
        self.ping[ep*2] = !odd;
    }

    fn set_address(&self, addr: u8) {
        unsafe {
            store(USB_ADDR as *mut u8, addr);
        }
    }

    fn ep_enable(&self, ep: uint, typ: Endpoint_Type) {
        // Make sure ep is in range
        if ep > 15 {
            abort();
        }

        // Work out flags required for type
        let val = match typ {
            rustusb::usb::Control         => 0x0D,
            rustusb::usb::Tx_Only         => 0x15,
            rustusb::usb::Rx_Only         => 0x19,
            rustusb::usb::Tx_Rx           => 0x1D,
            rustusb::usb::Isochronous_Tx  => 0x14,
            rustusb::usb::Isochronous_Rx  => 0x18
        };

        // Set enpoint
        let addr = USB_ENDPT0 + (ep as u32)*4;
        unsafe {
            store(addr as *mut u8, val as u8);
        }
    }

    fn ep_stall(&self, ep: uint) {

    }

    fn ep_unstall(&self, ep: uint) {

    }
}

/// Handler for usb interrupts
/// Uses following regs
///   ISTAT - To see which flasg are set and clear them after
///   STAT - To get info on completed transaction
/// Also reads from the BDT specified by STAT register
#[no_mangle]
pub extern "C" fn USB0_Handler() {
    // Check module has been initialised (needed?)
    if !Usb_Data::is_ready() {
        return;
    }

    // Get interrupt status
    let istat = unsafe { load(USB_ISTAT) };

    // Get module
    let module = Usb_Data::get();

    // On usbrst call reset and return
    if istat & 1 > 0 {
        module.on_reset();
        return;
    }

    // On stall call stall
    if istat & 0x80 > 0 {
        module.on_stall();
    }

    // On tokdne call out to handle_transaction
    // TODO: Will probably need to work differently for isochronous stuff
    if istat & 0x08 > 0 {
        // Get token info
        let stat = unsafe { load(USB_STAT as *mut u8) as uint };
        let ep = stat >> 4;
        let tx = (stat & 0x08) > 0;
        let odd = (stat & 0x04) > 0;

        let this = Freescale_Usb::get();
        let bdt_info = this.get_bdt_setting(ep, tx, odd);
        let pid = (bdt_info >> 2) & 0x0F;
        let len = (bdt_info >> 16) & 0x3FF;

        module.on_token(ep, tx, pid as uint, len as uint);

        // Update CTL after processing a SETUP token (will have been paused)
        if pid == 0x0D {
            unsafe { store(USB_CTL as *mut u8, 0x01); }
        }
    }

    // Clear flags
    // TODO: Does this need to be more complicated?
    unsafe { store(USB_ISTAT, istat); }
}
<|endoftext|>


<commit_before>use gfx_device_gl::Factory;
use piston_window::*;

use game::asset_factory::AssetFactory;
use game::config::Config;
use game::render_options::RenderOptions;
use game::scenes::{Scene, SceneResult};
use game::window::GameWindow;

pub struct Menu {
    config: Config,
    asset_factory: AssetFactory,
    menu_items: Vec<MenuItem>,
    selected_index: usize,
}

impl Menu {
    pub fn new(config: Config, gfx_factory: Factory) -> Menu {
        Menu {
            config: config,
            asset_factory: AssetFactory::new(gfx_factory),
            menu_items: Vec::new(),
            selected_index: 0,
        }
    }

    fn up(&mut self) -> MenuResult {
        if self.selected_index > 0 {
            self.selected_index -= 1;
        }

        MenuResult::None
    }

    fn down(&mut self) -> MenuResult {
        if self.selected_index < self.menu_items.len() - 1 {
            self.selected_index += 1;
        }

        MenuResult::None
    }

    pub fn add_item<S>(&mut self, text: S, result: MenuResult)
        where S: Into<String>
    {
        let current_length = self.menu_items.len();
        self.menu_items.push(MenuItem::new(text, result, current_length));
    }
}

impl Scene for Menu {
    fn process(&mut self, e: &Event) -> SceneResult {
        match *e {
            Event::Input(ref input_event) => {
                if let Input::Press(ref button) = *input_event {
                    SceneResult::from(match *button {
                        Button::Keyboard(Key::Up) => self.up(),
                        Button::Keyboard(Key::Down) => self.down(),
                        Button::Keyboard(Key::Return) => {
                            self.menu_items[self.selected_index].result
                        }
                        _ => MenuResult::None,
                    })
                } else {
                    SceneResult::None
                }
            }
            _ => SceneResult::None,
        }
    }

    fn render(&mut self, window: &mut GameWindow, e: &Event) {
        window.draw_2d(e, |mut c, g| {
            clear([0.0, 0.0, 0.0, 1.0], g);

            let mut options = RenderOptions {
                config: &self.config,
                context: &mut c,
                graphics: g,
                character_cache: self.asset_factory.font.as_mut().unwrap(),
            };

            let mut index = 0;
            for item in &mut self.menu_items {
                if self.selected_index == index {
                    item.label.set_font_size(24);
                } else {
                    item.label.set_font_size(16);
                }
                item.label.render(&mut options);
                index += 1;
            }
        });
    }
}

pub struct MenuItem {
    result: MenuResult,
    label: ::game::text::Text,
}

impl MenuItem {
    fn new<S>(text: S, result: MenuResult, item_count: usize) -> MenuItem
        where S: Into<String>
    {
        MenuItem {
            result: result,
            label: ::game::text::Text::new(text.into(), 16, 50, 200 + (item_count * 50), [1.0; 4]),
        }
    }
}

#[derive(Clone, Copy)]
pub enum MenuResult {
    None,
    NewGame,
    Quit,
}<commit_msg>Applied a clippy lint<commit_after>use gfx_device_gl::Factory;
use piston_window::*;

use game::asset_factory::AssetFactory;
use game::config::Config;
use game::render_options::RenderOptions;
use game::scenes::{Scene, SceneResult};
use game::window::GameWindow;

pub struct Menu {
    config: Config,
    asset_factory: AssetFactory,
    menu_items: Vec<MenuItem>,
    selected_index: usize,
}

impl Menu {
    pub fn new(config: Config, gfx_factory: Factory) -> Menu {
        Menu {
            config: config,
            asset_factory: AssetFactory::new(gfx_factory),
            menu_items: Vec::new(),
            selected_index: 0,
        }
    }

    fn up(&mut self) -> MenuResult {
        if self.selected_index > 0 {
            self.selected_index -= 1;
        }

        MenuResult::None
    }

    fn down(&mut self) -> MenuResult {
        if self.selected_index < self.menu_items.len() - 1 {
            self.selected_index += 1;
        }

        MenuResult::None
    }

    pub fn add_item<S>(&mut self, text: S, result: MenuResult)
        where S: Into<String>
    {
        let current_length = self.menu_items.len();
        self.menu_items.push(MenuItem::new(text, result, current_length));
    }
}

impl Scene for Menu {
    fn process(&mut self, e: &Event) -> SceneResult {
        match *e {
            Event::Input(ref input_event) => {
                if let Input::Press(ref button) = *input_event {
                    SceneResult::from(match *button {
                        Button::Keyboard(Key::Up) => self.up(),
                        Button::Keyboard(Key::Down) => self.down(),
                        Button::Keyboard(Key::Return) => {
                            self.menu_items[self.selected_index].result
                        }
                        _ => MenuResult::None,
                    })
                } else {
                    SceneResult::None
                }
            }
            _ => SceneResult::None,
        }
    }

    fn render(&mut self, window: &mut GameWindow, e: &Event) {
        window.draw_2d(e, |mut c, g| {
            clear([0.0, 0.0, 0.0, 1.0], g);

            let mut options = RenderOptions {
                config: &self.config,
                context: &mut c,
                graphics: g,
                character_cache: self.asset_factory.font.as_mut().unwrap(),
            };

            for (index, item) in self.menu_items.iter_mut().enumerate() {
                if self.selected_index == index {
                    item.label.set_font_size(24);
                } else {
                    item.label.set_font_size(16);
                }
                item.label.render(&mut options);
            }
        });
    }
}

pub struct MenuItem {
    result: MenuResult,
    label: ::game::text::Text,
}

impl MenuItem {
    fn new<S>(text: S, result: MenuResult, item_count: usize) -> MenuItem
        where S: Into<String>
    {
        MenuItem {
            result: result,
            label: ::game::text::Text::new(text.into(), 16, 50, 200 + (item_count * 50), [1.0; 4]),
        }
    }
}

#[derive(Clone, Copy)]
pub enum MenuResult {
    None,
    NewGame,
    Quit,
}<|endoftext|>


<commit_before>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::io::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::print::pprust;

import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

/*
// Imports for "the rest of driver::compile_input"
import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

fn file_contains(&str filename, &str needle) -> bool {
    auto r = io::file_reader(filename);
    auto contents = str::unsafe_from_bytes(r.read_whole_stream());
    ret str::find(contents, needle) != -1;
}

fn find_rust_files(&mutable str[] files, str path) {
    if (str::ends_with(path, ".rs")) {
        if (file_contains(path, "xfail-stage1")) {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else if (
            !str::ends_with(path, "constrained-type.rs") &&     // https://github.com/graydon/rust/issues/653
             str::find(path, "utf8") != -1 &&  // https://github.com/graydon/rust/issues/654
             true) {
            //log_err "Skipping " + path + " because of a known bug";
        } else {
            files += ~[path];
        }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1) {
        for (str p in fs::list_dir(path)) {
            find_rust_files(files, p);
        }
    }
}

fn steal_exprs(&ast::crate crate) -> ast::expr[] {
    let @mutable ast::expr[] exprs = @mutable ~[];
    // "Stash" cannot be type-parameterized because of https://github.com/graydon/rust/issues/375
    fn stash_expr(@mutable ast::expr[] es, &@ast::expr e) { *es += ~[*e]; }
    auto v = rec(visit_expr_pre = bind stash_expr(exprs, _) with walk::default_visitor());
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(ast::expr_ e) -> bool {
    alt (e) {
        case (ast::expr_if(_, _, _)) { false }
        case (ast::expr_block(_)) { false }
        case (_) { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(&ast::crate crate, uint i, ast::expr_ newexpr) -> ast::crate {
    let @mutable uint j = @mutable 0u;
    fn fold_expr_rep(@mutable uint j_, uint i_, &ast::expr_ newexpr_, &ast::expr_ original, fold::ast_fold fld) -> ast::expr_ {
      *j_ += 1u;
      if (i_ + 1u == *j_ && safe_to_replace(original)) {
        newexpr_
      } else {
        fold::noop_fold_expr(original, fld)
      }
    }
    auto afp = rec(fold_expr = bind fold_expr_rep(j, i, newexpr, _, _) with *fold::default_ast_fold());
    auto af = fold::make_fold(afp);
    let @ast::crate crate2 = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(uint n) -> uint { let uint i = 0u; while (i < n) { put i; i += 1u; } }

fn devnull() -> io::writer { std::io::string_writer().get_writer() }

fn pp_variants(&ast::crate crate, &session::session sess, &str filename) {
    auto exprs = steal_exprs(crate);
    auto exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each (uint i in under(uint::min(exprsL, 20u))) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each (uint j in under(uint::min(exprsL, 5u))) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                pprust::print_crate(sess.get_codemap(), crate2, filename, devnull(), pprust::no_ann());
            }
        }
    }
}

fn main(vec[str] args) {
    auto files = ~[];
    auto root = "/Users/jruderman/code/rust/src/"; // XXX
    find_rust_files(files, root); // not using time here because that currently screws with passing-a-mutable-array
    log_err uint::str(ivec::len(files)) + " files";

    auto binary = vec::shift[str](args);
    auto binary_dir = fs::dirname(binary);

    let @session::options sopts =
        @rec(library=false,
             static=false,
             optimize=0u,
             debuginfo=false,
             verify=true,
             run_typestate=true,
             save_temps=false,
             stats=false,
             time_passes=false,
             time_llvm_passes=false,
             output_type=link::output_type_bitcode,
             library_search_paths=[binary_dir + "/lib"],
             sysroot=driver::get_default_sysroot(binary),
             cfg=~[],
             test=false);

    for (str file in files) {
        log_err "=== " + file + " ===";
        let session::session sess = driver::build_session(sopts);
        let @ast::crate crate = time(true, "parsing " + file, bind driver::parse_input(sess, ~[], file));
        pprust::print_crate(sess.get_codemap(), crate, file, devnull(), pprust::no_ann());
        pp_variants(*crate, sess, file);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Fuzzer: test round trips through the pretty printer and parser<commit_after>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::io::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

/*
// Imports for "the rest of driver::compile_input"
import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

fn read_whole_file(&str filename) -> str {
    str::unsafe_from_bytes(io::file_reader(filename).read_whole_stream())
}

fn file_contains(&str filename, &str needle) -> bool {
    auto contents = read_whole_file(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(&str haystack, &str needle) -> bool { str::find(haystack, needle) != -1 }

fn find_rust_files(&mutable str[] files, str path) {
    if (str::ends_with(path, ".rs")) {
        if (file_contains(path, "xfail-stage1")) {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else {
            files += ~[path];
        }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1) {
        for (str p in fs::list_dir(path)) {
            find_rust_files(files, p);
        }
    }
}

fn safe_to_steal(ast::expr_ e) -> bool {
    alt (e) {
        // pretty-printer precedence issues -- https://github.com/graydon/rust/issues/670
        case (ast::expr_unary(_, _)) { false }
        case (ast::expr_lit(?lit)) {
            alt(lit.node) {
                case(ast::lit_str(_, _)) { true }
                case(ast::lit_char(_)) { true }
                case(ast::lit_int(_)) { false }
                case(ast::lit_uint(_)) { false }
                case(ast::lit_mach_int(_, _)) { false }
                case(ast::lit_float(_)) { false }
                case(ast::lit_mach_float(_, _)) { false }
                case(ast::lit_nil) { true }
                case(ast::lit_bool(_)) { true }
            }
        }
        case (ast::expr_cast(_, _)) { false }
        case (ast::expr_send(_, _)) { false }
        case (ast::expr_recv(_, _)) { false }
        case (ast::expr_assert(_)) { false }
        case (ast::expr_binary(_, _, _)) { false }
        case (ast::expr_assign(_, _)) { false }
        case (ast::expr_assign_op(_, _, _)) { false }

        // "if (ret) { }" doesn't make sense, at least from a typecheck point of view, but for some reason it's rejected by the *parser*
        case (ast::expr_ret(option::none)) { false }
        case (ast::expr_put(option::none)) { false }

        case (_) { true }
    }
}

fn steal_exprs(&ast::crate crate) -> ast::expr[] {
    let @mutable ast::expr[] exprs = @mutable ~[];
    // "Stash" is not type-parameterized because of the need for safe_to_steal
    fn stash_expr(@mutable ast::expr[] es, &@ast::expr e) { if (safe_to_steal(e.node)) { *es += ~[*e]; } else { /* now my indices are wrong :( */ } }
    auto v = rec(visit_expr_pre = bind stash_expr(exprs, _) with walk::default_visitor());
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(ast::expr_ e) -> bool {
    alt (e) {
        case (ast::expr_if(_, _, _)) { false }
        case (ast::expr_block(_)) { false }
        case (_) { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(&ast::crate crate, uint i, ast::expr_ newexpr) -> ast::crate {
    let @mutable uint j = @mutable 0u;
    fn fold_expr_rep(@mutable uint j_, uint i_, &ast::expr_ newexpr_, &ast::expr_ original, fold::ast_fold fld) -> ast::expr_ {
      *j_ += 1u;
      if (i_ + 1u == *j_ && safe_to_replace(original)) {
        newexpr_
      } else {
        fold::noop_fold_expr(original, fld)
      }
    }
    auto afp = rec(fold_expr = bind fold_expr_rep(j, i, newexpr, _, _) with *fold::default_ast_fold());
    auto af = fold::make_fold(afp);
    let @ast::crate crate2 = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(uint n) -> uint { let uint i = 0u; while (i < n) { put i; i += 1u; } }

fn devnull() -> io::writer { std::io::string_writer().get_writer() }

fn as_str(fn (io::writer) f) -> str { auto w = std::io::string_writer(); f(w.get_writer()); w.get_str() }

fn pp_variants(&ast::crate crate, &codemap::codemap cmap, &str filename) {
    auto exprs = steal_exprs(crate);
    auto exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each (uint i in under(uint::min(exprsL, 20u))) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each (uint j in under(uint::min(exprsL, 5u))) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                check_roundtrip(crate2, cmap, filename + ".4.rs");
            }
        }
    }
}

fn check_roundtrip(@ast::crate crate2, &codemap::codemap cmap, &str fakefilename) {
    auto str3 = as_str(bind pprust::print_crate(cmap, crate2, "empty.rs", _, pprust::no_ann()));
    auto cm4 = codemap::new_codemap();
    if (true
      && !contains(str3, "][]") // https://github.com/graydon/rust/issues/669
      && !contains(str3, "][mutable]") // https://github.com/graydon/rust/issues/669
      && !contains(str3, "][mutable ]") // https://github.com/graydon/rust/issues/669
      && !contains(str3, "self") // crazy rules enforced by parser rather than typechecker?
      && !contains(str3, "spawn") // more precedence issues
      && !contains(str3, "bind") // more precedence issues?
       ) {
        auto crate4 = parser::parse_crate_from_source_str(fakefilename, str3, ~[], cm4);
        // should compare crates at this point, but it's easier to compare strings
        auto str5 = as_str(bind pprust::print_crate(cmap, crate4, "empty.rs", _, pprust::no_ann()));
        if (!str::is_ascii(str3)) {
          log_err "Non-ASCII in " + fakefilename; // why does non-ASCII work correctly with "rustc --pretty normal" but not here???
        } else if (str3 != str5) {
          log_err "Mismatch: " + fakefilename;
          log_err "str3:\n" + str3;
          log_err "str5:\n" + str5;
          fail "Mismatch";
        }
   }
}

fn main(vec[str] args) {
    auto files = ~[];
    auto root = "/Users/jruderman/code/rust/src/"; // XXX
    find_rust_files(files, root); // not using time here because that currently screws with passing-a-mutable-array
    log_err uint::str(ivec::len(files)) + " files";

    for (str file in files) {
        log_err "=== " + file + " ===";
        auto cm = codemap::new_codemap();
        auto src = read_whole_file(file);
        auto crate = parser::parse_crate_from_source_str(file, src, ~[], cm);
        if (!contains(src, "#macro") // https://github.com/graydon/rust/issues/671
         && !str::ends_with(file, "block-expr-precedence.rs") // https://github.com/graydon/rust/issues/674
         && !str::ends_with(file, "syntax-extension-fmt.rs") // an issue where -2147483648 gains an extra negative sign each time through, which i can't reproduce using "rustc --pretty normal"???
) {
            check_roundtrip(crate, cm, file + ".pp.rs");
        }
        //pprust::print_crate(cm, crate, file, devnull(), pprust::no_ann());
        //pp_variants(*crate, cm, file);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>/*!

Rust MIR: a lowered representation of Rust. Also: an experiment!

*/

#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(drain_filter)]
#![feature(exhaustive_patterns)]
#![feature(iter_order_by)]
#![feature(never_type)]
#![feature(specialization)]
#![feature(trusted_len)]
#![feature(try_blocks)]
#![feature(associated_type_bounds)]
#![feature(range_is_empty)]
#![feature(stmt_expr_attributes)]
#![feature(trait_alias)]
#![recursion_limit = "256"]

#[macro_use]
extern crate log;
#[macro_use]
extern crate rustc;

mod borrow_check;
pub mod const_eval;
pub mod dataflow;
pub mod interpret;
pub mod monomorphize;
mod shim;
pub mod transform;
pub mod util;

use rustc::ty::query::Providers;

pub fn provide(providers: &mut Providers<'_>) {
    borrow_check::provide(providers);
    const_eval::provide(providers);
    shim::provide(providers);
    transform::provide(providers);
    monomorphize::partitioning::provide(providers);
    providers.const_eval_validated = const_eval::const_eval_validated_provider;
    providers.const_eval_raw = const_eval::const_eval_raw_provider;
    providers.const_caller_location = const_eval::const_caller_location;
    providers.const_field = |tcx, param_env_and_value| {
        let (param_env, (value, field)) = param_env_and_value.into_parts();
        const_eval::const_field(tcx, param_env, None, field, value)
    };
    providers.destructure_const = |tcx, param_env_and_value| {
        let (param_env, value) = param_env_and_value.into_parts();
        const_eval::destructure_const(tcx, param_env, value)
    };
}
<commit_msg>Rollup merge of #69349 - spastorino:mir-not-an-experiment, r=Dylan-DPC<commit_after>/*!

Rust MIR: a lowered representation of Rust.

*/

#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(drain_filter)]
#![feature(exhaustive_patterns)]
#![feature(iter_order_by)]
#![feature(never_type)]
#![feature(specialization)]
#![feature(trusted_len)]
#![feature(try_blocks)]
#![feature(associated_type_bounds)]
#![feature(range_is_empty)]
#![feature(stmt_expr_attributes)]
#![feature(trait_alias)]
#![recursion_limit = "256"]

#[macro_use]
extern crate log;
#[macro_use]
extern crate rustc;

mod borrow_check;
pub mod const_eval;
pub mod dataflow;
pub mod interpret;
pub mod monomorphize;
mod shim;
pub mod transform;
pub mod util;

use rustc::ty::query::Providers;

pub fn provide(providers: &mut Providers<'_>) {
    borrow_check::provide(providers);
    const_eval::provide(providers);
    shim::provide(providers);
    transform::provide(providers);
    monomorphize::partitioning::provide(providers);
    providers.const_eval_validated = const_eval::const_eval_validated_provider;
    providers.const_eval_raw = const_eval::const_eval_raw_provider;
    providers.const_caller_location = const_eval::const_caller_location;
    providers.const_field = |tcx, param_env_and_value| {
        let (param_env, (value, field)) = param_env_and_value.into_parts();
        const_eval::const_field(tcx, param_env, None, field, value)
    };
    providers.destructure_const = |tcx, param_env_and_value| {
        let (param_env, value) = param_env_and_value.into_parts();
        const_eval::destructure_const(tcx, param_env, value)
    };
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

use std::collections::{BTreeSet, VecDeque};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Point {
    x: usize,
    y: usize,
}

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "({}, {})", self.x, self.y)
    }
}

fn is_wall(Point { x, y }: Point, key: usize) -> bool {
    (x * x + 3 * x + 2 * x * y + y + y * y + key).count_ones() % 2 != 0
}

fn possible_moves(Point { x, y }: Point, key: usize) -> Vec<Point> {
    let mut moves = Vec::new();
    if !is_wall(Point { x: x, y: y + 1 }, key) {
        moves.push(Point { x: x, y: y + 1 })
    }
    if !is_wall(Point { x: x + 1, y: y }, key) {
        moves.push(Point { x: x + 1, y: y })
    }
    if y > 0 && !is_wall(Point { x: x, y: y - 1 }, key) {
        moves.push(Point { x: x, y: y - 1 })
    }
    if x > 0 && !is_wall(Point { x: x - 1, y: y }, key) {
        moves.push(Point { x: x - 1, y: y })
    }
    moves
}

fn shortest_path(start: Point, end: Point, key: usize) -> Option<Vec<Point>> {
    let mut move_queue = VecDeque::new();
    move_queue.push_back((start, Vec::new()));
    let mut visited = BTreeSet::new();
    while let Some((pos, moves)) = move_queue.pop_front() {
        for mov in possible_moves(pos, key) {
            if visited.contains(&mov) {
                continue;
            }
            let new_moves = moves.iter().cloned().chain(std::iter::once(mov)).collect();
            if mov == end {
                return Some(new_moves);
            }
            visited.insert(mov);
            move_queue.push_back((mov, new_moves));
        }
    }
    None
}

fn parse_args() -> std::io::Result<usize> {
    let matches = clap::App::new("Day 13")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("key")
            .index(1)
            .short("k")
            .long("key")
            .help("key for finding path")
            .required(true)
            .takes_value(true))
        .get_matches();
    matches.value_of("key")
        .unwrap()
        .parse()
        .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))
}

fn main() {
    let key = parse_args().unwrap_or_else(|err| panic!("Error reading args: {}", err));
    println!("part 1 path:");
    match shortest_path(Point { x: 1, y: 1 }, Point { x: 31, y: 39 }, key) {
        Some(path) => {
            for mov in &path {
                println!("{}", mov);
            }
            println!("Total length: {}", path.len());
        }
        None => println!("No path found!"),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn example_path() {
        assert_eq!(shortest_path(Point { x: 1, y: 1 }, Point { x: 7, y: 4 }, 10)
                       .expect("No path found")
                       .len(),
                   11)
    }
}
<commit_msg>Day 13 part 2<commit_after>#[macro_use]
extern crate clap;

use std::collections::{BTreeSet, VecDeque};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Point {
    x: usize,
    y: usize,
}

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "({}, {})", self.x, self.y)
    }
}

fn is_wall(Point { x, y }: Point, key: usize) -> bool {
    (x * x + 3 * x + 2 * x * y + y + y * y + key).count_ones() % 2 != 0
}

fn possible_moves(Point { x, y }: Point, key: usize) -> Vec<Point> {
    let mut moves = Vec::new();
    if !is_wall(Point { x: x, y: y + 1 }, key) {
        moves.push(Point { x: x, y: y + 1 })
    }
    if !is_wall(Point { x: x + 1, y: y }, key) {
        moves.push(Point { x: x + 1, y: y })
    }
    if y > 0 && !is_wall(Point { x: x, y: y - 1 }, key) {
        moves.push(Point { x: x, y: y - 1 })
    }
    if x > 0 && !is_wall(Point { x: x - 1, y: y }, key) {
        moves.push(Point { x: x - 1, y: y })
    }
    moves
}

fn shortest_path(start: Point, end: Point, key: usize) -> Option<Vec<Point>> {
    let mut move_queue = VecDeque::new();
    move_queue.push_back((start, Vec::new()));
    let mut visited = BTreeSet::new();
    while let Some((pos, moves)) = move_queue.pop_front() {
        for mov in possible_moves(pos, key) {
            if visited.contains(&mov) {
                continue;
            }
            let new_moves = moves.iter().cloned().chain(std::iter::once(mov)).collect();
            if mov == end {
                return Some(new_moves);
            }
            visited.insert(mov);
            move_queue.push_back((mov, new_moves));
        }
    }
    None
}

fn within_range(start: Point, limit: usize, key: usize) -> BTreeSet<Point> {
    let mut move_queue = VecDeque::new();
    move_queue.push_back((start, 0));
    let mut visited = BTreeSet::new();
    while let Some((pos, dist)) = move_queue.pop_front() {
        if dist == limit {
            continue;
        }
        for mov in possible_moves(pos, key) {
            if visited.contains(&mov) {
                continue;
            }
            visited.insert(mov);
            move_queue.push_back((mov, dist + 1));
        }
    }
    visited
}

fn parse_args() -> std::io::Result<usize> {
    let matches = clap::App::new("Day 13")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("key")
            .index(1)
            .short("k")
            .long("key")
            .help("key for finding path")
            .required(true)
            .takes_value(true))
        .get_matches();
    matches.value_of("key")
        .unwrap()
        .parse()
        .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))
}

fn main() {
    let key = parse_args().unwrap_or_else(|err| panic!("Error reading args: {}", err));
    println!("part 1 path:");
    match shortest_path(Point { x: 1, y: 1 }, Point { x: 31, y: 39 }, key) {
        Some(path) => {
            for mov in &path {
                println!("{}", mov);
            }
            println!("Total length: {}", path.len());
        }
        None => println!("No path found!"),
    }
    let within_50 = within_range(Point { x: 1, y: 1 }, 50, key);
    println!("Part 2: moves within 50 steps: {}", within_50.len())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn example_path() {
        assert_eq!(shortest_path(Point { x: 1, y: 1 }, Point { x: 7, y: 4 }, 10)
                       .expect("No path found")
                       .len(),
                   11)
    }
}
<|endoftext|>


<commit_before>extern crate nbez;

#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use nbez::{BezCurve, BezChain, Bez3o2d, Point2d};

use gfx::traits::FactoryExt;
use gfx::{Factory, Device, Primitive, BufferRole, Bind, Slice, IndexBuffer};
use gfx::state::Rasterizer;

use glutin::{Event, ElementState, MouseButton};

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!{ Vertex {
    pos: [f32; 2] = "v_pos",
    col: [f32; 3] = "v_col",
}}

gfx_pipeline!{ pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    window_matrix: gfx::Global<[[f32; 2]; 2]> = "window_matrix",
    offset: gfx::Global<[f32; 2]> = "offset",
    out: gfx::RenderTarget<ColorFormat> = "r_target",
}}

fn main() {
    let (mut win_x, mut win_y) = (720, 720);

    let builder = glutin::WindowBuilder::new()
        .with_dimensions(win_x, win_y)
        .with_multisampling(16)
        .with_title("Hello Bézier".into());
    let (window, mut device, mut factory, main_color, mut main_depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);
    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let sset = factory.create_shader_set(VERT, FRAG).unwrap();
    let pso = factory.create_pipeline_state(
        &sset,
        Primitive::TriangleStrip,
        Rasterizer::new_fill(),
        pipe::new()
    ).unwrap();

    let mut curve_chain: BezChain<_, Bez3o2d<_>, _> = BezChain::from_container(vec![
        Point2d::new(-0.5, -0.5),
        Point2d::new( 0.0, -0.5),
        Point2d::new(-0.5,  0.0),
        Point2d::new( 0.0,  0.0),
        Point2d::new( 0.5,  0.0),
        Point2d::new( 0.0,  0.5),
        Point2d::new( 0.5,  0.5)
    ]);

    let radius = 0.02;
    let circle = gen_circle(16, radius, [1.0, 0.0, 0.0]);
    let mut indices = vec![0u16; 42];
    for (ind, i) in indices.iter_mut().enumerate() {
        let ind = ind + 3;
        if ind % 3 == 1 {
            *i = ind as u16/3;
        } else if ind % 3 == 2 {
            *i = ind as u16/3 + 1
        }
    }

    let window_matrix: [[f32; 2]; 2] = [
        [1.0, 0.0],
        [0.0, 1.0],
    ];

    // A vec of curve vertices
    const SAMPLES: usize = 31;
    let mut cverts = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; SAMPLES * 2];
    let cvert_buffer = factory.create_buffer_dynamic(cverts.len(), BufferRole::Vertex, Bind::empty()).unwrap();
    let cvert_slice = Slice {
        start: 0,
        end: cvert_buffer.len() as u32,
        base_vertex: 0,
        instances: None,
        buffer: IndexBuffer::Auto
    };

    let (cir_buffer, cir_slice) = factory.create_vertex_buffer_with_slice(&circle, &indices[..]);
    let mut data = pipe::Data {
        vbuf: cvert_buffer.clone(),
        window_matrix: window_matrix,
        offset: [0.0, 0.0],
        out: main_color
    };

    // The index of the selected control point. If -1, no control point is selected.
    let mut selected = -1;
    // Mouse x, mouse y
    let (mut mox, mut moy) = (0.0, 0.0);
    'main: loop {
        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main,
                Event::Resized(x, y) => {
                    gfx_window_glutin::update_views(&window, &mut data.out, &mut main_depth);
                    win_x = x;
                    win_y = y;
                    data.window_matrix = [
                        [720.0/win_x as f32, 0.0],
                        [0.0, 720.0/win_y as f32]
                    ];
                }
                Event::MouseMoved(x, y) => {
                    mox =  pix_to_float(x, win_x) / data.window_matrix[0][0];
                    moy = -pix_to_float(y, win_y) / data.window_matrix[1][1];
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    for (i, c) in curve_chain.as_ref().iter().enumerate() {
                        let dist = (c.x-mox).hypot(c.y-moy);
                        if dist <= radius {
                            selected = i as isize;
                        }
                    }
                }
                Event::MouseInput(ElementState::Released, MouseButton::Left) => selected = -1,
                Event::MouseInput(ElementState::Pressed, MouseButton::Right) =>
                    curve_chain.as_mut().push(Point2d::new(mox, moy)),
                _ => ()
            }
        }

        // If any control point is selected, move that control point with the mouse.
        if 0 <= selected {
            let c = &mut curve_chain.as_mut()[selected as usize];
            c.x = mox;
            c.y = moy;
        }

        // Drawing code
        encoder.clear(&data.out, [0.0, 0.0, 0.0, 1.0]);

        for curve in curve_chain.iter() {
            // Calculate curve vertices based on control point position.
            for i in 0..SAMPLES {
                let t = i as f32/(SAMPLES-1) as f32;

                let interp = curve.interp(t).unwrap();

                let perp = curve.slope(t).unwrap().normalize().perp() * 0.01;
                cverts[i*2].pos = (-perp + interp).into();
                cverts[i*2 + 1].pos = (perp + interp).into();
            }
            encoder.update_buffer(&cvert_buffer, &cverts, 0).unwrap(); // Upload calculated vertex positions to vertex buffer

            data.offset = [0.0, 0.0];
            data.vbuf = cvert_buffer.clone();
            encoder.draw(&cvert_slice, &pso, &data);
        }

        // Draw control points
        data.vbuf = cir_buffer.clone();
        for point in curve_chain.as_ref().iter() {
            data.offset = (*point).into();
            encoder.draw(&cir_slice, &pso, &data);
        }

        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

pub fn pix_to_float(p: i32, s: u32) -> f32 {
    let s = s as f32;
    let p = p as f32;
    2.0 * (p - s/2.0)/s
}

fn gen_circle(divs: u16, scale: f32, col: [f32; 3]) -> Vec<Vertex> {
    use std::f32::consts::PI;
    let mut verts = vec![Vertex{ pos: [0.0, 0.0], col: col}; divs as usize];

    for d in 0..divs {
        let theta = 2.0 * PI * (d as f32/divs as f32);
        let d = d as usize;
        verts[d].pos[0] = theta.sin() * scale;
        verts[d].pos[1] = theta.cos() * scale;
    }

    verts
}

const VERT: &'static [u8] = br#"
    #version 150 core

    uniform vec2 offset;
    uniform mat2 window_matrix;

    in vec2 v_pos;
    in vec3 v_col;
    out vec4 f_col;

    void main() {
        f_col = vec4(v_col, 1.0);
        gl_Position = vec4(window_matrix * (v_pos + offset), 0.0, 1.0);
    }
"#;

const FRAG: &'static [u8] = br#"
    #version 150 core

    in vec4 f_col;
    out vec4 r_target;

    void main() {
        r_target = f_col;
    }
"#;<commit_msg>Renamed curve.rs window to not have accented 'e'<commit_after>extern crate nbez;

#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use nbez::{BezCurve, BezChain, Bez3o2d, Point2d};

use gfx::traits::FactoryExt;
use gfx::{Factory, Device, Primitive, BufferRole, Bind, Slice, IndexBuffer};
use gfx::state::Rasterizer;

use glutin::{Event, ElementState, MouseButton};

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!{ Vertex {
    pos: [f32; 2] = "v_pos",
    col: [f32; 3] = "v_col",
}}

gfx_pipeline!{ pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    window_matrix: gfx::Global<[[f32; 2]; 2]> = "window_matrix",
    offset: gfx::Global<[f32; 2]> = "offset",
    out: gfx::RenderTarget<ColorFormat> = "r_target",
}}

fn main() {
    let (mut win_x, mut win_y) = (720, 720);

    let builder = glutin::WindowBuilder::new()
        .with_dimensions(win_x, win_y)
        .with_multisampling(16)
        .with_title("Hello Bezier".into());
    let (window, mut device, mut factory, main_color, mut main_depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);
    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let sset = factory.create_shader_set(VERT, FRAG).unwrap();
    let pso = factory.create_pipeline_state(
        &sset,
        Primitive::TriangleStrip,
        Rasterizer::new_fill(),
        pipe::new()
    ).unwrap();

    let mut curve_chain: BezChain<_, Bez3o2d<_>, _> = BezChain::from_container(vec![
        Point2d::new(-0.5, -0.5),
        Point2d::new( 0.0, -0.5),
        Point2d::new(-0.5,  0.0),
        Point2d::new( 0.0,  0.0),
        Point2d::new( 0.5,  0.0),
        Point2d::new( 0.0,  0.5),
        Point2d::new( 0.5,  0.5)
    ]);

    let radius = 0.02;
    let circle = gen_circle(16, radius, [1.0, 0.0, 0.0]);
    let mut indices = vec![0u16; 42];
    for (ind, i) in indices.iter_mut().enumerate() {
        let ind = ind + 3;
        if ind % 3 == 1 {
            *i = ind as u16/3;
        } else if ind % 3 == 2 {
            *i = ind as u16/3 + 1
        }
    }

    let window_matrix: [[f32; 2]; 2] = [
        [1.0, 0.0],
        [0.0, 1.0],
    ];

    // A vec of curve vertices
    const SAMPLES: usize = 31;
    let mut cverts = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; SAMPLES * 2];
    let cvert_buffer = factory.create_buffer_dynamic(cverts.len(), BufferRole::Vertex, Bind::empty()).unwrap();
    let cvert_slice = Slice {
        start: 0,
        end: cvert_buffer.len() as u32,
        base_vertex: 0,
        instances: None,
        buffer: IndexBuffer::Auto
    };

    let (cir_buffer, cir_slice) = factory.create_vertex_buffer_with_slice(&circle, &indices[..]);
    let mut data = pipe::Data {
        vbuf: cvert_buffer.clone(),
        window_matrix: window_matrix,
        offset: [0.0, 0.0],
        out: main_color
    };

    // The index of the selected control point. If -1, no control point is selected.
    let mut selected = -1;
    // Mouse x, mouse y
    let (mut mox, mut moy) = (0.0, 0.0);
    'main: loop {
        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main,
                Event::Resized(x, y) => {
                    gfx_window_glutin::update_views(&window, &mut data.out, &mut main_depth);
                    win_x = x;
                    win_y = y;
                    data.window_matrix = [
                        [720.0/win_x as f32, 0.0],
                        [0.0, 720.0/win_y as f32]
                    ];
                }
                Event::MouseMoved(x, y) => {
                    mox =  pix_to_float(x, win_x) / data.window_matrix[0][0];
                    moy = -pix_to_float(y, win_y) / data.window_matrix[1][1];
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    for (i, c) in curve_chain.as_ref().iter().enumerate() {
                        let dist = (c.x-mox).hypot(c.y-moy);
                        if dist <= radius {
                            selected = i as isize;
                        }
                    }
                }
                Event::MouseInput(ElementState::Released, MouseButton::Left) => selected = -1,
                Event::MouseInput(ElementState::Pressed, MouseButton::Right) =>
                    curve_chain.as_mut().push(Point2d::new(mox, moy)),
                _ => ()
            }
        }

        // If any control point is selected, move that control point with the mouse.
        if 0 <= selected {
            let c = &mut curve_chain.as_mut()[selected as usize];
            c.x = mox;
            c.y = moy;
        }

        // Drawing code
        encoder.clear(&data.out, [0.0, 0.0, 0.0, 1.0]);

        for curve in curve_chain.iter() {
            // Calculate curve vertices based on control point position.
            for i in 0..SAMPLES {
                let t = i as f32/(SAMPLES-1) as f32;

                let interp = curve.interp(t).unwrap();

                let perp = curve.slope(t).unwrap().normalize().perp() * 0.01;
                cverts[i*2].pos = (-perp + interp).into();
                cverts[i*2 + 1].pos = (perp + interp).into();
            }
            encoder.update_buffer(&cvert_buffer, &cverts, 0).unwrap(); // Upload calculated vertex positions to vertex buffer

            data.offset = [0.0, 0.0];
            data.vbuf = cvert_buffer.clone();
            encoder.draw(&cvert_slice, &pso, &data);
        }

        // Draw control points
        data.vbuf = cir_buffer.clone();
        for point in curve_chain.as_ref().iter() {
            data.offset = (*point).into();
            encoder.draw(&cir_slice, &pso, &data);
        }

        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

pub fn pix_to_float(p: i32, s: u32) -> f32 {
    let s = s as f32;
    let p = p as f32;
    2.0 * (p - s/2.0)/s
}

fn gen_circle(divs: u16, scale: f32, col: [f32; 3]) -> Vec<Vertex> {
    use std::f32::consts::PI;
    let mut verts = vec![Vertex{ pos: [0.0, 0.0], col: col}; divs as usize];

    for d in 0..divs {
        let theta = 2.0 * PI * (d as f32/divs as f32);
        let d = d as usize;
        verts[d].pos[0] = theta.sin() * scale;
        verts[d].pos[1] = theta.cos() * scale;
    }

    verts
}

const VERT: &'static [u8] = br#"
    #version 150 core

    uniform vec2 offset;
    uniform mat2 window_matrix;

    in vec2 v_pos;
    in vec3 v_col;
    out vec4 f_col;

    void main() {
        f_col = vec4(v_col, 1.0);
        gl_Position = vec4(window_matrix * (v_pos + offset), 0.0, 1.0);
    }
"#;

const FRAG: &'static [u8] = br#"
    #version 150 core

    in vec4 f_col;
    out vec4 r_target;

    void main() {
        r_target = f_col;
    }
"#;<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(JS<WebGLRenderbuffer>),
    Texture { texture: JS<WebGLTexture>, level: i32 },
}

impl HeapGCValue for WebGLFramebufferAttachment {}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    status: Cell<u32>,
    #[ignore_heap_size_of = "Defined in ipc-channel"]
    renderer: IpcSender<CanvasMsg>,

    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(renderer: IpcSender<CanvasMsg>,
                     id: WebGLFramebufferId)
                     -> WebGLFramebuffer {
        WebGLFramebuffer {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            renderer: renderer,
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DOMRefCell::new(None),
            depth: DOMRefCell::new(None),
            stencil: DOMRefCell::new(None),
            depthstencil: DOMRefCell::new(None),
        }
    }

    pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>)
                     -> Option<Root<WebGLFramebuffer>> {
        let (sender, receiver) = ipc::channel().unwrap();
        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id))
    }

    pub fn new(global: &GlobalScope,
               renderer: IpcSender<CanvasMsg>,
               id: WebGLFramebufferId)
               -> Root<WebGLFramebuffer> {
        reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                           global,
                           WebGLFramebufferBinding::Wrap)
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    fn update_status(&self) {
        let has_c = self.color.borrow().is_some();
        let has_z = self.depth.borrow().is_some();
        let has_s = self.stencil.borrow().is_some();
        let has_zs = self.depthstencil.borrow().is_some();

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
                                                                                attachment,
                                                                                constants::RENDERBUFFER,
                                                                                rb_id))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
                    texture: JS::from_ref(texture),
                    level: level }
                );

                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                self.update_status();
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
                                                                               attachment,
                                                                               textarget,
                                                                               tex_id,
                                                                               level))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. })
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
            }
        }
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<commit_msg>webgl: Don't update texture attachment state if we throw an error.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(JS<WebGLRenderbuffer>),
    Texture { texture: JS<WebGLTexture>, level: i32 },
}

impl HeapGCValue for WebGLFramebufferAttachment {}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    status: Cell<u32>,
    #[ignore_heap_size_of = "Defined in ipc-channel"]
    renderer: IpcSender<CanvasMsg>,

    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(renderer: IpcSender<CanvasMsg>,
                     id: WebGLFramebufferId)
                     -> WebGLFramebuffer {
        WebGLFramebuffer {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            renderer: renderer,
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DOMRefCell::new(None),
            depth: DOMRefCell::new(None),
            stencil: DOMRefCell::new(None),
            depthstencil: DOMRefCell::new(None),
        }
    }

    pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>)
                     -> Option<Root<WebGLFramebuffer>> {
        let (sender, receiver) = ipc::channel().unwrap();
        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id))
    }

    pub fn new(global: &GlobalScope,
               renderer: IpcSender<CanvasMsg>,
               id: WebGLFramebufferId)
               -> Root<WebGLFramebuffer> {
        reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                           global,
                           WebGLFramebufferBinding::Wrap)
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    fn update_status(&self) {
        let has_c = self.color.borrow().is_some();
        let has_z = self.depth.borrow().is_some();
        let has_s = self.stencil.borrow().is_some();
        let has_zs = self.depthstencil.borrow().is_some();

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
                                                                                attachment,
                                                                                constants::RENDERBUFFER,
                                                                                rb_id))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
                    texture: JS::from_ref(texture),
                    level: level }
                );

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                self.update_status();
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
                                                                               attachment,
                                                                               textarget,
                                                                               tex_id,
                                                                               level))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. })
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
            }
        }
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<|endoftext|>