mod fullscreen_toggle;
mod logging;
use {
crate::trace,
anyhow::{Context, Result},
clap::Parser,
glfw::fail_on_errors,
std::sync::{atomic::AtomicBool, Arc},
};
pub use self::fullscreen_toggle::FullscreenToggle;
pub trait App {
type Args: Sized + Parser;
fn new(window: &mut glfw::Window, args: Self::Args) -> Result<Self>
where
Self: Sized;
fn handle_event(
&mut self,
#[allow(unused_variables)] window: &mut glfw::Window,
#[allow(unused_variables)] event: glfw::WindowEvent,
) -> Result<()> {
Ok(())
}
fn update(
&mut self,
#[allow(unused_variables)] window: &mut glfw::Window,
) -> Result<()> {
Ok(())
}
}
pub fn app_main<A>()
where
A: App + 'static,
{
let exit_result = try_app_main::<A>();
if let Some(err) = exit_result.err() {
let result: String = err
.chain()
.skip(1)
.enumerate()
.map(|(index, err)| format!(" {}| {}\n\n", index, err))
.to_owned()
.collect();
log::error!(
"{}\n\n{}\n\nCaused by:\n{}\n\nBacktrace:\n{}",
"Application exited with an error!",
err,
result,
err.backtrace()
);
}
}
fn try_app_main<A>() -> Result<()>
where
A: App + 'static,
{
logging::setup();
let args = argfile::expand_args(argfile::parse_fromfile, argfile::PREFIX)
.with_context(trace!("Error while expanding argfiles!"))?;
let args = A::Args::parse_from(args);
let mut glfw = glfw::init(fail_on_errors!())
.with_context(trace!("Unable to initalize GLFW!"))?;
glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi));
glfw.window_hint(glfw::WindowHint::ScaleToMonitor(true));
glfw.window_hint(glfw::WindowHint::Visible(false));
let interrupted = {
let interrupted = Arc::new(AtomicBool::new(false));
let cloned = interrupted.clone();
ctrlc::set_handler(move || {
interrupted.store(true, std::sync::atomic::Ordering::SeqCst);
})
.with_context(trace!("Unable to set the ctrl-c signal handler!"))?;
cloned
};
let (mut window, events) = glfw
.create_window(
640,
480,
"Shader Toy - Slang",
glfw::WindowMode::Windowed,
)
.with_context(trace!("Unable to create window!"))?;
let mut app = A::new(&mut window, args)
.with_context(trace!("Error while initializing the app!"))?;
window.show();
while !window.should_close() {
if interrupted.load(std::sync::atomic::Ordering::Relaxed) {
window.set_should_close(true);
};
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
app.handle_event(&mut window, event)?;
}
app.update(&mut window)?;
}
Ok(())
}