mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
DOC: Example applications in documentation
This commit is contained in:
parent
5b10127e02
commit
2817ad51da
9 changed files with 561 additions and 99 deletions
|
|
@ -28,5 +28,6 @@ The SDK and toolchain guides for creating your own `.elf` userland binaries.
|
|||
- [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls.
|
||||
- [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`.
|
||||
- [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO.
|
||||
- [`Example Apps`](appdev/examples/README.md): A collection of sample C applications ranging from basic terminal output to advanced TCP networking.
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
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.
|
||||
|
||||
> [!TIP]
|
||||
> **Looking for working code?** Check out the [Examples Directory](examples/README.md) for full source code demonstrating basic CLI, Windows, Animations, and TCP Networking.
|
||||
|
||||
## 📝 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`.
|
||||
|
|
|
|||
52
docs/appdev/examples/01_hello_cli.md
Normal file
52
docs/appdev/examples/01_hello_cli.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<div align="center">
|
||||
<h1>Example 01: Hello CLI</h1>
|
||||
<p><em>The absolute basics. Writing a terminal program.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
This example demonstrates the bare minimum structure of a BoredOS application that outputs text to the standard output (usually the Terminal executing the binary).
|
||||
|
||||
## 📝 Concepts Introduced
|
||||
* Including `stdlib.h` for basic IO.
|
||||
* The `main()` entry point.
|
||||
* Using `printf()` for formatted output.
|
||||
|
||||
---
|
||||
|
||||
## 💻 The Code (`src/userland/cli/hello_world.c`)
|
||||
|
||||
```c
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Standard library initialization is handled automatically by crt0.asm
|
||||
|
||||
// Print a simple string to the terminal
|
||||
printf("Hello, World from BoredOS Userland!\n");
|
||||
|
||||
// Print some formatted data
|
||||
int favorite_number = 67;
|
||||
printf("Did you know my favorite number is %d?\n", favorite_number);
|
||||
|
||||
// Returning from main automatically terminates the process cleanly
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **`#include <stdlib.h>`**: We include the SDK's standard library header which gives us access to `printf`.
|
||||
2. **`int main(...)`**: Every process begins execution here (managed transparently by `crt0.asm`).
|
||||
3. **`printf(...)`**: The SDK routes this call internally directly to the `SYS_WRITE` system call, making it available on the terminal.
|
||||
4. **`return 0`**: A successful exit code.
|
||||
|
||||
## 🚀 Running It
|
||||
|
||||
If you build the project, you can open the Terminal and type:
|
||||
```sh
|
||||
/ # hello_world
|
||||
Hello, World from BoredOS Userland!
|
||||
Did you know my favorite number is 67?
|
||||
/ #
|
||||
```
|
||||
71
docs/appdev/examples/02_basic_window.md
Normal file
71
docs/appdev/examples/02_basic_window.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<div align="center">
|
||||
<h1>Example 02: Basic Window</h1>
|
||||
<p><em>An introduction to libui and creating graphical apps.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
This example demonstrates how to create an empty window that stays active on the screen until the user explicitly closes it by clicking the 'X' button.
|
||||
|
||||
## 📝 Concepts Introduced
|
||||
* Including `libui.h` and the event structure.
|
||||
* Creating a `ui_window_t` handle.
|
||||
* Creating an infinite event loop using `ui_get_event()`.
|
||||
* Yielding CPU time to the kernel via `sys_yield()`.
|
||||
|
||||
---
|
||||
|
||||
## 💻 The Code (`src/userland/gui/basic_window.c`)
|
||||
|
||||
```c
|
||||
#include <stdlib.h>
|
||||
#include <libui.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(void) {
|
||||
// 1. Ask the Window Manager to create a new window
|
||||
// Arguments are: Title, X Position, Y Position, Width, Height
|
||||
ui_window_t wid = ui_window_create("My First GUI", 100, 100, 400, 300);
|
||||
|
||||
if (wid < 0) {
|
||||
printf("Failed to create the window!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 2. Define our event object
|
||||
gui_event_t event;
|
||||
|
||||
// 3. Enter the main event loop
|
||||
while (1) {
|
||||
// ui_get_event is non-blocking. It returns true if an event was waiting.
|
||||
if (ui_get_event(wid, &event)) {
|
||||
|
||||
// Check what type of event occurred
|
||||
if (event.type == GUI_EVENT_CLOSE) {
|
||||
// The user clicked the 'X' button in the titlebar!
|
||||
printf("Window closed cleanly by user.\n");
|
||||
break; // Break the infinite loop
|
||||
}
|
||||
}
|
||||
|
||||
// 4. CRITICAL: Yield the remainder of our timeslice
|
||||
// If we don't do this, the while(1) loop will consume 100% of the CPU
|
||||
// and starve the rest of the OS!
|
||||
sys_yield();
|
||||
}
|
||||
|
||||
// Returning from main will automatically destroy the window and exit the process.
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **Window Handle (`wid`)**: `ui_window_create` sends a request to the kernel. The kernel allocates the memory for the window and returns a numerical ID (the handle) that we use for all future interactions with that specific window.
|
||||
2. **The Event Loop**: Graphical programs run forever until closed. The `while (1)` loop serves this purpose.
|
||||
3. **Polling**: `ui_get_event` asks the kernel, "Hey, did the user click my window or press a key since the last time I asked?". It is non-blocking, so it immediately returns `false` if nothing happened.
|
||||
4. **CPU Yielding**: Since we are constantly polling in a tight loop, we call `sys_yield()` at the end of the loop frame. This politely tells the OS scheduler, "I'm done checking for events, go ahead and let another program run for a bit."
|
||||
|
||||
## 🚀 Running It
|
||||
|
||||
Launch the Terminal and type `basic_window`. You'll see an empty window appear that you can move around the screen!
|
||||
92
docs/appdev/examples/03_bouncing_ball.md
Normal file
92
docs/appdev/examples/03_bouncing_ball.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<div align="center">
|
||||
<h1>Example 03: Bouncing Ball</h1>
|
||||
<p><em>Animating graphics and managing application state.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
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()`.
|
||||
|
||||
---
|
||||
|
||||
## 💻 The Code (`src/userland/gui/bounce.c`)
|
||||
|
||||
```c
|
||||
#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
|
||||
sys_yield();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **State Management**: We store `pos_x`, `pos_y`, `vel_x`, and `vel_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.
|
||||
2. **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!
|
||||
3. **The Double Buffer**: `ui_draw_rect` and `ui_draw_string` do not immediately appear on your monitor. They just color a hidden buffer within the kernel.
|
||||
4. **`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?"
|
||||
|
||||
> [!WARNING]
|
||||
> Because `sys_yield()`'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.
|
||||
92
docs/appdev/examples/04_tcp_client.md
Normal file
92
docs/appdev/examples/04_tcp_client.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<div align="center">
|
||||
<h1>Example 04: TCP HTTP Client</h1>
|
||||
<p><em>Utilizing lwIP to establish an outbound TCP connection.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
This advanced example demonstrates the steps required to use the raw network system calls to establish a connection with an external HTTP server and dump the response over the terminal.
|
||||
|
||||
## 📝 Concepts Introduced
|
||||
* Verifying the network state (`sys_network_is_initialized`, `sys_network_has_ip`).
|
||||
* Performing DNS lookups manually via `sys_dns_lookup`.
|
||||
* Managing strict TCP flow logic (`sys_tcp_connect`, send, block for receive).
|
||||
* Using the terminal `SYS_WRITE` output for debugging.
|
||||
|
||||
---
|
||||
|
||||
## 💻 The Code (`src/userland/cli/http_get.c`)
|
||||
|
||||
```c
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(void) {
|
||||
if (!sys_network_is_initialized() || !sys_network_has_ip()) {
|
||||
printf("Network is unreachable! Make sure you inited the network first!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 1. Resolve host name to IP
|
||||
const char *target_host = "boreddev.nl";
|
||||
net_ipv4_address_t server_ip;
|
||||
|
||||
printf("Resolving %s...\n", target_host);
|
||||
if (sys_dns_lookup(target_host, &server_ip) < 0) {
|
||||
printf("DNS Lookup failed.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Resolved to: %d.%d.%d.%d\n", server_ip.bytes[0], server_ip.bytes[1],
|
||||
server_ip.bytes[2], server_ip.bytes[3]);
|
||||
|
||||
// 2. Establish a TCP connection on port 80 (HTTP)
|
||||
printf("Connecting...\n");
|
||||
if (sys_tcp_connect(&server_ip, 80) < 0) {
|
||||
printf("Connection failed.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Connected! Sending GET request...\n");
|
||||
|
||||
// 3. Format and send the raw HTTP Request
|
||||
char request[256];
|
||||
strcpy(request, "GET / HTTP/1.1\r\nHost: ");
|
||||
strcat(request, target_host);
|
||||
strcat(request, "\r\nConnection: close\r\n\r\n");
|
||||
|
||||
if (sys_tcp_send(request, strlen(request)) < 0) {
|
||||
printf("Failed to send data.\n");
|
||||
sys_tcp_close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 4. Block and wait for response data
|
||||
char recv_buf[512];
|
||||
int bytes_received;
|
||||
|
||||
printf("\n--- RESPONSE ---\n");
|
||||
while ((bytes_received = sys_tcp_recv(recv_buf, sizeof(recv_buf) - 1)) > 0) {
|
||||
recv_buf[bytes_received] = '\0'; // Null-terminate the chunk
|
||||
printf("%s", recv_buf); // Print the chunk to stdout
|
||||
}
|
||||
|
||||
// 5. Cleanup
|
||||
printf("\n--- END RESPONSE ---\n");
|
||||
sys_tcp_close();
|
||||
printf("Connection closed.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **Network Setup**: First, we must ensure the host machine or QEMU environment gave BoredOS a valid IP address via DHCP. The `sys_network_has_ip()` check prevents our app from hanging trying to route data to nowhere.
|
||||
2. **DNS (`sys_dns_lookup`)**: Since we want to connect to a domain name, not a raw IP, we query the DNS server configured by the OS (which it received via DHCP).
|
||||
3. **Connection (`sys_tcp_connect`)**: We block the application thread while the OS performs the 3-way TCP handshake over port 80.
|
||||
4. **Payload (`sys_tcp_send`)**: We format a compliant HTTP/1.1 payload representing a simple GET request for the root directory `/`.
|
||||
5. **Chunked Receiving (`sys_tcp_recv`)**: The server's response might be larger than our `recv_buf` (512 bytes). Therefore, we loop. `sys_tcp_recv` blocks execution until data arrives. If it returns `0`, the remote server cleanly closed the connection (which happens automatically because we specified `Connection: close` in our request payload!).
|
||||
|
||||
## 🚀 Running It
|
||||
|
||||
Make sure QEMU is running with networking enabled. Launch the terminal and type `http_get`. You will see the raw headers and HTML source of the target webpage scroll down the CLI interface!
|
||||
28
docs/appdev/examples/README.md
Normal file
28
docs/appdev/examples/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<div align="center">
|
||||
<h1>Example Applications</h1>
|
||||
<p><em>From basic output to complex Graphical and Network applications.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Welcome to the examples directory! These guides are designed to help you understand how to write C applications for the BoredOS userland, utilizing the custom `libc` SDK.
|
||||
|
||||
The examples are listed in order of increasing complexity. Click on a tutorial to view the complete source code and an explanation of the concepts it introduces.
|
||||
|
||||
## 🟢 Beginner
|
||||
|
||||
* **[`01_hello_cli.md`](01_hello_cli.md)**: The absolute basics. Learn how to write a simple Terminal program that outputs text and processes standard system calls.
|
||||
* **[`02_basic_window.md`](02_basic_window.md)**: An introduction to `libui.h`. Learn how to create an empty window, set up a basic event loop, and handle the "Close" button cleanly.
|
||||
|
||||
## 🟡 Intermediate
|
||||
|
||||
* **[`03_bouncing_ball.md`](03_bouncing_ball.md)**: Dive deeper into graphical rendering. This example introduces the `ui_mark_dirty` command, framerate independence via `sys_yield()`, and state management to animate a shape moving around the screen and bouncing off the window edges.
|
||||
|
||||
## 🔴 Advanced
|
||||
|
||||
* **[`04_tcp_client.md`](04_tcp_client.md)**: Using the lwIP networking stack. This example demonstrates how to perform a DNS lookup, connect to an external server over TCP (like an HTTP server), send a raw request, and print the response to the terminal.
|
||||
|
||||
---
|
||||
|
||||
> [!TIP]
|
||||
> If you want to test these out, simply create a new `.c` file in `src/userland/cli/` (for terminal apps) or `src/userland/gui/` (for windowed apps), paste the example code, then run `make clean && make run` from the project root!
|
||||
|
|
@ -1,45 +1,157 @@
|
|||
<div align="center">
|
||||
<h1>Userland SDK Reference</h1>
|
||||
<p><em>Custom libc implementations and system calls in BoredOS.</em></p>
|
||||
<p><em>Comprehensive manual for custom libc and system calls in BoredOS.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
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/`)
|
||||
All headers are located in `src/userland/libc/`.
|
||||
|
||||
The SDK comprises a few key files containing wrappers around kernel system calls:
|
||||
## 📚 Standard Library (`stdlib.h` & `string.h`)
|
||||
|
||||
- `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).
|
||||
The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls.
|
||||
|
||||
## 🖧 System Calls Overview
|
||||
### Memory Allocation
|
||||
* `void* malloc(size_t size);` - Allocate a block of memory on the heap.
|
||||
* `void free(void* ptr);` - Free a previously allocated memory block.
|
||||
* `void* calloc(size_t nmemb, size_t size);` - Allocate and zero-out a block of memory for an array.
|
||||
* `void* realloc(void* ptr, size_t size);` - Resize an existing memory block.
|
||||
|
||||
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**.
|
||||
### Memory Manipulation (`string.h`)
|
||||
* `void* memset(void *s, int c, size_t n);` - Fill a block of memory with a specific byte.
|
||||
* `void* memcpy(void *dest, const void *src, size_t n);` - Copy memory from source to destination.
|
||||
* `void* memmove(void *dest, const void *src, size_t n);` - Safely copy overlapping memory blocks.
|
||||
* `int memcmp(const void *s1, const void *s2, size_t n);` - Compare two memory blocks.
|
||||
|
||||
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.
|
||||
### String Utilities
|
||||
* `size_t strlen(const char *s);` - Get the length of a string.
|
||||
* `int strcmp(const char *s1, const char *s2);` - Compare two strings lexicographically.
|
||||
* `char* strcpy(char *dest, const char *src);` - Copy a string to a destination buffer.
|
||||
* `char* strcat(char *dest, const char *src);` - Concatenate two strings.
|
||||
|
||||
The custom `libc` provides `syscallX` wrapper functions that abstract the assembly details:
|
||||
### Conversion and Formatting
|
||||
* `int atoi(const char *nptr);` - String to integer conversion.
|
||||
* `void itoa(int n, char *buf);` - Integer to string conversion.
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
### Input / Output
|
||||
* `void puts(const char *s);` - Print a string followed by a newline to the standard output.
|
||||
* `void printf(const char *fmt, ...);` - Formatted print to standard output (supports `%d`, `%s`, `%x`, etc.).
|
||||
|
||||
### 📌 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.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you are developing a new application, **do not invoke syscalls manually**. Instead, include `stdlib.h` and use the C functions provided.
|
||||
### Process Control
|
||||
* `void exit(int status);` - Terminate the current process.
|
||||
* `void sleep(int ms);` - Pause execution for a specified number of milliseconds.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ System Calls (`syscall.h`)
|
||||
|
||||
For advanced operations, `syscall.h` provides direct wrappers into the kernel.
|
||||
|
||||
### Process & System Info
|
||||
* `void sys_exit(int status);` - Raw exit syscall.
|
||||
* `int sys_write(int fd, const char *buf, int len);` - Write to a file descriptor (usually fd 1 for stdout).
|
||||
* `void* sys_sbrk(int incr);` - Expand or shrink the process data segment (used internally by `malloc`).
|
||||
* `void sys_kill(int pid);` - Send a kill signal to a process.
|
||||
* `void sys_yield(void);` - Yield the remainder of the process's timeslice to the scheduler.
|
||||
* `int sys_system(int cmd, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);` - Execute a privileged system command (e.g., reboot, shutdown, beep).
|
||||
* `int sys_get_os_info(os_info_t *info);` - Populate an `os_info_t` struct with version and environment details.
|
||||
* `uint64_t sys_get_shell_config(const char *key);` - Retrieve internal shell configuration values.
|
||||
* `void sys_set_text_color(uint32_t color);` - Set the raw CLI text output color.
|
||||
|
||||
### File System API (VFS)
|
||||
Interacting with files and directories using the Virtual File System.
|
||||
* `int sys_open(const char *path, const char *mode);` - Open a file, returning a file descriptor.
|
||||
* `int sys_read(int fd, void *buf, uint32_t len);` - Read from an open file.
|
||||
* `int sys_write_fs(int fd, const void *buf, uint32_t len);` - Write to an open file.
|
||||
* `void sys_close(int fd);` - Close an open file descriptor.
|
||||
* `int sys_seek(int fd, int offset, int whence);` - Reposition the file offset.
|
||||
* `uint32_t sys_tell(int fd);` - Get the current file offset.
|
||||
* `uint32_t sys_size(int fd);` - Get the total file size.
|
||||
* `int sys_delete(const char *path);` - Delete a file.
|
||||
* `int sys_mkdir(const char *path);` - Create a new directory.
|
||||
* `int sys_exists(const char *path);` - Check if a path exists on the filesystem.
|
||||
* `int sys_getcwd(char *buf, int size);` - Get the current working directory string.
|
||||
* `int sys_chdir(const char *path);` - Change the current working directory.
|
||||
* `int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries);` - List directory contents into an array of `FAT32_FileInfo` structs.
|
||||
* `int sys_get_file_info(const char *path, FAT32_FileInfo *info);` - Retrieve metadata for a specific file.
|
||||
|
||||
### Networking Stack API
|
||||
BoredOS includes lwIP for hardware TCP/UDP networking.
|
||||
|
||||
#### Configuration and Status
|
||||
* `int sys_network_init(void);` - Initialize the network stack.
|
||||
* `int sys_network_is_initialized(void);` - Check stack status.
|
||||
* `int sys_network_dhcp_acquire(void);` - Request an IP configuration from a DHCP server.
|
||||
* `int sys_network_has_ip(void);` - Check if the system has a valid IP address.
|
||||
* `int sys_network_get_mac(net_mac_address_t *mac);` - Get the physical MAC address of the NIC.
|
||||
* `int sys_network_get_nic_name(char *name_out);` - Get the name of the active network interface card.
|
||||
* `int sys_network_get_ip(net_ipv4_address_t *ip);` - Get current local IPv4 address.
|
||||
* `int sys_network_set_ip(const net_ipv4_address_t *ip);` - Manually assign a static IP.
|
||||
* `int sys_network_get_gateway(net_ipv4_address_t *ip);` - Get the default gateway IP.
|
||||
* `int sys_network_get_dns(net_ipv4_address_t *ip);` - Get the primary DNS server IP.
|
||||
* `int sys_set_dns_server(const net_ipv4_address_t *ip);` - Set the primary DNS server.
|
||||
* `int sys_get_dns_server(net_ipv4_address_t *ip);` - Retrieve configured DNS server.
|
||||
* `int sys_network_get_stat(int stat_type);` - Get network statistics (packets in/out, drops, etc.).
|
||||
* `void sys_network_force_unlock(void);` - Force release of network stack locks (use with caution).
|
||||
|
||||
#### Communication
|
||||
* `int sys_icmp_ping(const net_ipv4_address_t *dest_ip);` - Send an ICMP echo request.
|
||||
* `int sys_udp_send(const net_ipv4_address_t *dest_ip, uint16_t dest_port, uint16_t src_port, const void *data, size_t data_len);` - Send a raw UDP datagram.
|
||||
* `int sys_dns_lookup(const char *name, net_ipv4_address_t *out_ip);` - Resolve a hostname to an IPv4 address.
|
||||
* `int sys_tcp_connect(const net_ipv4_address_t *ip, uint16_t port);` - Establish a TCP connection to a remote host.
|
||||
* `int sys_tcp_send(const void *data, size_t len);` - Send data over an active TCP socket.
|
||||
* `int sys_tcp_recv(void *buf, size_t max_len);` - Receive data from a TCP socket (blocking).
|
||||
* `int sys_tcp_recv_nb(void *buf, size_t max_len);` - Receive data from a TCP socket (non-blocking).
|
||||
* `int sys_tcp_close(void);` - Close the active TCP socket.
|
||||
|
||||
---
|
||||
|
||||
## 📑 Core Data Structures
|
||||
|
||||
### `os_info_t`
|
||||
Contains detailed build and version information about the OS.
|
||||
```c
|
||||
typedef struct {
|
||||
char os_name[64];
|
||||
char os_version[64];
|
||||
char os_codename[64];
|
||||
char kernel_name[64];
|
||||
char kernel_version[64];
|
||||
char build_date[64];
|
||||
char build_time[64];
|
||||
char build_arch[64];
|
||||
} os_info_t;
|
||||
```
|
||||
|
||||
### `FAT32_FileInfo`
|
||||
Represents a filesystem entry.
|
||||
```c
|
||||
typedef struct {
|
||||
char name[256];
|
||||
uint32_t size;
|
||||
uint8_t is_directory;
|
||||
uint32_t start_cluster;
|
||||
uint16_t write_date;
|
||||
uint16_t write_time;
|
||||
} FAT32_FileInfo;
|
||||
```
|
||||
|
||||
### `ProcessInfo`
|
||||
Provides status information for an active process.
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t pid;
|
||||
char name[64];
|
||||
uint64_t ticks;
|
||||
size_t used_memory;
|
||||
} ProcessInfo;
|
||||
```
|
||||
|
||||
### IP / MAC Addresses
|
||||
Wrappers for raw byte arrays.
|
||||
```c
|
||||
typedef struct { uint8_t bytes[6]; } net_mac_address_t;
|
||||
typedef struct { uint8_t bytes[4]; } net_ipv4_address_t;
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,92 +1,103 @@
|
|||
<div align="center">
|
||||
<h1>UI API (<code>libui.h</code>)</h1>
|
||||
<p><em>Interact with the BoredOS Window Manager (WM).</em></p>
|
||||
<p><em>Comprehensive manual for interacting with the Window Manager.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
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`.
|
||||
The UI library (`libui.h`) is the sole mechanism for Graphical Userland Applications to draw to the screen and receive input events in BoredOS. It wraps `SYS_GUI` kernel calls.
|
||||
|
||||
## 🧠 Core Concepts
|
||||
## 🪟 Window Management
|
||||
|
||||
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.
|
||||
A "Window" is a reserved drawing canvas managed by the compositor.
|
||||
|
||||
## 🪟 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;
|
||||
}
|
||||
```
|
||||
* `ui_window_t ui_window_create(const char *title, int x, int y, int w, int h);`
|
||||
Creates a new window at `(x, y)` with dimensions `w`x`h`. Returns a window handle.
|
||||
**Flags** are currently embedded in the syscall; standard windows include decorations (titlebar, borders).
|
||||
* `void ui_window_set_title(ui_window_t win, const char *title);`
|
||||
Dynamically update the text displayed in the window's titlebar.
|
||||
* `void ui_window_set_resizable(ui_window_t win, bool resizable);`
|
||||
Enable or disable the user's ability to resize the window by dragging its edges.
|
||||
* `void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h);`
|
||||
Query the global screen resolution of the display.
|
||||
|
||||
## 🎨 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.
|
||||
All drawing functions write to an off-screen buffer associated with the window. **You must call `ui_mark_dirty()` to instruct the compositor to push your changes to the physical screen.**
|
||||
|
||||
* `void ui_draw_rect(ui_window_t win, int x, int y, int w, int h, uint32_t color);`
|
||||
Draw a solid filled rectangle.
|
||||
* `void ui_draw_rounded_rect_filled(ui_window_t win, int x, int y, int w, int h, int radius, uint32_t color);`
|
||||
Fill a rectangle with rounded corners of a specified `radius`.
|
||||
* `void ui_draw_image(ui_window_t win, int x, int y, int w, int h, uint32_t *image_data);`
|
||||
Blit a raw ARGB pixel buffer (`image_data`) directly into the window canvas.
|
||||
* `void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h);`
|
||||
Mark a specific rectangular region of the window as "dirty". The Window Manager will redraw this area on the next compositing pass.
|
||||
|
||||
> [!TIP]
|
||||
> Colors are defined as 32-bit unsigned integers in **ARGB** format: `0xAARRGGBB`.
|
||||
> E.g., `0xFF000000` is opaque black, `0xFFFF0000` is opaque red.
|
||||
|
||||
## 🔤 Text Rendering
|
||||
|
||||
BoredOS provides multiple text rendering methodologies, including a default system font and scaled/bitmap alternatives.
|
||||
|
||||
* `void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t color);`
|
||||
Draw text using the default system typeface.
|
||||
* `void ui_draw_string_bitmap(ui_window_t win, int x, int y, const char *str, uint32_t color);`
|
||||
Draw text using a secondary fast bitmap font renderer.
|
||||
* `void ui_draw_string_scaled(ui_window_t win, int x, int y, const char *str, uint32_t color, float scale);`
|
||||
Draw text scaled up or down by a floating-point multiplier.
|
||||
* `void ui_draw_string_scaled_sloped(ui_window_t win, int x, int y, const char *str, uint32_t color, float scale, float slope);`
|
||||
Draw scaled text with an italic-like slope/shear applied.
|
||||
* `void ui_set_font(ui_window_t win, const char *path);`
|
||||
Load and set a custom `.ttf` or bitmap font from the filesystem for this window.
|
||||
|
||||
### Font Metrics
|
||||
Used for calculating layout bounds before drawing:
|
||||
* `uint32_t ui_get_string_width(const char *str);`
|
||||
* `uint32_t ui_get_font_height(void);`
|
||||
* `uint32_t ui_get_string_width_scaled(const char *str, float scale);`
|
||||
* `uint32_t ui_get_font_height_scaled(float scale);`
|
||||
|
||||
## 🔄 Event Handling
|
||||
|
||||
Applications must continuously poll for events inside an infinite `$while(1)` loop.
|
||||
|
||||
* `bool ui_get_event(ui_window_t win, gui_event_t *ev);`
|
||||
Returns `true` if an event was waiting in the queue, populating the `ev` structure. Returns `false` if the queue is empty.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Because `ui_get_event` is non-blocking, you must call `sys_yield();` inside your event loop if no event was received, otherwise your app will consume 100% of the CPU timeslice.
|
||||
|
||||
### Graphical Event Structure
|
||||
|
||||
```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);
|
||||
typedef struct {
|
||||
int type; // Specifies the event class (see below)
|
||||
int arg1; // Generic argument 1
|
||||
int arg2; // Generic argument 2
|
||||
int arg3; // Generic argument 3
|
||||
} gui_event_t;
|
||||
```
|
||||
|
||||
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.
|
||||
### Event Types & Arguments
|
||||
|
||||
## 🔄 Handling the Event Loop
|
||||
| Event Constant | `type` ID | Trigger | `arg1` | `arg2` | `arg3` |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| `GUI_EVENT_NONE` | `0` | Empty event | - | - | - |
|
||||
| `GUI_EVENT_PAINT` | `1` | Window needs redrawing | - | - | - |
|
||||
| `GUI_EVENT_CLICK` | `2` | Mouse click down | X Coord | Y Coord | Button State |
|
||||
| `GUI_EVENT_RIGHT_CLICK` | `3` | Mouse right-click down | X Coord | Y Coord | Button State |
|
||||
| `GUI_EVENT_CLOSE` | `4` | User clicked 'X' button | - | - | - |
|
||||
| `GUI_EVENT_KEY` | `5` | Keyboard key pressed | Keycode | Modifiers | - |
|
||||
| `GUI_EVENT_KEYUP` | `10` | Keyboard key released | Keycode | Modifiers | - |
|
||||
| `GUI_EVENT_MOUSE_DOWN` | `6` | Generic mouse button down | X Coord | Y Coord | Button State |
|
||||
| `GUI_EVENT_MOUSE_UP` | `7` | Generic mouse button release | X Coord | Y Coord | Button State |
|
||||
| `GUI_EVENT_MOUSE_MOVE` | `8` | Mouse cursor moved | X Coord | Y Coord | - |
|
||||
| `GUI_EVENT_MOUSE_WHEEL` | `9` | Scroll wheel rotated | Scroll Delta | - | - |
|
||||
| `GUI_EVENT_RESIZE` | `11` | Window dimensions changed| New Width | New Height | - |
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
*(Note: Coordinate arguments (`arg1`, `arg2`) for mouse events are typically relative to the top-left corner of the window's client area).*
|
||||
|
||||
---
|
||||
|
|
|
|||
Loading…
Reference in a new issue