<commit_before>//! Synchronization primitives for Vulkan objects.
//!
//! In Vulkan, you have to manually ensure two things:
//!
//! - That a buffer or an image are not read and written simultaneously (similarly to the CPU).
//! - That writes to a buffer or an image are propagated to other queues by inserting memory
//!   barriers.
//!
//! But don't worry ; this is automatically enforced by this library (as long as you don't use
//! any unsafe function). See the `memory` module for more info.
//!
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;

use device::Device;
use device::Queue;
use OomError;
use Success;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Base trait for objects that can be used as resources and must be synchronized.
pub unsafe trait Resource {
    /// Returns in which queue family or families this resource can be used.
    fn sharing_mode(&self) -> &SharingMode;

    /// Returns true if the `gpu_access` function should be passed a fence.
    #[inline]
    fn requires_fence(&self) -> bool {
        true
    }

    /// Returns true if the `gpu_access` function should be passed a semaphore.
    #[inline]
    fn requires_semaphore(&self) -> bool {
        true
    }
}

/// Declares in which queue(s) a resource can be used.
///
/// When you create a buffer or an image, you have to tell the Vulkan library in which queue
/// families it will be used. The vulkano library requires you to tell in which queue famiily
/// the resource will be used, even for exclusive mode.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SharingMode {
    /// The resource is used is only one queue family.
    Exclusive(u32),
    /// The resource is used in multiple queue families. Can be slower than `Exclusive`.
    Concurrent(Vec<u32>),       // TODO: Vec is too expensive here
}

impl<'a> From<&'a Arc<Queue>> for SharingMode {
    #[inline]
    fn from(queue: &'a Arc<Queue>) -> SharingMode {
        SharingMode::Exclusive(queue.family().id())
    }
}

impl<'a> From<&'a [&'a Arc<Queue>]> for SharingMode {
    #[inline]
    fn from(queues: &'a [&'a Arc<Queue>]) -> SharingMode {
        SharingMode::Concurrent(queues.iter().map(|queue| {
            queue.family().id()
        }).collect())
    }
}

/// A fence is used to know when a command buffer submission has finished its execution.
///
/// When a command buffer accesses a ressource, you have to ensure that the CPU doesn't access
/// the same ressource simultaneously (except for concurrent reads). Therefore in order to know
/// when the CPU can access a ressource again, a fence has to be used.
pub struct Fence {
    device: Arc<Device>,
    fence: vk::Fence,

    // If true, we know that the `Fence` is signaled. If false, we don't know.
    // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
    // multiple times.
    signaled: AtomicBool,
}

impl Fence {
    /// Builds a new fence.
    #[inline]
    pub fn new(device: &Arc<Device>) -> Result<Arc<Fence>, OomError> {
        Fence::new_impl(device, false)
    }

    /// Builds a new fence already in the "signaled" state.
    #[inline]
    pub fn signaled(device: &Arc<Device>) -> Result<Arc<Fence>, OomError> {
        Fence::new_impl(device, true)
    }

    fn new_impl(device: &Arc<Device>, signaled: bool) -> Result<Arc<Fence>, OomError> {
        let vk = device.pointers();

        let fence = unsafe {
            let infos = vk::FenceCreateInfo {
                sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
                pNext: ptr::null(),
                flags: if signaled { vk::FENCE_CREATE_SIGNALED_BIT } else { 0 },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateFence(device.internal_object(), &infos, ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Fence {
            device: device.clone(),
            fence: fence,
            signaled: AtomicBool::new(signaled),
        }))
    }

    /// Returns true if the fence is signaled.
    #[inline]
    pub fn ready(&self) -> Result<bool, OomError> {
        unsafe {
            if self.signaled.load(Ordering::Relaxed) { return Ok(true); }

            let vk = self.device.pointers();
            let result = try!(check_errors(vk.GetFenceStatus(self.device.internal_object(),
                                                             self.fence)));
            match result {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(true)
                },
                Success::NotReady => Ok(false),
                _ => unreachable!()
            }
        }
    }

    /// Waits until the fence is signaled, or at least until the number of nanoseconds of the
    /// timeout has elapsed.
    ///
    /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
    pub fn wait(&self, timeout_ns: u64) -> Result<(), OomError> {       // FIXME: wrong error
        unsafe {
            if self.signaled.load(Ordering::Relaxed) { return Ok(()); }

            let vk = self.device.pointers();
            let r = try!(check_errors(vk.WaitForFences(self.device.internal_object(), 1,
                                                       &self.fence, vk::TRUE, timeout_ns)));

            match r {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(())
                },
                Success::Timeout => panic!(),        // FIXME:
                _ => unreachable!()
            }
        }
    }

    /// Resets the fence.
    // FIXME: must synchronize the fence
    #[inline]
    pub fn reset(&self) {
        unsafe {
            let vk = self.device.pointers();
            vk.ResetFences(self.device.internal_object(), 1, &self.fence);
            self.signaled.store(false, Ordering::Relaxed);
        }
    }

    /// Resets multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_reset<'a, I>(iter: I)
        where I: IntoIterator<Item = &'a Fence>
    {
        let mut device = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(fence.device.clone()),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to reset multiple fences that didn't belong to the same device"),
            };

            fence.signaled.store(false, Ordering::Relaxed);
            fence.fence
        }).collect();

        if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                vk.ResetFences(device.internal_object(), fences.len() as u32, fences.as_ptr());
            }
        }
    }
}

unsafe impl VulkanObject for Fence {
    type Object = vk::Fence;

    #[inline]
    fn internal_object(&self) -> vk::Fence {
        self.fence
    }
}

impl Drop for Fence {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyFence(self.device.internal_object(), self.fence, ptr::null());
        }
    }
}

/// Used to provide synchronization between command buffers during their execution.
///
/// It is similar to a fence, except that it is purely on the GPU side. The CPU can't query a
/// semaphore's status or wait for it to be signaled.
pub struct Semaphore {
    device: Arc<Device>,
    semaphore: vk::Semaphore,
}

impl Semaphore {
    /// Builds a new semaphore.
    #[inline]
    pub fn new(device: &Arc<Device>) -> Result<Arc<Semaphore>, OomError> {
        let vk = device.pointers();

        let semaphore = unsafe {
            // since the creation is constant, we use a `static` instead of a struct on the stack
            static mut INFOS: vk::SemaphoreCreateInfo = vk::SemaphoreCreateInfo {
                sType: vk::STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
                pNext: 0 as *const _,   // ptr::null()
                flags: 0,   // reserved
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSemaphore(device.internal_object(), &INFOS,
                                                 ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Semaphore {
            device: device.clone(),
            semaphore: semaphore,
        }))
    }
}

unsafe impl VulkanObject for Semaphore {
    type Object = vk::Semaphore;

    #[inline]
    fn internal_object(&self) -> vk::Semaphore {
        self.semaphore
    }
}

impl Drop for Semaphore {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySemaphore(self.device.internal_object(), self.semaphore, ptr::null());
        }
    }
}

/// Used to block the GPU execution until an event on the CPU occurs.
///
/// Note that Vulkan implementations may have limits on how long a command buffer will wait for an
/// event to be signaled, in order to avoid interfering with progress of other clients of the GPU.
/// If the event isn't signaled within these limits, results are undefined and may include
/// device loss.
pub struct Event {
    device: Arc<Device>,
    event: Mutex<vk::Event>,
}

impl Event {
    /// Builds a new event.
    #[inline]
    pub fn new(device: &Arc<Device>) -> Result<Arc<Event>, OomError> {
        let vk = device.pointers();

        let event = unsafe {
            // since the creation is constant, we use a `static` instead of a struct on the stack
            static mut INFOS: vk::EventCreateInfo = vk::EventCreateInfo {
                sType: vk::STRUCTURE_TYPE_EVENT_CREATE_INFO,
                pNext: 0 as *const _, //ptr::null(),
                flags: 0,   // reserved
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateEvent(device.internal_object(), &INFOS,
                                             ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Event {
            device: device.clone(),
            event: Mutex::new(event),
        }))
    }

    /// Returns true if the event is signaled.
    #[inline]
    pub fn signaled(&self) -> Result<bool, OomError> {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            let result = try!(check_errors(vk.GetEventStatus(self.device.internal_object(),
                                                             *event)));
            match result {
                Success::EventSet => Ok(true),
                Success::EventReset => Ok(false),
                _ => unreachable!()
            }
        }
    }

    /// Changes the `Event` to the signaled state.
    ///
    /// If a command buffer is waiting on this event, it is then unblocked.
    #[inline]
    pub fn set(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            try!(check_errors(vk.SetEvent(self.device.internal_object(), *event)).map(|_| ()));
            Ok(())
        }
    }

    /// Changes the `Event` to the unsignaled state.
    #[inline]
    pub fn reset(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            try!(check_errors(vk.ResetEvent(self.device.internal_object(), *event)).map(|_| ()));
            Ok(())
        }
    }
}

unsafe impl SynchronizedVulkanObject for Event {
    type Object = vk::Event;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::Event> {
        self.event.lock().unwrap()
    }
}

impl Drop for Event {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            vk.DestroyEvent(self.device.internal_object(), *event, ptr::null());
        }
    }
}

#[cfg(test)]
mod tests {
    use sync::Fence;
    use sync::Semaphore;

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_create() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::new(&device).unwrap();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    fn fence_create_signaled() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        assert!(fence.ready().unwrap());
    }

    #[test]
    fn fence_signaled_wait() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.wait(10).unwrap();
    }

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_reset() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.reset();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    fn semaphore_create() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = Semaphore::new(&device).unwrap();
    }
}
<commit_msg>Add Fence::multi_wait function<commit_after>//! Synchronization primitives for Vulkan objects.
//!
//! In Vulkan, you have to manually ensure two things:
//!
//! - That a buffer or an image are not read and written simultaneously (similarly to the CPU).
//! - That writes to a buffer or an image are propagated to other queues by inserting memory
//!   barriers.
//!
//! But don't worry ; this is automatically enforced by this library (as long as you don't use
//! any unsafe function). See the `memory` module for more info.
//!
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;

use device::Device;
use device::Queue;
use OomError;
use Success;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Base trait for objects that can be used as resources and must be synchronized.
pub unsafe trait Resource {
    /// Returns in which queue family or families this resource can be used.
    fn sharing_mode(&self) -> &SharingMode;

    /// Returns true if the `gpu_access` function should be passed a fence.
    #[inline]
    fn requires_fence(&self) -> bool {
        true
    }

    /// Returns true if the `gpu_access` function should be passed a semaphore.
    #[inline]
    fn requires_semaphore(&self) -> bool {
        true
    }
}

/// Declares in which queue(s) a resource can be used.
///
/// When you create a buffer or an image, you have to tell the Vulkan library in which queue
/// families it will be used. The vulkano library requires you to tell in which queue famiily
/// the resource will be used, even for exclusive mode.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SharingMode {
    /// The resource is used is only one queue family.
    Exclusive(u32),
    /// The resource is used in multiple queue families. Can be slower than `Exclusive`.
    Concurrent(Vec<u32>),       // TODO: Vec is too expensive here
}

impl<'a> From<&'a Arc<Queue>> for SharingMode {
    #[inline]
    fn from(queue: &'a Arc<Queue>) -> SharingMode {
        SharingMode::Exclusive(queue.family().id())
    }
}

impl<'a> From<&'a [&'a Arc<Queue>]> for SharingMode {
    #[inline]
    fn from(queues: &'a [&'a Arc<Queue>]) -> SharingMode {
        SharingMode::Concurrent(queues.iter().map(|queue| {
            queue.family().id()
        }).collect())
    }
}

/// A fence is used to know when a command buffer submission has finished its execution.
///
/// When a command buffer accesses a ressource, you have to ensure that the CPU doesn't access
/// the same ressource simultaneously (except for concurrent reads). Therefore in order to know
/// when the CPU can access a ressource again, a fence has to be used.
pub struct Fence {
    device: Arc<Device>,
    fence: vk::Fence,

    // If true, we know that the `Fence` is signaled. If false, we don't know.
    // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
    // multiple times.
    signaled: AtomicBool,
}

impl Fence {
    /// Builds a new fence.
    #[inline]
    pub fn new(device: &Arc<Device>) -> Result<Arc<Fence>, OomError> {
        Fence::new_impl(device, false)
    }

    /// Builds a new fence already in the "signaled" state.
    #[inline]
    pub fn signaled(device: &Arc<Device>) -> Result<Arc<Fence>, OomError> {
        Fence::new_impl(device, true)
    }

    fn new_impl(device: &Arc<Device>, signaled: bool) -> Result<Arc<Fence>, OomError> {
        let vk = device.pointers();

        let fence = unsafe {
            let infos = vk::FenceCreateInfo {
                sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
                pNext: ptr::null(),
                flags: if signaled { vk::FENCE_CREATE_SIGNALED_BIT } else { 0 },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateFence(device.internal_object(), &infos, ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Fence {
            device: device.clone(),
            fence: fence,
            signaled: AtomicBool::new(signaled),
        }))
    }

    /// Returns true if the fence is signaled.
    #[inline]
    pub fn ready(&self) -> Result<bool, OomError> {
        unsafe {
            if self.signaled.load(Ordering::Relaxed) { return Ok(true); }

            let vk = self.device.pointers();
            let result = try!(check_errors(vk.GetFenceStatus(self.device.internal_object(),
                                                             self.fence)));
            match result {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(true)
                },
                Success::NotReady => Ok(false),
                _ => unreachable!()
            }
        }
    }

    /// Waits until the fence is signaled, or at least until the number of nanoseconds of the
    /// timeout has elapsed.
    ///
    /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
    pub fn wait(&self, timeout_ns: u64) -> Result<(), OomError> {       // FIXME: wrong error
        unsafe {
            if self.signaled.load(Ordering::Relaxed) { return Ok(()); }

            let vk = self.device.pointers();
            let r = try!(check_errors(vk.WaitForFences(self.device.internal_object(), 1,
                                                       &self.fence, vk::TRUE, timeout_ns)));

            match r {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(())
                },
                Success::Timeout => panic!(),        // FIXME:
                _ => unreachable!()
            }
        }
    }

    /// Waits for multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_wait<'a, I>(iter: I, timeout_ns: u64) -> Result<(), OomError>      // FIXME: wrong error
        where I: IntoIterator<Item = &'a Fence>
    {
        let mut device = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().filter_map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(fence.device.clone()),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to wait for  multiple fences that didn't belong to the same device"),
            };

            if fence.signaled.load(Ordering::Relaxed) {
                None
            } else {
                Some(fence.fence)
            }
        }).collect();

        let r = if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                try!(check_errors(vk.WaitForFences(device.internal_object(), fences.len() as u32,
                                                   fences.as_ptr(), vk::TRUE, timeout_ns)))
            }
        } else {
            return Ok(());
        };

        match r {
            Success::Success => Ok(()),
            Success::Timeout => panic!(),        // FIXME:
            _ => unreachable!()
        }
    }

    /// Resets the fence.
    // FIXME: must synchronize the fence
    #[inline]
    pub fn reset(&self) {
        unsafe {
            let vk = self.device.pointers();
            vk.ResetFences(self.device.internal_object(), 1, &self.fence);
            self.signaled.store(false, Ordering::Relaxed);
        }
    }

    /// Resets multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_reset<'a, I>(iter: I)
        where I: IntoIterator<Item = &'a Fence>
    {
        let mut device = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(fence.device.clone()),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to reset multiple fences that didn't belong to the same device"),
            };

            fence.signaled.store(false, Ordering::Relaxed);
            fence.fence
        }).collect();

        if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                vk.ResetFences(device.internal_object(), fences.len() as u32, fences.as_ptr());
            }
        }
    }
}

unsafe impl VulkanObject for Fence {
    type Object = vk::Fence;

    #[inline]
    fn internal_object(&self) -> vk::Fence {
        self.fence
    }
}

impl Drop for Fence {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyFence(self.device.internal_object(), self.fence, ptr::null());
        }
    }
}

/// Used to provide synchronization between command buffers during their execution.
///
/// It is similar to a fence, except that it is purely on the GPU side. The CPU can't query a
/// semaphore's status or wait for it to be signaled.
pub struct Semaphore {
    device: Arc<Device>,
    semaphore: vk::Semaphore,
}

impl Semaphore {
    /// Builds a new semaphore.
    #[inline]
    pub fn new(device: &Arc<Device>) -> Result<Arc<Semaphore>, OomError> {
        let vk = device.pointers();

        let semaphore = unsafe {
            // since the creation is constant, we use a `static` instead of a struct on the stack
            static mut INFOS: vk::SemaphoreCreateInfo = vk::SemaphoreCreateInfo {
                sType: vk::STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
                pNext: 0 as *const _,   // ptr::null()
                flags: 0,   // reserved
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSemaphore(device.internal_object(), &INFOS,
                                                 ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Semaphore {
            device: device.clone(),
            semaphore: semaphore,
        }))
    }
}

unsafe impl VulkanObject for Semaphore {
    type Object = vk::Semaphore;

    #[inline]
    fn internal_object(&self) -> vk::Semaphore {
        self.semaphore
    }
}

impl Drop for Semaphore {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySemaphore(self.device.internal_object(), self.semaphore, ptr::null());
        }
    }
}

/// Used to block the GPU execution until an event on the CPU occurs.
///
/// Note that Vulkan implementations may have limits on how long a command buffer will wait for an
/// event to be signaled, in order to avoid interfering with progress of other clients of the GPU.
/// If the event isn't signaled within these limits, results are undefined and may include
/// device loss.
pub struct Event {
    device: Arc<Device>,
    event: Mutex<vk::Event>,
}

impl Event {
    /// Builds a new event.
    #[inline]
    pub fn new(device: &Arc<Device>) -> Result<Arc<Event>, OomError> {
        let vk = device.pointers();

        let event = unsafe {
            // since the creation is constant, we use a `static` instead of a struct on the stack
            static mut INFOS: vk::EventCreateInfo = vk::EventCreateInfo {
                sType: vk::STRUCTURE_TYPE_EVENT_CREATE_INFO,
                pNext: 0 as *const _, //ptr::null(),
                flags: 0,   // reserved
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateEvent(device.internal_object(), &INFOS,
                                             ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Event {
            device: device.clone(),
            event: Mutex::new(event),
        }))
    }

    /// Returns true if the event is signaled.
    #[inline]
    pub fn signaled(&self) -> Result<bool, OomError> {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            let result = try!(check_errors(vk.GetEventStatus(self.device.internal_object(),
                                                             *event)));
            match result {
                Success::EventSet => Ok(true),
                Success::EventReset => Ok(false),
                _ => unreachable!()
            }
        }
    }

    /// Changes the `Event` to the signaled state.
    ///
    /// If a command buffer is waiting on this event, it is then unblocked.
    #[inline]
    pub fn set(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            try!(check_errors(vk.SetEvent(self.device.internal_object(), *event)).map(|_| ()));
            Ok(())
        }
    }

    /// Changes the `Event` to the unsignaled state.
    #[inline]
    pub fn reset(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            try!(check_errors(vk.ResetEvent(self.device.internal_object(), *event)).map(|_| ()));
            Ok(())
        }
    }
}

unsafe impl SynchronizedVulkanObject for Event {
    type Object = vk::Event;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::Event> {
        self.event.lock().unwrap()
    }
}

impl Drop for Event {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            let event = self.event.lock().unwrap();
            vk.DestroyEvent(self.device.internal_object(), *event, ptr::null());
        }
    }
}

#[cfg(test)]
mod tests {
    use sync::Fence;
    use sync::Semaphore;

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_create() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::new(&device).unwrap();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    fn fence_create_signaled() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        assert!(fence.ready().unwrap());
    }

    #[test]
    fn fence_signaled_wait() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.wait(10).unwrap();
    }

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_reset() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.reset();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    fn semaphore_create() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = Semaphore::new(&device).unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
use std::io::Cursor;
use std::io::Write;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

#[derive(Debug)]
pub enum MemoryError {
  PPUAccessViolation(String),
  APUAccessViolation(String),
}

pub struct Memory {
    pub mem: Vec<u8>,
}

impl Memory {
    pub fn new() -> Memory {
        let mut m =  Memory {
            mem: Vec::with_capacity(::std::u16::MAX as usize)
        };
        unsafe { &m.mem.set_len(::std::u16::MAX as usize); }
        return m;
    }

    // meant for a 'raw' write interface, not meant to be used by the 6502 processor itself, more
    // tests and other tools to be able to read blocks of memory quickly and easily
    //
    pub fn write(self:&mut Memory, index:u16, inp: &[u8]) {
        let idx = index as usize;
        if inp.len() + idx > self.mem.len() {
            panic!("memory vec length is {}, input array goes from {} to {}",self.mem.len(),index,inp.len()+idx)
        }
        &self.mem[idx..(idx+inp.len())].clone_from_slice(inp);
    }
    pub fn read8(self:&Memory,addr: u16) -> Result<u8,MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) => Ok(self.mem[raddr]),
            Err(err) => Err(err)
        }
    }
    pub fn read16(self:&Memory,addr: u16)  -> Result<u16,MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) =>{
                // splices are exclusive on the upper range (half open)
                let mut rdr = Cursor::new(&self.mem[raddr..(raddr+2)]);
                Ok(rdr.read_u16::<LittleEndian>().unwrap())
            },
            Err(err) => Err(err)
        }
    }
    pub fn write8(self:&mut Memory,addr: u16, val:u8) -> Result<(),MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) => {
                self.mem[raddr] = val;
                Ok(())
            },
            Err(err) => Err(err)
        }
    }
    pub fn write16(self:&mut Memory,addr: u16, val:u16) -> Result<(),MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) => {
                // splices are exclusive on the upper range (half open)
                (&mut self.mem[raddr..(raddr+2)]).write_u16::<LittleEndian>(val).unwrap();
                Ok(())
            },
            Err(err) => Err(err)
        }
    }

    // https://en.wikibooks.org/wiki/NES_Programming/Memory_Map
    //
    fn resolve_address(self:&Memory,addr: u16) -> Result<usize,MemoryError> {
        if addr <= 0x1FFF { return Ok( (addr & 0x7FF) as usize); }
        if addr <= 0x2007 { return Err(MemoryError::PPUAccessViolation("PPU Memory access is currently not implemented".to_string())); }
        if addr <= 0x3FFF { return Ok( (addr & 0x7FF & 0x08) as usize); }
        if addr <= 0x401F { return Err(MemoryError::APUAccessViolation("Memory access is currently not implemented".to_string())); }
        Ok(addr as usize)
    }
}
<commit_msg>memory vector is now the correct size<commit_after>extern crate byteorder;
use std::io::Cursor;
use std::io::Write;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

#[derive(Debug)]
pub enum MemoryError {
  PPUAccessViolation(String),
  APUAccessViolation(String),
}

pub struct Memory {
    pub mem: Vec<u8>,
}

impl Memory {
    pub fn new() -> Memory {
        let mut m =  Memory {
            mem: Vec::with_capacity(::std::u16::MAX as usize +1)
        };
        unsafe { &m.mem.set_len(::std::u16::MAX as usize +1); }
        return m;
    }

    // meant for a 'raw' write interface, not meant to be used by the 6502 processor itself, more
    // tests and other tools to be able to read blocks of memory quickly and easily
    //
    pub fn write(self:&mut Memory, index:u16, inp: &[u8]) {
        let idx = index as usize;
        if inp.len() + idx > self.mem.len() {
            panic!("memory vec length is {}, input array goes from {} to {}",self.mem.len(),index,inp.len()+idx)
        }
        &self.mem[idx..(idx+inp.len())].clone_from_slice(inp);
    }
    pub fn read8(self:&Memory,addr: u16) -> Result<u8,MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) => Ok(self.mem[raddr]),
            Err(err) => Err(err)
        }
    }
    pub fn read16(self:&Memory,addr: u16)  -> Result<u16,MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) =>{
                // splices are exclusive on the upper range (half open)
                let mut rdr = Cursor::new(&self.mem[raddr..(raddr+2)]);
                Ok(rdr.read_u16::<LittleEndian>().unwrap())
            },
            Err(err) => Err(err)
        }
    }
    pub fn write8(self:&mut Memory,addr: u16, val:u8) -> Result<(),MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) => {
                self.mem[raddr] = val;
                Ok(())
            },
            Err(err) => Err(err)
        }
    }
    pub fn write16(self:&mut Memory,addr: u16, val:u16) -> Result<(),MemoryError> {
        match self.resolve_address(addr) {
            Ok(raddr) => {
                // splices are exclusive on the upper range (half open)
                (&mut self.mem[raddr..(raddr+2)]).write_u16::<LittleEndian>(val).unwrap();
                Ok(())
            },
            Err(err) => Err(err)
        }
    }

    // https://en.wikibooks.org/wiki/NES_Programming/Memory_Map
    //
    fn resolve_address(self:&Memory,addr: u16) -> Result<usize,MemoryError> {
        if addr <= 0x1FFF { return Ok( (addr & 0x7FF) as usize); }
        if addr <= 0x2007 { return Err(MemoryError::PPUAccessViolation("PPU Memory access is currently not implemented".to_string())); }
        if addr <= 0x3FFF { return Ok( (addr & 0x7FF & 0x08) as usize); }
        if addr <= 0x401F { return Err(MemoryError::APUAccessViolation("Memory access is currently not implemented".to_string())); }
        Ok(addr as usize)
    }
}
<|endoftext|>


<commit_before>extern crate camera_controllers;
extern crate gfx;
extern crate piston_window;
extern crate terra;
extern crate vecmath;

use std::path::Path;

use piston_window::*;
use camera_controllers::{FirstPersonSettings, FirstPerson, CameraPerspective,
                         model_view_projection};

use terra::{Clipmap, DemSource, TerrainFileParams, MaterialSet};
use terra::cache::GeneratedAsset;

fn main() {
    let terrain_file = TerrainFileParams {
        latitude: 44,
        longitude: -72,
        source: DemSource::Usgs30m,
    }.load()
        .unwrap();

    let mut window: PistonWindow = WindowSettings::new("terra preview", [640, 480])
        .exit_on_esc(true)
        .opengl(OpenGL::V3_3)
        .samples(1)
        .build()
        .unwrap();
    window.set_capture_cursor(true);

    let materials = MaterialSet::load(
        Path::new("assets"),
        &mut window.factory,
        &mut window.encoder,
    );
    window.encoder.flush(&mut window.device);

    let mut terrain = Clipmap::new(
        terrain_file,
        materials,
        window.factory.clone(),
        &mut window.encoder,
        &window.output_color,
        &window.output_stencil,
    );

    let get_projection = |w: &PistonWindow| {
        let draw_size = w.window.draw_size();
        CameraPerspective {
            fov: 90.0,
            near_clip: 0.5,
            far_clip: 100000.0,
            aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32),
        }.projection()
    };

    let mut projection = get_projection(&window);
    let mut first_person = FirstPerson::new([0.0, 0.0, 0.0], FirstPersonSettings::keyboard_wasd());
    first_person.settings.speed_vertical = 500.0;
    first_person.settings.speed_horizontal = 500.0;

    while let Some(e) = window.next() {
        first_person.event(&e);

        if let Some(_) = e.resize_args() {
            projection = get_projection(&window);
        }

        window.draw_3d(&e, |window| {
            let args = e.render_args().unwrap();

            window.encoder.clear(
                &window.output_color,
                [0.3, 0.3, 0.3, 1.0],
            );
            window.encoder.clear_depth(&window.output_stencil, 1.0);

            let mut camera = first_person.camera(args.ext_dt);
            if let Some(h) = terrain.get_approximate_height(
                [camera.position[0], camera.position[2]],
            )
            {
                camera.position[1] += h + 2.0;
            }
            terrain.update(
                model_view_projection(vecmath::mat4_id(), camera.orthogonal(), projection),
                camera.position,
            );
            terrain.render(&mut window.encoder);
        });
    }
}
<commit_msg>Change preview location<commit_after>extern crate camera_controllers;
extern crate gfx;
extern crate piston_window;
extern crate terra;
extern crate vecmath;

use std::path::Path;

use piston_window::*;
use camera_controllers::{FirstPersonSettings, FirstPerson, CameraPerspective,
                         model_view_projection};

use terra::{Clipmap, DemSource, TerrainFileParams, MaterialSet};
use terra::cache::GeneratedAsset;

fn main() {
    let terrain_file = TerrainFileParams {
        latitude: 44,
        longitude: -74,
        source: DemSource::Usgs30m,
    }.load()
        .unwrap();

    let mut window: PistonWindow = WindowSettings::new("terra preview", [640, 480])
        .exit_on_esc(true)
        .opengl(OpenGL::V3_3)
        .samples(1)
        .build()
        .unwrap();
    window.set_capture_cursor(true);

    let materials = MaterialSet::load(
        Path::new("assets"),
        &mut window.factory,
        &mut window.encoder,
    );
    window.encoder.flush(&mut window.device);

    let mut terrain = Clipmap::new(
        terrain_file,
        materials,
        window.factory.clone(),
        &mut window.encoder,
        &window.output_color,
        &window.output_stencil,
    );

    let get_projection = |w: &PistonWindow| {
        let draw_size = w.window.draw_size();
        CameraPerspective {
            fov: 90.0,
            near_clip: 0.5,
            far_clip: 100000.0,
            aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32),
        }.projection()
    };

    let mut projection = get_projection(&window);
    let mut first_person = FirstPerson::new([0.0, 0.0, 0.0], FirstPersonSettings::keyboard_wasd());
    first_person.settings.speed_vertical = 500.0;
    first_person.settings.speed_horizontal = 500.0;

    while let Some(e) = window.next() {
        first_person.event(&e);

        if let Some(_) = e.resize_args() {
            projection = get_projection(&window);
        }

        window.draw_3d(&e, |window| {
            let args = e.render_args().unwrap();

            window.encoder.clear(
                &window.output_color,
                [0.3, 0.3, 0.3, 1.0],
            );
            window.encoder.clear_depth(&window.output_stencil, 1.0);

            let mut camera = first_person.camera(args.ext_dt);
            if let Some(h) = terrain.get_approximate_height(
                [camera.position[0], camera.position[2]],
            )
            {
                camera.position[1] += h + 2.0;
            }
            terrain.update(
                model_view_projection(vecmath::mat4_id(), camera.orthogonal(), projection),
                camera.position,
            );
            terrain.render(&mut window.encoder);
        });
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

pub fn spawn(cmd: &str, updated_paths: Vec<PathBuf>) -> Process {
    self::imp::Process::new(cmd, updated_paths).expect("unable to spawn process")
}

pub use self::imp::Process;

#[cfg(target_family = "unix")]
mod imp {
    use libc::*;
    use std::io::Result;
    use std::path::PathBuf;
    use std::process::Command;
    use std::sync::*;
    use signal::Signal;

    pub struct Process {
        pgid: pid_t,
        lock: Mutex<bool>,
        cvar: Condvar,
    }

    #[allow(unknown_lints)]
    #[allow(mutex_atomic)]
    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use nix::unistd::*;
            use std::io;
            use std::os::unix::process::CommandExt;

            let mut command = Command::new("sh");
            command.arg("-c").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            // Until process_exec lands in stable, handle fork/exec ourselves
            command
                .before_exec(|| setpgid(0, 0).map_err(io::Error::from))
                .spawn()
                .and_then(|p| {
                              Ok(Process {
                                     pgid: p.id() as i32,
                                     lock: Mutex::new(false),
                                     cvar: Condvar::new(),
                                 })
                          })
        }

        pub fn reap(&self) {
            use nix::sys::wait::*;

            let mut finished = true;
            loop {
                match waitpid(-self.pgid, Some(WNOHANG)) {
                    Ok(WaitStatus::Exited(_, _)) |
                    Ok(WaitStatus::Signaled(_, _, _)) => finished = finished && true,
                    Ok(_) => {
                        finished = false;
                        break;
                    }
                    Err(_) => break,
                }
            }

            if finished {
                let mut done = self.lock.lock().unwrap();
                *done = true;
                self.cvar.notify_one();
            }
        }

        pub fn signal(&self, signal: Signal) {
            use signal::ConvertToLibc;

            let signo = signal.convert_to_libc();
            debug!("Sending {:?} (int: {}) to child process", signal, signo);
            self.c_signal(signo);
        }

        fn c_signal(&self, sig: c_int) {
            extern "C" {
                fn killpg(pgrp: pid_t, sig: c_int) -> c_int;
            }

            unsafe {
                killpg(self.pgid, sig);
            }

        }

        pub fn wait(&self) {
            let mut done = self.lock.lock().unwrap();
            while !*done {
                done = self.cvar.wait(done).unwrap();
            }
        }
    }
}

#[cfg(target_family = "windows")]
mod imp {
    use std::io;
    use std::io::Result;
    use std::mem;
    use std::path::PathBuf;
    use std::process::Command;
    use kernel32::*;
    use winapi::*;
    use signal::Signal;

    pub struct Process {
        job: HANDLE,
    }

    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use std::os::windows::io::IntoRawHandle;

            fn last_err() -> io::Error {
                io::Error::last_os_error()
            }

            let job = unsafe { CreateJobObjectW(0 as *mut _, 0 as *const _) };
            if job.is_null() {
                panic!("failed to create job object: {}", last_err());
            }

            let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { mem::zeroed() };
            info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
            let r = unsafe {
                SetInformationJobObject(job,
                                        JobObjectExtendedLimitInformation,
                                        &mut info as *mut _ as LPVOID,
                                        mem::size_of_val(&info) as DWORD)
            };
            if r == 0 {
                panic!("failed to set job info: {}", last_err());
            }

            let mut command = Command::new("cmd.exe");
            command.arg("/C").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            command
                .spawn()
                .and_then(|p| {
                              let r = unsafe { AssignProcessToJobObject(job, p.into_raw_handle()) };
                              if r == 0 {
                                  panic!("failed to add to job object: {}", last_err());
                              }

                              Ok(Process { job: job })
                          })
        }

        pub fn reap(&self) {}

        pub fn signal(&self, signal: Signal) {
            debug!("Ignoring signal {:?} (not supported by Windows)", signal);
            unsafe {
                let _ = TerminateJobObject(self.job, 1);
            }
        }

        pub fn wait(&self) {
            unsafe {
                let _ = WaitForSingleObject(self.job, INFINITE);
            }
        }
    }

    impl Drop for Process {
        fn drop(&mut self) {
            unsafe {
                let _ = CloseHandle(self.job);
            }
        }
    }

    unsafe impl Send for Process {}
    unsafe impl Sync for Process {}
}

fn get_single_updated_path(paths: &[PathBuf]) -> Option<&str> {
    paths.get(0).and_then(|p| p.to_str())
}

fn get_longest_common_path(paths: &[PathBuf]) -> Option<String> {
    match paths.len() {
        0 => return None,
        1 => return paths[0].to_str().map(|ref_val| ref_val.to_string()),
        _ => {}
    };

    let mut longest_path: Vec<_> = paths[0].components().collect();

    for path in &paths[1..] {
        let mut greatest_distance = 0;
        for component_pair in path.components().zip(longest_path.iter()) {
            if component_pair.0 != *component_pair.1 {
                break;
            }

            greatest_distance += 1;
        }

        if greatest_distance != longest_path.len() {
            longest_path.truncate(greatest_distance);
        }
    }

    let mut result = PathBuf::new();
    for component in longest_path {
        result.push(component.as_os_str());
    }

    result.to_str().map(|ref_val| ref_val.to_string())
}

#[cfg(test)]
#[cfg(target_family = "unix")]
mod tests {
    use std::path::PathBuf;

    use super::spawn;
    use super::get_longest_common_path;

    #[test]
    fn test_start() {
        let _ = spawn("echo hi", vec![]);
    }

    #[test]
    fn longest_common_path_should_return_correct_value() {
        let single_path = vec![PathBuf::from("/tmp/random/")];
        let single_result = get_longest_common_path(&single_path).unwrap();
        assert_eq!(single_result, "/tmp/random/");

        let common_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/fly")];

        let common_result = get_longest_common_path(&common_paths).unwrap();
        assert_eq!(common_result, "/tmp/logs");

        let diverging_paths = vec![PathBuf::from("/tmp/logs/hi"), PathBuf::from("/var/logs/hi")];

        let diverging_result = get_longest_common_path(&diverging_paths).unwrap();
        assert_eq!(diverging_result, "/");

        let uneven_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/"),
                                PathBuf::from("/tmp/logs/bye")];

        let uneven_result = get_longest_common_path(&uneven_paths).unwrap();
        assert_eq!(uneven_result, "/tmp/logs");
    }
}
<commit_msg>Remove deprecated comment (before_exec)<commit_after>use std::path::PathBuf;

pub fn spawn(cmd: &str, updated_paths: Vec<PathBuf>) -> Process {
    self::imp::Process::new(cmd, updated_paths).expect("unable to spawn process")
}

pub use self::imp::Process;

#[cfg(target_family = "unix")]
mod imp {
    use libc::*;
    use std::io::Result;
    use std::path::PathBuf;
    use std::process::Command;
    use std::sync::*;
    use signal::Signal;

    pub struct Process {
        pgid: pid_t,
        lock: Mutex<bool>,
        cvar: Condvar,
    }

    #[allow(unknown_lints)]
    #[allow(mutex_atomic)]
    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use nix::unistd::*;
            use std::io;
            use std::os::unix::process::CommandExt;

            let mut command = Command::new("sh");
            command.arg("-c").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            command
                .before_exec(|| setpgid(0, 0).map_err(io::Error::from))
                .spawn()
                .and_then(|p| {
                              Ok(Process {
                                     pgid: p.id() as i32,
                                     lock: Mutex::new(false),
                                     cvar: Condvar::new(),
                                 })
                          })
        }

        pub fn reap(&self) {
            use nix::sys::wait::*;

            let mut finished = true;
            loop {
                match waitpid(-self.pgid, Some(WNOHANG)) {
                    Ok(WaitStatus::Exited(_, _)) |
                    Ok(WaitStatus::Signaled(_, _, _)) => finished = finished && true,
                    Ok(_) => {
                        finished = false;
                        break;
                    }
                    Err(_) => break,
                }
            }

            if finished {
                let mut done = self.lock.lock().unwrap();
                *done = true;
                self.cvar.notify_one();
            }
        }

        pub fn signal(&self, signal: Signal) {
            use signal::ConvertToLibc;

            let signo = signal.convert_to_libc();
            debug!("Sending {:?} (int: {}) to child process", signal, signo);
            self.c_signal(signo);
        }

        fn c_signal(&self, sig: c_int) {
            extern "C" {
                fn killpg(pgrp: pid_t, sig: c_int) -> c_int;
            }

            unsafe {
                killpg(self.pgid, sig);
            }

        }

        pub fn wait(&self) {
            let mut done = self.lock.lock().unwrap();
            while !*done {
                done = self.cvar.wait(done).unwrap();
            }
        }
    }
}

#[cfg(target_family = "windows")]
mod imp {
    use std::io;
    use std::io::Result;
    use std::mem;
    use std::path::PathBuf;
    use std::process::Command;
    use kernel32::*;
    use winapi::*;
    use signal::Signal;

    pub struct Process {
        job: HANDLE,
    }

    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use std::os::windows::io::IntoRawHandle;

            fn last_err() -> io::Error {
                io::Error::last_os_error()
            }

            let job = unsafe { CreateJobObjectW(0 as *mut _, 0 as *const _) };
            if job.is_null() {
                panic!("failed to create job object: {}", last_err());
            }

            let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { mem::zeroed() };
            info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
            let r = unsafe {
                SetInformationJobObject(job,
                                        JobObjectExtendedLimitInformation,
                                        &mut info as *mut _ as LPVOID,
                                        mem::size_of_val(&info) as DWORD)
            };
            if r == 0 {
                panic!("failed to set job info: {}", last_err());
            }

            let mut command = Command::new("cmd.exe");
            command.arg("/C").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            command
                .spawn()
                .and_then(|p| {
                              let r = unsafe { AssignProcessToJobObject(job, p.into_raw_handle()) };
                              if r == 0 {
                                  panic!("failed to add to job object: {}", last_err());
                              }

                              Ok(Process { job: job })
                          })
        }

        pub fn reap(&self) {}

        pub fn signal(&self, signal: Signal) {
            debug!("Ignoring signal {:?} (not supported by Windows)", signal);
            unsafe {
                let _ = TerminateJobObject(self.job, 1);
            }
        }

        pub fn wait(&self) {
            unsafe {
                let _ = WaitForSingleObject(self.job, INFINITE);
            }
        }
    }

    impl Drop for Process {
        fn drop(&mut self) {
            unsafe {
                let _ = CloseHandle(self.job);
            }
        }
    }

    unsafe impl Send for Process {}
    unsafe impl Sync for Process {}
}

fn get_single_updated_path(paths: &[PathBuf]) -> Option<&str> {
    paths.get(0).and_then(|p| p.to_str())
}

fn get_longest_common_path(paths: &[PathBuf]) -> Option<String> {
    match paths.len() {
        0 => return None,
        1 => return paths[0].to_str().map(|ref_val| ref_val.to_string()),
        _ => {}
    };

    let mut longest_path: Vec<_> = paths[0].components().collect();

    for path in &paths[1..] {
        let mut greatest_distance = 0;
        for component_pair in path.components().zip(longest_path.iter()) {
            if component_pair.0 != *component_pair.1 {
                break;
            }

            greatest_distance += 1;
        }

        if greatest_distance != longest_path.len() {
            longest_path.truncate(greatest_distance);
        }
    }

    let mut result = PathBuf::new();
    for component in longest_path {
        result.push(component.as_os_str());
    }

    result.to_str().map(|ref_val| ref_val.to_string())
}

#[cfg(test)]
#[cfg(target_family = "unix")]
mod tests {
    use std::path::PathBuf;

    use super::spawn;
    use super::get_longest_common_path;

    #[test]
    fn test_start() {
        let _ = spawn("echo hi", vec![]);
    }

    #[test]
    fn longest_common_path_should_return_correct_value() {
        let single_path = vec![PathBuf::from("/tmp/random/")];
        let single_result = get_longest_common_path(&single_path).unwrap();
        assert_eq!(single_result, "/tmp/random/");

        let common_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/fly")];

        let common_result = get_longest_common_path(&common_paths).unwrap();
        assert_eq!(common_result, "/tmp/logs");

        let diverging_paths = vec![PathBuf::from("/tmp/logs/hi"), PathBuf::from("/var/logs/hi")];

        let diverging_result = get_longest_common_path(&diverging_paths).unwrap();
        assert_eq!(diverging_result, "/");

        let uneven_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/"),
                                PathBuf::from("/tmp/logs/bye")];

        let uneven_result = get_longest_common_path(&uneven_paths).unwrap();
        assert_eq!(uneven_result, "/tmp/logs");
    }
}
<|endoftext|>


<commit_before>use cocoa;

export QuartzNativeFont, with_test_native_font;

import libc::size_t;
import ptr::null;
import unsafe::reinterpret_cast;
import result::{result, ok};
import glyph::GlyphIndex;
import cocoa::cg::{
    CGDataProviderRef,
    CGFontRef
};
import cocoa::cg::cg::{
    CGDataProviderCreateWithData,
    CGDataProviderRelease,
    CGFontCreateWithDataProvider,
    CGFontRelease
};

class QuartzNativeFont/& {
    let fontprov: CGDataProviderRef;
    let cgfont: CGFontRef;

    new (fontprov: CGDataProviderRef, cgfont: CGFontRef) {
        assert fontprov.is_not_null();
        assert cgfont.is_not_null();

        self.fontprov = fontprov;
        self.cgfont = cgfont;
    }

    drop {
        assert self.cgfont.is_not_null();
        assert self.fontprov.is_not_null();

        CGFontRelease(self.cgfont);
        CGDataProviderRelease(self.fontprov);
    }

    fn glyph_index(_codepoint: char) -> option<GlyphIndex> {
        fail;
    }

    // FIXME: What unit is this returning? Let's have a custom type
    fn glyph_h_advance(_glyph: GlyphIndex) -> option<int> {
        fail;
    }
}

fn create(buf: ~[u8]) -> result<QuartzNativeFont, ()> {
    let fontprov = vec::as_buf(buf, |cbuf, len| {
        CGDataProviderCreateWithData(
            null(),
            unsafe { reinterpret_cast(cbuf) },
            len as size_t,
            null())
    });
    // FIXME: Error handling
    assert fontprov.is_not_null();
    let cgfont = CGFontCreateWithDataProvider(fontprov);
    // FIXME: Error handling
    assert cgfont.is_not_null();

    return ok(QuartzNativeFont(fontprov, cgfont));
}

fn with_test_native_font(f: fn@(nf: &NativeFont)) {
    import font::test_font_bin;
    import unwrap_result = result::unwrap;

    let buf = test_font_bin();
    let res = create(buf);
    let font = unwrap_result(res);
    f(&font);
}
<commit_msg>Make QuartzNativeFont return placeholder values instead of failing<commit_after>use cocoa;

export QuartzNativeFont, with_test_native_font;

import libc::size_t;
import ptr::null;
import unsafe::reinterpret_cast;
import result::{result, ok};
import glyph::GlyphIndex;
import cocoa::cg::{
    CGDataProviderRef,
    CGFontRef
};
import cocoa::cg::cg::{
    CGDataProviderCreateWithData,
    CGDataProviderRelease,
    CGFontCreateWithDataProvider,
    CGFontRelease
};

class QuartzNativeFont/& {
    let fontprov: CGDataProviderRef;
    let cgfont: CGFontRef;

    new (fontprov: CGDataProviderRef, cgfont: CGFontRef) {
        assert fontprov.is_not_null();
        assert cgfont.is_not_null();

        self.fontprov = fontprov;
        self.cgfont = cgfont;
    }

    drop {
        assert self.cgfont.is_not_null();
        assert self.fontprov.is_not_null();

        CGFontRelease(self.cgfont);
        CGDataProviderRelease(self.fontprov);
    }

    fn glyph_index(_codepoint: char) -> option<GlyphIndex> {
        // FIXME
        some(40u)
    }

    // FIXME: What unit is this returning? Let's have a custom type
    fn glyph_h_advance(_glyph: GlyphIndex) -> option<int> {
        // FIXME
        some(15)
    }
}

fn create(buf: ~[u8]) -> result<QuartzNativeFont, ()> {
    let fontprov = vec::as_buf(buf, |cbuf, len| {
        CGDataProviderCreateWithData(
            null(),
            unsafe { reinterpret_cast(cbuf) },
            len as size_t,
            null())
    });
    // FIXME: Error handling
    assert fontprov.is_not_null();
    let cgfont = CGFontCreateWithDataProvider(fontprov);
    // FIXME: Error handling
    assert cgfont.is_not_null();

    return ok(QuartzNativeFont(fontprov, cgfont));
}

fn with_test_native_font(f: fn@(nf: &NativeFont)) {
    import font::test_font_bin;
    import unwrap_result = result::unwrap;

    let buf = test_font_bin();
    let res = create(buf);
    let font = unwrap_result(res);
    f(&font);
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate "termbox-sys" as termbox;

pub use self::running::running;
pub use self::style::{Style, RB_BOLD, RB_UNDERLINE, RB_REVERSE, RB_NORMAL};

use std::error::Error;
use std::fmt;
use std::kinds::marker;
use std::time::duration::Duration;

use termbox::RawEvent;
use libc::{c_int, c_uint};

#[deriving(Copy)]
pub enum Event {
    KeyEvent(u8, u16, u32),
    ResizeEvent(i32, i32),
    NoEvent
}

#[deriving(Copy, PartialEq)]
#[repr(C,u16)]
pub enum Color {
    Default =  0x00,
    Black =    0x01,
    Red =      0x02,
    Green =    0x03,
    Yellow =   0x04,
    Blue =     0x05,
    Magenta =  0x06,
    Cyan =     0x07,
    White =    0x08,
}

mod style {
    bitflags! {
        #[repr(C)]
        flags Style: u16 {
            const TB_NORMAL_COLOR = 0x000F,
            const RB_BOLD = 0x0100,
            const RB_UNDERLINE = 0x0200,
            const RB_REVERSE = 0x0400,
            const RB_NORMAL = 0x0000,
            const TB_ATTRIB = RB_BOLD.bits | RB_UNDERLINE.bits | RB_REVERSE.bits,
        }
    }

    impl Style {
        pub fn from_color(color: super::Color) -> Style {
            Style { bits: color as u16 & TB_NORMAL_COLOR.bits }
        }
    }
}

const NIL_RAW_EVENT: RawEvent = RawEvent { etype: 0, emod: 0, key: 0, ch: 0, w: 0, h: 0 };

// FIXME: Rust doesn't support this enum representation.
// #[deriving(Copy,FromPrimitive,Show)]
// #[repr(C,int)]
// pub enum EventErrorKind {
//     Error = -1,
// }
// pub type EventError = Option<EventErrorKind>;
#[allow(non_snake_case)]
pub mod EventErrorKind {
    #[deriving(Copy,Show)]
    pub struct Error;
}

pub type EventError = Option<EventErrorKind::Error>;

pub type EventResult<T> = Result<T, EventError>;

impl Error for EventError {
    fn description(&self) -> &str {
        match *self {
            // TODO: Check errno here
            Some(EventErrorKind::Error) => "Unknown error.",
            None => "Unexpected return code."
        }
    }
}

fn unpack_event(ev_type: c_int, ev: &RawEvent) -> EventResult<Event> {
    match ev_type {
        0 => Ok(Event::NoEvent),
        1 => Ok(Event::KeyEvent(ev.emod, ev.key, ev.ch)),
        2 => Ok(Event::ResizeEvent(ev.w, ev.h)),
        // FIXME: Rust doesn't support this error representation
        // res => FromPrimitive::from_int(res as int),
        -1 => Err(Some(EventErrorKind::Error)),
        _ => Err(None)
    }
}

#[deriving(Copy,FromPrimitive,Show)]
#[repr(C,int)]
pub enum InitErrorKind {
    UnsupportedTerminal = -1,
    FailedToOpenTty = -2,
    PipeTrapError = -3,
}

pub enum InitError {
    Opt(InitOption, Option<Box<Error>>),
    AlreadyOpen,
    TermBox(Option<InitErrorKind>),
}

impl fmt::Show for InitError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}", self.description())
    }
}

impl Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::Opt(InitOption::BufferStderr, _) => "Could not redirect stderr.",
            InitError::AlreadyOpen => "RustBox is already open.",
            InitError::TermBox(e) => e.map_or("Unexpected TermBox return code.", |e| match e {
                InitErrorKind::UnsupportedTerminal => "Unsupported terminal.",
                InitErrorKind::FailedToOpenTty => "Failed to open TTY.",
                InitErrorKind::PipeTrapError => "Pipe trap error.",
            }),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            InitError::Opt(_, Some(ref e)) => Some(&**e),
            _ => None
        }
    }
}

mod running {
    use std::sync::atomic::{mod, AtomicBool};

    // The state of the RustBox is protected by the lock.  Yay, global state!
    static RUSTBOX_RUNNING: AtomicBool = atomic::INIT_ATOMIC_BOOL;

    /// true iff RustBox is currently running.  Beware of races here--don't rely on this for anything
    /// critical unless you happen to know that RustBox cannot change state when it is called (a good
    /// usecase would be checking to see if it's worth risking double printing backtraces to avoid
    /// having them swallowed up by RustBox).
    pub fn running() -> bool {
        RUSTBOX_RUNNING.load(atomic::SeqCst)
    }

    // Internal RAII guard used to ensure we release the running lock whenever we acquire it.
    #[allow(missing_copy_implementations)]
    pub struct RunningGuard(());

    pub fn run() -> Option<RunningGuard> {
        // Ensure that we are not already running and simultaneously set RUSTBOX_RUNNING using an
        // atomic swap.  This ensures that contending threads don't trample each other.
        if RUSTBOX_RUNNING.swap(true, atomic::SeqCst) {
            // The Rustbox was already running.
            None
        } else {
            // The RustBox was not already running, and now we have the lock.
            Some(RunningGuard(()))
        }
    }

    impl Drop for RunningGuard {
        fn drop(&mut self) {
            // Indicate that we're free now.  We could probably get away with lower atomicity here,
            // but there's no reason to take that chance.
            RUSTBOX_RUNNING.store(false, atomic::SeqCst);
        }
    }
}

// RAII guard for input redirection
#[cfg(unix)]
mod redirect {
    use std::error::Error;

    use libc;
    use std::io::{util, IoError, PipeStream};
    use std::io::pipe::PipePair;
    use std::os::unix::AsRawFd;
    use super::{InitError, InitOption, RustBox};

    pub struct Redirect {
        pair: PipePair,
        fd: PipeStream,
    }

    impl Drop for Redirect {
        fn drop(&mut self) {
            // We make sure that we never actually create the Redirect without also putting it in a
            // RustBox.  This means that we know that this will always be dropped immediately after
            // the RustBox is destroyed.  We rely on destructor order here: destructors are always
            // executed top-down, so as long as this is included above the RunningGuard in the
            // RustBox struct, we can be confident that it is destroyed while we're still holding
            // onto the lock.

            unsafe {
                let old_fd = self.pair.writer.as_raw_fd();
                let new_fd = self.fd.as_raw_fd();
                // Reopen new_fd as writer.
                // (Note that if we fail here, we can't really do anything about it, so just ignore any
                // errors).
                if libc::dup2(old_fd, new_fd) != new_fd { return }
            }
            // Copy from reader to writer.
            drop(util::copy(&mut self.pair.reader, &mut self.pair.writer));
        }
    }

    fn redirect(new: PipeStream) -> Result<Redirect, Option<Box<Error>>> {
        // Create a pipe pair.
        let mut pair = try!(PipeStream::pair().map_err( |e| Some(box e as Box<Error>)));
        unsafe {
            let new_fd = new.as_raw_fd();
            // Copy new_fd to dup_fd.
            let dup_fd = match libc::dup(new_fd) {
                -1 => return Err(Some(box IoError::last_error() as Box<Error>)),
                fd => try!(PipeStream::open(fd).map_err( |e| Some(box e as Box<Error>))),
            };
            // Reopen new_fd as writer.
            let old_fd = pair.writer.as_raw_fd();
            let fd = libc::dup2(old_fd, new_fd);
            if fd == new_fd {
                // On success, the new file descriptor should be returned.  Replace the old one
                // with dup_fd, since we no longer need an explicit reference to the writer.
                pair.writer = dup_fd;
                Ok(Redirect {
                    pair: pair,
                    fd: new,
                })
            } else {
                Err(if fd == -1 { Some(box IoError::last_error() as Box<Error>) } else { None })
            }
        }
    }

    // The reason we take the rb reference is mostly to make sure we don't try to redirect before
    // the TermBox is set up.  Otherwise it is too easy to leave the file handles in a bad state.
    pub fn redirect_stderr(rb: &mut RustBox) -> Result<(), InitError> {
        match rb.stderr {
            Some(_) => {
                // Can only redirect once.
                Err(InitError::Opt(InitOption::BufferStderr, None))
            },
            None => {
                rb.stderr = Some(try!(redirect(
                            try!(PipeStream::open(libc::STDERR_FILENO)
                                 .map_err( |e| InitError::Opt(InitOption::BufferStderr,
                                                              Some(box e as Box<Error>)))))
                        .map_err( |e| InitError::Opt(InitOption::BufferStderr, e))));
                Ok(())
            }
        }
    }
}

#[cfg(not(unix))]
// Not sure how we'll do this on Windows, unimplemented for now.
mod redirect {
    pub enum Redirect { }

    pub fn redirect_stderr(_: &mut super::RustBox) -> Result<(), super::InitError> {
        Err(super::InitError::Opt(super::InitOption::BufferStderr, None))
    }
}

#[allow(missing_copy_implementations)]
pub struct RustBox {
    // Termbox is not thread safe
    no_sync: marker::NoSync,

    // We only bother to redirect stderr for the moment, since it's used for panic!
    stderr: Option<redirect::Redirect>,

    // RAII lock.
    //
    // Note that running *MUST* be the last field in the destructor, since destructors run in
    // top-down order.  Otherwise it will not properly protect the above fields.
    _running: running::RunningGuard,
}

#[deriving(Copy,Show)]
pub enum InitOption {
    /// Use this option to automatically buffer stderr while RustBox is running.  It will be
    /// written when RustBox exits.
    BufferStderr,
}

impl RustBox {
    pub fn init(opts: &[Option<InitOption>]) -> Result<RustBox, InitError> {
        // Acquire RAII lock.  This might seem like overkill, but it is easy to forget to release
        // it in the maze of error conditions below.
        let running = match running::run() {
            Some(r) => r,
            None => return Err(InitError::AlreadyOpen)
        };
        // Create the RustBox.
        let mut rb = unsafe {
            match termbox::tb_init() {
                0 => RustBox {
                    no_sync: marker::NoSync,
                    stderr: None,
                    _running: running,
                },
                res => {
                    return Err(InitError::TermBox(FromPrimitive::from_int(res as int)))
                }
            }
        };
        // Time to check our options.
        for opt in opts.iter().filter_map(|&opt| opt) {
            match opt {
                InitOption::BufferStderr => try!(redirect::redirect_stderr(&mut rb)),
            }
        }
        Ok(rb)
    }

    pub fn width(&self) -> uint {
        unsafe { termbox::tb_width() as uint }
    }

    pub fn height(&self) -> uint {
        unsafe { termbox::tb_height() as uint }
    }

    pub fn clear(&self) {
        unsafe { termbox::tb_clear() }
    }

    pub fn present(&self) {
        unsafe { termbox::tb_present() }
    }

    pub fn set_cursor(&self, x: int, y: int) {
        unsafe { termbox::tb_set_cursor(x as c_int, y as c_int) }
    }

    // Unsafe because u8 is not guaranteed to be a UTF-8 character
    pub unsafe fn change_cell(&self, x: uint, y: uint, ch: u32, fg: u16, bg: u16) {
        termbox::tb_change_cell(x as c_uint, y as c_uint, ch, fg, bg)
    }

    pub fn print(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, s: &str) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        for (i, ch) in s.chars().enumerate() {
            unsafe {
                self.change_cell(x+i, y, ch as u32, fg.bits(), bg.bits());
            }
        }
    }

    pub fn print_char(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, ch: char) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        unsafe {
            self.change_cell(x, y, ch as u32, fg.bits(), bg.bits());
        }
    }

    pub fn poll_event(&self) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_poll_event(&ev as *const RawEvent)
        };
        unpack_event(rc, &ev)
    }

    pub fn peek_event(&self, timeout: Duration) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_peek_event(&ev as *const RawEvent, timeout.num_milliseconds() as c_uint)
        };
        unpack_event(rc, &ev)
    }
}

impl Drop for RustBox {
    fn drop(&mut self) {
        // Since only one instance of the RustBox is ever accessible, we should not
        // need to do this atomically.
        // Note: we should definitely have RUSTBOX_RUNNING = true here.
        unsafe {
            termbox::tb_shutdown();
        }
    }
}
<commit_msg>Make the new stderr pipe nonblocking.<commit_after>extern crate libc;
extern crate "termbox-sys" as termbox;

pub use self::running::running;
pub use self::style::{Style, RB_BOLD, RB_UNDERLINE, RB_REVERSE, RB_NORMAL};

use std::error::Error;
use std::fmt;
use std::kinds::marker;
use std::time::duration::Duration;

use termbox::RawEvent;
use libc::{c_int, c_uint};

#[deriving(Copy)]
pub enum Event {
    KeyEvent(u8, u16, u32),
    ResizeEvent(i32, i32),
    NoEvent
}

#[deriving(Copy, PartialEq)]
#[repr(C,u16)]
pub enum Color {
    Default =  0x00,
    Black =    0x01,
    Red =      0x02,
    Green =    0x03,
    Yellow =   0x04,
    Blue =     0x05,
    Magenta =  0x06,
    Cyan =     0x07,
    White =    0x08,
}

mod style {
    bitflags! {
        #[repr(C)]
        flags Style: u16 {
            const TB_NORMAL_COLOR = 0x000F,
            const RB_BOLD = 0x0100,
            const RB_UNDERLINE = 0x0200,
            const RB_REVERSE = 0x0400,
            const RB_NORMAL = 0x0000,
            const TB_ATTRIB = RB_BOLD.bits | RB_UNDERLINE.bits | RB_REVERSE.bits,
        }
    }

    impl Style {
        pub fn from_color(color: super::Color) -> Style {
            Style { bits: color as u16 & TB_NORMAL_COLOR.bits }
        }
    }
}

const NIL_RAW_EVENT: RawEvent = RawEvent { etype: 0, emod: 0, key: 0, ch: 0, w: 0, h: 0 };

// FIXME: Rust doesn't support this enum representation.
// #[deriving(Copy,FromPrimitive,Show)]
// #[repr(C,int)]
// pub enum EventErrorKind {
//     Error = -1,
// }
// pub type EventError = Option<EventErrorKind>;
#[allow(non_snake_case)]
pub mod EventErrorKind {
    #[deriving(Copy,Show)]
    pub struct Error;
}

pub type EventError = Option<EventErrorKind::Error>;

pub type EventResult<T> = Result<T, EventError>;

impl Error for EventError {
    fn description(&self) -> &str {
        match *self {
            // TODO: Check errno here
            Some(EventErrorKind::Error) => "Unknown error.",
            None => "Unexpected return code."
        }
    }
}

fn unpack_event(ev_type: c_int, ev: &RawEvent) -> EventResult<Event> {
    match ev_type {
        0 => Ok(Event::NoEvent),
        1 => Ok(Event::KeyEvent(ev.emod, ev.key, ev.ch)),
        2 => Ok(Event::ResizeEvent(ev.w, ev.h)),
        // FIXME: Rust doesn't support this error representation
        // res => FromPrimitive::from_int(res as int),
        -1 => Err(Some(EventErrorKind::Error)),
        _ => Err(None)
    }
}

#[deriving(Copy,FromPrimitive,Show)]
#[repr(C,int)]
pub enum InitErrorKind {
    UnsupportedTerminal = -1,
    FailedToOpenTty = -2,
    PipeTrapError = -3,
}

pub enum InitError {
    Opt(InitOption, Option<Box<Error>>),
    AlreadyOpen,
    TermBox(Option<InitErrorKind>),
}

impl fmt::Show for InitError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}", self.description())
    }
}

impl Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::Opt(InitOption::BufferStderr, _) => "Could not redirect stderr.",
            InitError::AlreadyOpen => "RustBox is already open.",
            InitError::TermBox(e) => e.map_or("Unexpected TermBox return code.", |e| match e {
                InitErrorKind::UnsupportedTerminal => "Unsupported terminal.",
                InitErrorKind::FailedToOpenTty => "Failed to open TTY.",
                InitErrorKind::PipeTrapError => "Pipe trap error.",
            }),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            InitError::Opt(_, Some(ref e)) => Some(&**e),
            _ => None
        }
    }
}

mod running {
    use std::sync::atomic::{mod, AtomicBool};

    // The state of the RustBox is protected by the lock.  Yay, global state!
    static RUSTBOX_RUNNING: AtomicBool = atomic::INIT_ATOMIC_BOOL;

    /// true iff RustBox is currently running.  Beware of races here--don't rely on this for anything
    /// critical unless you happen to know that RustBox cannot change state when it is called (a good
    /// usecase would be checking to see if it's worth risking double printing backtraces to avoid
    /// having them swallowed up by RustBox).
    pub fn running() -> bool {
        RUSTBOX_RUNNING.load(atomic::SeqCst)
    }

    // Internal RAII guard used to ensure we release the running lock whenever we acquire it.
    #[allow(missing_copy_implementations)]
    pub struct RunningGuard(());

    pub fn run() -> Option<RunningGuard> {
        // Ensure that we are not already running and simultaneously set RUSTBOX_RUNNING using an
        // atomic swap.  This ensures that contending threads don't trample each other.
        if RUSTBOX_RUNNING.swap(true, atomic::SeqCst) {
            // The Rustbox was already running.
            None
        } else {
            // The RustBox was not already running, and now we have the lock.
            Some(RunningGuard(()))
        }
    }

    impl Drop for RunningGuard {
        fn drop(&mut self) {
            // Indicate that we're free now.  We could probably get away with lower atomicity here,
            // but there's no reason to take that chance.
            RUSTBOX_RUNNING.store(false, atomic::SeqCst);
        }
    }
}

// RAII guard for input redirection
#[cfg(unix)]
mod redirect {
    use std::error::Error;

    use libc;
    use std::io::{util, IoError, PipeStream};
    use std::io::pipe::PipePair;
    use std::os::unix::AsRawFd;
    use super::{InitError, InitOption, RustBox};

    pub struct Redirect {
        pair: PipePair,
        fd: PipeStream,
    }

    impl Drop for Redirect {
        fn drop(&mut self) {
            // We make sure that we never actually create the Redirect without also putting it in a
            // RustBox.  This means that we know that this will always be dropped immediately after
            // the RustBox is destroyed.  We rely on destructor order here: destructors are always
            // executed top-down, so as long as this is included above the RunningGuard in the
            // RustBox struct, we can be confident that it is destroyed while we're still holding
            // onto the lock.

            unsafe {
                let old_fd = self.pair.writer.as_raw_fd();
                let new_fd = self.fd.as_raw_fd();
                // Reopen new_fd as writer.
                // (Note that if we fail here, we can't really do anything about it, so just ignore any
                // errors).
                if libc::dup2(old_fd, new_fd) != new_fd { return }
            }
            // Copy from reader to writer.
            drop(util::copy(&mut self.pair.reader, &mut self.pair.writer));
        }
    }

    fn redirect(new: PipeStream) -> Result<Redirect, Option<Box<Error>>> {
        // Create a pipe pair.
        let mut pair = try!(PipeStream::pair().map_err( |e| Some(box e as Box<Error>)));
        unsafe {
            let new_fd = new.as_raw_fd();
            // Copy new_fd to dup_fd.
            let dup_fd = match libc::dup(new_fd) {
                -1 => return Err(Some(box IoError::last_error() as Box<Error>)),
                fd => try!(PipeStream::open(fd).map_err( |e| Some(box e as Box<Error>))),
            };
            // Make the writer nonblocking.  This means that even if the stderr pipe fills up,
            // exceptions from stack traces will not block the program.  Unfortunately, if this
            // does happen stderr outputwill be lost until RustBox exits.
            let old_fd = pair.writer.as_raw_fd();
            let res = libc::fcntl(old_fd, libc::F_SETFL, libc::O_NONBLOCK);
            if res != 0 {
                return Err(if res == -1 {
                    Some(box IoError::last_error() as Box<Error>)
                } else { None }) // This should really never happen, but no reason to unwind here.
            }
            // Reopen new_fd as writer.
            let fd = libc::dup2(old_fd, new_fd);
            if fd == new_fd {
                // On success, the new file descriptor should be returned.  Replace the old one
                // with dup_fd, since we no longer need an explicit reference to the writer.
                pair.writer = dup_fd;
                Ok(Redirect {
                    pair: pair,
                    fd: new,
                })
            } else {
                Err(if fd == -1 { Some(box IoError::last_error() as Box<Error>) } else { None })
            }
        }
    }

    // The reason we take the rb reference is mostly to make sure we don't try to redirect before
    // the TermBox is set up.  Otherwise it is too easy to leave the file handles in a bad state.
    pub fn redirect_stderr(rb: &mut RustBox) -> Result<(), InitError> {
        match rb.stderr {
            Some(_) => {
                // Can only redirect once.
                Err(InitError::Opt(InitOption::BufferStderr, None))
            },
            None => {
                rb.stderr = Some(try!(redirect(
                            try!(PipeStream::open(libc::STDERR_FILENO)
                                 .map_err( |e| InitError::Opt(InitOption::BufferStderr,
                                                              Some(box e as Box<Error>)))))
                        .map_err( |e| InitError::Opt(InitOption::BufferStderr, e))));
                Ok(())
            }
        }
    }
}

#[cfg(not(unix))]
// Not sure how we'll do this on Windows, unimplemented for now.
mod redirect {
    pub enum Redirect { }

    pub fn redirect_stderr(_: &mut super::RustBox) -> Result<(), super::InitError> {
        Err(super::InitError::Opt(super::InitOption::BufferStderr, None))
    }
}

#[allow(missing_copy_implementations)]
pub struct RustBox {
    // Termbox is not thread safe
    no_sync: marker::NoSync,

    // We only bother to redirect stderr for the moment, since it's used for panic!
    stderr: Option<redirect::Redirect>,

    // RAII lock.
    //
    // Note that running *MUST* be the last field in the destructor, since destructors run in
    // top-down order.  Otherwise it will not properly protect the above fields.
    _running: running::RunningGuard,
}

#[deriving(Copy,Show)]
pub enum InitOption {
    /// Use this option to automatically buffer stderr while RustBox is running.  It will be
    /// written when RustBox exits.
    ///
    /// This option uses a nonblocking OS pipe to buffer stderr output.  This means that if the
    /// pipe fills up, subsequent writes will fail until RustBox exits.  If this is a concern for
    /// your program, don't use RustBox's default pipe-based redirection; instead, redirect stderr
    /// to a log file or another process that is capable of handling it better.
    BufferStderr,
}

impl RustBox {
    pub fn init(opts: &[Option<InitOption>]) -> Result<RustBox, InitError> {
        // Acquire RAII lock.  This might seem like overkill, but it is easy to forget to release
        // it in the maze of error conditions below.
        let running = match running::run() {
            Some(r) => r,
            None => return Err(InitError::AlreadyOpen)
        };
        // Create the RustBox.
        let mut rb = unsafe {
            match termbox::tb_init() {
                0 => RustBox {
                    no_sync: marker::NoSync,
                    stderr: None,
                    _running: running,
                },
                res => {
                    return Err(InitError::TermBox(FromPrimitive::from_int(res as int)))
                }
            }
        };
        // Time to check our options.
        for opt in opts.iter().filter_map(|&opt| opt) {
            match opt {
                InitOption::BufferStderr => try!(redirect::redirect_stderr(&mut rb)),
            }
        }
        Ok(rb)
    }

    pub fn width(&self) -> uint {
        unsafe { termbox::tb_width() as uint }
    }

    pub fn height(&self) -> uint {
        unsafe { termbox::tb_height() as uint }
    }

    pub fn clear(&self) {
        unsafe { termbox::tb_clear() }
    }

    pub fn present(&self) {
        unsafe { termbox::tb_present() }
    }

    pub fn set_cursor(&self, x: int, y: int) {
        unsafe { termbox::tb_set_cursor(x as c_int, y as c_int) }
    }

    // Unsafe because u8 is not guaranteed to be a UTF-8 character
    pub unsafe fn change_cell(&self, x: uint, y: uint, ch: u32, fg: u16, bg: u16) {
        termbox::tb_change_cell(x as c_uint, y as c_uint, ch, fg, bg)
    }

    pub fn print(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, s: &str) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        for (i, ch) in s.chars().enumerate() {
            unsafe {
                self.change_cell(x+i, y, ch as u32, fg.bits(), bg.bits());
            }
        }
    }

    pub fn print_char(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, ch: char) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        unsafe {
            self.change_cell(x, y, ch as u32, fg.bits(), bg.bits());
        }
    }

    pub fn poll_event(&self) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_poll_event(&ev as *const RawEvent)
        };
        unpack_event(rc, &ev)
    }

    pub fn peek_event(&self, timeout: Duration) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_peek_event(&ev as *const RawEvent, timeout.num_milliseconds() as c_uint)
        };
        unpack_event(rc, &ev)
    }
}

impl Drop for RustBox {
    fn drop(&mut self) {
        // Since only one instance of the RustBox is ever accessible, we should not
        // need to do this atomically.
        // Note: we should definitely have RUSTBOX_RUNNING = true here.
        unsafe {
            termbox::tb_shutdown();
        }
    }
}
<|endoftext|>