Windows & a Desktop
Primitives become a system. Build a window manager that stacks, drags, and resizes windows; a global chrome so every titlebar matches; and a dock. This is where a pile of pixels becomes a desktop.
Build and boot in QEMU first. Follow the exact QEMU, USB and real-PC deployment guide before changing disks or firmware settings.
A window is just a rectangle that remembers its position, size, title, and z-order. A window manager is the code that stacks them, decides which one the mouse is over, and lets you drag and resize. Build it in that order.
4.1 A window struct and the stack
typedef struct {
int x, y, w, h;
const char *title;
int z; // stacking order; higher = on top
int focused;
int minimized;
void (*draw_content)(struct Window*); // each app fills this
} Window;
static Window windows[MAX_WINDOWS];
// draw back-to-front so higher-z windows land on top
void wm_draw_all(void) {
sort_by_z(windows);
for (int i = 0; i < window_count; i++)
if (!windows[i].minimized) draw_window(&windows[i]);
}4.2 Global window chrome
The mistake to avoid: letting each app draw its own titlebar. They drift apart instantly. Instead, one chrome function draws the frame — titlebar, border, shadow, close/min/max buttons, active vs inactive treatment — for every window. Apps only fill the content area. Change the look in one place, every window updates.
// spec-driven constants: change here, whole OS follows
#define TITLE_H 40
#define RADIUS 10
#define CTRL_SIZE 28
void chrome_draw_frame(Window *win) {
int focused = win->focused;
UINT32 body = focused ? 0x141B27 : 0x0F1622;
UINT32 title = focused ? 0xE8EDF4 : 0x64738A;
draw_shadow(win->x, win->y, win->w, win->h);
draw_rounded_rect(win->x, win->y, win->w, win->h, RADIUS, body);
draw_rounded_rect(win->x, win->y, win->w, TITLE_H, RADIUS, 0x101724);
draw_string(win->x+16, win->y+12, win->title, title);
draw_window_controls(win, focused); // close/min/max, right-aligned
}A focused window gets bright title text, a solid border, and a deeper shadow; an unfocused one goes dim, flatter, and its controls stop reacting to hover. That single contrast is most of what makes a desktop read as a real OS rather than a mockup. Bake it into the chrome from the start.
4.3 Dragging and resizing
Dragging is bookkeeping: on mouse-down in the titlebar, record the offset between the cursor and the window's corner; on every mouse-move while held, move the window to keep that offset constant. Resizing is the same idea on the edges, adjusting width/height instead of position.
static int drag_dx, drag_dy;
static Window *dragging;
void on_mouse_down(int mx, int my) {
Window *w = window_at(mx, my);
if (w && in_titlebar(w, mx, my)) {
dragging = w;
drag_dx = mx - w->x; // remember grab offset
drag_dy = my - w->y;
}
}
void on_mouse_move(int mx, int my) {
if (dragging) {
mark_dirty(dragging); // erase old position
dragging->x = mx - drag_dx;
dragging->y = my - drag_dy;
mark_dirty(dragging); // draw new position
}
}4.4 Hit-testing and focus
When the mouse clicks, you need the topmost window under it. Walk the stack from highest z to lowest; the first window whose rectangle contains the point wins. Clicking it raises it to the top and gives it focus.
Window *window_at(int mx, int my) {
// highest z first — topmost window wins
for (int i = window_count - 1; i >= 0; i--) {
Window *w = z_order[i];
if (!w->minimized && point_in_rect(mx, my, w->x, w->y, w->w, w->h))
return w;
}
return 0; // clicked the desktop
}4.5 A dock and the desktop
The desktop is the bottom layer: a wallpaper fill, a row of dock icons that open apps, and desktop icons. It's the same primitives — rects, text, hit-testing — assembled into a launcher. Click a dock icon, spawn a window, and the loop is complete: you have a desktop you can actually use.
- A window struct with z-order and a content callback
- One global chrome renderer — every window matches
- Draggable titlebars and resizable edges
- Top-down hit-testing and click-to-focus
- A dock that launches apps onto a real desktop
Module 05 — USB & a Filesystem
Claim the xHCI controller from the firmware, talk to a USB drive over Bulk-Only Transport, and read & write real FAT32 files — then a working file manager on top.
See pricing — from $9/mo or $149 once →