<commit_before>use luminance::{Dim2, Equation, Factor, Flat, Mode, Pipe, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use std::cell::RefCell;

use compositing;
use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_win_coord(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

#[derive(Copy, Clone)]
pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_lower: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_lower: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_lower: left_lower
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

pub struct Overlay {
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Overlay {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3 + max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Overlay {
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  /// Dispatch render input primitives into GPU buffers.
  fn dispatch(&self, input: &RenderInput) -> (usize, usize) {
    let mut tris_ref = self.tris.borrow_mut();
    let mut tris = tris_ref.as_slice_mut().unwrap();
    let mut tri_i = 0;

    let mut discs_ref = self.discs.borrow_mut();
    let mut discs = discs_ref.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, c, b, d];

      for &v in &abcd {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    (tri_i, disc_i)
  }

  /// Render the overlay.
  ///
  /// The render is not directly returned but instead is passed to a continuation to work with
  /// (CPS).
  pub fn render<F, R>(&self, input: RenderInput, f: F) -> R where F: FnOnce(Render) -> R {
    let (tri_vert_nb, disc_vert_nb) = self.dispatch(&input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, tri_vert_nb);

    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, disc_vert_nb);

    let text_quad = TessRender::one_whole(&self.text_quad);
    let text_quad_render = [Pipe::new(text_quad)];

    let tri_program = self.tri_program.borrow();
    let disc_program = self.disc_program.borrow();
    let text_program = self.text_program.borrow();

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
        TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_lower.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
    let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, &text_quad_render))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    let tris = &[Pipe::new(tris)];
    let tris_render_cmds = &[Pipe::new(RenderCommand::new(None, true, tris))];
    let discs = &[Pipe::new(discs)];
    let discs_render_cmds = &[Pipe::new(RenderCommand::new(None, true, discs))];
    let shading_cmds = &[
      Pipe::new(ShadingCommand::new(&tri_program, tris_render_cmds)),
      Pipe::empty().uniforms(&disc_uniforms).unwrap(ShadingCommand::new(&disc_program, discs_render_cmds)),
      Pipe::new(ShadingCommand::new(&text_program, &text_render_cmds))
    ];

    let render = Render::new(shading_cmds);
    f(render)
  }
}

pub struct Render<'a, 'b> where 'b: 'a {
  shading_cmds: &'a [Pipe<'b, ShadingCommand<'b>>]
}

impl<'a, 'b> Render<'a, 'b> where 'b: 'a {
  fn new(shading_cmds: &'a [Pipe<'b, ShadingCommand<'b>>]) -> Self {
    Render {
      shading_cmds: shading_cmds
    }
  }
}

impl<'a, 'b> compositing::Render<'b> for Render<'a, 'b> where 'b: 'a {
  fn push_shading_commands(&self, shading_commands: &mut Vec<Pipe<'b, ShadingCommand<'b>>>) {
    shading_commands.extend_from_slice(&self.shading_cmds);
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  rh: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);
    let rh = 1. / (h as f32);

    UnitConverter {
      rw: rw,
      rh: rh,
      twice_rw: 2. * rw,
      twice_rh: 2. * rh
    }
  }

  /// Convert from *window space* coordinates to clip space* ones.
  pub fn from_win_coord(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    let y_ = y * self.twice_rh - 1.;

    [x_.min(1.).max(-1.), y_.min(1.).max(-1.)]
  }

  /// Convert from *window space* dimensions to clip space* ones.
  pub fn from_win_dim(&self, w: f32, h: f32) -> [f32; 2] {
    let w_ = w * self.rw;
    let h_ = h * self.rh;

    [w_.min(1.).max(-1.), h_.min(1.).max(-1.)]
  }
}
<commit_msg>More doc.<commit_after>use luminance::{Dim2, Equation, Factor, Flat, Mode, Pipe, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use std::cell::RefCell;

use compositing;
use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_win_coord(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

#[derive(Copy, Clone)]
pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_lower: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_lower: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_lower: left_lower
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

pub struct Overlay {
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Overlay {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3 + max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Overlay {
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  /// Dispatch render input primitives into GPU buffers.
  fn dispatch(&self, input: &RenderInput) -> (usize, usize) {
    let mut tris_ref = self.tris.borrow_mut();
    let mut tris = tris_ref.as_slice_mut().unwrap();
    let mut tri_i = 0;

    let mut discs_ref = self.discs.borrow_mut();
    let mut discs = discs_ref.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, c, b, d];

      for &v in &abcd {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    (tri_i, disc_i)
  }

  /// Render the overlay.
  ///
  /// The render is not directly returned but instead is passed to a continuation to work with
  /// (CPS). The render implements the `compositing::Render` trait.
  pub fn render<F, R>(&self, input: RenderInput, f: F) -> R where F: FnOnce(Render) -> R {
    let (tri_vert_nb, disc_vert_nb) = self.dispatch(&input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, tri_vert_nb);

    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, disc_vert_nb);

    let text_quad = TessRender::one_whole(&self.text_quad);
    let text_quad_render = [Pipe::new(text_quad)];

    let tri_program = self.tri_program.borrow();
    let disc_program = self.disc_program.borrow();
    let text_program = self.text_program.borrow();

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
        TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_lower.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
    let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, &text_quad_render))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    let tris = &[Pipe::new(tris)];
    let tris_render_cmds = &[Pipe::new(RenderCommand::new(None, true, tris))];
    let discs = &[Pipe::new(discs)];
    let discs_render_cmds = &[Pipe::new(RenderCommand::new(None, true, discs))];
    let shading_cmds = &[
      Pipe::new(ShadingCommand::new(&tri_program, tris_render_cmds)),
      Pipe::empty().uniforms(&disc_uniforms).unwrap(ShadingCommand::new(&disc_program, discs_render_cmds)),
      Pipe::new(ShadingCommand::new(&text_program, &text_render_cmds))
    ];

    let render = Render::new(shading_cmds);
    f(render)
  }
}

pub struct Render<'a, 'b> where 'b: 'a {
  shading_cmds: &'a [Pipe<'b, ShadingCommand<'b>>]
}

impl<'a, 'b> Render<'a, 'b> where 'b: 'a {
  fn new(shading_cmds: &'a [Pipe<'b, ShadingCommand<'b>>]) -> Self {
    Render {
      shading_cmds: shading_cmds
    }
  }
}

impl<'a, 'b> compositing::Render<'b> for Render<'a, 'b> where 'b: 'a {
  fn push_shading_commands(&self, shading_commands: &mut Vec<Pipe<'b, ShadingCommand<'b>>>) {
    shading_commands.extend_from_slice(&self.shading_cmds);
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  rh: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);
    let rh = 1. / (h as f32);

    UnitConverter {
      rw: rw,
      rh: rh,
      twice_rw: 2. * rw,
      twice_rh: 2. * rh
    }
  }

  /// Convert from *window space* coordinates to clip space* ones.
  pub fn from_win_coord(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    let y_ = y * self.twice_rh - 1.;

    [x_.min(1.).max(-1.), y_.min(1.).max(-1.)]
  }

  /// Convert from *window space* dimensions to clip space* ones.
  pub fn from_win_dim(&self, w: f32, h: f32) -> [f32; 2] {
    let w_ = w * self.rw;
    let h_ = h * self.rh;

    [w_.min(1.).max(-1.), h_.min(1.).max(-1.)]
  }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate x11;

use x11::xlib;
use std::ptr;
use super::layout::{ self, Rectangle };
use super::super::libx;

pub const TITLE_HEIGHT: libc::c_int = 20;

pub enum Mode {
    Normal,
    Fullscreen,
}

pub struct Container {
    pub id: Option<xlib::Window>,
    pub visible: bool,
    pub titlebar_height: u32,
    parent: *mut Container,
    pub clients: Vec<Container>,
    pub mode: Mode,
    pub titlebar: Option<Rectangle>,
    pub portion: f32,
    pub context: libx::Context,

    pub layout: layout::Type,
    pub direction: layout::Direction,
}

impl PartialEq for Container {
    fn eq(&self, other: &Self) -> bool {
        self.raw_id() == other.raw_id()
    }
}

impl Container {
    pub fn new(context: libx::Context) -> Container{
        let pid = context.root;
        let attrs = libx::get_window_attributes(context, pid);

        let id = libx::create_window(context, pid,
                                     attrs.x, attrs.y,
                                     attrs.width as u32,
                                     attrs.height as u32);
        libx::select_input(context, id, xlib::SubstructureNotifyMask | xlib::SubstructureRedirectMask | xlib::ButtonPressMask);
        Container {
            context: context,
            clients: Vec::new(),
            visible: false,
            id: Some(id),
            mode: Mode::Normal,
            category: Type::Container,
            parent: ptr::null_mut(),
            titlebar: None,
            titlebar_height: 0,
            portion: 1.0,

            layout: layout::Type::Tiling,
            direction: layout::Direction::Horizontal,
        }
    }

    pub fn from_id(context: libx::Context, id: xlib::Window) -> Container {
        Container {
            context: context,
            clients: Vec::new(),
            visible: false,
            id: Some(id),
            mode: Mode::Normal,
            parent: ptr::null_mut(),
            titlebar: None,
            titlebar_height: 0,
            portion: 1.0,

            layout: layout::Type::Tiling,
            direction: layout::Direction::Horizontal,
        }
    }

    pub fn print_tree(&self, indent: i32) {
        for i in 0..indent {
            print!(" ");
        }
        let attrs = libx::get_window_attributes(self.context, self.raw_id());
        let pid = match self.get_parent() {
            Some(p) => { p.raw_id() }
            None => { 0}
        };
        println!("C {} pid:{} x:{} y:{} w:{} h:{}", self.raw_id(), pid, attrs.x, attrs.y, attrs.width, attrs.height);
        for client in self.clients.iter() {
            client.print_tree(indent+4);
        }
    }

    pub fn raw_id(&self) -> xlib::Window {
        // let mut id: xlib::Window = 0;
        // if self.id.is_some() {
        //     id = self.id.unwrap();
        // }
        // if self.display.is_some() {
        //     id = unsafe {
        //         self.display.as_ref().unwrap().get_window().unwrap().platform_window() as xlib::Window
        //     };
        // };
        // id
        self.id.unwrap()
    }

    // pub fn is_top(&self) -> bool {
    //     let top = self.get_top();
    //     match top {
    //         Some(w) => {
    //             w.id == self.id
    //         }
    //         Noen => false
    //     }
    // }

    pub fn is_empty(&self) -> bool{
        self.clients.is_empty()
    }

    pub fn size(&self) -> usize {
        self.clients.len()
    }

    pub fn be_parent(&mut self, client: &mut Container) {
        let need_reprent = match client.get_parent() {
            Some(p) => {
                if p.raw_id() == self.raw_id() { false } else { true }
            }
            None => {
                true
            }
        };
        if need_reprent {
            libx::reparent(self.context, client.raw_id(), self.raw_id(), 0, 0);
            client.parent = self;
        }
    }

    pub fn add(&mut self, mut client: Container) {
        self.be_parent(&mut client);
        let portion = 1.0 / (self.size() as f32 + 1.0);
        for client in self.clients.iter_mut() {
            client.portion = client.portion * (1.0-portion);
        }
        client.portion = portion;
        self.clients.push(client);
    }

    pub fn insert(&mut self, index: usize,  mut client: Container) {
        self.be_parent(&mut client);
        let portion = 1.0 / (self.size() as f32 + 1.0);
        for client in self.clients.iter_mut() {
            client.portion = client.portion * (1.0-portion);
        }
        client.portion = portion;
        self.clients.insert(index, client);
    }

    pub fn remove(&mut self, id: xlib::Window) -> Option<Container>{
        let res = self.contain(id);
        match res {
            Some(index) => {
                let r = self.clients.remove(index);
                Some(r)
            }
            None => {
                // for c in self.clients.iter_mut() {
                //     let r = c.remove(id);
                //     if r.is_some(){
                //         return r;
                //     }
                // }
                None
            }
        }
    }

    /// remove App container and destroy all its parent containers that are empty
    pub fn tree_remove(&mut self, id: xlib::Window) -> Option<Container> {
        println!("try remove {} from {}", id, self.raw_id());
        let res = self.contain(id);
        match res {
            Some(i) => {
                let r = self.clients.remove(i);
                Some(r)
            }
            None => {
                let mut r: Option<Container> = None;
                let mut index: i32 = -1;
                for (i, c) in self.clients.iter_mut().enumerate() {
                    r = c.tree_remove(id);
                    if r.is_some(){
                        index = i as i32;
                        break
                    }
                }
                if index >= 0 {
                    let i = index as usize;
                    let size = self.get_child(i).unwrap().size();
                    if size == 0 {
                        let container = self.clients.remove(i);
                        container.destroy();
                    }
                }
                r
            }
        }
    }
    pub fn get_child(&mut self, index: usize) -> Option<&mut Container> {
        self.clients.get_mut(index)
    }

    pub fn get_parent(&self) -> Option<&mut Container> {
        if self.parent == ptr::null_mut() {
            None
        }
        else{
            Some(unsafe{ &mut *self.parent })
        }
    }

    pub fn tree_search(&mut self, id: xlib::Window) -> Option<&mut Container>{
        if self.raw_id() == id {
            return Some(self);
        }

        for client in self.clients.iter_mut() {
            let r = client.tree_search(id);
            if r.is_some(){
                return r
            }
        }
        None
    }

    pub fn contain(&self, id: xlib::Window) -> Option<usize>{
        self.clients.iter().position(|x| (*x).raw_id() == id)
    }

    pub fn configure(&mut self, x: i32, y: i32, width: u32, height: u32) {
        libx::resize_window(self.context, self.raw_id(), x, y, width, height);
        // layout for children clients
        self.update_layout();
    }

    pub fn update_layout(&mut self) {
        layout::update_layout(self);
    }

    pub fn map(&self) {
        // self.visible = true;
        libx::map_window(self.context, self.raw_id());
        for client in self.clients.iter() {
            client.map();
        }
    }

    pub fn unmap(&self) {
        // self.visible = false;
        for client in self.clients.iter() {
            client.unmap();
        }
        libx::unmap_window(self.context, self.raw_id());
    }

    pub fn destroy(&self) -> bool {
        // can distroy only if it has no clients
        if self.clients.is_empty() {
            unsafe{
                xlib::XDestroyWindow(self.context.display, self.raw_id());
                true
            }
        }
        else {
            false
        }
    }

    pub fn next_client(&mut self, id: xlib::Window) -> Option<&mut Container>{
        match self.contain(id) {
            Some(i) => {
                let mut next = i+1;
                if next == self.size() {
                    next = 0;
                }
                self.get_child(next)
            }
            None => {
                None
            }
        }
    }

    pub fn last_client(&mut self, id: xlib::Window) -> Option<&mut Container>{
        match self.contain(id) {
            Some(i) => {
                let mut last = i-1;
                if last < 0 {
                    last = self.size() - 1
                }
                self.get_child(last)
            }
            None => {
                None
            }
        }
    }

    pub fn change_layout(&mut self, layout_type: layout::Type) {
        if self.layout == layout_type {
            match self.direction {
                layout::Direction::Horizontal => {
                    self.direction = layout::Direction::Vertical;
                }
                layout::Direction::Vertical => {
                    self.direction = layout::Direction::Horizontal;
                }
                _ => {}
            }
        }
        else{
            self.layout = layout_type;
            self.direction = layout::Direction::Horizontal;
        }
    }

    pub fn split(&mut self) -> bool {
        if !self.is_empty() {
            return false;
        }

        let mut container = Container::new(self.context);
        // add to parent
        match self.get_parent() {
            Some(p) => {
                let attrs = self.rec();

                libx::reparent(self.context, container.raw_id(), p.raw_id(),
                               0, 0);
                // configure to the same size and position like old one
                container.configure(attrs.x, attrs.y,
                                    attrs.width,
                                    attrs.height);
            }
            None => {}
        }

        // swap the property
        let id = self.id;
        self.id = container.id;
        container.id = id;
        container.titlebar_height = self.titlebar_height;
        self.add(container);
        self.map();
        true
    }

    pub fn focus(&self) {
        let id = self.raw_id();
        libx::set_input_focus(self.context, id);
        libx::raise_window(self.context, id);
        self.decorate(true);
    }

    pub fn unfocus(&self) {
        // libx::lower_window(self.context, self.id);
        self.decorate(false);
    }

    pub fn switch_client(&self) {
        let (x, _) = libx::get_input_focus(self.context);

    }

    pub fn decorate(&self, focused: bool) {
        match self.get_parent() {
            Some(p) => {
                layout::decorate(self, focused);
            }
            None => {}
        }

    }

    // fullscreen & normal toggle
    pub fn mode_toggle(&mut self) {
        let context = self.context;
        let id = self.raw_id();
        match self.mode {
            Mode::Normal => {
                self.mode = Mode::Fullscreen;
                println!("fullscreen {} {}", id, context.root);
                libx::reparent(context, id, context.root, 0, 0);

                let width = libx::display_width(context, context.screen_num);
                let height = libx::display_height(context, context.screen_num);
                libx::resize_window(context, id, 0, 0,
                                    width,
                                    height);
            }
            Mode::Fullscreen => {
                self.mode = Mode::Normal;

                match self.get_parent() {
                    Some(p) => {
                        let pid = p.raw_id();
                        libx::reparent(context, id, pid, 0, 0);
                        p.update_layout();
                    }
                    None => {
                        let pid =  context.root;
                        libx::reparent(context, id, pid, 0, 0);
                    }
                };
            }
        }
    }

    // decide which client when click on titlebar
    pub fn query_point(&self, x: i32, y: i32) -> Option<&Container>{
        for client in self.clients.iter() {
            match client.titlebar {
                Some(rec) => {
                    if rec.contain(x, y) {
                        return Some(client);
                    }
                }
                None => {}
            }
        }
        None
    }

    pub fn rec(&self) -> layout::Rectangle {
        let attrs = libx::get_window_attributes(self.context, self.raw_id());
        layout::Rectangle {
            x: attrs.x,
            y: attrs.y,
            width: attrs.width as u32,
            height: attrs.height as u32
        }
    }
}

// #[test]
// fn window_eq() {
//     use std::ptr;
//     let c1 = libx::Context {
//         display: ptr::null_mut()
//     };
//     let w1 = Window::new(c1, 1);
//     let c2 = libx::Context {
//         display: ptr::null_mut()
//     };
//     let w2 = Window::new(c2, 1);
//     assert_eq!(w1, w2);
// }
<commit_msg>remove by index<commit_after>extern crate libc;
extern crate x11;

use x11::xlib;
use std::ptr;
use super::layout::{ self, Rectangle };
use super::super::libx;

pub const TITLE_HEIGHT: libc::c_int = 20;

pub enum Mode {
    Normal,
    Fullscreen,
}

pub struct Container {
    pub id: Option<xlib::Window>,
    pub visible: bool,
    pub titlebar_height: u32,
    parent: *mut Container,
    pub clients: Vec<Container>,
    pub mode: Mode,
    pub titlebar: Option<Rectangle>,
    pub portion: f32,
    pub context: libx::Context,

    pub layout: layout::Type,
    pub direction: layout::Direction,
}

impl PartialEq for Container {
    fn eq(&self, other: &Self) -> bool {
        self.raw_id() == other.raw_id()
    }
}

impl Container {
    pub fn new(context: libx::Context) -> Container{
        let pid = context.root;
        let attrs = libx::get_window_attributes(context, pid);

        let id = libx::create_window(context, pid,
                                     attrs.x, attrs.y,
                                     attrs.width as u32,
                                     attrs.height as u32);
        libx::select_input(context, id, xlib::SubstructureNotifyMask | xlib::SubstructureRedirectMask | xlib::ButtonPressMask);
        Container {
            context: context,
            clients: Vec::new(),
            visible: false,
            id: Some(id),
            mode: Mode::Normal,
            category: Type::Container,
            parent: ptr::null_mut(),
            titlebar: None,
            titlebar_height: 0,
            portion: 1.0,

            layout: layout::Type::Tiling,
            direction: layout::Direction::Horizontal,
        }
    }

    pub fn from_id(context: libx::Context, id: xlib::Window) -> Container {
        Container {
            context: context,
            clients: Vec::new(),
            visible: false,
            id: Some(id),
            mode: Mode::Normal,
            parent: ptr::null_mut(),
            titlebar: None,
            titlebar_height: 0,
            portion: 1.0,

            layout: layout::Type::Tiling,
            direction: layout::Direction::Horizontal,
        }
    }

    pub fn print_tree(&self, indent: i32) {
        for i in 0..indent {
            print!(" ");
        }
        let attrs = libx::get_window_attributes(self.context, self.raw_id());
        let pid = match self.get_parent() {
            Some(p) => { p.raw_id() }
            None => { 0}
        };
        println!("C {} pid:{} x:{} y:{} w:{} h:{}", self.raw_id(), pid, attrs.x, attrs.y, attrs.width, attrs.height);
        for client in self.clients.iter() {
            client.print_tree(indent+4);
        }
    }

    pub fn raw_id(&self) -> xlib::Window {
        self.id.unwrap()
    }

    // pub fn is_top(&self) -> bool {
    //     let top = self.get_top();
    //     match top {
    //         Some(w) => {
    //             w.id == self.id
    //         }
    //         Noen => false
    //     }
    // }

    pub fn is_empty(&self) -> bool{
        self.clients.is_empty()
    }

    pub fn size(&self) -> usize {
        self.clients.len()
    }

    pub fn be_parent(&mut self, client: &mut Container) {
        let need_reprent = match client.get_parent() {
            Some(p) => {
                if p.raw_id() == self.raw_id() { false } else { true }
            }
            None => {
                true
            }
        };
        if need_reprent {
            libx::reparent(self.context, client.raw_id(), self.raw_id(), 0, 0);
            client.parent = self;
        }
    }

    pub fn add(&mut self, mut client: Container) {
        self.be_parent(&mut client);
        let portion = 1.0 / (self.size() as f32 + 1.0);
        for client in self.clients.iter_mut() {
            client.portion = client.portion * (1.0-portion);
        }
        client.portion = portion;
        self.clients.push(client);
    }

    pub fn insert(&mut self, index: usize,  mut client: Container) {
        self.be_parent(&mut client);
        let portion = 1.0 / (self.size() as f32 + 1.0);
        client.portion = portion;
        for client in self.clients.iter_mut() {
            client.portion = client.portion * (1.0-portion);
        }
        self.clients.insert(index, client);
    }

    pub fn remove(&mut self, id: xlib::Window) -> Option<Container>{
        let res = self.contain(id);
        match res {
            Some(index) => {
                self.remove_by_index(index)
            }
            None => {
                None
            }
        }
    }

    fn remove_by_index(&mut self, index: usize) -> Option<Container>{
        if index >= self.size() {
            None
        }
        else{
            let r = self.clients.remove(index);
            let portion = 1.0 - r.portion;
            for client in self.clients.iter_mut() {
                client.portion = client.portion / portion;
            }
            Some(r)
        }
    }

    /// remove App container and destroy all its parent containers that are empty
    pub fn tree_remove(&mut self, id: xlib::Window) -> Option<Container> {
        println!("try remove {} from {}", id, self.raw_id());
        let res = self.remove(id);
        match res {
            Some(c) => {
                Some(c)
            }
            None => {
                let mut r: Option<Container> = None;
                let mut index: i32 = -1;
                for (i, c) in self.clients.iter_mut().enumerate() {
                    r = c.tree_remove(id);
                    if r.is_some(){
                        index = i as i32;
                        break
                    }
                }
                if index >= 0 {
                    let i = index as usize;
                    let size = self.get_child(i).unwrap().size();
                    if size == 0 {
                        let container = self.remove_by_index(i);
                        container.unwrap().destroy();
                    }
                }
                r
            }
        }
    }
    pub fn get_child(&mut self, index: usize) -> Option<&mut Container> {
        self.clients.get_mut(index)
    }

    pub fn get_parent(&self) -> Option<&mut Container> {
        if self.parent == ptr::null_mut() {
            None
        }
        else{
            Some(unsafe{ &mut *self.parent })
        }
    }

    pub fn tree_search(&mut self, id: xlib::Window) -> Option<&mut Container>{
        if self.raw_id() == id {
            return Some(self);
        }

        for client in self.clients.iter_mut() {
            let r = client.tree_search(id);
            if r.is_some(){
                return r
            }
        }
        None
    }

    pub fn contain(&self, id: xlib::Window) -> Option<usize>{
        self.clients.iter().position(|x| (*x).raw_id() == id)
    }

    pub fn configure(&mut self, x: i32, y: i32, width: u32, height: u32) {
        libx::resize_window(self.context, self.raw_id(), x, y, width, height);
        // layout for children clients
        self.update_layout();
    }

    pub fn update_layout(&mut self) {
        layout::update_layout(self);
    }

    pub fn map(&self) {
        // self.visible = true;
        libx::map_window(self.context, self.raw_id());
        for client in self.clients.iter() {
            client.map();
        }
    }

    pub fn unmap(&self) {
        // self.visible = false;
        for client in self.clients.iter() {
            client.unmap();
        }
        libx::unmap_window(self.context, self.raw_id());
    }

    pub fn destroy(&self) -> bool {
        // can distroy only if it has no clients
        if self.clients.is_empty() {
            unsafe{
                xlib::XDestroyWindow(self.context.display, self.raw_id());
                true
            }
        }
        else {
            false
        }
    }

    pub fn next_client(&mut self, id: xlib::Window) -> Option<&mut Container>{
        match self.contain(id) {
            Some(i) => {
                let mut next = i+1;
                if next == self.size() {
                    next = 0;
                }
                self.get_child(next)
            }
            None => {
                None
            }
        }
    }

    pub fn last_client(&mut self, id: xlib::Window) -> Option<&mut Container>{
        match self.contain(id) {
            Some(i) => {
                let mut last = i-1;
                if last < 0 {
                    last = self.size() - 1
                }
                self.get_child(last)
            }
            None => {
                None
            }
        }
    }

    pub fn change_layout(&mut self, layout_type: layout::Type) {
        if self.layout == layout_type {
            match self.direction {
                layout::Direction::Horizontal => {
                    self.direction = layout::Direction::Vertical;
                }
                layout::Direction::Vertical => {
                    self.direction = layout::Direction::Horizontal;
                }
                _ => {}
            }
        }
        else{
            self.layout = layout_type;
            self.direction = layout::Direction::Horizontal;
        }
    }

    pub fn split(&mut self) -> bool {
        if !self.is_empty() {
            return false;
        }

        let mut container = Container::new(self.context);
        // add to parent
        match self.get_parent() {
            Some(p) => {
                let attrs = self.rec();

                libx::reparent(self.context, container.raw_id(), p.raw_id(),
                               0, 0);
                // configure to the same size and position like old one
                container.configure(attrs.x, attrs.y,
                                    attrs.width,
                                    attrs.height);
            }
            None => {}
        }

        // swap the property
        let id = self.id;
        self.id = container.id;
        container.id = id;
        container.titlebar_height = self.titlebar_height;
        self.add(container);
        self.map();
        true
    }

    pub fn focus(&self) {
        let id = self.raw_id();
        libx::set_input_focus(self.context, id);
        libx::raise_window(self.context, id);
        self.decorate(true);
    }

    pub fn unfocus(&self) {
        // libx::lower_window(self.context, self.id);
        self.decorate(false);
    }

    pub fn switch_client(&self) {
        let (x, _) = libx::get_input_focus(self.context);

    }

    pub fn decorate(&self, focused: bool) {
        match self.get_parent() {
            Some(p) => {
                layout::decorate(self, focused);
            }
            None => {}
        }

    }

    // fullscreen & normal toggle
    pub fn mode_toggle(&mut self) {
        let context = self.context;
        let id = self.raw_id();
        match self.mode {
            Mode::Normal => {
                self.mode = Mode::Fullscreen;
                println!("fullscreen {} {}", id, context.root);
                libx::reparent(context, id, context.root, 0, 0);

                let width = libx::display_width(context, context.screen_num);
                let height = libx::display_height(context, context.screen_num);
                libx::resize_window(context, id, 0, 0,
                                    width,
                                    height);
            }
            Mode::Fullscreen => {
                self.mode = Mode::Normal;

                match self.get_parent() {
                    Some(p) => {
                        let pid = p.raw_id();
                        libx::reparent(context, id, pid, 0, 0);
                        p.update_layout();
                    }
                    None => {
                        let pid =  context.root;
                        libx::reparent(context, id, pid, 0, 0);
                    }
                };
            }
        }
    }

    // decide which client when click on titlebar
    pub fn query_point(&self, x: i32, y: i32) -> Option<&Container>{
        for client in self.clients.iter() {
            match client.titlebar {
                Some(rec) => {
                    if rec.contain(x, y) {
                        return Some(client);
                    }
                }
                None => {}
            }
        }
        None
    }

    pub fn rec(&self) -> layout::Rectangle {
        let attrs = libx::get_window_attributes(self.context, self.raw_id());
        layout::Rectangle {
            x: attrs.x,
            y: attrs.y,
            width: attrs.width as u32,
            height: attrs.height as u32
        }
    }
}

// #[test]
// fn window_eq() {
//     use std::ptr;
//     let c1 = libx::Context {
//         display: ptr::null_mut()
//     };
//     let w1 = Window::new(c1, 1);
//     let c2 = libx::Context {
//         display: ptr::null_mut()
//     };
//     let w2 = Window::new(c2, 1);
//     assert_eq!(w1, w2);
// }
<|endoftext|>


<commit_before>use luminance::{Dim2, Equation, Factor, Flat, Mode, Pipe, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use luminance::shader::program::AlterUniform;
use std::cell::{Ref, RefCell, RefMut};

use compositing;
use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_win_coord(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

#[derive(Copy, Clone)]
pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_lower: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_lower: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_lower: left_lower
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

pub struct Render<'a, 'b> where 'b: 'a {
  render_input: RenderInput<'a, 'b>,
  ratio: f32,
  tri_program: Ref<'a, Program>,
  tris: RefMut<'a, Tess>,
  tri_vert_nb: usize,
  disc_program: Ref<'a, Program>,
  discs: RefMut<'a, Tess>,
  disc_vert_nb: usize,
  disc_uniforms: [AlterUniform<'a>; 1],
  text_program: Ref<'a, Program>,
  text_quad: &'a Tess,
  unit_converter: &'a UnitConverter,
}

  //pub fn render<F, R>(&self, input: RenderInput, f: F) -> R where F: for<'b> FnOnce([Pipe<'b, ShadingCommand<'b>>; 3]) -> R {
  //  // FIXME: no alloc?
  //  let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
  //    let (tex_w, tex_h) = text.text_texture.size();
  //
  //    [
  //      TEXT_SAMPLER.alter(Unit::new(0)),
  //      TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
  //      TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
  //      TEXT_SCALE.alter(text_scale),
  //      TEXT_COLOR.alter(text.left_lower.color),
  //    ]
  //  }).collect()).unwrap_or(Vec::new());

  //  let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

  //  let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
  //  let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
  //      let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
  //      Pipe::empty()
  //        .uniforms(&text_uniforms[i])
  //        .textures(&text_textures[i])
  //        .unwrap(RenderCommand::new(Some(blending), true, vec![
  //          Pipe::new(text_quad.clone())
  //        ]))
  //    }).collect();

  //  let disc_uniforms = [
  //    DISC_SCREEN_RATIO.alter(self.ratio)
  //  ];

  //  let tri_program = self.tri_program.borrow();
  //  let disc_program = self.disc_program.borrow();
  //  let text_program = self.text_program.borrow();

  //  let shading_commands = [
  //    // render triangles
  //    Pipe::new(ShadingCommand::new(&tri_program, vec![
  //      Pipe::new(RenderCommand::new(None, true, vec![
  //        Pipe::new(tris)
  //      ]))
  //    ])),
  //    // render discs
  //    Pipe::empty()
  //      .uniforms(&disc_uniforms)
  //      .unwrap(ShadingCommand::new(&disc_program, vec![
  //        Pipe::new(RenderCommand::new(None, true, vec![
  //          Pipe::new(discs)
  //        ]))
  //      ])),
  //    // render texts
  //    Pipe::new(ShadingCommand::new(&text_program, text_render_cmds))
  //  ];

  //  f(shading_commands)
  //}

pub struct Overlay {
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Overlay {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3 + max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Overlay {
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  /// Dispatch render input primitives into GPU buffers.
  fn dispatch(&self, input: &RenderInput) -> (usize, usize) {
    let mut tris_ref = self.tris.borrow_mut();
    let mut tris = tris_ref.as_slice_mut().unwrap();
    let mut tri_i = 0;

    let mut discs_ref = self.discs.borrow_mut();
    let mut discs = discs_ref.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, c, b, d];

      for &v in &abcd {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    (tri_i, disc_i)
  }

  pub fn render<F>(&self, input: RenderInput, f: F) where F: FnOnce() {
    let (tri_vert_nb, disc_vert_nb) = self.dispatch(&input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, tri_vert_nb);

    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, disc_vert_nb);

    let text_quad = TessRender::one_whole(&self.text_quad);
    let text_quad_render = [Pipe::new(text_quad)];

    let tri_program = self.tri_program.borrow();
    let disc_program = self.disc_program.borrow();
    let text_program = self.text_program.borrow();

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
        TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_lower.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
    let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, &text_quad_render))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    let _ = [
      // render triangles
      Pipe::new(ShadingCommand::new(&tri_program, &[
        Pipe::new(RenderCommand::new(None, true, &[
          Pipe::new(tris)
        ]))
      ])),
      // render discs
      Pipe::empty()
        .uniforms(&disc_uniforms)
        .unwrap(ShadingCommand::new(&disc_program, &[
          Pipe::new(RenderCommand::new(None, true, &[
            Pipe::new(discs)
          ]))
        ])),
      // render texts
      Pipe::new(ShadingCommand::new(&text_program, &text_render_cmds))
    ];

    unimplemented!()
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  rh: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);
    let rh = 1. / (h as f32);

    UnitConverter {
      rw: rw,
      rh: rh,
      twice_rw: 2. * rw,
      twice_rh: 2. * rh
    }
  }

  /// Convert from *window space* coordinates to clip space* ones.
  pub fn from_win_coord(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    let y_ = y * self.twice_rh - 1.;

    [x_.min(1.).max(-1.), y_.min(1.).max(-1.)]
  }

  /// Convert from *window space* dimensions to clip space* ones.
  pub fn from_win_dim(&self, w: f32, h: f32) -> [f32; 2] {
    let w_ = w * self.rw;
    let h_ = h * self.rh;

    [w_.min(1.).max(-1.), h_.min(1.).max(-1.)]
  }
}
<commit_msg>CPS overlay rendering.<commit_after>use luminance::{Dim2, Equation, Factor, Flat, Mode, Pipe, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use luminance::shader::program::AlterUniform;
use std::cell::{Ref, RefCell, RefMut};

use compositing;
use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_win_coord(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

#[derive(Copy, Clone)]
pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_lower: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_lower: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_lower: left_lower
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

pub struct Overlay {
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Overlay {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3 + max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Overlay {
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  /// Dispatch render input primitives into GPU buffers.
  fn dispatch(&self, input: &RenderInput) -> (usize, usize) {
    let mut tris_ref = self.tris.borrow_mut();
    let mut tris = tris_ref.as_slice_mut().unwrap();
    let mut tri_i = 0;

    let mut discs_ref = self.discs.borrow_mut();
    let mut discs = discs_ref.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, c, b, d];

      for &v in &abcd {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    (tri_i, disc_i)
  }

  pub fn render<F, R>(&self, input: RenderInput, f: F) -> R where F: FnOnce(Render) -> R {
    let (tri_vert_nb, disc_vert_nb) = self.dispatch(&input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, tri_vert_nb);

    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, disc_vert_nb);

    let text_quad = TessRender::one_whole(&self.text_quad);
    let text_quad_render = [Pipe::new(text_quad)];

    let tri_program = self.tri_program.borrow();
    let disc_program = self.disc_program.borrow();
    let text_program = self.text_program.borrow();

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
        TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_lower.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
    let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, &text_quad_render))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    let tris = &[Pipe::new(tris)];
    let tris_render_cmds = &[Pipe::new(RenderCommand::new(None, true, tris))];
    let discs = &[Pipe::new(discs)];
    let discs_render_cmds = &[Pipe::new(RenderCommand::new(None, true, discs))];
    let shading_cmds = &[
      Pipe::new(ShadingCommand::new(&tri_program, tris_render_cmds)),
      Pipe::empty().uniforms(&disc_uniforms).unwrap(ShadingCommand::new(&disc_program, discs_render_cmds)),
      Pipe::new(ShadingCommand::new(&text_program, &text_render_cmds))
    ];

    let render = Render::new(shading_cmds);
    f(render)
  }
}

struct Render<'a, 'b> where 'b: 'a {
  shading_cmds: &'a [Pipe<'b, ShadingCommand<'b>>]
}

impl<'a, 'b> Render<'a, 'b> where 'b: 'a {
  fn new(shading_cmds: &'a [Pipe<'b, ShadingCommand<'b>>]) -> Self {
    Render {
      shading_cmds: shading_cmds
    }
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  rh: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);
    let rh = 1. / (h as f32);

    UnitConverter {
      rw: rw,
      rh: rh,
      twice_rw: 2. * rw,
      twice_rh: 2. * rh
    }
  }

  /// Convert from *window space* coordinates to clip space* ones.
  pub fn from_win_coord(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    let y_ = y * self.twice_rh - 1.;

    [x_.min(1.).max(-1.), y_.min(1.).max(-1.)]
  }

  /// Convert from *window space* dimensions to clip space* ones.
  pub fn from_win_dim(&self, w: f32, h: f32) -> [f32; 2] {
    let w_ = w * self.rw;
    let h_ = h * self.rh;

    [w_.min(1.).max(-1.), h_.min(1.).max(-1.)]
  }
}
<|endoftext|>


<commit_before>// Copyright © 2014, Simonas Kazlauskas <git@kazlauskas.me>
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without
// fee is hereby granted, provided that the above copyright notice and this permission notice
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
// SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
//! An implementation of random number generators based on `rdrand` and `rdseed` instructions.

#![feature(asm,globs)]
use std::rand::Rng;
use std::result::Result;
mod util;

#[deriving(Copy)]
#[stable]
pub enum Error {
    /// The processor does not support the instruction used in the generator.
    UnsupportedProcessor
}

/// A cryptographically secure pseudo-random number generator.
///
/// This generator is a viable replacement to any [std::rand] generator, however, since nobody has
/// audited Intel hardware yet, the usual disclaimers apply.
///
/// It is much faster than `OsRng` (and slower than `StdRng`), but is only supported on more recent
/// (since Ivy Bridge) Intel processors.
///
/// [std::rand]: http://doc.rust-lang.org/std/rand/index.html
pub struct RdRand(());

impl RdRand {
    /// Build a generator object. The function will only succeed if `rdrand` instruction can be
    /// successfully used.
    pub fn new() -> Result<RdRand, Error> {
        if util::is_intel() && util::has_rdrand() {
            return Ok(RdRand(()));
        } else {
            return Err(Error::UnsupportedProcessor);
        }
    }

    /// Generate a value.
    #[inline]
    fn gen_value<T>(&self) -> T {
        let mut var;
        unsafe {
            asm!("1: rdrand $0; jnc 1b;" : "=r"(var));
        }
        var
    }

    /// Generate a u16 value.
    pub fn next_u16(&self) -> u16 {
        self.gen_value()
    }
}

impl Rng for RdRand {
    fn next_u32(&mut self) -> u32 {
        self.gen_value()
    }

    fn next_u64(&mut self) -> u64 {
        self.gen_value()
    }
}

/// A random number generator suited to seed other pseudo-random generators.
///
/// This instruction currently is only available in Intel Broadwell processors.
#[experimental="The implementation has not been tested due to the lack of hardware supporting \
                the feature"]
pub struct RdSeed(());

impl RdSeed {
    #[experimental]
    pub fn new() -> Result<RdSeed, Error> {
        if util::is_intel() && util::has_rdseed() {
            return Ok(RdSeed(()));
        } else {
            return Err(Error::UnsupportedProcessor);
        }
    }

    /// Generate a value.
    #[inline]
    fn gen_value<T>(&self) -> T {
        let mut var;
        unsafe {
            asm!("1: rdseed $0; jnc 1b;" : "=r"(var));
        }
        var
    }

    /// Generate a u16 value.
    pub fn next_u16(&self) -> u16 {
        self.gen_value()
    }
}

impl Rng for RdSeed {
    fn next_u32(&mut self) -> u32 {
        self.gen_value()
    }

    fn next_u64(&mut self) -> u64 {
        self.gen_value()
    }
}
<commit_msg>deriving → derive for new rust<commit_after>// Copyright © 2014, Simonas Kazlauskas <git@kazlauskas.me>
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without
// fee is hereby granted, provided that the above copyright notice and this permission notice
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
// SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
//! An implementation of random number generators based on `rdrand` and `rdseed` instructions.

#![feature(asm,globs)]
use std::rand::Rng;
use std::result::Result;
mod util;

#[derive(Copy)]
#[stable]
pub enum Error {
    /// The processor does not support the instruction used in the generator.
    UnsupportedProcessor
}

/// A cryptographically secure pseudo-random number generator.
///
/// This generator is a viable replacement to any [std::rand] generator, however, since nobody has
/// audited Intel hardware yet, the usual disclaimers apply.
///
/// It is much faster than `OsRng` (and slower than `StdRng`), but is only supported on more recent
/// (since Ivy Bridge) Intel processors.
///
/// [std::rand]: http://doc.rust-lang.org/std/rand/index.html
#[allow(missing_copy_implementations)]
#[derive(Clone)]
pub struct RdRand(());

impl RdRand {
    /// Build a generator object. The function will only succeed if `rdrand` instruction can be
    /// successfully used.
    pub fn new() -> Result<RdRand, Error> {
        if util::is_intel() && util::has_rdrand() {
            return Ok(RdRand(()));
        } else {
            return Err(Error::UnsupportedProcessor);
        }
    }

    /// Generate a value.
    #[inline]
    fn gen_value<T>(&self) -> T {
        let mut var;
        unsafe {
            asm!("1: rdrand $0; jnc 1b;" : "=r"(var));
        }
        var
    }

    /// Generate a u16 value.
    pub fn next_u16(&self) -> u16 {
        self.gen_value()
    }
}

impl Rng for RdRand {
    fn next_u32(&mut self) -> u32 {
        self.gen_value()
    }

    fn next_u64(&mut self) -> u64 {
        self.gen_value()
    }
}

/// A random number generator suited to seed other pseudo-random generators.
///
/// This instruction currently is only available in Intel Broadwell processors.
#[experimental="The implementation has not been tested due to the lack of hardware supporting \
                the feature"]
#[allow(missing_copy_implementations)]
#[derive(Clone)]
pub struct RdSeed(());

impl RdSeed {
    #[experimental]
    pub fn new() -> Result<RdSeed, Error> {
        if util::is_intel() && util::has_rdseed() {
            return Ok(RdSeed(()));
        } else {
            return Err(Error::UnsupportedProcessor);
        }
    }

    /// Generate a value.
    #[inline]
    fn gen_value<T>(&self) -> T {
        let mut var;
        unsafe {
            asm!("1: rdseed $0; jnc 1b;" : "=r"(var));
        }
        var
    }

    /// Generate a u16 value.
    pub fn next_u16(&self) -> u16 {
        self.gen_value()
    }
}

impl Rng for RdSeed {
    fn next_u32(&mut self) -> u32 {
        self.gen_value()
    }

    fn next_u64(&mut self) -> u64 {
        self.gen_value()
    }
}
<|endoftext|>