do not edit — generated by btf.
git.druid.rocksindexmilitantprogrammerastral_canvasnotes/overview.md

notes/overview.md


# Terminal — program overview
 
This is a raw X11/Wayland window and input template for a terminal emulator. It does not speak a PTY yet. It opens a window, feeds keyboard and mouse into a shared event queue, and draws an XRGB8888 software framebuffer so the rest of the terminal (cells, glyphs, child process) can sit on top later.
 
There is no GTK, SDL, or GLFW. Both backends talk to the display server directly and present the same CPU pixel buffer.
 
## Layout
 
```
src/main.c                 app loop and debug HUD
src/platform/platform.h    window + buffer + event API
src/platform/platform.c    backend pick (wayland / x11 / auto)
src/platform/event.h       event types and the queue
src/platform/x11.c         xcb window, MIT-SHM, XKB, pointer
src/platform/wayland.c     wl_compositor, xdg-shell, wl_seat, wl_shm
src/platform/shm.c         memfd/shm_open helper for pixel buffers
src/input/input.c          xkbcommon keymap, compose, modifiers
src/render/vk.c            Vulkan WSI, HUD/text pipelines
src/render/font.c          8×8 debug font atlas
shaders/                   GLSL for HUD and text
build/                     object files and generated Wayland protocol
notes/                     this documentation
```
 
This is the **Astral Canvas** GUI library (`libcanvas`), not a terminal. See `notes/design.md`.
 
`make` writes `.o` files and `wayland-scanner` output into `build/`. Public API is `A_*` in `<astral/canvas.h>`.
 
Install into the FHS local prefix (`/usr/local`):
 
```
make
sudo make install
```
 
That puts:
 
```
/usr/local/include/astral/canvas.h
/usr/local/lib/libcanvas.so
/usr/local/lib/libcanvas.a
/usr/local/lib/pkgconfig/canvas.pc
```
 
gcc already searches `/usr/local/include`, so this works with no extra `-I`:
 
```c
#include <astral/canvas.h>
```
 
```
cc main.c -lcanvas -o app
```
 
```
make
./terminal
./terminal --backend=x11
./terminal --backend=wayland
```
 
Auto backend uses `WAYLAND_DISPLAY` if set, otherwise `DISPLAY`. An explicit `--backend=` does not fall back.
 
## Runtime shape
 
```
main
  └─ platform_open()          pick x11 or wayland
       └─ platform_*_open()   connect, create window, first buffer
  loop
    plat.poll_events()        wait on the display fd
    platform_pop()            drain Event queue
    draw into plat.pixels     XRGB8888, stride in bytes
    plat.present()            upload to the server
  platform_close()
```
 
`Platform` is the only type `main` needs:
 
| Field / op | Meaning |
|---|---|
| `backend` | `"x11"` or `"wayland"` |
| `width`, `height` | logical surface size (compositor units) |
| `scale` | integer buffer scale (Wayland HiDPI; X11 is 1) |
| `buf_w`, `buf_h`, `stride` | pixel buffer geometry |
| `pixels` | XRGB `0xFFRRGGBB`, invalid after resize |
| `mods`, `buttons`, `mouse_x/y` | live input snapshot |
| `fd()` | display socket — poll this next to a PTY |
| `poll_events(timeout_ms)` | `-1` block, `0` nonblock, `>0` milliseconds |
| `present()` | attach/put the current buffer |
 
On `EVENT_RESIZE` the pixel pointer is already the new buffer. Redraw the whole frame.
 
## Events
 
Backends translate native messages into the same `Event` and push it on `Platform.events`. Consecutive pointer-motion events collapse to the latest so the queue cannot fill with moves.
 
**`EVENT_QUIT`** — close button, compositor close, or a dead connection.
 
**`EVENT_RESIZE`** — logical width/height and scale.
 
**`EVENT_EXPOSE`** — X11 expose (Wayland does not send this; just draw when dirty).
 
**`EVENT_FOCUS`** — keyboard focus in/out.
 
**`EVENT_KEY`**
 
- `keycode` is an xkb keycode (Linux evdev + 8 on both backends)
- `keysym`, `utf8`, `utf32` after xkb + compose
- `mods` is `MOD_CTRL | MOD_ALT | MOD_SHIFT | MOD_SUPER | …`
- `pressed` / `repeat`
 
X11 uses detectable auto-repeat (extra presses, no fake releases). Wayland repeats in the client from `wl_keyboard.repeat_info`. Compose sequences (dead keys) are handled in `input.c` so accented input is already UTF-8 by the time the PTY writer exists.
 
**`EVENT_POINTER`** — `kind` is enter, leave, motion, button, or axis.
 
- `x`, `y` are logical surface pixels, not buffer pixels
- buttons are 1 / 2 / 3 / 8 / 9 (left, middle, right, back, forward)
- wheel is **not** a fake button 4/5; it is `POINTER_AXIS` with `axis_steps` (detents) and `axis_value` (continuous). Positive vertical is down.
- `buttons` is the currently held mask
 
The HUD already converts the pointer to a cell: `col = mouse_x / CELL_W`, `row = (mouse_y - hud) / CELL_H`. That is the mapping later SGR mouse reporting will use, once the HUD is gone and the grid is the whole window.
 
## Drawing
 
Both backends own an XRGB8888 CPU buffer.
 
- **X11** maps MIT-SHM (fd attach, else SysV shm, else slow `PutImage`) and `xcb_shm_put_image`s it onto the window.
- **Wayland** draws into a staging buffer, copies into a free `wl_shm` buffer (double-buffered, `release` listener), then `attach` + `damage` + `commit`. Integer `wl_surface.set_buffer_scale` is applied from the outputs the surface is on.
 
`main` currently fills the buffer with a dark background, a character-cell grid (placeholder 16×16), a hover cell, and a four-line HUD. `font.c` is only for that HUD. Real glyphs will replace it.
 
Esc or `q` with no modifiers quits. That is demo behavior; a real terminal must send those keys to the child.
 
## X11 backend (`src/x11.c`)
 
- `xcb_connect`, InputOutput window, ICCCM `WM_DELETE_WINDOW` / `WM_CLASS`, `_NET_WM_NAME`
- Event mask: keys, buttons, motion, enter/leave, structure, expose, focus
- Keyboard: xkbcommon-x11 against the core device, `StateNotify` / `MapNotify` / `NewKeyboardNotify`
- Pointer: X11 buttons 4–7 become axis events; 1/2/3/8/9 stay buttons
- Cursor: xcb-cursor theme name `xterm`, fallback `left_ptr`
 
## Wayland backend (`src/wayland.c`)
 
- Registry binds `wl_compositor` (v4), `wl_shm`, `wl_seat` (up to v8), `wl_output` (v2), `xdg_wm_base`, optional `zxdg_decoration_manager_v1`
- xdg-shell toplevel, app id `terminal`; SSD requested when the compositor has decoration manager
- Seat: `wl_pointer` (frame-coalesced motion/button/axis, `axis_value120` accumulated to 120-unit steps) and `wl_keyboard` (keymap fd → xkb, modifiers, client-side repeat)
- Scale: max `wl_output.scale` among outputs the surface has entered
- Cursor surface uses the `xterm` / `text` / `left_ptr` theme
 
Wayland protocol C is generated at build time from `wayland-protocols` (`xdg-shell`, `xdg-decoration-unstable-v1`) into `build/`.
 
## Where a terminal plugs in
 
The display fd is `plat.fd()`. The next loop is roughly:
 
```
poll(display_fd, pty_fd, timeout for cursor blink / key repeat)
if display → dispatch + pop events
if pty     → read bytes, parse VT, dirty cells
if dirty   → rasterize glyphs into plat.pixels, present
```
 
Still missing, on purpose:
 
1. PTY (`posix_openpt`) and a child shell
2. VT parser and a grid of cells
3. A real font atlas (the 8×8 blit is HUD-only)
4. Clipboard / selection
5. xterm mouse protocols (X10, SGR, SGR-pixels) using the pointer events already in the queue
powered by btf.