1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
#![warn(missing_docs)] //! # pxl //! //! A simple framework for making graphical programs in Rust. `pxl` is intended to avoid //! Rust's most challenging concepts, while still providing a compelling platform upon //! which to develop graphical games and programs. //! //! ## Features //! //! - Pixel-based rendering //! - Sample-based audio synthesis //! - Custom vertex and fragment shaders //! - `pxl-build`, a compile-time resource loading crate //! - Action and text input mod runtime; use std::sync::{Arc, Mutex}; /// The number of audio samples in a second. Synthesizer /// implementations will need this to calculate the current /// time from the number of samples played so far. pub const SAMPLES_PER_SECOND: u32 = 48_000; /// An RGBA pixel. Components should normally be between /// `0.0` and `1.0` inclusive. #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq)] pub struct Pixel { /// The red component pub red: f32, /// The green component pub green: f32, /// The blue component pub blue: f32, /// The alpha component. Currently ignored, but in the /// future, pixels rendered with an alpha value of less /// than 1.0 may be transparent, exposing the desktop or /// window behind the `pxl` window. pub alpha: f32, } /// An image made of pixels. Used by the `pxl-build` crate /// for image resources pub struct Image<'pixels> { /// Width in pixels of the image pub width: usize, /// Height in pixels of the image pub height: usize, /// Pixels of the image, containing `width*height` pixels pub pixels: &'pixels [Pixel], } /// A single stereo audio sample, representing `1/SAMPLES_PER_SECOND` /// of an audio signal #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq)] pub struct Sample { /// The left channel value pub left: f32, /// The right channel value pub right: f32, } /// Enum representing input buttons /// /// In the current runtime, the arrow keys produce `Left`, `Right`, `Up` and /// `Down` events, and the spacebar produces `Action` events. /// /// Buttons are intended to be abstract, and in the future gamepad, /// touch, and/or keyboard input may produce Button events #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Button { /// Left button Left, /// Right button Right, /// Up button Up, /// Down button Down, /// Action button Action, } /// Enum representing the state of an input button #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum ButtonState { /// The button was pressed Pressed, /// The button was released Released, } /// Input events /// /// Note that a single keyboard press may generate both /// a `Button` and `Key` event. For example, the spacebar /// will generate both a `Button::Action` event and a /// `Key{character: ' '}` event. #[derive(Copy, Clone, Debug, PartialEq)] pub enum Event { /// A button event, representing a pressed or released button Button { /// The button in question button: Button, /// The state of the button: `Pressed` or `Released` state: ButtonState, }, /// A text input event Key { /// The input character character: char, }, } /// Trait for things that can generate sound /// /// When a computer program plays audio, it typically generates audio samples that /// are fed to a DAC, a digital-to-analog converter. This is highly latency-sensitive: /// audible pops and crackles may be heard if the program does not generate samples /// quickly enough. /// /// A `pxl::Program` may not be available to generate samples at all times, for example /// if it is busy rendering. To make sure that samples for the underlying audio hardware /// can be generated at all times, even during rendering, audio-sample synthesis is delegated /// to a dedicated `Synthesizer` object. /// /// A `pxl::Program` that wishes to play audio should return an `Arc<Mutex<Synthesizer>>` /// from `Program::synthesizer`. `Synthesizer::synthesize` will be called as needed on the /// returned `Sythesizer` object to generate samples to feed the underlying audio hardware. /// /// To communicate with and update the `Synthesizer`, the `pxl::Program` implementation /// should keep it's own `Arc<Mutex<Synthesizer>>` with a reference to the synthesizer /// object, locking it and updating it as needed. /// /// In order to avoid starving the audio hardware of samples, the lock holding the /// synthesizer should only be kept locked briefly. pub trait Synthesizer: Send + 'static { /// Synthesize audio /// /// Called by the runtime as needed to fill the outgoing audio buffer /// /// * `samples_played` β number of samples written by previous calls to synthesize /// * `output_buffer` β the audio samples that synthesize should write fn synthesize(&mut self, _samples_played: u64, _output_buffer: &mut [Sample]) {} } /// Trait representing a `pxl::Program` /// /// To run a program, see the `run` function below. pub trait Program: 'static { /// Initialize a new Program object fn new() -> Self where Self: Sized; /// Return the desired width and height of pixel surface /// /// Will be called immediately before calling `render()`. /// Determines the length of the pixel slice passed to /// `render()`. If (256, 256) is returned, the pixel /// slice passed to `render()` will contain 256 * 256, /// elements. fn dimensions(&self) -> (usize, usize); /// Return the vertex shader to be used in the runtime's /// rendering pipeline /// /// Will be called immediately before calling `render()` /// /// See `vertex_shader.glsl` for details of the interface /// that custom vertex shaders should adhere to. fn vertex_shader(&self) -> &str { include_str!("vertex_shader.glsl") } /// Return the fragment shader to be used in the runtime's /// rendering pipeline /// /// Will be called immediately before calling `render()` /// /// See `fragment_shader.glsl` for details of the interface /// that custom fragment shaders should adhere to. fn fragment_shader(&self) -> &str { include_str!("fragment_shader.glsl") } /// Return the title of the program /// /// Called by the runtime to set the window title fn title(&self) -> &str { "pxl" } /// Return true if the program should stop running /// /// Called by the runtime at the end of every pass through the event loop fn should_quit(&mut self) -> bool { false } /// Process events and update the state of the program /// /// Called by the runtime 60 times per second. /// /// * `events` β events that have occurred since the last call to `tick` fn tick(&mut self, _events: &[Event]) {} /// Draw to the display /// /// Called by the runtime whenever the display is ready to present a new frame /// /// WIDTH β first element of the tuple returned by `dimensions()` /// HEIGHT β second element of the tuple returned by `dimensions()` /// /// * `pixels` β a slice of pixels with `WIDTH * HEIGHT` elements /// `pixels[x + y * WIDTH]` is the `x`th pixel in the /// `y`th row, with `(0,0)` starting in the upper left /// corner of the screen fn render(&mut self, _pixels: &mut [Pixel]) {} /// The program's synthesizer /// /// Will be called by the runtime during initialization. If it returns /// Some, the contained Synthesizer will be moved to a dedicated audio /// thread and called periodically to produce samples for the outgoing /// audio stream. /// /// In order to prevent buffer underruns, avoid locking the `Mutex` /// containing the Synthesizer for long periods of time. fn synthesizer(&self) -> Option<Arc<Mutex<Synthesizer>>> { None } } /// Run a `pxl::Program`. `run` takes care of instantiating your /// program, so your program is passed as a type parameter, not /// as a value. /// /// For example, to get a 256 by 256 black window that does nothing, /// aside from being quietly awesome, do: /// /// ```no_run /// // Import the pxl create /// extern crate pxl; /// /// // Bring public members of pxl into scope /// use pxl::*; /// /// // Create an empty stuct to hold your program state /// struct AwesomeProgram { /// } /// /// // Implement `pxl::Program` for your program. Only /// // a few methods are required. /// impl Program for AwesomeProgram { /// // Create a new `AwesomeProgram` /// fn new() -> AwesomeProgram { /// AwesomeProgram{} /// } /// /// // Set the dimensions of the window /// fn dimensions(&self) -> (usize, usize) { /// (256, 256) /// } /// } /// /// // The crate's main function /// fn main() { /// // Run the program, using the beloved turbofish syntax to /// // indicate to the runtime which type of program to be run /// run::<AwesomeProgram>(); /// } /// ``` /// /// Once you have this working, you can start extending it /// by implementing the various methods in `pxl::Program`. /// /// Good luck and have fun! /// /// <3, /// Casey pub fn run<P: Program>() -> ! { // Instantiate a new program let program = P::new(); // Construct a new runtime let result = runtime::Runtime::new(Box::new(program)); // Run the program if let Err(error) = result.and_then(|runtime| runtime.run()) { // Print an error message if something went wrong eprintln!("{}", error); // Terminate with an status code indicating that something went wrong std::process::exit(1); } else { // Terminate with an status code indicating that nothing went wrong std::process::exit(0); } }