feat: multi-user system, bug fixes, security & performance fixes, and more
This commit is contained in:
@@ -0,0 +1,916 @@
|
||||
# Writing GUI Applications for MontaukOS
|
||||
|
||||
This guide covers how to build graphical applications for MontaukOS, from standalone Window Server clients to desktop-integrated apps.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [App Types](#app-types)
|
||||
- [Standalone Window Server Apps](#standalone-window-server-apps)
|
||||
- [Desktop-Integrated Apps](#desktop-integrated-apps)
|
||||
- [Drawing and Rendering](#drawing-and-rendering)
|
||||
- [Text and Fonts](#text-and-fonts)
|
||||
- [Input Handling](#input-handling)
|
||||
- [Widgets](#widgets)
|
||||
- [Colors and Theming](#colors-and-theming)
|
||||
- [Memory Management](#memory-management)
|
||||
- [Networking and HTTPS](#networking-and-https)
|
||||
- [App Manifests](#app-manifests)
|
||||
- [Build System](#build-system)
|
||||
|
||||
---
|
||||
|
||||
## App Types
|
||||
|
||||
MontaukOS supports two kinds of GUI applications:
|
||||
|
||||
| | Standalone Apps | Desktop Apps |
|
||||
|---|---|---|
|
||||
| **Location** | `programs/src/<appname>/` | `programs/src/desktop/apps/` |
|
||||
| **Window** | Own process, shared-memory pixel buffer | Embedded in desktop compositor |
|
||||
| **Event loop** | `win_poll()` syscall | Callback-driven (`on_draw`, `on_mouse`, `on_key`) |
|
||||
| **Drawing** | Direct pixel buffer writes | `Canvas` abstraction |
|
||||
| **Examples** | Spreadsheet, Music, Wikipedia | Calculator, File Manager, Terminal |
|
||||
|
||||
**Choose standalone** when you need a separate process (e.g., networking, heavy computation, isolation). **Choose desktop-integrated** for lightweight tools that benefit from tight compositor integration.
|
||||
|
||||
---
|
||||
|
||||
## Standalone Window Server Apps
|
||||
|
||||
Standalone apps are separate ELF binaries that communicate with the Window Server through syscalls. They get a shared-memory pixel buffer and manage their own event loop.
|
||||
|
||||
### Minimal Example
|
||||
|
||||
```cpp
|
||||
#include <montauk/syscall.h>
|
||||
#include <montauk/heap.h>
|
||||
#include <montauk/string.h>
|
||||
#include <gui/gui.hpp>
|
||||
#include <gui/truetype.hpp>
|
||||
|
||||
static TrueTypeFont* g_font;
|
||||
static int g_win_w, g_win_h;
|
||||
|
||||
static void render(uint32_t* pixels) {
|
||||
// Clear background
|
||||
for (int i = 0; i < g_win_w * g_win_h; i++)
|
||||
pixels[i] = Color::from_rgb(0xFF, 0xFF, 0xFF).to_pixel();
|
||||
|
||||
// Draw text
|
||||
if (g_font)
|
||||
g_font->draw_to_buffer(pixels, g_win_w, g_win_h,
|
||||
20, 30, "Hello, MontaukOS!",
|
||||
Color::from_rgb(0x33, 0x33, 0x33), 18);
|
||||
}
|
||||
|
||||
extern "C" void _start() {
|
||||
// Load font
|
||||
g_font = new TrueTypeFont();
|
||||
g_font->init("0:/fonts/Roboto-Medium.ttf");
|
||||
|
||||
// Create window
|
||||
g_win_w = 400;
|
||||
g_win_h = 300;
|
||||
Montauk::WinCreateResult wres;
|
||||
montauk::win_create("My App", g_win_w, g_win_h, &wres);
|
||||
int win_id = wres.id;
|
||||
uint32_t* pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
|
||||
|
||||
// Initial render
|
||||
render(pixels);
|
||||
montauk::win_present(win_id);
|
||||
|
||||
// Event loop
|
||||
while (true) {
|
||||
Montauk::WinEvent ev;
|
||||
int r = montauk::win_poll(win_id, &ev);
|
||||
|
||||
if (r < 0) break; // Window destroyed externally
|
||||
if (r == 0) {
|
||||
montauk::sleep_ms(16); // ~60 FPS idle
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev.type == 3) break; // Close event
|
||||
|
||||
if (ev.type == 2) { // Resize
|
||||
g_win_w = ev.resize.w;
|
||||
g_win_h = ev.resize.h;
|
||||
pixels = (uint32_t*)(uintptr_t)montauk::win_resize(win_id, g_win_w, g_win_h);
|
||||
}
|
||||
|
||||
if (ev.type == 0) { // Keyboard
|
||||
// ev.key.ascii, ev.key.scancode, ev.key.pressed
|
||||
}
|
||||
|
||||
if (ev.type == 1) { // Mouse
|
||||
// ev.mouse.x, ev.mouse.y, ev.mouse.buttons
|
||||
}
|
||||
|
||||
render(pixels);
|
||||
montauk::win_present(win_id);
|
||||
}
|
||||
|
||||
montauk::win_destroy(win_id);
|
||||
montauk::exit(0);
|
||||
}
|
||||
```
|
||||
|
||||
### Window Lifecycle
|
||||
|
||||
```
|
||||
win_create() Create window, get pixel buffer pointer
|
||||
|
|
||||
v
|
||||
win_present() Push current pixel buffer to screen
|
||||
|
|
||||
v
|
||||
win_poll() Receive events (returns 0 if none, <0 if closed)
|
||||
|
|
||||
v
|
||||
win_resize() Handle resize, get new pixel buffer pointer
|
||||
|
|
||||
v
|
||||
win_destroy() Clean up window
|
||||
```
|
||||
|
||||
### Event Types
|
||||
|
||||
Events are delivered via `win_poll()` into a `WinEvent` struct:
|
||||
|
||||
| `ev.type` | Event | Fields |
|
||||
|---|---|---|
|
||||
| 0 | Keyboard | `ev.key.scancode`, `ev.key.ascii`, `ev.key.pressed`, `ev.key.shift`, `ev.key.ctrl`, `ev.key.alt` |
|
||||
| 1 | Mouse | `ev.mouse.x`, `ev.mouse.y`, `ev.mouse.buttons`, `ev.mouse.prev_buttons`, `ev.mouse.scroll` |
|
||||
| 2 | Resize | `ev.resize.w`, `ev.resize.h` |
|
||||
| 3 | Close | (none) |
|
||||
|
||||
### Drawing Helpers
|
||||
|
||||
Standalone apps typically define local pixel-drawing helpers since they work with raw `uint32_t*` buffers:
|
||||
|
||||
```cpp
|
||||
static void px_fill(uint32_t* px, int bw, int bh,
|
||||
int x, int y, int w, int h, uint32_t color) {
|
||||
for (int row = y; row < y + h && row < bh; row++)
|
||||
for (int col = x; col < x + w && col < bw; col++)
|
||||
px[row * bw + col] = color;
|
||||
}
|
||||
|
||||
static void px_hline(uint32_t* px, int bw, int bh,
|
||||
int x, int y, int w, uint32_t color) {
|
||||
for (int col = x; col < x + w && col < bw; col++)
|
||||
if (y >= 0 && y < bh) px[y * bw + col] = color;
|
||||
}
|
||||
|
||||
static void px_text(uint32_t* px, int bw, int bh,
|
||||
int x, int y, const char* text, Color c, int size) {
|
||||
if (g_font)
|
||||
g_font->draw_to_buffer(px, bw, bh, x, y, text, c, size);
|
||||
}
|
||||
```
|
||||
|
||||
### Rounded Rectangles
|
||||
|
||||
```cpp
|
||||
static void px_fill_rounded(uint32_t* px, int bw, int bh,
|
||||
int x, int y, int w, int h, int r, uint32_t color) {
|
||||
for (int row = y; row < y + h && row < bh; row++) {
|
||||
for (int col = x; col < x + w && col < bw; col++) {
|
||||
int dx = 0, dy = 0;
|
||||
if (col < x + r && row < y + r) { dx = x + r - col; dy = y + r - row; }
|
||||
else if (col >= x+w-r && row < y + r) { dx = col - (x+w-r-1); dy = y + r - row; }
|
||||
else if (col < x + r && row >= y+h-r) { dx = x + r - col; dy = row - (y+h-r-1); }
|
||||
else if (col >= x+w-r && row >= y+h-r) { dx = col - (x+w-r-1); dy = row - (y+h-r-1); }
|
||||
if (dx * dx + dy * dy <= r * r)
|
||||
px[row * bw + col] = color;
|
||||
else if (dx == 0 && dy == 0)
|
||||
px[row * bw + col] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Desktop-Integrated Apps
|
||||
|
||||
Desktop apps are compiled into the desktop binary itself. They register callback functions that the compositor calls during its event loop.
|
||||
|
||||
### Creating a Desktop App
|
||||
|
||||
**Step 1: Define your app state**
|
||||
|
||||
```cpp
|
||||
// In apps/app_myapp.cpp
|
||||
struct MyAppState {
|
||||
int counter;
|
||||
char label[64];
|
||||
};
|
||||
```
|
||||
|
||||
**Step 2: Implement callbacks**
|
||||
|
||||
```cpp
|
||||
static void myapp_on_draw(Window* win, Framebuffer& fb) {
|
||||
MyAppState* state = (MyAppState*)win->app_data;
|
||||
Canvas c(win);
|
||||
|
||||
c.fill(colors::WINDOW_BG);
|
||||
|
||||
// Draw toolbar
|
||||
c.fill_rect(0, 0, win->content_w, 36, Color::from_rgb(0xF5, 0xF5, 0xF5));
|
||||
c.hline(0, 36, win->content_w, colors::BORDER);
|
||||
|
||||
// Draw content
|
||||
c.text(20, 60, state->label, colors::TEXT_COLOR);
|
||||
}
|
||||
|
||||
static void myapp_on_mouse(Window* win, MouseEvent& ev) {
|
||||
MyAppState* state = (MyAppState*)win->app_data;
|
||||
if (ev.left_pressed()) {
|
||||
state->counter++;
|
||||
win->dirty = true; // Request redraw
|
||||
}
|
||||
}
|
||||
|
||||
static void myapp_on_key(Window* win, const Montauk::KeyEvent& key) {
|
||||
if (!key.pressed) return;
|
||||
MyAppState* state = (MyAppState*)win->app_data;
|
||||
// Handle keystrokes...
|
||||
win->dirty = true;
|
||||
}
|
||||
|
||||
static void myapp_on_close(Window* win) {
|
||||
MyAppState* state = (MyAppState*)win->app_data;
|
||||
montauk::mfree(state);
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Write the open function**
|
||||
|
||||
```cpp
|
||||
void open_myapp(DesktopState* ds) {
|
||||
int idx = desktop_create_window(ds, "My App", 400, 300, 320, 400);
|
||||
if (idx < 0) return;
|
||||
Window* win = &ds->windows[idx];
|
||||
|
||||
MyAppState* state = (MyAppState*)montauk::malloc(sizeof(MyAppState));
|
||||
montauk::memset(state, 0, sizeof(MyAppState));
|
||||
|
||||
win->app_data = state;
|
||||
win->on_draw = myapp_on_draw;
|
||||
win->on_mouse = myapp_on_mouse;
|
||||
win->on_key = myapp_on_key;
|
||||
win->on_close = myapp_on_close;
|
||||
win->dirty = true;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: Register in the app menu**
|
||||
|
||||
Add an entry in `desktop_init()` (`main.cpp`) to the app menu so users can launch it. Include the `open_myapp` function in `apps_common.hpp` or similar.
|
||||
|
||||
### Callback Reference
|
||||
|
||||
| Callback | Signature | When Called |
|
||||
|---|---|---|
|
||||
| `on_draw` | `void(Window*, Framebuffer&)` | Every frame when `win->dirty` is true |
|
||||
| `on_mouse` | `void(Window*, MouseEvent&)` | Mouse event within window content area |
|
||||
| `on_key` | `void(Window*, const KeyEvent&)` | Keyboard event while window is focused |
|
||||
| `on_close` | `void(Window*)` | Window close button clicked |
|
||||
| `on_poll` | `void(Window*)` | Every frame, for background processing |
|
||||
|
||||
### The `dirty` Flag
|
||||
|
||||
Desktop apps use a `dirty` flag to control redraws. Set `win->dirty = true` after any state change that requires a visual update. The compositor skips `on_draw` for non-dirty windows.
|
||||
|
||||
---
|
||||
|
||||
## Drawing and Rendering
|
||||
|
||||
### Canvas API (Desktop Apps)
|
||||
|
||||
The `Canvas` wraps a window's pixel buffer with drawing primitives:
|
||||
|
||||
```cpp
|
||||
Canvas c(win); // Construct from Window*
|
||||
|
||||
// Fills
|
||||
c.fill(Color c); // Entire buffer
|
||||
c.fill_rect(int x, int y, int w, int h, Color c); // Rectangle
|
||||
c.fill_rounded_rect(int x, int y, int w, int h, int r, Color c); // Rounded rect
|
||||
|
||||
// Lines
|
||||
c.hline(int x, int y, int len, Color c); // Horizontal
|
||||
c.vline(int x, int y, int len, Color c); // Vertical
|
||||
c.rect(int x, int y, int w, int h, Color c); // Outline
|
||||
|
||||
// Text
|
||||
c.text(int x, int y, const char* str, Color c); // TrueType or bitmap
|
||||
c.text_2x(int x, int y, const char* str, Color c); // 2x scaled bitmap
|
||||
c.text_mono(int x, int y, const char* str, Color c); // Monospace
|
||||
|
||||
// UI elements
|
||||
c.button(int x, int y, int w, int h, const char* label,
|
||||
Color bg, Color fg, int radius); // Styled button
|
||||
c.icon(int x, int y, const SvgIcon& ic); // SVG icon
|
||||
|
||||
// Layout helpers
|
||||
c.kv_line(int x, int* y, const char* line, Color c, int line_h); // Key-value line
|
||||
c.separator(int x_start, int x_end, int* y, Color c, int spacing); // Horizontal separator
|
||||
```
|
||||
|
||||
### Framebuffer API (Low-Level)
|
||||
|
||||
For direct framebuffer access (used by the compositor itself and fullscreen apps):
|
||||
|
||||
```cpp
|
||||
Framebuffer fb;
|
||||
fb.put_pixel(x, y, color);
|
||||
fb.put_pixel_alpha(x, y, color); // With alpha blending
|
||||
fb.fill_rect(x, y, w, h, color);
|
||||
fb.fill_rect_alpha(x, y, w, h, color);
|
||||
fb.blit(x, y, w, h, pixels); // Copy pixel region
|
||||
fb.blit_alpha(x, y, w, h, pixels); // With alpha blending
|
||||
fb.clear(color);
|
||||
fb.flip(); // Swap to hardware
|
||||
```
|
||||
|
||||
### Drawing Primitives
|
||||
|
||||
From `gui/draw.hpp`, available for both Framebuffer-based rendering:
|
||||
|
||||
```cpp
|
||||
draw_hline(fb, x, y, w, color);
|
||||
draw_vline(fb, x, y, h, color);
|
||||
draw_rect(fb, x, y, w, h, color);
|
||||
fill_rounded_rect(fb, x, y, w, h, radius, color);
|
||||
fill_circle(fb, cx, cy, r, color);
|
||||
draw_circle(fb, cx, cy, r, color);
|
||||
draw_line(fb, x0, y0, x1, y1, color); // Bresenham's
|
||||
draw_shadow(fb, x, y, w, h, offset, color);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Text and Fonts
|
||||
|
||||
### TrueType Fonts (Preferred)
|
||||
|
||||
MontaukOS uses `stb_truetype` for font rendering. Fonts are loaded from the VFS:
|
||||
|
||||
```cpp
|
||||
TrueTypeFont* font = new TrueTypeFont();
|
||||
font->init("0:/fonts/Roboto-Medium.ttf");
|
||||
|
||||
// Render text to a pixel buffer
|
||||
font->draw_to_buffer(pixels, buf_w, buf_h, x, y, "Hello", color, 18);
|
||||
|
||||
// Measure text width before drawing
|
||||
int width = font->measure_text("Hello", 18);
|
||||
|
||||
// Get line height for layout
|
||||
int line_h = font->get_line_height(18);
|
||||
```
|
||||
|
||||
### System Fonts
|
||||
|
||||
The desktop initializes a set of global fonts:
|
||||
|
||||
```cpp
|
||||
fonts::init(); // Call once at startup
|
||||
|
||||
// Available fonts
|
||||
fonts::system_font // Roboto-Medium.ttf (UI text)
|
||||
fonts::system_bold // Roboto-Bold.ttf (headings)
|
||||
fonts::mono // JetBrainsMono-Regular.ttf (code/terminal)
|
||||
fonts::mono_bold // JetBrainsMono-Bold.ttf
|
||||
|
||||
// Standard sizes
|
||||
fonts::UI_SIZE // 18 (body text)
|
||||
fonts::TITLE_SIZE // 18 (window titles)
|
||||
fonts::LARGE_SIZE // 28 (headings)
|
||||
fonts::TERM_SIZE // 18 (terminal)
|
||||
```
|
||||
|
||||
### Glyph Caching
|
||||
|
||||
TrueType fonts cache rasterized glyphs per pixel size. Up to 4 size caches are maintained per font. Access the cache directly for advanced metrics:
|
||||
|
||||
```cpp
|
||||
GlyphCache* cache = font->get_cache(18);
|
||||
// cache->ascent, cache->descent — for line-height calculation
|
||||
```
|
||||
|
||||
### Bitmap Font (Fallback)
|
||||
|
||||
An 8x8 bitmap font is always available for basic text rendering when TrueType is not loaded:
|
||||
|
||||
```cpp
|
||||
draw_char(fb, x, y, 'A', color);
|
||||
draw_text(fb, x, y, "Hello", color);
|
||||
int w = text_width("Hello");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Handling
|
||||
|
||||
### Keyboard
|
||||
|
||||
```cpp
|
||||
// In standalone apps (via win_poll)
|
||||
if (ev.type == 0) {
|
||||
Montauk::KeyEvent& key = ev.key;
|
||||
if (!key.pressed) { /* key release */ }
|
||||
|
||||
if (key.ascii >= 0x20 && key.ascii < 0x7F) {
|
||||
// Printable character
|
||||
}
|
||||
|
||||
// Special keys by scancode
|
||||
switch (key.scancode) {
|
||||
case 0x01: /* Escape */ break;
|
||||
case 0x0E: /* Backspace */ break;
|
||||
case 0x1C: /* Enter */ break;
|
||||
case 0x0F: /* Tab */ break;
|
||||
case 0x53: /* Delete */ break;
|
||||
case 0x48: /* Up */ break;
|
||||
case 0x50: /* Down */ break;
|
||||
case 0x4B: /* Left */ break;
|
||||
case 0x4D: /* Right */ break;
|
||||
case 0x47: /* Home */ break;
|
||||
case 0x4F: /* End */ break;
|
||||
case 0x49: /* Page Up */ break;
|
||||
case 0x51: /* Page Down */ break;
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
if (key.ctrl) { /* Ctrl held */ }
|
||||
if (key.shift) { /* Shift held */ }
|
||||
if (key.alt) { /* Alt held */ }
|
||||
}
|
||||
```
|
||||
|
||||
### Mouse
|
||||
|
||||
```cpp
|
||||
// In standalone apps (via win_poll)
|
||||
if (ev.type == 1) {
|
||||
int mx = ev.mouse.x;
|
||||
int my = ev.mouse.y;
|
||||
|
||||
// Button state
|
||||
bool left_down = ev.mouse.buttons & 0x01;
|
||||
bool right_down = ev.mouse.buttons & 0x02;
|
||||
|
||||
// Detect clicks (press edge)
|
||||
bool left_pressed = (ev.mouse.buttons & 0x01) && !(ev.mouse.prev_buttons & 0x01);
|
||||
bool left_released = !(ev.mouse.buttons & 0x01) && (ev.mouse.prev_buttons & 0x01);
|
||||
|
||||
// Scroll wheel
|
||||
int scroll = ev.mouse.scroll; // Positive = up, negative = down
|
||||
}
|
||||
```
|
||||
|
||||
In desktop apps, the `MouseEvent` struct provides convenience methods:
|
||||
|
||||
```cpp
|
||||
void myapp_on_mouse(Window* win, MouseEvent& ev) {
|
||||
if (ev.left_pressed()) { /* click start */ }
|
||||
if (ev.left_released()) { /* click end */ }
|
||||
if (ev.left_held()) { /* dragging */ }
|
||||
if (ev.right_pressed()) { /* context menu */ }
|
||||
if (ev.scroll != 0) { /* scroll */ }
|
||||
}
|
||||
```
|
||||
|
||||
### Hit Testing
|
||||
|
||||
A common pattern for clickable UI regions:
|
||||
|
||||
```cpp
|
||||
struct ButtonRect { int x, y, w, h; };
|
||||
|
||||
bool hit_test(ButtonRect& btn, int mx, int my) {
|
||||
return mx >= btn.x && mx < btn.x + btn.w &&
|
||||
my >= btn.y && my < btn.y + btn.h;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Widgets
|
||||
|
||||
MontaukOS provides built-in widget types in `gui/widgets.hpp`:
|
||||
|
||||
### Button
|
||||
|
||||
```cpp
|
||||
Button btn;
|
||||
btn.init(x, y, width, height, "Click Me");
|
||||
btn.bg = colors::ACCENT;
|
||||
btn.fg = colors::WHITE;
|
||||
btn.on_click = [](void* data) { /* handle click */ };
|
||||
btn.userdata = my_state;
|
||||
|
||||
// In draw callback
|
||||
btn.draw(fb);
|
||||
|
||||
// In mouse callback
|
||||
btn.handle_mouse(ev);
|
||||
```
|
||||
|
||||
### TextBox
|
||||
|
||||
```cpp
|
||||
TextBox tb;
|
||||
tb.init(x, y, width, height);
|
||||
|
||||
// In draw callback
|
||||
tb.draw(fb);
|
||||
|
||||
// In mouse callback (sets focus)
|
||||
tb.handle_mouse(ev);
|
||||
|
||||
// In key callback (text input)
|
||||
tb.handle_key(key);
|
||||
|
||||
// Read value
|
||||
const char* value = tb.text;
|
||||
```
|
||||
|
||||
### Scrollbar
|
||||
|
||||
```cpp
|
||||
Scrollbar sb;
|
||||
sb.init(x, y, width, height);
|
||||
sb.content_height = 2000; // Total content height
|
||||
sb.view_height = 400; // Visible area height
|
||||
|
||||
// In draw callback
|
||||
sb.draw(fb);
|
||||
|
||||
// In mouse callback
|
||||
sb.handle_mouse(ev);
|
||||
|
||||
// Use scroll_offset for content positioning
|
||||
int offset = sb.scroll_offset;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Colors and Theming
|
||||
|
||||
### Color Construction
|
||||
|
||||
```cpp
|
||||
Color c1 = Color::from_rgb(0xFF, 0x00, 0x00); // Red
|
||||
Color c2 = Color::from_rgba(0x00, 0x00, 0xFF, 0x80); // Semi-transparent blue
|
||||
Color c3 = Color::from_hex(0x367BF0); // From hex
|
||||
uint32_t pixel = c3.to_pixel(); // ARGB for pixel buffer
|
||||
```
|
||||
|
||||
### System Colors
|
||||
|
||||
Defined in `gui/gui.hpp` under the `colors` namespace:
|
||||
|
||||
| Constant | Hex | Usage |
|
||||
|---|---|---|
|
||||
| `WINDOW_BG` | `#FFFFFF` | Window content background |
|
||||
| `TEXT_COLOR` | `#333333` | Primary text |
|
||||
| `ACCENT` | `#367BF0` | Links, selections, active elements |
|
||||
| `BORDER` | `#D0D0D0` | Window/widget borders |
|
||||
| `PANEL_BG` | `#2B3E50` | Taskbar/panel background |
|
||||
| `PANEL_TEXT` | `#FFFFFF` | Panel text |
|
||||
| `TITLEBAR_BG` | `#F5F5F5` | Window titlebar |
|
||||
| `DESKTOP_BG` | `#E0E0E0` | Desktop background |
|
||||
| `CLOSE_BTN` | `#FF5F57` | Close button (red) |
|
||||
| `MAX_BTN` | `#28CA42` | Maximize button (green) |
|
||||
| `MIN_BTN` | `#FFBD2E` | Minimize button (yellow) |
|
||||
| `TERM_BG` | `#2D2D2D` | Terminal background |
|
||||
| `TERM_FG` | `#CCCCCC` | Terminal text |
|
||||
| `SCROLLBAR_BG` | | Scrollbar track |
|
||||
| `SCROLLBAR_FG` | | Scrollbar thumb |
|
||||
|
||||
### Toolbar Convention
|
||||
|
||||
Standard toolbar pattern used across desktop apps:
|
||||
|
||||
```cpp
|
||||
// 36px tall, light gray background, thin bottom border
|
||||
c.fill_rect(0, 0, win->content_w, 36, Color::from_rgb(0xF5, 0xF5, 0xF5));
|
||||
c.hline(0, 36, win->content_w, colors::BORDER);
|
||||
|
||||
// 24x24 icon buttons centered at y=6
|
||||
c.icon(8, 6, my_icon);
|
||||
|
||||
// Content starts below toolbar
|
||||
int content_y = 37;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Userspace Heap
|
||||
|
||||
Use `montauk::malloc`, `montauk::mfree`, and `montauk::realloc` for dynamic allocation:
|
||||
|
||||
```cpp
|
||||
#include <montauk/heap.h>
|
||||
|
||||
MyState* state = (MyState*)montauk::malloc(sizeof(MyState));
|
||||
montauk::memset(state, 0, sizeof(MyState));
|
||||
// ... use state ...
|
||||
montauk::mfree(state);
|
||||
|
||||
// Resize
|
||||
char* buf = (char*)montauk::malloc(256);
|
||||
buf = (char*)montauk::realloc(buf, 512);
|
||||
```
|
||||
|
||||
The allocator uses size-class buckets (32 to 4096 bytes) with an overflow list for larger allocations.
|
||||
|
||||
### Kernel Page Allocation
|
||||
|
||||
`montauk::alloc` / `montauk::free` allocate kernel pages. Avoid for temporary buffers; use the heap instead.
|
||||
|
||||
### Important Notes
|
||||
|
||||
- **User stack is 32 KiB** (8 pages). Deep call chains (e.g., TrueType rendering) can approach this limit. Avoid large stack allocations.
|
||||
- Use `inline` (not `static`) for shared globals in headers to avoid per-translation-unit copies and heap corruption.
|
||||
- The libc needs `-fno-tree-loop-distribute-patterns` in CFLAGS to prevent GCC from converting `memcpy`/`memset` into calls to themselves.
|
||||
|
||||
---
|
||||
|
||||
## Networking and HTTPS
|
||||
|
||||
MontaukOS provides a shared TLS library (`tls/tls.hpp`) backed by BearSSL, and the MontaukAI dev environment adds a higher-level HTTP wrapper (`http/http.hpp`) on top. Build with `USE_TLS=1` to link TLS support.
|
||||
|
||||
### HTTP Wrapper (`http/http.hpp`)
|
||||
|
||||
Header-only library that handles DNS resolution, request building, TLS, response parsing, and cleanup. All functions return an `http::Response` struct.
|
||||
|
||||
#### Setup
|
||||
|
||||
```cpp
|
||||
#include <http/http.hpp>
|
||||
|
||||
// Load CA certificates once at startup (required for HTTPS)
|
||||
tls::TrustAnchors tas = tls::load_trust_anchors();
|
||||
```
|
||||
|
||||
#### GET
|
||||
|
||||
```cpp
|
||||
auto resp = http::get("api.example.com", "/v1/data", tas);
|
||||
if (resp.status == 200) {
|
||||
// resp.body is a pointer to the response body
|
||||
// resp.body_len is its length
|
||||
}
|
||||
http::free_response(&resp);
|
||||
```
|
||||
|
||||
#### POST
|
||||
|
||||
```cpp
|
||||
const char* json = "{\"name\":\"MontaukOS\",\"version\":1}";
|
||||
auto resp = http::post("api.example.com", "/v1/submit",
|
||||
"application/json",
|
||||
json, montauk::slen(json),
|
||||
tas);
|
||||
if (resp.status == 201) {
|
||||
// Created successfully
|
||||
}
|
||||
http::free_response(&resp);
|
||||
```
|
||||
|
||||
#### Other Methods (PUT, PATCH, DELETE, ...)
|
||||
|
||||
```cpp
|
||||
auto resp = http::request("PUT", "api.example.com", "/v1/item/42",
|
||||
"application/json",
|
||||
body, bodyLen, tas);
|
||||
http::free_response(&resp);
|
||||
|
||||
// DELETE with no body
|
||||
auto resp2 = http::request("DELETE", "api.example.com", "/v1/item/42",
|
||||
nullptr, nullptr, 0, tas);
|
||||
http::free_response(&resp2);
|
||||
```
|
||||
|
||||
#### Plain HTTP (No TLS, Port 80)
|
||||
|
||||
```cpp
|
||||
auto resp = http::get_plain("example.com", "/");
|
||||
if (resp.status == 200) {
|
||||
// resp.body ...
|
||||
}
|
||||
http::free_response(&resp);
|
||||
```
|
||||
|
||||
#### Reading Response Headers
|
||||
|
||||
```cpp
|
||||
char content_type[128];
|
||||
if (http::get_header(&resp, "Content-Type", content_type, sizeof(content_type))) {
|
||||
// content_type is e.g. "application/json; charset=utf-8"
|
||||
}
|
||||
```
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
Pass extra headers as a string with `\r\n` terminators:
|
||||
|
||||
```cpp
|
||||
auto resp = http::get("api.example.com", "/v1/data", tas,
|
||||
32768, // response buffer size
|
||||
"Authorization: Bearer tok_abc123\r\n"
|
||||
"Accept: application/json\r\n");
|
||||
http::free_response(&resp);
|
||||
```
|
||||
|
||||
#### Cancellable Requests
|
||||
|
||||
For GUI apps that need to stay responsive during network I/O:
|
||||
|
||||
```cpp
|
||||
static bool g_quit = false;
|
||||
static bool check_abort() { return g_quit; }
|
||||
|
||||
auto resp = http::get("api.example.com", "/v1/slow", tas,
|
||||
32768, nullptr, check_abort);
|
||||
http::free_response(&resp);
|
||||
```
|
||||
|
||||
Set `g_quit = true` from your keyboard handler (e.g., on Escape) to cancel mid-request.
|
||||
|
||||
#### Response Struct Reference
|
||||
|
||||
```cpp
|
||||
struct http::Response {
|
||||
int status; // HTTP status code (200, 404, ...) or -1 on error
|
||||
const char* headers; // Pointer to header block (within raw buffer)
|
||||
int headers_len;
|
||||
const char* body; // Pointer to body (within raw buffer)
|
||||
int body_len;
|
||||
char* raw; // Owned buffer — freed by free_response()
|
||||
int raw_len;
|
||||
};
|
||||
```
|
||||
|
||||
#### Function Signatures
|
||||
|
||||
```cpp
|
||||
// HTTPS GET
|
||||
http::Response http::get(const char* host, const char* path,
|
||||
const tls::TrustAnchors& tas,
|
||||
int resp_buf_size = 32768,
|
||||
const char* extra_headers = nullptr,
|
||||
tls::AbortCheckFn abort_check = nullptr);
|
||||
|
||||
// HTTPS POST
|
||||
http::Response http::post(const char* host, const char* path,
|
||||
const char* content_type,
|
||||
const char* body, int body_len,
|
||||
const tls::TrustAnchors& tas,
|
||||
int resp_buf_size = 32768,
|
||||
const char* extra_headers = nullptr,
|
||||
tls::AbortCheckFn abort_check = nullptr);
|
||||
|
||||
// HTTPS with any method
|
||||
http::Response http::request(const char* method,
|
||||
const char* host, const char* path,
|
||||
const char* content_type,
|
||||
const char* body, int body_len,
|
||||
const tls::TrustAnchors& tas,
|
||||
int resp_buf_size = 32768,
|
||||
const char* extra_headers = nullptr,
|
||||
tls::AbortCheckFn abort_check = nullptr);
|
||||
|
||||
// Plain HTTP GET (port 80, no TLS)
|
||||
http::Response http::get_plain(const char* host, const char* path,
|
||||
int resp_buf_size = 32768,
|
||||
const char* extra_headers = nullptr);
|
||||
|
||||
// Parse raw HTTP response buffer (used internally, available if needed)
|
||||
int http::parse_response(char* buf, int len, http::Response* out);
|
||||
|
||||
// Case-insensitive header lookup
|
||||
bool http::get_header(const http::Response* resp, const char* name,
|
||||
char* out_val, int max_len);
|
||||
|
||||
// Free the response's raw buffer
|
||||
void http::free_response(http::Response* resp);
|
||||
```
|
||||
|
||||
### Low-Level TLS API (`tls/tls.hpp`)
|
||||
|
||||
If you need more control than `http::` provides (e.g., streaming responses, custom BearSSL setup), use the TLS layer directly:
|
||||
|
||||
```cpp
|
||||
#include <tls/tls.hpp>
|
||||
|
||||
tls::TrustAnchors tas = tls::load_trust_anchors();
|
||||
|
||||
// Build raw HTTP request yourself
|
||||
char req[512];
|
||||
// ... "GET /stream HTTP/1.1\r\nHost: ...\r\n\r\n" ...
|
||||
|
||||
char buf[65536];
|
||||
int n = tls::https_fetch("example.com", ip, 443, req, reqLen, tas, buf, sizeof(buf));
|
||||
```
|
||||
|
||||
See `syscalls.md` for the full `tls::` API reference.
|
||||
|
||||
### Plain TCP/UDP
|
||||
|
||||
For non-TLS networking without the HTTP wrapper, use the raw socket syscalls directly (see `syscalls.md`):
|
||||
|
||||
```cpp
|
||||
int sock = montauk::socket(Montauk::SOCK_TCP); // or SOCK_UDP
|
||||
montauk::connect(sock, ip, port);
|
||||
montauk::send(sock, data, len);
|
||||
int n = montauk::recv(sock, buf, maxLen);
|
||||
montauk::closesocket(sock);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## App Manifests
|
||||
|
||||
External standalone apps can be discovered by the desktop through TOML manifest files placed in `0:/apps/`:
|
||||
|
||||
```toml
|
||||
[app]
|
||||
name = "My App"
|
||||
binary = "myapp"
|
||||
icon = "myapp_icon.svg"
|
||||
|
||||
[menu]
|
||||
category = "Applications"
|
||||
visible = true
|
||||
```
|
||||
|
||||
**Categories:** `Applications`, `Internet`, `System`, `Games`
|
||||
|
||||
The desktop scans `0:/apps/` at startup and adds matching entries to the application menu. The `binary` field is the executable name (looked up in the system path).
|
||||
|
||||
---
|
||||
|
||||
## Build System
|
||||
|
||||
### Independent Development (MontaukAI)
|
||||
|
||||
The `MontaukAI/` directory provides a self-contained build environment. Edit the top of `Makefile` to configure your app:
|
||||
|
||||
```makefile
|
||||
APP_NAME := myapp
|
||||
SRCS := src/main.cpp src/stb_truetype_impl.cpp src/cxxrt.cpp src/network.cpp
|
||||
```
|
||||
|
||||
Build with optional feature flags:
|
||||
|
||||
```bash
|
||||
make # GUI-only app
|
||||
make USE_TLS=1 # With HTTPS/TLS (links libtls + libbearssl)
|
||||
make USE_JPEG=1 # With JPEG decoding (links libjpeg)
|
||||
make USE_TLS=1 USE_JPEG=1 # Both
|
||||
make install # Copy ELF to MontaukOS ramdisk
|
||||
```
|
||||
|
||||
The sysroot contains all headers and pre-built libraries:
|
||||
|
||||
```
|
||||
sysroot/
|
||||
├── include/
|
||||
│ ├── montauk/ syscall.h, heap.h, string.h, config.h, toml.h, user.h
|
||||
│ ├── gui/ gui.hpp, canvas.hpp, truetype.hpp, widgets.hpp, svg.hpp, ...
|
||||
│ ├── tls/ tls.hpp (HTTPS/TLS)
|
||||
│ ├── Api/ Syscall.hpp (low-level syscall numbers)
|
||||
│ ├── libc/ stdio.h, stdlib.h, string.h, ... (freestanding libc)
|
||||
│ ├── bearssl*.h BearSSL headers (for USE_TLS=1)
|
||||
│ └── (freestanding C/C++ standard headers)
|
||||
└── lib/
|
||||
├── liblibc.a C library (always linked)
|
||||
├── libtls.a TLS helper library
|
||||
├── libbearssl.a BearSSL crypto
|
||||
└── libjpeg.a JPEG decoding (stb_image)
|
||||
```
|
||||
|
||||
Key build details:
|
||||
- **Toolchain:** `x86_64-elf-g++` cross-compiler (falls back to system g++)
|
||||
- **Standard:** C++20 (`-std=gnu++20`), freestanding, no exceptions/RTTI
|
||||
- **SSE:** Enabled (`-msse -msse2`) for floating-point / TrueType rendering
|
||||
- **Entry point:** `extern "C" void _start()` (not `main`)
|
||||
- **Load address:** `0x400000` (set in `link.ld`)
|
||||
- **Runtime support:** `src/cxxrt.cpp` provides `operator new/delete` via `montauk::malloc/mfree`
|
||||
- **TrueType support:** `src/stb_truetype_impl.cpp` provides the stb_truetype implementation
|
||||
|
||||
### In-Tree Development
|
||||
|
||||
Standalone apps can also live in `MontaukOS/programs/src/<appname>/` with their own `Makefile`. Each app's Makefile sets `SRCS`, `CXXFLAGS`, `LDFLAGS`, and links against libraries in `programs/lib/`.
|
||||
|
||||
Desktop-integrated apps are compiled as part of the desktop binary — add your `.cpp` file to the desktop's source list in `programs/src/desktop/Makefile`.
|
||||
@@ -0,0 +1,572 @@
|
||||
# MontaukOS Syscall Reference
|
||||
|
||||
All syscalls are available through `#include <montauk/syscall.h>` in the `montauk` namespace. The syscall layer provides `syscall0()` through `syscall6()` primitives using AMD64 calling conventions. All raw syscalls return `int64_t`.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Process Management](#process-management)
|
||||
- [File System](#file-system)
|
||||
- [Memory](#memory)
|
||||
- [Console I/O](#console-io)
|
||||
- [Keyboard and Mouse](#keyboard-and-mouse)
|
||||
- [Window Server](#window-server)
|
||||
- [Framebuffer](#framebuffer)
|
||||
- [Networking](#networking)
|
||||
- [Audio](#audio)
|
||||
- [Bluetooth](#bluetooth)
|
||||
- [Timekeeping](#timekeeping)
|
||||
- [System Information](#system-information)
|
||||
- [Storage and Disks](#storage-and-disks)
|
||||
- [Terminal](#terminal)
|
||||
- [Process I/O Redirection](#process-io-redirection)
|
||||
- [Configuration](#configuration)
|
||||
- [User Management](#user-management)
|
||||
- [Utility Libraries](#utility-libraries)
|
||||
|
||||
---
|
||||
|
||||
## Process Management
|
||||
|
||||
```cpp
|
||||
void exit(int code); // Terminate process (noreturn)
|
||||
void yield(); // Yield CPU to scheduler
|
||||
void sleep_ms(uint64_t ms); // Sleep for milliseconds
|
||||
int getpid(); // Get current process ID
|
||||
int spawn(const char* path, const char* args); // Spawn child process (-1 on error)
|
||||
int waitpid(int pid); // Wait for process to exit
|
||||
int kill(int pid); // Kill a process
|
||||
int proclist(ProcInfo* buf, int max); // List all processes (returns count)
|
||||
```
|
||||
|
||||
**`ProcInfo` struct:**
|
||||
```cpp
|
||||
struct ProcInfo {
|
||||
int pid;
|
||||
char name[32];
|
||||
// ... additional fields
|
||||
};
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
||||
```cpp
|
||||
int getargs(char* buf, uint64_t maxLen); // Get command-line arguments passed to this process
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File System
|
||||
|
||||
```cpp
|
||||
int open(const char* path); // Open file (returns fd, negative on error)
|
||||
int close(int handle); // Close file handle
|
||||
int read(int handle, uint8_t* buf, uint64_t off, uint64_t size); // Read bytes at offset (returns bytes read)
|
||||
int fwrite(int handle, const uint8_t* buf, uint64_t off, uint64_t size); // Write bytes at offset
|
||||
uint64_t getsize(int handle); // Get file size
|
||||
int readdir(const char* path, const char** names, int max); // List directory entries (returns count)
|
||||
int fcreate(const char* path); // Create file (returns fd)
|
||||
int fdelete(const char* path); // Delete file (0 on success)
|
||||
int fmkdir(const char* path); // Create directory
|
||||
int drivelist(int* outDrives, int max); // Enumerate mounted drives (returns count)
|
||||
```
|
||||
|
||||
### Path Format
|
||||
|
||||
Paths use the format `<drive>:/<path>`, e.g. `0:/fonts/Roboto-Medium.ttf`. Drive 0 is the boot drive (ramdisk).
|
||||
|
||||
### Example: Reading a File
|
||||
|
||||
```cpp
|
||||
int fd = montauk::open("0:/config/settings.toml");
|
||||
if (fd < 0) { /* error */ }
|
||||
|
||||
uint64_t size = montauk::getsize(fd);
|
||||
uint8_t* buf = (uint8_t*)montauk::malloc(size + 1);
|
||||
montauk::read(fd, buf, 0, size);
|
||||
buf[size] = 0; // null-terminate
|
||||
montauk::close(fd);
|
||||
|
||||
// ... use buf ...
|
||||
montauk::mfree(buf);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory
|
||||
|
||||
```cpp
|
||||
void* alloc(uint64_t size); // Allocate kernel pages (avoid for temp buffers)
|
||||
void free(void* ptr); // Free kernel pages
|
||||
```
|
||||
|
||||
For general-purpose allocation, prefer the userspace heap (`montauk/heap.h`):
|
||||
|
||||
```cpp
|
||||
void* malloc(uint64_t size);
|
||||
void mfree(void* ptr);
|
||||
void* realloc(void* ptr, uint64_t size);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Console I/O
|
||||
|
||||
```cpp
|
||||
void print(const char* text); // Write string to kernel console
|
||||
void putchar(char c); // Write single character
|
||||
```
|
||||
|
||||
These write to the kernel's text-mode console, not to a GUI window.
|
||||
|
||||
---
|
||||
|
||||
## Keyboard and Mouse
|
||||
|
||||
### Keyboard (Direct, Non-Windowed)
|
||||
|
||||
```cpp
|
||||
bool is_key_available(); // Poll for pending keystroke
|
||||
void getkey(Montauk::KeyEvent* out); // Get next keystroke (blocks)
|
||||
char getchar(); // Get single ASCII character (blocks)
|
||||
```
|
||||
|
||||
**`KeyEvent` struct:**
|
||||
```cpp
|
||||
struct KeyEvent {
|
||||
bool pressed; // true = key down, false = key up
|
||||
uint8_t scancode; // PS/2 scancode
|
||||
char ascii; // ASCII value (0 if non-printable)
|
||||
bool shift, ctrl, alt;
|
||||
};
|
||||
```
|
||||
|
||||
**Common Scancodes:**
|
||||
|
||||
| Scancode | Key |
|
||||
|---|---|
|
||||
| `0x01` | Escape |
|
||||
| `0x0E` | Backspace |
|
||||
| `0x0F` | Tab |
|
||||
| `0x1C` | Enter |
|
||||
| `0x39` | Space |
|
||||
| `0x53` | Delete |
|
||||
| `0x48` | Up |
|
||||
| `0x50` | Down |
|
||||
| `0x4B` | Left |
|
||||
| `0x4D` | Right |
|
||||
| `0x47` | Home |
|
||||
| `0x4F` | End |
|
||||
| `0x49` | Page Up |
|
||||
| `0x51` | Page Down |
|
||||
|
||||
### Mouse (Direct, Non-Windowed)
|
||||
|
||||
```cpp
|
||||
void mouse_state(Montauk::MouseState* out); // Get current mouse state
|
||||
void set_mouse_bounds(int32_t maxX, int32_t maxY); // Set mouse boundary
|
||||
```
|
||||
|
||||
**`MouseState` struct:**
|
||||
```cpp
|
||||
struct MouseState {
|
||||
int32_t x, y;
|
||||
uint8_t buttons; // 0x01=Left, 0x02=Right, 0x04=Middle
|
||||
};
|
||||
```
|
||||
|
||||
For GUI apps, prefer the windowed event system (`win_poll`) over direct mouse/keyboard syscalls.
|
||||
|
||||
---
|
||||
|
||||
## Window Server
|
||||
|
||||
These syscalls are for standalone GUI apps that run as separate processes. The desktop compositor manages window frames; your app renders into a shared pixel buffer.
|
||||
|
||||
```cpp
|
||||
int win_create(const char* title, int w, int h, WinCreateResult* result);
|
||||
void win_destroy(int id);
|
||||
uint64_t win_present(int id); // Mark window dirty, present to screen
|
||||
int win_poll(int id, WinEvent* event); // Poll events (0=none, 1=event, <0=closed)
|
||||
uint64_t win_resize(int id, int w, int h); // Resize, returns new pixel buffer address
|
||||
uint64_t win_map(int id); // Get pixel buffer address
|
||||
int win_enumerate(WinInfo* info, int max); // List all windows
|
||||
int win_sendevent(int id, const WinEvent* event); // Send event to a window
|
||||
int win_setscale(int scale); // Set UI scale factor
|
||||
int win_getscale(); // Get UI scale factor
|
||||
int win_setcursor(int id, int cursor); // Set cursor style
|
||||
```
|
||||
|
||||
**`WinCreateResult` struct:**
|
||||
```cpp
|
||||
struct WinCreateResult {
|
||||
int id; // Window ID for subsequent calls
|
||||
uint64_t pixelVa; // Virtual address of shared pixel buffer (uint32_t* ARGB)
|
||||
};
|
||||
```
|
||||
|
||||
**`WinEvent` struct:**
|
||||
```cpp
|
||||
struct WinEvent {
|
||||
int type; // 0=Key, 1=Mouse, 2=Resize, 3=Close
|
||||
union {
|
||||
struct { uint8_t scancode; char ascii; bool pressed, shift, ctrl, alt; } key;
|
||||
struct { int x, y; uint8_t buttons, prev_buttons; int32_t scroll; } mouse;
|
||||
struct { int w, h; } resize;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Event Loop Pattern
|
||||
|
||||
```cpp
|
||||
while (true) {
|
||||
Montauk::WinEvent ev;
|
||||
int r = montauk::win_poll(win_id, &ev);
|
||||
if (r < 0) break; // Window closed
|
||||
if (r == 0) {
|
||||
montauk::sleep_ms(16); // No events, idle at ~60fps
|
||||
continue;
|
||||
}
|
||||
switch (ev.type) {
|
||||
case 0: handle_key(ev.key); break;
|
||||
case 1: handle_mouse(ev.mouse); break;
|
||||
case 2: handle_resize(ev.resize); break;
|
||||
case 3: goto done; // Close
|
||||
}
|
||||
render(pixels);
|
||||
montauk::win_present(win_id);
|
||||
}
|
||||
done:
|
||||
montauk::win_destroy(win_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Framebuffer
|
||||
|
||||
For direct framebuffer access (fullscreen apps, not typical for windowed apps):
|
||||
|
||||
```cpp
|
||||
void fb_info(FbInfo* info); // Get dimensions and pitch
|
||||
void* fb_map(); // Map framebuffer to user address space
|
||||
```
|
||||
|
||||
**`FbInfo` struct:**
|
||||
```cpp
|
||||
struct FbInfo {
|
||||
uint32_t width, height;
|
||||
uint32_t pitch; // Bytes per row
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Networking
|
||||
|
||||
### DNS and Connectivity
|
||||
|
||||
```cpp
|
||||
int32_t ping(uint32_t ip, uint32_t timeoutMs); // Ping, returns RTT in ms (-1 on timeout)
|
||||
uint32_t resolve(const char* hostname); // DNS lookup, returns IPv4 (0 on failure)
|
||||
void get_netcfg(NetCfg* out); // Get network config
|
||||
int set_netcfg(const NetCfg* cfg); // Set network config
|
||||
```
|
||||
|
||||
### Sockets
|
||||
|
||||
```cpp
|
||||
int socket(int type); // Create socket (0=TCP, 1=UDP)
|
||||
int connect(int fd, uint32_t ip, uint16_t port); // TCP connect
|
||||
int bind(int fd, uint16_t port); // Bind to port
|
||||
int listen(int fd); // Start listening
|
||||
int accept(int fd); // Accept connection (returns new fd)
|
||||
int send(int fd, const void* data, uint32_t len); // Send data (returns bytes sent)
|
||||
int recv(int fd, void* buf, uint32_t maxLen); // Receive data (returns bytes read)
|
||||
int closesocket(int fd); // Close socket
|
||||
```
|
||||
|
||||
### UDP
|
||||
|
||||
```cpp
|
||||
int sendto(int fd, const void* data, uint32_t len,
|
||||
uint32_t destIp, uint16_t destPort);
|
||||
int recvfrom(int fd, void* buf, uint32_t maxLen,
|
||||
uint32_t* srcIp, uint16_t* srcPort);
|
||||
```
|
||||
|
||||
### Example: Plain HTTP GET (Port 80)
|
||||
|
||||
```cpp
|
||||
uint32_t ip = montauk::resolve("example.com");
|
||||
int sock = montauk::socket(0); // TCP
|
||||
montauk::connect(sock, ip, 80);
|
||||
|
||||
const char* req = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
|
||||
montauk::send(sock, req, montauk::slen(req));
|
||||
|
||||
char buf[4096];
|
||||
int n = montauk::recv(sock, buf, sizeof(buf) - 1);
|
||||
buf[n] = 0;
|
||||
|
||||
montauk::closesocket(sock);
|
||||
```
|
||||
|
||||
### TLS / HTTPS Library
|
||||
|
||||
Header: `tls/tls.hpp` — requires linking `libtls.a` and `libbearssl.a` (build with `USE_TLS=1`).
|
||||
|
||||
Provides a high-level `tls::https_fetch()` that handles socket creation, BearSSL TLS 1.2 setup, handshake, request/response exchange, and cleanup in a single call.
|
||||
|
||||
```cpp
|
||||
#include <tls/tls.hpp>
|
||||
|
||||
// Load CA certificates (from 0:/etc/ca-certificates.crt)
|
||||
tls::TrustAnchors tas = tls::load_trust_anchors();
|
||||
|
||||
// Build HTTP request
|
||||
const char* host = "en.wikipedia.org";
|
||||
uint32_t ip = montauk::resolve(host);
|
||||
|
||||
char req[256];
|
||||
// ... build "GET /path HTTP/1.1\r\nHost: ...\r\n\r\n" into req ...
|
||||
int reqLen = montauk::slen(req);
|
||||
|
||||
// Fetch (handles TLS handshake, send, receive, teardown)
|
||||
char resp[32768];
|
||||
int n = tls::https_fetch(host, ip, 443, req, reqLen, tas, resp, sizeof(resp));
|
||||
if (n > 0) {
|
||||
resp[n] = 0;
|
||||
// ... parse HTTP response ...
|
||||
}
|
||||
```
|
||||
|
||||
**API reference:**
|
||||
|
||||
```cpp
|
||||
namespace tls {
|
||||
// Load PEM CA bundle from 0:/etc/ca-certificates.crt
|
||||
TrustAnchors load_trust_anchors();
|
||||
|
||||
// High-level: DNS → socket → TLS → exchange → cleanup
|
||||
int https_fetch(const char* host, uint32_t ip, uint16_t port,
|
||||
const char* request, int reqLen,
|
||||
const TrustAnchors& tas,
|
||||
char* respBuf, int respMax,
|
||||
AbortCheckFn abort_check = nullptr);
|
||||
|
||||
// Lower-level: run TLS exchange on an existing socket + BearSSL engine
|
||||
int tls_exchange(int fd, br_ssl_engine_context* eng,
|
||||
const char* request, int reqLen,
|
||||
char* respBuf, int respMax,
|
||||
AbortCheckFn abort_check = nullptr);
|
||||
|
||||
// Helpers for manual BearSSL usage
|
||||
int tls_send_all(int fd, const unsigned char* data, size_t len);
|
||||
int tls_recv_some(int fd, unsigned char* buf, size_t maxlen);
|
||||
void get_bearssl_time(uint32_t* days, uint32_t* seconds);
|
||||
}
|
||||
```
|
||||
|
||||
The optional `AbortCheckFn` callback (e.g., `bool check_quit()`) lets terminal/GUI apps cancel a fetch in progress (return `true` to abort).
|
||||
|
||||
### HTTP Wrapper (`http/http.hpp`)
|
||||
|
||||
The MontaukAI dev environment includes a higher-level HTTP wrapper built on top of `tls::https_fetch()`. It handles DNS, request building, TLS, and response parsing automatically. See the "Networking and HTTPS" section in `gui-apps.md` for full documentation and examples.
|
||||
|
||||
```cpp
|
||||
#include <http/http.hpp>
|
||||
|
||||
tls::TrustAnchors tas = tls::load_trust_anchors();
|
||||
|
||||
auto resp = http::get("api.example.com", "/v1/data", tas);
|
||||
if (resp.status == 200) { /* resp.body, resp.body_len */ }
|
||||
http::free_response(&resp);
|
||||
|
||||
auto resp2 = http::post("api.example.com", "/v1/submit",
|
||||
"application/json", json, jsonLen, tas);
|
||||
http::free_response(&resp2);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio
|
||||
|
||||
```cpp
|
||||
int audio_open(uint32_t sampleRate, uint8_t channels, uint8_t bitsPerSample);
|
||||
int audio_close(int handle);
|
||||
int audio_write(int handle, const void* data, uint32_t size); // Write PCM samples
|
||||
int audio_ctl(int handle, int cmd, int value); // Generic control
|
||||
```
|
||||
|
||||
### Convenience Wrappers
|
||||
|
||||
```cpp
|
||||
int audio_set_volume(int handle, int percent); // 0-100
|
||||
int audio_get_volume(int handle);
|
||||
int audio_pause(int handle);
|
||||
int audio_resume(int handle);
|
||||
int audio_get_pos(int handle); // Playback position
|
||||
int audio_get_output(int handle); // Current output device
|
||||
int audio_bt_status(int handle); // Bluetooth audio status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bluetooth
|
||||
|
||||
```cpp
|
||||
int bt_scan(BtScanResult* buf, int maxCount, uint32_t timeoutMs); // Scan for devices
|
||||
int bt_connect(const uint8_t* bdAddr); // Connect
|
||||
int bt_disconnect(const uint8_t* bdAddr); // Disconnect
|
||||
int bt_list(BtDevInfo* buf, int maxCount); // List connected devices
|
||||
int bt_info(BtAdapterInfo* buf); // Adapter info
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Timekeeping
|
||||
|
||||
```cpp
|
||||
uint64_t get_ticks(); // CPU tick counter
|
||||
uint64_t get_milliseconds(); // Milliseconds since boot
|
||||
void gettime(Montauk::DateTime* out); // Wall-clock time
|
||||
```
|
||||
|
||||
**`DateTime` struct:**
|
||||
```cpp
|
||||
struct DateTime {
|
||||
uint16_t year;
|
||||
uint8_t month, day, hour, minute, second;
|
||||
uint8_t weekday;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## System Information
|
||||
|
||||
```cpp
|
||||
void get_info(SysInfo* info); // CPU, memory, system info
|
||||
void memstats(MemStats* out); // Memory usage statistics
|
||||
int64_t getrandom(void* buf, uint32_t len); // Cryptographic random bytes
|
||||
void reset(); // System reset (noreturn)
|
||||
void shutdown(); // System shutdown (noreturn)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Storage and Disks
|
||||
|
||||
```cpp
|
||||
int partlist(PartInfo* buf, int max); // List GPT partitions
|
||||
int disk_read(int blockDev, uint64_t lba, uint32_t sectorCount, void* buf); // Raw sector read
|
||||
int disk_write(int blockDev, uint64_t lba, uint32_t sectorCount, const void* buf); // Raw sector write
|
||||
int gpt_init(int blockDev); // Initialize GPT
|
||||
int gpt_add(const GptAddParams* params); // Add partition
|
||||
int fs_mount(int partIndex, int driveNum); // Mount filesystem
|
||||
int fs_format(const FsFormatParams* params); // Format filesystem
|
||||
int diskinfo(DiskInfo* buf, int port); // Get disk info
|
||||
int devlist(DevInfo* buf, int max); // List block devices
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Terminal
|
||||
|
||||
For terminal-mode (non-GUI) applications:
|
||||
|
||||
```cpp
|
||||
void termsize(int* cols, int* rows); // Get terminal dimensions
|
||||
void termscale(int scale_x, int scale_y); // Set text scaling
|
||||
void get_termscale(int* scale_x, int* scale_y); // Get text scaling
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Process I/O Redirection
|
||||
|
||||
For launching child processes with redirected I/O (used by the terminal emulator):
|
||||
|
||||
```cpp
|
||||
int spawn_redir(const char* path, const char* args); // Spawn with I/O pipes
|
||||
int childio_read(int childPid, char* buf, int maxLen); // Read child stdout
|
||||
int childio_write(int childPid, const char* data, int len); // Write to child stdin
|
||||
int childio_writekey(int childPid, const Montauk::KeyEvent* key); // Send keystroke to child
|
||||
int childio_settermsz(int childPid, int cols, int rows); // Set child terminal size
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Header: `montauk/config.h`
|
||||
|
||||
TOML-based configuration stored in `0:/config/`. Provides per-system and per-user config files.
|
||||
|
||||
```cpp
|
||||
toml::Doc config::load(const char* name); // Load 0:/config/<name>.toml
|
||||
int config::save(const char* name, toml::Doc* doc); // Save config (0 = success)
|
||||
toml::Doc config::load_user(const char* username, const char* name); // Per-user config
|
||||
int config::save_user(const char* username, const char* name, toml::Doc* doc);
|
||||
|
||||
void config::set_string(toml::Doc* doc, const char* key, const char* val);
|
||||
void config::set_int(toml::Doc* doc, const char* key, int64_t val);
|
||||
void config::set_bool(toml::Doc* doc, const char* key, bool val);
|
||||
bool config::unset(toml::Doc* doc, const char* key);
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```cpp
|
||||
auto doc = montauk::config::load("session");
|
||||
const char* user = doc.get_string("session.username", "guest");
|
||||
|
||||
montauk::config::set_string(&doc, "session.theme", "dark");
|
||||
montauk::config::save("session", &doc);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## User Management
|
||||
|
||||
Header: `montauk/user.h`
|
||||
|
||||
User authentication and session management with SHA-256 password hashing.
|
||||
|
||||
```cpp
|
||||
bool user::authenticate(const char* username, const char* password);
|
||||
bool user::create_user(const char* username, const char* display_name,
|
||||
const char* password, const char* role);
|
||||
bool user::delete_user(const char* username);
|
||||
bool user::change_password(const char* username, const char* new_password);
|
||||
|
||||
void user::set_session(const char* username); // Log in
|
||||
void user::clear_session(); // Log out
|
||||
bool user::get_session_username(char* buf, int sz); // Get current user
|
||||
bool user::get_home_dir(char* buf, int sz); // Get user's home directory
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utility Libraries
|
||||
|
||||
### String Functions (`montauk/string.h`)
|
||||
|
||||
```cpp
|
||||
int slen(const char* s);
|
||||
bool streq(const char* a, const char* b);
|
||||
bool starts_with(const char* str, const char* prefix);
|
||||
void memcpy(void* dst, const void* src, uint64_t n);
|
||||
void memmove(void* dst, const void* src, uint64_t n);
|
||||
void memset(void* dst, int val, uint64_t n);
|
||||
void strcpy(char* dst, const char* src);
|
||||
void strncpy(char* dst, const char* src, int max);
|
||||
```
|
||||
|
||||
### Heap (`montauk/heap.h`)
|
||||
|
||||
```cpp
|
||||
void* malloc(uint64_t size);
|
||||
void mfree(void* ptr);
|
||||
void* realloc(void* ptr, uint64_t size);
|
||||
```
|
||||
Reference in New Issue
Block a user