Banjo API 1.0.0-rc.2
Low-level C99 game development API
Loading...
Searching...
No Matches
start.c

Hello, Window: open a window, draw shapes, react to ESC.

Hello, Window: open a window, draw shapes, react to ESC. This is the canonical first program in Banjo. It opens a 640×480 window and registers a draw callback that paints a red circle and a cyan rectangle, then runs an event loop until the user closes the window or presses ESC.

What you'll learn

  • Drive the program through Banjo's application lifecycle by passing setup, step, and teardown callbacks to bj_run_app.
  • Initialise Banjo's video subsystem with bj_begin.
  • Open a window with bj_bind_window. The window owns its framebuffer; there's no separate renderer object to create.
  • Install the built-in ESC-to-close key callback (bj_set_key_callback + bj_close_on_escape).
  • Register a draw callback with bj_set_draw_callback. Inside the callback, reach the framebuffer with bj_render_target_bitmap, clear it, and draw 2D primitives.
  • Request the first paint with bj_invalidate_window. Banjo fires the callback on the next natural-cadence event; you never call a present.
  • Pump events with bj_dispatch_events until the user asks to quit.
  • Tear everything down in reverse order with bj_unbind_window and bj_end.

Prerequisites

You need CMake 3.21+, a C99 compiler, and (on Linux) the platform development headers. See the Prerequisites topic for the one-shot install command for your distro.

Project setup

Create a new directory:

mkdir my_banjo_app && cd my_banjo_app

Drop this file in as main.c, then create CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(MyBanjoApp C)
include(FetchContent)
FetchContent_Declare(
banjo
GIT_REPOSITORY https://forge.codework-orange.com/OragonEfreet/banjo.git
GIT_TAG v0.1.5 # pin a release tag (see the Releases page)
)
FetchContent_MakeAvailable(banjo)
add_executable(my_banjo_app main.c)
target_link_libraries(my_banjo_app PRIVATE banjo)

Build and run:

cmake -B build
cmake --build build
./build/my_banjo_app

(On Windows the binary lives under build\Debug\ or build\Release\.)

Walkthrough

1. The application lifecycle

Every banjo program is driven by an application handle. main itself is small and uniform across tutorials: bj_run_app receives the setup, step, and teardown callbacks directly, creates an internal app handle, drives the loop, and releases the handle on return. Everything interesting happens inside the three callbacks.

On Linux/macOS/Windows bj_run_app is a plain loop. On Emscripten it hands the step callback to the browser's run loop instead. Your callbacks don't need to know which.

2. Initialisation (the setup callback)

bj_begin starts the requested subsystems, here just video. It returns BJ_FALSE if no backend was selected (see Windows for the fall-through chain). On failure, calling bj_quit_app then returning tells the run loop to skip the step phase and proceed straight to teardown.

bj_bind_window opens a 640×480 window titled "My First Banjo App" and allocates the per-window framebuffer behind it; there's no separate renderer object to manage.

bj_set_key_callback with bj_close_on_escape is a one-liner shortcut: it installs a built-in callback that closes the active window when ESC is pressed.

bj_set_draw_callback registers on_draw as the function banjo fires whenever the window has pending damage. bj_invalidate_window marks the whole window dirty so banjo paints it on the next natural-cadence event (compositor frame callback, WM_PAINT, drawRect:, requestAnimationFrame, whichever the backend uses).

3. The draw callback

Banjo passes the callback a bj_render_target. Reach the software framebuffer with bj_render_target_bitmap, then draw with the regular Bitmap and Drawing functions: bj_clear_bitmap fills it with the current clear colour, bj_make_bitmap_pixel converts an (R, G, B) triple into a packed pixel value matching the bitmap's pixel mode (so the same code works whether the framebuffer is RGB565 or XRGB8888), and bj_draw_filled_circle / bj_draw_rectangle mutate the framebuffer. The backend presents what you drew after the callback returns; you never call a present.

The dirty argument carries the damaged region: a sub-rect when only part of the window needs repainting (uncovered from behind another window, OS resize, etc.), or NULL when the whole window is dirty. This tutorial just repaints the whole scene each call; load_bmp.c shows the partial-damage path.

4. The step callback

The step callback receives a bj_tick_info giving real wall-clock delta (seconds since the previous step) and a [0, 1) alpha interpolation factor against the configured fixed-step rate. This program ignores both since it doesn't run physics; the physics examples (physics_kinematics.c, physics_particle.c) and the interpolation.c demo put them to use.

bj_dispatch_events drains the event queue and fires registered event callbacks (including the ESC handler installed in setup). Calling bj_quit_app from inside the step exits the loop on the next iteration; here we forward the window's close request from bj_should_close_window.

Notice there is no manual sleep here. bj_run_app paces the loop for you at 60 iterations per second by default, yielding the CPU between iterations so a windowed program doesn't pin a core. Change or remove the cap with bj_set_frame_rate (pass 0 to run uncapped).

Because the scene is static, step does not invalidate the window each iteration; banjo only fires the draw callback when there's damage. A real animated program would bj_invalidate_window every step to drive its redraws.

5. The teardown callback

Release in reverse order: bj_unbind_window, then bj_end. The test harness's allocator tracking will scream if any of these are skipped. See the error management topic (Error Management) for the leak diagnostic. The app handle is released automatically when bj_run_app returns.

What's next

See the Windows and Audio topics for the subsystem rundowns.

#include <banjo/app.h>
#include <banjo/main.h>
#include <banjo/bitmap.h>
#include <banjo/draw.h>
#include <banjo/event.h>
#include <banjo/system.h>
#include <banjo/time.h>
#include <banjo/window.h>
static void on_draw(
struct bj_window* w,
struct bj_render_target* target,
const struct bj_rect* dirty,
void* user_data
) {
(void)w; (void)dirty; (void)user_data;
const uint32_t red = bj_make_bitmap_pixel(bmp, 0xFF, 0x00, 0x00);
const uint32_t cyan = bj_make_bitmap_pixel(bmp, 0x00, 0xFF, 0xFF);
bj_draw_filled_circle(bmp, 320, 240, 100, red);
bj_draw_rectangle(bmp, &(bj_rect){.x = 200, .y = 120, .w = 240, .h = 240}, cyan);
}
static void* setup(struct bj_app* app, void* init_data) {
(void)init_data;
bj_quit_app(app, 1);
return 0;
}
window = bj_bind_window("My First Banjo App", 100, 100, 640, 480, 0, 0);
return 0;
}
static void step(struct bj_app* app, struct bj_tick_info tick, void* user_data) {
(void)tick;
(void)user_data;
bj_quit_app(app, 0);
}
}
static void teardown(struct bj_app* app, void* user_data) {
(void)app; (void)user_data;
bj_end();
}
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
return bj_run_app(setup, step, 0, teardown, 0);
}
Application lifecycle: callback-driven setup, step, and teardown.
int main(int argc, char *argv[])
Definition audio_pcm.c:177
static void step(struct bj_app *app, struct bj_tick_info tick, void *user_data)
Definition audio_pcm.c:144
static void teardown(struct bj_app *app, void *user_data)
Definition audio_pcm.c:170
static void * setup(struct bj_app *app, void *init_data)
Definition audio_pcm.c:107
Header file for Bitmap type.
static void on_draw(struct bj_window *w, struct bj_render_target *target, const struct bj_rect *dirty, void *user_data)
Definition bitmap_blit.c:32
bj_window * window
Definition bitmap_blit.c:24
Header file for Bitmap drawing functions.
Sytem event management API.
int bj_run_app(bj_app_setup_fn setup, bj_app_step_fn step, bj_app_fixed_step_fn fixed_step, bj_app_teardown_fn teardown, void *init_data)
Drive the application lifecycle.
void bj_quit_app(struct bj_app *app, int exit_code)
Signal the given application to exit on the next iteration.
Timing snapshot handed to the step and fixed-step callbacks.
Definition app.h:106
void bj_clear_bitmap(struct bj_bitmap *bitmap)
Fills the entire bitmap with the clear colour.
uint32_t bj_make_bitmap_pixel(struct bj_bitmap *bitmap, uint8_t red, uint8_t green, uint8_t blue)
Returns an opaque value representing a pixel colour, given its RGB composition.
struct bj_render_target bj_render_target
Definition api.h:346
struct bj_bitmap bj_bitmap
Definition api.h:328
struct bj_window bj_window
Definition api.h:354
void bj_draw_rectangle(struct bj_bitmap *bitmap, const struct bj_rect *area, uint32_t pixel)
Draws a rectangle in the given bitmap.
void bj_draw_filled_circle(struct bj_bitmap *bitmap, int cx, int cy, int radius, uint32_t color)
Draw a filled circle onto a bitmap.
void bj_dispatch_events(void)
Poll and dispatch all pending events.
void bj_close_on_escape(struct bj_window *window, const struct bj_key_event *event, void *user_data)
Handle the ESC key to close a window.
bj_key_callback_fn bj_set_key_callback(bj_key_callback_fn callback, void *user_data)
Set the global callback for keyboard key events.
Axis-aligned rectangle: a top-left corner plus a width and height.
Definition rect.h:33
bj_bool bj_begin(int systems, struct bj_error **error)
Initialises the system.
void bj_end(void)
De-initialises the system.
@ BJ_VIDEO_SYSTEM
Definition system.h:81
void bj_set_draw_callback(struct bj_window *window, bj_window_draw_fn fn, void *user_data)
Register the redraw callback for window.
static void bj_invalidate_window(struct bj_window *window)
Mark the whole window as needing a repaint.
Definition window.h:470
struct bj_window * bj_bind_window(const char *title, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t flags, struct bj_error **error)
Create a new struct bj_window with the specified attributes.
struct bj_bitmap * bj_render_target_bitmap(struct bj_render_target *target)
Reach the software framebuffer behind a render target.
bj_bool bj_should_close_window(struct bj_window *window)
Get the close flag state of a window.
void bj_unbind_window(struct bj_window *window)
Deletes a struct bj_window object and releases associated memory.
Portable main() replacement with platform-aware entry shim.
Header file for system interactions.
Header file for time manipulation utilities.
Header file for bj_window type.