pxl/runtime/
mod.rs

1//!
2
3extern crate cpal;
4extern crate gl;
5extern crate glutin;
6
7mod display;
8mod error;
9mod speaker;
10
11use std::{ffi::CString, mem, ptr, str, thread};
12
13use super::*;
14
15use self::{
16  display::Display, error::Error, glutin::{GlContext, GlWindow}, speaker::Speaker,
17};
18
19static DEFAULT_PIXEL: Pixel = Pixel {
20  red: 0.0,
21  green: 0.0,
22  blue: 0.0,
23  alpha: 1.0,
24};
25
26pub struct Runtime {
27  events: Vec<Event>,
28  window_event_loop: glutin::EventsLoop,
29  pixels: Vec<Pixel>,
30  program: Box<Program>,
31  should_quit: bool,
32  gl_window: GlWindow,
33  current_title: String,
34  display: Display,
35}
36
37impl Runtime {
38  pub fn new(program: Box<Program>) -> Result<Runtime, Error> {
39    let window_event_loop = glutin::EventsLoop::new();
40
41    let current_title = program.title().to_string();
42    let dimensions = program.dimensions();
43    let synthesizer = program.synthesizer();
44
45    let window = glutin::WindowBuilder::new()
46      .with_title(current_title.as_str())
47      .with_dimensions(dimensions.0 as u32, dimensions.1 as u32);
48
49    let context = glutin::ContextBuilder::new().with_vsync(true);
50
51    let gl_window = GlWindow::new(window, context, &window_event_loop)?;
52
53    unsafe {
54      gl_window.make_current()?;
55      gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _);
56    }
57
58    let display = Display::new()?;
59
60    if let Some(synthesizer) = synthesizer {
61      let speaker = Speaker::new(synthesizer)?;
62
63      thread::spawn(move || {
64        speaker.play();
65      });
66    }
67
68    Ok(Runtime {
69      should_quit: false,
70      events: Vec::new(),
71      pixels: Vec::new(),
72      program,
73      window_event_loop,
74      gl_window,
75      current_title,
76      display,
77    })
78  }
79
80  pub fn run(mut self) -> Result<(), Error> {
81    while !self.should_quit {
82      let mut new_size = None;
83      let mut should_quit = false;
84      let mut events = mem::replace(&mut self.events, Vec::new());
85
86      events.clear();
87
88      self.window_event_loop.poll_events(|event| {
89        use self::glutin::WindowEvent::*;
90        match event {
91          glutin::Event::WindowEvent { event, .. } => match event {
92            CloseRequested => should_quit = true,
93            Resized(w, h) => new_size = Some((w, h)),
94            KeyboardInput { input, .. } => if let Some(virtual_keycode) = input.virtual_keycode {
95              use self::glutin::VirtualKeyCode::*;
96              let button = match virtual_keycode {
97                Up => Button::Up,
98                Down => Button::Down,
99                Left => Button::Left,
100                Right => Button::Right,
101                Space => Button::Action,
102                _ => return,
103              };
104
105              use self::glutin::ElementState::*;
106              let state = match input.state {
107                Pressed => ButtonState::Pressed,
108                Released => ButtonState::Released,
109              };
110              events.push(Event::Button { state, button });
111            },
112            ReceivedCharacter(character) => events.push(Event::Key { character }),
113            _ => (),
114          },
115          _ => (),
116        }
117      });
118
119      mem::replace(&mut self.events, events);
120
121      if let Some((w, h)) = new_size {
122        self.gl_window.resize(w, h);
123      }
124
125      self.program.tick(&self.events);
126
127      let dimensions = self.program.dimensions();
128
129      let pixel_count = dimensions.0 * dimensions.1;
130      if self.pixels.len() != pixel_count {
131        self.pixels.resize(pixel_count, DEFAULT_PIXEL);
132      }
133
134      self
135        .display
136        .set_shaders(self.program.vertex_shader(), self.program.fragment_shader())?;
137
138      self.program.render(&mut self.pixels);
139      self.should_quit = self.program.should_quit() | should_quit;
140      let title = self.program.title();
141      if title != self.current_title {
142        self.gl_window.set_title(title);
143        self.current_title.clear();
144        self.current_title.push_str(&title);
145      }
146
147      self.display.present(&self.pixels, dimensions);
148
149      self.gl_window.swap_buffers()?;
150    }
151
152    Ok(())
153  }
154}