#![warn(missing_docs)]
#![doc(
html_logo_url = "https://libcala.github.io/logo.svg",
html_favicon_url = "https://libcala.github.io/icon.svg"
)]
use std::ffi::c_void;
#[macro_export]
macro_rules! shader {
($shadername: literal) => {
include!(concat!(env!("OUT_DIR"), "/res/", $shadername, ".rs"));
};
}
mod ffi;
pub mod input;
mod mat4;
mod shape;
#[cfg(unix)]
mod wayland;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
mod opengl;
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;
#[allow(clippy::borrowed_box)] fn connect(&mut self, draw: &mut Box<dyn Draw>);
fn run(&mut self, window: *mut crate::Window) -> bool;
fn dimensions(&self) -> (u16, u16);
}
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: &dyn Ngroup);
fn graphic(
&mut self,
pixels: &[u8],
width: usize,
height: usize,
) -> Box<dyn Ngraphic>;
fn bind_graphic(&mut self, graphic: &dyn Ngraphic);
fn camera(&mut self, cam: Transform);
fn tint(&mut self, shader: &dyn Nshader, tint: [f32; 4]);
fn resize(&mut self, width: u16, height: u16);
}
trait Nshader {
fn depth(&self) -> bool;
fn camera(&self) -> 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(&self);
fn id(&self) -> u32;
fn write(
&mut self,
location: (usize, usize),
shape: &crate::Shape,
transform: &crate::Transform,
) -> (usize, usize);
fn write_texcoords(
&mut self,
location: (usize, usize),
shape: &crate::Shape,
transform: &crate::Transform,
tex_coords: ([f32; 2], [f32; 2]),
) -> (usize, usize);
}
pub struct Group(Box<dyn Ngroup>);
impl Group {
pub fn write(
&mut self,
location: (usize, usize),
shape: &crate::Shape,
transform: &crate::Transform,
) -> (usize, usize) {
self.0.write(location, shape, transform)
}
pub fn write_tex(
&mut self,
location: (usize, usize),
shape: &crate::Shape,
transform: &crate::Transform,
tex_coords: ([f32; 2], [f32; 2]),
) -> (usize, usize) {
self.0
.write_texcoords(location, 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 RasterId(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 {
draw: Box<dyn Draw>,
nwin: Box<dyn Nwin>,
}
impl Window {
pub fn new(
name: &str,
run: fn(window: &mut Window, elapsed: std::time::Duration) -> (),
) -> Self {
let mut nwin = Err("No backends built!".to_string())
.or_else(|_| wayland::Wayland::new(name, run))
.map_err(|e| format!("Couldn't find a window manager: {}", e))
.unwrap();
let mut draw = None
.or_else(|| opengl::OpenGL::new(&mut *nwin))
.expect("Couldn't find a GPU library.");
nwin.connect(&mut draw);
Window { nwin, draw }
}
pub fn run(&mut self) -> bool {
let this: *mut _ = self;
self.nwin.run(this)
}
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,
) -> RasterId {
RasterId(self.draw.graphic(pixels, width, height))
}
pub fn update_graphic(
&mut self,
graphic: &mut RasterId,
closure: &mut dyn FnMut(&mut [u8], u16),
) {
graphic.0.update(closure);
}
pub fn camera(&mut self, cam: Transform) {
self.draw.camera(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: &Group,
graphic: &RasterId,
) {
self.draw.bind_graphic(&*graphic.0);
self.draw(shader, shape);
}
pub fn draw(&mut self, shader: &Shader, group: &Group) {
self.draw.draw(&*shader.0, &*group.0);
}
pub fn aspect(&self) -> f32 {
let (w, h) = self.nwin.dimensions();
let (w, h) = (f32::from(w), f32::from(h));
h / w
}
}
impl Drop for Window {
fn drop(&mut self) {
std::process::exit(0);
}
}