<commit_before>use {NixResult, NixError, from_ffi};
use super::{ffi, consts, SockOpt};
use errno::Errno;
use libc::{c_int, uint8_t, c_void, socklen_t};
use std::mem;
use std::os::unix::Fd;

// Helper to generate the sockopt accessors
// TODO: Figure out how to ommit gets when not supported by opt
macro_rules! sockopt_impl {
    ($name:ident, $flag:path, bool) => {
        sockopt_impl!($name, $flag, bool, GetBool, bool, SetBool);
    };

    ($name:ident, $flag:path, u8) => {
        sockopt_impl!($name, $flag, u8, GetU8, u8, SetU8);
    };

    ($name:ident, $flag:path, $ty:ty) => {
        sockopt_impl!($name, $flag, $ty, GetStruct<$ty>, &'a $ty, SetStruct<$ty>);
    };

    ($name:ident, $flag:path, $get_ty:ty, $getter:ty, $set_ty:ty, $setter:ty) => {
        #[derive(Copy, Debug)]
        pub struct $name;

        impl<'a> SockOpt for $name {
            type Get = $get_ty;
            type Set = $set_ty;

            fn get(&self, fd: Fd, level: c_int) -> NixResult<$get_ty> {
                unsafe {
                    let mut getter: $getter = Get::blank();

                    let res = ffi::getsockopt(
                        fd, level, $flag,
                        getter.ffi_ptr(),
                        getter.ffi_len());

                    if res < 0 {
                        return Err(NixError::Sys(Errno::last()));
                    }

                    Ok(getter.unwrap())
                }
            }

            fn set(&self, fd: Fd, level: c_int, val: $set_ty) -> NixResult<()> {
                unsafe {
                    let setter: $setter = Set::new(val);

                    let res = ffi::setsockopt(
                        fd, level, $flag,
                        setter.ffi_ptr(),
                        setter.ffi_len());

                    from_ffi(res)
                }
            }
        }
    };
}

/*
 *
 * ===== Define sockopts =====
 *
 */

sockopt_impl!(ReuseAddr, consts::SO_REUSEADDR, bool);
sockopt_impl!(ReusePort, consts::SO_REUSEPORT, bool);
sockopt_impl!(TcpNoDelay, consts::TCP_NODELAY, bool);
sockopt_impl!(Linger, consts::SO_LINGER, super::linger);
sockopt_impl!(IpAddMembership, consts::IP_ADD_MEMBERSHIP, super::ip_mreq);
sockopt_impl!(IpDropMembership, consts::IP_DROP_MEMBERSHIP, super::ip_mreq);
sockopt_impl!(IpMulticastTtl, consts::IP_MULTICAST_TTL, u8);

/*
 *
 * ===== Accessor helpers =====
 *
 */

trait Get<T> {
    unsafe fn blank() -> Self;
    unsafe fn ffi_ptr(&mut self) -> *mut c_void;
    unsafe fn ffi_len(&mut self) -> *mut socklen_t;
    unsafe fn unwrap(self) -> T;
}

trait Set<T> {
    fn new(val: T) -> Self;
    unsafe fn ffi_ptr(&self) -> *const c_void;
    unsafe fn ffi_len(&self) -> socklen_t;
}

struct GetStruct<T> {
    len: socklen_t,
    val: T,
}

impl<T> Get<T> for GetStruct<T> {
    unsafe fn blank() -> Self {
        mem::zeroed()
    }

    unsafe fn ffi_ptr(&mut self) -> *mut c_void {
        mem::transmute(&mut self.val)
    }

    unsafe fn ffi_len(&mut self) -> *mut socklen_t {
        mem::transmute(&mut self.len)
    }

    unsafe fn unwrap(self) -> T {
        assert!(self.len as usize == mem::size_of::<T>(), "invalid getsockopt implementation");
        self.val
    }
}

struct SetStruct<'a, T: 'static> {
    ptr: &'a T,
}

impl<'a, T> Set<&'a T> for SetStruct<'a, T> {
    fn new(ptr: &'a T) -> SetStruct<'a, T> {
        SetStruct { ptr: ptr }
    }

    unsafe fn ffi_ptr(&self) -> *const c_void {
        mem::transmute(self.ptr)
    }

    unsafe fn ffi_len(&self) -> socklen_t {
        mem::size_of::<T>() as socklen_t
    }
}

struct GetBool {
    len: socklen_t,
    val: c_int,
}

impl Get<bool> for GetBool {
    unsafe fn blank() -> Self {
        mem::zeroed()
    }

    unsafe fn ffi_ptr(&mut self) -> *mut c_void {
        mem::transmute(&mut self.val)
    }

    unsafe fn ffi_len(&mut self) -> *mut socklen_t {
        mem::transmute(&mut self.len)
    }

    unsafe fn unwrap(self) -> bool {
        assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation");
        self.val != 0
    }
}

struct SetBool {
    val: c_int,
}

impl Set<bool> for SetBool {
    fn new(val: bool) -> SetBool {
        SetBool { val: if val { 1 } else { 0 } }
    }

    unsafe fn ffi_ptr(&self) -> *const c_void {
        mem::transmute(&self.val)
    }

    unsafe fn ffi_len(&self) -> socklen_t {
        mem::size_of::<c_int>() as socklen_t
    }
}

struct GetU8 {
    len: socklen_t,
    val: uint8_t,
}

impl Get<u8> for GetU8 {
    unsafe fn blank() -> Self {
        mem::zeroed()
    }

    unsafe fn ffi_ptr(&mut self) -> *mut c_void {
        mem::transmute(&mut self.val)
    }

    unsafe fn ffi_len(&mut self) -> *mut socklen_t {
        mem::transmute(&mut self.len)
    }

    unsafe fn unwrap(self) -> u8 {
        assert!(self.len as usize == mem::size_of::<uint8_t>(), "invalid getsockopt implementation");
        self.val as u8
    }
}

struct SetU8 {
    val: uint8_t,
}

impl Set<u8> for SetU8 {
    fn new(val: u8) -> SetU8 {
        SetU8 { val: val as uint8_t }
    }

    unsafe fn ffi_ptr(&self) -> *const c_void {
        mem::transmute(&self.val)
    }

    unsafe fn ffi_len(&self) -> socklen_t {
        mem::size_of::<c_int>() as socklen_t
    }
}
<commit_msg>Expose socket timeouts<commit_after>use {NixResult, NixError, from_ffi};
use super::{ffi, consts, SockOpt};
use errno::Errno;
use sys::time::TimeVal;
use libc::{c_int, uint8_t, c_void, socklen_t};
use std::mem;
use std::os::unix::Fd;

// Helper to generate the sockopt accessors
// TODO: Figure out how to ommit gets when not supported by opt
macro_rules! sockopt_impl {
    ($name:ident, $flag:path, bool) => {
        sockopt_impl!($name, $flag, bool, GetBool, bool, SetBool);
    };

    ($name:ident, $flag:path, u8) => {
        sockopt_impl!($name, $flag, u8, GetU8, u8, SetU8);
    };

    ($name:ident, $flag:path, $ty:ty) => {
        sockopt_impl!($name, $flag, $ty, GetStruct<$ty>, &'a $ty, SetStruct<$ty>);
    };

    ($name:ident, $flag:path, $get_ty:ty, $getter:ty, $set_ty:ty, $setter:ty) => {
        #[derive(Copy, Debug)]
        pub struct $name;

        impl<'a> SockOpt for $name {
            type Get = $get_ty;
            type Set = $set_ty;

            fn get(&self, fd: Fd, level: c_int) -> NixResult<$get_ty> {
                unsafe {
                    let mut getter: $getter = Get::blank();

                    let res = ffi::getsockopt(
                        fd, level, $flag,
                        getter.ffi_ptr(),
                        getter.ffi_len());

                    if res < 0 {
                        return Err(NixError::Sys(Errno::last()));
                    }

                    Ok(getter.unwrap())
                }
            }

            fn set(&self, fd: Fd, level: c_int, val: $set_ty) -> NixResult<()> {
                unsafe {
                    let setter: $setter = Set::new(val);

                    let res = ffi::setsockopt(
                        fd, level, $flag,
                        setter.ffi_ptr(),
                        setter.ffi_len());

                    from_ffi(res)
                }
            }
        }
    };
}

/*
 *
 * ===== Define sockopts =====
 *
 */

sockopt_impl!(ReuseAddr, consts::SO_REUSEADDR, bool);
sockopt_impl!(ReusePort, consts::SO_REUSEPORT, bool);
sockopt_impl!(TcpNoDelay, consts::TCP_NODELAY, bool);
sockopt_impl!(Linger, consts::SO_LINGER, super::linger);
sockopt_impl!(IpAddMembership, consts::IP_ADD_MEMBERSHIP, super::ip_mreq);
sockopt_impl!(IpDropMembership, consts::IP_DROP_MEMBERSHIP, super::ip_mreq);
sockopt_impl!(IpMulticastTtl, consts::IP_MULTICAST_TTL, u8);
sockopt_impl!(ReceiveTimeout, consts::SO_RCVTIMEO, TimeVal);
sockopt_impl!(SendTimeout, consts::SO_SNDTIMEO, TimeVal);

/*
 *
 * ===== Accessor helpers =====
 *
 */

trait Get<T> {
    unsafe fn blank() -> Self;
    unsafe fn ffi_ptr(&mut self) -> *mut c_void;
    unsafe fn ffi_len(&mut self) -> *mut socklen_t;
    unsafe fn unwrap(self) -> T;
}

trait Set<T> {
    fn new(val: T) -> Self;
    unsafe fn ffi_ptr(&self) -> *const c_void;
    unsafe fn ffi_len(&self) -> socklen_t;
}

struct GetStruct<T> {
    len: socklen_t,
    val: T,
}

impl<T> Get<T> for GetStruct<T> {
    unsafe fn blank() -> Self {
        mem::zeroed()
    }

    unsafe fn ffi_ptr(&mut self) -> *mut c_void {
        mem::transmute(&mut self.val)
    }

    unsafe fn ffi_len(&mut self) -> *mut socklen_t {
        mem::transmute(&mut self.len)
    }

    unsafe fn unwrap(self) -> T {
        assert!(self.len as usize == mem::size_of::<T>(), "invalid getsockopt implementation");
        self.val
    }
}

struct SetStruct<'a, T: 'static> {
    ptr: &'a T,
}

impl<'a, T> Set<&'a T> for SetStruct<'a, T> {
    fn new(ptr: &'a T) -> SetStruct<'a, T> {
        SetStruct { ptr: ptr }
    }

    unsafe fn ffi_ptr(&self) -> *const c_void {
        mem::transmute(self.ptr)
    }

    unsafe fn ffi_len(&self) -> socklen_t {
        mem::size_of::<T>() as socklen_t
    }
}

struct GetBool {
    len: socklen_t,
    val: c_int,
}

impl Get<bool> for GetBool {
    unsafe fn blank() -> Self {
        mem::zeroed()
    }

    unsafe fn ffi_ptr(&mut self) -> *mut c_void {
        mem::transmute(&mut self.val)
    }

    unsafe fn ffi_len(&mut self) -> *mut socklen_t {
        mem::transmute(&mut self.len)
    }

    unsafe fn unwrap(self) -> bool {
        assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation");
        self.val != 0
    }
}

struct SetBool {
    val: c_int,
}

impl Set<bool> for SetBool {
    fn new(val: bool) -> SetBool {
        SetBool { val: if val { 1 } else { 0 } }
    }

    unsafe fn ffi_ptr(&self) -> *const c_void {
        mem::transmute(&self.val)
    }

    unsafe fn ffi_len(&self) -> socklen_t {
        mem::size_of::<c_int>() as socklen_t
    }
}

struct GetU8 {
    len: socklen_t,
    val: uint8_t,
}

impl Get<u8> for GetU8 {
    unsafe fn blank() -> Self {
        mem::zeroed()
    }

    unsafe fn ffi_ptr(&mut self) -> *mut c_void {
        mem::transmute(&mut self.val)
    }

    unsafe fn ffi_len(&mut self) -> *mut socklen_t {
        mem::transmute(&mut self.len)
    }

    unsafe fn unwrap(self) -> u8 {
        assert!(self.len as usize == mem::size_of::<uint8_t>(), "invalid getsockopt implementation");
        self.val as u8
    }
}

struct SetU8 {
    val: uint8_t,
}

impl Set<u8> for SetU8 {
    fn new(val: u8) -> SetU8 {
        SetU8 { val: val as uint8_t }
    }

    unsafe fn ffi_ptr(&self) -> *const c_void {
        mem::transmute(&self.val)
    }

    unsafe fn ffi_len(&self) -> socklen_t {
        mem::size_of::<c_int>() as socklen_t
    }
}
<|endoftext|>


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

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

use std::path::PathBuf;

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

use gtk::prelude::*;
use gtk::{ApplicationWindow, Button, FileChooserAction, FileChooserDialog,
          HeaderBar, ResponseType, Label, ToolButton};

use ::media::{Context, ContextMessage, Timestamp};
use ::media::ContextMessage::*;

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

pub struct MainController {
    window: ApplicationWindow,
    header_bar: HeaderBar,
    play_pause_btn: ToolButton,
    position_lbl: Label,
    info_ctrl: InfoController,
    video_ctrl: VideoController,
    audio_ctrl: Rc<RefCell<AudioController>>,

    context: Option<Context>,
    duration: u64,
    last_position: u64,

    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        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(),
            position_lbl: builder.get_object("position-lbl").unwrap(),
            info_ctrl: InfoController::new(&builder),
            video_ctrl: VideoController::new(&builder),
            audio_ctrl: AudioController::new(&builder),

            context: None,
            duration: 0,
            last_position: 0,

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

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = this.clone();
            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 = this.clone();
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });
        }

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

        this
    }

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

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

        match context.get_state() {
            gst::State::Paused => {
                self.register_tracker(25); // 40 Hz
                self.play_pause_btn.set_icon_name("media-playback-pause");
                context.play().unwrap();
            }
            gst::State::Playing => {
                context.pause().unwrap();
                self.play_pause_btn.set_icon_name("media-playback-start");
                self.remove_tracker();
            },
            state => println!("Can't play/pause in state {:?}", state),
        };

        self.context = Some(context);
    }

    pub fn stop(&mut self) {
        if let Some(context) = self.context.as_mut() {
            context.stop();
        };

        // remove callbacks in order to avoid conflict on borrowing of self
        self.remove_listener();
        self.remove_tracker();

        self.audio_ctrl.borrow_mut().cleanup();

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

    fn select_media(&mut self) {
        self.stop();

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

        if file_dlg.run() == ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }

        file_dlg.close();
    }

    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>,
    )
    {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut message_iter = ui_rx.try_iter();

            let mut keep_going = true;
            for message in message_iter.next() {
                match message {
                    AsyncDone => {
                        println!("Received AsyncDone");
                    },
                    InitDone => {
                        println!("Received InitDone");

                        let mut this_mut = this_rc.borrow_mut();

                        let context = this_mut.context.take()
                            .expect("... but no context available");

                        let duration = context.get_duration();
                        if duration.is_negative() {
                            panic!("Negative duration");
                        }
                        this_mut.duration = duration as u64;

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

                        this_mut.header_bar.set_subtitle(
                            Some(context.file_name.as_str())
                        );

                        this_mut.context = Some(context);
                    },
                    Eos => {
                        println!("Received Eos");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.remove_tracker();
                        let duration = this_mut.duration;
                        this_mut.tic(duration);

                        // TODO: exit tracker
                        this_mut.play_pause_btn.set_icon_name("media-playback-start");
                    },
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.context = None;
                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                };

                if !keep_going { break; }
            }

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

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

    fn tic(&mut self, position: u64) {
        self.position_lbl.set_text(
            &format!("{}", Timestamp::from_nano(position as i64))
        );
        self.last_position = position;

        self.audio_ctrl.borrow_mut().tic(position);
    }

    fn register_tracker(&mut self, timeout: u32) {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

        self.tracker_src = Some(gtk::timeout_add(timeout, move || {
            let mut this_mut = this_rc.borrow_mut();

            let position = this_mut.context.as_mut()
                .expect("No context in tracker")
                .get_position();
            if this_mut.last_position != position
            && position <= this_mut.duration {
                this_mut.tic(position);
            }

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

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

        self.position_lbl.set_text("00:00.000");

        let (ctx_tx, ui_rx) = channel();

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

        match Context::open_media_path(
                filepath,
                10_000_000_000,
                self.video_ctrl.video_box.clone(),
                ctx_tx
            )
            {
            Ok(context) => {
                self.context = Some(context);
                self.last_position = 0;
            },
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<commit_msg>Set tracker to 60Hz (#25)<commit_after>extern crate gtk;
extern crate glib;
extern crate gstreamer as gst;

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

use std::path::PathBuf;

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

use gtk::prelude::*;
use gtk::{ApplicationWindow, Button, FileChooserAction, FileChooserDialog,
          HeaderBar, ResponseType, Label, ToolButton};

use ::media::{Context, ContextMessage, Timestamp};
use ::media::ContextMessage::*;

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

pub struct MainController {
    window: ApplicationWindow,
    header_bar: HeaderBar,
    play_pause_btn: ToolButton,
    position_lbl: Label,
    info_ctrl: InfoController,
    video_ctrl: VideoController,
    audio_ctrl: Rc<RefCell<AudioController>>,

    context: Option<Context>,
    duration: u64,
    last_position: u64,

    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        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(),
            position_lbl: builder.get_object("position-lbl").unwrap(),
            info_ctrl: InfoController::new(&builder),
            video_ctrl: VideoController::new(&builder),
            audio_ctrl: AudioController::new(&builder),

            context: None,
            duration: 0,
            last_position: 0,

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

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = this.clone();
            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 = this.clone();
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });
        }

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

        this
    }

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

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

        match context.get_state() {
            gst::State::Paused => {
                self.register_tracker(17); // 60 Hz
                self.play_pause_btn.set_icon_name("media-playback-pause");
                context.play().unwrap();
            }
            gst::State::Playing => {
                context.pause().unwrap();
                self.play_pause_btn.set_icon_name("media-playback-start");
                self.remove_tracker();
            },
            state => println!("Can't play/pause in state {:?}", state),
        };

        self.context = Some(context);
    }

    pub fn stop(&mut self) {
        if let Some(context) = self.context.as_mut() {
            context.stop();
        };

        // remove callbacks in order to avoid conflict on borrowing of self
        self.remove_listener();
        self.remove_tracker();

        self.audio_ctrl.borrow_mut().cleanup();

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

    fn select_media(&mut self) {
        self.stop();

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

        if file_dlg.run() == ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }

        file_dlg.close();
    }

    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>,
    )
    {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut message_iter = ui_rx.try_iter();

            let mut keep_going = true;
            for message in message_iter.next() {
                match message {
                    AsyncDone => {
                        println!("Received AsyncDone");
                    },
                    InitDone => {
                        println!("Received InitDone");

                        let mut this_mut = this_rc.borrow_mut();

                        let context = this_mut.context.take()
                            .expect("... but no context available");

                        let duration = context.get_duration();
                        if duration.is_negative() {
                            panic!("Negative duration");
                        }
                        this_mut.duration = duration as u64;

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

                        this_mut.header_bar.set_subtitle(
                            Some(context.file_name.as_str())
                        );

                        this_mut.context = Some(context);
                    },
                    Eos => {
                        println!("Received Eos");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.remove_tracker();
                        let duration = this_mut.duration;
                        this_mut.tic(duration);

                        // TODO: exit tracker
                        this_mut.play_pause_btn.set_icon_name("media-playback-start");
                    },
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.context = None;
                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                };

                if !keep_going { break; }
            }

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

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

    fn tic(&mut self, position: u64) {
        self.position_lbl.set_text(
            &format!("{}", Timestamp::from_nano(position as i64))
        );
        self.last_position = position;

        self.audio_ctrl.borrow_mut().tic(position);
    }

    fn register_tracker(&mut self, timeout: u32) {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

        self.tracker_src = Some(gtk::timeout_add(timeout, move || {
            let mut this_mut = this_rc.borrow_mut();

            let position = this_mut.context.as_mut()
                .expect("No context in tracker")
                .get_position();
            if this_mut.last_position != position
            && position <= this_mut.duration {
                this_mut.tic(position);
            }

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

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

        self.position_lbl.set_text("00:00.000");

        let (ctx_tx, ui_rx) = channel();

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

        match Context::open_media_path(
                filepath,
                10_000_000_000,
                self.video_ctrl.video_box.clone(),
                ctx_tx
            )
            {
            Ok(context) => {
                self.context = Some(context);
                self.last_position = 0;
            },
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<|endoftext|>