summaryrefslogtreecommitdiffhomepage
path: root/.rules/plan/phase-02-window-manager.md
diff options
context:
space:
mode:
Diffstat (limited to '.rules/plan/phase-02-window-manager.md')
-rw-r--r--.rules/plan/phase-02-window-manager.md93
1 files changed, 93 insertions, 0 deletions
diff --git a/.rules/plan/phase-02-window-manager.md b/.rules/plan/phase-02-window-manager.md
new file mode 100644
index 0000000..2627a57
--- /dev/null
+++ b/.rules/plan/phase-02-window-manager.md
@@ -0,0 +1,93 @@
+# Phase 2 — Become a Window Manager
+
+---
+
+## Step 2.1 — Open an Xlib display connection alongside raylib
+
+Before `InitWindow()`, call `XOpenDisplay(NULL)` to get a `Display*`.
+Store it globally. Get the root window with `DefaultRootWindow()`. After
+the raylib loop exits, `XCloseDisplay()`.
+
+Print the display name and root window ID to confirm.
+
+**Verify:** The output prints something like `Display: :1, Root: 0x...`.
+Raylib window still works as before.
+
+---
+
+## Step 2.2 — Register as the window manager (SubstructureRedirect)
+
+After opening the display, call:
+
+```c
+XSelectInput(dpy, root,
+ SubstructureRedirectMask |
+ SubstructureNotifyMask |
+ StructureNotifyMask);
+XSync(dpy, False);
+```
+
+Set a custom X error handler that catches `BadAccess` (another WM is
+already running) and prints a clear message.
+
+**Verify:** Running in Xephyr succeeds (no other WM). Running a second
+instance prints "Another window manager is already running" and exits.
+
+---
+
+## Step 2.3 — Process X events in the raylib loop
+
+Inside the raylib `while (!WindowShouldClose())` loop, add a non-blocking
+X event drain:
+
+```c
+while (XPending(dpy) > 0) {
+ XEvent ev;
+ XNextEvent(dpy, &ev);
+ printf("Event: %d\n", ev.type);
+}
+```
+
+**Verify:** `./bin/run.sh --clients` — launching `xeyes` or `xterm` into
+`:1` prints `MapRequest`, `ConfigureRequest`, etc. to the terminal. The
+clients don't appear yet (because we don't handle `MapRequest`).
+
+---
+
+## Step 2.4 — Handle MapRequest and ConfigureRequest (basic)
+
+Add a simple event handler:
+
+- **`MapRequest`** → call `XMapWindow(dpy, ev.xmaprequest.window)`.
+- **`ConfigureRequest`** → honor the request by calling
+ `XConfigureWindow()` with the requested values.
+
+**Verify:** `./bin/run.sh --clients` — `xeyes`, `xclock`, and `xterm` now
+appear inside the Xephyr window. They're drawn by X directly (not
+composited yet), floating on top of/beside the raylib window. You can type
+in `xterm`.
+
+---
+
+## Step 2.5 — Track managed windows in a list
+
+Create a simple dynamic array (or linked list) of `WmWindow` structs:
+
+```c
+typedef struct {
+ Window xwin;
+ int x, y;
+ unsigned int width, height;
+ bool mapped;
+} WmWindow;
+```
+
+- On `MapRequest`: add the window to the list, then `XMapWindow`.
+- On `UnmapNotify`: mark `mapped = false`.
+- On `DestroyNotify`: remove from the list.
+- On `ConfigureRequest`: update stored geometry, then `XConfigureWindow`.
+
+Print the window list count on each change.
+
+**Verify:** Spawn/close clients in `:1`. The log shows windows being added
+and removed. Count matches what's visible.