Opengl example using the C3 standard library gfx module

/*
	needs opengl bindings: `c3c vendor-fetch opengl`
*/
module app;
import std, opengl;

fn int main()
{
	// Initialize the platform windowing subsystem
	if (catch err = gfx::init("OpenGLApp", { .init_opengl }))
	{
		return 1; // opengl failed to initialize, maybe fallback to software rendering or something
	}
	defer gfx::finalize();

	// Create a window with an OpenGL context, centered on screen
	GfxWindow* win = gfx::create_window("Static UI", 0, 0, 800, 600, { .open_gl, .center })!!;
	defer win.destroy();
	win.set_exit_key(ESCAPE);

	// Make the OpenGL context current on the calling thread
	gfx::set_current_opengl_window(win);

	// Load OpenGL function pointers via the gfx proc address resolver
	opengl::init((GLLoadFn)&gfx::get_opengl_proc)!!;

	// Set clear color once
	gl::clearColor(red: 0.2f, green: 0.3f, blue: 0.3f, alpha: 1.0f);

	// Initial render
	GfxPoint px = win.get_size_in_pixels();
	gl::viewport(0, 0, px.x, px.y);
	gl::clear(gl::GL_COLOR_BUFFER_BIT);
	win.swap_opengl_buffers();

	// Mode A: Event-driven loop (Static UI / apps: sleeps until input arrives)
	while (!win.should_close())
	{
		// 1. Block thread until at least one event arrives
		if (try event = gfx::wait(gfx::WAIT_FOREVER))
		{
			handle_event(win, event);

			// 2. Drain all remaining queued events without blocking
			while (try queued = gfx::peep(win))
			{
				handle_event(win, queued);
			}
		}

		// 3. Redraw once for the entire batch of events
		gl::clear(gl::GL_COLOR_BUFFER_BIT);
		win.swap_opengl_buffers();
	}

	// Mode B: Continuous loop (Games / animations: non-blocking poll with V-Sync pacing)
	/*
	win.set_opengl_swap_interval(1); // Enable V-Sync to cap frame rate

	while (!win.should_close())
	{
		// 1. Poll and drain all pending events without blocking
		while (try event = gfx::poll(win))
		{
			handle_event(win, event);
		}

		// 2. Update and render continuously
		gl::clear(gl::GL_COLOR_BUFFER_BIT);
		win.swap_opengl_buffers(); // Blocks until next display refresh
	}
	*/
	return 0;
}

// Dispatch input and window state events
fn void handle_event(GfxWindow* win, GfxEvent event)
{
	switch (event.type)
	{
		case WINDOW_RESIZED:
		case SCALE_UPDATED:
			GfxPoint cur_px = win.get_size_in_pixels();
			gl::viewport(0, 0, cur_px.x, cur_px.y);

		default:
			break;
	}

	// Debug printout (comment out this block to disable):
	switch (event.type)
	{
		case KEY_PRESSED:
			io::printfn("Key pressed: %c (%d)", (char)event.key.value, event.key.value);
		case KEY_RELEASED:
			io::printfn("Key released: %c (%d)", (char)event.key.value, event.key.value);
		case MOUSE_BUTTON_PRESSED:
			io::printfn("Mouse down: %s", event.button.value);
		case MOUSE_BUTTON_RELEASED:
			io::printfn("Mouse up: %s", event.button.value);
		case MOUSE_MOTION:
			io::printfn("Mouse pos: %s", event.mouse.pos);
		case MOUSE_SCROLL:
			io::printfn("Mouse scroll: %s", event.delta.vector);
		default:
			break;
	}
}