<commit_before>use std::sync::mpsc::{Receiver, Sender};
use std::rc::Rc;

use action::ExecResult;
use super::{config, Context, Event, Message, TimerEvent};

#[derive(Debug)]
pub enum Request {
    Event(Event),
    Exit
}

#[derive(Debug)]
pub enum Response {
    Event(ExecResult),
    Exit
}

pub struct Dispatcher {
    contexts: Vec<Context>,
    output_channel: Sender<Response>,
    exits_received: u32
}

impl Dispatcher {
    pub fn new(contexts: Vec<config::Context>, action_output_channel: Sender<Response>) -> Dispatcher {
        let contexts = contexts.into_iter().map(|ctx| Context::from(ctx)).collect::<Vec<Context>>();
        Dispatcher {
            contexts: contexts,
            output_channel: action_output_channel,
            exits_received: 0
        }
    }

    pub fn start_loop(&mut self, channel: Receiver<Request>) {
        for i in channel.iter() {
            match i {
                Request::Event(event) => self.dispatch(event),
                Request::Exit => {
                    if self.on_exit() {
                        break;
                    }
                }
            }
        }
    }

    fn on_exit(&mut self) -> bool {
        self.exits_received += 1;
        let _ = self.output_channel.send(Response::Exit);
        self.exits_received >= 2
    }

    pub fn dispatch(&mut self, event: Event) {
        match event {
            Event::Message(event) => {
                let event = Rc::new(event);
                self.on_message(event);
            },
            Event::Timer(ref event) => {
                self.on_timer(event);
            }
        };
    }

    fn on_message(&mut self, event: Rc<Message>) {
        for context in self.contexts.iter_mut() {
            if let Some(result) = context.on_message(event.clone()) {
                for i in result.into_iter() {
                    let _ = self.output_channel.send(i.into());
                }
            }
        }
    }

    fn on_timer(&mut self, event: &TimerEvent) {
        for context in self.contexts.iter_mut() {
            if let Some(result) = context.on_timer(event) {
                for i in result.into_iter() {
                    let _ = self.output_channel.send(i.into());
                }
            }
        }
    }
}

mod condition {
    use std::cell::RefCell;
    use std::rc::Rc;
    use std::clone::Clone;

    #[derive(Clone, Debug)]
    pub struct Condition(Rc<RefCell<bool>>);

    impl Condition {
      pub fn is_active(&self) -> bool {
        *self.0.borrow()
      }

      pub fn activate(&mut self) {
        *self.0.borrow_mut() = true;
      }

      pub fn deactivate(&mut self) {
        *self.0.borrow_mut() = false;
      }
    }
}
<commit_msg>dispatcher: move ExitHandler into its own module<commit_after>use std::sync::mpsc::{Receiver, Sender};
use std::rc::Rc;

use action::ExecResult;
use super::{config, Condition, Context, Event, Message, TimerEvent};
use reactor;

#[derive(Debug)]
pub enum Request {
    Event(Event),
    Exit
}

#[derive(Debug)]
pub enum Response {
    Event(ExecResult),
    Exit
}

pub struct Dispatcher {
    contexts: Vec<Context>,
    output_channel: Sender<Response>,
    exits_received: u32
}

impl Dispatcher {
    pub fn new(contexts: Vec<config::Context>, action_output_channel: Sender<Response>) -> Dispatcher {
        let contexts = contexts.into_iter().map(|ctx| Context::from(ctx)).collect::<Vec<Context>>();
        Dispatcher {
            contexts: contexts,
            output_channel: action_output_channel,
            exits_received: 0
        }
    }

    pub fn start_loop(&mut self, channel: Receiver<Request>) {
        for i in channel.iter() {
            match i {
                Request::Event(event) => self.dispatch(event),
                Request::Exit => {
                    if self.on_exit() {
                        break;
                    }
                }
            }
        }
    }

    fn on_exit(&mut self) -> bool {
        self.exits_received += 1;
        let _ = self.output_channel.send(Response::Exit);
        self.exits_received >= 2
    }

    pub fn dispatch(&mut self, event: Event) {
        match event {
            Event::Message(event) => {
                let event = Rc::new(event);
                self.on_message(event);
            },
            Event::Timer(ref event) => {
                self.on_timer(event);
            }
        };
    }

    fn on_message(&mut self, event: Rc<Message>) {
        for context in self.contexts.iter_mut() {
            if let Some(result) = context.on_message(event.clone()) {
                for i in result.into_iter() {
                    let _ = self.output_channel.send(i.into());
                }
            }
        }
    }

    fn on_timer(&mut self, event: &TimerEvent) {
        for context in self.contexts.iter_mut() {
            if let Some(result) = context.on_timer(event) {
                for i in result.into_iter() {
                    let _ = self.output_channel.send(i.into());
                }
            }
        }
    }
}

mod handlers {
    mod exit {
        use dispatcher::Request;
        use condition::Condition;
        use reactor::EventHandler;

        struct ExitHandler{
            condition: Condition,
            stops: u32
        }

        impl ExitHandler {
            fn new(condition: Condition) -> ExitHandler {
                ExitHandler {
                    condition: condition,
                    stops: 0
                }
            }
        }

        impl EventHandler<Request> for ExitHandler {
            fn handle_event(&mut self, event: Request) {
                if let Request::Exit = event {
                    self.stops += 1;

                    if self.stops >= 2 {
                        self.condition.activate();
                    }
                } else {
                    unreachable!("An ExitHandler should only receive Exit events");
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Jerome Rasky <jerome@rasky.co>
//
// Licensed under the Apache License, version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. See the
// License for the specific language concerning governing permissions and
// limitations under the License.
use std::io::prelude::*;

use threadpool::ThreadPool;

use std::sync::Arc;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;
use std::iter::FromIterator;
use std::error::Error;

use std::sync::mpsc;
use std::raw;
use std::mem;

use terminal::Terminal;
use flx::SearchBase;
use threads;

use ui::*;
use error::*;
use types::*;
use constants::*;

pub struct EventLoop {
    emit: Sender<Event>,
    events: Receiver<Event>,
    terminal: Terminal,
    escape: Escape,
    matches: Matches,
    selected: usize,
    query: Arc<String>,
    success: bool,
    input_thread: Option<JoinHandle<()>>,
    input_stop: Arc<AtomicBool>,
    search: Option<Arc<SearchBase>>,
    pool: ThreadPool,
    recent: Vec<Arc<String>>,
}

impl EventLoop {
    pub fn create() -> StrResult<EventLoop> {
        let (emit, events) = mpsc::channel();
        let (input_thread, input_stop) = trys!(threads::start_threads(emit.clone()),
                                               "Failed to start threads");

        Ok(EventLoop {
            emit: emit,
            events: events,
            terminal: trys!(Terminal::create(), "Failed to create terminal instance"),
            escape: trys!(Escape::create(), "Failed to create escape instance"),
            matches: Matches::from_iter(vec![]),
            selected: 0,
            query: Arc::new(format!("")),
            success: false,
            input_thread: Some(input_thread),
            input_stop: input_stop,
            search: None,
            pool: ThreadPool::new(NUM_THREADS),
            recent: vec![],
        })
    }

    fn stop_threads(&mut self) -> StrResult<()> {
        // prompt the input thread to stop
        self.input_stop.store(true, Ordering::Relaxed);

        // insert a bogus byte to wake it up
        trys!(self.terminal.insert_input(" "),
              "Failed to insert bogus byte");

        // get our input thread handle
        let handle = match self.input_thread.take() {
            None => return Err(StrError::new("No input thread handle", None)),
            Some(handle) => handle,
        };

        // join the thread
        match handle.join() {
            Ok(_) => Ok(()),
            Err(opaque) => {
                Err({
                    let error = opaque.downcast_ref::<StrError>().map(|error: &StrError| {
                        let dummy = StrError::new("dummy", None);
                        let value: Box<Error> = Box::new(dummy);
                        let raw_object: raw::TraitObject = unsafe { mem::transmute(value) };
                        let synthetic: Box<Error> = unsafe {
                            mem::transmute(raw::TraitObject {
                                data: error as *const _ as *mut (),
                                vtable: raw_object.vtable,
                            })
                        };
                        synthetic
                    });
                    StrError::new("Input thread failed", error)
                })
            }
        }
    }

    fn start_query(&self) {
        if !self.query.is_empty() {
            // only execute queries on non-empty queries
            match self.search {
                None => {} // do nothing
                Some(ref base) => {
                    let emit = self.emit.clone();
                    let query = self.query.clone();
                    let base = base.clone();
                    self.pool.execute(move || {
                        threads::start_query(emit, base, query);
                    });
                }
            }
        }
    }

    pub fn run(&mut self) -> StrResult<()> {
        // draw the prompt
        let size = self.terminal.rows() as usize;
        trys!(self.terminal.output_str(self.escape.render_prompt(size)),
              "Failed to render prompt");

        // flush the terminal
        trys!(self.terminal.flush(), "Failed to flush terminal");

        for event in self.events.iter() {
            match event {
                Event::HistoryReady(recent) => {
                    self.recent = recent;
                    if self.query.is_empty() {
                        self.matches = Matches::from_iter(self.recent.iter().cloned());
                        self.selected = 0;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    }
                }
                Event::SearchReady(base) => {
                    self.search = Some(Arc::new(base));
                    self.start_query();
                }
                Event::Input(chr) => {
                    Arc::make_mut(&mut self.query).push(chr);
                    trys!(self.terminal.output_str(self.escape.query_output(chr)),
                          "Failed to output character");
                    self.start_query();
                }
                Event::Match(matches, query) => {
                    debug!("Got match event: {:?}, {:?}", matches, query);
                    if query == self.query {
                        // only draw matches for the current query
                        self.selected = 0;
                        self.matches = Matches::from_iter(matches);
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    }
                }
                Event::Quit(success) => {
                    debug!("Got quit event: {:?}", success);
                    self.success = success;
                    break;
                }
                Event::KeyDown => {
                    if self.selected + 1 < self.matches.len() {
                        self.selected += 1;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.match_down(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::KeyUp => {
                    if self.selected > 0 {
                        self.selected -= 1;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.match_up(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::Clear => {
                    if !self.query.is_empty() {
                        trys!(self.terminal.output_str(self.escape.move_back(self.query.len())),
                              "Failed to output to terminal");
                        self.query = Arc::new(format!(""));
                        self.matches = Matches::from_iter(self.recent.iter().cloned());
                        self.selected = 0;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::Backspace => {
                    if !self.query.is_empty() {
                        Arc::make_mut(&mut self.query).pop();
                        trys!(self.terminal.output_str(self.escape.move_back(1)),
                              "Failed to output to terminal");
                        if !self.query.is_empty() {
                            self.start_query();
                        } else {
                            self.matches = Matches::from_iter(self.recent.iter().cloned());
                            let size = self.terminal.cols() as usize;
                            trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                                  "Failed to output matches");
                        }
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::Bell => {
                    trys!(self.terminal.output_str(self.escape.bell()),
                          "Failed to output bell");
                }
            }

            debug!("Flushing output");
            trys!(self.terminal.flush(), "Failed to flush terminal");
        }

        // stop the input thread
        trys!(self.stop_threads(), "Failed to stop threads");

        // draw the best match if it exists
        trys!(self.terminal
                  .output_str(self.escape.best_match_output(&self.matches, self.selected, self.query.is_empty())),
              "Failed to draw best match");

        debug!("Flushing output");
        trys!(self.terminal.flush(), "Failed to flush terminal");

        // insert the successful match onto the terminal input buffer
        if self.success {
            match self.matches.get(self.selected) {
                None => debug!("No best match"),
                Some(m) => {
                    trys!(self.terminal.insert_input(m.get()),
                          "Failed to insert input");
                }
            }
        }

        // success
        Ok(())
    }
}
<commit_msg>Remove unused import<commit_after>// Copyright 2015 Jerome Rasky <jerome@rasky.co>
//
// Licensed under the Apache License, version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. See the
// License for the specific language concerning governing permissions and
// limitations under the License.
use threadpool::ThreadPool;

use std::sync::Arc;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;
use std::iter::FromIterator;
use std::error::Error;

use std::sync::mpsc;
use std::raw;
use std::mem;

use terminal::Terminal;
use flx::SearchBase;
use threads;

use ui::*;
use error::*;
use types::*;
use constants::*;

pub struct EventLoop {
    emit: Sender<Event>,
    events: Receiver<Event>,
    terminal: Terminal,
    escape: Escape,
    matches: Matches,
    selected: usize,
    query: Arc<String>,
    success: bool,
    input_thread: Option<JoinHandle<()>>,
    input_stop: Arc<AtomicBool>,
    search: Option<Arc<SearchBase>>,
    pool: ThreadPool,
    recent: Vec<Arc<String>>,
}

impl EventLoop {
    pub fn create() -> StrResult<EventLoop> {
        let (emit, events) = mpsc::channel();
        let (input_thread, input_stop) = trys!(threads::start_threads(emit.clone()),
                                               "Failed to start threads");

        Ok(EventLoop {
            emit: emit,
            events: events,
            terminal: trys!(Terminal::create(), "Failed to create terminal instance"),
            escape: trys!(Escape::create(), "Failed to create escape instance"),
            matches: Matches::from_iter(vec![]),
            selected: 0,
            query: Arc::new(format!("")),
            success: false,
            input_thread: Some(input_thread),
            input_stop: input_stop,
            search: None,
            pool: ThreadPool::new(NUM_THREADS),
            recent: vec![],
        })
    }

    fn stop_threads(&mut self) -> StrResult<()> {
        // prompt the input thread to stop
        self.input_stop.store(true, Ordering::Relaxed);

        // insert a bogus byte to wake it up
        trys!(self.terminal.insert_input(" "),
              "Failed to insert bogus byte");

        // get our input thread handle
        let handle = match self.input_thread.take() {
            None => return Err(StrError::new("No input thread handle", None)),
            Some(handle) => handle,
        };

        // join the thread
        match handle.join() {
            Ok(_) => Ok(()),
            Err(opaque) => {
                Err({
                    let error = opaque.downcast_ref::<StrError>().map(|error: &StrError| {
                        let dummy = StrError::new("dummy", None);
                        let value: Box<Error> = Box::new(dummy);
                        let raw_object: raw::TraitObject = unsafe { mem::transmute(value) };
                        let synthetic: Box<Error> = unsafe {
                            mem::transmute(raw::TraitObject {
                                data: error as *const _ as *mut (),
                                vtable: raw_object.vtable,
                            })
                        };
                        synthetic
                    });
                    StrError::new("Input thread failed", error)
                })
            }
        }
    }

    fn start_query(&self) {
        if !self.query.is_empty() {
            // only execute queries on non-empty queries
            match self.search {
                None => {} // do nothing
                Some(ref base) => {
                    let emit = self.emit.clone();
                    let query = self.query.clone();
                    let base = base.clone();
                    self.pool.execute(move || {
                        threads::start_query(emit, base, query);
                    });
                }
            }
        }
    }

    pub fn run(&mut self) -> StrResult<()> {
        // draw the prompt
        let size = self.terminal.rows() as usize;
        trys!(self.terminal.output_str(self.escape.render_prompt(size)),
              "Failed to render prompt");

        // flush the terminal
        trys!(self.terminal.flush(), "Failed to flush terminal");

        for event in self.events.iter() {
            match event {
                Event::HistoryReady(recent) => {
                    self.recent = recent;
                    if self.query.is_empty() {
                        self.matches = Matches::from_iter(self.recent.iter().cloned());
                        self.selected = 0;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    }
                }
                Event::SearchReady(base) => {
                    self.search = Some(Arc::new(base));
                    self.start_query();
                }
                Event::Input(chr) => {
                    Arc::make_mut(&mut self.query).push(chr);
                    trys!(self.terminal.output_str(self.escape.query_output(chr)),
                          "Failed to output character");
                    self.start_query();
                }
                Event::Match(matches, query) => {
                    debug!("Got match event: {:?}, {:?}", matches, query);
                    if query == self.query {
                        // only draw matches for the current query
                        self.selected = 0;
                        self.matches = Matches::from_iter(matches);
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    }
                }
                Event::Quit(success) => {
                    debug!("Got quit event: {:?}", success);
                    self.success = success;
                    break;
                }
                Event::KeyDown => {
                    if self.selected + 1 < self.matches.len() {
                        self.selected += 1;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.match_down(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::KeyUp => {
                    if self.selected > 0 {
                        self.selected -= 1;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.match_up(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::Clear => {
                    if !self.query.is_empty() {
                        trys!(self.terminal.output_str(self.escape.move_back(self.query.len())),
                              "Failed to output to terminal");
                        self.query = Arc::new(format!(""));
                        self.matches = Matches::from_iter(self.recent.iter().cloned());
                        self.selected = 0;
                        let size = self.terminal.cols() as usize;
                        trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                              "Failed to output matches");
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::Backspace => {
                    if !self.query.is_empty() {
                        Arc::make_mut(&mut self.query).pop();
                        trys!(self.terminal.output_str(self.escape.move_back(1)),
                              "Failed to output to terminal");
                        if !self.query.is_empty() {
                            self.start_query();
                        } else {
                            self.matches = Matches::from_iter(self.recent.iter().cloned());
                            let size = self.terminal.cols() as usize;
                            trys!(self.terminal.output_str(self.escape.matches_output(&self.matches, size, self.selected)),
                                  "Failed to output matches");
                        }
                    } else {
                        trys!(self.emit.send(Event::Bell), "Failed to send bell event");
                    }
                }
                Event::Bell => {
                    trys!(self.terminal.output_str(self.escape.bell()),
                          "Failed to output bell");
                }
            }

            debug!("Flushing output");
            trys!(self.terminal.flush(), "Failed to flush terminal");
        }

        // stop the input thread
        trys!(self.stop_threads(), "Failed to stop threads");

        // draw the best match if it exists
        trys!(self.terminal
                  .output_str(self.escape.best_match_output(&self.matches, self.selected, self.query.is_empty())),
              "Failed to draw best match");

        debug!("Flushing output");
        trys!(self.terminal.flush(), "Failed to flush terminal");

        // insert the successful match onto the terminal input buffer
        if self.success {
            match self.matches.get(self.selected) {
                None => debug!("No best match"),
                Some(m) => {
                    trys!(self.terminal.insert_input(m.get()),
                          "Failed to insert input");
                }
            }
        }

        // success
        Ok(())
    }
}
<|endoftext|>


<commit_before>pub mod greetings; // greetings.rs

pub mod farewells; // farewells.rs<commit_msg>Re-exported Function inside Module for External Interface presentation<commit_after>// Re-export a Function inside another Module to present External Interface
// that does not directly map to internal code organisation
// 'pub use' declaration brings Functions into scope at this level of
// Module hierarchy so internal organisation does not define the External Interface
pub use self::greetings::guten_tag;
pub use self::farewells::auf_wiedersehen;

pub mod greetings; // greetings.rs

pub mod farewells; // farewells.rs<|endoftext|>


<commit_before>//! Function interfaces for VST 2.4 API.

#![doc(hidden)]

use std::ffi::{CStr, CString};
use std::{mem, slice};

use std::os::raw::{c_char, c_void};
use libc::strncpy;

use buffer::AudioBuffer;
use api::consts::*;
use api::{self, AEffect, ChannelProperties};
use editor::{Rect, KeyCode, Key, KnobMode};
use host::Host;
use event::Event;

/// Deprecated process function.
pub fn process_deprecated(_effect: *mut AEffect, _inputs_raw: *mut *mut f32, _outputs_raw: *mut *mut f32, _samples: i32) { }

/// VST2.4 replacing function.
pub fn process_replacing(effect: *mut AEffect, inputs_raw: *mut *mut f32, outputs_raw: *mut *mut f32, samples: i32) {
    // Handle to the vst
    let mut plugin = unsafe { (*effect).get_plugin() };

    let buffer = unsafe {
        AudioBuffer::from_raw(inputs_raw,
                              outputs_raw,
                              plugin.get_info().inputs as usize,
                              plugin.get_info().outputs as usize,
                              samples as usize)
    };

    plugin.process(buffer);
}

/// VST2.4 replacing function with `f64` values.
pub fn process_replacing_f64(effect: *mut AEffect, inputs_raw: *mut *mut f64, outputs_raw: *mut *mut f64, samples: i32) {
    let mut plugin = unsafe { (*effect).get_plugin() };

    if plugin.get_info().f64_precision {
        let buffer = unsafe {
            AudioBuffer::from_raw(inputs_raw,
                                  outputs_raw,
                                  plugin.get_info().inputs as usize,
                                  plugin.get_info().outputs as usize,
                                  samples as usize)
        };

        plugin.process_f64(buffer);
    }
}

/// VST2.4 set parameter function.
pub fn set_parameter(effect: *mut AEffect, index: i32, value: f32) {
    unsafe { (*effect).get_plugin() }.set_parameter(index, value);
}

/// VST2.4 get parameter function.
pub fn get_parameter(effect: *mut AEffect, index: i32) -> f32 {
    unsafe { (*effect).get_plugin() }.get_parameter(index)
}

/// VST2.4 dispatch function. This function handles dispatching all opcodes to the vst plugin.
pub fn dispatch(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize {
    use plugin::{CanDo, OpCode};

    // Convert passed in opcode to enum
    let opcode = OpCode::from(opcode);
    // Plugin handle
    let mut plugin = unsafe { (*effect).get_plugin() };

    // Copy a string into the `ptr` buffer
    let copy_string = |string: &String, max: usize| {
        unsafe {
            strncpy(ptr as *mut c_char,
                          CString::new(string.clone()).unwrap().as_ptr(),
                          max);
        }
    };

    match opcode {
        OpCode::Initialize => plugin.init(),
        OpCode::Shutdown => unsafe {
            (*effect).drop_plugin();
            drop(mem::transmute::<*mut AEffect, Box<AEffect>>(effect));
        },

        OpCode::ChangePreset => plugin.change_preset(value as i32),
        OpCode::GetCurrentPresetNum => return plugin.get_preset_num() as isize,
        OpCode::SetCurrentPresetName => plugin.set_preset_name(read_string(ptr)),
        OpCode::GetCurrentPresetName => {
            let num = plugin.get_preset_num();
            copy_string(&plugin.get_preset_name(num), MAX_PRESET_NAME_LEN);
        }

        OpCode::GetParameterLabel => copy_string(&plugin.get_parameter_label(index), MAX_PARAM_STR_LEN),
        OpCode::GetParameterDisplay => copy_string(&plugin.get_parameter_text(index), MAX_PARAM_STR_LEN),
        OpCode::GetParameterName => copy_string(&plugin.get_parameter_name(index), MAX_PARAM_STR_LEN),

        OpCode::SetSampleRate => plugin.set_sample_rate(opt),
        OpCode::SetBlockSize => plugin.set_block_size(value as i64),
        OpCode::StateChanged => {
            if value == 1 {
                plugin.resume();
            } else {
                plugin.suspend();
            }
        }

        OpCode::EditorGetRect => {
            if let Some(editor) = plugin.get_editor() {
                let size = editor.size();
                let pos = editor.position();

                unsafe {
                    // Given a Rect** structure
                    // TODO: Investigate whether we are given a valid Rect** pointer already
                    *(ptr as *mut *mut c_void) =
                        mem::transmute(Box::new(Rect {
                            left: pos.0 as i16, // x coord of position
                            top: pos.1 as i16, // y coord of position
                            right: (pos.0 + size.0) as i16, // x coord of pos + x coord of size
                            bottom: (pos.1 + size.1) as i16 // y coord of pos + y coord of size
                        }));
                }
            }
        }
        OpCode::EditorOpen => {
            if let Some(editor) = plugin.get_editor() {
                editor.open(ptr); //ptr is raw window handle, eg HWND* on windows
            }
        }
        OpCode::EditorClose => {
            if let Some(editor) = plugin.get_editor() {
                editor.close();
            }
        }

        OpCode::EditorIdle => {
            if let Some(editor) = plugin.get_editor() {
                editor.idle();
            }
        }

        OpCode::GetData => {
            let chunks = if index == 0 {
                plugin.get_bank_data()
            } else {
                plugin.get_preset_data()
            };

            let len = chunks.len() as isize;

            // u8 array to **void ptr
            // TODO: Release the allocated memory for the chunk in resume / suspend event
            unsafe {
                *(ptr as *mut *mut c_void) =
                    chunks.into_boxed_slice().as_ptr() as *mut c_void;
            }

            return len;
        }
        OpCode::SetData => {
            let chunks = unsafe { slice::from_raw_parts(ptr as *mut u8, value as usize) };
            if index == 0 {
                plugin.load_bank_data(chunks);
            } else {
                plugin.load_preset_data(chunks);
            }
        }

        OpCode::ProcessEvents => {
            let events: *const api::Events = ptr as *const api::Events;

            let events: Vec<Event> = unsafe {
                // Create a slice of type &mut [*mut Event]
                slice::from_raw_parts(&(*events).events[0], (*events).num_events as usize)
                // Deref and clone each event to get a slice
                .iter().map(|item| Event::from(**item)).collect()
            };

            plugin.process_events(events);
        }
        OpCode::CanBeAutomated => return plugin.can_be_automated(index) as isize,
        OpCode::StringToParameter => return plugin.string_to_parameter(index, read_string(ptr)) as isize,

        OpCode::GetPresetName => copy_string(&plugin.get_preset_name(index), MAX_PRESET_NAME_LEN),

        OpCode::GetInputInfo => {
            if index >= 0 && index < plugin.get_info().inputs {
                unsafe {
                    let ptr = mem::transmute::<_, *mut ChannelProperties>(ptr);
                    *ptr = plugin.get_input_info(index).into();
                }
            }
        }
        OpCode::GetOutputInfo => {
            if index >= 0 && index < plugin.get_info().outputs {
                unsafe {
                    let ptr = mem::transmute::<_, *mut ChannelProperties>(ptr);
                    *ptr = plugin.get_output_info(index).into();
                }
            }
        }
        OpCode::GetCategory => {
            return plugin.get_info().category.into();
        }

        OpCode::GetVendorName => copy_string(&plugin.get_info().vendor, MAX_VENDOR_STR_LEN),
        OpCode::GetProductName => copy_string(&plugin.get_info().name, MAX_PRODUCT_STR_LEN),
        OpCode::GetVendorVersion => return plugin.get_info().version as isize,
        OpCode::VendorSpecific => return plugin.vendor_specific(index, value, ptr, opt),
        OpCode::CanDo => {
            let can_do: CanDo = match read_string(ptr).parse() {
                Ok(c) => c,
                Err(e) => { warn!("{}", e); return 0; }
            };
            return plugin.can_do(can_do).into();
        }
        OpCode::GetTailSize => if plugin.get_tail_size() == 0 { return 1; } else { return plugin.get_tail_size() },

        //OpCode::GetParamInfo => { /*TODO*/ }

        OpCode::GetApiVersion => return 2400,

        OpCode::EditorKeyDown => {
            if let Some(editor) = plugin.get_editor() {
                editor.key_down(KeyCode {
                    character: index as u8 as char,
                    key: Key::from(value),
                    modifier: unsafe { mem::transmute::<f32, i32>(opt) } as u8
                });
            }
        }
        OpCode::EditorKeyUp => {
            if let Some(editor) = plugin.get_editor() {
                editor.key_up(KeyCode {
                    character: index as u8 as char,
                    key: Key::from(value),
                    modifier: unsafe { mem::transmute::<f32, i32>(opt) } as u8
                });
            }
        }
        OpCode::EditorSetKnobMode => {
            if let Some(editor) = plugin.get_editor() {
                editor.set_knob_mode(KnobMode::from(value));
            }
        }

        _ => {
            warn!("Unimplemented opcode ({:?})", opcode);
            trace!("Arguments; index: {}, value: {}, ptr: {:?}, opt: {}", index, value, ptr, opt);
        }
    }

    0
}

pub fn host_dispatch(host: &mut Host,
                     effect: *mut AEffect,
                     opcode: i32,
                     index: i32,
                     value: isize,
                     ptr: *mut c_void,
                     opt: f32) -> isize {
    use host::OpCode;

    // Copy a string into the `ptr` buffer
    let copy_string = |string: &String, max: usize| {
        unsafe {
            strncpy(ptr as *mut c_char,
                          CString::new(string.clone()).unwrap().as_ptr(),
                          max);
        }
    };

    match OpCode::from(opcode) {
        OpCode::Version => return 2400,
        OpCode::Automate => host.automate(index, opt),

        OpCode::Idle => host.idle(),

        // ...

        OpCode::CanDo => {
            info!("Plugin is asking if host can: {}.", read_string(ptr));
        }

        OpCode::GetVendorVersion => return host.get_info().0,
        OpCode::GetVendorString => copy_string(&host.get_info().1, MAX_VENDOR_STR_LEN),
        OpCode::GetProductString => copy_string(&host.get_info().2, MAX_PRODUCT_STR_LEN),
        OpCode::ProcessEvents => {
            let events: *const api::Events = ptr as *const api::Events;

            let events: Vec<Event> = unsafe {
                // Create a slice of type &mut [*mut Event]
                slice::from_raw_parts(&(*events).events[0], (*events).num_events as usize)
                // Deref and clone each event to get a slice
                .iter().map(|item| Event::from(**item)).collect()
            };

            host.process_events(events);
        }

        unimplemented => {
            trace!("VST: Got unimplemented host opcode ({:?})", unimplemented);
            trace!("Arguments; effect: {:?}, index: {}, value: {}, ptr: {:?}, opt: {}",
                    effect, index, value, ptr, opt);
        }
    }
    0
}

// Read a string from the `ptr` buffer
fn read_string(ptr: *mut c_void) -> String {
    String::from_utf8_lossy(
        unsafe { CStr::from_ptr(ptr as *mut c_char).to_bytes() }
    ).into_owned()
}

/// Translate `Vec<Event>` into `&api::Events` and use via a callback.
///
/// Both plugins and hosts can receive VST events, this simply translates the rust structure into
/// the equivalent API structure and takes care of cleanup.
pub fn process_events<F>(events: Vec<Event>, callback: F)
    where F: FnOnce(*mut c_void)
{
    use api::flags::REALTIME_EVENT;

    let len = events.len();

    // The `api::Events` structure uses a variable length array which is difficult to represent in
    // rust. We begin by creating a vector with the appropriate byte size by calculating the header
    // and the variable length body seperately.
    let header_size = mem::size_of::<api::Events>() - (mem::size_of::<*mut api::Event>() * 2);
    let body_size = mem::size_of::<*mut api::Event>() * len;

    let mut send = vec![0u8; header_size + body_size];

    let send_events: &mut [*mut api::Event] = unsafe {
        // The header is updated by casting the array to the `api::Events` type and specifying the
        // required fields. We create a slice from the position of the first event and the length
        // of the array.
        let ptr = send.as_mut_ptr() as *mut api::Events;
        (*ptr).num_events = len as i32;

        // A slice view of the body
        slice::from_raw_parts_mut(&mut (*ptr).events[0], len)
    };

    // Each event is zipped with the target body array slot. Most of what's happening here is just
    // copying data but the key thing to notice is that each event is boxed and cast to
    // (*mut api::Event). This way we can let the callback handle the event, and then later create
    // the box again from the raw pointer so that it can be properly dropped.
    for (event, out) in events.iter().zip(send_events.iter_mut()) {
        *out = match *event {
            Event::Midi { data, delta_frames, live,
                          note_length, note_offset,
                          detune, note_off_velocity } => {
                Box::into_raw(Box::new(api::MidiEvent {
                    event_type: api::EventType::Midi,
                    byte_size: mem::size_of::<api::MidiEvent>() as i32,
                    delta_frames: delta_frames,
                    flags: if live { REALTIME_EVENT.bits() } else { 0 },
                    note_length: note_length.unwrap_or(0),
                    note_offset: note_offset.unwrap_or(0),
                    midi_data: data,
                    _midi_reserved: 0,
                    detune: detune,
                    note_off_velocity: note_off_velocity,
                    _reserved1: 0,
                    _reserved2: 0
                })) as *mut api::Event
            }
            Event::SysEx { payload, delta_frames } => {
                Box::into_raw(Box::new(api::SysExEvent {
                    event_type: api::EventType::SysEx,
                    byte_size: mem::size_of::<api::SysExEvent>() as i32,
                    delta_frames: delta_frames,
                    _flags: 0,
                    data_size: payload.len() as i32,
                    _reserved1: 0,
                    system_data: payload.as_ptr() as *const u8 as *mut u8,
                    _reserved2: 0,
                })) as *mut api::Event
            }
            Event::Deprecated(e) => Box::into_raw(Box::new(e))
        };
    }

    // Allow the callback to use the pointer
    callback(send.as_mut_ptr() as *mut c_void);

    // Clean up the created events
    unsafe {
        for &mut event in send_events {
            match (*event).event_type {
                api::EventType::Midi => {
                    drop(Box::from_raw(event as *mut api::MidiEvent));
                }
                api::EventType::SysEx => {
                    drop(Box::from_raw(event as *mut api::SysExEvent));
                }
                _ => {
                    drop(Box::from_raw(event));
                }
            }
        }
    }
}
<commit_msg>removed allocations in copy_string<commit_after>//! Function interfaces for VST 2.4 API.

#![doc(hidden)]

use std::{mem, slice};

use std::os::raw::{c_char, c_void};

use buffer::AudioBuffer;
use api::consts::*;
use api::{self, AEffect, ChannelProperties};
use editor::{Rect, KeyCode, Key, KnobMode};
use host::Host;
use event::Event;

/// Deprecated process function.
pub fn process_deprecated(_effect: *mut AEffect, _inputs_raw: *mut *mut f32, _outputs_raw: *mut *mut f32, _samples: i32) { }

/// VST2.4 replacing function.
pub fn process_replacing(effect: *mut AEffect, inputs_raw: *mut *mut f32, outputs_raw: *mut *mut f32, samples: i32) {
    // Handle to the vst
    let mut plugin = unsafe { (*effect).get_plugin() };

    let buffer = unsafe {
        AudioBuffer::from_raw(inputs_raw,
                              outputs_raw,
                              plugin.get_info().inputs as usize,
                              plugin.get_info().outputs as usize,
                              samples as usize)
    };

    plugin.process(buffer);
}

/// VST2.4 replacing function with `f64` values.
pub fn process_replacing_f64(effect: *mut AEffect, inputs_raw: *mut *mut f64, outputs_raw: *mut *mut f64, samples: i32) {
    let mut plugin = unsafe { (*effect).get_plugin() };

    if plugin.get_info().f64_precision {
        let buffer = unsafe {
            AudioBuffer::from_raw(inputs_raw,
                                  outputs_raw,
                                  plugin.get_info().inputs as usize,
                                  plugin.get_info().outputs as usize,
                                  samples as usize)
        };

        plugin.process_f64(buffer);
    }
}

/// VST2.4 set parameter function.
pub fn set_parameter(effect: *mut AEffect, index: i32, value: f32) {
    unsafe { (*effect).get_plugin() }.set_parameter(index, value);
}

/// VST2.4 get parameter function.
pub fn get_parameter(effect: *mut AEffect, index: i32) -> f32 {
    unsafe { (*effect).get_plugin() }.get_parameter(index)
}

// Copy a string into the `ptr` buffer
fn copy_string(dst: *mut c_void, src: &str, max: usize) {
    unsafe {
        use std::cmp::min;
        use libc::{c_void, memset, memcpy};

        let dst = dst as *mut c_void;
        memset(dst, 0, max);
        memcpy(dst, src.as_ptr() as *const c_void, min(max, src.len()));
    }
}

/// VST2.4 dispatch function. This function handles dispatching all opcodes to the vst plugin.
pub fn dispatch(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize {
    use plugin::{CanDo, OpCode};

    // Convert passed in opcode to enum
    let opcode = OpCode::from(opcode);
    // Plugin handle
    let mut plugin = unsafe { (*effect).get_plugin() };

    match opcode {
        OpCode::Initialize => plugin.init(),
        OpCode::Shutdown => unsafe {
            (*effect).drop_plugin();
            drop(mem::transmute::<*mut AEffect, Box<AEffect>>(effect));
        },

        OpCode::ChangePreset => plugin.change_preset(value as i32),
        OpCode::GetCurrentPresetNum => return plugin.get_preset_num() as isize,
        OpCode::SetCurrentPresetName => plugin.set_preset_name(read_string(ptr)),
        OpCode::GetCurrentPresetName => {
            let num = plugin.get_preset_num();
            copy_string(ptr, &plugin.get_preset_name(num), MAX_PRESET_NAME_LEN);
        }

        OpCode::GetParameterLabel => copy_string(ptr, &plugin.get_parameter_label(index), MAX_PARAM_STR_LEN),
        OpCode::GetParameterDisplay => copy_string(ptr, &plugin.get_parameter_text(index), MAX_PARAM_STR_LEN),
        OpCode::GetParameterName => copy_string(ptr, &plugin.get_parameter_name(index), MAX_PARAM_STR_LEN),

        OpCode::SetSampleRate => plugin.set_sample_rate(opt),
        OpCode::SetBlockSize => plugin.set_block_size(value as i64),
        OpCode::StateChanged => {
            if value == 1 {
                plugin.resume();
            } else {
                plugin.suspend();
            }
        }

        OpCode::EditorGetRect => {
            if let Some(editor) = plugin.get_editor() {
                let size = editor.size();
                let pos = editor.position();

                unsafe {
                    // Given a Rect** structure
                    // TODO: Investigate whether we are given a valid Rect** pointer already
                    *(ptr as *mut *mut c_void) =
                        mem::transmute(Box::new(Rect {
                            left: pos.0 as i16, // x coord of position
                            top: pos.1 as i16, // y coord of position
                            right: (pos.0 + size.0) as i16, // x coord of pos + x coord of size
                            bottom: (pos.1 + size.1) as i16 // y coord of pos + y coord of size
                        }));
                }
            }
        }
        OpCode::EditorOpen => {
            if let Some(editor) = plugin.get_editor() {
                editor.open(ptr); //ptr is raw window handle, eg HWND* on windows
            }
        }
        OpCode::EditorClose => {
            if let Some(editor) = plugin.get_editor() {
                editor.close();
            }
        }

        OpCode::EditorIdle => {
            if let Some(editor) = plugin.get_editor() {
                editor.idle();
            }
        }

        OpCode::GetData => {
            let chunks = if index == 0 {
                plugin.get_bank_data()
            } else {
                plugin.get_preset_data()
            };

            let len = chunks.len() as isize;

            // u8 array to **void ptr
            // TODO: Release the allocated memory for the chunk in resume / suspend event
            unsafe {
                *(ptr as *mut *mut c_void) =
                    chunks.into_boxed_slice().as_ptr() as *mut c_void;
            }

            return len;
        }
        OpCode::SetData => {
            let chunks = unsafe { slice::from_raw_parts(ptr as *mut u8, value as usize) };
            if index == 0 {
                plugin.load_bank_data(chunks);
            } else {
                plugin.load_preset_data(chunks);
            }
        }

        OpCode::ProcessEvents => {
            let events: *const api::Events = ptr as *const api::Events;

            let events: Vec<Event> = unsafe {
                // Create a slice of type &mut [*mut Event]
                slice::from_raw_parts(&(*events).events[0], (*events).num_events as usize)
                // Deref and clone each event to get a slice
                .iter().map(|item| Event::from(**item)).collect()
            };

            plugin.process_events(events);
        }
        OpCode::CanBeAutomated => return plugin.can_be_automated(index) as isize,
        OpCode::StringToParameter => return plugin.string_to_parameter(index, read_string(ptr)) as isize,

        OpCode::GetPresetName => copy_string(ptr, &plugin.get_preset_name(index), MAX_PRESET_NAME_LEN),

        OpCode::GetInputInfo => {
            if index >= 0 && index < plugin.get_info().inputs {
                unsafe {
                    let ptr = mem::transmute::<_, *mut ChannelProperties>(ptr);
                    *ptr = plugin.get_input_info(index).into();
                }
            }
        }
        OpCode::GetOutputInfo => {
            if index >= 0 && index < plugin.get_info().outputs {
                unsafe {
                    let ptr = mem::transmute::<_, *mut ChannelProperties>(ptr);
                    *ptr = plugin.get_output_info(index).into();
                }
            }
        }
        OpCode::GetCategory => {
            return plugin.get_info().category.into();
        }

        OpCode::GetVendorName => copy_string(ptr, &plugin.get_info().vendor, MAX_VENDOR_STR_LEN),
        OpCode::GetProductName => copy_string(ptr, &plugin.get_info().name, MAX_PRODUCT_STR_LEN),
        OpCode::GetVendorVersion => return plugin.get_info().version as isize,
        OpCode::VendorSpecific => return plugin.vendor_specific(index, value, ptr, opt),
        OpCode::CanDo => {
            let can_do: CanDo = match read_string(ptr).parse() {
                Ok(c) => c,
                Err(e) => { warn!("{}", e); return 0; }
            };
            return plugin.can_do(can_do).into();
        }
        OpCode::GetTailSize => if plugin.get_tail_size() == 0 { return 1; } else { return plugin.get_tail_size() },

        //OpCode::GetParamInfo => { /*TODO*/ }

        OpCode::GetApiVersion => return 2400,

        OpCode::EditorKeyDown => {
            if let Some(editor) = plugin.get_editor() {
                editor.key_down(KeyCode {
                    character: index as u8 as char,
                    key: Key::from(value),
                    modifier: unsafe { mem::transmute::<f32, i32>(opt) } as u8
                });
            }
        }
        OpCode::EditorKeyUp => {
            if let Some(editor) = plugin.get_editor() {
                editor.key_up(KeyCode {
                    character: index as u8 as char,
                    key: Key::from(value),
                    modifier: unsafe { mem::transmute::<f32, i32>(opt) } as u8
                });
            }
        }
        OpCode::EditorSetKnobMode => {
            if let Some(editor) = plugin.get_editor() {
                editor.set_knob_mode(KnobMode::from(value));
            }
        }

        _ => {
            warn!("Unimplemented opcode ({:?})", opcode);
            trace!("Arguments; index: {}, value: {}, ptr: {:?}, opt: {}", index, value, ptr, opt);
        }
    }

    0
}

pub fn host_dispatch(host: &mut Host,
                     effect: *mut AEffect,
                     opcode: i32,
                     index: i32,
                     value: isize,
                     ptr: *mut c_void,
                     opt: f32) -> isize {
    use host::OpCode;

    match OpCode::from(opcode) {
        OpCode::Version => return 2400,
        OpCode::Automate => host.automate(index, opt),

        OpCode::Idle => host.idle(),

        // ...

        OpCode::CanDo => {
            info!("Plugin is asking if host can: {}.", read_string(ptr));
        }

        OpCode::GetVendorVersion => return host.get_info().0,
        OpCode::GetVendorString => copy_string(ptr, &host.get_info().1, MAX_VENDOR_STR_LEN),
        OpCode::GetProductString => copy_string(ptr, &host.get_info().2, MAX_PRODUCT_STR_LEN),
        OpCode::ProcessEvents => {
            let events: *const api::Events = ptr as *const api::Events;

            let events: Vec<Event> = unsafe {
                // Create a slice of type &mut [*mut Event]
                slice::from_raw_parts(&(*events).events[0], (*events).num_events as usize)
                // Deref and clone each event to get a slice
                .iter().map(|item| Event::from(**item)).collect()
            };

            host.process_events(events);
        }

        unimplemented => {
            trace!("VST: Got unimplemented host opcode ({:?})", unimplemented);
            trace!("Arguments; effect: {:?}, index: {}, value: {}, ptr: {:?}, opt: {}",
                    effect, index, value, ptr, opt);
        }
    }
    0
}

// Read a string from the `ptr` buffer
fn read_string(ptr: *mut c_void) -> String {
    use std::ffi::CStr;

    String::from_utf8_lossy(
        unsafe { CStr::from_ptr(ptr as *mut c_char).to_bytes() }
    ).into_owned()
}

/// Translate `Vec<Event>` into `&api::Events` and use via a callback.
///
/// Both plugins and hosts can receive VST events, this simply translates the rust structure into
/// the equivalent API structure and takes care of cleanup.
pub fn process_events<F>(events: Vec<Event>, callback: F)
    where F: FnOnce(*mut c_void)
{
    use api::flags::REALTIME_EVENT;

    let len = events.len();

    // The `api::Events` structure uses a variable length array which is difficult to represent in
    // rust. We begin by creating a vector with the appropriate byte size by calculating the header
    // and the variable length body seperately.
    let header_size = mem::size_of::<api::Events>() - (mem::size_of::<*mut api::Event>() * 2);
    let body_size = mem::size_of::<*mut api::Event>() * len;

    let mut send = vec![0u8; header_size + body_size];

    let send_events: &mut [*mut api::Event] = unsafe {
        // The header is updated by casting the array to the `api::Events` type and specifying the
        // required fields. We create a slice from the position of the first event and the length
        // of the array.
        let ptr = send.as_mut_ptr() as *mut api::Events;
        (*ptr).num_events = len as i32;

        // A slice view of the body
        slice::from_raw_parts_mut(&mut (*ptr).events[0], len)
    };

    // Each event is zipped with the target body array slot. Most of what's happening here is just
    // copying data but the key thing to notice is that each event is boxed and cast to
    // (*mut api::Event). This way we can let the callback handle the event, and then later create
    // the box again from the raw pointer so that it can be properly dropped.
    for (event, out) in events.iter().zip(send_events.iter_mut()) {
        *out = match *event {
            Event::Midi { data, delta_frames, live,
                          note_length, note_offset,
                          detune, note_off_velocity } => {
                Box::into_raw(Box::new(api::MidiEvent {
                    event_type: api::EventType::Midi,
                    byte_size: mem::size_of::<api::MidiEvent>() as i32,
                    delta_frames: delta_frames,
                    flags: if live { REALTIME_EVENT.bits() } else { 0 },
                    note_length: note_length.unwrap_or(0),
                    note_offset: note_offset.unwrap_or(0),
                    midi_data: data,
                    _midi_reserved: 0,
                    detune: detune,
                    note_off_velocity: note_off_velocity,
                    _reserved1: 0,
                    _reserved2: 0
                })) as *mut api::Event
            }
            Event::SysEx { payload, delta_frames } => {
                Box::into_raw(Box::new(api::SysExEvent {
                    event_type: api::EventType::SysEx,
                    byte_size: mem::size_of::<api::SysExEvent>() as i32,
                    delta_frames: delta_frames,
                    _flags: 0,
                    data_size: payload.len() as i32,
                    _reserved1: 0,
                    system_data: payload.as_ptr() as *const u8 as *mut u8,
                    _reserved2: 0,
                })) as *mut api::Event
            }
            Event::Deprecated(e) => Box::into_raw(Box::new(e))
        };
    }

    // Allow the callback to use the pointer
    callback(send.as_mut_ptr() as *mut c_void);

    // Clean up the created events
    unsafe {
        for &mut event in send_events {
            match (*event).event_type {
                api::EventType::Midi => {
                    drop(Box::from_raw(event as *mut api::MidiEvent));
                }
                api::EventType::SysEx => {
                    drop(Box::from_raw(event as *mut api::SysExEvent));
                }
                _ => {
                    drop(Box::from_raw(event));
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate euclid;
extern crate image;

#[cfg(feature = "glium_backend")]
extern crate glium;
extern crate vitral;

fn main() { feature::main(); }

#[cfg(feature = "glium_backend")]
mod feature {
    use euclid::{Rect, Point2D, point2, Size2D, vec2};
    use glium::{self, glutin};
    use image;
    use std::path::Path;
    use vitral::{self, Align, ButtonAction, RectUtil};
    use vitral::glium_backend as backend;
    use vitral::glium_backend::{DefaultVertex, TextureHandle};

    type Core = vitral::Core<TextureHandle, DefaultVertex>;
    type Backend = backend::Backend<DefaultVertex>;

    pub fn clamp<C: PartialOrd + Copy>(mn: C, mx: C, x: C) -> C {
        if x < mn {
            mn
        } else if x > mx {
            mx
        } else {
            x
        }
    }

    fn load_image<V>(backend: &mut backend::Backend<V>, path: &str) -> vitral::ImageData<usize>
    where
        V: vitral::Vertex + glium::Vertex,
    {
        let image: vitral::ImageBuffer = image::open(&Path::new(path)).unwrap().into();
        let size = image.size;
        let texture = backend.make_texture(image);

        vitral::ImageData {
            texture,
            size,
            tex_coords: Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1.0, 1.0)),
        }
    }

    struct App {
        core: Core,
        font: vitral::FontData<TextureHandle>,
        image: vitral::ImageData<TextureHandle>,
        fore_color: [f32; 4],
        back_color: [f32; 4],
        bounds: Rect<f32>,
    }

    impl App {
        pub fn new(core: Core, backend: &mut Backend) -> App {
            let bounds = core.bounds();
            let font = vitral::build_default_font(|img| backend.make_texture(img));
            let image = load_image(backend, "julia.png");
            App {
                core,
                font,
                image,
                fore_color: [1.0, 0.5, 0.1, 1.0],
                back_color: [0.0, 0.0, 0.0, 1.0],
                bounds,
            }
        }

        fn bright_color(&self) -> [f32; 4] {
            [
                clamp(0.0, 1.0, self.fore_color[0] * 1.5),
                clamp(0.0, 1.0, self.fore_color[1] * 1.5),
                clamp(0.0, 1.0, self.fore_color[2] * 1.5),
                1.0,
            ]
        }

        fn begin(&mut self) { self.core.begin_frame(); }

        fn outline_text(&mut self, pos: Point2D<f32>, text: &str) -> Point2D<f32> {
            for offset in &[
                vec2(-1.0, 0.0),
                vec2(1.0, 0.0),
                vec2(0.0, -1.0),
                vec2(0.0, 1.0),
            ]
            {
                self.core.draw_text(
                    &self.font,
                    pos + *offset,
                    Align::Left,
                    self.back_color,
                    text,
                );
            }

            self.core.draw_text(
                &self.font,
                pos,
                Align::Left,
                self.fore_color,
                text,
            )
        }

        fn title_bar(&mut self, bounds: &Rect<f32>, text: &str) {
            self.core.fill_rect(bounds, self.back_color);
            {
                let bounds = bounds.inclusivize();
                self.core.draw_line(
                    1.0,
                    self.fore_color,
                    bounds.bottom_left(),
                    bounds.bottom_right(),
                );
            }

            // Margin
            let bounds = bounds.inflate(-2.0, -2.0);

            self.core.draw_text(
                &self.font,
                bounds.anchor(&point2(0.0, -1.0)),
                Align::Center,
                self.fore_color,
                text,
            );
        }

        fn quit_button(&mut self, bounds: &Rect<f32>) -> bool {
            let click_state = self.core.click_state(bounds);

            let color = if click_state != ButtonAction::Inert {
                self.bright_color()
            } else {
                self.fore_color
            };

            self.core.fill_rect(bounds, color);
            self.core.fill_rect(
                &bounds.inflate(-1.0, -1.0),
                self.back_color,
            );

            let inner = bounds.inflate(-3.0, -3.0).inclusivize();

            self.core.draw_line(
                1.0,
                color,
                inner.bottom_right(),
                inner.origin,
            );

            self.core.draw_line(
                1.0,
                color,
                inner.top_right(),
                inner.bottom_left(),
            );

            self.core.click_state(bounds) == ButtonAction::LeftClicked
        }

        fn render(&mut self) {
            self.core.draw_image(
                &self.image,
                point2(20.0, 20.0),
                [1.0, 1.0, 1.0, 1.0],
            );
            self.outline_text(point2(22.0, 22.0), "Hello, world!");
        }
    }

    pub fn main() {
        let size = Size2D::new(640.0, 360.0);
        let mut backend: Backend = backend::start(
            size.width as u32,
            size.height as u32,
            "Vitral Demo",
            backend::DEFAULT_SHADER,
        ).expect("Failed to start Glium backend!");

        let core = vitral::Builder::new().build(size, |img| backend.make_texture(img));
        let mut app = App::new(core, &mut backend);

        loop {
            app.begin();
            app.render();
            let (_, title_area) = app.bounds.horizontal_split(12.0);
            app.title_bar(&title_area, "Vitral Demo");

            let (_, widget_area) = title_area.vertical_split(-12.0);
            if app.quit_button(&widget_area) {
                return;
            }

            while let Some(k) = backend.poll_key() {
                if k.state == glutin::ElementState::Released {
                    continue;
                }

                if k.key_code == glutin::VirtualKeyCode::Q {
                    return;
                }

                if k.key_code == glutin::VirtualKeyCode::F12 {
                    let screenshot: image::ImageBuffer<image::Rgb<u8>, Vec<u8>> =
                        backend.screenshot().into();
                    image::save_buffer(
                        "screenshot.png",
                        &screenshot,
                        screenshot.width(),
                        screenshot.height(),
                        image::ColorType::RGB(8),
                    ).unwrap();
                    println!("Screenshot saved!");
                }
            }

            if !backend.update(&mut app.core) {
                break;
            }
        }
    }
}

#[cfg(not(feature = "glium_backend"))]
mod feature {
    pub fn main() {
        println!("Try running `cargo run --features \"glium_backend\" --example <example_name>");
    }
}
<commit_msg>Remove unnecessary glium dep<commit_after>extern crate euclid;
extern crate image;

#[cfg(feature = "glium_backend")]
extern crate glium;
extern crate vitral;

fn main() { feature::main(); }

#[cfg(feature = "glium_backend")]
mod feature {
    use euclid::{Rect, Point2D, point2, Size2D, vec2};
    use glium::glutin;
    use image;
    use std::path::Path;
    use vitral::{self, Align, ButtonAction, RectUtil};
    use vitral::glium_backend as backend;
    use vitral::glium_backend::{DefaultVertex, TextureHandle};

    type Core = vitral::Core<TextureHandle, DefaultVertex>;
    type Backend = backend::Backend<DefaultVertex>;
    type ImageData = vitral::ImageData<TextureHandle>;

    pub fn clamp<C: PartialOrd + Copy>(mn: C, mx: C, x: C) -> C {
        if x < mn {
            mn
        } else if x > mx {
            mx
        } else {
            x
        }
    }

    fn load_image(backend: &mut Backend, path: &str) -> ImageData {
        let image: vitral::ImageBuffer = image::open(&Path::new(path)).unwrap().into();
        let size = image.size;
        let texture = backend.make_texture(image);

        vitral::ImageData {
            texture,
            size,
            tex_coords: Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1.0, 1.0)),
        }
    }

    struct App {
        core: Core,
        font: vitral::FontData<TextureHandle>,
        image: vitral::ImageData<TextureHandle>,
        fore_color: [f32; 4],
        back_color: [f32; 4],
        bounds: Rect<f32>,
    }

    impl App {
        pub fn new(core: Core, backend: &mut Backend) -> App {
            let bounds = core.bounds();
            let font = vitral::build_default_font(|img| backend.make_texture(img));
            let image = load_image(backend, "julia.png");
            App {
                core,
                font,
                image,
                fore_color: [1.0, 0.5, 0.1, 1.0],
                back_color: [0.0, 0.0, 0.0, 1.0],
                bounds,
            }
        }

        fn bright_color(&self) -> [f32; 4] {
            [
                clamp(0.0, 1.0, self.fore_color[0] * 1.5),
                clamp(0.0, 1.0, self.fore_color[1] * 1.5),
                clamp(0.0, 1.0, self.fore_color[2] * 1.5),
                1.0,
            ]
        }

        fn begin(&mut self) { self.core.begin_frame(); }

        fn outline_text(&mut self, pos: Point2D<f32>, text: &str) -> Point2D<f32> {
            for offset in &[
                vec2(-1.0, 0.0),
                vec2(1.0, 0.0),
                vec2(0.0, -1.0),
                vec2(0.0, 1.0),
            ]
            {
                self.core.draw_text(
                    &self.font,
                    pos + *offset,
                    Align::Left,
                    self.back_color,
                    text,
                );
            }

            self.core.draw_text(
                &self.font,
                pos,
                Align::Left,
                self.fore_color,
                text,
            )
        }

        fn title_bar(&mut self, bounds: &Rect<f32>, text: &str) {
            self.core.fill_rect(bounds, self.back_color);
            {
                let bounds = bounds.inclusivize();
                self.core.draw_line(
                    1.0,
                    self.fore_color,
                    bounds.bottom_left(),
                    bounds.bottom_right(),
                );
            }

            // Margin
            let bounds = bounds.inflate(-2.0, -2.0);

            self.core.draw_text(
                &self.font,
                bounds.anchor(&point2(0.0, -1.0)),
                Align::Center,
                self.fore_color,
                text,
            );
        }

        fn quit_button(&mut self, bounds: &Rect<f32>) -> bool {
            let click_state = self.core.click_state(bounds);

            let color = if click_state != ButtonAction::Inert {
                self.bright_color()
            } else {
                self.fore_color
            };

            self.core.fill_rect(bounds, color);
            self.core.fill_rect(
                &bounds.inflate(-1.0, -1.0),
                self.back_color,
            );

            let inner = bounds.inflate(-3.0, -3.0).inclusivize();

            self.core.draw_line(
                1.0,
                color,
                inner.bottom_right(),
                inner.origin,
            );

            self.core.draw_line(
                1.0,
                color,
                inner.top_right(),
                inner.bottom_left(),
            );

            self.core.click_state(bounds) == ButtonAction::LeftClicked
        }

        fn render(&mut self) {
            self.core.draw_image(
                &self.image,
                point2(20.0, 20.0),
                [1.0, 1.0, 1.0, 1.0],
            );
            self.outline_text(point2(22.0, 22.0), "Hello, world!");
        }
    }

    pub fn main() {
        let size = Size2D::new(640.0, 360.0);
        let mut backend: Backend = backend::start(
            size.width as u32,
            size.height as u32,
            "Vitral Demo",
            backend::DEFAULT_SHADER,
        ).expect("Failed to start Glium backend!");

        let core = vitral::Builder::new().build(size, |img| backend.make_texture(img));
        let mut app = App::new(core, &mut backend);

        loop {
            app.begin();
            app.render();
            let (_, title_area) = app.bounds.horizontal_split(12.0);
            app.title_bar(&title_area, "Vitral Demo");

            let (_, widget_area) = title_area.vertical_split(-12.0);
            if app.quit_button(&widget_area) {
                return;
            }

            while let Some(k) = backend.poll_key() {
                if k.state == glutin::ElementState::Released {
                    continue;
                }

                if k.key_code == glutin::VirtualKeyCode::Q {
                    return;
                }

                if k.key_code == glutin::VirtualKeyCode::F12 {
                    let screenshot: image::ImageBuffer<image::Rgb<u8>, Vec<u8>> =
                        backend.screenshot().into();
                    image::save_buffer(
                        "screenshot.png",
                        &screenshot,
                        screenshot.width(),
                        screenshot.height(),
                        image::ColorType::RGB(8),
                    ).unwrap();
                    println!("Screenshot saved!");
                }
            }

            if !backend.update(&mut app.core) {
                break;
            }
        }
    }
}

#[cfg(not(feature = "glium_backend"))]
mod feature {
    pub fn main() {
        println!("Try running `cargo run --features \"glium_backend\" --example <example_name>");
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate modparse_derive;
#[macro_use]
extern crate derive_builder;
#[macro_use]
extern crate torchrs;
#[macro_use]
extern crate clap;

use torchrs::autograd::{Variable, VariableArgs, VarAccess};
use torchrs::tensor::Tensor;
use torchrs::optim;
use torchrs::optim::OptimVal;
use torchrs::nn;
use torchrs::nn::{ModuleStruct, ModIntf, ModDelegate, Module, ModRefMut};
use torchrs::nn::functional as F;
use torchrs::utils::data as D;
use torchrs::utils::torchvision::{datasets, transforms};

use clap::{Arg, App, SubCommand};

use std::env;

#[derive(Clone, Builder)]
struct NetArgs {
    #[builder(default="64")]
    batch_size: usize,
    #[builder(default="1000")]
    test_batch_size: usize,
    #[builder(default="10")]
    epochs: u32,
    #[builder(default="0.01")]
    lr: f32,
    #[builder(default="0.5")]
    momentum: f32,
    #[builder(default="true")]
    cuda: bool,
    #[builder(default="1")]
    seed: usize,
    #[builder(default="10")]
    log_interval: usize,
}

impl Default for NetArgs {
    fn default() -> Self {
        NetArgsBuilder::default().build().unwrap()
    }
}

fn parse_args() -> NetArgs {
    unimplemented!();
    let cmd_args: Vec<String> = env::args().collect();
    let mut args = NetArgs::default();
    let matches = App::new("Torch.rs MNIST Example")
        .version("0.1")
        .author("Some Guy")
        .about("recognize handwritten digits")
        .arg(Arg::with_name("batch-size").takes_value(true))
        .arg(Arg::with_name("test-batch-size").takes_value(true))
        .arg(Arg::with_name("epochs").takes_value(true))
        .arg(Arg::with_name("lr").takes_value(true))
        .arg(Arg::with_name("momentum").takes_value(true))
        .arg(Arg::with_name("no-cuda").takes_value(false))
        .arg(Arg::with_name("seed").takes_value(true))
        .arg(Arg::with_name("log-interval").takes_value(true))
        .get_matches();
    let (b_size, tb_size, epochs, lr, momentum, seed, log_int) =
        (value_t!(matches.value_of("batch-size"), usize).ok(),
         value_t!(matches.value_of("test-batch-size"), usize).ok(),
         value_t!(matches.value_of("epochs"), u32).ok(),
         value_t!(matches.value_of("lr"), f32).ok(),
         value_t!(matches.value_of("momentum"), f32).ok(),
         value_t!(matches.value_of("seed"), usize).ok(),
         value_t!(matches.value_of("log-interval"), usize).ok());
    if let Some(b_size) = b_size {
        args.batch_size = b_size
    }
    if let Some(tb_size) = tb_size {
        args.test_batch_size = tb_size
    }
    if let Some(epochs) = epochs {
        args.epochs = epochs
    }
    if let Some(lr) = lr {
        args.lr = lr
    }
    if let Some(momentum) = momentum {
        args.momentum = momentum
    }
    if let Some(seed) = seed {
        args.seed = seed
    }
    if let Some(log_int) = log_int {
        args.log_interval = log_int
    }
    //XXX CUDA?
    args
}

#[derive(ModParse)]
struct Net<T: Default + Copy> {
    delegate: nn::Module<T>,
    conv1: nn::Conv2d<T>,
    conv2: nn::Conv2d<T>,
    fc1: nn::Linear<T>,
    fc2: nn::Linear<T>,
}

impl<T: Default + Copy> Net<T> {
    pub fn new() -> Net<T> {
        Net {
                delegate: nn::Module::new(),
                conv1: nn::Conv2d::build(1, 10, (5, 5)).done(),
                conv2: nn::Conv2d::build(10, 20, (5, 5)).done(),
                fc1: nn::Linear::build(320, 50).done(),
                fc2: nn::Linear::build(50, 10).done(),
            }
            .init_module()
    }
}
impl_mod_delegate!(Net);

// The forward operations could take on one of two implementations.
// The first supporting a near verbatim version of the python
// implementation, and the second supporting a slightly more
// idiomatic to Rust method chaining.

// a) as a near verbatim implementation of the python version
impl<T: Copy + Default + 'static> ModIntf<T> for Net<T> {
    fn forward(&mut self, args: &mut Variable<T>) -> Variable<T> {
        let pool_val = F::MaxPool2dArgs::default();
        let mut dropout_val = F::DropoutArgs::default();
        dropout_val.training = self.delegate.training;
        let mut x = F::relu(&F::max_pool2d(&self.conv1.f(args), (2, 2), &pool_val));
        x = F::relu(&F::max_pool2d(&F::dropout2d(&self.conv2.f(&mut x), &dropout_val),
                                   (2, 2),
                                   &pool_val));
        x = x.view(&[-1, 320]);
        x = F::relu(&self.fc1.f(&mut x));
        x = F::dropout(&x, &dropout_val);
        x = self.fc2.f(&mut x);
        F::log_softmax(&x)
    }
}

fn train<'a>(model: &mut Net<f32>,
             args: &NetArgs,
             train_loader: &D::BatchLoader<f32, i64>,
             epoch: u32,
             optimizer: &mut optim::OptIntf<Net<f32>, f32>) {
    model.train();
    for (batch_idx, (ref data, ref target)) in train_loader.iter().enumerate() {
        let (mut data, target) = if args.cuda {
            (Variable::new(data.cuda(None).clone()), Variable::new(target.cuda(None).clone()))
        } else {
            (Variable::new(data.clone()), Variable::new(target.clone()))
        };
        optimizer.zero_grad();
        let output = model.f(&mut data);
        let mut loss = F::nll_loss(&output, &target, &F::NLLLossArgs::default());
        loss.backward();
        optimizer.step();
        if batch_idx % args.log_interval == 0 {
            println!("Train Epoch: {} [{}/{} ({:.0}%)]\tLoss: {:.6}",
                     epoch,
                     batch_idx * data.data().len(),
                     train_loader.dataset.len(),
                     100. * (batch_idx as f32) / train_loader.len() as f32,
                     loss.data()[0]);
        }
    }
}

fn test(model: &mut Net<f32>, args: &NetArgs, test_loader: &D::BatchLoader<f32, i64>) {
    model.eval();
    let mut test_loss = 0.;
    let mut correct = 0;
    for (ref data, ref target) in test_loader.iter() {
        let varargs = VariableArgs {
            volatile: true,
            ..Default::default()
        };
        let (mut data, mut target) = if args.cuda {
            (Variable::new_args(data.cuda(None).clone(), &varargs),
             Variable::new(target.cuda(None).clone()))
        } else {
            (Variable::new_args(data.clone(), &varargs), Variable::new(target.clone()))
        };
        let mut output = model.f(&mut data);
        test_loss += F::nll_loss(&output, &target, &F::NLLLossArgs::default());
        let pred = output.data().max_reduce(1).1;
        correct += pred.eq_tensor(&*target.data()).cpu().sum() as u32;
    }
    test_loss /= test_loader.len() as f32;
    println!("\nTest set: Average loss: {:.4}, Accuracy: {}/{} ({:.0}%)\n",
             test_loss,
             correct,
             test_loader.dataset.len(),
             100. * (correct as f32) / (test_loader.dataset.len() as f32))

}

fn main() {
    // no data loaders yet so demonstrate with Vec placeholder

    let train_loader: D::BatchLoader<f32, i64> =
        D::DataLoader::new(datasets::MNIST::<f32>::build("../data").done(None),
                           D::DataLoaderArgs::default());

    let test_loader: D::BatchLoader<f32, i64> =
        D::DataLoader::new(datasets::MNIST::<f32>::build("../data").done(None),
                           D::DataLoaderArgs::default());
    let args = parse_args();
    let mut model = Net::new();
    let refmut: ModRefMut<Net<f32>>;
    {
        let p = &mut model as *mut Net<f32>;
        refmut = p.into();
    }
    let mut optimizer = optim::SGD::new(refmut,
                                        map_opt!{"lr" => args.lr, "momentum" => args.momentum});
    for epoch in 1..args.epochs + 1 {
        train(&mut model, &args, &train_loader, epoch, &mut optimizer);
        test(&mut model, &args, &test_loader);
    }
}
<commit_msg>eliminate need for casting to pointer with ModRefMut<commit_after>#[macro_use]
extern crate modparse_derive;
#[macro_use]
extern crate derive_builder;
#[macro_use]
extern crate torchrs;
#[macro_use]
extern crate clap;

use torchrs::autograd::{Variable, VariableArgs, VarAccess};
use torchrs::tensor::Tensor;
use torchrs::optim;
use torchrs::optim::OptimVal;
use torchrs::nn;
use torchrs::nn::{ModuleStruct, ModIntf, ModDelegate, Module, ModRefMut};
use torchrs::nn::functional as F;
use torchrs::utils::data as D;
use torchrs::utils::torchvision::{datasets, transforms};

use clap::{Arg, App, SubCommand};

use std::env;

#[derive(Clone, Builder)]
struct NetArgs {
    #[builder(default="64")]
    batch_size: usize,
    #[builder(default="1000")]
    test_batch_size: usize,
    #[builder(default="10")]
    epochs: u32,
    #[builder(default="0.01")]
    lr: f32,
    #[builder(default="0.5")]
    momentum: f32,
    #[builder(default="true")]
    cuda: bool,
    #[builder(default="1")]
    seed: usize,
    #[builder(default="10")]
    log_interval: usize,
}

impl Default for NetArgs {
    fn default() -> Self {
        NetArgsBuilder::default().build().unwrap()
    }
}

fn parse_args() -> NetArgs {
    unimplemented!();
    let cmd_args: Vec<String> = env::args().collect();
    let mut args = NetArgs::default();
    let matches = App::new("Torch.rs MNIST Example")
        .version("0.1")
        .author("Some Guy")
        .about("recognize handwritten digits")
        .arg(Arg::with_name("batch-size").takes_value(true))
        .arg(Arg::with_name("test-batch-size").takes_value(true))
        .arg(Arg::with_name("epochs").takes_value(true))
        .arg(Arg::with_name("lr").takes_value(true))
        .arg(Arg::with_name("momentum").takes_value(true))
        .arg(Arg::with_name("no-cuda").takes_value(false))
        .arg(Arg::with_name("seed").takes_value(true))
        .arg(Arg::with_name("log-interval").takes_value(true))
        .get_matches();
    let (b_size, tb_size, epochs, lr, momentum, seed, log_int) =
        (value_t!(matches.value_of("batch-size"), usize).ok(),
         value_t!(matches.value_of("test-batch-size"), usize).ok(),
         value_t!(matches.value_of("epochs"), u32).ok(),
         value_t!(matches.value_of("lr"), f32).ok(),
         value_t!(matches.value_of("momentum"), f32).ok(),
         value_t!(matches.value_of("seed"), usize).ok(),
         value_t!(matches.value_of("log-interval"), usize).ok());
    if let Some(b_size) = b_size {
        args.batch_size = b_size
    }
    if let Some(tb_size) = tb_size {
        args.test_batch_size = tb_size
    }
    if let Some(epochs) = epochs {
        args.epochs = epochs
    }
    if let Some(lr) = lr {
        args.lr = lr
    }
    if let Some(momentum) = momentum {
        args.momentum = momentum
    }
    if let Some(seed) = seed {
        args.seed = seed
    }
    if let Some(log_int) = log_int {
        args.log_interval = log_int
    }
    //XXX CUDA?
    args
}

#[derive(ModParse)]
struct Net<T: Default + Copy> {
    delegate: nn::Module<T>,
    conv1: nn::Conv2d<T>,
    conv2: nn::Conv2d<T>,
    fc1: nn::Linear<T>,
    fc2: nn::Linear<T>,
}

impl<T: Default + Copy> Net<T> {
    pub fn new() -> Net<T> {
        Net {
                delegate: nn::Module::new(),
                conv1: nn::Conv2d::build(1, 10, (5, 5)).done(),
                conv2: nn::Conv2d::build(10, 20, (5, 5)).done(),
                fc1: nn::Linear::build(320, 50).done(),
                fc2: nn::Linear::build(50, 10).done(),
            }
            .init_module()
    }
}
impl_mod_delegate!(Net);

// The forward operations could take on one of two implementations.
// The first supporting a near verbatim version of the python
// implementation, and the second supporting a slightly more
// idiomatic to Rust method chaining.

// a) as a near verbatim implementation of the python version
impl<T: Copy + Default + 'static> ModIntf<T> for Net<T> {
    fn forward(&mut self, args: &mut Variable<T>) -> Variable<T> {
        let pool_val = F::MaxPool2dArgs::default();
        let mut dropout_val = F::DropoutArgs::default();
        dropout_val.training = self.delegate.training;
        let mut x = F::relu(&F::max_pool2d(&self.conv1.f(args), (2, 2), &pool_val));
        x = F::relu(&F::max_pool2d(&F::dropout2d(&self.conv2.f(&mut x), &dropout_val),
                                   (2, 2),
                                   &pool_val));
        x = x.view(&[-1, 320]);
        x = F::relu(&self.fc1.f(&mut x));
        x = F::dropout(&x, &dropout_val);
        x = self.fc2.f(&mut x);
        F::log_softmax(&x)
    }
}

fn train<'a>(model: &mut Net<f32>,
             args: &NetArgs,
             train_loader: &D::BatchLoader<f32, i64>,
             epoch: u32,
             optimizer: &mut optim::OptIntf<f32>) {
    model.train();
    for (batch_idx, (ref data, ref target)) in train_loader.iter().enumerate() {
        let (mut data, target) = if args.cuda {
            (Variable::new(data.cuda(None).clone()), Variable::new(target.cuda(None).clone()))
        } else {
            (Variable::new(data.clone()), Variable::new(target.clone()))
        };
        optimizer.zero_grad(model.parameters());
        let output = model.f(&mut data);
        let mut loss = F::nll_loss(&output, &target, &F::NLLLossArgs::default());
        loss.backward();
        optimizer.step(model.parameters());
        if batch_idx % args.log_interval == 0 {
            println!("Train Epoch: {} [{}/{} ({:.0}%)]\tLoss: {:.6}",
                     epoch,
                     batch_idx * data.data().len(),
                     train_loader.dataset.len(),
                     100. * (batch_idx as f32) / train_loader.len() as f32,
                     loss.data()[0]);
        }
    }
}

fn test(model: &mut Net<f32>, args: &NetArgs, test_loader: &D::BatchLoader<f32, i64>) {
    model.eval();
    let mut test_loss = 0.;
    let mut correct = 0;
    for (ref data, ref target) in test_loader.iter() {
        let varargs = VariableArgs {
            volatile: true,
            ..Default::default()
        };
        let (mut data, mut target) = if args.cuda {
            (Variable::new_args(data.cuda(None).clone(), &varargs),
             Variable::new(target.cuda(None).clone()))
        } else {
            (Variable::new_args(data.clone(), &varargs), Variable::new(target.clone()))
        };
        let mut output = model.f(&mut data);
        test_loss += F::nll_loss(&output, &target, &F::NLLLossArgs::default());
        let pred = output.data().max_reduce(1).1;
        correct += pred.eq_tensor(&*target.data()).cpu().sum() as u32;
    }
    test_loss /= test_loader.len() as f32;
    println!("\nTest set: Average loss: {:.4}, Accuracy: {}/{} ({:.0}%)\n",
             test_loss,
             correct,
             test_loader.dataset.len(),
             100. * (correct as f32) / (test_loader.dataset.len() as f32))

}

fn main() {
    // no data loaders yet so demonstrate with Vec placeholder

    let train_loader: D::BatchLoader<f32, i64> =
        D::DataLoader::new(datasets::MNIST::<f32>::build("../data").done(None),
                           D::DataLoaderArgs::default());

    let test_loader: D::BatchLoader<f32, i64> =
        D::DataLoader::new(datasets::MNIST::<f32>::build("../data").done(None),
                           D::DataLoaderArgs::default());
    let args = parse_args();
    let mut model = Net::new();
    let mut optimizer = optim::SGD::new(map_opt!{"lr" => args.lr, "momentum" => args.momentum});
    for epoch in 1..args.epochs + 1 {
        train(&mut model, &args, &train_loader, epoch, &mut optimizer);
        test(&mut model, &args, &test_loader);
    }
}
<|endoftext|>