<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::fmt;
use std::str;
use super::gl;

use Capabilities;
use shade;

/// A version number for a specific component of an OpenGL implementation
#[deriving(Eq, PartialEq, Ord, PartialOrd)]
pub struct Version {
    pub major: uint,
    pub minor: uint,
    pub revision: Option<uint>,
    pub vendor_info: &'static str,
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: uint, minor: uint, revision: Option<uint>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (src.slice_to(i), src.slice_from(i + 1)),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(from_str);
        let minor = it.next().and_then(from_str);
        let revision = it.next().and_then(from_str);

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Show for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = gl.GetString(name) as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { str::raw::c_str_to_static_slice(ptr) }
    } else {
        fail!("Invalid GLenum passed to `get_string`: {:x}", name)
    }
}

fn get_uint(gl: &gl::Gl, name: gl::types::GLenum) -> uint {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as uint
}

/// A unique platform identifier that does not change between releases
#[deriving(Eq, PartialEq, Show)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[deriving(Show)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 2, None, "") {
            let num_exts = get_uint(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            range(0, num_exts).map(|i| {
                unsafe {
                    str::raw::c_str_to_static_slice(
                        gl.GetStringi(gl::EXTENSIONS, i) as *const i8,
                    )
                }
            }).collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &str) -> bool {
        self.extensions.contains_equiv(&s)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    match *v {
        v if v < Version::new(1, 2, None, "") => shade::ModelUnsupported,
        v if v < Version::new(1, 5, None, "") => shade::Model30,
        v if v < Version::new(3, 0, None, "") => shade::Model40,
        v if v < Version::new(4, 3, None, "") => shade::Model41,
        _                                     => shade::Model50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model: to_shader_model(&info.shading_language),
        max_draw_buffers: get_uint(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size: get_uint(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes: get_uint(gl, gl::MAX_VERTEX_ATTRIBS),
        uniform_block_supported: info.version >= Version::new(3, 1, None, "")
            || info.is_extension_supported("GL_ARB_uniform_buffer_object"),
        array_buffer_supported: info.version >= Version::new(3, 0, None, "")
            || info.is_extension_supported("GL_ARB_vertex_array_object"),
        immutable_storage_supported: info.version >= Version::new(4, 2, None, "")
            || info.is_extension_supported("GL_ARB_texture_storage"),
        sampler_objects_supported: info.version >= Version::new(3, 3, None, "")
            || info.is_extension_supported("GL_ARB_sampler_objects"),
    };
    (info, caps)
}

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

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version(1, 2, Some(3), "h3l1o. W0rld")));
    }
}
<commit_msg>Fix Shader version to shader model mapping<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::fmt;
use std::str;
use super::gl;

use Capabilities;
use shade;

/// A version number for a specific component of an OpenGL implementation
#[deriving(Eq, PartialEq, Ord, PartialOrd)]
pub struct Version {
    pub major: uint,
    pub minor: uint,
    pub revision: Option<uint>,
    pub vendor_info: &'static str,
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: uint, minor: uint, revision: Option<uint>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (src.slice_to(i), src.slice_from(i + 1)),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(from_str);
        let minor = it.next().and_then(from_str);
        let revision = it.next().and_then(from_str);

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Show for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = gl.GetString(name) as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { str::raw::c_str_to_static_slice(ptr) }
    } else {
        fail!("Invalid GLenum passed to `get_string`: {:x}", name)
    }
}

fn get_uint(gl: &gl::Gl, name: gl::types::GLenum) -> uint {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as uint
}

/// A unique platform identifier that does not change between releases
#[deriving(Eq, PartialEq, Show)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[deriving(Show)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 2, None, "") {
            let num_exts = get_uint(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            range(0, num_exts).map(|i| {
                unsafe {
                    str::raw::c_str_to_static_slice(
                        gl.GetStringi(gl::EXTENSIONS, i) as *const i8,
                    )
                }
            }).collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &str) -> bool {
        self.extensions.contains_equiv(&s)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    match *v {
        v if v < Version::new(1, 20, None, "") => shade::ModelUnsupported,
        v if v < Version::new(1, 50, None, "") => shade::Model30,
        v if v < Version::new(3,  0, None, "") => shade::Model40,
        v if v < Version::new(4, 30, None, "") => shade::Model41,
        _                                      => shade::Model50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model: to_shader_model(&info.shading_language),
        max_draw_buffers: get_uint(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size: get_uint(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes: get_uint(gl, gl::MAX_VERTEX_ATTRIBS),
        uniform_block_supported: info.version >= Version::new(3, 1, None, "")
            || info.is_extension_supported("GL_ARB_uniform_buffer_object"),
        array_buffer_supported: info.version >= Version::new(3, 0, None, "")
            || info.is_extension_supported("GL_ARB_vertex_array_object"),
        immutable_storage_supported: info.version >= Version::new(4, 2, None, "")
            || info.is_extension_supported("GL_ARB_texture_storage"),
        sampler_objects_supported: info.version >= Version::new(3, 3, None, "")
            || info.is_extension_supported("GL_ARB_sampler_objects"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;
    use shade;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), shade::ModelUnsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), shade::Model30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), shade::Model40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), shade::Model41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), shade::Model50);
    }
}
<|endoftext|>


<commit_before>// mod connection

use std::fmt;
use std::net;
use std::string::String;
use std::io::BufReader;
use std::io::prelude::*;
use error::{Error, SQError};
use command::Command;

#[derive(Debug)]
pub struct Connection {
    addr: net::SocketAddrV4,
    conn: BufReader<net::TcpStream>,
}

impl Connection {
    pub fn new(addr: String) -> super::Result<Connection> {
        let a = try!(addr.parse());
        let c = try!(net::TcpStream::connect(a));
        let mut connection = Connection {
            addr: a,
            conn: BufReader::new(c),
        };
        let mut tmp = String::new();
        try!(connection.conn.read_line(&mut tmp));
        if tmp.trim() != "TS3" {
            return Err(From::from("the given server is not a TS3 server"));
        }
        try!(connection.read_line(&mut tmp));
        Ok(connection)
    }

    fn read_line<'a>(&mut self, buf: &'a mut String) -> super::Result<&'a str> {
        let _ = try!(self.conn.read_line(buf));
        Ok(buf.trim_left_matches(char::is_control))
    }

    fn get_stream_mut(&mut self) -> &mut net::TcpStream {
        self.conn.get_mut()
    }

    pub fn send_command<C>(&mut self, command: C) -> super::Result<String>
        where C: Command
    {
        let command = command.string();
        if command.is_empty() {
            return Err(Error::from("no command"));
        }

        try!(writeln!(self.get_stream_mut(), "{}", command));

        try!(self.get_stream_mut().flush());

        let mut result = String::new();
        loop {
            let mut line = String::new();
            let line = try!(self.read_line(&mut line));
            let ok = try!(SQError::parse_is_ok(line));
            if ok {
                break;
            }
            result = result + line; // + "\n";
        }

        Ok(result)
    }

    pub fn send_command_vec<C>(&mut self, commands: C) -> super::Result<Vec<String>>
        where C: IntoIterator,
              C::Item: Command
    {
        let mut results = Vec::new();
        for cmd in commands {
            let res = try!(self.send_command(cmd));
            results.push(res);
        }
        Ok(results)
    }

    pub fn quit(&mut self) -> super::Result<()> {
        try!(writeln!(self.get_stream_mut(), "quit"));
        try!(self.get_stream_mut().flush());
        try!(self.conn.get_ref().shutdown(net::Shutdown::Both));
        Ok(())
    }
}

impl fmt::Display for Connection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.addr)
    }
}
<commit_msg>adding some functions to get clientlists and channellists<commit_after>// mod connection

use std::fmt;
use std::net;
use std::string::String;
use std::io::BufReader;
use std::io::prelude::*;
use error::{Error, SQError};
use client::ClientList;
use channel::ChannelList;
use command::Command;
use map::to_map;

#[derive(Debug)]
pub struct Connection {
    addr: net::SocketAddrV4,
    conn: BufReader<net::TcpStream>,
}

impl Connection {
    pub fn new(addr: String) -> super::Result<Connection> {
        let a = try!(addr.parse());
        let c = try!(net::TcpStream::connect(a));
        let mut connection = Connection {
            addr: a,
            conn: BufReader::new(c),
        };
        let mut tmp = String::new();
        try!(connection.conn.read_line(&mut tmp));
        if tmp.trim() != "TS3" {
            return Err(From::from("the given server is not a TS3 server"));
        }
        try!(connection.read_line(&mut tmp));
        Ok(connection)
    }

    fn read_line<'a>(&mut self, buf: &'a mut String) -> super::Result<&'a str> {
        let _ = try!(self.conn.read_line(buf));
        Ok(buf.trim_left_matches(char::is_control))
    }

    fn get_stream_mut(&mut self) -> &mut net::TcpStream {
        self.conn.get_mut()
    }

    pub fn send_command<C>(&mut self, command: C) -> super::Result<String>
        where C: Command
    {
        let command = command.string();
        if command.is_empty() {
            return Err(Error::from("no command"));
        }

        try!(writeln!(self.get_stream_mut(), "{}", command));

        try!(self.get_stream_mut().flush());

        let mut result = String::new();
        loop {
            let mut line = String::new();
            let line = try!(self.read_line(&mut line));
            let ok = try!(SQError::parse_is_ok(line));
            if ok {
                break;
            }
            result = result + line; // + "\n";
        }

        Ok(result)
    }

    pub fn send_command_vec<C>(&mut self, commands: C) -> super::Result<Vec<String>>
        where C: IntoIterator,
              C::Item: Command
    {
        let mut results = Vec::new();
        for cmd in commands {
            let res = try!(self.send_command(cmd));
            results.push(res);
        }
        Ok(results)
    }

    pub fn quit(&mut self) -> super::Result<()> {
        try!(self.send_command("quit"));
        try!(self.conn.get_ref().shutdown(net::Shutdown::Both));
        Ok(())
    }

    pub fn clientlist(&mut self) -> super::Result<ClientList> {
        let s = try!(self.send_command("clientlist"));
        let cl = try!(s.parse());
        Ok(cl)
    }

    pub fn clientlist_with_info(&mut self) -> super::Result<ClientList> {
        let mut clients = try!(self.clientlist());
        for client in clients.get_mut().iter_mut() {
            let command = format!("clientinfo clid={}", client.clid);
            let str = try!(self.send_command(command));
            let map = to_map(&str);
            client.mut_from_map(&map);
        }
        Ok(clients)
    }

    pub fn channellist(&mut self) -> super::Result<ChannelList> {
        let s = try!(self.send_command("channellist"));
        let cl = try!(s.parse());
        Ok(cl)
    }

    pub fn channellist_with_clients(&mut self) -> super::Result<ChannelList> {
        let clients = try!(self.clientlist_with_info());
        let mut channels = try!(self.channellist());
        channels.merge_clients(&clients);
        Ok(channels)
    }
}

impl fmt::Display for Connection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.addr)
    }
}
<|endoftext|>


<commit_before>use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::renderer::EncoderQueue;
use gfx;
use bullet;
use terrain;
use character;
use zombie;
use specs;
use specs::{Fetch, WriteStorage};
use std::time::Instant;
use critter::CharacterSprite;
use graphics::orientation::Stance;
use graphics::DeltaTime;

pub struct DrawSystem<D: gfx::Device> {
  render_target_view: gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
  terrain_system: terrain::TerrainDrawSystem<D::Resources>,
  character_system: character::CharacterDrawSystem<D::Resources>,
  zombie_system: zombie::ZombieDrawSystem<D::Resources>,
  bullet_system: bullet::BulletDrawSystem<D::Resources>,
  encoder_queue: EncoderQueue<D>,
  game_time: Instant,
  frames: u32,
  cool_down: f64,
  fire_cool_down: f64,
}

impl<D: gfx::Device> DrawSystem<D> {
  pub fn new<F>(factory: &mut F,
                rtv: &gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
                dsv: &gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
                encoder_queue: EncoderQueue<D>)
                -> DrawSystem<D>
    where F: gfx::Factory<D::Resources>
  {
    DrawSystem {
      render_target_view: rtv.clone(),
      depth_stencil_view: dsv.clone(),
      terrain_system: terrain::TerrainDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      character_system: character::CharacterDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      zombie_system: zombie::ZombieDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      bullet_system: bullet::BulletDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      encoder_queue,
      game_time: Instant::now(),
      frames: 0,
      cool_down: 1.0,
      fire_cool_down: 1.0
    }
  }
}

impl<'a, D> specs::System<'a> for DrawSystem<D>
  where D: gfx::Device,
        D::CommandBuffer: Send,
{
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, terrain::TerrainDrawable>,
                     WriteStorage<'a, character::CharacterDrawable>,
                     WriteStorage<'a, CharacterSprite>,
                     WriteStorage<'a, zombie::zombies::Zombies>,
                     WriteStorage<'a, bullet::bullets::Bullets>,
                     Fetch<'a, DeltaTime>);

  fn run(&mut self, (mut terrain, mut character, mut character_sprite, mut zombies, mut bullets, d): Self::SystemData) {
    use specs::Join;
    let mut encoder = self.encoder_queue.receiver.recv().unwrap();

    let delta = d.0;

    if self.cool_down == 0.0 {
      self.cool_down += 0.1;
    }
    if self.fire_cool_down == 0.0 {
      self.fire_cool_down += 0.2;
    }
    self.cool_down = (self.cool_down - delta).max(0.0);
    self.fire_cool_down = (self.fire_cool_down - delta).max(0.0);

    let current_time = Instant::now();
    self.frames += 1;
    if cfg!(feature = "fps") && (current_time.duration_since(self.game_time).as_secs()) >= 1 {
      println!("{:?} ms/frames", 1000.0 / f64::from(self.frames));
      self.frames = 0;
      self.game_time = Instant::now();
    }

    encoder.clear(&self.render_target_view, [16.0 / 256.0, 16.0 / 256.0, 20.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.depth_stencil_view, 1.0);

    for (t, c, cs, zs, bs) in (&mut terrain, &mut character, &mut character_sprite, &mut zombies, &mut bullets).join() {
      self.terrain_system.draw(t, &mut encoder);

      if self.cool_down == 0.0 {
        if c.stance == Stance::Walking {
          cs.update_run();
        }
        for mut z in &mut zs.zombies {
          match z.stance {
            Stance::NormalDeath => z.update_normal_death(),
            Stance::CriticalDeath => z.update_critical_death(),
            Stance::Walking => z.update_walk(),
            Stance::Still => z.update_still(),
            _ => ()
          };
        }
      } else if self.fire_cool_down == 0.0 && c.stance == Stance::Firing {
        cs.update_fire();
      }
      for mut z in &mut zs.zombies {
        self.zombie_system.draw(&mut z, &mut encoder);
      }
      self.character_system.draw(c, cs, &mut encoder);
      for b in &mut bs.bullets {
        self.bullet_system.draw(b, &mut encoder);
      }
    }

    if let Err(e) = self.encoder_queue.sender.send(encoder) {
      panic!("Disconnected, cannot return encoder to mpsc: {}", e);
    };
  }
}
<commit_msg>Handle Z-index between character and zombies<commit_after>use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::renderer::EncoderQueue;
use gfx;
use bullet;
use terrain;
use character;
use zombie;
use specs;
use specs::{Fetch, WriteStorage};
use std::time::Instant;
use critter::CharacterSprite;
use graphics::orientation::Stance;
use graphics::DeltaTime;

pub struct DrawSystem<D: gfx::Device> {
  render_target_view: gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
  terrain_system: terrain::TerrainDrawSystem<D::Resources>,
  character_system: character::CharacterDrawSystem<D::Resources>,
  zombie_system: zombie::ZombieDrawSystem<D::Resources>,
  bullet_system: bullet::BulletDrawSystem<D::Resources>,
  encoder_queue: EncoderQueue<D>,
  game_time: Instant,
  frames: u32,
  cool_down: f64,
  fire_cool_down: f64,
}

impl<D: gfx::Device> DrawSystem<D> {
  pub fn new<F>(factory: &mut F,
                rtv: &gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
                dsv: &gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
                encoder_queue: EncoderQueue<D>)
                -> DrawSystem<D>
    where F: gfx::Factory<D::Resources>
  {
    DrawSystem {
      render_target_view: rtv.clone(),
      depth_stencil_view: dsv.clone(),
      terrain_system: terrain::TerrainDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      character_system: character::CharacterDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      zombie_system: zombie::ZombieDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      bullet_system: bullet::BulletDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      encoder_queue,
      game_time: Instant::now(),
      frames: 0,
      cool_down: 1.0,
      fire_cool_down: 1.0
    }
  }
}

impl<'a, D> specs::System<'a> for DrawSystem<D>
  where D: gfx::Device,
        D::CommandBuffer: Send,
{
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, terrain::TerrainDrawable>,
                     WriteStorage<'a, character::CharacterDrawable>,
                     WriteStorage<'a, CharacterSprite>,
                     WriteStorage<'a, zombie::zombies::Zombies>,
                     WriteStorage<'a, bullet::bullets::Bullets>,
                     Fetch<'a, DeltaTime>);

  fn run(&mut self, (mut terrain, mut character, mut character_sprite, mut zombies, mut bullets, d): Self::SystemData) {
    use specs::Join;
    let mut encoder = self.encoder_queue.receiver.recv().unwrap();

    let delta = d.0;

    if self.cool_down == 0.0 {
      self.cool_down += 0.1;
    }
    if self.fire_cool_down == 0.0 {
      self.fire_cool_down += 0.2;
    }
    self.cool_down = (self.cool_down - delta).max(0.0);
    self.fire_cool_down = (self.fire_cool_down - delta).max(0.0);

    let current_time = Instant::now();
    self.frames += 1;
    if cfg!(feature = "fps") && (current_time.duration_since(self.game_time).as_secs()) >= 1 {
      println!("{:?} ms/frames", 1000.0 / f64::from(self.frames));
      self.frames = 0;
      self.game_time = Instant::now();
    }

    encoder.clear(&self.render_target_view, [16.0 / 256.0, 16.0 / 256.0, 20.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.depth_stencil_view, 1.0);

    for (t, c, cs, zs, bs) in (&mut terrain, &mut character, &mut character_sprite, &mut zombies, &mut bullets).join() {
      self.terrain_system.draw(t, &mut encoder);

      if self.cool_down == 0.0 {
        if c.stance == Stance::Walking {
          cs.update_run();
        }
        for mut z in &mut zs.zombies {
          match z.stance {
            Stance::NormalDeath => z.update_normal_death(),
            Stance::CriticalDeath => z.update_critical_death(),
            Stance::Walking => z.update_walk(),
            Stance::Still => z.update_still(),
            _ => ()
          };
        }
      } else if self.fire_cool_down == 0.0 && c.stance == Stance::Firing {
        cs.update_fire();
      }
      for mut z in &mut zs.zombies {
        if c.position.position[1] <= z.position.position[1] {
          self.zombie_system.draw(&mut z, &mut encoder);
        }
      }
      self.character_system.draw(c, cs, &mut encoder);
      for mut z in &mut zs.zombies {
        if c.position.position[1] > z.position.position[1] {
          self.zombie_system.draw(&mut z, &mut encoder);
        }
      }

      for b in &mut bs.bullets {
        self.bullet_system.draw(b, &mut encoder);
      }
    }

    if let Err(e) = self.encoder_queue.sender.send(encoder) {
      panic!("Disconnected, cannot return encoder to mpsc: {}", e);
    };
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#![allow(dead_code)]

use std::fmt;
use std::thread;
use std::time::{Duration, Instant};

use crate::gpio::epoll::{epoll_event, Epoll, EventFd, EPOLLERR, EPOLLET, EPOLLIN, EPOLLPRI};
use crate::gpio::ioctl;
use crate::gpio::pin::InputPin;
use crate::gpio::{Error, Level, Result, Trigger};

#[derive(Debug)]
struct Interrupt {
    pin: u8,
    trigger: Trigger,
    cdev_fd: i32,
    event_request: ioctl::EventRequest,
}

impl Interrupt {
    fn new(cdev_fd: i32, pin: u8, trigger: Trigger) -> Result<Interrupt> {
        Ok(Interrupt {
            pin,
            trigger,
            cdev_fd,
            event_request: ioctl::EventRequest::new(cdev_fd, pin, trigger)?,
        })
    }

    fn trigger(&self) -> Trigger {
        self.trigger
    }

    fn fd(&self) -> i32 {
        self.event_request.fd
    }

    fn pin(&self) -> u8 {
        self.pin
    }

    fn set_trigger(&mut self, trigger: Trigger) -> Result<()> {
        self.trigger = trigger;

        self.reset()
    }

    fn event(&mut self) -> Result<ioctl::Event> {
        // This might block if there are no events waiting
        ioctl::get_event(self.event_request.fd)
    }

    fn reset(&mut self) -> Result<()> {
        // Close the old event fd before opening a new one
        self.event_request.close();
        self.event_request = ioctl::EventRequest::new(self.cdev_fd, self.pin, self.trigger)?;

        Ok(())
    }
}

#[derive(Debug)]
struct TriggerStatus {
    interrupt: Option<Interrupt>,
    triggered: bool,
    level: Level,
}

pub struct EventLoop {
    poll: Epoll,
    events: Vec<epoll_event>,
    trigger_status: Vec<TriggerStatus>,
    cdev_fd: i32,
}

impl fmt::Debug for EventLoop {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EventLoop")
            .field("poll", &self.poll)
            .field("events", &format_args!("{{ .. }}"))
            .field("trigger_status", &format_args!("{{ .. }}"))
            .field("cdev_fd", &self.cdev_fd)
            .finish()
    }
}

impl EventLoop {
    pub fn new(cdev_fd: i32, capacity: usize) -> Result<EventLoop> {
        let mut trigger_status = Vec::with_capacity(capacity);

        // Initialize trigger_status while circumventing the Copy/Clone requirement
        for _ in 0..trigger_status.capacity() {
            trigger_status.push(TriggerStatus {
                interrupt: None,
                triggered: false,
                level: Level::Low,
            });
        }

        Ok(EventLoop {
            poll: Epoll::new()?,
            events: vec![epoll_event { events: 0, u64: 0 }; capacity],
            trigger_status,
            cdev_fd,
        })
    }

    pub fn poll<'a>(
        &mut self,
        pins: &[&'a InputPin],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin, Level)>> {
        for pin in pins {
            let trigger_status = &mut self.trigger_status[pin.pin() as usize];

            // Did we cache any trigger events during the previous poll?
            if trigger_status.triggered {
                trigger_status.triggered = false;

                if !reset {
                    return Ok(Some((pin, trigger_status.level)));
                }
            }

            // Reset any pending trigger events
            if let Some(ref mut interrupt) = trigger_status.interrupt {
                if reset {
                    self.poll.delete(interrupt.fd())?;
                    interrupt.reset()?;
                    self.poll.add(
                        interrupt.fd(),
                        u64::from(interrupt.pin()),
                        EPOLLIN | EPOLLPRI,
                    )?;
                }
            }
        }

        // Loop until we get any of the events we're waiting for, or a timeout occurs
        let now = Instant::now();
        loop {
            let num_events = self.poll.wait(&mut self.events, timeout)?;

            // No events means a timeout occurred
            if num_events == 0 {
                return Ok(None);
            }

            for event in &self.events[0..num_events] {
                let pin = event.u64 as usize;

                let trigger_status = &mut self.trigger_status[pin];

                debug_assert!(
                    trigger_status.interrupt.is_some(),
                    "No interrupt set for pin {}",
                    pin
                );

                if let Some(ref mut interrupt) = trigger_status.interrupt {
                    trigger_status.level = match interrupt.event()?.trigger {
                        Trigger::RisingEdge => Level::High,
                        Trigger::FallingEdge => Level::Low,
                        _ => unsafe { std::hint::unreachable_unchecked() },
                    };

                    trigger_status.triggered = true;
                };
            }

            // Were any interrupts triggered? If so, return one. The rest
            // will be saved for the next poll.
            for pin in pins {
                let trigger_status = &mut self.trigger_status[pin.pin() as usize];

                if trigger_status.triggered {
                    trigger_status.triggered = false;
                    return Ok(Some((pin, trigger_status.level)));
                }
            }

            // It's possible a pin we're not waiting for continuously triggers
            // an interrupt, causing repeated loops with calls to poll() using a
            // reset timeout value. Make sure we haven't been looping longer than
            // the requested timeout.
            if let Some(t) = timeout {
                if now.elapsed() > t {
                    return Ok(None);
                }
            }
        }
    }

    pub fn set_interrupt(&mut self, pin: u8, trigger: Trigger) -> Result<()> {
        let trigger_status = &mut self.trigger_status[pin as usize];

        trigger_status.triggered = false;

        // Interrupt already exists. We just need to change the trigger.
        if let Some(ref mut interrupt) = trigger_status.interrupt {
            if interrupt.trigger != trigger {
                // This requires a new event request, so the fd might change
                self.poll.delete(interrupt.fd())?;
                interrupt.set_trigger(trigger)?;
                self.poll
                    .add(interrupt.fd(), u64::from(pin), EPOLLIN | EPOLLPRI)?;
            }

            return Ok(());
        }

        // Register a new interrupt
        let interrupt = Interrupt::new(self.cdev_fd, pin, trigger)?;
        self.poll
            .add(interrupt.fd(), u64::from(pin), EPOLLIN | EPOLLPRI)?;
        trigger_status.interrupt = Some(interrupt);

        Ok(())
    }

    pub fn clear_interrupt(&mut self, pin: u8) -> Result<()> {
        let trigger_status = &mut self.trigger_status[pin as usize];

        trigger_status.triggered = false;

        if let Some(interrupt) = trigger_status.interrupt.take() {
            self.poll.delete(interrupt.fd())?;
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct AsyncInterrupt {
    poll_thread: Option<thread::JoinHandle<Result<()>>>,
    tx: EventFd,
}

impl AsyncInterrupt {
    pub fn new<C>(fd: i32, pin: u8, trigger: Trigger, mut callback: C) -> Result<AsyncInterrupt>
    where
        C: FnMut(Level) + Send + 'static,
    {
        let tx = EventFd::new()?;
        let rx = tx.fd();

        let poll_thread = thread::spawn(move || -> Result<()> {
            let poll = Epoll::new()?;

            // rx becomes readable when the main thread calls notify()
            poll.add(rx, rx as u64, EPOLLERR | EPOLLET | EPOLLIN)?;

            let mut interrupt = Interrupt::new(fd, pin, trigger)?;
            poll.add(interrupt.fd(), interrupt.fd() as u64, EPOLLIN | EPOLLPRI)?;

            let mut events = [epoll_event { events: 0, u64: 0 }; 2];
            loop {
                let num_events = poll.wait(&mut events, None)?;
                if num_events > 0 {
                    for event in &events[0..num_events] {
                        let fd = event.u64 as i32;
                        if fd == rx {
                            return Ok(()); // The main thread asked us to stop
                        } else if fd == interrupt.fd() {
                            let level = match interrupt.event()?.trigger {
                                Trigger::RisingEdge => Level::High,
                                _ => Level::Low,
                            };

                            callback(level);
                        }
                    }
                }
            }
        });

        Ok(AsyncInterrupt {
            poll_thread: Some(poll_thread),
            tx,
        })
    }

    pub fn stop(&mut self) -> Result<()> {
        self.tx.notify()?;

        if let Some(poll_thread) = self.poll_thread.take() {
            match poll_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for AsyncInterrupt {
    fn drop(&mut self) {
        // Don't wait for the poll thread to exit if the main thread is panicking,
        // because we could potentially block indefinitely while unwinding if the
        // poll thread is executing a callback that doesn't return.
        if !thread::panicking() {
            let _ = self.stop();
        }
    }
}
<commit_msg>Remove debug_assert<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#![allow(dead_code)]

use std::fmt;
use std::thread;
use std::time::{Duration, Instant};

use crate::gpio::epoll::{epoll_event, Epoll, EventFd, EPOLLERR, EPOLLET, EPOLLIN, EPOLLPRI};
use crate::gpio::ioctl;
use crate::gpio::pin::InputPin;
use crate::gpio::{Error, Level, Result, Trigger};

#[derive(Debug)]
struct Interrupt {
    pin: u8,
    trigger: Trigger,
    cdev_fd: i32,
    event_request: ioctl::EventRequest,
}

impl Interrupt {
    fn new(cdev_fd: i32, pin: u8, trigger: Trigger) -> Result<Interrupt> {
        Ok(Interrupt {
            pin,
            trigger,
            cdev_fd,
            event_request: ioctl::EventRequest::new(cdev_fd, pin, trigger)?,
        })
    }

    fn trigger(&self) -> Trigger {
        self.trigger
    }

    fn fd(&self) -> i32 {
        self.event_request.fd
    }

    fn pin(&self) -> u8 {
        self.pin
    }

    fn set_trigger(&mut self, trigger: Trigger) -> Result<()> {
        self.trigger = trigger;

        self.reset()
    }

    fn event(&mut self) -> Result<ioctl::Event> {
        // This might block if there are no events waiting
        ioctl::get_event(self.event_request.fd)
    }

    fn reset(&mut self) -> Result<()> {
        // Close the old event fd before opening a new one
        self.event_request.close();
        self.event_request = ioctl::EventRequest::new(self.cdev_fd, self.pin, self.trigger)?;

        Ok(())
    }
}

#[derive(Debug)]
struct TriggerStatus {
    interrupt: Option<Interrupt>,
    triggered: bool,
    level: Level,
}

pub struct EventLoop {
    poll: Epoll,
    events: Vec<epoll_event>,
    trigger_status: Vec<TriggerStatus>,
    cdev_fd: i32,
}

impl fmt::Debug for EventLoop {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EventLoop")
            .field("poll", &self.poll)
            .field("events", &format_args!("{{ .. }}"))
            .field("trigger_status", &format_args!("{{ .. }}"))
            .field("cdev_fd", &self.cdev_fd)
            .finish()
    }
}

impl EventLoop {
    pub fn new(cdev_fd: i32, capacity: usize) -> Result<EventLoop> {
        let mut trigger_status = Vec::with_capacity(capacity);

        // Initialize trigger_status while circumventing the Copy/Clone requirement
        for _ in 0..trigger_status.capacity() {
            trigger_status.push(TriggerStatus {
                interrupt: None,
                triggered: false,
                level: Level::Low,
            });
        }

        Ok(EventLoop {
            poll: Epoll::new()?,
            events: vec![epoll_event { events: 0, u64: 0 }; capacity],
            trigger_status,
            cdev_fd,
        })
    }

    pub fn poll<'a>(
        &mut self,
        pins: &[&'a InputPin],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin, Level)>> {
        for pin in pins {
            let trigger_status = &mut self.trigger_status[pin.pin() as usize];

            // Did we cache any trigger events during the previous poll?
            if trigger_status.triggered {
                trigger_status.triggered = false;

                if !reset {
                    return Ok(Some((pin, trigger_status.level)));
                }
            }

            // Reset any pending trigger events
            if let Some(ref mut interrupt) = trigger_status.interrupt {
                if reset {
                    self.poll.delete(interrupt.fd())?;
                    interrupt.reset()?;
                    self.poll.add(
                        interrupt.fd(),
                        u64::from(interrupt.pin()),
                        EPOLLIN | EPOLLPRI,
                    )?;
                }
            }
        }

        // Loop until we get any of the events we're waiting for, or a timeout occurs
        let now = Instant::now();
        loop {
            let num_events = self.poll.wait(&mut self.events, timeout)?;

            // No events means a timeout occurred
            if num_events == 0 {
                return Ok(None);
            }

            for event in &self.events[0..num_events] {
                let pin = event.u64 as usize;

                let trigger_status = &mut self.trigger_status[pin];

                if let Some(ref mut interrupt) = trigger_status.interrupt {
                    trigger_status.level = match interrupt.event()?.trigger {
                        Trigger::RisingEdge => Level::High,
                        Trigger::FallingEdge => Level::Low,
                        _ => unsafe { std::hint::unreachable_unchecked() },
                    };

                    trigger_status.triggered = true;
                };
            }

            // Were any interrupts triggered? If so, return one. The rest
            // will be saved for the next poll.
            for pin in pins {
                let trigger_status = &mut self.trigger_status[pin.pin() as usize];

                if trigger_status.triggered {
                    trigger_status.triggered = false;
                    return Ok(Some((pin, trigger_status.level)));
                }
            }

            // It's possible a pin we're not waiting for continuously triggers
            // an interrupt, causing repeated loops with calls to poll() using a
            // reset timeout value. Make sure we haven't been looping longer than
            // the requested timeout.
            if let Some(t) = timeout {
                if now.elapsed() > t {
                    return Ok(None);
                }
            }
        }
    }

    pub fn set_interrupt(&mut self, pin: u8, trigger: Trigger) -> Result<()> {
        let trigger_status = &mut self.trigger_status[pin as usize];

        trigger_status.triggered = false;

        // Interrupt already exists. We just need to change the trigger.
        if let Some(ref mut interrupt) = trigger_status.interrupt {
            if interrupt.trigger != trigger {
                // This requires a new event request, so the fd might change
                self.poll.delete(interrupt.fd())?;
                interrupt.set_trigger(trigger)?;
                self.poll
                    .add(interrupt.fd(), u64::from(pin), EPOLLIN | EPOLLPRI)?;
            }

            return Ok(());
        }

        // Register a new interrupt
        let interrupt = Interrupt::new(self.cdev_fd, pin, trigger)?;
        self.poll
            .add(interrupt.fd(), u64::from(pin), EPOLLIN | EPOLLPRI)?;
        trigger_status.interrupt = Some(interrupt);

        Ok(())
    }

    pub fn clear_interrupt(&mut self, pin: u8) -> Result<()> {
        let trigger_status = &mut self.trigger_status[pin as usize];

        trigger_status.triggered = false;

        if let Some(interrupt) = trigger_status.interrupt.take() {
            self.poll.delete(interrupt.fd())?;
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct AsyncInterrupt {
    poll_thread: Option<thread::JoinHandle<Result<()>>>,
    tx: EventFd,
}

impl AsyncInterrupt {
    pub fn new<C>(fd: i32, pin: u8, trigger: Trigger, mut callback: C) -> Result<AsyncInterrupt>
    where
        C: FnMut(Level) + Send + 'static,
    {
        let tx = EventFd::new()?;
        let rx = tx.fd();

        let poll_thread = thread::spawn(move || -> Result<()> {
            let poll = Epoll::new()?;

            // rx becomes readable when the main thread calls notify()
            poll.add(rx, rx as u64, EPOLLERR | EPOLLET | EPOLLIN)?;

            let mut interrupt = Interrupt::new(fd, pin, trigger)?;
            poll.add(interrupt.fd(), interrupt.fd() as u64, EPOLLIN | EPOLLPRI)?;

            let mut events = [epoll_event { events: 0, u64: 0 }; 2];
            loop {
                let num_events = poll.wait(&mut events, None)?;
                if num_events > 0 {
                    for event in &events[0..num_events] {
                        let fd = event.u64 as i32;
                        if fd == rx {
                            return Ok(()); // The main thread asked us to stop
                        } else if fd == interrupt.fd() {
                            let level = match interrupt.event()?.trigger {
                                Trigger::RisingEdge => Level::High,
                                _ => Level::Low,
                            };

                            callback(level);
                        }
                    }
                }
            }
        });

        Ok(AsyncInterrupt {
            poll_thread: Some(poll_thread),
            tx,
        })
    }

    pub fn stop(&mut self) -> Result<()> {
        self.tx.notify()?;

        if let Some(poll_thread) = self.poll_thread.take() {
            match poll_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for AsyncInterrupt {
    fn drop(&mut self) {
        // Don't wait for the poll thread to exit if the main thread is panicking,
        // because we could potentially block indefinitely while unwinding if the
        // poll thread is executing a callback that doesn't return.
        if !thread::panicking() {
            let _ = self.stop();
        }
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;

use self::super::{ERR_HTSI, ERR_INVALID, MAP, OK};
use self::super::super::http;

use base64::{encode, decode};
use std::slice;

pub fn data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> i8 {

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", safe_command);

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    if !MAP.lock().unwrap().contains_key(&ctn) {
        error!(
            "Card terminal has not been opened. Returning {}",
            ERR_INVALID
        );
        return ERR_INVALID;
    }

    let request_data = RequestData {
        dad: *safe_dad,
        sad: *safe_sad,
        lenc,
        command: encode(safe_command),
        lenr: *safe_lenr,
    };

    let pn = MAP.lock().unwrap().get(&ctn).unwrap().clone();
    let path = format!("ct_data/{}/{}", ctn, pn);
    let json = serde_json::to_string(&request_data).unwrap();
    let response = http::request().post(&path, Some(json)).response();
    if response.status() != 200 {
        error!("Request failed! Returning {}", ERR_HTSI);
        return ERR_HTSI;
    }

    let data: ResponseData = match serde_json::from_str(&response.body()) {
        Ok(response) => response,
        Err(error) => {
            error!("Failed to parse response data. {}", error);
            error!("Returning {}", ERR_HTSI);
            return ERR_HTSI;
        }
    };

    if data.responseCode == OK {
        *safe_dad = data.dad;
        *safe_sad = data.sad;
        *safe_lenr = data.lenr;

        let decoded = decode(&data.response).unwrap();
        debug!("Decoded response field {:?}", decoded);

        for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
            *place = *element;
        }
    }
    debug!("Returning {}", data.responseCode);
    return data.responseCode;
}

#[derive(Serialize)]
struct RequestData {
    dad: u8,
    sad: u8,
    lenc: u16,
    command: String,
    lenr: u16,
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    responseCode: i8,
}
<commit_msg>replace struct by format macro<commit_after>extern crate serde_json;

use self::super::{ERR_HTSI, ERR_INVALID, MAP, OK};
use self::super::super::http;

use base64::{encode, decode};
use std::slice;

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct Response {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    responseCode: i8,
}

pub fn data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> i8 {

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", safe_command);

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    if !MAP.lock().unwrap().contains_key(&ctn) {
        error!(
            "Card terminal has not been opened. Returning {}",
            ERR_INVALID
        );
        return ERR_INVALID;
    }

    let json = format!(
        "{{\"dad\":{},\"sad\":{},\"lenc\":{},\"command\":\"{}\",\"lenr\":{}}}",
        *safe_dad,
        *safe_sad,
        lenc,
        encode(safe_command),
        *safe_lenr
    );

    let pn = MAP.lock().unwrap().get(&ctn).unwrap().clone();
    let path = format!("ct_data/{}/{}", ctn, pn);
    let response = http::request().post(&path, Some(json)).response();
    if response.status() != 200 {
        error!("Request failed! Returning {}", ERR_HTSI);
        return ERR_HTSI;
    }

    let data: Response = match serde_json::from_str(&response.body()) {
        Ok(response) => response,
        Err(error) => {
            error!("Failed to parse response data. {}", error);
            error!("Returning {}", ERR_HTSI);
            return ERR_HTSI;
        }
    };

    if data.responseCode == OK {
        *safe_dad = data.dad;
        *safe_sad = data.sad;
        *safe_lenr = data.lenr;

        let decoded = decode(&data.response).unwrap();
        debug!("Decoded response field {:?}", decoded);

        for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
            *place = *element;
        }
    }
    debug!("Returning {}", data.responseCode);
    return data.responseCode;
}
<|endoftext|>


<commit_before>#[macro_use] extern crate rustler;
#[macro_use] extern crate rustler_codegen;
#[macro_use] extern crate lazy_static;
extern crate primal;

use rustler::{NifEnv, NifTerm, NifResult, NifEncoder};
use rustler::schedule::NifScheduleFlags;
use std::iter::FromIterator;

mod atoms {
    rustler_atoms! {
        atom ok;
        //atom error;
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

rustler_export_nifs! {
    "Elixir.PrimalEx.NativePrime",
    [
        ("primes", 1, primes, NifScheduleFlags::DirtyCpu),
        ("primes", 2, primes_x_y, NifScheduleFlags::DirtyCpu),
        ("n_primes", 1, n_primes, NifScheduleFlags::DirtyCpu),
        ("n_primes", 2, n_primes_x_y, NifScheduleFlags::DirtyCpu),
        ("nth_prime", 1, nth_prime, NifScheduleFlags::DirtyCpu),
        ("count_primes", 1, count_primes, NifScheduleFlags::DirtyCpu),
        ("is_prime", 1, is_prime, NifScheduleFlags::DirtyCpu),
    ],
    None
}

fn primes<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let num: usize = try!(args[0].decode());
    let thing  = primal::Primes::all().take_while(|p| *p < num);
    let sieve = Vec::from_iter(thing);
    Ok((atoms::ok(), sieve).encode(env))
}

fn primes_x_y<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let y: usize = try!(args[1].decode());
    let thing  = primal::Primes::all().take_while(|p| *p < y).filter(|p| *p > x );
    let sieve = Vec::from_iter(thing);
    Ok((atoms::ok(), sieve).encode(env))
}

fn n_primes<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let num: usize = try!(args[0].decode());
    let thing  = primal::Primes::all().take(num);
    let sieve = Vec::from_iter(thing);
    Ok((atoms::ok(), sieve).encode(env))
}

fn n_primes_x_y<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let y: usize = try!(args[1].decode());
    let limit = bare_nth_prime(x+y);

    let sieve = primal::Sieve::new(limit);
    let iter_results = sieve.primes_from(y).take(x);
    let vec_results = Vec::from_iter(iter_results);
    Ok((atoms::ok(), vec_results).encode(env))
}

fn bare_nth_prime(x: usize) -> usize {
    let (_, hi) = primal::estimate_nth_prime(x as u64);
    let sieve = primal::Sieve::new(hi as usize);
    sieve.nth_prime(x)
}

fn nth_prime<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let results = bare_nth_prime(x);
    Ok((atoms::ok(), results).encode(env))
}

fn count_primes<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let count = primal::StreamingSieve::prime_pi(x);
    Ok((atoms::ok(), count).encode(env))
}

fn is_prime<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let result = primal::is_prime(x as u64);
    Ok((atoms::ok(), result).encode(env))
}
<commit_msg>Add better naming in Rust module<commit_after>#[macro_use] extern crate rustler;
#[macro_use] extern crate rustler_codegen;
#[macro_use] extern crate lazy_static;
extern crate primal;

use rustler::{NifEnv, NifTerm, NifResult, NifEncoder};
use rustler::schedule::NifScheduleFlags;
use std::iter::FromIterator;

mod atoms {
    rustler_atoms! {
        atom ok;
        //atom error;
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

rustler_export_nifs! {
    "Elixir.PrimalEx.NativePrime",
    [
        ("primes", 1, primes, NifScheduleFlags::DirtyCpu),
        ("primes", 2, primes_x_y, NifScheduleFlags::DirtyCpu),
        ("n_primes", 1, n_primes, NifScheduleFlags::DirtyCpu),
        ("n_primes", 2, n_primes_x_y, NifScheduleFlags::DirtyCpu),
        ("nth_prime", 1, nth_prime, NifScheduleFlags::DirtyCpu),
        ("count_primes", 1, count_primes, NifScheduleFlags::DirtyCpu),
        ("is_prime", 1, is_prime, NifScheduleFlags::DirtyCpu),
    ],
    None
}

fn primes<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let num: usize = try!(args[0].decode());
    let primes_iter  = primal::Primes::all().take_while(|p| *p < num);
    let primes_vec = Vec::from_iter(primes_iter);
    Ok((atoms::ok(), primes_vec).encode(env))
}

fn primes_x_y<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let y: usize = try!(args[1].decode());
    let primes_iter  = primal::Primes::all().take_while(|p| *p < y).filter(|p| *p > x );
    let primes_vec = Vec::from_iter(primes_iter);
    Ok((atoms::ok(), primes_vec).encode(env))
}

fn n_primes<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let num: usize = try!(args[0].decode());
    let primes_iter  = primal::Primes::all().take(num);
    let primes_vec = Vec::from_iter(primes_iter);
    Ok((atoms::ok(), primes_vec).encode(env))
}

fn n_primes_x_y<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let y: usize = try!(args[1].decode());
    let limit = bare_nth_prime(x+y);

    let sieve = primal::Sieve::new(limit);
    let primes_iter = sieve.primes_from(y).take(x);
    let primes_vec = Vec::from_iter(primes_iter);
    Ok((atoms::ok(), primes_vec).encode(env))
}

fn bare_nth_prime(x: usize) -> usize {
    let (_, hi) = primal::estimate_nth_prime(x as u64);
    let sieve = primal::Sieve::new(hi as usize);
    sieve.nth_prime(x)
}

fn nth_prime<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let results = bare_nth_prime(x);
    Ok((atoms::ok(), results).encode(env))
}

fn count_primes<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let count = primal::StreamingSieve::prime_pi(x);
    Ok((atoms::ok(), count).encode(env))
}

fn is_prime<'a>(env: NifEnv<'a>, args: &[NifTerm<'a>]) -> NifResult<NifTerm<'a>> {
    let x: usize = try!(args[0].decode());
    let result = primal::is_prime(x as u64);
    Ok((atoms::ok(), result).encode(env))
}
<|endoftext|>


<commit_before>//! # Exception handler system

mod idt;

/// This macro saves all scratch registers before calling an exception.
macro_rules! save_scratch_registers {
    () => {
        asm!("push rax
              push rcx
              push rdx
              push rsi
              push rdi
              push r8
              push r9
              push r10
              push r11
        " :::: "intel", "volatile");
    }
}

/// This macro restores all scratch registers before calling an exception.
macro_rules! restore_scratch_registers {
    () => {
        asm!("pop r11
              pop r10
              pop r9
              pop r8
              pop rdi
              pop rsi
              pop rdx
              pop rcx
              pop rax
            " :::: "intel", "volatile");
    }
}

/// This macro creates a wrapper for the handler in order to get the
/// exception stack frame to pass it to the actual exception handler.
macro_rules! handler {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                // save all scratch registers
                save_scratch_registers!();

                // align the stack, and call the handler passing the exception stack frame as a
                // argument.
                asm!("mov rdi, rsp
                    add rdi, 9*8 // calculate exception stack frame pointer
                    call $0"
                    :: "i"($name as extern "C" fn(&ExceptionStackFrame))
                    : "rdi" : "intel");

                // restore scratch registerns
                restore_scratch_registers!();

                // this allow us to return from an exception
                asm!("iretq"
                    :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

/// This macro creates a wrapper for the handler  to exception that support error codes, in order to
/// get the exception stack frame to pass it to the actual exception handler, and the error code.
macro_rules! handler_with_error_code {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                // save all scratch registers
                save_scratch_registers!();

                // align the stack, and call the handler passing the exception stack frame and the
                // error code as a arguments.
                asm!("mov rsi, [rsp + 9*8] // load error code into rsi
                    mov rdi, rsp
                    add rsi, 10*8 // calculate exception stack frame pointer
                    sub rsp, 8 // align the stack pointer
                    call $0
                    add rsp, 8 // under stack pointer alignment"
                    :: "i"($name as extern "C" fn(&ExceptionStackFrame, u64))
                    : "rdi" : "intel");

                // restore scratch registerns
                restore_scratch_registers!();

                // this allow us to return from an exception
                asm!("add rsp, 8 // pop error code
                    iretq"
                    :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

// The IDT is allocated statically to ensure that this stays in memory until the end of the kernel
// execution.
lazy_static! {
    #[derive(Debug)]
    static ref IDT: idt::Idt = {
        // create a new IDT structure
        let mut idt = idt::Idt::new();

        // set the handler for the zero division exception
        idt.set_handler(0, handler!(divide_by_zero_handler));
        idt.set_handler(3, handler!(breakpoint_handler));
        idt.set_handler(6, handler!(invalid_opcode_handler));
        idt.set_handler(14, handler_with_error_code!(page_fault_exception));

        idt
    };
}

bitflags! {
    flags PageFaultErrorCode: u64 {
        const PROTECTION_VIOLATION = 1 << 0,
        const CAUSED_BY_WRITE = 1 << 1,
        const USER_MODE = 1 << 2,
        const MALFORMED_TABLE = 1 << 3,
        const INSTRUCTION_FETCH = 1 << 4,
    }
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

/// Initialize the IDT
pub fn init() {
    // load the IDT table into the CPU
    IDT.load();
}

/// Handler for the division by zero exception
extern "C" fn divide_by_zero_handler(stack_frame: &ExceptionStackFrame) {
    // print out the error message and the stack_frame
    println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
    loop {}
}

/// Handler for a invalid opcode exception
extern "C" fn invalid_opcode_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}\n",
        stack_frame.instruction_pointer, stack_frame);
    loop {}
}

/// Handler for page faults
extern "C" fn page_fault_exception(stack_frame: &ExceptionStackFrame, error_code: u64) {
    use x86::shared::control_regs;

    println!("\nEXCEPTION: PAGE FAULT while accessing {:#x}\
        \nerror code: {:?}\n{:#?}",
        unsafe { control_regs::cr2() },
        PageFaultErrorCode::from_bits(error_code).unwrap(),
        stack_frame);
    loop {}
}

/// Handler to catch breakpoint exceptions
extern "C" fn breakpoint_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
        stack_frame.instruction_pointer, stack_frame);
}
<commit_msg>#18 - handle double faults exceptions<commit_after>//! # Exception handler system

mod idt;

/// This macro saves all scratch registers before calling an exception.
macro_rules! save_scratch_registers {
    () => {
        asm!("push rax
              push rcx
              push rdx
              push rsi
              push rdi
              push r8
              push r9
              push r10
              push r11
        " :::: "intel", "volatile");
    }
}

/// This macro restores all scratch registers before calling an exception.
macro_rules! restore_scratch_registers {
    () => {
        asm!("pop r11
              pop r10
              pop r9
              pop r8
              pop rdi
              pop rsi
              pop rdx
              pop rcx
              pop rax
            " :::: "intel", "volatile");
    }
}

/// This macro creates a wrapper for the handler in order to get the
/// exception stack frame to pass it to the actual exception handler.
macro_rules! handler {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                // save all scratch registers
                save_scratch_registers!();

                // align the stack, and call the handler passing the exception stack frame as a
                // argument.
                asm!("mov rdi, rsp
                    add rdi, 9*8 // calculate exception stack frame pointer
                    call $0"
                    :: "i"($name as extern "C" fn(&ExceptionStackFrame))
                    : "rdi" : "intel");

                // restore scratch registerns
                restore_scratch_registers!();

                // this allow us to return from an exception
                asm!("iretq"
                    :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

/// This macro creates a wrapper for the handler  to exception that support error codes, in order to
/// get the exception stack frame to pass it to the actual exception handler, and the error code.
macro_rules! handler_with_error_code {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                // save all scratch registers
                save_scratch_registers!();

                // align the stack, and call the handler passing the exception stack frame and the
                // error code as a arguments.
                asm!("mov rsi, [rsp + 9*8] // load error code into rsi
                    mov rdi, rsp
                    add rsi, 10*8 // calculate exception stack frame pointer
                    sub rsp, 8 // align the stack pointer
                    call $0
                    add rsp, 8 // under stack pointer alignment"
                    :: "i"($name as extern "C" fn(&ExceptionStackFrame, u64))
                    : "rdi" : "intel");

                // restore scratch registerns
                restore_scratch_registers!();

                // this allow us to return from an exception
                asm!("add rsp, 8 // pop error code
                    iretq"
                    :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

// The IDT is allocated statically to ensure that this stays in memory until the end of the kernel
// execution.
lazy_static! {
    #[derive(Debug)]
    static ref IDT: idt::Idt = {
        // create a new IDT structure
        let mut idt = idt::Idt::new();

        // set the handler for the zero division exception
        idt.set_handler(0, handler!(divide_by_zero_handler));
        idt.set_handler(3, handler!(breakpoint_handler));
        idt.set_handler(6, handler!(invalid_opcode_handler));
        idt.set_handler(8, handler_with_error_code!(double_fault_handler));
        idt.set_handler(14, handler_with_error_code!(page_fault_exception));

        idt
    };
}

bitflags! {
    flags PageFaultErrorCode: u64 {
        const PROTECTION_VIOLATION = 1 << 0,
        const CAUSED_BY_WRITE = 1 << 1,
        const USER_MODE = 1 << 2,
        const MALFORMED_TABLE = 1 << 3,
        const INSTRUCTION_FETCH = 1 << 4,
    }
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

/// Initialize the IDT
pub fn init() {
    // load the IDT table into the CPU
    IDT.load();
}

/// Handler for the division by zero exception
extern "C" fn divide_by_zero_handler(stack_frame: &ExceptionStackFrame) {
    // print out the error message and the stack_frame
    println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
    loop {}
}

/// Handler for a invalid opcode exception
extern "C" fn invalid_opcode_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}\n",
        stack_frame.instruction_pointer, stack_frame);
    loop {}
}

/// Handler for page faults
extern "C" fn page_fault_exception(stack_frame: &ExceptionStackFrame, error_code: u64) {
    use x86::shared::control_regs;

    println!("\nEXCEPTION: PAGE FAULT while accessing {:#x}\
        \nerror code: {:?}\n{:#?}",
        unsafe { control_regs::cr2() },
        PageFaultErrorCode::from_bits(error_code).unwrap(),
        stack_frame);
    loop {}
}

/// Handler to catch breakpoint exceptions
extern "C" fn breakpoint_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
        stack_frame.instruction_pointer, stack_frame);
}

/// Handler to catch double faults
extern "C" fn double_fault_handler(stack_frame: &ExceptionStackFrame, error_code: u64) {
    println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
    loop {}
}
<|endoftext|>