<commit_before>extern crate gstreamer as gst;

extern crate gtk;
use gtk::prelude::*;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use media::PlaybackContext;

use super::MainController;

const ALIGN_LEFT: f32 = 0f32;
const ALIGN_CENTER: f32 = 0.5f32;
const ALIGN_RIGHT: f32 = 1f32;

const STREAM_ID_COL: u32 = 0;
const STREAM_ID_DISPLAY_COL: u32 = 1;
const LANGUAGE_COL: u32 = 2;
const CODEC_COL: u32 = 3;
const COMMENT_COL: u32 = 4;

const VIDEO_WIDTH_COL: u32 = 5;
const VIDEO_HEIGHT_COL: u32 = 6;

const AUDIO_RATE_COL: u32 = 5;
const AUDIO_CHANNELS_COL: u32 = 6;

const TEXT_FORMAT_COL: u32 = 5;

pub struct StreamsController {
    streams_button: gtk::ToggleButton,
    display_streams_stack: gtk::Stack,

    video_treeview: gtk::TreeView,
    video_store: gtk::ListStore,
    audio_treeview: gtk::TreeView,
    audio_store: gtk::ListStore,
    text_treeview: gtk::TreeView,
    text_store: gtk::ListStore,

    main_ctrl: Option<Weak<RefCell<MainController>>>,
}

impl StreamsController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this_rc = Rc::new(RefCell::new(StreamsController {
            streams_button: builder.get_object("streams-toggle").unwrap(),
            display_streams_stack: builder.get_object("display_streams-stack").unwrap(),

            video_treeview: builder.get_object("video_streams-treeview").unwrap(),
            video_store: builder.get_object("video_streams-liststore").unwrap(),
            audio_treeview: builder.get_object("audio_streams-treeview").unwrap(),
            audio_store: builder.get_object("audio_streams-liststore").unwrap(),
            text_treeview: builder.get_object("text_streams-treeview").unwrap(),
            text_store: builder.get_object("text_streams-liststore").unwrap(),

            main_ctrl: None,
        }));

        {
            let mut this = this_rc.borrow_mut();
            this.cleanup();
            this.init_treeviews();
        }

        this_rc
    }

    pub fn register_callbacks(
        this_rc: &Rc<RefCell<Self>>,
        main_ctrl: &Rc<RefCell<MainController>>,
    ) {
        let mut this = this_rc.borrow_mut();

        this.main_ctrl = Some(Rc::downgrade(main_ctrl));

        // streams button
        let this_clone = Rc::clone(this_rc);
        this.streams_button.connect_clicked(move |button| {
            let page_name = if button.get_active() {
                "streams".into()
            } else {
                "display".into()
            };
            this_clone.borrow_mut().display_streams_stack.set_visible_child_name(page_name);
        });
    }

    pub fn new_media(&mut self, context: &PlaybackContext) {
        self.streams_button.set_sensitive(true);

        let info = context
            .info
            .lock()
            .expect("StreamsController::new_media: failed to lock media info");

        // Video streams
        for (stream_id, &(ref caps, ref tags)) in &info.video_streams {
            let iter = self.add_stream(&self.video_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(width) = caps_structure.get::<i32>("width") {
                self.video_store.set_value(&iter, VIDEO_WIDTH_COL, &gtk::Value::from(&width));
            }
            if let Some(height) = caps_structure.get::<i32>("height") {
                self.video_store.set_value(&iter, VIDEO_HEIGHT_COL, &gtk::Value::from(&height));
            }
        }

        match self.video_store.get_iter_first() {
            Some(ref iter) => self.video_treeview.get_selection().select_iter(iter),
            None => (),
        }

        // Audio streams
        for (stream_id, &(ref caps, ref tags)) in &info.audio_streams {
            let iter = self.add_stream(&self.audio_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(rate) = caps_structure.get::<i32>("rate") {
                self.audio_store.set_value(&iter, AUDIO_RATE_COL, &gtk::Value::from(&rate));
            }
            if let Some(channels) = caps_structure.get::<i32>("channels") {
                self.audio_store.set_value(&iter, AUDIO_CHANNELS_COL, &gtk::Value::from(&channels));
            }
        }

        match self.audio_store.get_iter_first() {
            Some(ref iter) => self.audio_treeview.get_selection().select_iter(iter),
            None => (),
        }

        // Text streams
        for (stream_id, &(ref caps, ref tags)) in &info.text_streams {
            let iter = self.add_stream(&self.text_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(format) = caps_structure.get::<&str>("format") {
                self.text_store.set_value(&iter, TEXT_FORMAT_COL, &gtk::Value::from(&format));
            }
        }

        match self.text_store.get_iter_first() {
            Some(ref iter) => self.text_treeview.get_selection().select_iter(iter),
            None => (),
        }
    }

    fn add_stream(
        &self,
        store: &gtk::ListStore,
        stream_id: &str,
        caps: &gst::Caps,
        tags: &Option<gst::TagList>,
    ) -> gtk::TreeIter {
        let id_parts: Vec<&str> = stream_id.split('/').collect();
        let stream_id_display = if id_parts.len() == 2 {
            id_parts[1]
        } else {
            "unknown"
        };

        let caps_structure = caps.get_structure(0).unwrap();

        let iter = store.insert_with_values(
            None,
            &[STREAM_ID_COL, STREAM_ID_DISPLAY_COL, CODEC_COL],
            &[&stream_id, &stream_id_display, &(caps_structure.get_name())],
        );

        let codec = match tags.as_ref() {
            Some(tags) => {
                let language = match tags.get_index::<gst::tags::LanguageName>(0).as_ref() {
                    Some(language) => language.get().unwrap(),
                    None => match tags.get_index::<gst::tags::LanguageCode>(0).as_ref() {
                        Some(language) => language.get().unwrap(),
                        None => "-",
                    }
                };
                store.set_value(&iter, LANGUAGE_COL, &gtk::Value::from(language));

                if let Some(comment) = tags.get_index::<gst::tags::Comment>(0).as_ref() {
                    store.set_value(&iter, COMMENT_COL, &gtk::Value::from(comment.get().unwrap()));
                }

                match tags.get_index::<gst::tags::VideoCodec>(0).as_ref() {
                    Some(codec) => codec.get(),
                    None => match tags.get_index::<gst::tags::AudioCodec>(0).as_ref() {
                        Some(codec) => codec.get(),
                        None => match tags.get_index::<gst::tags::SubtitleCodec>(0).as_ref() {
                            Some(codec) => codec.get(),
                            None => match tags.get_index::<gst::tags::Codec>(0).as_ref() {
                                Some(codec) => codec.get(),
                                None => None,
                            }
                        }
                    }
                }
            }
            None => None,
        };

        let codec = match codec {
            Some(codec) => codec,
            None => {
                // codec in the form "streamtype/x-codec"
                let codec = caps_structure.get_name();
                let id_parts: Vec<&str> = codec.split('/').collect();
                if id_parts.len() == 2 {
                    if id_parts[1].starts_with("x-") {
                        &id_parts[1][2..]
                    } else {
                        id_parts[1]
                    }
                } else {
                    codec
                }
            },
        };
        store.set_value(&iter, CODEC_COL, &gtk::Value::from(&codec));

        iter
    }

    pub fn cleanup(&mut self) {
        self.streams_button.set_sensitive(false);
        self.video_store.clear();
        self.audio_store.clear();
        self.text_store.clear();
    }

    fn init_treeviews(&self) {
        self.video_treeview.set_model(Some(&self.video_store));
        self.add_column(
            &self.video_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.video_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.video_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.video_treeview, "Width", ALIGN_RIGHT, VIDEO_WIDTH_COL, None);
        self.add_column(&self.video_treeview, "Height", ALIGN_RIGHT, VIDEO_HEIGHT_COL, None);
        self.add_column(&self.video_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.audio_treeview.set_model(Some(&self.audio_store));
        self.add_column(
            &self.audio_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.audio_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.audio_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.audio_treeview, "Rate", ALIGN_RIGHT, AUDIO_RATE_COL, None);
        self.add_column(&self.audio_treeview, "Channels", ALIGN_RIGHT, AUDIO_CHANNELS_COL, None);
        self.add_column(&self.audio_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.text_treeview.set_model(Some(&self.text_store));
        self.add_column(
            &self.text_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.text_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.text_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.text_treeview, "Format", ALIGN_LEFT, TEXT_FORMAT_COL, None);
        self.add_column(&self.text_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);
    }

    fn add_column(
        &self,
        treeview: &gtk::TreeView,
        title: &str,
        alignment: f32,
        col_id: u32,
        width: Option<i32>,
    ) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);

        let renderer = gtk::CellRendererText::new();
        renderer.set_alignment(alignment, 0.5f32);
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id as i32);

        if let Some(width) = width {
            renderer.set_fixed_size(width, -1);
        }

        treeview.append_column(&col);
    }
}
<commit_msg>Streams: minor optimizations (#46)<commit_after>extern crate gstreamer as gst;

extern crate gtk;
use gtk::prelude::*;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use media::PlaybackContext;

use super::MainController;

const ALIGN_LEFT: f32 = 0f32;
const ALIGN_CENTER: f32 = 0.5f32;
const ALIGN_RIGHT: f32 = 1f32;

const STREAM_ID_COL: u32 = 0;
const STREAM_ID_DISPLAY_COL: u32 = 1;
const LANGUAGE_COL: u32 = 2;
const CODEC_COL: u32 = 3;
const COMMENT_COL: u32 = 4;

const VIDEO_WIDTH_COL: u32 = 5;
const VIDEO_HEIGHT_COL: u32 = 6;

const AUDIO_RATE_COL: u32 = 5;
const AUDIO_CHANNELS_COL: u32 = 6;

const TEXT_FORMAT_COL: u32 = 5;

pub struct StreamsController {
    streams_button: gtk::ToggleButton,
    display_streams_stack: gtk::Stack,

    video_treeview: gtk::TreeView,
    video_store: gtk::ListStore,
    audio_treeview: gtk::TreeView,
    audio_store: gtk::ListStore,
    text_treeview: gtk::TreeView,
    text_store: gtk::ListStore,

    main_ctrl: Option<Weak<RefCell<MainController>>>,
}

impl StreamsController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this_rc = Rc::new(RefCell::new(StreamsController {
            streams_button: builder.get_object("streams-toggle").unwrap(),
            display_streams_stack: builder.get_object("display_streams-stack").unwrap(),

            video_treeview: builder.get_object("video_streams-treeview").unwrap(),
            video_store: builder.get_object("video_streams-liststore").unwrap(),
            audio_treeview: builder.get_object("audio_streams-treeview").unwrap(),
            audio_store: builder.get_object("audio_streams-liststore").unwrap(),
            text_treeview: builder.get_object("text_streams-treeview").unwrap(),
            text_store: builder.get_object("text_streams-liststore").unwrap(),

            main_ctrl: None,
        }));

        {
            let mut this = this_rc.borrow_mut();
            this.cleanup();
            this.init_treeviews();
        }

        this_rc
    }

    pub fn register_callbacks(
        this_rc: &Rc<RefCell<Self>>,
        main_ctrl: &Rc<RefCell<MainController>>,
    ) {
        let mut this = this_rc.borrow_mut();

        this.main_ctrl = Some(Rc::downgrade(main_ctrl));

        // streams button
        let this_clone = Rc::clone(this_rc);
        this.streams_button.connect_clicked(move |button| {
            let page_name = if button.get_active() {
                "streams".into()
            } else {
                "display".into()
            };
            this_clone.borrow_mut().display_streams_stack.set_visible_child_name(page_name);
        });
    }

    pub fn new_media(&mut self, context: &PlaybackContext) {
        self.streams_button.set_sensitive(true);

        let info = context
            .info
            .lock()
            .expect("StreamsController::new_media: failed to lock media info");

        // Video streams
        for (stream_id, &(ref caps, ref tags)) in &info.video_streams {
            let iter = self.add_stream(&self.video_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(width) = caps_structure.get::<i32>("width") {
                self.video_store.set_value(&iter, VIDEO_WIDTH_COL, &gtk::Value::from(&width));
            }
            if let Some(height) = caps_structure.get::<i32>("height") {
                self.video_store.set_value(&iter, VIDEO_HEIGHT_COL, &gtk::Value::from(&height));
            }
        }

        match self.video_store.get_iter_first() {
            Some(ref iter) => self.video_treeview.get_selection().select_iter(iter),
            None => (),
        }

        // Audio streams
        for (stream_id, &(ref caps, ref tags)) in &info.audio_streams {
            let iter = self.add_stream(&self.audio_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(rate) = caps_structure.get::<i32>("rate") {
                self.audio_store.set_value(&iter, AUDIO_RATE_COL, &gtk::Value::from(&rate));
            }
            if let Some(channels) = caps_structure.get::<i32>("channels") {
                self.audio_store.set_value(&iter, AUDIO_CHANNELS_COL, &gtk::Value::from(&channels));
            }
        }

        match self.audio_store.get_iter_first() {
            Some(ref iter) => self.audio_treeview.get_selection().select_iter(iter),
            None => (),
        }

        // Text streams
        for (stream_id, &(ref caps, ref tags)) in &info.text_streams {
            let iter = self.add_stream(&self.text_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(format) = caps_structure.get::<&str>("format") {
                self.text_store.set_value(&iter, TEXT_FORMAT_COL, &gtk::Value::from(&format));
            }
        }

        match self.text_store.get_iter_first() {
            Some(ref iter) => self.text_treeview.get_selection().select_iter(iter),
            None => (),
        }
    }

    fn add_stream(
        &self,
        store: &gtk::ListStore,
        stream_id: &str,
        caps: &gst::Caps,
        tags: &Option<gst::TagList>,
    ) -> gtk::TreeIter {
        let id_parts: Vec<&str> = stream_id.split('/').collect();
        let stream_id_display = if id_parts.len() == 2 {
            id_parts[1]
        } else {
            "unknown"
        };

        let iter = store.insert_with_values(
            None,
            &[STREAM_ID_COL, STREAM_ID_DISPLAY_COL],
            &[&stream_id, &stream_id_display],
        );

        let codec = match tags.as_ref() {
            Some(tags) => {
                let language = match tags.get_index::<gst::tags::LanguageName>(0).as_ref() {
                    Some(language) => language.get().unwrap(),
                    None => match tags.get_index::<gst::tags::LanguageCode>(0).as_ref() {
                        Some(language) => language.get().unwrap(),
                        None => "-",
                    }
                };
                store.set_value(&iter, LANGUAGE_COL, &gtk::Value::from(language));

                if let Some(comment) = tags.get_index::<gst::tags::Comment>(0).as_ref() {
                    store.set_value(&iter, COMMENT_COL, &gtk::Value::from(comment.get().unwrap()));
                }

                match tags.get_index::<gst::tags::VideoCodec>(0).as_ref() {
                    Some(codec) => codec.get(),
                    None => match tags.get_index::<gst::tags::AudioCodec>(0).as_ref() {
                        Some(codec) => codec.get(),
                        None => match tags.get_index::<gst::tags::SubtitleCodec>(0).as_ref() {
                            Some(codec) => codec.get(),
                            None => match tags.get_index::<gst::tags::Codec>(0).as_ref() {
                                Some(codec) => codec.get(),
                                None => None,
                            }
                        }
                    }
                }
            }
            None => None,
        };

        let codec = match codec {
            Some(codec) => codec,
            None => {
                // codec in caps in the form "streamtype/x-codec"
                let codec = caps.get_structure(0).unwrap().get_name();
                let id_parts: Vec<&str> = codec.split('/').collect();
                if id_parts.len() == 2 {
                    if id_parts[1].starts_with("x-") {
                        &id_parts[1][2..]
                    } else {
                        id_parts[1]
                    }
                } else {
                    codec
                }
            },
        };
        store.set_value(&iter, CODEC_COL, &gtk::Value::from(&codec));

        iter
    }

    pub fn cleanup(&mut self) {
        self.streams_button.set_sensitive(false);
        self.video_store.clear();
        self.audio_store.clear();
        self.text_store.clear();
    }

    fn init_treeviews(&self) {
        self.video_treeview.set_model(Some(&self.video_store));
        self.add_column(
            &self.video_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.video_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.video_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.video_treeview, "Width", ALIGN_RIGHT, VIDEO_WIDTH_COL, None);
        self.add_column(&self.video_treeview, "Height", ALIGN_RIGHT, VIDEO_HEIGHT_COL, None);
        self.add_column(&self.video_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.audio_treeview.set_model(Some(&self.audio_store));
        self.add_column(
            &self.audio_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.audio_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.audio_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.audio_treeview, "Rate", ALIGN_RIGHT, AUDIO_RATE_COL, None);
        self.add_column(&self.audio_treeview, "Channels", ALIGN_RIGHT, AUDIO_CHANNELS_COL, None);
        self.add_column(&self.audio_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.text_treeview.set_model(Some(&self.text_store));
        self.add_column(
            &self.text_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.text_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.text_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.text_treeview, "Format", ALIGN_LEFT, TEXT_FORMAT_COL, None);
        self.add_column(&self.text_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);
    }

    fn add_column(
        &self,
        treeview: &gtk::TreeView,
        title: &str,
        alignment: f32,
        col_id: u32,
        width: Option<i32>,
    ) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);

        let renderer = gtk::CellRendererText::new();
        renderer.set_alignment(alignment, 0.5f32);
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id as i32);

        if let Some(width) = width {
            renderer.set_fixed_size(width, -1);
        }

        treeview.append_column(&col);
    }
}
<|endoftext|>


<commit_before>use glib::GString;
use gtk;
use gtk::prelude::*;

use std::{cell::RefCell, rc::Rc};

use super::{
    streams_controller::{EXPORT_FLAG_COL, STREAM_ID_COL},
    MainController, UIDispatcher,
};

macro_rules! on_stream_selected(
    ($main_ctrl_rc:expr, $store:ident, $tree_path:expr, $selected:ident) => {
        let mut main_ctrl = $main_ctrl_rc.borrow_mut();
        let streams_ctrl = &mut main_ctrl.streams_ctrl;

        if let Some(iter) = streams_ctrl.$store.get_iter($tree_path) {
            let stream = streams_ctrl.get_stream_at(&streams_ctrl.$store, &iter);
            let stream_to_select = match streams_ctrl.$selected {
                Some(ref stream_id) => {
                    if stream_id != &stream {
                        // Stream has changed
                        Some(stream)
                    } else {
                        None
                    }
                }
                None => Some(stream),
            };
            if let Some(new_stream) = stream_to_select {
                streams_ctrl.$selected = Some(new_stream);
                let streams = streams_ctrl.get_selected_streams();

                // Asynchronoulsy notify the main controller
                let main_ctrl_weak = Rc::downgrade(&$main_ctrl_rc);
                gtk::idle_add(move || {
                    let main_ctrl_rc = main_ctrl_weak.upgrade().unwrap();
                    main_ctrl_rc.borrow_mut().select_streams(&streams);
                    glib::Continue(false)
                });
            }
        }
    };
);

macro_rules! on_export_toggled(
    ($main_ctrl_rc:expr, $store:ident, $tree_path:expr, $streams_getter:ident) => {
        let mut main_ctrl = $main_ctrl_rc.borrow_mut();
        let store = main_ctrl.streams_ctrl.$store.clone();

        store.get_iter(&$tree_path).map(|iter| {
            let stream_id = store
                .get_value(&iter, STREAM_ID_COL as i32)
                .get::<GString>()
                .unwrap();
            let value = !store
                .get_value(&iter, EXPORT_FLAG_COL as i32)
                .get::<bool>()
                .unwrap();
            store.set_value(&iter, EXPORT_FLAG_COL, &gtk::Value::from(&value));

            if let Some(pipeline) = main_ctrl.pipeline.as_mut() {
                pipeline
                    .info
                    .write()
                    .unwrap()
                    .streams
                    .$streams_getter(stream_id)
                    .as_mut()
                    .unwrap()
                    .must_export = value;
            }
        });
    };
);

pub struct StreamsDispatcher;
impl UIDispatcher for StreamsDispatcher {
    fn setup(_gtk_app: &gtk::Application, main_ctrl_rc: &Rc<RefCell<MainController>>) {
        let main_ctrl = main_ctrl_rc.borrow();
        let streams_ctrl = &main_ctrl.streams_ctrl;

        // Video stream selection
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        streams_ctrl
            .video_treeview
            .connect_row_activated(move |_, tree_path, _| {
                on_stream_selected!(main_ctrl_rc_cb, video_store, tree_path, video_selected);
            });

        // Audio stream selection
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        streams_ctrl
            .audio_treeview
            .connect_row_activated(move |_, tree_path, _| {
                on_stream_selected!(main_ctrl_rc_cb, audio_store, tree_path, audio_selected);
            });

        // Text stream selection
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        streams_ctrl
            .text_treeview
            .connect_row_activated(move |_, tree_path, _| {
                on_stream_selected!(main_ctrl_rc_cb, text_store, tree_path, text_selected);
            });

        // Video stream export toggled
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        if let Some(col) = streams_ctrl
            .video_treeview
            .get_column(EXPORT_FLAG_COL as i32)
        {
            let mut renderers = col.get_cells();
            debug_assert!(renderers.len() == 1);
            renderers
                .pop()
                .unwrap()
                .downcast::<gtk::CellRendererToggle>()
                .expect("Unexpected `CellRenderer` type for `export` column")
                .connect_toggled(move |_, tree_path| {
                    on_export_toggled!(main_ctrl_rc_cb, video_store, tree_path, get_video_mut);
                });
        }

        // Audio stream export toggled
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        if let Some(col) = streams_ctrl
            .audio_treeview
            .get_column(EXPORT_FLAG_COL as i32)
        {
            let mut renderers = col.get_cells();
            debug_assert!(renderers.len() == 1);
            renderers
                .pop()
                .unwrap()
                .downcast::<gtk::CellRendererToggle>()
                .expect("Unexpected `CellRenderer` type for `export` column")
                .connect_toggled(move |_, tree_path| {
                    on_export_toggled!(main_ctrl_rc_cb, audio_store, tree_path, get_audio_mut);
                });
        }

        // Text stream export toggled
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        if let Some(col) = streams_ctrl
            .text_treeview
            .get_column(EXPORT_FLAG_COL as i32)
        {
            let mut renderers = col.get_cells();
            debug_assert!(renderers.len() == 1);
            renderers
                .pop()
                .unwrap()
                .downcast::<gtk::CellRendererToggle>()
                .expect("Unexpected `CellRenderer` type for `export` column")
                .connect_toggled(move |_, tree_path| {
                    on_export_toggled!(main_ctrl_rc_cb, text_store, tree_path, get_text_mut);
                });
        }
    }
}
<commit_msg>StreamsDispatcher: factorize<commit_after>use glib::GString;
use gtk;
use gtk::prelude::*;

use std::{cell::RefCell, rc::Rc};

use super::{
    streams_controller::{EXPORT_FLAG_COL, STREAM_ID_COL},
    MainController, UIDispatcher,
};

macro_rules! on_stream_selected(
    ($main_ctrl_rc:expr, $store:ident, $tree_path:expr, $selected:ident) => {
        let mut main_ctrl = $main_ctrl_rc.borrow_mut();
        let streams_ctrl = &mut main_ctrl.streams_ctrl;

        if let Some(iter) = streams_ctrl.$store.get_iter($tree_path) {
            let stream = streams_ctrl.get_stream_at(&streams_ctrl.$store, &iter);
            let stream_to_select = match streams_ctrl.$selected {
                Some(ref stream_id) => {
                    if stream_id != &stream {
                        // Stream has changed
                        Some(stream)
                    } else {
                        None
                    }
                }
                None => Some(stream),
            };
            if let Some(new_stream) = stream_to_select {
                streams_ctrl.$selected = Some(new_stream);
                let streams = streams_ctrl.get_selected_streams();

                // Asynchronoulsy notify the main controller
                let main_ctrl_weak = Rc::downgrade(&$main_ctrl_rc);
                gtk::idle_add(move || {
                    let main_ctrl_rc = main_ctrl_weak.upgrade().unwrap();
                    main_ctrl_rc.borrow_mut().select_streams(&streams);
                    glib::Continue(false)
                });
            }
        }
    };
);

macro_rules! register_on_export_toggled(
    ($streams_ctrl:expr, $main_ctrl_rc:expr, $treeview:ident, $store:ident, $streams_getter:ident) => {
        if let Some(col) = $streams_ctrl
            .$treeview
            .get_column(EXPORT_FLAG_COL as i32)
        {
            let mut renderers = col.get_cells();
            debug_assert!(renderers.len() == 1);
            let main_ctrl_rc_cb = Rc::clone($main_ctrl_rc);
            renderers
                .pop()
                .unwrap()
                .downcast::<gtk::CellRendererToggle>()
                .expect("Unexpected `CellRenderer` type for `export` column")
                .connect_toggled(move |_, tree_path| {
                    let mut main_ctrl = main_ctrl_rc_cb.borrow_mut();
                    let store = main_ctrl.streams_ctrl.$store.clone();

                    store.get_iter(&tree_path).map(|iter| {
                        let stream_id = store
                            .get_value(&iter, STREAM_ID_COL as i32)
                            .get::<GString>()
                            .unwrap();
                        let value = !store
                            .get_value(&iter, EXPORT_FLAG_COL as i32)
                            .get::<bool>()
                            .unwrap();
                        store.set_value(&iter, EXPORT_FLAG_COL, &gtk::Value::from(&value));

                        if let Some(pipeline) = main_ctrl.pipeline.as_mut() {
                            pipeline
                                .info
                                .write()
                                .unwrap()
                                .streams
                                .$streams_getter(stream_id)
                                .as_mut()
                                .unwrap()
                                .must_export = value;
                        }
                    });
                });
        }
    };
);

pub struct StreamsDispatcher;
impl UIDispatcher for StreamsDispatcher {
    fn setup(_gtk_app: &gtk::Application, main_ctrl_rc: &Rc<RefCell<MainController>>) {
        let main_ctrl = main_ctrl_rc.borrow();
        let streams_ctrl = &main_ctrl.streams_ctrl;

        // Video stream selection
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        streams_ctrl
            .video_treeview
            .connect_row_activated(move |_, tree_path, _| {
                on_stream_selected!(main_ctrl_rc_cb, video_store, tree_path, video_selected);
            });

        // Audio stream selection
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        streams_ctrl
            .audio_treeview
            .connect_row_activated(move |_, tree_path, _| {
                on_stream_selected!(main_ctrl_rc_cb, audio_store, tree_path, audio_selected);
            });

        // Text stream selection
        let main_ctrl_rc_cb = Rc::clone(main_ctrl_rc);
        streams_ctrl
            .text_treeview
            .connect_row_activated(move |_, tree_path, _| {
                on_stream_selected!(main_ctrl_rc_cb, text_store, tree_path, text_selected);
            });

        // Video stream export toggled
        register_on_export_toggled!(
            streams_ctrl,
            main_ctrl_rc,
            video_treeview,
            video_store,
            get_video_mut
        );

        // Audio stream export toggled
        register_on_export_toggled!(
            streams_ctrl,
            main_ctrl_rc,
            audio_treeview,
            audio_store,
            get_audio_mut
        );

        // Text stream export toggled
        register_on_export_toggled!(
            streams_ctrl,
            main_ctrl_rc,
            text_treeview,
            text_store,
            get_text_mut
        );
    }
}
<|endoftext|>


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

use std::path::Path;
use std::mem::size_of;
use std::mem::transmute;
use std::mem::zeroed;
use std::os::errno;
use std::num::Float;

use geom::point::TypedPoint2D;

use libc::c_int;
use libc::c_long;
use libc::time_t;
use libc::open;
use libc::read;
use libc::O_RDONLY;

use compositing::windowing::WindowEvent;
use compositing::windowing::MouseWindowEvent;

extern {
    // XXX: no variadic form in std libs?
    fn ioctl(fd: c_int, req: c_int, ...) -> c_int;
}

#[repr(C)]
struct linux_input_event {
    sec: time_t,
    msec: c_long,
    evt_type: u16,
    code: u16,
    value: i32,
}

#[repr(C)]
struct linux_input_absinfo {
    value: i32,
    minimum: i32,
    maximum: i32,
    fuzz: i32,
    flat: i32,
    resolution: i32,
}

const IOC_NONE: c_int = 0;
const IOC_WRITE: c_int = 1;
const IOC_READ: c_int = 2;

fn ioc(dir: c_int, ioctype: c_int, nr: c_int, size: c_int) -> c_int {
    dir << 30 | size << 16 | ioctype << 8 | nr
}

fn ev_ioc_g_abs(abs: u16) -> c_int {
    ioc(IOC_READ, 'E' as c_int, (0x40 + abs) as i32, size_of::<linux_input_absinfo>() as i32)
}

const EV_SYN: u16 = 0;
const EV_ABS: u16 = 3;

const EV_REPORT: u16 = 0;

const ABS_MT_SLOT: u16 = 0x2F;
const ABS_MT_TOUCH_MAJOR: u16 = 0x30;
const ABS_MT_TOUCH_MINOR: u16 = 0x31;
const ABS_MT_WIDTH_MAJOR: u16 = 0x32;
const ABS_MT_WIDTH_MINOR: u16 = 0x33;
const ABS_MT_ORIENTATION: u16 = 0x34;
const ABS_MT_POSITION_X: u16 = 0x35;
const ABS_MT_POSITION_Y: u16 = 0x36;
const ABS_MT_TRACKING_ID: u16 = 0x39;

struct input_slot {
    tracking_id: i32,
    x: i32,
    y: i32,
}

fn dist(x1: i32, x2: i32, y1: i32, y2: i32) -> f32 {
    let deltaX = (x2 - x1) as f32;
    let deltaY = (y2 - y1) as f32;
    (deltaX * deltaX + deltaY * deltaY).sqrt()
}

fn read_input_device(device_path: &Path,
                     sender: &Sender<WindowEvent>) {
    // XXX we really want to use std::io:File but it currently doesn't expose
    // the raw FD which is necessary for ioctl.
    let device = unsafe { device_path.as_str().unwrap().with_c_str(|s| open(s, O_RDONLY, 0)) };
    if device == -1 {
        panic!("Couldn't open {}", device_path.as_str().unwrap());
    }

    let mut xInfo: linux_input_absinfo = unsafe { zeroed() };
    let mut yInfo: linux_input_absinfo = unsafe { zeroed() };
    unsafe {
        let ret = ioctl(device, ev_ioc_g_abs(ABS_MT_POSITION_X), &xInfo);
        if ret < 0 {
            println!("Couldn't get ABS_MT_POSITION_X info {} {}", ret, errno());
        }
    }
    unsafe {
        let ret = ioctl(device, ev_ioc_g_abs(ABS_MT_POSITION_Y), &yInfo);
        if ret < 0 {
            println!("Couldn't get ABS_MT_POSITION_Y info {} {}", ret, errno());
        }
    }

    let touchWidth = xInfo.maximum - xInfo.minimum;
    let touchHeight = yInfo.maximum - yInfo.minimum;

    println!("xMin: {}, yMin: {}, touchWidth: {}, touchHeight: {}", xInfo.minimum, yInfo.minimum, touchWidth, touchHeight);

    // XXX: Why isn't size_of treated as constant?
    // let buf: [u8, ..(16 * size_of::<linux_input_event>())];
    let mut buf: [u8, ..(16 * 16)] = unsafe { zeroed() };
    let mut slots: [input_slot, ..10] = unsafe { zeroed() };
    for slot in slots.iter_mut() {
        slot.tracking_id = -1;
    }

    let mut last_x = 0;
    let mut last_y = 0;
    let mut first_x = 0;
    let mut first_y = 0;

    let mut last_dist: f32 = 0f32;
    let mut touch_count: i32 = 0;
    let mut current_slot: uint = 0;
    // XXX: Need to use the real dimensions of the screen
    let screen_dist = dist(0, 480, 854, 0);
    loop {
        let read = unsafe { read(device, transmute(buf.as_mut_ptr()), buf.len() as u32) };

        if read < 0 {
            println!("Couldn't read device! error {}", read);
            return;
        }
        assert!(read % (size_of::<linux_input_event>() as i32) == 0,
                "Unexpected input device read length!");

        let count = read / (size_of::<linux_input_event>() as i32);
        let events: *mut linux_input_event = unsafe { transmute(buf.as_mut_ptr()) };
        let mut tracking_updated = false;
        for idx in range(0, count as int) {
            let event: &linux_input_event = unsafe { transmute(events.offset(idx)) };
            match (event.evt_type, event.code) {
                (EV_SYN, EV_REPORT) => {
                    let slotA = &slots[0];
                    if tracking_updated {
                        tracking_updated = false;
                        if slotA.tracking_id == -1 {
                            println!("Touch up");
                            let delta_x = slotA.x - first_x;
                            let delta_y = slotA.y - first_y;
                            let dist = delta_x * delta_x + delta_y * delta_y;
                            if dist < 16 {
                                let click_pt = TypedPoint2D(slotA.x as f32, slotA.y as f32);
                                println!("Dispatching click!");
                                sender.send(WindowEvent::MouseWindowEventClass(MouseWindowEvent::MouseDown(0, click_pt)));
                                sender.send(WindowEvent::MouseWindowEventClass(MouseWindowEvent::MouseUp(0, click_pt)));
                                sender.send(WindowEvent::MouseWindowEventClass(MouseWindowEvent::Click(0, click_pt)));
                            }
                        } else {
                            println!("Touch down");
                            last_x = slotA.x;
                            last_y = slotA.y;
                            first_x = slotA.x;
                            first_y = slotA.y;
                            if touch_count >= 2 {
                                let slotB = &slots[1];
                                last_dist = dist(slotA.x, slotB.x, slotA.y, slotB.y);
                            }
                        }
                    } else {
                        println!("Touch move x: {}, y: {}", slotA.x, slotA.y);
                        sender.send(WindowEvent::Scroll(TypedPoint2D((slotA.x - last_x) as f32, (slotA.y - last_y) as f32), TypedPoint2D(slotA.x, slotA.y)));
                        last_x = slotA.x;
                        last_y = slotA.y;
                        if touch_count >= 2 {
                            let slotB = &slots[1];
                            let cur_dist = dist(slotA.x, slotB.x, slotA.y, slotB.y);
                            println!("Zooming {} {} {} {}", cur_dist, last_dist, screen_dist, ((screen_dist + (cur_dist - last_dist))/screen_dist));
                            sender.send(WindowEvent::Zoom((screen_dist + (cur_dist - last_dist))/screen_dist));
                            last_dist = cur_dist;
                        }
                    }
                },
                (EV_SYN, _) => println!("Unknown SYN code {}", event.code),
                (EV_ABS, ABS_MT_SLOT) => {
                            if (event.value as uint) < slots.len() {
                                current_slot = event.value as uint;
                            } else {
                                println!("Invalid slot! {}", event.value);
                            }
                        },
                (EV_ABS, ABS_MT_TOUCH_MAJOR) => (),
                (EV_ABS, ABS_MT_TOUCH_MINOR) => (),
                (EV_ABS, ABS_MT_WIDTH_MAJOR) => (),
                (EV_ABS, ABS_MT_WIDTH_MINOR) => (),
                (EV_ABS, ABS_MT_ORIENTATION) => (),
                (EV_ABS, ABS_MT_POSITION_X) => {
                    slots[current_slot].x = event.value - xInfo.minimum;
                },
                (EV_ABS, ABS_MT_POSITION_Y) => {
                    slots[current_slot].y = event.value - yInfo.minimum;
                },
                (EV_ABS, ABS_MT_TRACKING_ID) => {
                    let current_id = slots[current_slot].tracking_id;
                    if current_id != event.value &&
                       (current_id == -1 || event.value == -1) {
                        tracking_updated = true;
                        if event.value == -1 {
                            touch_count -= 1;
                        } else {
                            touch_count += 1;
                        }
                    }
                    slots[current_slot].tracking_id = event.value;
                },
                (EV_ABS, _) => println!("Unknown ABS code {}", event.code),
                (_, _) => println!("Unknown event type {}", event.evt_type),
            }
        }
    }
}

pub fn run_input_loop(event_sender: &Sender<WindowEvent>) {
    let sender = event_sender.clone();
    spawn(proc () {
        // XXX need to scan all devices and read every one.
        let touchinputdev = Path::new("/dev/input/event0");
        read_input_device(&touchinputdev, &sender);
    });
}
<commit_msg>auto merge of #4599 : michaelwu/servo/use-file-apis-in-gonk, r=larsbergstrom<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::path::Path;
use std::mem::size_of;
use std::mem::transmute;
use std::mem::zeroed;
use std::os::errno;
use std::os::unix::AsRawFd;
use std::num::Float;
use std::io::File;

use geom::point::TypedPoint2D;

use libc::c_int;
use libc::c_long;
use libc::time_t;

use compositing::windowing::WindowEvent;
use compositing::windowing::MouseWindowEvent;

extern {
    // XXX: no variadic form in std libs?
    fn ioctl(fd: c_int, req: c_int, ...) -> c_int;
}

#[repr(C)]
struct linux_input_event {
    sec: time_t,
    msec: c_long,
    evt_type: u16,
    code: u16,
    value: i32,
}

#[repr(C)]
struct linux_input_absinfo {
    value: i32,
    minimum: i32,
    maximum: i32,
    fuzz: i32,
    flat: i32,
    resolution: i32,
}

const IOC_NONE: c_int = 0;
const IOC_WRITE: c_int = 1;
const IOC_READ: c_int = 2;

fn ioc(dir: c_int, ioctype: c_int, nr: c_int, size: c_int) -> c_int {
    dir << 30 | size << 16 | ioctype << 8 | nr
}

fn ev_ioc_g_abs(abs: u16) -> c_int {
    ioc(IOC_READ, 'E' as c_int, (0x40 + abs) as i32, size_of::<linux_input_absinfo>() as i32)
}

const EV_SYN: u16 = 0;
const EV_ABS: u16 = 3;

const EV_REPORT: u16 = 0;

const ABS_MT_SLOT: u16 = 0x2F;
const ABS_MT_TOUCH_MAJOR: u16 = 0x30;
const ABS_MT_TOUCH_MINOR: u16 = 0x31;
const ABS_MT_WIDTH_MAJOR: u16 = 0x32;
const ABS_MT_WIDTH_MINOR: u16 = 0x33;
const ABS_MT_ORIENTATION: u16 = 0x34;
const ABS_MT_POSITION_X: u16 = 0x35;
const ABS_MT_POSITION_Y: u16 = 0x36;
const ABS_MT_TRACKING_ID: u16 = 0x39;

struct input_slot {
    tracking_id: i32,
    x: i32,
    y: i32,
}

fn dist(x1: i32, x2: i32, y1: i32, y2: i32) -> f32 {
    let deltaX = (x2 - x1) as f32;
    let deltaY = (y2 - y1) as f32;
    (deltaX * deltaX + deltaY * deltaY).sqrt()
}

fn read_input_device(device_path: &Path,
                     sender: &Sender<WindowEvent>) {
    let mut device = match File::open(device_path) {
        Ok(dev) => dev,
        Err(e) => {
            println!("Couldn't open device! {}", e);
            return;
        },
    };
    let fd = device.as_raw_fd();

    let mut xInfo: linux_input_absinfo = unsafe { zeroed() };
    let mut yInfo: linux_input_absinfo = unsafe { zeroed() };
    unsafe {
        let ret = ioctl(fd, ev_ioc_g_abs(ABS_MT_POSITION_X), &xInfo);
        if ret < 0 {
            println!("Couldn't get ABS_MT_POSITION_X info {} {}", ret, errno());
        }
    }
    unsafe {
        let ret = ioctl(fd, ev_ioc_g_abs(ABS_MT_POSITION_Y), &yInfo);
        if ret < 0 {
            println!("Couldn't get ABS_MT_POSITION_Y info {} {}", ret, errno());
        }
    }

    let touchWidth = xInfo.maximum - xInfo.minimum;
    let touchHeight = yInfo.maximum - yInfo.minimum;

    println!("xMin: {}, yMin: {}, touchWidth: {}, touchHeight: {}", xInfo.minimum, yInfo.minimum, touchWidth, touchHeight);

    // XXX: Why isn't size_of treated as constant?
    // let buf: [u8, ..(16 * size_of::<linux_input_event>())];
    let mut buf: [u8, ..(16 * 16)] = unsafe { zeroed() };
    let mut slots: [input_slot, ..10] = unsafe { zeroed() };
    for slot in slots.iter_mut() {
        slot.tracking_id = -1;
    }

    let mut last_x = 0;
    let mut last_y = 0;
    let mut first_x = 0;
    let mut first_y = 0;

    let mut last_dist: f32 = 0f32;
    let mut touch_count: i32 = 0;
    let mut current_slot: uint = 0;
    // XXX: Need to use the real dimensions of the screen
    let screen_dist = dist(0, 480, 854, 0);
    loop {
        let read = match device.read(buf.as_mut_slice()) {
            Ok(count) => {
                assert!(count % size_of::<linux_input_event>() == 0,
                        "Unexpected input device read length!");
                count
            },
            Err(e) => {
                println!("Couldn't read device! {}", e);
                return;
            }
        };

        let count = read / size_of::<linux_input_event>();
        let events: *mut linux_input_event = unsafe { transmute(buf.as_mut_ptr()) };
        let mut tracking_updated = false;
        for idx in range(0, count as int) {
            let event: &linux_input_event = unsafe { transmute(events.offset(idx)) };
            match (event.evt_type, event.code) {
                (EV_SYN, EV_REPORT) => {
                    let slotA = &slots[0];
                    if tracking_updated {
                        tracking_updated = false;
                        if slotA.tracking_id == -1 {
                            println!("Touch up");
                            let delta_x = slotA.x - first_x;
                            let delta_y = slotA.y - first_y;
                            let dist = delta_x * delta_x + delta_y * delta_y;
                            if dist < 16 {
                                let click_pt = TypedPoint2D(slotA.x as f32, slotA.y as f32);
                                println!("Dispatching click!");
                                sender.send(WindowEvent::MouseWindowEventClass(MouseWindowEvent::MouseDown(0, click_pt)));
                                sender.send(WindowEvent::MouseWindowEventClass(MouseWindowEvent::MouseUp(0, click_pt)));
                                sender.send(WindowEvent::MouseWindowEventClass(MouseWindowEvent::Click(0, click_pt)));
                            }
                        } else {
                            println!("Touch down");
                            last_x = slotA.x;
                            last_y = slotA.y;
                            first_x = slotA.x;
                            first_y = slotA.y;
                            if touch_count >= 2 {
                                let slotB = &slots[1];
                                last_dist = dist(slotA.x, slotB.x, slotA.y, slotB.y);
                            }
                        }
                    } else {
                        println!("Touch move x: {}, y: {}", slotA.x, slotA.y);
                        sender.send(WindowEvent::Scroll(TypedPoint2D((slotA.x - last_x) as f32, (slotA.y - last_y) as f32), TypedPoint2D(slotA.x, slotA.y)));
                        last_x = slotA.x;
                        last_y = slotA.y;
                        if touch_count >= 2 {
                            let slotB = &slots[1];
                            let cur_dist = dist(slotA.x, slotB.x, slotA.y, slotB.y);
                            println!("Zooming {} {} {} {}", cur_dist, last_dist, screen_dist, ((screen_dist + (cur_dist - last_dist))/screen_dist));
                            sender.send(WindowEvent::Zoom((screen_dist + (cur_dist - last_dist))/screen_dist));
                            last_dist = cur_dist;
                        }
                    }
                },
                (EV_SYN, _) => println!("Unknown SYN code {}", event.code),
                (EV_ABS, ABS_MT_SLOT) => {
                            if (event.value as uint) < slots.len() {
                                current_slot = event.value as uint;
                            } else {
                                println!("Invalid slot! {}", event.value);
                            }
                        },
                (EV_ABS, ABS_MT_TOUCH_MAJOR) => (),
                (EV_ABS, ABS_MT_TOUCH_MINOR) => (),
                (EV_ABS, ABS_MT_WIDTH_MAJOR) => (),
                (EV_ABS, ABS_MT_WIDTH_MINOR) => (),
                (EV_ABS, ABS_MT_ORIENTATION) => (),
                (EV_ABS, ABS_MT_POSITION_X) => {
                    slots[current_slot].x = event.value - xInfo.minimum;
                },
                (EV_ABS, ABS_MT_POSITION_Y) => {
                    slots[current_slot].y = event.value - yInfo.minimum;
                },
                (EV_ABS, ABS_MT_TRACKING_ID) => {
                    let current_id = slots[current_slot].tracking_id;
                    if current_id != event.value &&
                       (current_id == -1 || event.value == -1) {
                        tracking_updated = true;
                        if event.value == -1 {
                            touch_count -= 1;
                        } else {
                            touch_count += 1;
                        }
                    }
                    slots[current_slot].tracking_id = event.value;
                },
                (EV_ABS, _) => println!("Unknown ABS code {}", event.code),
                (_, _) => println!("Unknown event type {}", event.evt_type),
            }
        }
    }
}

pub fn run_input_loop(event_sender: &Sender<WindowEvent>) {
    let sender = event_sender.clone();
    spawn(proc () {
        // XXX need to scan all devices and read every one.
        let touchinputdev = Path::new("/dev/input/event0");
        read_input_device(&touchinputdev, &sender);
    });
}
<|endoftext|>


<commit_before>//! Methods for converting shapes into triangles.

use std::f64::consts::{
    PI,
    PI_2,
    FRAC_PI_2,
};
use {
    Image,
};
use interpolation::{lerp};
use internal::{
    Color,
    Line,
    Matrix2d,
    SourceRectangle,
    Polygon,
    Polygons,
    Radius,
    Rectangle,
    Scalar,
    Vec2d,
};
use vecmath::{
    multiply,
    orient,
    translate,
};

/// Transformed x coordinate as f32.
#[inline(always)]
fn tx(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

/// Transformed y coordinate as f32.
#[inline(always)]
fn ty(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: Matrix2d,
    polygons: Polygons,
    tween_factor: Scalar,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: Matrix2d,
    rect: Rectangle,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round border line.
#[inline(always)]
pub fn with_round_border_line_tri_list_xy_f32_rgba_f32(
    resolution_cap: uint,
    m: Matrix2d,
    line: Line,
    round_border_radius: Radius,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let radius = round_border_radius;
    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
    let (dx, dy) = (x2 - x1, y2 - y1);
    let w = (dx * dx + dy * dy).sqrt();
    let m = multiply(m, translate(x1, y1));
    let m = multiply(m, orient(dx, dy));
    let n = resolution_cap * 2;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        // Detect the half circle from index.
        // There is one half circle at each end of the line.
        // Together they form a full circle if the length of the line is zero.
        match j {
            j if j >= resolution_cap => {
                // Compute the angle to match start and end point of half circle.
                // This requires an angle offset since the other end of line is the first half circle.
                let angle = (j - resolution_cap) as f64 / (resolution_cap - 1) as f64 * PI + PI;
                // Rotate 90 degrees since the line is horizontal.
                let angle = angle + FRAC_PI_2;
                Some([w + angle.cos() * radius, angle.sin() * radius])
            },
            j => {
                // Compute the angle to match start and end point of half circle.
                let angle = j as f64 / (resolution_cap - 1) as f64 * PI;
                // Rotate 90 degrees since the line is horizontal.
                let angle = angle + FRAC_PI_2;
                Some([angle.cos() * radius, angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: Matrix2d,
    rect: Rectangle,
    round_radius: Radius,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = round_radius;
    let n = resolution_corner * 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        // Detect quarter circle from index.
        // There is one quarter circle at each corner.
        // Together they form a full circle if each side of rectangle is 2 times the radius.
        match j {
            j if j >= resolution_corner * 3 => {
                // Compute the angle to match start and end point of quarter circle.
                // This requires an angle offset since this is the last quarter.
                let angle = (j - resolution_corner * 3) as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2
                    + 3.0 * FRAC_PI_2;
                // Set center of the circle to the last corner.
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                // Compute the angle to match start and end point of quarter circle.
                // This requires an angle offset since this is the second last quarter.
                let angle = (j - resolution_corner * 2) as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2
                    + PI;
                // Set center of the circle to the second last corner.
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                // Compute the angle to match start and end point of quarter circle.
                // This requires an angle offset since this is the second quarter.
                let angle = (j - resolution_corner) as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2
                    + FRAC_PI_2;
                // Set center of the circle to the second corner.
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                // Compute the angle to match start and end point of quarter circle.
                let angle = j as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2;
                // Set center of the circle to the first corner.
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: Matrix2d,
    polygon: || -> Option<Vec2d>,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: Matrix2d,
    polygon: Polygon,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
#[inline(always)]
pub fn rect_tri_list_xy_f32(
    m: Matrix2d,
    rect: Rectangle
) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
#[inline(always)]
pub fn rect_tri_list_rgba_f32(
    color: Color
) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
#[inline(always)]
pub fn rect_tri_list_uv_f32<I: Image>(image: &I, source_rect: SourceRectangle) -> [f32, ..12] {
    let (w, h) = image.get_size();
    let x1 = source_rect[0] as f32 / w as f32;
    let y1 = source_rect[1] as f32 / h as f32;
    let x2 = (source_rect[0] + source_rect[2]) as f32 / w as f32;
    let y2 = (source_rect[1] + source_rect[3]) as f32 / h as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}
<commit_msg>Changed buffer sizes in `stream_polygon_tri_list_xy_f32_rgba_f32`<commit_after>//! Methods for converting shapes into triangles.

use std::f64::consts::{
    PI,
    PI_2,
    FRAC_PI_2,
};
use {
    Image,
};
use interpolation::{lerp};
use internal::{
    Color,
    Line,
    Matrix2d,
    SourceRectangle,
    Polygon,
    Polygons,
    Radius,
    Rectangle,
    Scalar,
    Vec2d,
};
use vecmath::{
    multiply,
    orient,
    translate,
};

/// Transformed x coordinate as f32.
#[inline(always)]
fn tx(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

/// Transformed y coordinate as f32.
#[inline(always)]
fn ty(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: Matrix2d,
    polygons: Polygons,
    tween_factor: Scalar,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: Matrix2d,
    rect: Rectangle,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round border line.
#[inline(always)]
pub fn with_round_border_line_tri_list_xy_f32_rgba_f32(
    resolution_cap: uint,
    m: Matrix2d,
    line: Line,
    round_border_radius: Radius,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let radius = round_border_radius;
    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
    let (dx, dy) = (x2 - x1, y2 - y1);
    let w = (dx * dx + dy * dy).sqrt();
    let m = multiply(m, translate(x1, y1));
    let m = multiply(m, orient(dx, dy));
    let n = resolution_cap * 2;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        // Detect the half circle from index.
        // There is one half circle at each end of the line.
        // Together they form a full circle if the length of the line is zero.
        match j {
            j if j >= resolution_cap => {
                // Compute the angle to match start and end point of half circle.
                // This requires an angle offset since the other end of line is the first half circle.
                let angle = (j - resolution_cap) as f64 / (resolution_cap - 1) as f64 * PI + PI;
                // Rotate 90 degrees since the line is horizontal.
                let angle = angle + FRAC_PI_2;
                Some([w + angle.cos() * radius, angle.sin() * radius])
            },
            j => {
                // Compute the angle to match start and end point of half circle.
                let angle = j as f64 / (resolution_cap - 1) as f64 * PI;
                // Rotate 90 degrees since the line is horizontal.
                let angle = angle + FRAC_PI_2;
                Some([angle.cos() * radius, angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: Matrix2d,
    rect: Rectangle,
    round_radius: Radius,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = round_radius;
    let n = resolution_corner * 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        // Detect quarter circle from index.
        // There is one quarter circle at each corner.
        // Together they form a full circle if each side of rectangle is 2 times the radius.
        match j {
            j if j >= resolution_corner * 3 => {
                // Compute the angle to match start and end point of quarter circle.
                // This requires an angle offset since this is the last quarter.
                let angle = (j - resolution_corner * 3) as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2
                    + 3.0 * FRAC_PI_2;
                // Set center of the circle to the last corner.
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                // Compute the angle to match start and end point of quarter circle.
                // This requires an angle offset since this is the second last quarter.
                let angle = (j - resolution_corner * 2) as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2
                    + PI;
                // Set center of the circle to the second last corner.
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                // Compute the angle to match start and end point of quarter circle.
                // This requires an angle offset since this is the second quarter.
                let angle = (j - resolution_corner) as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2
                    + FRAC_PI_2;
                // Set center of the circle to the second corner.
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                // Compute the angle to match start and end point of quarter circle.
                let angle = j as f64 / (resolution_corner - 1) as f64 * FRAC_PI_2;
                // Set center of the circle to the first corner.
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: Matrix2d,
    polygon: || -> Option<Vec2d>,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..720] = [0.0, ..720];
    let mut colors: [f32, ..1440] = [0.0, ..1440];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: Matrix2d,
    polygon: Polygon,
    color: Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
#[inline(always)]
pub fn rect_tri_list_xy_f32(
    m: Matrix2d,
    rect: Rectangle
) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
#[inline(always)]
pub fn rect_tri_list_rgba_f32(
    color: Color
) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
#[inline(always)]
pub fn rect_tri_list_uv_f32<I: Image>(image: &I, source_rect: SourceRectangle) -> [f32, ..12] {
    let (w, h) = image.get_size();
    let x1 = source_rect[0] as f32 / w as f32;
    let y1 = source_rect[1] as f32 / h as f32;
    let x2 = (source_rect[0] + source_rect[2]) as f32 / w as f32;
    let y2 = (source_rect[1] + source_rect[3]) as f32 / h as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use error::*;
use bytecode;

pub struct VM {
    ip: usize,
    sp: usize,
    globals: Vec<u64>,
    stack: Vec<u64>,
}

impl VM {
    pub fn new() -> VM {
        VM {
            ip: 0,
            sp: 0,
            globals: Vec::new(),
            stack: Vec::new(),
        }
    }

    pub fn exec<P: AsRef<Path>>(&mut self, path: P) -> VMResult<()> {
        let mut image_file = File::open(path).chain_err(|| "unable to open game image")?;
        let mut image_string = String::new();
        image_file.read_to_string(&mut image_string).chain_err(|| "unable to read image")?;

        let byte_iter: Vec<u8> = image_string.bytes().collect();
        let mut byte = byte_iter[self.ip];
        while self.ip < byte_iter.len() {
            match byte {
                bytecode::HALT => break,
                bytecode::ADD => self.add().chain_err(|| "unable to execute 'add' instruction")?,
                bytecode::SUB => self.sub().chain_err(|| "unable to execute 'sub' instruction")?,
                bytecode::MUL => self.mul().chain_err(|| "unable to execute 'mul' instruction")?,
                bytecode::DIV => self.div().chain_err(|| "unable to execute 'div' instruction")?,
                bytecode::PRINT => {
                    self.print().chain_err(|| "unable to execute 'print' instruction")?
                }
                bytecode::PUSH => {
                    // Build a u32 from single bytes
                    let mut res: u32 = 0;
                    for _ in 0..4 {
                        res <<= 8;
                        self.ip += 1;
                        res |= byte_iter[self.ip] as u32;
                    }

                    self.push(res).chain_err(|| "unable to push value to the stack")?;
                }
                bytecode::JMP => {
                    // Build a u32 from single bytes
                    let mut res: u32 = 0;
                    for _ in 0..4 {
                        res <<= 8;
                        self.ip += 1;
                        res |= byte_iter[self.ip] as u32;
                    }

                    self.jmp(res).chain_err(|| "unable to jump")?;
                    byte = byte_iter[self.ip];
                    continue;
                }
                _ => bail!("unexpected opcode: {:08x}", byte),
            }

            self.ip += 1;

            byte = byte_iter[self.ip];
        }

        Ok(())
    }

    fn push(&mut self, value: u32) -> VMResult<()> {
        if !self.stack.is_empty() {
            self.sp += 1;
        }

        self.stack.push(value as u64);

        Ok(())
    }

    fn pop(&mut self) -> VMResult<u64> {
        if self.stack.is_empty() {
            bail!("unable to pop value off an empty Stack");
        }

        let res = self.stack.remove(self.sp);
        if !self.stack.is_empty() {
            self.sp -= 1;
        }

        Ok(res)
    }

    fn add(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a + b) as u32).chain_err(|| "unable to push value to the stack")?;
        Ok(())
    }

    fn sub(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a - b) as u32).chain_err(|| "unable to push to stack")?;
        Ok(())
    }

    fn mul(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a * b) as u32).chain_err(|| "unable to push to stack")?;
        Ok(())
    }

    fn div(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a / b) as u32).chain_err(|| "unable to push to stack")?;
        Ok(())
    }

    fn jmp(&mut self, addr: u32) -> VMResult<()> {
        self.ip = addr as usize;
        Ok(())
    }

    fn print(&mut self) -> VMResult<()> {
        println!("{:?}", self.stack[self.sp]);
        Ok(())
    }
}
<commit_msg>Implemented conditional jumps<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use error::*;
use bytecode;

pub struct VM {
    ip: usize,
    sp: usize,
    globals: Vec<u64>,
    stack: Vec<u64>,
}

impl VM {
    pub fn new() -> VM {
        VM {
            ip: 0,
            sp: 0,
            globals: Vec::new(),
            stack: Vec::new(),
        }
    }

    pub fn exec<P: AsRef<Path>>(&mut self, path: P) -> VMResult<()> {
        let mut image_file = File::open(path).chain_err(|| "unable to open game image")?;
        let mut image_string = String::new();
        image_file.read_to_string(&mut image_string).chain_err(|| "unable to read image")?;

        let byte_iter: Vec<u8> = image_string.bytes().collect();
        while self.ip < byte_iter.len() {
            let byte = self.current_byte(&byte_iter)
                .chain_err(|| "unable to read current byte")?;

            match byte {
                bytecode::HALT => break,
                bytecode::ADD => self.add().chain_err(|| "unable to execute 'add' instruction")?,
                bytecode::SUB => self.sub().chain_err(|| "unable to execute 'sub' instruction")?,
                bytecode::MUL => self.mul().chain_err(|| "unable to execute 'mul' instruction")?,
                bytecode::DIV => self.div().chain_err(|| "unable to execute 'div' instruction")?,
                bytecode::PRINT => {
                    self.print().chain_err(|| "unable to execute 'print' instruction")?
                }
                bytecode::PUSH => {
                    let res = self.read_word(&byte_iter).chain_err(|| "unable to read word")?;

                    self.push(res).chain_err(|| "unable to push value to the stack")?;
                }
                bytecode::JMP => {
                    let res = self.read_word(&byte_iter).chain_err(|| "unable to read word")?;

                    self.jmp(res).chain_err(|| "unable to jump")?;
                    continue;
                }
                bytecode::JZ => {
                    let res = self.read_word(&byte_iter).chain_err(|| "unable to read word")?;

                    let top_of_stack = self.peek()
                        .chain_err(|| "unable to get current top of stack")?;

                    if top_of_stack == 0 {
                        self.jmp(res).chain_err(|| "unable to jump")?;
                        continue;
                    }
                }
                bytecode::JNZ => {
                    let res = self.read_word(&byte_iter).chain_err(|| "unable to read word")?;

                    let top_of_stack = self.peek()
                        .chain_err(|| "unable to get current top of stack")?;

                    if top_of_stack != 0 {
                        self.jmp(res).chain_err(|| "unable to jump")?;
                        continue;
                    }
                }
                _ => bail!("unexpected opcode: {:08x}", byte),
            }

            self.advance_ip().chain_err(|| "unable to advance instruction pointer")?;
        }

        Ok(())
    }

    fn push(&mut self, value: u32) -> VMResult<()> {
        if !self.stack.is_empty() {
            self.sp += 1;
        }

        self.stack.push(value as u64);

        Ok(())
    }

    fn pop(&mut self) -> VMResult<u64> {
        if self.stack.is_empty() {
            bail!("unable to pop value off an empty Stack");
        }

        let res = self.stack.remove(self.sp);
        if !self.stack.is_empty() {
            self.sp -= 1;
        }

        Ok(res)
    }

    fn add(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a + b) as u32).chain_err(|| "unable to push value to the stack")?;
        Ok(())
    }

    fn sub(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a - b) as u32).chain_err(|| "unable to push to stack")?;
        Ok(())
    }

    fn mul(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a * b) as u32).chain_err(|| "unable to push to stack")?;
        Ok(())
    }

    fn div(&mut self) -> VMResult<()> {
        let b = self.pop().chain_err(|| "unable to pop value off the stack")?;
        let a = self.pop().chain_err(|| "unable to pop value off the stack")?;
        self.push((a / b) as u32).chain_err(|| "unable to push to stack")?;
        Ok(())
    }

    fn jmp(&mut self, addr: u32) -> VMResult<()> {
        self.ip = addr as usize;
        Ok(())
    }

    fn read_word(&mut self, bytes: &Vec<u8>) -> VMResult<u32> {
        // Build a u32 from single bytes
        let mut res: u32 = 0;
        for _ in 0..4 {
            res <<= 8;
            self.advance_ip().chain_err(|| "unable to advance instruction pointer")?;
            let current_byte = self.current_byte(&bytes)
                .chain_err(|| "unable to read current byte")?;
            res |= current_byte as u32;
        }

        Ok(res)
    }

    fn advance_ip(&mut self) -> VMResult<()> {
        self.ip += 1;
        Ok(())
    }

    fn current_byte(&mut self, bytes: &Vec<u8>) -> VMResult<u8> {
        if self.ip < bytes.len() {
            Ok(bytes[self.ip])
        } else {
            bail!("instruction pointer out of bounds");
        }
    }

    fn peek(&mut self) -> VMResult<u64> {
        if self.sp < self.stack.len() {
            Ok(self.stack[self.sp])
        } else {
            bail!("stack pointer out of bounds");
        }
    }

    fn print(&mut self) -> VMResult<()> {
        let top = self.peek().chain_err(|| "unable to get current top of stack")?;
        println!("{:?}", top);
        Ok(())
    }
}
<|endoftext|>