03_bouncing_ball
Example 03: Bouncing Ball
Animating graphics and managing application state.
This example builds upon the 02_basic_window guide. It demonstrates how to constantly update the screen to simulate a bouncing square moving freely inside the window bounds.
Concepts Introduced
- Maintaining application state across frames (Velocity/Position).
- Drawing primitives (
ui_fill_rect,ui_draw_string). - The importance of clearing the screen on a new frame.
- Explicitly forcing standard visual updates via
ui_mark_dirty(). - Declaring app metadata via source annotations.
The Code (src/userland/gui/bounce.c)
// BOREDOS_APP_DESC: Bouncing ball animation demo.
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/applications-games.png
#include <stdlib.h>
#include <libui.h>
#include <syscall.h>
// Window Dimensions
#define W_WIDTH 400
#define W_HEIGHT 300
// Square Dimensions
#define SQ_SIZE 30
int main(void) {
ui_window_t wid = ui_window_create("Bouncing Box Animation", 50, 50, W_WIDTH, W_HEIGHT);
if (wid < 0) return 1;
// Define object state variables
int pos_x = 50;
int pos_y = 50;
int vel_x = 2; // Move 2 pixels per frame horizontally
int vel_y = 2; // Move 2 pixels per frame vertically
gui_event_t event;
while (1) {
// 1. Process Events
while (ui_get_event(wid, &event)) {
if (event.type == GUI_EVENT_CLOSE) {
return 0; // Exit cleanly
}
}
// 2. Physics & Logic Update
pos_x += vel_x;
pos_y += vel_y;
// Collision logic (Bounce off edges)
// The window has a 20px title bar, so the usable client height is W_HEIGHT - 20.
if (pos_x <= 0 || pos_x + SQ_SIZE >= W_WIDTH) {
vel_x = -vel_x; // Reverse horizontal direction
}
if (pos_y <= 0 || pos_y + SQ_SIZE >= W_HEIGHT - 20) {
vel_y = -vel_y; // Reverse vertical direction
}
// 3. Rendering Update
// Step A: Clear the entire background to Black (0xFF000000)
ui_draw_rect(wid, 0, 0, W_WIDTH, W_HEIGHT, 0xFF000000);
// Step B: Draw our shape in Red (0xFFFF0000) at the new position
ui_draw_rect(wid, pos_x, pos_y, SQ_SIZE, SQ_SIZE, 0xFFFF0000);
// Step C: Draw some UI text over the animation in White
ui_draw_string(wid, 10, 10, "BoredOS Animation Demo!", 0xFFFFFFFF);
// Step D: Instruct the compositor to flush our drawing buffer to the physical screen
ui_mark_dirty(wid, 0, 0, W_WIDTH, W_HEIGHT);
// 4. Yield and throttle (targeting ~60 FPS)
sys_system(SYSTEM_CMD_SLEEP, 16, 0, 0, 0);
}
return 0;
}
How it Works
- State Management: We store
pos_x,pos_y,vel_x, andvel_y. These variables represent the "physics" of our system. Notice that they update outside the event-checking logic so that the animation runs even if the user isn't clicking the mouse. - Screen Clearing: We must fill the screen with black (
ui_draw_rect(wid, 0, 0, W_WIDTH, W_HEIGHT, ...)). If we don't clear the screen, the red square will leave a permanent trailing smear everywhere it goes! - The Double Buffer:
ui_draw_rectandui_draw_stringdo not immediately appear on your monitor. They just color a hidden buffer within the kernel. ui_mark_dirty: This is the crucial command that tells the kernel Window Manager, "I'm done drawing my frame. Can you quickly copy my hidden buffer over to the real screen now?"BOREDOS_APP_DESC/BOREDOS_APP_ICONS: Embedded into the compiled.elfas a BoredOS NOTE section. The Desktop and File Explorer read this to show the game's icon instead of the generic binary icon. Seeelf_metadata.mdfor full details.
WARNING
Because sys_system(SYSTEM_CMD_SLEEP, ...)'s pause duration depends heavily on CPU load and how many other processes are running (or QEMU emulation speed), tying physics/movement strictly to loops can make the game run faster on faster computers. Advanced developers will want to calculate delta time (time elapsed since the last frame) for smooth motion.