pxl 0.0.9

🕹 A simple framework for making games
Documentation
//!

extern crate cpal;
extern crate gl;
extern crate glutin;

mod display;
mod error;
mod speaker;

use std::{ffi::CString, mem, ptr, str, thread};

use super::*;

use self::{
  display::Display, error::Error, glutin::{GlContext, GlWindow}, speaker::Speaker,
};

static DEFAULT_PIXEL: Pixel = Pixel {
  red: 0.0,
  green: 0.0,
  blue: 0.0,
  alpha: 1.0,
};

pub struct Runtime {
  events: Vec<Event>,
  window_event_loop: glutin::EventsLoop,
  pixels: Vec<Pixel>,
  program: Box<Program>,
  should_quit: bool,
  gl_window: GlWindow,
  current_title: String,
  display: Display,
}

impl Runtime {
  pub fn new(program: Box<Program>) -> Result<Runtime, Error> {
    let window_event_loop = glutin::EventsLoop::new();

    let current_title = program.title().to_string();
    let dimensions = program.dimensions();
    let synthesizer = program.synthesizer();

    let window = glutin::WindowBuilder::new()
      .with_title(current_title.as_str())
      .with_dimensions(dimensions.0 as u32, dimensions.1 as u32);

    let context = glutin::ContextBuilder::new().with_vsync(true);

    let gl_window = GlWindow::new(window, context, &window_event_loop)?;

    unsafe {
      gl_window.make_current()?;
      gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _);
    }

    let display = Display::new()?;

    if let Some(synthesizer) = synthesizer {
      let speaker = Speaker::new(synthesizer)?;

      thread::spawn(move || {
        speaker.play();
      });
    }

    Ok(Runtime {
      should_quit: false,
      events: Vec::new(),
      pixels: Vec::new(),
      program,
      window_event_loop,
      gl_window,
      current_title,
      display,
    })
  }

  pub fn run(mut self) -> Result<(), Error> {
    while !self.should_quit {
      let mut new_size = None;
      let mut should_quit = false;
      let mut events = mem::replace(&mut self.events, Vec::new());

      events.clear();

      self.window_event_loop.poll_events(|event| {
        use self::glutin::WindowEvent::*;
        match event {
          glutin::Event::WindowEvent { event, .. } => match event {
            CloseRequested => should_quit = true,
            Resized(w, h) => new_size = Some((w, h)),
            KeyboardInput { input, .. } => if let Some(virtual_keycode) = input.virtual_keycode {
              use self::glutin::VirtualKeyCode::*;
              let button = match virtual_keycode {
                Up => Button::Up,
                Down => Button::Down,
                Left => Button::Left,
                Right => Button::Right,
                Space => Button::Action,
                _ => return,
              };

              use self::glutin::ElementState::*;
              let state = match input.state {
                Pressed => ButtonState::Pressed,
                Released => ButtonState::Released,
              };
              events.push(Event::Button { state, button });
            },
            ReceivedCharacter(character) => events.push(Event::Key { character }),
            _ => (),
          },
          _ => (),
        }
      });

      mem::replace(&mut self.events, events);

      if let Some((w, h)) = new_size {
        self.gl_window.resize(w, h);
      }

      self.program.tick(&self.events);

      let dimensions = self.program.dimensions();

      let pixel_count = dimensions.0 * dimensions.1;
      if self.pixels.len() != pixel_count {
        self.pixels.resize(pixel_count, DEFAULT_PIXEL);
      }

      self
        .display
        .set_shaders(self.program.vertex_shader(), self.program.fragment_shader())?;

      self.program.render(&mut self.pixels);
      self.should_quit = self.program.should_quit() | should_quit;
      let title = self.program.title();
      if title != self.current_title {
        self.gl_window.set_title(title);
        self.current_title.clear();
        self.current_title.push_str(&title);
      }

      self.display.present(&self.pixels, dimensions);

      self.gl_window.swap_buffers()?;
    }

    Ok(())
  }
}