bracket-terminal 0.8.7

ASCII/Codepage 437 terminal emulator with a game loop. Defaults to OpenGL, also support WebGPU (for Vulkan/Metal/WGPU), Curses and Crossterm for output. Part of the bracket-lib family.
Documentation
use bracket_terminal::prelude::*;
#[cfg(feature = "opengl")]
use glow::HasContext;
use glow::{NativeVertexArray, NativeBuffer};
use std::mem;

bracket_terminal::add_wasm_support!();

#[cfg(not(target_arch = "wasm32"))]
const VERTEX_SHADER_SOURCE: &str = r#"
    #version 330 core
    layout (location = 0) in vec3 aPos;
    void main() {
       gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    }
"#;

#[cfg(not(target_arch = "wasm32"))]
const FRAGMENT_SHADER_SOURCE: &str = r#"
    #version 330 core
    out vec4 FragColor;
    void main() {
       FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
    }
"#;

#[cfg(target_arch = "wasm32")]
const VERTEX_SHADER_SOURCE: &str = r#"#version 300 es
    precision mediump float;
    layout (location = 0) in vec3 aPos;
    void main() {
       gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    }
"#;

#[cfg(target_arch = "wasm32")]
const FRAGMENT_SHADER_SOURCE: &str = r#"#version 300 es
    precision mediump float;
    out vec4 FragColor;
    void main() {
       FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
    }
"#;

#[cfg(feature = "opengl")]
fn gl_setup(gl: &glow::Context, state: &mut State) {
    state.my_shader = Some(Shader::new(
        gl,
        VERTEX_SHADER_SOURCE,
        FRAGMENT_SHADER_SOURCE,
    ));

    let vertices: [f32; 9] = [
        -0.5, -0.5, 0.0, // left
        0.5, -0.5, 0.0, // right
        0.0, 0.5, 0.0, // top
    ];
    unsafe {
        let vao = gl.create_vertex_array().unwrap();
        let vbo = gl.create_buffer().unwrap();

        gl.bind_vertex_array(Some(vao));
        gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
        gl.buffer_data_u8_slice(
            glow::ARRAY_BUFFER,
            &vertices.align_to::<u8>().1,
            glow::STATIC_DRAW,
        );

        let stride = 3 * mem::size_of::<f32>() as i32;
        gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, stride, 0);
        gl.enable_vertex_attrib_array(0);

        state.vao = Some(vao);
        state.vbo = Some(vbo);

        gl.bind_buffer(glow::ARRAY_BUFFER, None);
        gl.bind_vertex_array(None);
    }
}

#[cfg(feature = "opengl")]
fn gl_render(gs: &mut dyn std::any::Any, gl: &glow::Context) {
    let state = gs.downcast_ref::<State>().unwrap();
    unsafe {
        state.my_shader.as_ref().unwrap().useProgram(gl);
        gl.bind_vertex_array(state.vao);
        gl.draw_arrays(glow::TRIANGLES, 0, 3);
        gl.bind_vertex_array(None);
    }
}

#[cfg(not(target_arch = "wasm32"))]
struct State {
    setup_gl: bool,
    my_shader: Option<Shader>,
    vao: Option<NativeVertexArray>,
    vbo: Option<NativeBuffer>,
}

#[cfg(target_arch = "wasm32")]
struct State {
    setup_gl: bool,
    my_shader: Option<Shader>,
    vao: Option<glow::WebVertexArrayKey>,
    vbo: Option<glow::WebBufferKey>,
}

impl GameState for State {
    #[cfg(feature = "opengl")]
    fn tick(&mut self, ctx: &mut BTerm) {
        if !self.setup_gl {
            let mut be = BACKEND.lock();
            let gl = be.gl.as_ref().unwrap();
            self.setup_gl = true;

            gl_setup(gl, self);
            be.gl_callback = Some(gl_render);
        }

        ctx.print(1, 1, "Hello, the triangle is a native OpenGL call.");
    }

    #[cfg(not(feature = "opengl"))]
    fn tick(&mut self, ctx: &mut BTerm) {
        ctx.print(1, 1, "This example is OpenGL (native or WASM) only.");
    }
}

fn main() -> BError {
    let context = BTermBuilder::simple80x50()
        .with_title("Hello Native GL!")
        .build()?;

    let gs: State = State {
        setup_gl: false,
        my_shader: None,
        vao: None,
        vbo: None,
    };

    main_loop(context, gs)
}