1extern 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}