<commit_before>//! EGL context related structs
use std::collections::HashSet;
use std::os::raw::c_int;
use std::sync::atomic::Ordering;

use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::allocator::Format as DrmFormat;
use crate::backend::egl::display::{EGLDisplay, PixelFormat};
use crate::backend::egl::EGLSurface;

use slog::{info, o, trace};

/// EGL context for rendering
#[derive(Debug)]
pub struct EGLContext {
    context: ffi::egl::types::EGLContext,
    pub(crate) display: EGLDisplay,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: Option<PixelFormat>,
}
// EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {}

impl EGLContext {
    /// Creates a new configless `EGLContext` from a given `EGLDisplay`
    pub fn new<L>(display: &EGLDisplay, log: L) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, None, log)
    }

    /// Create a new [`EGLContext`] from a given `EGLDisplay` and configuration requirements
    pub fn new_with_config<L>(
        display: &EGLDisplay,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, Some((attributes, reqs)), log)
    }

    /// Create a new configless `EGLContext` from a given `EGLDisplay` sharing resources with another context
    pub fn new_shared<L>(display: &EGLDisplay, share: &EGLContext, log: L) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some(share), None, log)
    }

    /// Create a new `EGLContext` from a given `EGLDisplay` and configuration requirements sharing resources with another context
    pub fn new_shared_with_config<L>(
        display: &EGLDisplay,
        share: &EGLContext,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some(share), Some((attributes, reqs)), log)
    }

    fn new_internal<L>(
        display: &EGLDisplay,
        shared: Option<&EGLContext>,
        config: Option<(GlAttributes, PixelFormatRequirements)>,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "backend_egl"));

        let (pixel_format, config_id) = match config {
            Some((attributes, reqs)) => {
                let (format, config_id) = display.choose_config(attributes, reqs)?;
                (Some(format), config_id)
            }
            None => {
                if !display
                    .extensions
                    .iter()
                    .any(|x| x == "EGL_KHR_no_config_context")
                    && !display
                        .extensions
                        .iter()
                        .any(|x| x == "EGL_MESA_configless_context")
                    && !display
                        .extensions
                        .iter()
                        .any(|x| x == "EGL_KHR_surfaceless_context")
                {
                    return Err(Error::EglExtensionNotSupported(&[
                        "EGL_KHR_no_config_context",
                        "EGL_MESA_configless_context",
                        "EGL_KHR_surfaceless_context",
                    ]));
                }
                (None, ffi::egl::NO_CONFIG_KHR)
            }
        };

        let mut context_attributes = Vec::with_capacity(10);

        if let Some((attributes, _)) = config {
            let version = attributes.version;

            if display.egl_version >= (1, 5)
                || display.extensions.iter().any(|s| s == "EGL_KHR_create_context")
            {
                trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
                context_attributes.push(version.0 as i32);
                trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
                context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
                context_attributes.push(version.1 as i32);

                if attributes.debug && display.egl_version >= (1, 5) {
                    trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
                    context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
                    context_attributes.push(ffi::egl::TRUE as i32);
                }

                context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
                context_attributes.push(0);
            } else if display.egl_version >= (1, 3) {
                trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
                context_attributes.push(version.0 as i32);
            }
        } else {
            trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
            context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
            context_attributes.push(2);
        }

        context_attributes.push(ffi::egl::NONE as i32);

        trace!(log, "Creating EGL context...");
        let context = wrap_egl_call(|| unsafe {
            ffi::egl::CreateContext(
                **display.display,
                config_id,
                shared
                    .map(|context| context.context)
                    .unwrap_or(ffi::egl::NO_CONTEXT),
                context_attributes.as_ptr(),
            )
        })
        .map_err(Error::CreationFailed)?;

        info!(log, "EGL context created");

        Ok(EGLContext {
            context,
            display: display.clone(),
            config_id,
            pixel_format,
        })
    }

    /// Makes the OpenGL context the current context in the current thread with a surface to
    /// read/write to.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads without being unbound again (see `unbind`).
    pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError> {
        let surface_ptr = surface.surface.load(Ordering::SeqCst);
        wrap_egl_call(|| {
            ffi::egl::MakeCurrent(**self.display.display, surface_ptr, surface_ptr, self.context)
        })
        .map(|_| ())
        .map_err(Into::into)
    }

    /// Makes the OpenGL context the current context in the current thread with no surface bound.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads without being unbound again (see `unbind`).
    pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
        wrap_egl_call(|| {
            ffi::egl::MakeCurrent(
                **self.display.display,
                ffi::egl::NO_SURFACE,
                ffi::egl::NO_SURFACE,
                self.context,
            )
        })
        .map(|_| ())
        .map_err(Into::into)
    }

    /// Returns true if the OpenGL context is the current one in the thread.
    pub fn is_current(&self) -> bool {
        unsafe { ffi::egl::GetCurrentContext() == self.context as *const _ }
    }

    /// Returns the egl config for this context
    pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
        self.config_id
    }

    /// Returns the pixel format of the main framebuffer of the context.
    pub fn pixel_format(&self) -> Option<PixelFormat> {
        self.pixel_format
    }

    /// Unbinds this context from the current thread, if set.
    ///
    /// This does nothing if this context is not the current context.
    pub fn unbind(&self) -> Result<(), MakeCurrentError> {
        if self.is_current() {
            wrap_egl_call(|| unsafe {
                ffi::egl::MakeCurrent(
                    **self.display.display,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_CONTEXT,
                )
            })?;
        }
        Ok(())
    }

    /// Returns a list of formats for dmabufs that can be rendered to.
    pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> {
        &self.display.dmabuf_render_formats
    }

    /// Returns a list of formats for dmabufs that can be used as textures.
    pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {
        &self.display.dmabuf_import_formats
    }
}

impl Drop for EGLContext {
    fn drop(&mut self) {
        unsafe {
            // We need to ensure the context is unbound, otherwise it egl stalls the destroy call
            // ignore failures at this point
            let _ = self.unbind();
            ffi::egl::DestroyContext(**self.display.display, self.context);
        }
    }
}

/// Attributes to use when creating an OpenGL context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlAttributes {
    /// Describes the OpenGL API and version that are being requested when a context is created.
    ///
    /// `(3, 0)` will request a OpenGL ES 3.0 context for example.
    /// `(2, 0)` is the minimum.
    pub version: (u8, u8),
    /// OpenGL profile to use.
    pub profile: Option<GlProfile>,
    /// Whether to enable the debug flag of the context.
    ///
    /// Debug contexts are usually slower but give better error reporting.
    pub debug: bool,
    /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will block until the screen refreshes.
    /// This is typically used to prevent screen tearing.
    pub vsync: bool,
}

/// Describes the requested OpenGL context profiles.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
    /// Include all the immediate functions and definitions.
    Compatibility,
    /// Include all the future-compatible functions and definitions.
    Core,
}

/// Describes how the backend should choose a pixel format.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormatRequirements {
    /// If `true`, only hardware-accelerated formats will be considered. If `false`, only software renderers.
    /// `None` means "don't care". Default is `None`.
    pub hardware_accelerated: Option<bool>,
    /// Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care". The default is `None`.
    pub color_bits: Option<u8>,
    /// If `true`, the color buffer must be in a floating point format. Default is `false`.
    ///
    /// Using floating points allows you to write values outside the `[0.0, 1.0]` range.
    pub float_color_buffer: bool,
    /// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`.
    pub alpha_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub depth_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub stencil_bits: Option<u8>,
    /// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
    /// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled.
    pub multisampling: Option<u16>,
}

impl Default for PixelFormatRequirements {
    fn default() -> Self {
        PixelFormatRequirements {
            hardware_accelerated: Some(true),
            color_bits: Some(24),
            float_color_buffer: false,
            alpha_bits: Some(8),
            depth_bits: Some(24),
            stencil_bits: Some(8),
            multisampling: None,
        }
    }
}

impl PixelFormatRequirements {
    /// Append the requirements to the given attribute list
    pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) {
        if let Some(hardware_accelerated) = self.hardware_accelerated {
            out.push(ffi::egl::CONFIG_CAVEAT as c_int);
            out.push(if hardware_accelerated {
                trace!(logger, "Setting CONFIG_CAVEAT to NONE");
                ffi::egl::NONE as c_int
            } else {
                trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
                ffi::egl::SLOW_CONFIG as c_int
            });
        }

        if let Some(color) = self.color_bits {
            trace!(logger, "Setting RED_SIZE to {}", color / 3);
            out.push(ffi::egl::RED_SIZE as c_int);
            out.push((color / 3) as c_int);
            trace!(
                logger,
                "Setting GREEN_SIZE to {}",
                color / 3 + if color % 3 != 0 { 1 } else { 0 }
            );
            out.push(ffi::egl::GREEN_SIZE as c_int);
            out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
            trace!(
                logger,
                "Setting BLUE_SIZE to {}",
                color / 3 + if color % 3 == 2 { 1 } else { 0 }
            );
            out.push(ffi::egl::BLUE_SIZE as c_int);
            out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
        }

        if let Some(alpha) = self.alpha_bits {
            trace!(logger, "Setting ALPHA_SIZE to {}", alpha);
            out.push(ffi::egl::ALPHA_SIZE as c_int);
            out.push(alpha as c_int);
        }

        if let Some(depth) = self.depth_bits {
            trace!(logger, "Setting DEPTH_SIZE to {}", depth);
            out.push(ffi::egl::DEPTH_SIZE as c_int);
            out.push(depth as c_int);
        }

        if let Some(stencil) = self.stencil_bits {
            trace!(logger, "Setting STENCIL_SIZE to {}", stencil);
            out.push(ffi::egl::STENCIL_SIZE as c_int);
            out.push(stencil as c_int);
        }

        if let Some(multisampling) = self.multisampling {
            trace!(logger, "Setting SAMPLES to {}", multisampling);
            out.push(ffi::egl::SAMPLES as c_int);
            out.push(multisampling as c_int);
        }
    }
}
<commit_msg>egl: Add `UserDataMap` to `EGLContext`<commit_after>//! EGL context related structs
use std::{
    collections::HashSet,
    os::raw::c_int,
    sync::{atomic::Ordering, Arc},
};

use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::{
    backend::{
        allocator::Format as DrmFormat,
        egl::{
            display::{EGLDisplay, PixelFormat},
            EGLSurface,
        },
    },
    utils::user_data::UserDataMap,
};

use slog::{info, o, trace};

/// EGL context for rendering
#[derive(Debug)]
pub struct EGLContext {
    context: ffi::egl::types::EGLContext,
    pub(crate) display: EGLDisplay,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: Option<PixelFormat>,
    user_data: Arc<UserDataMap>,
}
// EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {}

impl EGLContext {
    /// Creates a new configless `EGLContext` from a given `EGLDisplay`
    pub fn new<L>(display: &EGLDisplay, log: L) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, None, log)
    }

    /// Create a new [`EGLContext`] from a given `EGLDisplay` and configuration requirements
    pub fn new_with_config<L>(
        display: &EGLDisplay,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, Some((attributes, reqs)), log)
    }

    /// Create a new configless `EGLContext` from a given `EGLDisplay` sharing resources with another context
    pub fn new_shared<L>(display: &EGLDisplay, share: &EGLContext, log: L) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some(share), None, log)
    }

    /// Create a new `EGLContext` from a given `EGLDisplay` and configuration requirements sharing resources with another context
    pub fn new_shared_with_config<L>(
        display: &EGLDisplay,
        share: &EGLContext,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some(share), Some((attributes, reqs)), log)
    }

    fn new_internal<L>(
        display: &EGLDisplay,
        shared: Option<&EGLContext>,
        config: Option<(GlAttributes, PixelFormatRequirements)>,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "backend_egl"));

        let (pixel_format, config_id) = match config {
            Some((attributes, reqs)) => {
                let (format, config_id) = display.choose_config(attributes, reqs)?;
                (Some(format), config_id)
            }
            None => {
                if !display
                    .extensions
                    .iter()
                    .any(|x| x == "EGL_KHR_no_config_context")
                    && !display
                        .extensions
                        .iter()
                        .any(|x| x == "EGL_MESA_configless_context")
                    && !display
                        .extensions
                        .iter()
                        .any(|x| x == "EGL_KHR_surfaceless_context")
                {
                    return Err(Error::EglExtensionNotSupported(&[
                        "EGL_KHR_no_config_context",
                        "EGL_MESA_configless_context",
                        "EGL_KHR_surfaceless_context",
                    ]));
                }
                (None, ffi::egl::NO_CONFIG_KHR)
            }
        };

        let mut context_attributes = Vec::with_capacity(10);

        if let Some((attributes, _)) = config {
            let version = attributes.version;

            if display.egl_version >= (1, 5)
                || display.extensions.iter().any(|s| s == "EGL_KHR_create_context")
            {
                trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
                context_attributes.push(version.0 as i32);
                trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
                context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
                context_attributes.push(version.1 as i32);

                if attributes.debug && display.egl_version >= (1, 5) {
                    trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
                    context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
                    context_attributes.push(ffi::egl::TRUE as i32);
                }

                context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
                context_attributes.push(0);
            } else if display.egl_version >= (1, 3) {
                trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
                context_attributes.push(version.0 as i32);
            }
        } else {
            trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
            context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
            context_attributes.push(2);
        }

        context_attributes.push(ffi::egl::NONE as i32);

        trace!(log, "Creating EGL context...");
        let context = wrap_egl_call(|| unsafe {
            ffi::egl::CreateContext(
                **display.display,
                config_id,
                shared
                    .map(|context| context.context)
                    .unwrap_or(ffi::egl::NO_CONTEXT),
                context_attributes.as_ptr(),
            )
        })
        .map_err(Error::CreationFailed)?;

        info!(log, "EGL context created");

        Ok(EGLContext {
            context,
            display: display.clone(),
            config_id,
            pixel_format,
            user_data: if let Some(shared) = shared {
                shared.user_data.clone()
            } else {
                Arc::new(UserDataMap::default())
            },
        })
    }

    /// Makes the OpenGL context the current context in the current thread with a surface to
    /// read/write to.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads without being unbound again (see `unbind`).
    pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError> {
        let surface_ptr = surface.surface.load(Ordering::SeqCst);
        wrap_egl_call(|| {
            ffi::egl::MakeCurrent(**self.display.display, surface_ptr, surface_ptr, self.context)
        })
        .map(|_| ())
        .map_err(Into::into)
    }

    /// Makes the OpenGL context the current context in the current thread with no surface bound.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads without being unbound again (see `unbind`).
    pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
        wrap_egl_call(|| {
            ffi::egl::MakeCurrent(
                **self.display.display,
                ffi::egl::NO_SURFACE,
                ffi::egl::NO_SURFACE,
                self.context,
            )
        })
        .map(|_| ())
        .map_err(Into::into)
    }

    /// Returns true if the OpenGL context is the current one in the thread.
    pub fn is_current(&self) -> bool {
        unsafe { ffi::egl::GetCurrentContext() == self.context as *const _ }
    }

    /// Returns the egl config for this context
    pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
        self.config_id
    }

    /// Returns the pixel format of the main framebuffer of the context.
    pub fn pixel_format(&self) -> Option<PixelFormat> {
        self.pixel_format
    }

    /// Unbinds this context from the current thread, if set.
    ///
    /// This does nothing if this context is not the current context.
    pub fn unbind(&self) -> Result<(), MakeCurrentError> {
        if self.is_current() {
            wrap_egl_call(|| unsafe {
                ffi::egl::MakeCurrent(
                    **self.display.display,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_CONTEXT,
                )
            })?;
        }
        Ok(())
    }

    /// Returns a list of formats for dmabufs that can be rendered to.
    pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> {
        &self.display.dmabuf_render_formats
    }

    /// Returns a list of formats for dmabufs that can be used as textures.
    pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {
        &self.display.dmabuf_import_formats
    }

    /// Retrieve user_data associated with this context
    ///
    /// *Note:* UserData is shared between shared context, if constructed with
    /// [`new_shared`](EGLContext::new_shared) or [`new_shared_with_config`](EGLContext::new_shared_with_config).
    pub fn user_data(&self) -> &UserDataMap {
        &*self.user_data
    }
}

impl Drop for EGLContext {
    fn drop(&mut self) {
        unsafe {
            // We need to ensure the context is unbound, otherwise it egl stalls the destroy call
            // ignore failures at this point
            let _ = self.unbind();
            ffi::egl::DestroyContext(**self.display.display, self.context);
        }
    }
}

/// Attributes to use when creating an OpenGL context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlAttributes {
    /// Describes the OpenGL API and version that are being requested when a context is created.
    ///
    /// `(3, 0)` will request a OpenGL ES 3.0 context for example.
    /// `(2, 0)` is the minimum.
    pub version: (u8, u8),
    /// OpenGL profile to use.
    pub profile: Option<GlProfile>,
    /// Whether to enable the debug flag of the context.
    ///
    /// Debug contexts are usually slower but give better error reporting.
    pub debug: bool,
    /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will block until the screen refreshes.
    /// This is typically used to prevent screen tearing.
    pub vsync: bool,
}

/// Describes the requested OpenGL context profiles.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
    /// Include all the immediate functions and definitions.
    Compatibility,
    /// Include all the future-compatible functions and definitions.
    Core,
}

/// Describes how the backend should choose a pixel format.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormatRequirements {
    /// If `true`, only hardware-accelerated formats will be considered. If `false`, only software renderers.
    /// `None` means "don't care". Default is `None`.
    pub hardware_accelerated: Option<bool>,
    /// Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care". The default is `None`.
    pub color_bits: Option<u8>,
    /// If `true`, the color buffer must be in a floating point format. Default is `false`.
    ///
    /// Using floating points allows you to write values outside the `[0.0, 1.0]` range.
    pub float_color_buffer: bool,
    /// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`.
    pub alpha_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub depth_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub stencil_bits: Option<u8>,
    /// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
    /// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled.
    pub multisampling: Option<u16>,
}

impl Default for PixelFormatRequirements {
    fn default() -> Self {
        PixelFormatRequirements {
            hardware_accelerated: Some(true),
            color_bits: Some(24),
            float_color_buffer: false,
            alpha_bits: Some(8),
            depth_bits: Some(24),
            stencil_bits: Some(8),
            multisampling: None,
        }
    }
}

impl PixelFormatRequirements {
    /// Append the requirements to the given attribute list
    pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) {
        if let Some(hardware_accelerated) = self.hardware_accelerated {
            out.push(ffi::egl::CONFIG_CAVEAT as c_int);
            out.push(if hardware_accelerated {
                trace!(logger, "Setting CONFIG_CAVEAT to NONE");
                ffi::egl::NONE as c_int
            } else {
                trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
                ffi::egl::SLOW_CONFIG as c_int
            });
        }

        if let Some(color) = self.color_bits {
            trace!(logger, "Setting RED_SIZE to {}", color / 3);
            out.push(ffi::egl::RED_SIZE as c_int);
            out.push((color / 3) as c_int);
            trace!(
                logger,
                "Setting GREEN_SIZE to {}",
                color / 3 + if color % 3 != 0 { 1 } else { 0 }
            );
            out.push(ffi::egl::GREEN_SIZE as c_int);
            out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
            trace!(
                logger,
                "Setting BLUE_SIZE to {}",
                color / 3 + if color % 3 == 2 { 1 } else { 0 }
            );
            out.push(ffi::egl::BLUE_SIZE as c_int);
            out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
        }

        if let Some(alpha) = self.alpha_bits {
            trace!(logger, "Setting ALPHA_SIZE to {}", alpha);
            out.push(ffi::egl::ALPHA_SIZE as c_int);
            out.push(alpha as c_int);
        }

        if let Some(depth) = self.depth_bits {
            trace!(logger, "Setting DEPTH_SIZE to {}", depth);
            out.push(ffi::egl::DEPTH_SIZE as c_int);
            out.push(depth as c_int);
        }

        if let Some(stencil) = self.stencil_bits {
            trace!(logger, "Setting STENCIL_SIZE to {}", stencil);
            out.push(ffi::egl::STENCIL_SIZE as c_int);
            out.push(stencil as c_int);
        }

        if let Some(multisampling) = self.multisampling {
            trace!(logger, "Setting SAMPLES to {}", multisampling);
            out.push(ffi::egl::SAMPLES as c_int);
            out.push(multisampling as c_int);
        }
    }
}
<|endoftext|>


<commit_before>//! Reads input from a wav file and mirrors it to a different wav file.
//!
//! The destination wav file should be an exact duplicate of the source.

extern crate oxcable;

#[cfg(not(test))]
fn main() {
    use oxcable::chain::{DeviceChain, Tick};
    use oxcable::io::wav::{WavReader, WavWriter};
    use oxcable::types::AudioDevice;

    println!("Initializing signal chain...");
    let inwav = WavReader::open("wav/song.wav").unwrap();
    let samples = inwav.get_num_samples();
    let outwav = WavWriter::create("wav/test_wav.wav", inwav.num_outputs()).unwrap();
    let mut chain = DeviceChain::from(inwav).into(outwav);

    println!("Mirroring wav/song.wav input to wav/test_wav.wav...");
    chain.tick_n_times(samples);
    println!("Done!");
}
<commit_msg>Remove test wav bin<commit_after><|endoftext|>


<commit_before>//! # Virtual File System
//!
//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
//! always returns the same contents, unless VFS was explicitly modified with
//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via
//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
//! triggers incremental recomputation.
//!
//! Files in VFS are identified with [`FileId`]s -- interned paths. The notion of
//! the path, [`VfsPath`] is somewhat abstract: at the moment, it is represented
//! as an [`std::path::PathBuf`] internally, but this is an implementation detail.
//!
//! VFS doesn't do IO or file watching itself. For that, see the [`loader`]
//! module. [`loader::Handle`] is an object-safe trait which abstracts both file
//! loading and file watching. [`Handle`] is dynamically configured with a set of
//! directory entries which should be scanned and watched. [`Handle`] then
//! asynchronously pushes file changes. Directory entries are configured in
//! free-form via list of globs, it's up to the [`Handle`] to interpret the globs
//! in any specific way.
//!
//! VFS stores a flat list of files. [`file_set::FileSet`] can partition this list
//! of files into disjoint sets of files. Traversal-like operations (including
//! getting the neighbor file by the relative path) are handled by the [`FileSet`].
//! [`FileSet`]s are also pushed to salsa and cause it to re-check `mod foo;`
//! declarations when files are created or deleted.
//!
//! [`FileSet`] and [`loader::Entry`] play similar, but different roles.
//! Both specify the "set of paths/files", one is geared towards file watching,
//! the other towards salsa changes. In particular, single [`FileSet`]
//! may correspond to several [`loader::Entry`]. For example, a crate from
//! crates.io which uses code generation would have two [`Entries`] -- for sources
//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
//! have a single [`FileSet`] which unions the two sources.
//!
//! [`set_file_contents`]: Vfs::set_file_contents
//! [`take_changes`]: Vfs::take_changes
//! [`FileSet`]: file_set::FileSet
//! [`Handle`]: loader::Handle
//! [`Entries`]: loader::Entry
mod anchored_path;
pub mod file_set;
pub mod loader;
mod path_interner;
mod vfs_path;

use std::{fmt, mem};

use crate::path_interner::PathInterner;

pub use crate::{
    anchored_path::{AnchoredPath, AnchoredPathBuf},
    vfs_path::VfsPath,
};
pub use paths::{AbsPath, AbsPathBuf};

/// Handle to a file in [`Vfs`]
///
/// Most functions in rust-analyzer use this when they need to refer to a file.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FileId(pub u32);

/// Storage for all files read by rust-analyzer.
///
/// For more informations see the [crate-level](crate) documentation.
#[derive(Default)]
pub struct Vfs {
    interner: PathInterner,
    data: Vec<Option<Vec<u8>>>,
    changes: Vec<ChangedFile>,
}

/// Changed file in the [`Vfs`].
pub struct ChangedFile {
    /// Id of the changed file
    pub file_id: FileId,
    /// Kind of change
    pub change_kind: ChangeKind,
}

impl ChangedFile {
    /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete).
    pub fn exists(&self) -> bool {
        self.change_kind != ChangeKind::Delete
    }

    /// Returns `true` if the change is [`Create`](ChangeKind::Create) or
    /// [`Delete`](ChangeKind::Delete).
    pub fn is_created_or_deleted(&self) -> bool {
        matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
    }
}

/// Kind of [file change](ChangedFile).
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum ChangeKind {
    /// The file was (re-)created
    Create,
    /// The file was modified
    Modify,
    /// The file was deleted
    Delete,
}

impl Vfs {
    /// Amount of files currently stored.
    ///
    /// Note that this includes deleted files.
    pub fn len(&self) -> usize {
        self.data.len()
    }

    /// Id of the given path if it exists in the `Vfs` and is not deleted.
    pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
        let it = self.interner.get(path)?;
        let _ = self.get(it).as_ref()?;
        Some(it)
    }

    /// File path corresponding to the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if the id is not present in the `Vfs`.
    pub fn file_path(&self, file_id: FileId) -> VfsPath {
        self.interner.lookup(file_id).clone()
    }

    /// File content corresponding to the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if the id is not present in the `Vfs`, or if the corresponding file is
    /// deleted.
    pub fn file_contents(&self, file_id: FileId) -> &[u8] {
        self.get(file_id).as_deref().unwrap()
    }

    /// Returns an iterator over the stored ids and their corresponding paths.
    ///
    /// This will skip deleted files.
    pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
        (0..self.data.len())
            .map(|it| FileId(it as u32))
            .filter(move |&file_id| self.get(file_id).is_some())
            .map(move |file_id| {
                let path = self.interner.lookup(file_id);
                (file_id, path)
            })
    }

    /// Update the `path` with the given `contents`. `None` means the file was deleted.
    ///
    /// Returns `true` if the file was modified, and saves the [change](ChangedFile).
    ///
    /// If the path does not currently exists in the `Vfs`, allocates a new
    /// [`FileId`] for it.
    pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
        let file_id = self.alloc_file_id(path);
        let change_kind = match (&self.get(file_id), &contents) {
            (None, None) => return false,
            (None, Some(_)) => ChangeKind::Create,
            (Some(_), None) => ChangeKind::Delete,
            (Some(old), Some(new)) if old == new => return false,
            (Some(_), Some(_)) => ChangeKind::Modify,
        };

        *self.get_mut(file_id) = contents;
        self.changes.push(ChangedFile { file_id, change_kind });
        true
    }

    /// Returns `true` if the `Vfs` contains [changes](ChangedFile).
    pub fn has_changes(&self) -> bool {
        !self.changes.is_empty()
    }

    /// Drain and returns all the changes in the `Vfs`.
    pub fn take_changes(&mut self) -> Vec<ChangedFile> {
        mem::take(&mut self.changes)
    }

    /// Returns the id associated with `path`
    ///
    /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a
    /// deleted file;
    /// - Else, returns `path`'s id.
    ///
    /// Does not record a change.
    fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
        let file_id = self.interner.intern(path);
        let idx = file_id.0 as usize;
        let len = self.data.len().max(idx + 1);
        self.data.resize_with(len, || None);
        file_id
    }

    /// Returns the content associated with the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if no file is associated to that id.
    fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
        &self.data[file_id.0 as usize]
    }

    /// Mutably returns the content associated with the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if no file is associated to that id.
    fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
        &mut self.data[file_id.0 as usize]
    }
}

impl fmt::Debug for Vfs {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Vfs").field("n_files", &self.data.len()).finish()
    }
}
<commit_msg>Refactor: use `filter_map` when iterating over stored IDs and<commit_after>//! # Virtual File System
//!
//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
//! always returns the same contents, unless VFS was explicitly modified with
//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via
//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
//! triggers incremental recomputation.
//!
//! Files in VFS are identified with [`FileId`]s -- interned paths. The notion of
//! the path, [`VfsPath`] is somewhat abstract: at the moment, it is represented
//! as an [`std::path::PathBuf`] internally, but this is an implementation detail.
//!
//! VFS doesn't do IO or file watching itself. For that, see the [`loader`]
//! module. [`loader::Handle`] is an object-safe trait which abstracts both file
//! loading and file watching. [`Handle`] is dynamically configured with a set of
//! directory entries which should be scanned and watched. [`Handle`] then
//! asynchronously pushes file changes. Directory entries are configured in
//! free-form via list of globs, it's up to the [`Handle`] to interpret the globs
//! in any specific way.
//!
//! VFS stores a flat list of files. [`file_set::FileSet`] can partition this list
//! of files into disjoint sets of files. Traversal-like operations (including
//! getting the neighbor file by the relative path) are handled by the [`FileSet`].
//! [`FileSet`]s are also pushed to salsa and cause it to re-check `mod foo;`
//! declarations when files are created or deleted.
//!
//! [`FileSet`] and [`loader::Entry`] play similar, but different roles.
//! Both specify the "set of paths/files", one is geared towards file watching,
//! the other towards salsa changes. In particular, single [`FileSet`]
//! may correspond to several [`loader::Entry`]. For example, a crate from
//! crates.io which uses code generation would have two [`Entries`] -- for sources
//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
//! have a single [`FileSet`] which unions the two sources.
//!
//! [`set_file_contents`]: Vfs::set_file_contents
//! [`take_changes`]: Vfs::take_changes
//! [`FileSet`]: file_set::FileSet
//! [`Handle`]: loader::Handle
//! [`Entries`]: loader::Entry
mod anchored_path;
pub mod file_set;
pub mod loader;
mod path_interner;
mod vfs_path;

use std::{fmt, mem};

use crate::path_interner::PathInterner;

pub use crate::{
    anchored_path::{AnchoredPath, AnchoredPathBuf},
    vfs_path::VfsPath,
};
pub use paths::{AbsPath, AbsPathBuf};

/// Handle to a file in [`Vfs`]
///
/// Most functions in rust-analyzer use this when they need to refer to a file.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FileId(pub u32);

/// Storage for all files read by rust-analyzer.
///
/// For more informations see the [crate-level](crate) documentation.
#[derive(Default)]
pub struct Vfs {
    interner: PathInterner,
    data: Vec<Option<Vec<u8>>>,
    changes: Vec<ChangedFile>,
}

/// Changed file in the [`Vfs`].
pub struct ChangedFile {
    /// Id of the changed file
    pub file_id: FileId,
    /// Kind of change
    pub change_kind: ChangeKind,
}

impl ChangedFile {
    /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete).
    pub fn exists(&self) -> bool {
        self.change_kind != ChangeKind::Delete
    }

    /// Returns `true` if the change is [`Create`](ChangeKind::Create) or
    /// [`Delete`](ChangeKind::Delete).
    pub fn is_created_or_deleted(&self) -> bool {
        matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
    }
}

/// Kind of [file change](ChangedFile).
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum ChangeKind {
    /// The file was (re-)created
    Create,
    /// The file was modified
    Modify,
    /// The file was deleted
    Delete,
}

impl Vfs {
    /// Amount of files currently stored.
    ///
    /// Note that this includes deleted files.
    pub fn len(&self) -> usize {
        self.data.len()
    }

    /// Id of the given path if it exists in the `Vfs` and is not deleted.
    pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
        let it = self.interner.get(path)?;
        let _ = self.get(it).as_ref()?;
        Some(it)
    }

    /// File path corresponding to the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if the id is not present in the `Vfs`.
    pub fn file_path(&self, file_id: FileId) -> VfsPath {
        self.interner.lookup(file_id).clone()
    }

    /// File content corresponding to the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if the id is not present in the `Vfs`, or if the corresponding file is
    /// deleted.
    pub fn file_contents(&self, file_id: FileId) -> &[u8] {
        self.get(file_id).as_deref().unwrap()
    }

    /// Returns an iterator over the stored ids and their corresponding paths.
    ///
    /// This will skip deleted files.
    pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
        (0..self.data.len()).filter_map(move |it| {
            let file_id = FileId(it as u32);
            let _ = self.get(file_id).as_ref()?;
            let path = self.interner.lookup(file_id);
            Some((file_id, path))
        })
    }

    /// Update the `path` with the given `contents`. `None` means the file was deleted.
    ///
    /// Returns `true` if the file was modified, and saves the [change](ChangedFile).
    ///
    /// If the path does not currently exists in the `Vfs`, allocates a new
    /// [`FileId`] for it.
    pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
        let file_id = self.alloc_file_id(path);
        let change_kind = match (&self.get(file_id), &contents) {
            (None, None) => return false,
            (None, Some(_)) => ChangeKind::Create,
            (Some(_), None) => ChangeKind::Delete,
            (Some(old), Some(new)) if old == new => return false,
            (Some(_), Some(_)) => ChangeKind::Modify,
        };

        *self.get_mut(file_id) = contents;
        self.changes.push(ChangedFile { file_id, change_kind });
        true
    }

    /// Returns `true` if the `Vfs` contains [changes](ChangedFile).
    pub fn has_changes(&self) -> bool {
        !self.changes.is_empty()
    }

    /// Drain and returns all the changes in the `Vfs`.
    pub fn take_changes(&mut self) -> Vec<ChangedFile> {
        mem::take(&mut self.changes)
    }

    /// Returns the id associated with `path`
    ///
    /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a
    /// deleted file;
    /// - Else, returns `path`'s id.
    ///
    /// Does not record a change.
    fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
        let file_id = self.interner.intern(path);
        let idx = file_id.0 as usize;
        let len = self.data.len().max(idx + 1);
        self.data.resize_with(len, || None);
        file_id
    }

    /// Returns the content associated with the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if no file is associated to that id.
    fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
        &self.data[file_id.0 as usize]
    }

    /// Mutably returns the content associated with the given `file_id`.
    ///
    /// # Panics
    ///
    /// Panics if no file is associated to that id.
    fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
        &mut self.data[file_id.0 as usize]
    }
}

impl fmt::Debug for Vfs {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Vfs").field("n_files", &self.data.len()).finish()
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate web_view;
extern crate clap;
extern crate proton;

#[cfg(feature = "prod")]
#[macro_use]
extern crate rouille;

#[cfg(feature = "dev")]
use clap::{Arg, App};

#[cfg(feature = "prod")]
use std::thread;

mod cmd;

include!(concat!(env!("OUT_DIR"), "/data.rs"));

fn main() {
    let debug;
    let content;
    let _matches: clap::ArgMatches;

    #[cfg(feature = "dev")]
    {
        let app = App::new("app")
            .version("1.0.0")
            .author("Author")
            .about("About")
            .arg(Arg::with_name("url")
                .short("u")
                .long("url")
                .value_name("URL")
                .help("Loads the specified URL into webview")
                .required(true)
                .takes_value(true)
            );

        _matches = app.get_matches();
        content = web_view::Content::Url(_matches.value_of("url").unwrap());
        debug = true;
    }
    #[cfg(feature="prod")]
    {
        if let Some(available_port) = proton::tcp::get_available_port() {
            let server_url = format!("{}:{}", "0.0.0.0", available_port);
            content = web_view::Content::Url(format!("http://{}", server_url));
            debug = cfg!(debug_assertions);

            thread::spawn(move || {
                rouille::start_server(server_url, move |request| {
                    router!(request,
                        (GET) (/) => {
                            rouille::Response::html(String::from_utf8(ASSETS.get("../dist/proton/UnPackaged/index.html").unwrap().into_owned()).unwrap())
                        },

                        (GET) (/js/{id: String}) => {
                            rouille::Response::text(String::from_utf8(ASSETS.get(&format!("../dist/proton/UnPackaged/js/{}", id)).unwrap().into_owned()).unwrap())
                        },

                        (GET) (/css/{id: String}) => {
                            rouille::Response::from_data("text/css;charset=utf-8", ASSETS.get(&format!("../dist/proton/UnPackaged/css/{}", id)).unwrap().into_owned())
                        },

                        // webpack managed assets
                        (GET) (/img/{id: String}) => {
                            rouille::Response::from_data("application/octet-stream", ASSETS.get(&format!("../dist/proton/UnPackaged/img/{}", id)).unwrap().into_owned())
                        },

                        // static assets
                        (GET) (/statics/{id: String}) => {
                            rouille::Response::from_data("application/octet-stream", ASSETS.get(&format!("../dist/proton/UnPackaged/statics/{}", id)).unwrap().into_owned())
                        },

                        (GET) (/fonts/{id: String}) => {
                            rouille::Response::from_data("application/octet-stream", ASSETS.get(&format!("../dist/proton/UnPackaged/fonts/{}", id)).unwrap().into_owned())
                        },

                        _ => rouille::Response::empty_404()
                    )
                });
            });
        }
        else
        {
            panic!("Could not find an open port");
        }
    }

    let webview = web_view::builder()
        .title("MyAppTitle")
        .content(content)
        .size(800, 600) // TODO:Resolution is fixed right now, change this later to be dynamic
        .resizable(true)
        .debug(debug)
        .user_data(())
        .invoke_handler(|_webview, arg| {
            use cmd::Cmd::*;
            match serde_json::from_str(arg).unwrap() {
                Init => (),
                ReadAsString { path, callback, error } => {
                    proton::file_system::read_text_file(_webview, path, callback, error);
                }
                ReadAsBinary { path, callback, error } => {
                    proton::file_system::read_binary_file(_webview, path, callback, error);
                }
                Write { file, contents, callback, error } => {
                    proton::file_system::write_file(_webview, file, contents, callback, error);
                }
                ListDirs { path, callback, error } => {
                    proton::file_system::list_dirs(_webview, path, callback, error);

                }
                List { path, callback, error } => {
                    proton::file_system::list(_webview, path, callback, error);
                }
                SetTitle { title } => {
                    _webview.set_title(&title).unwrap();
                }
                Call { command, args, callback, error } => {
                    proton::command::call(_webview, command, args, callback, error);
                }
            }
            Ok(())
        })
        .build().unwrap();

    webview.run().unwrap();
}
<commit_msg>feat(proton) properly serve images (#4563)<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate web_view;
extern crate clap;
extern crate proton;

#[cfg(feature = "prod")]
#[macro_use]
extern crate rouille;

#[cfg(feature = "dev")]
use clap::{Arg, App};

#[cfg(feature = "prod")]
use std::thread;

mod cmd;

include!(concat!(env!("OUT_DIR"), "/data.rs"));

fn main() {
    let debug;
    let content;
    let _matches: clap::ArgMatches;

    #[cfg(feature = "dev")]
    {
        let app = App::new("app")
            .version("1.0.0")
            .author("Author")
            .about("About")
            .arg(Arg::with_name("url")
                .short("u")
                .long("url")
                .value_name("URL")
                .help("Loads the specified URL into webview")
                .required(true)
                .takes_value(true)
            );

        _matches = app.get_matches();
        content = web_view::Content::Url(_matches.value_of("url").unwrap());
        debug = true;
    }
    #[cfg(feature="prod")]
    {
        if let Some(available_port) = proton::tcp::get_available_port() {
            let server_url = format!("{}:{}", "0.0.0.0", available_port);
            content = web_view::Content::Url(format!("http://{}", server_url));
            debug = cfg!(debug_assertions);

            thread::spawn(move || {
                rouille::start_server(server_url, move |request| {
                    let url = request.url();
                    if url.starts_with("/statics/") || url.starts_with("/img/")
                    {
                        let asset = ASSETS.get(&format!("../dist/proton/UnPackaged{}", url)).unwrap().into_owned();
                        if url.ends_with(".svg")
                        {
                            rouille::Response::svg(String::from_utf8(asset).unwrap())
                        }
                        else
                        {
                            rouille::Response::from_data("application/octet-stream", asset)
                        }
                    }
                    else
                    {
                        router!(request,
                            (GET) (/) => {
                                rouille::Response::html(String::from_utf8(ASSETS.get("../dist/proton/UnPackaged/index.html").unwrap().into_owned()).unwrap())
                            },

                            (GET) (/js/{id: String}) => {
                                rouille::Response::text(String::from_utf8(ASSETS.get(&format!("../dist/proton/UnPackaged/js/{}", id)).unwrap().into_owned()).unwrap())
                            },

                            (GET) (/css/{id: String}) => {
                                rouille::Response::from_data("text/css;charset=utf-8", ASSETS.get(&format!("../dist/proton/UnPackaged/css/{}", id)).unwrap().into_owned())
                            },

                            (GET) (/fonts/{id: String}) => {
                                rouille::Response::from_data("application/octet-stream", ASSETS.get(&format!("../dist/proton/UnPackaged/fonts/{}", id)).unwrap().into_owned())
                            },

                            _ => rouille::Response::empty_404()
                    )
                    }
                });
            });
        }
        else
        {
            panic!("Could not find an open port");
        }
    }

    let webview = web_view::builder()
        .title("MyAppTitle")
        .content(content)
        .size(800, 600) // TODO:Resolution is fixed right now, change this later to be dynamic
        .resizable(true)
        .debug(debug)
        .user_data(())
        .invoke_handler(|_webview, arg| {
            use cmd::Cmd::*;
            match serde_json::from_str(arg).unwrap() {
                Init => (),
                ReadAsString { path, callback, error } => {
                    proton::file_system::read_text_file(_webview, path, callback, error);
                }
                ReadAsBinary { path, callback, error } => {
                    proton::file_system::read_binary_file(_webview, path, callback, error);
                }
                Write { file, contents, callback, error } => {
                    proton::file_system::write_file(_webview, file, contents, callback, error);
                }
                ListDirs { path, callback, error } => {
                    proton::file_system::list_dirs(_webview, path, callback, error);

                }
                List { path, callback, error } => {
                    proton::file_system::list(_webview, path, callback, error);
                }
                SetTitle { title } => {
                    _webview.set_title(&title).unwrap();
                }
                Call { command, args, callback, error } => {
                    proton::command::call(_webview, command, args, callback, error);
                }
            }
            Ok(())
        })
        .build().unwrap();

    webview.run().unwrap();
}
<|endoftext|>


<commit_before>use std::cmp;
use std::iter;
use std::time::{Instant, Duration};

use core::shell::Verbosity;
use util::{Config, CargoResult};

pub struct Progress<'cfg> {
    state: Option<State<'cfg>>,
}

struct State<'cfg> {
    config: &'cfg Config,
    width: usize,
    first: bool,
    last_update: Instant,
    name: String,
    done: bool,
}

impl<'cfg> Progress<'cfg> {
    pub fn new(name: &str, cfg: &'cfg Config) -> Progress<'cfg> {
        // no progress if `-q` is passed as, well, we're supposed to be quiet
        if cfg.shell().verbosity() == Verbosity::Quiet {
            return Progress { state: None }
        }

        Progress {
            state: cfg.shell().err_width().map(|n| {
                State {
                    config: cfg,
                    width: cmp::min(n, 80),
                    first: true,
                    last_update: Instant::now(),
                    name: name.to_string(),
                    done: false,
                }
            }),
        }
    }

    pub fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
        match self.state {
            Some(ref mut s) => s.tick(cur, max),
            None => Ok(())
        }
    }
}

impl<'cfg> State<'cfg> {
    fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
        if self.done {
            return Ok(())
        }

        // Don't update too often as it can cause excessive performance loss
        // just putting stuff onto the terminal. We also want to avoid
        // flickering by not drawing anything that goes away too quickly. As a
        // result we've got two branches here:
        //
        // 1. If we haven't drawn anything, we wait for a period of time to
        //    actually start drawing to the console. This ensures that
        //    short-lived operations don't flicker on the console. Currently
        //    there's a 500ms delay to when we first draw something.
        // 2. If we've drawn something, then we rate limit ourselves to only
        //    draw to the console every so often. Currently there's a 100ms
        //    delay between updates.
        if self.first {
            let delay = Duration::from_millis(500);
            if self.last_update.elapsed() < delay {
                return Ok(())
            }
            self.first = false;
        } else {
            let interval = Duration::from_millis(100);
            if self.last_update.elapsed() < interval {
                return Ok(())
            }
        }
        self.last_update = Instant::now();

        // Render the percentage at the far right and then figure how long the
        // progress bar is
        let pct = (cur as f64) / (max as f64);
        let pct = if !pct.is_finite() { 0.0 } else { pct };
        let stats = format!(" {:6.02}%", pct * 100.0);
        let extra_len = stats.len() + 2 /* [ and ] */ + 15 /* status header */;
        let display_width = match self.width.checked_sub(extra_len) {
            Some(n) => n,
            None => return Ok(()),
        };
        let mut string = String::from("[");
        let hashes = display_width as f64 * pct;
        let hashes = hashes as usize;

        // Draw the `===>`
        if hashes > 0 {
            for _ in 0..hashes-1 {
                string.push_str("=");
            }
            if cur == max {
                self.done = true;
                string.push_str("=");
            } else {
                string.push_str(">");
            }
        }

        // Draw the empty space we have left to do
        for _ in 0..(display_width - hashes) {
            string.push_str(" ");
        }
        string.push_str("]");
        string.push_str(&stats);

        // Write out a pretty header, then the progress bar itself, and then
        // return back to the beginning of the line for the next print.
        self.config.shell().status_header(&self.name)?;
        write!(self.config.shell().err(), "{}\r", string)?;
        Ok(())
    }
}

fn clear(width: usize, config: &Config) {
    let blank = iter::repeat(" ").take(width).collect::<String>();
    drop(write!(config.shell().err(), "{}\r", blank));
}

impl<'cfg> Drop for State<'cfg> {
    fn drop(&mut self) {
        clear(self.width, self.config);
    }
}
<commit_msg>Auto merge of #4832 - andreastt:dumb_terminal_progress, r=alexcrichton<commit_after>use std::cmp;
use std::env;
use std::iter;
use std::time::{Instant, Duration};

use core::shell::Verbosity;
use util::{Config, CargoResult};

pub struct Progress<'cfg> {
    state: Option<State<'cfg>>,
}

struct State<'cfg> {
    config: &'cfg Config,
    width: usize,
    first: bool,
    last_update: Instant,
    name: String,
    done: bool,
}

impl<'cfg> Progress<'cfg> {
    pub fn new(name: &str, cfg: &'cfg Config) -> Progress<'cfg> {
        // report no progress when -q (for quiet) or TERM=dumb are set
        let dumb = match env::var("TERM") {
            Ok(term) => term == "dumb",
            Err(_) => false,
        };
        if cfg.shell().verbosity() == Verbosity::Quiet || dumb {
            return Progress { state: None }
        }

        Progress {
            state: cfg.shell().err_width().map(|n| {
                State {
                    config: cfg,
                    width: cmp::min(n, 80),
                    first: true,
                    last_update: Instant::now(),
                    name: name.to_string(),
                    done: false,
                }
            }),
        }
    }

    pub fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
        match self.state {
            Some(ref mut s) => s.tick(cur, max),
            None => Ok(())
        }
    }
}

impl<'cfg> State<'cfg> {
    fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
        if self.done {
            return Ok(())
        }

        // Don't update too often as it can cause excessive performance loss
        // just putting stuff onto the terminal. We also want to avoid
        // flickering by not drawing anything that goes away too quickly. As a
        // result we've got two branches here:
        //
        // 1. If we haven't drawn anything, we wait for a period of time to
        //    actually start drawing to the console. This ensures that
        //    short-lived operations don't flicker on the console. Currently
        //    there's a 500ms delay to when we first draw something.
        // 2. If we've drawn something, then we rate limit ourselves to only
        //    draw to the console every so often. Currently there's a 100ms
        //    delay between updates.
        if self.first {
            let delay = Duration::from_millis(500);
            if self.last_update.elapsed() < delay {
                return Ok(())
            }
            self.first = false;
        } else {
            let interval = Duration::from_millis(100);
            if self.last_update.elapsed() < interval {
                return Ok(())
            }
        }
        self.last_update = Instant::now();

        // Render the percentage at the far right and then figure how long the
        // progress bar is
        let pct = (cur as f64) / (max as f64);
        let pct = if !pct.is_finite() { 0.0 } else { pct };
        let stats = format!(" {:6.02}%", pct * 100.0);
        let extra_len = stats.len() + 2 /* [ and ] */ + 15 /* status header */;
        let display_width = match self.width.checked_sub(extra_len) {
            Some(n) => n,
            None => return Ok(()),
        };
        let mut string = String::from("[");
        let hashes = display_width as f64 * pct;
        let hashes = hashes as usize;

        // Draw the `===>`
        if hashes > 0 {
            for _ in 0..hashes-1 {
                string.push_str("=");
            }
            if cur == max {
                self.done = true;
                string.push_str("=");
            } else {
                string.push_str(">");
            }
        }

        // Draw the empty space we have left to do
        for _ in 0..(display_width - hashes) {
            string.push_str(" ");
        }
        string.push_str("]");
        string.push_str(&stats);

        // Write out a pretty header, then the progress bar itself, and then
        // return back to the beginning of the line for the next print.
        self.config.shell().status_header(&self.name)?;
        write!(self.config.shell().err(), "{}\r", string)?;
        Ok(())
    }
}

fn clear(width: usize, config: &Config) {
    let blank = iter::repeat(" ").take(width).collect::<String>();
    drop(write!(config.shell().err(), "{}\r", blank));
}

impl<'cfg> Drop for State<'cfg> {
    fn drop(&mut self) {
        clear(self.width, self.config);
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "with-tremor")]
extern crate librespot_tremor as vorbis;
#[cfg(not(feature = "with-tremor"))]
extern crate vorbis;

use std::error;
use std::fmt;
use std::io::{Read, Seek};

pub struct VorbisDecoder<R: Read + Seek>(vorbis::Decoder<R>);
pub struct VorbisPacket(vorbis::Packet);
pub struct VorbisError(vorbis::VorbisError);

impl<R> VorbisDecoder<R>
where
    R: Read + Seek,
{
    pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
        Ok(VorbisDecoder(vorbis::Decoder::new(input)?))
    }

    #[cfg(not(feature = "with-tremor"))]
    pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
        self.0.time_seek(ms as f64 / 1000f64)?;
        Ok(())
    }

    #[cfg(feature = "with-tremor")]
    pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
        self.0.time_seek(ms)?;
        Ok(())
    }

    pub fn next_packet(&mut self) -> Result<Option<VorbisPacket>, VorbisError> {
        loop {
            match self.0.packets().next() {
                Some(Ok(packet)) => return Ok(Some(VorbisPacket(packet))),
                None => return Ok(None),

                Some(Err(vorbis::VorbisError::Hole)) => (),
                Some(Err(err)) => return Err(err.into()),
            }
        }
    }
}

impl VorbisPacket {
    pub fn data(&self) -> &[i16] {
        &self.0.data
    }

    pub fn data_mut(&mut self) -> &mut [i16] {
        &mut self.0.data
    }
}

impl From<vorbis::VorbisError> for VorbisError {
    fn from(err: vorbis::VorbisError) -> VorbisError {
        VorbisError(err)
    }
}

impl fmt::Debug for VorbisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

impl fmt::Display for VorbisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl error::Error for VorbisError {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}
<commit_msg>Remove depreciated error description method<commit_after>#[cfg(feature = "with-tremor")]
extern crate librespot_tremor as vorbis;
#[cfg(not(feature = "with-tremor"))]
extern crate vorbis;

use std::error;
use std::fmt;
use std::io::{Read, Seek};

pub struct VorbisDecoder<R: Read + Seek>(vorbis::Decoder<R>);
pub struct VorbisPacket(vorbis::Packet);
pub struct VorbisError(vorbis::VorbisError);

impl<R> VorbisDecoder<R>
where
    R: Read + Seek,
{
    pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
        Ok(VorbisDecoder(vorbis::Decoder::new(input)?))
    }

    #[cfg(not(feature = "with-tremor"))]
    pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
        self.0.time_seek(ms as f64 / 1000f64)?;
        Ok(())
    }

    #[cfg(feature = "with-tremor")]
    pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
        self.0.time_seek(ms)?;
        Ok(())
    }

    pub fn next_packet(&mut self) -> Result<Option<VorbisPacket>, VorbisError> {
        loop {
            match self.0.packets().next() {
                Some(Ok(packet)) => return Ok(Some(VorbisPacket(packet))),
                None => return Ok(None),

                Some(Err(vorbis::VorbisError::Hole)) => (),
                Some(Err(err)) => return Err(err.into()),
            }
        }
    }
}

impl VorbisPacket {
    pub fn data(&self) -> &[i16] {
        &self.0.data
    }

    pub fn data_mut(&mut self) -> &mut [i16] {
        &mut self.0.data
    }
}

impl From<vorbis::VorbisError> for VorbisError {
    fn from(err: vorbis::VorbisError) -> VorbisError {
        VorbisError(err)
    }
}

impl fmt::Debug for VorbisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

impl fmt::Display for VorbisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl error::Error for VorbisError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use std::mem;

use self::rand::Rng;

use Scalar;
use maths::{lcp_solvers, CrossProduct, DotProduct, Integrator, LCP, LCPSolver, Matrix, UnitVec3D, Vec3D};
use maths::integrators::SemiImplicitEuler;
use solvers::{ConstraintSolver, ImpulseSolver};
use entities::{BodyType, BodyTypeMut, RigidBody};
use detection::ContactEvent;

pub struct MachConstraintSolver;

impl MachConstraintSolver {
    pub fn new() -> MachConstraintSolver {
        MachConstraintSolver
    }

    fn formuate_lcp(time_step: Scalar, contacts: &Vec<ContactEvent>) -> (LCP, Vec<UnitVec3D>) {
        let number_of_contacts = contacts.len();
        let size = number_of_contacts * 2;
        let mut problem = LCP::new(size);
        let mut friction_directions: Vec<UnitVec3D> = Vec::new();

        for (i, contact_event) in contacts.iter().enumerate() {
            // TODO handle more than one contact point
            let contact_center = contact_event.point(0);
            let contact_normal = contact_event.normal();
            let body_0 = contact_event.bodies().0.borrow();
            let body_1 = contact_event.bodies().1.borrow();

            let (mu, mass_inverse, inertia_inverse, rel_vel, contact_offset) = match (body_0.downcast(), body_1.downcast()) {
                (BodyType::Rigid(rigid_body_0), BodyType::Rigid(rigid_body_1)) => {
                    let contact_offset_0 = contact_center - rigid_body_0.translation();
                    let contact_offset_1 = contact_center - rigid_body_1.translation();

                    let mu = rigid_body_0.friction_coefficient() * rigid_body_1.friction_coefficient();
                    let mass_inverse = (rigid_body_0.mass_inverse(), rigid_body_1.mass_inverse());
                    let inertia_inverse = (rigid_body_0.inertia_inverse(), rigid_body_1.inertia_inverse());

                    let rel_vel = rigid_body_0.velocity() - rigid_body_1.velocity() +
                        contact_offset_0.cross(rigid_body_0.angular_velocity().clone()) -
                        contact_offset_1.cross(rigid_body_1.angular_velocity().clone());

                    (mu, mass_inverse, inertia_inverse, rel_vel, (contact_offset_0, contact_offset_1))
                },

                (BodyType::Rigid(rigid_body), BodyType::Static(static_body)) => {
                    let contact_offset_0 = contact_center - rigid_body.translation();
                    let contact_offset_1 = contact_center - static_body.translation();

                    let mu = rigid_body.friction_coefficient() * static_body.friction_coefficient();
                    let mass_inverse = (rigid_body.mass_inverse(), 0.0);
                    let inertia_inverse = (rigid_body.inertia_inverse(), Matrix::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0));

                    let rel_vel = rigid_body.velocity() +
                        contact_offset_0.cross(rigid_body.angular_velocity().clone());

                    (mu, mass_inverse, inertia_inverse, rel_vel, (contact_offset_0, contact_offset_1))
                },

                _otherwise => panic!("unhandled body type combination!"),
            };

            mem::drop(body_0);
            mem::drop(body_1);

            let impulse = ImpulseSolver::compute_impulse_for_event(contact_event);
            let impulse = if impulse < 0.0 {
                0.0
            } else {
                impulse
            };
            let impulse_offset = number_of_contacts * i;
            let friction_offset = impulse_offset + 1;

            let generalized_mass_inverse = |vect: UnitVec3D| -> Vec3D {
                (mass_inverse.0 + mass_inverse.1) * vect +
                    contact_offset.0.cross(inertia_inverse.0 * contact_offset.0.cross(vect)) +
                    contact_offset.1.cross(inertia_inverse.1 * contact_offset.1.cross(vect))
            };

            let generalized_mass_inverse_norm = generalized_mass_inverse(contact_normal);

            // FRICTION
            let mut perpendicular_direction = rel_vel.cross(contact_normal).normalize();
            // ASSUMPTION: Non-finite length means that the relative velocity
            // and contact normal directions are aligned
            while !perpendicular_direction.squared_length().is_finite() {
                // pick any arbitrary direction to avoid the singularity when
                // relative velocity is aligned  with the contact normal
                let mut rng = rand::thread_rng();
                let guess = Vec3D::new(
                    rng.gen_range(-1.0, 1.0),
                    rng.gen_range(-1.0, 1.0),
                    rng.gen_range(-1.0, 1.0),
                );
                perpendicular_direction = rel_vel.cross(guess).normalize();
            };
            let friction_direction = -contact_normal.cross(perpendicular_direction);
            let generalized_mass_inverse_fric = generalized_mass_inverse(friction_direction);

            *problem.matrix_mut(friction_offset, friction_offset) = friction_direction.dot(generalized_mass_inverse_fric);
            *problem.matrix_mut(impulse_offset, friction_offset) = contact_normal.dot(generalized_mass_inverse_fric);
            *problem.matrix_mut(friction_offset, impulse_offset) = friction_direction.dot(generalized_mass_inverse_norm);

            *problem.unknown_mut(friction_offset) = time_step * mu * impulse;

            *problem.bias_mut(friction_offset) = friction_direction.dot(rel_vel);

            let max_friction_impulse = time_step * rel_vel.dot(friction_direction).abs();
            problem.add_value_constraint(friction_offset, Box::new(move |local_problem, value| {
                if value < 0.0 {
                    0.0
                } else {
                    let friction_limit_due_to_normal_impulse = mu * local_problem.solution(impulse_offset);
                    let friction_limit = if max_friction_impulse > friction_limit_due_to_normal_impulse {
                        friction_limit_due_to_normal_impulse
                    } else {
                        max_friction_impulse
                    };

                    if value > friction_limit {
                        friction_limit
                    } else {
                        value
                    }
                }
            }));

            friction_directions.push(friction_direction);

            // IMPULSE
            let impulse_diagonal = contact_normal.dot(generalized_mass_inverse_norm);
            *problem.matrix_mut(impulse_offset, impulse_offset) = impulse_diagonal;
            *problem.unknown_mut(impulse_offset) = time_step * impulse;
            // TODO this satisfies the constraints when friction is zero, but
            // is it theoretically valid?
            *problem.bias_mut(impulse_offset) = problem.solution(impulse_offset) * impulse_diagonal;

            let impulse_limit = time_step * impulse;
            problem.add_value_constraint(impulse_offset, Box::new(move |_problem: &LCP, value: Scalar| -> Scalar {
                if value < 0.0 {
                    0.0
                } else if value > impulse_limit {
                    impulse_limit
                } else {
                    value
                }
            }));
        }

        return (problem, friction_directions);
    }

    fn apply_lcp_solution(problem: LCP, friction_directions: Vec<UnitVec3D>, time_step: Scalar, contact_events: &Vec<ContactEvent>) {
        let number_of_contacts = contact_events.len();
        for (i, contact_event) in contact_events.iter().enumerate() {
            let impulse_offset = number_of_contacts * i;
            let friction_offset = impulse_offset + 1;

            let friction_direction = &friction_directions[i];

            let contact_normal = contact_event.normal();
            let contact_center = contact_event.point(0);
            let penetration_depth = contact_event.penetration_depth(0);
            let mut body_0 = contact_event.bodies().0.borrow_mut();
            let mut body_1 = contact_event.bodies().1.borrow_mut();

            match (body_0.downcast_mut(), body_1.downcast_mut()) {
                (BodyTypeMut::Rigid(rigid_body_0), BodyTypeMut::Rigid(rigid_body_1)) => {
                    let velocity_change = (contact_normal * problem.solution(impulse_offset)
                        + friction_direction * problem.solution(friction_offset))
                        / time_step;

                    let mass = [rigid_body_0.mass(), rigid_body_1.mass()];
                    let inertia_inverse = [rigid_body_0.inertia().inverse(), rigid_body_1.inertia().inverse()];
                    // relative vector from position to contact center
                    let to_contact_center = [
                        contact_center - rigid_body_0.translation(),
                        contact_center - rigid_body_1.translation(),
                    ];

                    let angular_velocity_change_0 = inertia_inverse[0]*to_contact_center[0].cross( velocity_change);
                    let angular_velocity_change_1 = inertia_inverse[1]*to_contact_center[1].cross(-velocity_change);

                    let correction = -0.5 * penetration_depth * contact_normal;
                    let change_0 = (velocity_change / mass[0], angular_velocity_change_0);
                    MachConstraintSolver::update_rigid_body(rigid_body_0, change_0, time_step, correction);

                    let change_1 = (-velocity_change / mass[1], angular_velocity_change_1);
                    MachConstraintSolver::update_rigid_body(rigid_body_1, change_1, 0.0, -correction);
                },

                (BodyTypeMut::Rigid(rigid_body), BodyTypeMut::Static(_static_body)) => {
                    let velocity_change = (contact_normal * problem.solution(impulse_offset)
                        + friction_direction * problem.solution(friction_offset))
                        / time_step;

                    let to_contact_center = contact_center - rigid_body.translation();

                    let angular_velocity_change = rigid_body.inertia().inverse()*to_contact_center.cross(velocity_change);

                    let correction = -0.5 * penetration_depth * contact_normal;
                    let change = (velocity_change / rigid_body.mass(), angular_velocity_change);
                    MachConstraintSolver::update_rigid_body(rigid_body, change, 0.0, correction);
                },

                _otherwise => panic!("unhandled body combination"),
            }
        }
    }

    fn update_rigid_body(rigid_body: &mut RigidBody, change: (Vec3D, Vec3D), remaining_time: Scalar, correction: Vec3D) {
        let v = rigid_body.velocity().clone();
        let w = rigid_body.angular_velocity().clone();
        *rigid_body.velocity_mut() = v + change.0;
        *rigid_body.angular_velocity_mut() = w + change.1;

        let position = rigid_body.translation().clone();
        *rigid_body.translation_mut() = position + correction;

        // TODO missing gravity!
        SemiImplicitEuler.integrate_in_place(&mut rigid_body.as_integratable_mut(), remaining_time, Vec3D::zero());
    }
}

impl ConstraintSolver for MachConstraintSolver {
    fn solve_with_contacts(&mut self, time_step: Scalar, contact_events: &Vec<ContactEvent>) {
        let (mut problem, friction_directions) = MachConstraintSolver::formuate_lcp(time_step, contact_events);

        lcp_solvers::GaussSeidel.solve_in_place(&mut problem);

        MachConstraintSolver::apply_lcp_solution(problem, friction_directions, time_step, contact_events);
    }
}
<commit_msg>Fix a bug in MachConstraintSolver<commit_after>extern crate rand;

use std::mem;

use self::rand::Rng;

use Scalar;
use maths::{lcp_solvers, CrossProduct, DotProduct, Integrator, LCP, LCPSolver, Matrix, UnitVec3D, Vec3D};
use maths::integrators::SemiImplicitEuler;
use solvers::{ConstraintSolver, ImpulseSolver};
use entities::{BodyType, BodyTypeMut, RigidBody};
use detection::ContactEvent;

static NUM_COMPONENTS: usize = 2;

pub struct MachConstraintSolver;

impl MachConstraintSolver {
    pub fn new() -> MachConstraintSolver {
        MachConstraintSolver
    }

    fn formulate_lcp(time_step: Scalar, contacts: &Vec<ContactEvent>) -> (LCP, Vec<UnitVec3D>) {
        let number_of_contacts = contacts.len();
        let size = number_of_contacts * NUM_COMPONENTS;
        let mut problem = LCP::new(size);
        let mut friction_directions: Vec<UnitVec3D> = Vec::new();

        for (i, contact_event) in contacts.iter().enumerate() {
            // TODO handle more than one contact point
            // NOTE simple approximation of contact center
            let contact_center = contact_event.points().iter()
                .fold(Vec3D::zero(), |total, point| {
                    total + point
                }) / contact_event.points().len() as Scalar;
            let contact_normal = contact_event.normal();
            let body_0 = contact_event.bodies().0.borrow();
            let body_1 = contact_event.bodies().1.borrow();

            let (mu, mass_inverse, inertia_inverse, rel_vel, contact_offset) = match (body_0.downcast(), body_1.downcast()) {
                (BodyType::Rigid(rigid_body_0), BodyType::Rigid(rigid_body_1)) => {
                    let contact_offset_0 = contact_center - rigid_body_0.translation();
                    let contact_offset_1 = contact_center - rigid_body_1.translation();

                    let mu = rigid_body_0.friction_coefficient() * rigid_body_1.friction_coefficient();
                    let mass_inverse = (rigid_body_0.mass_inverse(), rigid_body_1.mass_inverse());
                    let inertia_inverse = (rigid_body_0.inertia_inverse(), rigid_body_1.inertia_inverse());

                    let rel_vel = rigid_body_0.velocity() - rigid_body_1.velocity() +
                        contact_offset_0.cross(rigid_body_0.angular_velocity().clone()) -
                        contact_offset_1.cross(rigid_body_1.angular_velocity().clone());

                    (mu, mass_inverse, inertia_inverse, rel_vel, (contact_offset_0, contact_offset_1))
                },

                (BodyType::Rigid(rigid_body), BodyType::Static(static_body)) => {
                    let contact_offset_0 = contact_center - rigid_body.translation();
                    let contact_offset_1 = contact_center - static_body.translation();

                    let mu = rigid_body.friction_coefficient() * static_body.friction_coefficient();
                    let mass_inverse = (rigid_body.mass_inverse(), 0.0);
                    let inertia_inverse = (rigid_body.inertia_inverse(), Matrix::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0));

                    let rel_vel = rigid_body.velocity() +
                        contact_offset_0.cross(rigid_body.angular_velocity().clone());

                    (mu, mass_inverse, inertia_inverse, rel_vel, (contact_offset_0, contact_offset_1))
                },

                _otherwise => panic!("unhandled body type combination!"),
            };

            mem::drop(body_0);
            mem::drop(body_1);

            let impulse = ImpulseSolver::compute_impulse_for_event(contact_event);
            let impulse = if impulse < 0.0 {
                0.0
            } else {
                impulse
            };
            let impulse_offset = i * NUM_COMPONENTS;
            let friction_offset = impulse_offset + 1;

            let generalized_mass_inverse = |vect: UnitVec3D| -> Vec3D {
                (mass_inverse.0 + mass_inverse.1) * vect +
                    contact_offset.0.cross(inertia_inverse.0 * contact_offset.0.cross(vect)) +
                    contact_offset.1.cross(inertia_inverse.1 * contact_offset.1.cross(vect))
            };

            let generalized_mass_inverse_norm = generalized_mass_inverse(contact_normal);

            // FRICTION
            let mut perpendicular_direction = rel_vel.cross(contact_normal).normalize();
            // ASSUMPTION: Non-finite length means that the relative velocity
            // and contact normal directions are aligned
            while !perpendicular_direction.squared_length().is_finite() {
                // pick any arbitrary direction to avoid the singularity when
                // relative velocity is aligned  with the contact normal
                let mut rng = rand::thread_rng();
                let guess = Vec3D::new(
                    rng.gen_range(-1.0, 1.0),
                    rng.gen_range(-1.0, 1.0),
                    rng.gen_range(-1.0, 1.0),
                );
                perpendicular_direction = rel_vel.cross(guess).normalize();
            };
            let friction_direction = -contact_normal.cross(perpendicular_direction);
            let generalized_mass_inverse_fric = generalized_mass_inverse(friction_direction);

            *problem.matrix_mut(friction_offset, friction_offset) = friction_direction.dot(generalized_mass_inverse_fric);
            *problem.matrix_mut(impulse_offset, friction_offset) = contact_normal.dot(generalized_mass_inverse_fric);
            *problem.matrix_mut(friction_offset, impulse_offset) = friction_direction.dot(generalized_mass_inverse_norm);

            *problem.unknown_mut(friction_offset) = time_step * mu * impulse;

            *problem.bias_mut(friction_offset) = friction_direction.dot(rel_vel);

            let max_friction_impulse = time_step * rel_vel.dot(friction_direction).abs();
            problem.add_value_constraint(friction_offset, Box::new(move |local_problem, value| {
                if value < 0.0 {
                    0.0
                } else {
                    let friction_limit_due_to_normal_impulse = mu * local_problem.solution(impulse_offset);
                    let friction_limit = if max_friction_impulse > friction_limit_due_to_normal_impulse {
                        friction_limit_due_to_normal_impulse
                    } else {
                        max_friction_impulse
                    };

                    if value > friction_limit {
                        friction_limit
                    } else {
                        value
                    }
                }
            }));

            friction_directions.push(friction_direction);

            // IMPULSE
            let impulse_diagonal = contact_normal.dot(generalized_mass_inverse_norm);
            *problem.matrix_mut(impulse_offset, impulse_offset) = impulse_diagonal;
            *problem.unknown_mut(impulse_offset) = time_step * impulse;
            // TODO this satisfies the constraints when friction is zero, but
            // is it theoretically valid?
            *problem.bias_mut(impulse_offset) = problem.solution(impulse_offset) * impulse_diagonal;

            let impulse_limit = time_step * impulse;
            problem.add_value_constraint(impulse_offset, Box::new(move |_problem: &LCP, value: Scalar| -> Scalar {
                if value < 0.0 {
                    0.0
                } else if value > impulse_limit {
                    impulse_limit
                } else {
                    value
                }
            }));
        }

        return (problem, friction_directions);
    }

    fn apply_lcp_solution(problem: LCP, friction_directions: Vec<UnitVec3D>, time_step: Scalar, contact_events: &Vec<ContactEvent>) {
        for (i, contact_event) in contact_events.iter().enumerate() {
            let impulse_offset = NUM_COMPONENTS * i;
            let friction_offset = impulse_offset + 1;

            let friction_direction = &friction_directions[i];

            let contact_normal = contact_event.normal();
            let contact_center = contact_event.point(0);
            let penetration_depth = contact_event.penetration_depth(0);
            let mut body_0 = contact_event.bodies().0.borrow_mut();
            let mut body_1 = contact_event.bodies().1.borrow_mut();

            match (body_0.downcast_mut(), body_1.downcast_mut()) {
                (BodyTypeMut::Rigid(rigid_body_0), BodyTypeMut::Rigid(rigid_body_1)) => {
                    let velocity_change = (contact_normal * problem.solution(impulse_offset)
                        + friction_direction * problem.solution(friction_offset))
                        / time_step;

                    let mass = [rigid_body_0.mass(), rigid_body_1.mass()];
                    let inertia_inverse = [rigid_body_0.inertia().inverse(), rigid_body_1.inertia().inverse()];
                    // relative vector from position to contact center
                    let to_contact_center = [
                        contact_center - rigid_body_0.translation(),
                        contact_center - rigid_body_1.translation(),
                    ];

                    let angular_velocity_change_0 = inertia_inverse[0]*to_contact_center[0].cross( velocity_change);
                    let angular_velocity_change_1 = inertia_inverse[1]*to_contact_center[1].cross(-velocity_change);

                    let correction = -0.5 * penetration_depth * contact_normal;
                    let change_0 = (velocity_change / mass[0], angular_velocity_change_0);
                    MachConstraintSolver::update_rigid_body(rigid_body_0, change_0, time_step, correction);

                    let change_1 = (-velocity_change / mass[1], angular_velocity_change_1);
                    MachConstraintSolver::update_rigid_body(rigid_body_1, change_1, 0.0, -correction);
                },

                (BodyTypeMut::Rigid(rigid_body), BodyTypeMut::Static(_static_body)) => {
                    let velocity_change = (contact_normal * problem.solution(impulse_offset)
                        + friction_direction * problem.solution(friction_offset))
                        / time_step;

                    let to_contact_center = contact_center - rigid_body.translation();

                    let angular_velocity_change = rigid_body.inertia().inverse()*to_contact_center.cross(velocity_change);

                    let correction = -0.5 * penetration_depth * contact_normal;
                    let change = (velocity_change / rigid_body.mass(), angular_velocity_change);
                    MachConstraintSolver::update_rigid_body(rigid_body, change, 0.0, correction);
                },

                _otherwise => panic!("unhandled body combination"),
            }
        }
    }

    fn update_rigid_body(rigid_body: &mut RigidBody, change: (Vec3D, Vec3D), remaining_time: Scalar, correction: Vec3D) {
        let v = rigid_body.velocity().clone();
        let w = rigid_body.angular_velocity().clone();
        *rigid_body.velocity_mut() = v + change.0;
        *rigid_body.angular_velocity_mut() = w + change.1;

        let position = rigid_body.translation().clone();
        *rigid_body.translation_mut() = position + correction;

        // TODO missing gravity!
        SemiImplicitEuler.integrate_in_place(&mut rigid_body.as_integratable_mut(), remaining_time, Vec3D::zero());
    }
}

impl ConstraintSolver for MachConstraintSolver {
    fn solve_with_contacts(&mut self, time_step: Scalar, contact_events: &Vec<ContactEvent>) {
        let (mut problem, friction_directions) = MachConstraintSolver::formulate_lcp(time_step, contact_events);

        lcp_solvers::GaussSeidel.solve_in_place(&mut problem);

        MachConstraintSolver::apply_lcp_solution(problem, friction_directions, time_step, contact_events);
    }
}
<|endoftext|>