The prefix holds the installed clients and game (about 40 GB). The Nix
launcher always exported DPREFIX=${DPREFIX:-~/.darling}, so the prefix saved
in build/config.json was never used and the only way to move it was an
environment variable in every launch context.
- The launcher uses DPREFIX if set, then the saved prefix, then ~/.darling.
- darling-lol --set-prefix PATH runs scripts/nix-prefix.py in the namespace,
whose root owns the prefix files, so cp -a keeps their owners and xattrs.
It renames the prefix when possible; across filesystems it copies to
PATH.partial and removes the old prefix and its overlay work directory
only after the copy completes. A PATH that already holds a Darling prefix
is used as it is. It refuses non-empty directories that are not prefixes,
nested paths and paths with commas or colons (overlayfs separators), then
saves the path with configure.py --prefix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After the clients quit, the session kept Darling running: Riot and League
crash handlers never exit under Darling, and the idle check counted them as
clients. Ctrl+C or closing the terminal killed bubblewrap, whose
--die-with-parent then killed nix-lifecycle.py before it could shut Darling
down, which left a live server marker behind.
- nix-idle.py ignores crash handlers when deciding whether a client still
uses the prefix.
- The launcher starts bubblewrap with INT, QUIT, HUP and TERM ignored, and
env restores them inside. The supervisor turns them into a stop request.
- nix-lifecycle.py polls for a signal or stop request and then shuts Darling
down without waiting for clients. It stops retrying a failed shutdown once
the server is gone, and waits for or kills its own command afterwards.
- darling-lol --stop makes the stop request and waits for the session. If
the session does not exit within 30 seconds, or no session holds the lock,
it kills the prefix's darlingserver and launchd like darling shutdown,
after checking that the marker PID is this user's darlingserver for this
prefix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
NixOS keeps the GPU driver's EGL vendor files and external-platform files
under /run/opengl-driver, which glvnd and NVIDIA's driver inside the FHS
environment do not search. glvnd then only knows Mesa, and NVIDIA cannot open
X11 displays because its X11 support is the egl-x11 external platform. The
client fell back to Mesa's software rasterizer: garbled text in the client and
no way to present the game's NVIDIA Vulkan frames.
When /run/opengl-driver provides them, prepend those directories to
__EGL_VENDOR_LIBRARY_DIRS and __EGL_EXTERNAL_PLATFORM_CONFIG_DIRS, keeping the
FHS paths and any caller-provided value after them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Chromium on macOS takes its temp directory from NSTemporaryDirectory(), which is
unusable under Darling. Electron opens files in app.asar asynchronously by
copying them to a temp file, and reports the failed copy as "not found", so the
Riot client showed a "Not Found" page instead of its UI. Running the client
binary as Node reproduces it: fs.readFileSync on app.asar/dist/renderer/index.html
works, fs.createReadStream fails with ENOENT, and MAC_CHROMIUM_TMPDIR fixes it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With __GL_THREADED_OPTIMIZATIONS=1, NVIDIA 580 (the last branch for Pascal
GPUs) with egl-x11 1.0.6 rejects the X11 EGLDisplay with EGL_BAD_DISPLAY on
every thread that has a context current. A native EGL program reproduces it
outside Darling. In the Riot client the main thread loses EGL after its first
window frame, so CGLCreateContext fails with error 10004 and the game window
stays black. Default the launcher to 0; setting the variable restores 1.
CGLDestroyWindow also returns early for the NULL window CGLGetWindow gives
when eglCreateWindowSurface fails, instead of passing EGL_NO_SURFACE on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>