This commit is contained in:
boreddevnl
2026-03-16 10:23:40 +01:00
parent d01c309166
commit b427b1d4ac
13 changed files with 464 additions and 87 deletions

View File

@@ -0,0 +1,78 @@
# Creating a Custom App (Step-by-Step)
This guide explains how to write a new "Hello World" application locally, compile it as an `.elf` binary into the `bin/` folder, and launch it inside BoredOS.
## Step 1: Write the C Source
Applications reside entirely in the `src/userland/` directory. Create a new file, for example, `src/userland/gui/hello.c`.
> [!TIP]
> Group CLI apps into `src/userland/cli/` and windowed apps into `src/userland/gui/` for organization.
```c
// src/userland/gui/hello.c
#include <stdlib.h>
#include <libui.h>
int main(void) {
// Attempt to open a 300x200 window
int wid = ui_create_window("My Custom App", 300, 200, 0);
if (wid < 0) {
printf("Error creating window!\n");
return 1;
}
// Write text in center
ui_draw_string(wid, "Hello, BoredOS!!", 50, 90, 0xFFFFFFFF);
// Commit drawing to screen
ui_swap_buffers(wid);
ui_event_t event;
while (1) {
if (ui_poll_event(&event)) {
if (event.window_id == wid && event.type == UI_EVENT_WINDOW_CLOSE) {
break; // Exit loop if 'X' is clicked
}
}
syscall1(SYSTEM_CMD_YIELD, 0);
}
return 0; // Returning 0 smoothly exits the process via crt0.asm
}
```
## Step 2: Edit the Makefile
Now you need to tell the build system to compile `hello.c`. Fortunately, the `src/userland/Makefile` is designed to detect new C files largely automatically!
1. Open `src/userland/Makefile`.
2. Find the line specifying `APP_SOURCES_FULL`:
```make
APP_SOURCES_FULL = $(wildcard cli/*.c gui/*.c sys/*.c games/*.c *.c)
```
Since you placed the file in `gui/hello.c`, the wildcard logic will pick it up automatically.
3. The Makefile will generate `bin/hello.elf` during the build phase.
## Step 3: Bundle it into the OS
The main overarching `Makefile` (in the project root) takes binaries from `src/userland/bin/*.elf` and places them into the `iso_root/bin/` directory, while also adding them to `limine.conf` as loadable boot modules.
1. Go back to the root of the OS:
```sh
cd ../..
```
2. Compile the entire project to build the ISO and test in QEMU:
```sh
make clean && make run
```
## Step 4: Run it inside BoredOS
1. When BoredOS boots, launch the **Terminal** application.
2. The OS automatically maps built applications to standard shell commands. Simply type your application's filename (without the `.elf` extension).
3. Type `hello` in the terminal and press Enter.
4. Your custom window will appear!
*you can also open your app by opening the file explorer and navigating to the bin directory and double clicking the executable.*

View File

@@ -0,0 +1,36 @@
# Userland SDK Reference
BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features.
## The Custom libc Structure (`src/userland/libc/`)
The SDK comprises a few key files containing wrappers around kernel system calls:
- `stdlib.h` / `stdlib.c`: Memory allocation (`malloc`, `free`), integer conversion (`itoa`, `atoi`), printing (`printf`, `sprintf`), and random numbers (`rand`, `srand`).
- `string.h` / `string.c`: String manipulation utilities (`strlen`, `strcpy`, `strcmp`, `memset`, `memcpy`).
- `syscall.h` / `syscall.c`: The raw interface to issue `syscall` assembly instructions, routing requests to the kernel.
- `libui.h` / `libui.c`: Graphical interface commands (creating windows, drawing pixels, events).
## System Calls Overview
When a userland application wants to interact with the hardware (print to screen, read a file, create a window), it must ask the kernel via a **System Call**.
In BoredOS (`x86_64`), system calls are issued using the `syscall` instruction. The kernel intercepts this instruction and inspects the processor's RAX register to figure out *what* the application wants to do.
The custom `libc` provides `syscallX` wrapper functions that abstract the assembly details:
```c
// Example: Performing a minimal system call from userland
int sys_write(int fd, const char *buf, int len) {
return syscall3(SYS_WRITE, fd, (uint64_t)buf, len);
}
```
### Notable System Calls
- **`SYS_WRITE` (1)**: Currently acts as a generic output mechanism for `printf`, typically routing text to the kernel's serial output for debugging, or to an active text-mode console.
- **`SYS_GUI` (3)**: The primary multiplexer for all window manager operations. The arguments define subcommands (like `UI_CREATE_WINDOW`, `UI_FILL_RECT`).
- **`SYS_FS` (4)**: Interacts with the virtual filesystem (e.g., `FS_CMD_OPEN`, `FS_CMD_READ`). Under the hood, this reads from the loaded RAMFS or an attached physical ATA disk via the native FAT32 driver.
- **`SYS_EXIT` (60)**: Terminates the current process and returns control to the kernel.
- **`SYSTEM_CMD_YIELD` (43)**: Instructs the process scheduler to pause the current process and let another process run.
If you are developing a new application, **do not invoke syscalls manually**. Instead, include `stdlib.h` and use the C functions provided.

85
docs/appdev/ui_api.md Normal file
View File

@@ -0,0 +1,85 @@
# UI API (`libui.h`)
For an application to be visible on the screen, it must interact with the BoredOS Window Manager (WM). The tools required for this are located in `src/userland/libc/libui.h` and `libui.c`.
## Core Concepts
The UI library sends requests (via `SYS_GUI`) to the kernel to reserve an area on the screen (a `Window`) and then issues commands to color specific pixels within that area. The kernel is responsible for compositing this area over other windows.
## Example: Creating a Window
First, include the library and define an event structure:
```c
#include <libui.h>
#include <stdlib.h>
int main(void) {
// 1. Create the window
// Arguments: Title, Width, Height, Flags (e.g. 0 for bordered window)
int window_id = ui_create_window("Hello World App", 400, 300, 0);
if (window_id < 0) {
printf("Failed to create window!\n");
return 1;
}
// ... Event loop will go here ...
return 0;
}
```
## Drawing Primitives
The library offers functions to mutate the window's internal buffer. After issuing drawing commands, you **must** instruct the kernel to push the changes onto the screen.
```c
// Fill the entire window with a solid blue background
// Arguments: Window ID, X, Y, Width, Height, ARGB Color value
ui_fill_rect(window_id, 0, 0, 400, 300, 0xFF0000FF);
// Tell the kernel to commit the drawing commands to the screen
ui_swap_buffers(window_id);
```
Available rendering methods:
- `ui_fill_rect(id, x, y, w, h, color)`: Draw a solid rectangle.
- `ui_draw_rect(id, x, y, w, h, color)`: Draw an outline of a rectangle.
- `ui_draw_line(id, x0, y0, x1, y1, color)`: Bresenham line algorithm.
- `ui_draw_string(id, string, x, y, color)`: Render text using the kernel's built-in font.
- `ui_update_region(id, x, y, w, h)`: A targeted version of `ui_swap_buffers` that only updates a specific area, saving performance.
## Handling the Event Loop
Graphical applications are event-driven. They stay alive inside a `while (1)` loop, periodically asking the kernel if the user clicked the mouse or pressed a key inside their window.
```c
ui_event_t event;
// Main UI Loop
while (1) {
// ui_poll_event is non-blocking. It returns 1 if an event occurred, 0 otherwise.
if (ui_poll_event(&event)) {
// The WM dispatch sets event.window_id
// We only care about events meant for our specific window
if (event.window_id == window_id) {
if (event.type == UI_EVENT_MOUSE_DOWN) {
printf("User clicked at X:%d Y:%d\n", event.mouse_x, event.mouse_y);
// Respond visually to the click
ui_fill_rect(window_id, event.mouse_x, event.mouse_y, 10, 10, 0xFFFF0000); // Red dot
ui_swap_buffers(window_id);
}
else if (event.type == UI_EVENT_WINDOW_CLOSE) {
// Start tearing down the application safely
break;
}
}
}
// Prevent 100% CPU usage by yielding execution time back to the OS scheduler
syscall1(SYSTEM_CMD_YIELD, 0);
}
```