use async_recursion::*; use batbox_cli as cli; use batbox_color::*; use batbox_la::*; use batbox_time::*; use futures::prelude::*; use geng_window as window; use rand::prelude::*; use ugli::Ugli; mod renderer { use super::*; #[derive(ugli::Vertex)] struct Vertex { a_pos: vec2, } pub struct Renderer { program: ugli::Program, data: ugli::VertexBuffer, } const PROGRAM_SOURCE: &str = r#" #ifdef VERTEX_SHADER attribute vec2 a_pos; uniform mat3 u_transform; void main() { gl_Position = mat4(u_transform) * vec4(a_pos, 0.0, 1.0); } #endif #ifdef FRAGMENT_SHADER uniform vec4 u_color; void main() { gl_FragColor = u_color; } #endif "#; impl Renderer { pub fn new(ugli: &Ugli) -> Self { Self { program: geng_shader::Library::new(ugli, false, None) .compile(PROGRAM_SOURCE) .unwrap(), data: ugli::VertexBuffer::new_static( ugli, vec![ Vertex { a_pos: vec2(-0.5, -0.5), }, Vertex { a_pos: vec2(0.5, -0.5), }, Vertex { a_pos: vec2(0.0, 0.5), }, ], ), } } pub fn draw( &self, framebuffer: &mut ugli::Framebuffer, transform: mat3, color: Rgba, ) { ugli::clear(framebuffer, Some(Rgba::BLACK), None, None); ugli::draw( framebuffer, &self.program, ugli::DrawMode::Triangles, &self.data, ugli::uniforms! { u_transform: transform, u_color: color, }, ugli::DrawParameters::default(), ); } } } use renderer::Renderer; #[derive(clap::Parser)] struct CliArgs { #[clap(flatten)] window: window::CliArgs, #[clap(long)] auto_close: Option, } #[async_recursion(?Send)] async fn space_escape(depth: usize, renderer: &Renderer, window: &window::Window) { log::info!("Entering depth {depth:?}"); let timer = Timer::new(); let transform = mat3::rotate(thread_rng().gen()) * mat3::scale_uniform((depth as f32 * 0.1).exp()); while let Some(event) = window.events().next().await { match event { window::Event::KeyPress { key } => match key { window::Key::Escape => { break; } window::Key::Space => { space_escape(depth + 1, renderer, window).await; } window::Key::F => { window.toggle_fullscreen(); } window::Key::M => { if window.cursor_locked() { log::info!("unlocking cursor"); window.unlock_cursor(); } else { log::info!("locking cursor"); window.lock_cursor(); } } window::Key::T if window.is_key_pressed(window::Key::ControlLeft) => { if window.is_editing_text() { log::info!("stop editing text"); window.stop_text_edit(); } else { log::info!("start editing text"); window.start_text_edit("text"); } } _ => {} }, window::Event::Draw => { let color = Hsla::new(timer.elapsed().as_secs_f64() as f32, 1.0, 0.5, 1.0).into(); window.with_framebuffer(|framebuffer| { renderer.draw(framebuffer, transform, color); }); } _ => {} } } log::info!("Exiting depth {depth:?}"); } fn main() { batbox_logger::init(); let args: CliArgs = cli::parse(); window::run( &{ let mut options = window::Options::new("geng window demo"); options.with_cli(&args.window); options.auto_close = args.auto_close.unwrap_or(false); options.start_hidden = true; options }, |window| async move { // TODO sleep window.show(); window.set_cursor_type(window::CursorType::Pointer); let log_events = async { let mut events = window.events(); while let Some(event) = events.next().await { if event != window::Event::Draw { log::info!("{event:?}"); } } }; let close_requested = async { window .events() .filter(|event| future::ready(*event == window::Event::CloseRequested)) .next() .await; }; let renderer = Renderer::new(window.ugli()); let space_escape = space_escape(0, &renderer, &window); futures::select! { () = log_events.fuse() => { unreachable!() }, () = close_requested.fuse() => { log::info!("Exiting because of request"); }, () = space_escape.fuse() => { log::info!("Exiting because space_escape finished"); }, } }, ); }