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
mod runtime;

use std::sync::{Arc, Mutex};

pub const SAMPLES_PER_SECOND: u32 = 48_000;

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Pixel {
  pub red: f32,
  pub green: f32,
  pub blue: f32,
  pub alpha: f32,
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Sample {
  pub left: f32,
  pub right: f32,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Button {
  Left,
  Right,
  Up,
  Down,
  Action,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ButtonState {
  Pressed,
  Released,
}

#[derive(Copy, Clone, Debug)]
pub enum Event {
  Button { state: ButtonState, button: Button },
  Key { character: char },
}

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]) {}
}

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()`
  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()`
  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
  }
}

pub fn run<P: Program>() -> ! {
  let program = P::new();
  let result = runtime::Runtime::new(Box::new(program)).and_then(|runtime| runtime.run());

  if let Err(error) = result {
    eprintln!("{}", error);
    std::process::exit(1);
  } else {
    std::process::exit(0);
  }
}