1mod fullscreen_toggle;
7mod logging;
8use {
9 crate::trace,
10 anyhow::{Context, Result},
11 clap::Parser,
12 glfw::fail_on_errors,
13 std::sync::{atomic::AtomicBool, Arc},
14};
15
16pub use self::fullscreen_toggle::FullscreenToggle;
17
18pub trait App {
21 type Args: Sized + Parser;
22
23 fn new(window: &mut glfw::Window, args: Self::Args) -> Result<Self>
28 where
29 Self: Sized;
30
31 fn handle_event(
36 &mut self,
37 #[allow(unused_variables)] window: &mut glfw::Window,
38 #[allow(unused_variables)] event: glfw::WindowEvent,
39 ) -> Result<()> {
40 Ok(())
41 }
42
43 fn update(
48 &mut self,
49 #[allow(unused_variables)] window: &mut glfw::Window,
50 ) -> Result<()> {
51 Ok(())
52 }
53}
54
55pub fn app_main<A>() -> Result<()>
60where
61 A: App + 'static,
62{
63 let exit_result = try_app_main::<A>();
64 if let Some(err) = exit_result.as_ref().err() {
65 let result: String = err
66 .chain()
67 .skip(1)
68 .enumerate()
69 .map(|(index, err)| format!(" {}| {}\n\n", index, err))
70 .to_owned()
71 .collect();
72 log::error!(
73 "{}\n\n{}\n\nCaused by:\n{}\n\nBacktrace:\n{}",
74 "Application exited with an error!",
75 err,
76 result,
77 err.backtrace()
78 );
79 };
80 exit_result
81}
82
83fn try_app_main<A>() -> Result<()>
84where
85 A: App + 'static,
86{
87 logging::setup();
88
89 let args = argfile::expand_args(argfile::parse_fromfile, argfile::PREFIX)
90 .with_context(trace!("Error while expanding argfiles!"))?;
91 let args = A::Args::parse_from(args);
92
93 let mut glfw = glfw::init(fail_on_errors!())
94 .with_context(trace!("Unable to initalize GLFW!"))?;
95 glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi));
96 glfw.window_hint(glfw::WindowHint::ScaleToMonitor(true));
97 glfw.window_hint(glfw::WindowHint::Visible(false));
98
99 let interrupted = {
100 let interrupted = Arc::new(AtomicBool::new(false));
101 let cloned = interrupted.clone();
102 ctrlc::set_handler(move || {
103 interrupted.store(true, std::sync::atomic::Ordering::SeqCst);
104 })
105 .with_context(trace!("Unable to set the ctrl-c signal handler!"))?;
106 cloned
107 };
108
109 let (mut window, events) = glfw
110 .create_window(
111 640,
112 480,
113 "Shader Toy - Slang",
114 glfw::WindowMode::Windowed,
115 )
116 .with_context(trace!("Unable to create window!"))?;
117
118 let mut app = A::new(&mut window, args)
119 .with_context(trace!("Error while initializing the app!"))?;
120
121 window.show();
122 while !window.should_close() {
123 if interrupted.load(std::sync::atomic::Ordering::Relaxed) {
124 window.set_should_close(true);
125 };
126
127 glfw.poll_events();
128 for (_, event) in glfw::flush_messages(&events) {
129 app.handle_event(&mut window, event)?;
130 }
131
132 app.update(&mut window)?;
133 }
134
135 Ok(())
136}