Tutorial: Input via callbacks.
Tutorial: Input via callbacks. Register handlers for keyboard, mouse buttons, cursor motion, and enter/leave events.
Where start.c got you a window and ESC-to-quit, this tutorial shows the general callback-based event-handling pattern. You'll register a dedicated function for each event type, see how Banjo plumbs your user_data pointer through, and see how to share state across the App API's setup / step / teardown callbacks.
What you'll learn
Prerequisites
You've already worked through start.c: opening a window, the dispatch loop, and the basic teardown order. This tutorial assumes you know what bj_begin, bj_bind_window, and bj_dispatch_events do.
Walkthrough
1. The callback signature
Every event callback follows the same shape:
void cb(bj_window* window, const T_event* event, void* user_data);
where T_event is one of bj_cursor_event, bj_button_event, bj_key_event, or bj_enter_event. window is the window the event targets; user_data is whatever pointer you passed at registration time, opaque to Banjo. (Window resize uses a slightly different callback, registered per-window and handed the new size directly; see step 5.)
2. Sharing state across callbacks
start.c only needed a file-scope window pointer. This tutorial wants to count events of each type, and the counter needs to be visible from setup, step, teardown, and every event callback. The App API plumbs that for us: the user_data pointer returned by setup is passed to step and teardown, and we pass the same pointer to bj_set_key_callback and friends. Allocate the counter in setup, return its address, and every callback receives it.
3. Callbacks fire from bj_dispatch_events
Registering a callback doesn't start a thread. Callbacks fire synchronously, on your main thread, from inside bj_dispatch_events. That's the same call site that drains the queue when you use polling; the two styles share the same machinery.
4. ESC handler, by hand
start.c used the built-in bj_close_on_escape shortcut. This tutorial does the same thing manually inside key_callback: when the key event matches BJ_KEY_ESCAPE, call bj_set_window_should_close. Doing it by hand is useful when you want ESC to do something more than just close (save state, prompt, etc.).
5. Opting into resize
Windows are fixed-size by default. Passing BJ_WINDOW_FLAG_RESIZABLE to bj_bind_window lets the user drag the window's edges; without it the window cannot be resized. Banjo recreates the framebuffer at the new size for you, so the only thing left to do is react, which is what bj_set_resize_callback is for.
That callback differs from the input callbacks above in two ways: it is registered on a specific window rather than globally, and it receives the new width and height directly instead of an event struct. This is where a real program recomputes a projection, re-lays out its scene, or rescales an off-screen surface; here we just count and log it.
Registering the callback also fires it once immediately with the current size, so the first Resize event line logged at startup is the initial 800x600. That is what lets a program keep all of its size-dependent setup in the resize handler and nowhere else.
What's next
event_polling.c: the alternative polling style, useful when you need full control over when each event fires.
drawing_2d.c: actually put pixels on the screen.
- The Event topic for the full event-type reference.
typedef struct {
size_t cursor;
size_t button;
size_t key;
size_t enter;
size_t resize;
bj_info(
"Cursor event, window %p, (%d,%d)",
(
void*)p_window, e->
x, e->
y
);
}
bj_info(
"Button event, window %p, button %d, %s, (%d,%d)",
);
}
(void)p_window;
const char* action_str = "pressed";
}
bj_info(
"Key 0x%04X (%s) Scancode 0x%04X (with no mods) was %s",
);
}
}
bj_info(
"Enter event, window %p, %s, (%d,%d)",
(void*)p_window,
e->
enter ?
"entered" :
"left",
);
}
bj_info(
"Resize event, window %p, %dx%d", (
void*)p_window, width, height);
}
static void*
setup(
struct bj_app* app,
void* init_data) {
(void)init_data;
return 0;
}
return counter;
}
static void step(
struct bj_app* app,
struct bj_tick_info tick,
void* user_data) {
(void)tick;
(void)user_data;
}
}
static void teardown(
struct bj_app* app,
void* user_data) {
(void)app;
bj_info(
"Total events: %ld cursor, %ld button, %ld key, %ld enter, %ld resize",
);
}
int main(
int argc,
char* argv[]) {
(void)argc; (void)argv;
}
Application lifecycle: callback-driven setup, step, and teardown.
int main(int argc, char *argv[])
static void step(struct bj_app *app, struct bj_tick_info tick, void *user_data)
static void teardown(struct bj_app *app, void *user_data)
bj_audio_play_note_data data
static void * setup(struct bj_app *app, void *init_data)
Recoverable error handling.
Sytem event management API.
void button_callback(bj_window *p_window, const bj_button_event *e, void *data)
void cursor_callback(bj_window *p_window, const bj_cursor_event *e, void *data)
void resize_callback(bj_window *p_window, int width, int height, void *data)
void enter_callback(bj_window *p_window, const bj_enter_event *e, void *data)
void key_callback(bj_window *p_window, const bj_key_event *e, void *data)
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.
struct bj_window bj_window
int scancode
Scancode (layout-independent)
int button
Button identifier (e.g., BJ_BUTTON_LEFT)
bj_bool enter
BJ_TRUE if entering window, BJ_FALSE if leaving.
enum bj_event_action action
Action (press/release/repeat)
enum bj_event_action action
Action (press/release)
enum bj_key key
Key identifier.
const char * bj_key_name(int key)
Get the string name of a key.
void bj_dispatch_events(void)
Poll and dispatch all pending events.
bj_cursor_callback_fn bj_set_cursor_callback(bj_cursor_callback_fn callback, void *user_data)
Set the global callback for cursor events.
bj_enter_callback_fn bj_set_enter_callback(bj_enter_callback_fn callback, void *user_data)
Set the global callback for mouse enter/leave events.
bj_key_callback_fn bj_set_key_callback(bj_key_callback_fn callback, void *user_data)
Set the global callback for keyboard key events.
bj_button_callback_fn bj_set_button_callback(bj_button_callback_fn callback, void *user_data)
Set the global callback for mouse button events.
@ BJ_PRESS
The key or button was pressed.
@ BJ_RELEASE
The key or button was released.
Represent a mouse cursor movement event.
Represent a mouse enter or leave event.
Represent a keyboard key event.
#define bj_info(...)
Log a message using the BJ_LOG_INFO level.
void * bj_calloc(size_t size)
Allocate size bytes of zero-initialised memory.
void bj_free(void *memory)
Free a previously allocated memory block.
bj_bool bj_begin(int systems, struct bj_error **error)
Initialises the system.
void bj_end(void)
De-initialises the system.
void bj_set_window_should_close(struct bj_window *window)
Flag a given window to be closed.
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.
void bj_set_resize_callback(struct bj_window *window, bj_window_resize_fn fn, void *user_data)
Register the resize callback for window.
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.
@ BJ_WINDOW_FLAG_RESIZABLE
User may resize the window.
Logging utility functions.
Portable main() replacement with platform-aware entry shim.
All memory-related functions, including custom allocators.
Header file for system interactions.
Header file for bj_window type.