1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# Phase 3 — Compositing: Redirect & Capture Window Contents
---
## Step 3.1 — Query and enable the Composite extension
At startup (after becoming WM), call:
```c
XCompositeQueryExtension(dpy, &composite_event, &composite_error);
XCompositeQueryVersion(dpy, &major, &minor);
XCompositeRedirectSubwindows(dpy, root, CompositeRedirectManual);
```
Print the extension version to confirm.
**Verify:** Prints "Composite extension: 0.4" (or similar). Clients
launched into `:1` are now **invisible** (redirected to off-screen pixmaps
but nobody is painting them). This is expected — the WM is responsible for
compositing now.
---
## Step 3.2 — Name window pixmaps
When a window is mapped (in the `MapRequest` handler or after
`XMapWindow`), call:
```c
Pixmap pixmap = XCompositeNameWindowPixmap(dpy, win->xwin);
```
Store the `Pixmap` in the `WmWindow` struct. On `ConfigureNotify` (resize),
free the old pixmap and re-obtain it.
**Verify:** No visual change yet (windows are still invisible), but no X
errors are printed. Confirm pixmap handles are non-zero via log output.
---
## Step 3.3 — Read pixmap into a raylib Texture (CPU copy path)
For each mapped window with a valid pixmap:
1. Call `XGetImage(dpy, pixmap, 0, 0, width, height, AllPlanes, ZPixmap)`
to read the pixel data.
2. Convert the BGRA data (X11 format) to RGBA.
3. Create a raylib `Image` from the buffer, then `LoadTextureFromImage()`.
4. Store the `Texture2D` in the `WmWindow` struct.
In the raylib draw loop, draw each managed window's texture at its
position using `DrawTexture()`.
**Verify:** `./bin/run.sh --clients` — `xeyes`, `xclock`, and `xterm`
appear as textured quads rendered by raylib on the dark blue background.
They are static snapshots (no live updates yet). The raylib window is the
compositor.
---
## Step 3.4 — Enable the Damage extension for live updates
At startup, query and enable Damage:
```c
XDamageQueryExtension(dpy, &damage_event, &damage_error);
```
For each managed window, create a damage object:
```c
win->damage = XDamageCreate(dpy, win->xwin, XDamageReportNonEmpty);
```
In the event loop, handle `DamageNotify`:
```c
if (ev.type == damage_event + XDamageNotify) {
// mark the window dirty
win->dirty = true;
}
```
When a window is dirty, before drawing, re-read the pixmap via
`XGetImage`, update the texture with `UpdateTexture()`, then call
`XDamageSubtract(dpy, win->damage, None, None)`.
**Verify:** `xeyes` follows your mouse cursor in real-time. `xclock`
ticks. Typing in `xterm` is visible immediately. This is a working (CPU-
copied) compositor.
---
## Step 3.5 — Handle window stacking order
Draw windows in the correct stacking order (bottom to top). Maintain the
list order to match X's stacking order. Use `XQueryTree()` at startup to
get the initial order, and update on `ConfigureNotify` with
`Above`/`Below` sibling fields, or re-query when the stack changes.
Also handle `XRaiseWindow` / `XLowerWindow` requests via
`ConfigureRequest`.
**Verify:** Overlapping windows draw in the correct order. Clicking
`xterm` (if focus-follows-click is wired) or manually calling
`DISPLAY=:1 xdotool windowraise <id>` changes the draw order.
|