#![warn(missing_docs)]
#![doc(
html_logo_url = "https://jeronlau.plopgrizzly.com/cala/icon.svg",
html_favicon_url = "https://jeronlau.plopgrizzly.com/cala/icon.svg"
)]
use std::ffi::c_void;
#[macro_export(self)]
macro_rules! shader {
($shadername: literal) => {
include!(concat!(env!("OUT_DIR"), "/res/", $shadername, ".rs"));
};
}
mod keycodes;
mod mat4;
mod shape;
#[cfg(unix)]
mod wayland;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
mod opengl;
pub use self::keycodes::*;
pub use self::mat4::*;
pub use self::shape::*;
enum NwinHandle {
#[cfg(all(
unix,
not(any(
target_os = "android",
target_os = "macos",
target_os = "ios"
))
))]
Wayland(*mut c_void),
}
#[allow(unused)]
enum DrawHandle {
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
Gl(*mut c_void),
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
Vulkan(*mut c_void),
#[cfg(any(target_os = "macos", target_os = "ios"))]
Metal(*mut c_void),
}
trait Nwin {
fn handle(&self) -> NwinHandle;
fn connect(&mut self, draw: &mut dyn Draw);
fn run(&mut self) -> bool;
fn dimensions(&self) -> (u16, u16);
fn key_held(&self, key: crate::Key) -> bool;
}
trait Draw {
fn handle(&self) -> DrawHandle;
fn connect(&mut self, connection: *mut c_void);
fn begin_draw(&mut self);
fn finish_draw(&mut self);
fn background(&mut self, r: f32, g: f32, b: f32);
fn shader_new(&mut self, builder: ShaderBuilder) -> Box<dyn Nshader>;
fn group_new(&mut self) -> Box<dyn Ngroup>;
fn draw(&mut self, shader: &dyn Nshader, shape: &mut dyn Ngroup);
fn graphic(
&mut self,
pixels: &[u8],
width: usize,
height: usize,
) -> Box<dyn Ngraphic>;
fn bind_graphic(&mut self, graphic: &dyn Ngraphic);
fn toolbar(
&mut self,
w: u16,
height: u16,
toolbar_height: u16,
shader: &dyn Nshader,
shape: &mut dyn Ngroup,
);
fn camera(&mut self, shader: &dyn Nshader, cam: Transform);
fn tint(&mut self, shader: &dyn Nshader, tint: [f32; 4]);
}
trait Nshader {
fn depth(&self) -> Option<i32>;
fn tint(&self) -> Option<i32>;
fn gradient(&self) -> bool;
fn graphic(&self) -> bool;
fn blending(&self) -> bool;
fn bind(&self);
fn program(&self) -> u32;
}
trait Ngroup {
fn len(&self) -> i32;
fn bind(&mut self);
fn id(&self) -> u32;
fn push(&mut self, shape: &crate::Shape, transform: &crate::Transform);
fn push_tex(
&mut self,
shape: &crate::Shape,
transform: &crate::Transform,
tex_coords: ([f32; 2], [f32; 2]),
);
}
pub struct Group(Box<dyn Ngroup>);
impl Group {
pub fn push(&mut self, shape: &crate::Shape, transform: &crate::Transform) {
self.0.push(shape, transform);
}
pub fn push_tex(
&mut self,
shape: &crate::Shape,
transform: &crate::Transform,
tex_coords: ([f32; 2], [f32; 2]),
) {
self.0.push_tex(shape, transform, tex_coords);
}
}
trait Ngraphic {
fn id(&self) -> u32;
fn width(&self) -> u16;
fn height(&self) -> u16;
fn resize(&mut self, pixels: &[u8], width: usize);
fn update(&mut self, updater: &mut dyn FnMut(&mut [u8], u16));
}
pub struct Graphic(Box<dyn Ngraphic>);
fn nearly_equal(a: f32, b: f32) -> bool {
let abs_a = a.abs();
let abs_b = b.abs();
let diff = (a - b).abs();
let both = abs_a + abs_b;
if a.to_bits() == b.to_bits() {
true
} else if a.to_bits() == 0
|| b.to_bits() == 0
|| (abs_a + abs_b < std::f32::MIN_POSITIVE)
{
diff < (std::f32::EPSILON * std::f32::MIN_POSITIVE)
} else if both < std::f32::MAX {
diff / both < std::f32::EPSILON
} else {
diff / std::f32::MAX < std::f32::EPSILON
}
}
pub struct Shader(Box<dyn Nshader>);
pub struct ShaderBuilder {
pub tint: bool,
pub gradient: bool,
pub graphic: bool,
pub depth: bool,
pub blend: bool,
pub opengl_frag: &'static str,
pub opengl_vert: &'static str,
}
pub struct Window {
toolbar_graphic: Graphic,
toolbar_shader: Shader,
toolbar_shape: Group,
toolbar_callback: fn(&mut [u8], u16),
pub toolbar_height: u16,
draw: Box<dyn Draw>,
nwin: Box<dyn Nwin>,
redraw: fn(nanos: u64) -> (),
}
impl Window {
pub fn new(
name: &str,
run: fn(nanos: u64) -> (),
toolbar: fn(&mut Self) -> (Shader, Group),
) -> Box<Self> {
let mut window = Box::new(unsafe { std::mem::zeroed() });
let mut win = None;
#[cfg(unix)]
{
win = win.or_else(|| wayland::new(name, &mut window));
}
win.or_else(|| {
panic!("Couldn't find a window manager.");
});
let mut draw = None;
{
draw = draw.or_else(|| opengl::new(&mut window));
}
unsafe {
std::ptr::write(
&mut window.draw,
draw.or_else(|| {
panic!("Couldn't find a graphics API.");
})
.unwrap(),
);
}
window.nwin.connect(&mut *window.draw);
unsafe {
std::ptr::write(&mut window.redraw, run);
}
window.toolbar_height = 48;
let (toolbar_shader, toolbar_shape) = (toolbar)(&mut window);
let width = window.nwin.dimensions().0;
let height = window.toolbar_height;
let pixels = vec![255; (width * window.toolbar_height) as usize * 4];
let toolbar_graphic =
window.graphic(pixels.as_slice(), width as usize, height as usize);
fn toolbar_callback(_: &mut [u8], _: u16) {}
unsafe {
std::ptr::write(&mut window.toolbar_shader, toolbar_shader);
std::ptr::write(&mut window.toolbar_shape, toolbar_shape);
std::ptr::write(&mut window.toolbar_graphic, toolbar_graphic);
std::ptr::write(&mut window.toolbar_callback, toolbar_callback);
}
window
}
pub fn run(&mut self) -> bool {
self.nwin.run()
}
pub fn background(&mut self, r: f32, g: f32, b: f32) {
self.draw.background(r, g, b)
}
pub fn shader_new(&mut self, builder: ShaderBuilder) -> Shader {
Shader(self.draw.shader_new(builder))
}
pub fn group_new(&mut self) -> Group {
Group(self.draw.group_new())
}
pub fn graphic(
&mut self,
pixels: &[u8],
width: usize,
height: usize,
) -> Graphic {
Graphic(self.draw.graphic(pixels, width, height))
}
pub fn update_graphic(
&mut self,
graphic: &mut Graphic,
closure: &mut dyn FnMut(&mut [u8], u16),
) {
graphic.0.update(closure);
}
pub fn camera(&mut self, shader: &Shader, cam: Transform) {
self.draw.camera(&*shader.0, cam)
}
pub fn tint(&mut self, shader: &Shader, color: [f32; 4]) {
self.draw.tint(&*shader.0, color)
}
pub fn draw_graphic(
&mut self,
shader: &Shader,
shape: &mut Group,
graphic: &Graphic,
) {
self.draw.bind_graphic(&*graphic.0);
self.draw(shader, shape);
}
pub fn draw(&mut self, shader: &Shader, group: &mut Group) {
self.draw.draw(&*shader.0, &mut *group.0);
}
fn draw_toolbar(
&mut self,
shader: &Shader,
shape: &mut Group,
graphic: &Graphic,
) {
self.draw.bind_graphic(&*graphic.0);
self.draw.toolbar(
self.nwin.dimensions().0,
self.nwin.dimensions().1,
self.toolbar_height,
&*shader.0,
&mut *shape.0,
);
}
pub fn toolbar(&mut self, callback: fn(&mut [u8], u16)) {
self.toolbar_graphic.0.update(&mut |a, b| callback(a, b));
self.toolbar_callback = callback;
}
pub fn key(&self, key: Key) -> bool {
self.nwin.key_held(key)
}
pub fn aspect(&self) -> f32 {
let (w, h) = self.nwin.dimensions();
let (w, h) = (f32::from(w), f32::from(h));
h / w
}
}