Commit Graph
50 Commits
Author SHA1 Message Date
3AvisD 899576cbbc feat(launcher): hide the League client UI while a match runs
Add an opt-in alternative to closing LeagueClientUx: hide the managed
LeagueClientUx and Riot Client X11 windows while the game process runs and
show them again when it exits. The client stays open; the game window is
never touched.

Detection is attributed to the launcher prefix through __mldr_sockpath and
fails closed when proc, ownership or window state is unreadable. The helper
requires X11/Xwayland, restores the window on end, signal or error, and is
mutually exclusive with RCLOL_CLOSE_CLIENT_IN_GAME=1.

Document the flag, compliance scope and limits. A local 30-second test on an
i5-3470/RX 550 reduced summed client/CEF CPU from 120.7% to 93.4%; this is a
CPU-bound-host measurement, not a universal FPS claim.

No client binaries, CEF, assets or anti-cheat are modified.
2026-10-22 12:15:35 -06:00
lucannez64 4d0affe1fc fix(launcher): only remove launcher artifacts this project created
Match the legacy entry against this checkout's desktop-run.sh instead of any
desktop-run.sh, and delete the legacy icon files only after that entry or the
old artwork proves ownership.  Quote and escape Exec arguments as the Desktop
Entry specification requires, so a checkout path with reserved characters no
longer produces an invalid entry, and escape the Path value.

Use StartupWMClass=RiotClientServices in both entries: Darling derives WM_CLASS
from -[NSProcessInfo processName], the basename of argv[0], and the launcher
starts .../RiotClientServices.  "Riot Client" is only the bundle display name,
so the checkout entry never matched the window.  Both entries now share
Name/GenericName/Comment/Icon/Keywords/Categories and a test keeps them in
sync; Terminal stays different because the checkout entry logs to
build/desktop-launch.log while the packaged entry runs the session in a
terminal.

Also drop the stray top-level icon copies, handle dangling legacy symlinks,
name the icon for assistive technology, document the alias and menu entry, and
cover the helper with disposable-HOME tests.
2026-10-21 17:38:42 +02:00
lucannez64 bdbf1e9a37 test(nix): run font compatibility regressions in CI 2026-10-20 13:57:05 +02:00
Nicolai Bruns 699b96ffc8 docs(tests): document fontconfig regression helpers 2026-10-20 13:43:22 +02:00
Nicolai Bruns 6a96869db8 fix(nix): harden fontconfig compatibility across host setups 2026-10-20 13:31:02 +02:00
nbandlucannez64 ec85cfb96b fix: preserve ambiguous guest processes during prefix cleanup (#111)
* fix: preserve ambiguous guest processes during prefix cleanup

* fix: warn only for matching runtime cleanup candidates

---------

Co-authored-by: lucannez64 <[email protected]>
2026-10-20 13:02:29 +02:00
nbandKalah bbfa4c61b0 fix(appkit): add NSSavePanel tag-name compatibility (#112)
Co-authored-by: Kalah <[email protected]>
2026-10-20 12:24:10 +02:00
lucannez64 306c6c5bf8 Use the host DNS resolver in Darling 2026-10-19 18:11:49 +02:00
lucannez64 71efcdf5f6 Merge current main into the tested VSync and VRR fixes 2026-10-19 12:59:58 +02:00
lucannez64 86cfbb1fd0 Merge VSync and VRR fixes for combined testing 2026-10-19 12:15:09 +02:00
lucannez64andClaude Sonnet 5.5 caec5f1dce Make NSOpenPanel Open/Cancel always end the modal session
Open with no row selected passed nil to -_setFilename:, and NSOpenPanel boxed it in an
array literal, so the action threw before the modal session was stopped and the panel
stayed up. Both buttons now go through one helper that stops the session only when
this panel is the modal window and always orders the panel out; Open accepts the
selection (filtered by canChooseFiles/canChooseDirectories and multiple-selection),
or the shown directory for directory choosers. Escape cancels.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-19 08:49:04 +02:00
lucannez64andClaude Sonnet 5.5 6a3f81cd3f Keep hidden X11 windows unmapped
Restack and makeKey no longer map a window AppKit considers hidden, and
setIsVisible: NO always unmaps, so the client's splash stays hidden.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-19 08:00:41 +02:00
lucannez64 99cd0f038d Present full-window Metal layers on the managed X11 surface for VRR 2026-10-19 07:35:16 +02:00
lucannez64 3142128575 Fix Metal VSync propagation to window swap intervals 2026-10-19 01:10:03 +02:00
lucannez64andClaude Opus 5.5 d6829e8201 tests: expect the shared-identity step at the end of rootless update
#79 made update.sh stop Darling again after the overlays and run
configure-darling-platform.py --replace-known-defaults --write. The
rootless update test still expected the old call list and failed. Stub the
identity tool, expect the stop + identity calls last, and check a failed
install never reaches them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 00:58:45 +02:00
lucannez64andClaude Opus 5.5 fd8732d4bc update.sh: replace the shared serial and UUID automatically
Older setups shipped serial C02ZL0GVMD6N and UUID
6F3B2A1E-8C4D-4E5F-9A7B-1D2C3E4F5A6B to every install. After installing,
update.sh now runs configure-darling-platform.py --replace-known-defaults,
which provisions the prefix only when its saved identity still has one of
those values. Other custom values (imported serials/UUIDs, model, board,
manufacturer, ROM, MLB) are kept; prefixes with no saved identity or only
custom values are not touched.

provision() also drops a standalone system-id or provisioning_UDID derived
from the shared UUID instead of keeping it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 00:17:22 +02:00
lucannez64andClaude Opus 5.5 4ae311b14e close-league-client-in-game: don't block closes right after boot
last_close started at 0.0 and the cooldown compares it with
time.monotonic(), which counts from boot. For the first 30 s of uptime
every close was refused as "in cooldown". That made the tests flaky on
freshly booted CI runners (seen on PR #76). Use None for "never closed".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 23:16:18 +02:00
lucannez64andClaude Opus 5.5 957b960985 close-league-client-in-game: start the idle clock only after a client was seen
A long patch or login can keep the League client closed for more than ten
minutes after launch; the helper then left before the first match.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 20:48:43 +02:00
3AvisD bc8fa71a7f fix(launcher): close the League client UI while a match runs
The client does not come back properly after a match when its own "Close client
during game" option is enabled. The UI (LeagueClientUx and its helpers) is a
Chromium/CEF instance and, under Darling, the pre-match instance keeps running:
the client the backend starts when the match ends is a second CEF instance on the
same --user-data-dir, and its window never paints. Its DOM and audio work and the
window stays black; closing the client by hand during a match leaves the same
state for the same reason.

RCLOL_CLOSE_CLIENT_IN_GAME=1 opts in to closing that UI from the host, and only
while a match runs, so the client that appears afterwards is the only instance.
The UI also competes with the game for CPU: on a four-core host with a Radeon
RX 550 a practice match reported roughly 120 FPS with the UI running and roughly
180 FPS with it closed. This is not the helper-killing memory optimization the
RAM section warns against, and the Riot client, the League backend and the game
are never touched.

The helper fails closed. It attributes the game and the client to the launcher's
prefix through the loader's __mldr_sockpath, since guest processes do not carry
DPREFIX (checked on a live session). It never closes a client younger than 60
seconds (the one the backend starts after a match), preserves a process of
another installation or owner, acts only when the environment and the stat fields
can be read, gives up after three closes in one match, and leaves after ten
minutes without a client or a match. It is off by default.

Tests use fake /proc entries and mock every signal.
2026-10-18 12:18:33 -06:00
lucannez64andClaude Sonnet 5.5 2be6d0d6bd Make --set-prefix safe against data loss
Stage a cross-filesystem copy in a private directory instead of deleting a
pre-existing PATH.partial, reject destinations inside the overlay work
directory, save the new path before deleting the old prefix, and verify that
the target keeps user.* xattrs before and after copying. Add tests.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-18 19:04:30 +02:00
David GhegheaandClaude Opus 5.5 48ae75855f Add darling-lol --set-prefix to keep the prefix on another drive
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]>
2026-10-18 18:32:14 +02:00
Kalah 46bd7ca472 Merge branch 'main' into feat/stop-and-signal-cleanup 2026-10-18 18:15:02 +02:00
lucannez64 fca543a62d Merge remote-tracking branch 'origin/main' into pr61/system-profiler-overview
# Conflicts:
#	.github/workflows/source-check.yml
2026-10-18 17:32:11 +02:00
lucannez64 7d74ed611b Merge remote-tracking branch 'origin/main' into pr61/metal-device-fixes
# Conflicts:
#	.github/workflows/source-check.yml
2026-10-18 17:31:14 +02:00
lucannez64andClaude Opus 5.5 59327fa9f0 Report the MacBookPro16,1 GPU name for Riot on AMD and Intel hosts
With DARLING_METAL_MAC_GPU_NAME=1, MTLDevice.name is the GPU the
MacBookPro16,1 that IOKit reports shipped with, from the host's vendor:
"AMD Radeon Pro 5500M" (vendor 0x1002) or "Intel UHD Graphics 630"
(0x8086). NVIDIA keeps its own name so the game never takes another
vendor's driver paths. recommendedMaxWorkingSetSize and every other value
stay the host's, which was the risky part of #61's identity.

The Riot launcher turns it on by default (0 or empty turns it off); other
Darling apps keep the host name. DARLING_METAL_GPU_NAME still wins.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 17:24:49 +02:00
34c3e4ae9f Report hardware and software overviews from IOKit and sysctl in system_profiler
system_profiler now answers SPHardwareDataType and SPSoftwareDataType (and
lists them in -listDataTypes). Model, serial number, platform UUID and
provisioning UDID come from IOKit's IOPlatformExpertDevice via ioreg; CPU,
cache and memory from sysctl; the OS version from sw_vers and uname. A
field the guest does not provide is left out.

#61 instead read a separate darling-smbios.conf written by a new
generator (gen-darling-smbios.py), with hardcoded fallbacks (an Intel Core
i9, macOS 14.7, SIP enabled). iokitd reads darling-platform.plist, so
system_profiler and IOKit could report different serials and models on the
same prefix, and the generator overwrote identities imported with
configure-darling-platform.py. Reading the guest's own APIs keeps a single
source of truth: configure-darling-platform.py remains the only way to set
or provision an identity, and it is still never run by install or launch.

tests/test_system_profiler.py runs the script under sh with stub guest
commands and runs in CI.

Split from #61.

Co-authored-by: DevWebeloper <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 17:17:05 +02:00
3752d9e812 Fix Metal device edge cases; keep reporting the host GPU
From #61, without the default "AMD Radeon Pro 5500M" / 4 GB identity: the
host GPU's name and Vulkan working set stay the default for every app, and
the existing DARLING_METAL_GPU_NAME and DARLING_METAL_VRAM_MB overrides
remain the opt-in. Reporting 4 GB on a 2 GB card invites allocation
failures, and under-reports larger cards.

Kept from #61:
- registryID returns MTLDeviceInfo::kDefaultRegistryID instead of 0; a
  real Mac never reports 0 and apps key devices by it.
- Vertex buffer layout and attribute arrays release the old item after
  retiring it; the retire step read the item after the release could free it.
- newDepthStencilState, newSamplerState and the device name return nil (or
  the fallback name) when Indium throws, instead of unwinding through
  Objective-C.
- MTLDepthStencilGetIndium is declared only with DARLING_METAL_ENABLED.
- The replaceRegion trace probes at most `width` bytes, which stays inside
  the first row for every pixel format.

metal.patch is regenerated in its existing file order, so the diff is
these changes only. tests/test_metal_device_info.py compiles the host
check against MTLDeviceInfo.h as shipped in the patch and runs in CI.

Split from #61.

Co-authored-by: DevWebeloper <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 17:14:47 +02:00
David GhegheaandClaude Opus 5.5 0a9ec5f67e Stop Darling on Ctrl+C, terminal hangup and darling-lol --stop
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]>
2026-10-18 17:14:26 +02:00
03fdd25bc0 Add an opt-in filter for host desktop-session variables
RCLOL_FILTER_HOST_ENV=1 makes exec-guest.py drop host session metadata
(WAYLAND_DISPLAY, XDG_SESSION_*, XDG_CURRENT_DESKTOP, GNOME/KDE/GDM,
systemd, SSH agent, terminal, toolkit, Flatpak/Snap/AppImage, exported
shell functions) and set SHELL to the guest's /bin/zsh. It is off by
default; RCLOL_KEEP_HOST_ENV lists names to keep anyway.

Unlike the denylist in #61, the filter runs after the launcher sets its
GPU tuning, keeps everything Darling and the host graphics and audio
libraries read (DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR and the XDG base
directories, D-Bus, PulseAudio/PipeWire/ALSA, DRI_PRIME, MESA_*, __GL_*,
__NV_*, VK_*, LD_PRELOAD for gamemoderun, LD_LIBRARY_PATH), and lives in
one place instead of three.

tests/test_exec_guest.py runs in CI; the launcher check also verifies
that MESA_NO_ERROR=1 and __GL_THREADED_OPTIMIZATIONS=0 reach the guest
with the filter on.

Split from #61.

Co-authored-by: DevWebeloper <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 17:13:24 +02:00
lucannez64 4c1f4a44c5 fix(test): let Darling initialize the private session prefix 2026-10-18 14:38:15 +02:00
lucannez64 a682eba974 Merge remote-tracking branch 'origin/main' into fix/pr27-reviewed 2026-10-18 14:30:00 +02:00
lucannez64 70d2d3121f test(cfnetwork): scope probe overlays after private-prefix startup 2026-10-18 14:20:40 +02:00
lucannez64andSypherrDev 8d462f2246 perf(indium): rebase lazy wakeups and batch tuning onto current polling
Keep the merged per-device completion budget, bounded parsing, idle blocking and timeout accounting. Scope the registration/blocking handshake to each device, restrict live batch windows to the timer resolution, and test registration interleavings, spin-disabled polling, shutdown, opt-out and independent devices with actual patched methods.

Co-authored-by: SypherrDev <[email protected]>
2026-10-18 14:17:35 +02:00
lucannez64 e07cf72a15 fix(cfnetwork): preserve shared sessions and delegate-queue completions
Ignore both invalidation methods on the shared session; deliver task completion through the owning session delegate queue. Extend the standalone probe to exercise singleton reuse and a suspended custom queue. Require stable old-client process identities and complete window data before optional recovery, and continue launch after shutdown failures.
2026-10-18 14:16:32 +02:00
Daniel Souza 0087384a02 Fix Riot session aborts, CDN downloads and hidden client recovery 2026-10-18 14:09:59 +02:00
lucannez64andPirrandi a5ceabb8e8 fix(nvidia): retain readback, resize and bounded completion fixes
Rebuild the remaining NVIDIA changes on current main, preserving the merged visual, draw-buffer, descriptor ownership and mixed Metal-tree fixes. Scope native-thread forwarding to resize notifications and make completion polling per device, bounded and part of the caller timeout budget.

Co-authored-by: Pirrandi <[email protected]>
2026-10-18 13:59:49 +02:00
lucannez64 8dc17b8e80 test(nix): cover region routing, stable readiness and lost terminals 2026-10-18 13:54:18 +02:00
Nicolai Bruns 57223d4c0a fix(nix): simplify compiler setup and complete rootless session lifecycle 2026-10-18 13:35:15 +02:00
Nicolai Bruns f5e65e3cf1 feat(nix): add experimental rootless FHS package and desktop launcher 2026-10-18 00:30:14 +02:00
lucannez64 0f2233304a Add stable opt-in per-prefix identity provisioning 2026-10-17 23:38:49 +02:00
lucannez64 d87ad2ce45 Implement bounded registry APIs and opt-in platform configuration 2026-10-17 23:32:01 +02:00
lucannez64 3ed48bd2ac Fix shell stream ownership and CPU frequency units 2026-10-17 23:31:23 +02:00
lucannez64 633c129a4f Extract runtime compatibility fixes from PR 41 2026-10-17 23:04:23 +02:00
lucannez64 d6199db86f Merge main into setup runtime safety, preserving certificate setup 2026-10-17 22:56:34 +02:00
Nicolai Bruns 445ec26a1a test(setup): isolate missing guest validation 2026-10-17 22:40:53 +02:00
Nicolai Bruns 100bc8252d fix(setup): validate final runtime destinations 2026-10-17 22:24:06 +02:00
Nicolai Bruns 9adfaf010f fix(setup): use consistent runtime paths and reject incomplete installs 2026-10-17 22:09:29 +02:00
lucannez64andClaude Opus 5.5 a1c00ee884 Build Darling's system root store from the host CA bundle
Security reads the system trust domain from
/System/Library/Keychains/SystemRootCertificates.keychain and
SystemTrustSettings.plist (TrustSettingsSchema.h). Darling ships neither, so
SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem) returns
errSecNoTrustSettings (-25263); Chromium's known_roots_mac and Riot's
CertBundle then report that they cannot read the system roots.

ensure-darling-root-certificates.py parses each host root with a minimal DER
walker and writes the trust record keyed by the uppercase SHA-1, with the
encoded issuer Name, the serial INTEGER's content octets, modDate and an
empty (unrestricted) trustSettings array. The keychain is built with the
guest security tool in one darling shell, in a private temporary directory,
and published from inside the guest so a running Darling sees it, falling
back to the host when the guest cannot write the directory. A digest stamp
skips later runs without starting Darling. The launcher warns and continues
on failure; install-runtime.sh runs it before stopping Darling.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-17 21:03:32 +02:00
lucannez64 f98afb2c44 Bound installer shutdown retries and reject failed shutdowns 2026-10-15 23:26:14 +02:00
lucannez64 9383dd2963 Handle process exit race during Darling shutdown 2026-10-15 22:26:28 +02:00