<commit_before>use std::fmt;
use std::hash::Hash;

use recognisable::Instruction;
use util::integerisable::Integerisable1;
use tree_stack_automaton::TreeStack;

use integeriser::{HashIntegeriser, Integeriser};

/// Instruction on `TreeStack<A>`s.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Hash)]
pub enum TreeStackInstruction<A> {
    Up {
        n: usize,
        current_val: A,
        old_val: A,
        new_val: A,
    },
    Push { n: usize, current_val: A, new_val: A },
    Down {
        current_val: A,
        old_val: A,
        new_val: A,
    },
}

impl<A> TreeStackInstruction<A> {
    fn map<F, B>(&self, mut f: F) -> TreeStackInstruction<B>
        where F: FnMut(&A) -> B
    {
        match *self {
            TreeStackInstruction::Up { n, ref current_val, ref old_val, ref new_val } =>
                TreeStackInstruction::Up { n,
                                           current_val: f(current_val),
                                           old_val: f(old_val),
                                           new_val: f(new_val),
                },
            TreeStackInstruction::Push { n, ref current_val, ref new_val } =>
                TreeStackInstruction::Push { n,
                                             current_val: f(current_val),
                                             new_val: f(new_val),
                },
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } =>
                TreeStackInstruction::Down { current_val: f(current_val),
                                             old_val: f(old_val),
                                             new_val: f(new_val),
                },
        }
    }
}

impl<A: Ord + PartialEq + Clone + Hash> Instruction for TreeStackInstruction<A> {
    type Storage = TreeStack<A>;

    fn apply(&self, t: TreeStack<A>) -> Vec<TreeStack<A>> {
        match *self {
            TreeStackInstruction::Up { n, ref current_val, ref old_val, ref new_val } => {
                if t.current_symbol() == current_val {
                    match t.clone().up(n) {
                        Ok(child) => {
                            if child.current_symbol() == old_val {
                                vec![child.set(new_val.clone())]
                            } else {
                                vec![]
                            }
                        },
                        Err(_) => {
                            vec![]
                        },
                    }
                } else {
                    vec![]
                }
            }
            TreeStackInstruction::Push { n, ref current_val, ref new_val } => {
                if t.current_symbol() == current_val {
                    t.clone().push(n, new_val.clone()).into_iter().collect()
                } else {
                    vec![]
                }
            }
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } => {
                if t.current_symbol() == current_val {
                    match t.clone().down() {
                        Ok(parent) => {
                            if parent.current_symbol() == old_val {
                                vec![parent.set(new_val.clone())]
                            } else {
                                vec![]
                            }
                        },
                        _ => vec![],
                    }
                } else {
                    vec![]
                }
            }
        }
    }
}

impl<A: Clone + Eq + Hash> Integerisable1 for TreeStackInstruction<A> {
    type AInt = TreeStackInstruction<usize>;
    type I = HashIntegeriser<A>;

    fn integerise(&self, integeriser: &mut Self::I) -> Self::AInt {
        self.map(|a| integeriser.integerise(a.clone()))
    }

    fn un_integerise(v: &Self::AInt, integeriser: &Self::I) -> Self {
        v.map(|i| integeriser.find_value(*i).unwrap().clone())
    }
}

impl<A: fmt::Display> fmt::Display for TreeStackInstruction<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TreeStackInstruction::Up { n, ref current_val, ref old_val, ref new_val } =>
                write!(f, "(Up {} {} {} {})", n, current_val, old_val, new_val),
            TreeStackInstruction::Push { n, ref current_val, ref new_val } =>
                write!(f, "(Push {} {} {})", n, current_val, new_val),
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } =>
                write!(f, "(Down {} {} {})", current_val, old_val, new_val),
        }
    }
}
<commit_msg>src/tree_stack_automaton/tree_stack_instruction.rs: added tests<commit_after>use std::fmt;
use std::hash::Hash;

use recognisable::Instruction;
use util::integerisable::Integerisable1;
use tree_stack_automaton::TreeStack;

use integeriser::{HashIntegeriser, Integeriser};

/// Instruction on `TreeStack<A>`s.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Hash)]
pub enum TreeStackInstruction<A> {
    Up {
        n: usize,
        current_val: A,
        old_val: A,
        new_val: A,
    },
    Push { n: usize, current_val: A, new_val: A },
    Down {
        current_val: A,
        old_val: A,
        new_val: A,
    },
}

impl<A> TreeStackInstruction<A> {
    fn map<F, B>(&self, mut f: F) -> TreeStackInstruction<B>
        where F: FnMut(&A) -> B
    {
        match *self {
            TreeStackInstruction::Up { n, ref current_val, ref old_val, ref new_val } =>
                TreeStackInstruction::Up { n,
                                           current_val: f(current_val),
                                           old_val: f(old_val),
                                           new_val: f(new_val),
                },
            TreeStackInstruction::Push { n, ref current_val, ref new_val } =>
                TreeStackInstruction::Push { n,
                                             current_val: f(current_val),
                                             new_val: f(new_val),
                },
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } =>
                TreeStackInstruction::Down { current_val: f(current_val),
                                             old_val: f(old_val),
                                             new_val: f(new_val),
                },
        }
    }
}

impl<A: Ord + PartialEq + Clone + Hash> Instruction for TreeStackInstruction<A> {
    type Storage = TreeStack<A>;

    fn apply(&self, t: TreeStack<A>) -> Vec<TreeStack<A>> {
        match *self {
            TreeStackInstruction::Up { n, ref current_val, ref old_val, ref new_val } => {
                if t.current_symbol() == current_val {
                    match t.clone().up(n) {
                        Ok(child) => {
                            if child.current_symbol() == old_val {
                                vec![child.set(new_val.clone())]
                            } else {
                                vec![]
                            }
                        },
                        Err(_) => {
                            vec![]
                        },
                    }
                } else {
                    vec![]
                }
            }
            TreeStackInstruction::Push { n, ref current_val, ref new_val } => {
                if t.current_symbol() == current_val {
                    t.clone().push(n, new_val.clone()).into_iter().collect()
                } else {
                    vec![]
                }
            }
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } => {
                if t.current_symbol() == current_val {
                    match t.clone().down() {
                        Ok(parent) => {
                            if parent.current_symbol() == old_val {
                                vec![parent.set(new_val.clone())]
                            } else {
                                vec![]
                            }
                        },
                        _ => vec![],
                    }
                } else {
                    vec![]
                }
            }
        }
    }
}

impl<A: Clone + Eq + Hash> Integerisable1 for TreeStackInstruction<A> {
    type AInt = TreeStackInstruction<usize>;
    type I = HashIntegeriser<A>;

    fn integerise(&self, integeriser: &mut Self::I) -> Self::AInt {
        self.map(|a| integeriser.integerise(a.clone()))
    }

    fn un_integerise(v: &Self::AInt, integeriser: &Self::I) -> Self {
        v.map(|i| integeriser.find_value(*i).unwrap().clone())
    }
}

impl<A: fmt::Display> fmt::Display for TreeStackInstruction<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TreeStackInstruction::Up { n, ref current_val, ref old_val, ref new_val } =>
                write!(f, "(Up {} {} {} {})", n, current_val, old_val, new_val),
            TreeStackInstruction::Push { n, ref current_val, ref new_val } =>
                write!(f, "(Push {} {} {})", n, current_val, new_val),
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } =>
                write!(f, "(Down {} {} {})", current_val, old_val, new_val),
        }
    }
}

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

    #[test]
    fn test_tree_stack_instruction_map_correctness() {
        let inputs = vec![
            (TreeStackInstruction::Up { n: 1, current_val: 1, old_val: 2, new_val: 3 },
                TreeStackInstruction::Up { n: 1, current_val: 2, old_val: 4, new_val: 6 }),
            (TreeStackInstruction::Down { current_val: 1, old_val: 2, new_val: 3 },
                TreeStackInstruction::Down { current_val: 2, old_val: 4, new_val: 6 }),
            (TreeStackInstruction::Push { n: 1, current_val: 1, new_val: 2 },
                TreeStackInstruction::Push { n: 1, current_val: 2, new_val: 4 }),
        ];

        for (instruction, mapped_control_instruction) in inputs {
            assert_eq!(
                mapped_control_instruction,
                instruction.map(&|x: &u8| x * 2)
            );
        }
    }

    #[test]
    fn test_tree_stack_instruction_map_inverse() {
        let instruction = TreeStackInstruction::Up { n: 1, current_val: 1, old_val: 2, new_val: 3 };
        let mapped_instruction = instruction.map(&|x: &u8| x * 2);

        assert_eq!(
            instruction,
            mapped_instruction.map(&|x: &u8| x / 2)
        );
    }

    #[test]
    fn test_tree_stack_instruction_apply_correctness() {
        let mut tree_stack = TreeStack::new('@').push(1, 'a').unwrap().down().unwrap();

        let up_instruction = TreeStackInstruction::Up {
            n: 1, current_val: '@', old_val: 'a', new_val: 'a'
        };
        assert_eq!(
            vec![tree_stack.clone().up(1).unwrap()],
            up_instruction.apply(tree_stack.clone())
        );

        tree_stack = tree_stack.up(1).unwrap();
        let down_instruction = TreeStackInstruction::Down {
            current_val: 'a', old_val: '@', new_val: '@'
        };
        assert_eq!(
            vec![tree_stack.clone().down().unwrap()],
            down_instruction.apply(tree_stack.clone())
        );

        tree_stack = tree_stack.down().unwrap();
        let push_instruction = TreeStackInstruction::Push {
            n: 2, current_val: '@', new_val: 'b'
        };
        assert_eq!(
            vec![tree_stack.clone().push(2, 'b').unwrap()],
            push_instruction.apply(tree_stack.clone())
        );

        let invalid_instructions = vec![
            TreeStackInstruction::Up { n: 1, current_val: 'x', old_val: 'a', new_val: 'a' },
            TreeStackInstruction::Down { current_val: '@', old_val: 'x', new_val: 'x' },
            TreeStackInstruction::Push { n: 1, current_val: '@', new_val: 'y' },
        ];

        for invalid_instruction in invalid_instructions {
            assert_eq!(
                Vec::<TreeStack<char>>::new(),
                invalid_instruction.apply(tree_stack.clone())
            );
        }
    }

    #[test]
    fn test_tree_stack_instruction_apply_inverse() {
        let tree_stack = TreeStack::new('@').push(1, 'a').unwrap().down().unwrap();
        let up_instruction = TreeStackInstruction::Up {
            n: 1, current_val: '@', old_val: 'a', new_val: 'a'
        };
        let down_instruction = TreeStackInstruction::Down {
            current_val: 'a', old_val: '@', new_val: '@'
        };
        let upped_tree_stack = up_instruction.apply(tree_stack.clone()).pop().unwrap();

        assert_eq!(
            vec![tree_stack],
            down_instruction.apply(upped_tree_stack)
        );
    }

    #[test]
    fn test_tree_stack_instruction_integerise_inverse() {
        let instructions = vec![
            TreeStackInstruction::Up { n: 1, current_val: 1, old_val: 2, new_val: 3 },
            TreeStackInstruction::Down { current_val: 1, old_val: 2, new_val: 3 },
            TreeStackInstruction::Push { n: 1, current_val: 1, new_val: 2 },
        ];
        let mut integeriser = HashIntegeriser::new();

        for instruction in instructions {
            let integerised_instruction = instruction.integerise(&mut integeriser);

            assert_eq!(
                instruction,
                TreeStackInstruction::un_integerise(&integerised_instruction, &integeriser)
            );
        }
    }
}
<|endoftext|>


<commit_before>extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{PlaybackContext, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Ready,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Stopped,
    Seeking(bool, bool), // (must_switch_to_play, must_keep_paused)
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    export_toc_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let export_toc_btn: gtk::Button = builder.get_object("export_toc-btn").unwrap();
        export_toc_btn.set_sensitive(false);

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            export_toc_btn: export_toc_btn,

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.export_toc_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    pub fn play_pause(&mut self) {
        let mut context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    AudioController::register_tick_callback(&self.audio_ctrl);
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    AudioController::remove_tick_callback(&self.audio_ctrl);
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                let (must_switch_to_play, must_keep_paused) = match self.state {
                    ControllerState::EOS |
                        ControllerState::Ready |
                        ControllerState::Seeking(true, false) => (true, false),
                    ControllerState::Seeking(true, true) => (true, true),
                    ControllerState::Paused |
                        ControllerState::Seeking(false, true) => (false, true),
                    _ => (false, false),
                };
                self.state = ControllerState::Seeking(must_switch_to_play, must_keep_paused);

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            AudioController::register_tick_callback(&self.audio_ctrl);
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    pub fn refresh_info(&mut self, position: u64) {
        self.info_ctrl.borrow_mut().tick(position, false);
    }

    fn hold(&mut self) {
        AudioController::remove_tick_callback(&self.audio_ctrl);
        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };

        self.play_pause_btn.set_icon_name("media-playback-start");
        self.window.set_sensitive(false);
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn restore_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.window.set_sensitive(true);
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking(must_switch_to_play, must_keep_paused) => {
                                if must_switch_to_play {
                                    this.context
                                        .as_mut()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    AudioController::register_tick_callback(&this.audio_ctrl);
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if must_keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();
                        let context = this.context.take().expect(
                            "MainController: InitDone but no context available",
                        );

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController::listener(InitDone) failed to lock media info")
                            .video_best
                            .is_some();

                        this.header_bar.set_subtitle(
                            Some(context.file_name.as_str()),
                        );

                        this.video_ctrl.new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);

                        this.context = Some(context);

                        this.state = ControllerState::Ready;
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                                this.seek(pos_to_restore, true); // accurate
                            }
                            _ => {
                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tick callback will be register again in case of a seek
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();
                        this.context = None;
                        this.state = ControllerState::Stopped;
                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.remove_listener();
                AudioController::remove_tick_callback(&this.audio_ctrl);
            }

            glib::Continue(keep_going)
        }));
    }

    fn select_media(&mut self) {
        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            self.state = ControllerState::Paused;
            self.window.set_sensitive(true);
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");
        self.export_toc_btn.set_sensitive(false);

        let (ctx_tx, ui_rx) = channel();

        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
                self.export_toc_btn.set_sensitive(true);
            }
            Err(error) => {
                eprintln!("Error opening media: {}", error);
            }
        };

        self.window.set_sensitive(true);
    }
}
<commit_msg>Fix warning<commit_after>extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{PlaybackContext, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Ready,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Stopped,
    Seeking(bool, bool), // (must_switch_to_play, must_keep_paused)
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    export_toc_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let export_toc_btn: gtk::Button = builder.get_object("export_toc-btn").unwrap();
        export_toc_btn.set_sensitive(false);

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            export_toc_btn: export_toc_btn,

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.export_toc_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    pub fn play_pause(&mut self) {
        let mut context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    AudioController::register_tick_callback(&self.audio_ctrl);
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    AudioController::remove_tick_callback(&self.audio_ctrl);
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                let (must_switch_to_play, must_keep_paused) = match self.state {
                    ControllerState::EOS |
                        ControllerState::Ready |
                        ControllerState::Seeking(true, false) => (true, false),
                    ControllerState::Seeking(true, true) => (true, true),
                    ControllerState::Paused |
                        ControllerState::Seeking(false, true) => (false, true),
                    _ => (false, false),
                };
                self.state = ControllerState::Seeking(must_switch_to_play, must_keep_paused);

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            AudioController::register_tick_callback(&self.audio_ctrl);
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    pub fn refresh_info(&mut self, position: u64) {
        self.info_ctrl.borrow_mut().tick(position, false);
    }

    fn hold(&mut self) {
        AudioController::remove_tick_callback(&self.audio_ctrl);
        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };

        self.play_pause_btn.set_icon_name("media-playback-start");
        self.window.set_sensitive(false);
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn restore_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.window.set_sensitive(true);
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking(must_switch_to_play, must_keep_paused) => {
                                if must_switch_to_play {
                                    this.context
                                        .as_mut()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    AudioController::register_tick_callback(&this.audio_ctrl);
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if must_keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();
                        let context = this.context.take().expect(
                            "MainController: InitDone but no context available",
                        );

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController::listener(InitDone) failed to lock media info")
                            .video_best
                            .is_some();

                        this.header_bar.set_subtitle(
                            Some(context.file_name.as_str()),
                        );

                        this.video_ctrl.new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);

                        this.context = Some(context);

                        this.state = ControllerState::Ready;
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                                this.seek(pos_to_restore, true); // accurate
                            }
                            _ => {
                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tick callback will be register again in case of a seek
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();
                        this.context = None;
                        this.state = ControllerState::Stopped;
                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.remove_listener();
                AudioController::remove_tick_callback(&this.audio_ctrl);
            }

            glib::Continue(keep_going)
        }));
    }

    fn select_media(&mut self) {
        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            self.state = ControllerState::Paused;
            self.window.set_sensitive(true);
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");
        self.export_toc_btn.set_sensitive(false);

        let (ctx_tx, ui_rx) = channel();

        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
                self.export_toc_btn.set_sensitive(true);
            }
            Err(error) => {
                eprintln!("Error opening media: {}", error);
            }
        };

        self.window.set_sensitive(true);
    }
}
<|endoftext|>


<commit_before>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;
use error::check;

struct Platform {
    id: cl_platform_id
}

impl Platform {
    fn get_devices() -> ~[Device] {
        get_devices(self)
    }

    fn name() -> ~str {
        let mut size = 0;

        clGetPlatformInfo(self.id,
                          CL_PLATFORM_NAME,
                          0,
                          ptr::null(),
                          ptr::mut_addr_of(&size));

        let name = str::repeat(" ", size as uint);

        do str::as_buf(name) |p, len| {
            clGetPlatformInfo(self.id,
                              CL_PLATFORM_NAME,
                              len as libc::size_t,
                              p as *libc::c_void,
                              ptr::mut_addr_of(&size));
        };

        move name
    }
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    check(errcode, "Failed to create opencl context!");

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,
    device: Device,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: & Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    check(errcode, "Failed to create command queue!");

    CommandQueue {
        cqueue: cqueue,
        device: device
    }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: & Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    check(errcode, "Failed to create buffer!");

    Buffer { buffer: buffer, size: size }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

pub impl Program {
    fn build(&self, device: Device) -> Result<(), ~str> {
        build_program(self, device)
    }

    fn create_kernel(&self, name: &str) -> Kernel {
        create_kernel(self, name)
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: & Context, device: Device,
                                  binary_path: & Path) -> Program {
    let mut errcode = 0;
    let binary = match move io::read_whole_file_str(binary_path) {
        result::Ok(move binary) => move binary,
        Err(e) => fail fmt!("%?", e)
    };
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.len() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::c_uchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    check(errcode, "Failed to create open cl program with binary!");

    Program { prg: program }
}

pub fn build_program(program: & Program, device: Device) -> Result<(), ~str> {
    let ret = clBuildProgram(program.prg, 1, ptr::addr_of(&device.id),
                             ptr::null(), ptr::null(), ptr::null());
    if ret == CL_SUCCESS as cl_int {
        Ok(())
    }
    else {
        let mut size = 0 as libc::size_t;
        let status = clGetProgramBuildInfo(
            program.prg,
            device.id,
            CL_PROGRAM_BUILD_LOG,
            0,
            ptr::null(),
            ptr::addr_of(&size));
        check(status, "Could not get build log");

        let buf = vec::from_elem(size as uint, 0u8);
        do vec::as_imm_buf(buf) |p, len| {
            let status = clGetProgramBuildInfo(
                program.prg,
                device.id,
                CL_PROGRAM_BUILD_LOG,
                len as libc::size_t,
                p as *libc::c_void,
                ptr::null());
            check(status, "Could not get build log");

            unsafe { Err(str::raw::from_c_str(p as *libc::c_char)) }
        }
    }
}

struct Kernel {
    kernel: cl_kernel,

    drop {
        clReleaseKernel(self.kernel);
    }
}

pub impl Kernel {
    fn set_arg<T: KernelArg>(i: uint, x: &T) {
        set_kernel_arg(&self, i as CL::cl_uint, x)
    }
}

pub fn create_kernel(program: & Program, kernel: & str) -> Kernel unsafe{
    let mut errcode = 0;
    let bytes = str::to_bytes(kernel);
    let kernel = clCreateKernel(program.prg, vec::raw::to_ptr(bytes) as *libc::c_char, ptr::addr_of(&errcode));

    check(errcode, "Failed to create kernel!");

    Kernel { kernel: kernel }
}

pub trait KernelArg {
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void);
}

macro_rules! scalar_kernel_arg (
    ($t:ty) => (pub impl $t: KernelArg {
        pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
            (sys::size_of::<$t>() as libc::size_t,
             ptr::addr_of(self) as *libc::c_void)
        }
    })
)

scalar_kernel_arg!(int)
scalar_kernel_arg!(uint)

pub fn set_kernel_arg<T: KernelArg>(kernel: & Kernel,
                                    position: cl_uint,
                                    arg: &T)
{
    let (size, p) = arg.get_value();
    let ret = clSetKernelArg(kernel.kernel, position,
                             size,
                             p);

    check(ret, "Failed to set kernel arg!");
}

pub fn enqueue_nd_range_kernel(cqueue: & CommandQueue, kernel: & Kernel, work_dim: cl_uint,
                               _global_work_offset: int, global_work_size: int,
                               local_work_size: int) unsafe{
    let ret = clEnqueueNDRangeKernel(cqueue.cqueue, kernel.kernel, work_dim,
                                     // ptr::addr_of(&global_work_offset) as *libc::size_t,
                                     ptr::null(),
                                     ptr::addr_of(&global_work_size) as *libc::size_t,
                                     ptr::addr_of(&local_work_size) as *libc::size_t,
                                     0, ptr::null(), ptr::null());
    check(ret, "Failed to enqueue nd range kernel!");
}

pub impl Buffer: KernelArg{
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
        (sys::size_of::<cl_mem>() as libc::size_t,
         ptr::addr_of(&self.buffer) as *libc::c_void)
    }
}

/**
This packages an OpenCL context, a device, and a command queue to
simplify handling all these structures.
*/
struct ComputeContext {
    ctx: Context,
    device: Device,
    q: CommandQueue
}

impl ComputeContext {
    fn create_program_from_source(src: &str) -> Program {
        do str::as_c_str(src) |src| {
            let mut status = CL_SUCCESS as cl_int;
            let program = clCreateProgramWithSource(
                self.ctx.ctx,
                1,
                ptr::addr_of(&src),
                ptr::null(),
                ptr::addr_of(&status));
            check(status, "Could not create program");

            Program { prg: program }
        }
    }
}

pub fn create_compute_context() -> @ComputeContext {
    // Enumerate all platforms until we find a device that works.

    for get_platforms().each |p| {
        let devices = p.get_devices();
        if devices.len() > 0 {
            let device = devices[0];
            let ctx = create_context(device);
            let q = create_commandqueue(&ctx, device);
            return @ComputeContext {
                ctx: move ctx,
                device: move device,
                q: move q
            }
        }
    }
    fail ~"Could not find an acceptable device."
}

#[cfg(test)]
mod test {
    macro_rules! expect (
        ($test: expr, $expected: expr) => ({
            let test = $test;
            let expected = $expected;
            if test != expected {
                fail fmt!("Test failure in %s: expected %?, got %?",
                          stringify!($test),
                          expected, test)
            }
        })
    )

    use vector::*;

    #[test]
    fn program_build() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);
    }

    #[test]
    fn simple_kernel() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn add_k() {
        let src = "__kernel void test(__global int *i, int k) { \
                       *i += k; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);
        k.set_arg(1, &42);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 43);
    }
}<commit_msg>New KernelIndex trait allows for 2D kernels.<commit_after>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;
use error::check;

struct Platform {
    id: cl_platform_id
}

impl Platform {
    fn get_devices() -> ~[Device] {
        get_devices(self)
    }

    fn name() -> ~str {
        let mut size = 0;

        clGetPlatformInfo(self.id,
                          CL_PLATFORM_NAME,
                          0,
                          ptr::null(),
                          ptr::mut_addr_of(&size));

        let name = str::repeat(" ", size as uint);

        do str::as_buf(name) |p, len| {
            clGetPlatformInfo(self.id,
                              CL_PLATFORM_NAME,
                              len as libc::size_t,
                              p as *libc::c_void,
                              ptr::mut_addr_of(&size));
        };

        move name
    }
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    check(errcode, "Failed to create opencl context!");

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,
    device: Device,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: & Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    check(errcode, "Failed to create command queue!");

    CommandQueue {
        cqueue: cqueue,
        device: device
    }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: & Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    check(errcode, "Failed to create buffer!");

    Buffer { buffer: buffer, size: size }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

pub impl Program {
    fn build(&self, device: Device) -> Result<(), ~str> {
        build_program(self, device)
    }

    fn create_kernel(&self, name: &str) -> Kernel {
        create_kernel(self, name)
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: & Context, device: Device,
                                  binary_path: & Path) -> Program {
    let mut errcode = 0;
    let binary = match move io::read_whole_file_str(binary_path) {
        result::Ok(move binary) => move binary,
        Err(e) => fail fmt!("%?", e)
    };
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.len() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::c_uchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    check(errcode, "Failed to create open cl program with binary!");

    Program { prg: program }
}

pub fn build_program(program: & Program, device: Device) -> Result<(), ~str> {
    let ret = clBuildProgram(program.prg, 1, ptr::addr_of(&device.id),
                             ptr::null(), ptr::null(), ptr::null());
    if ret == CL_SUCCESS as cl_int {
        Ok(())
    }
    else {
        let mut size = 0 as libc::size_t;
        let status = clGetProgramBuildInfo(
            program.prg,
            device.id,
            CL_PROGRAM_BUILD_LOG,
            0,
            ptr::null(),
            ptr::addr_of(&size));
        check(status, "Could not get build log");

        let buf = vec::from_elem(size as uint, 0u8);
        do vec::as_imm_buf(buf) |p, len| {
            let status = clGetProgramBuildInfo(
                program.prg,
                device.id,
                CL_PROGRAM_BUILD_LOG,
                len as libc::size_t,
                p as *libc::c_void,
                ptr::null());
            check(status, "Could not get build log");

            unsafe { Err(str::raw::from_c_str(p as *libc::c_char)) }
        }
    }
}

struct Kernel {
    kernel: cl_kernel,

    drop {
        clReleaseKernel(self.kernel);
    }
}

pub impl Kernel {
    fn set_arg<T: KernelArg>(i: uint, x: &T) {
        set_kernel_arg(&self, i as CL::cl_uint, x)
    }
}

pub fn create_kernel(program: & Program, kernel: & str) -> Kernel unsafe{
    let mut errcode = 0;
    let bytes = str::to_bytes(kernel);
    let kernel = clCreateKernel(program.prg, vec::raw::to_ptr(bytes) as *libc::c_char, ptr::addr_of(&errcode));

    check(errcode, "Failed to create kernel!");

    Kernel { kernel: kernel }
}

pub trait KernelArg {
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void);
}

macro_rules! scalar_kernel_arg (
    ($t:ty) => (pub impl $t: KernelArg {
        pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
            (sys::size_of::<$t>() as libc::size_t,
             ptr::addr_of(self) as *libc::c_void)
        }
    })
)

scalar_kernel_arg!(int)
scalar_kernel_arg!(uint)

pub fn set_kernel_arg<T: KernelArg>(kernel: & Kernel,
                                    position: cl_uint,
                                    arg: &T)
{
    let (size, p) = arg.get_value();
    let ret = clSetKernelArg(kernel.kernel, position,
                             size,
                             p);

    check(ret, "Failed to set kernel arg!");
}

pub fn enqueue_nd_range_kernel(cqueue: & CommandQueue, kernel: & Kernel, work_dim: cl_uint,
                               _global_work_offset: int, global_work_size: int,
                               local_work_size: int) unsafe{
    let ret = clEnqueueNDRangeKernel(cqueue.cqueue, kernel.kernel, work_dim,
                                     // ptr::addr_of(&global_work_offset) as *libc::size_t,
                                     ptr::null(),
                                     ptr::addr_of(&global_work_size) as *libc::size_t,
                                     ptr::addr_of(&local_work_size) as *libc::size_t,
                                     0, ptr::null(), ptr::null());
    check(ret, "Failed to enqueue nd range kernel!");
}

pub impl Buffer: KernelArg{
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
        (sys::size_of::<cl_mem>() as libc::size_t,
         ptr::addr_of(&self.buffer) as *libc::c_void)
    }
}

struct Event {
    event: cl_event,

    drop {
        clReleaseEvent(self.event);
    }
}

impl Event {
    fn wait() {
        let status = clWaitForEvents(1, ptr::addr_of(&self.event));
        check(status, "Error waiting for event");
    }
}

/**
This packages an OpenCL context, a device, and a command queue to
simplify handling all these structures.
*/
struct ComputeContext {
    ctx: Context,
    device: Device,
    q: CommandQueue
}

impl ComputeContext {
    fn create_program_from_source(src: &str) -> Program {
        do str::as_c_str(src) |src| {
            let mut status = CL_SUCCESS as cl_int;
            let program = clCreateProgramWithSource(
                self.ctx.ctx,
                1,
                ptr::addr_of(&src),
                ptr::null(),
                ptr::addr_of(&status));
            check(status, "Could not create program");

            Program { prg: program }
        }
    }

    fn enqueue_async_kernel<I: KernelIndex>(k: &Kernel, global: I, local: I)
        -> Event
    {
        let e: cl_event = ptr::null();
        let status = clEnqueueNDRangeKernel(
            self.q.cqueue,
            k.kernel,
            KernelIndex::num_dimensions::<I>(),
            ptr::null(),
            global.get_ptr(),
            local.get_ptr(),
            0,
            ptr::null(),
            ptr::addr_of(&e));
        check(status, "Error enqueuing kernel.");

        Event { event: e }
    }
}

pub fn create_compute_context() -> @ComputeContext {
    // Enumerate all platforms until we find a device that works.

    for get_platforms().each |p| {
        let devices = p.get_devices();
        if devices.len() > 0 {
            let device = devices[0];
            let ctx = create_context(device);
            let q = create_commandqueue(&ctx, device);
            return @ComputeContext {
                ctx: move ctx,
                device: move device,
                q: move q
            }
        }
    }
    fail ~"Could not find an acceptable device."
}

trait KernelIndex {
    static pure fn num_dimensions() -> cl_uint;
    pure fn get_ptr(&self) -> *libc::size_t;
}

impl int: KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 1 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl (int, int): KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 2 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

#[cfg(test)]
mod test {
    macro_rules! expect (
        ($test: expr, $expected: expr) => ({
            let test = $test;
            let expected = $expected;
            if test != expected {
                fail fmt!("Test failure in %s: expected %?, got %?",
                          stringify!($test),
                          expected, test)
            }
        })
    )

    use vector::*;

    #[test]
    fn program_build() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);
    }

    #[test]
    fn simple_kernel() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn add_k() {
        let src = "__kernel void test(__global int *i, int k) { \
                       *i += k; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);
        k.set_arg(1, &42);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 43);
    }

    #[test]
    fn simple_kernel_index() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        ctx.enqueue_async_kernel(&k, 1, 1).wait();

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn kernel_2d() {
        let src = "__kernel void test(__global long int *N) { \
                       int i = get_global_id(0); \
                       int j = get_global_id(1); \
                       int s = get_global_size(0); \
                       N[i * s + j] = i * j;
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);

        match prog.build(ctx.device) {
            Ok(()) => (),
            Err(build_log) => {
                io::println("Error building program:\n");
                io::println(build_log);
                fail
            }
        }

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1, 2, 3, 4, 5, 6, 7, 8, 9]);

        k.set_arg(0, &v);

        ctx.enqueue_async_kernel(&k, (3, 3), (1, 1)).wait();

        let v = v.to_vec();

        expect!(v, ~[0, 0, 0, 0, 1, 2, 0, 2, 4]);
    }
}<|endoftext|>


<commit_before>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;

struct Platform {
    id: cl_platform_id
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create opencl context!"
    }

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: &Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create command queue!"
    }

    CommandQueue { cqueue: cqueue }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: &Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create buffer!"
    }

    Buffer { buffer: buffer, size: size }
}

pub fn enqueue_write_buffer(cqueue: CommandQueue, buf: Buffer, host_vector: ~[int]) {
    let ret = clEnqueueWriteBuffer(cqueue.cqueue, buf.buffer, CL_TRUE, 0,
                                   buf.size as libc::size_t,
                                   vec::raw::to_ptr(host_vector) as *libc::c_void,
                                   0, ptr::null(), ptr::null());
    if ret != CL_SUCCESS {
        fail ~"Failed to enqueue write buffer!"
    }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: Context, device: Device, binary_path: & Path){
    let mut errcode = 0;
    let binary = io::read_whole_file_str(binary_path).get();
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.leb() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::cuchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    if errcode != CL_SUCCESS {
        fail ~"Failed to create open cl program with binary!"
    }

    Program { prg: program }
}

<commit_msg>minor fix to function definition to make it unsafe.<commit_after>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;

struct Platform {
    id: cl_platform_id
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create opencl context!"
    }

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: &Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create command queue!"
    }

    CommandQueue { cqueue: cqueue }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: &Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create buffer!"
    }

    Buffer { buffer: buffer, size: size }
}

pub fn enqueue_write_buffer(cqueue: CommandQueue, buf: Buffer, host_vector: ~[int]) unsafe {
    let ret = clEnqueueWriteBuffer(cqueue.cqueue, buf.buffer, CL_TRUE, 0,
                                   buf.size as libc::size_t,
                                   vec::raw::to_ptr(host_vector) as *libc::c_void,
                                   0, ptr::null(), ptr::null());
    if ret != CL_SUCCESS {
        fail ~"Failed to enqueue write buffer!"
    }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: Context, device: Device, binary_path: & Path){
    let mut errcode = 0;
    let binary = io::read_whole_file_str(binary_path).get();
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.leb() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::cuchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    if errcode != CL_SUCCESS {
        fail ~"Failed to create open cl program with binary!"
    }

    Program { prg: program }
}

<|endoftext|>


<commit_before>use FromCursor;
use std::io::Cursor;

/// `BodyReqAuthSuccess` is a frame that represents a successfull authentication response.
#[derive(Debug)]
pub struct BodyReqAuthSuccess {}

impl FromCursor for BodyReqAuthSuccess {
    fn from_cursor(mut _cursor: &mut Cursor<Vec<u8>>) -> BodyReqAuthSuccess {
        BodyReqAuthSuccess {}
    }
}
<commit_msg>Create tests for frame_auth_success<commit_after>use FromCursor;
use std::io::Cursor;

/// `BodyReqAuthSuccess` is a frame that represents a successfull authentication response.
#[derive(Debug, PartialEq)]
pub struct BodyReqAuthSuccess {}

impl FromCursor for BodyReqAuthSuccess {
    fn from_cursor(mut _cursor: &mut Cursor<Vec<u8>>) -> BodyReqAuthSuccess {
        BodyReqAuthSuccess {}
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use FromCursor;
    use std::io::Cursor;

    #[test]
    fn test_name() {
        let rnd_bytes: Vec<u8> = vec![4, 5, 3, 8, 4, 6, 5, 0, 3, 7, 2];
        let mut cursor = Cursor::new(rnd_bytes);
        let body = BodyReqAuthSuccess::from_cursor(&mut cursor);
        assert_eq!(body, BodyReqAuthSuccess{});
    }
}
<|endoftext|>