You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2cde8de
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: docs/selkies/components/baseimages.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,7 +26,7 @@ There is deliberately **no `latest` tag** for base images. Downstream images pin
26
26
-**The LSIO foundation**: each tag builds on the corresponding LinuxServer.io distro baseimage, inheriting s6-overlay init, the `abc` user with PUID and PGID remapping, `TZ`, Docker mods, and the `/custom-cont-init.d` and `/custom-services.d` hooks.
27
27
-**Selkies** (pinned commit) installed into the `/lsiopy` virtualenv, with pixelflux 2.x and pcmflux 2.x, plus [Pelorus](pelorus.md) preinstalled.
28
28
-**The web client**: prebuilt dashboards under `/usr/share/selkies/`, selected at runtime by the `DASHBOARD` variable.
29
-
-**Compositors for both stacks**: labwc (built from source with a small IPC patch that adds the window query socket Pelorus uses) for Wayland, and a patched Xvfb (with `-vfbdevice`DRI3 support) plus Openbox for the legacy X11 fallback. A patched wlroots build makes the compositor survive pixman rendering faults instead of crashing.
29
+
-**Display servers for both stacks**: labwc (built from source with a small IPC patch that adds the window query socket Pelorus uses) for Wayland, and an XLibre Xvfb built with glamor and DRI3 plus Openbox for X11. The Xvfb carries a LinuxServer patch that keeps the screen pixmap on the GPU and lets the framebuffer follow RandR resizes, which is what makes zero copy capture possible on X11. Both are prebuilt per distro in [selkies-layers](https://github.com/linuxserver/selkies-layers) and copied into the image. A patched wlroots build makes the compositor survive pixman rendering faults instead of crashing.
30
30
-**[Selkies Desktop](selkies-desktop.md)** at `/usr/bin/selkies-desktop`, activated by env var.
31
31
-**Nginx** with the fancyindex module, serving the client, proxying the WebSocket, handling basic auth, subfolder support, and the `/files` download index.
32
32
-**PulseAudio** with null sinks (`output` and `input`) wired for stream audio and microphone return.
@@ -54,8 +54,10 @@ At startup a chain of one shot init scripts configures everything from environme
54
54
55
55
## The two session modes
56
56
57
-
-**Wayland (default on capable hardware)**: pixelflux hosts the virtual compositor; labwc (single apps) or a full desktop (Webtop flavors) nests on it; zero copy GPU encoding is available. `PIXELFLUX_WAYLAND=true` is baked into current downstream images.
58
-
-**X11 (legacy fallback)**: patched Xvfb with DRI3, Openbox, XSHM capture. Selected with `PIXELFLUX_WAYLAND=false` or on flavors that have not moved to Wayland yet. Deprecated for GPU work.
57
+
-**Wayland**: pixelflux hosts the virtual compositor; labwc (single apps) or a full desktop (Webtop flavors) nests on it. `PIXELFLUX_WAYLAND=true` is baked into images whose application or desktop runs on Wayland.
58
+
-**X11**: XLibre Xvfb with glamor and DRI3 on `:1`, Openbox for single apps or the desktop's own window manager, and pixelflux capturing the GPU resident screen through DRI3. Selected with `PIXELFLUX_WAYLAND=false`, and the stack for applications and desktops that run best on X11.
59
+
60
+
Both stacks encode with zero copy on a GPU and share the same CPU encoders without one.
Copy file name to clipboardExpand all lines: docs/selkies/components/index.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,7 +56,7 @@ graph TD
56
56
57
57
To make the layering concrete, here is the life of one frame in a Wayland mode container:
58
58
59
-
1. The application renders into a buffer belonging to **labwc** (or KWin on KDE), which is itself a client of the headless Smithay compositor that **pixelflux** hosts in process.
59
+
1. The application renders into a buffer belonging to **labwc** (or KWin on KDE), which is itself a client of the headless Smithay compositor that **pixelflux** hosts in process. On X11 the application instead draws into an XLibre Xvfb whose screen pixmap lives on the GPU, and pixelflux blits it out through DRI3 rather than compositing it.
60
60
2. Pixelflux composites the output. If a GPU holds the framebuffer and the encoder lives on the same GPU, the frame is passed as a DMA-BUF straight into NVENC or VA-API, zero copy, for any codec the card carries. Otherwise it is read back and encoded on CPU, in parallel stripes for H.264 and JPEG and full frame for the other codecs.
61
61
3. Only regions that changed get encoded at all; a static screen costs almost nothing, and after motion stops a high quality paint over pass restores perfect detail.
62
62
4. The encoded frame, with a small binary header, is handed to **Selkies**, which broadcasts it over the WebSocket to every connected viewer with backpressure control per client.
Copy file name to clipboardExpand all lines: docs/selkies/components/pixelflux.md
+6-6Lines changed: 6 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,8 +10,8 @@ Pcmflux is its audio sibling: it captures PulseAudio output and encodes Opus fra
10
10
11
11
As of the 2.0.0 release, pixelflux is a **Rust** library exposed to Python through PyO3 (earlier versions were C++ with ctypes). One extension module contains:
12
12
13
-
- An **X11 backend**: XShm screen capture with XFixes cursor tracking, for the legacy X11 stack.
14
-
- A **Wayland backend**: a full headless Wayland compositor built on [Smithay](https://github.com/Smithay/smithay), running in process. This is the key architectural point of the modern stack: **pixelflux does not capture a Wayland compositor, it is the compositor.** It synthesizes the output, seat, and clipboard itself, which is why the framebuffer can live directly on a GPU and why input is injected through its API rather than tools like xdotool.
13
+
- An **X11 backend**: DRI3 capture that blits the GPU resident screen into DMA-BUFs the encoder imports in place, and XShm capture with XFixes cursor tracking where that does not apply.
14
+
- A **Wayland backend**: a full headless Wayland compositor built on [Smithay](https://github.com/Smithay/smithay), running in process. This is the key architectural point of the Wayland stack: **pixelflux does not capture a Wayland compositor, it is the compositor.** It synthesizes the output, seat, and clipboard itself, which is why the framebuffer can live directly on a GPU and why input is injected through its API rather than tools like xdotool.
15
15
- Six codecs across software and hardware encoders, and the damage tracking, paint over, and rate control logic shared between them.
16
16
17
17
## Encoders
@@ -37,7 +37,7 @@ Hardware encoders always operate full frame, delivered as a single full height s
37
37
38
38
### Damage detection
39
39
40
-
-**X11:** each stripe's pixels are hashed (xxh3) every frame, a changed hash marks the stripe dirty. Continuously changing regions enter a "damage block" state that skips re hashing for a configured number of frames to save CPU.
40
+
-**X11:**on the DRI3 path the Damage extension reports whether anything was drawn since the last frame, so nothing is hashed. On the XShm path each stripe's pixels are hashed (xxh3) every frame, a changed hash marks the stripe dirty. Continuously changing regions enter a "damage block" state that skips re hashing for a configured number of frames to save CPU.
41
41
-**Wayland:** no hashing needed, the compositor knows exactly which rectangles clients damaged and maps them to stripes.
42
42
- A fully idle screen takes a fast path that skips the encode thread pool entirely.
43
43
@@ -50,11 +50,11 @@ The signature quality feature. After a region has been static for a configurable
50
50
-**CRF/CQP mode** (default): constant quality, bits go wherever needed.
51
51
- Infinite GOP by default: keyframes are only sent on demand (client join, recovery, or an optional periodic interval). Bitrate, framerate, and quality are all adjustable live without restarting the capture.
52
52
53
-
## Zero copy on Wayland
53
+
## Zero copy
54
54
55
-
When the compositor renders on a GPU and the encoder is on the same GPU, frames flow as DMA-BUF handles from the render buffer straight into NVENC or VA-API. The pixels never touch system RAM and the CPU never sees them. If the render and encode devices differ, or a software encoder is selected, pixelflux falls back to a readback path automatically and logs which decision it made. Zero copy is a property of the capture path, not the codec: any codec the GPU carries takes it.
55
+
**Wayland.**When the compositor renders on a GPU and the encoder is on the same GPU, frames flow as DMA-BUF handles from the render buffer straight into NVENC or VA-API. The pixels never touch system RAM and the CPU never sees them. If the render and encode devices differ, or a software encoder is selected, pixelflux falls back to a readback path automatically and logs which decision it made. Zero copy is a property of the capture path, not the codec: any codec the GPU carries takes it.
56
56
57
-
On X11 there is one zero copy path as well. When the session encodes on NVENC, capture goes through NvFBC, the Nvidia X driver composites the screen into a bufferin video memory, and that buffer is registered with the encoder in place. Every other X11 session copies each frame once into shared memory. Nothing selects this, the driver decides and the log says which path was taken.
57
+
**X11.**On an X server whose screen lives on the GPU, which is what the containers run (XLibre Xvfb started with `-glamor -dri`), pixelflux allocates a small pool of DMA-BUFs through GBM on the render node the server draws with, hands each to the server as a pixmap through DRI3, and captures every frame as one `CopyArea` of the root window into the next buffer, a GPU blit in glamor. The encoder imports that buffer in place through the same path the Wayland capture uses, so no frame crosses to the CPU. The Damage extension gates capture so a static screen costs nothing, and the server composites the cursor and any watermark through Render. Nvidia, Intel, and AMD all take this path. It is declined, with one log line saying why, for software encoding, a codec the GPU has no engine for, a server drawing on a different device than the encoder, or a server without DRI3 1.2, Damage, or Render, and the session then streams through XShm with one copy per frame.
58
58
59
59
GPU selection is automatic: it walks `/sys/class/drm`, identifies cards by driver (`nvidia` goes to NVENC, `i915` and `amdgpu` to VA-API), and can be pinned by device path, index, or an `auto_gpu` token matching a driver or vendor ID.
Copy file name to clipboardExpand all lines: docs/selkies/developer-guide/architecture.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,14 +22,14 @@ The subtle point people miss: **pixelflux is the display server.** When the Selk
22
22
23
23
labwc then runs as a *client* of that compositor (using its Wayland backend, the nested pattern) and provides window management, decorations, and XWayland on display `:0` for legacy apps. Applications connect to labwc's `wayland-0` socket. Full desktops swap labwc for a heavier nested compositor: Webtop KDE runs `kwin_wayland` nested on `wayland-1` with plasmashell on top.
24
24
25
-
In X11 fallback mode the shape is more traditional: a patched Xvfb (with DRI3 device support) provides `:1`, Openbox manages windows, and pixelflux captures via XSHM with per stripe hashing for damage detection.
25
+
In X11 mode the shape is more traditional: an XLibre Xvfb built with glamor and DRI3 provides `:1` with its screen pixmap on the GPU, Openbox manages windows, and pixelflux captures the screen with a DRI3 blit into DMA-BUFs the encoder imports in place. Without a GPU, or when the DRI3 path is declined, it captures via XShm with per stripe hashing for damage detection.
26
26
27
27
## The video pipeline
28
28
29
29
### Capture and damage
30
30
31
31
- Wayland: the compositor knows exactly which rectangles changed each frame, damage tracking is free and exact.
32
-
- X11: pixelflux hashes each horizontal stripe of the framebuffer per frame (xxh3) and marks changed stripes dirty, with a "damage block" heuristic that stops re hashing regions that are continuously changing.
32
+
- X11: on the DRI3 path the Damage extension reports whether anything changed since the last frame. On the XShm path pixelflux hashes each horizontal stripe of the framebuffer per frame (xxh3) and marks changed stripes dirty, with a "damage block" heuristic that stops re hashing regions that are continuously changing.
33
33
- Idle screens take a fast path that costs close to zero CPU.
Copy file name to clipboardExpand all lines: docs/selkies/developer-guide/baseimage-internals.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ What actually happens inside a Selkies container, boot order, services, and the
6
6
7
7
The baseimage Dockerfile is a multi stage build assembling, onto a LinuxServer.io distro base (Debian, Ubuntu, Alpine, Fedora, Arch, or Kali):
8
8
9
-
-A patched **Xvfb**(adds a `-vfbdevice` flag for DRI3 GPU passthrough) from the LSIO xvfb image
9
+
-An **XLibre Xvfb**built with glamor and DRI3 from [selkies-layers](https://github.com/linuxserver/selkies-layers), patched so the screen pixmap lives on the GPU (`-glamor -dri /dev/dri/renderD###`) and the framebuffer follows RandR resizes
10
10
- The **web frontend**: the Selkies repository pinned to an exact commit, with `selkies-web-core` and the dashboards built and dropped in `/usr/share/selkies/`
11
11
-**labwc 0.9.7** built from source with the IPC patch (adds `labwc -i`, a read only JSON window query socket at `$XDG_RUNTIME_DIR/labwc.sock`, consumed by Pelorus)
12
12
- A rebuilt **wlroots** with a defensive patch that catches SIGSEGV and SIGBUS inside pixman draw calls and skips the frame instead of crashing the compositor
@@ -15,7 +15,7 @@ The baseimage Dockerfile is a multi stage build assembling, onto a LinuxServer.i
15
15
- The **input interposer** (`/usr/lib/selkies_input_interposer.so`) and **fake udev** (`/opt/lib/libudev.so.1.0.0-fake`) compiled from the Selkies addons
16
16
- Nginx with fancyindex, PulseAudio, mesa and VA-API userspace, Vulkan loaders, all system locales, proot-apps, Docker in Docker machinery, and passwordless sudo for `abc`
17
17
18
-
Baked ENV defaults worth knowing: `HOME=/config`, `DISPLAY=:1`, `TITLE=Selkies`, `SELKIES_ENCODER="h264enc,h265enc,vp8enc,vp9enc,av1enc,jpeg"`, `SELKIES_ENABLE_BASIC_AUTH=false`, `SELKIES_VIDEO_STREAMING_MODE=false`, `SELKIES_ALLOWED_ORIGINS="*"`, `START_DOCKER=true`, `DISABLE_ZINK=false`, `DISABLE_DRI3=false`, `NVIDIA_DRIVER_CAPABILITIES=all`, and the interposer path in `SELKIES_INTERPOSER`.
18
+
Baked ENV defaults worth knowing: `HOME=/config`, `DISPLAY=:1`, `TITLE=Selkies`, `SELKIES_ENCODER="h264enc,h265enc,vp8enc,vp9enc,av1enc,jpeg"`, `SELKIES_ENABLE_BASIC_AUTH=false`, `SELKIES_VIDEO_STREAMING_MODE=false`, `SELKIES_ALLOWED_ORIGINS="*"`, `START_DOCKER=true`, `DISABLE_DRI3=false`, `NVIDIA_DRIVER_CAPABILITIES=all`, and the interposer path in `SELKIES_INTERPOSER`.
19
19
20
20
## Boot: the init chain
21
21
@@ -51,7 +51,7 @@ init-os-end
51
51
| --- | --- |
52
52
|`svc-nginx`| Reaps zombie workers then runs Nginx in the foreground |
53
53
|`svc-pulseaudio`| PulseAudio as `abc`, never idle exits, runtime dir under `/defaults`|
54
-
|`svc-xorg`| X11 mode: Xvfb on `:1` with a max resolution from `MAX_RES` and the DRI3 device flag. Wayland mode: sleeps |
54
+
|`svc-xorg`| X11 mode: picks the render node (`DRINODE`, else the Nvidia node when one exists, else the first) and starts Xvfb on `:1` with `-glamor -dri <node>` so the screen lives on the GPU, unless `DISABLE_DRI3=true` or no node exists. Wayland mode: sleeps |
55
55
|`svc-selkies`| Loads the `output` and `input` null sinks once PulseAudio is up, then runs `selkies --addr=localhost --mode=websockets` as `abc`. In Wayland mode this is what brings up the pixelflux compositor on `wayland-1`|
56
56
|`svc-de`| Waits for the display (the `wayland-1` socket, or `xset q` on X11), then executes `/defaults/startwm_wayland.sh` or `/defaults/startwm.sh` as `abc`, recording the PID for clean teardown. On X11 it also sets the initial 1024x768 mode (or `SELKIES_MANUAL_WIDTH` and `HEIGHT`) via xrandr |
57
57
|`svc-xsettingsd`| X11 only, DPI hinting for legacy toolkits |
Copy file name to clipboardExpand all lines: docs/selkies/developer-guide/building-images.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,7 +27,7 @@ with a repository layout of:
27
27
root/
28
28
└── defaults/
29
29
├── autostart_wayland # what to run in Wayland mode
30
-
└── autostart # what to run in X11 fallback mode
30
+
└── autostart # what to run in X11 mode
31
31
```
32
32
33
33
and `root/defaults/autostart_wayland` containing, in the simplest case, one line:
@@ -46,7 +46,7 @@ The baseimage looks for these files. All are optional, sane defaults exist for e
46
46
| File | Role |
47
47
| --- | --- |
48
48
|`/defaults/autostart_wayland`| Command launched inside labwc (Wayland). Runs as the `abc` user. Copied to `/config/.config/labwc/autostart` on first run only, so users can edit it and their edits persist |
49
-
|`/defaults/autostart`| Same for Openbox (X11 fallback) |
49
+
|`/defaults/autostart`| Same for Openbox (X11) |
50
50
|`/defaults/menu_wayland.xml`, `/defaults/menu.xml`| Right click root menu (Openbox menu XML schema, shared by labwc) |
51
51
|`/defaults/startwm_wayland.sh`, `/defaults/startwm.sh`| Replace the *entire* session startup. This is the full desktop hook, override these and you own the session |
52
52
|`/usr/share/selkies/www/icon.png`| App icon: favicon, PWA icon, and the Selkies Desktop wallpaper mark |
@@ -124,7 +124,7 @@ The other Webtop flavors are lighter: Alpine XFCE's entire session script is ess
124
124
125
125
## The window manager layer: labwc and Openbox
126
126
127
-
Single application containers run [labwc](https://github.com/labwc/labwc) on Wayland, which is a **1:1 replacement for Openbox**: it consumes the same menu XML schema and an rc.xml with the same concepts, so everything below applies to both stacks, with the X11 fallback simply using Openbox itself. Full desktop Webtops (KDE, XFCE, and friends) bring their own window management and ignore this layer entirely.
127
+
Single application containers run [labwc](https://github.com/labwc/labwc) on Wayland, which is a **1:1 replacement for Openbox**: it consumes the same menu XML schema and an rc.xml with the same concepts, so everything below applies to both stacks, with X11 sessions simply using Openbox itself. Full desktop Webtops (KDE, XFCE, and friends) bring their own window management and ignore this layer entirely.
128
128
129
129
### The right click menu: menu.xml
130
130
@@ -186,7 +186,7 @@ Checklist:
186
186
- Kill the app inside the session: can you relaunch from the right click menu? Does `RESTART_APP=true` bring it back?
187
187
- Recreate the container against the same `/config`: settings persist, no first run dialogs
188
188
- Try `SELKIES_DESKTOP=true` and `PELORUS=true` if you intend to support them
189
-
- Run the X11 fallback (`-e PIXELFLUX_WAYLAND=false`) unless you are deliberately Wayland only, in which case ship the explanatory stub `startwm.sh`
189
+
- Run the X11 stack too (`-e PIXELFLUX_WAYLAND=false`) unless you are deliberately Wayland only, in which case ship the explanatory stub `startwm.sh`
0 commit comments