Conversation
Survey of the fork's Linux readiness: build system, packaging template gap, per-feature platform matrix, and an enhancement-porting roadmap.
Reintroduce the four UNIX configure_file inputs referenced by cmake/prep/special_package_configuration.cmake (desktop entries, appstream metainfo, systemd user service). Content taken verbatim from LizardByte/Sunshine at b2d44f5, the last commit before upstream renamed these files; all @var@ placeholders are defined by this fork's CMake.
- The pinned third-party/tray implements Linux with Qt + libnotify (src/tray_linux.cpp); the referenced tray_linux.c no longer exists. Link the submodule's tray::tray target like upstream does and gate on libnotify only (the appindicator defines were unused by src/tray). - Remove the dead src/platform/linux/input.cpp entry; input lives in src/platform/linux/input/inputtino*.cpp picked up by the existing glob.
Platform fixes needed to build the fork's Windows-centric sources on
Linux (GCC 16, C++23):
- vdd_ioctl.cpp / vdd_utils.cpp: guard the Windows ZakoVDD transport and
driver logic with _WIN32; add Linux stubs so the shared display
session staging code links (driver reported absent/unsupported).
- vdd_utils.h: drop the unconditional windows.h include and add Linux
no-op SessionEventListener / w_utils::is_any_rdp_session_active
helpers used by session.cpp.
- globals.{h,cpp}: unguard VDD_NAME/ZAKO_NAME/is_running_as_system_user
(plain constants; Linux reports the device as absent).
- entry_handler.cpp, session.cpp, system_tray.cpp: move Windows-only
includes under _WIN32.
- config.cpp: add the fork's QVBR/HQVBR AMF rate-control constants
(4/5/6, matching AMF 1.5.2 headers) to the non-Windows fallback table.
- video.cpp: use NVENC SDK numeric option values in the avcodec branch
(the branch never compiled anywhere before; values from nvEncodeAPI.h).
- linux/display_device.cpp: update settings_t::apply_config stub to the
fork's signature and stub the fork-added settings/device helpers.
- linux/misc.cpp: platf::adapter_names() stub (Windows-side concept).
- linux/vaapi.cpp: include libavutil/pixdesc.h explicitly.
- confighttp.cpp: include boost/regex.hpp explicitly and guard the
GetConsoleWindow branch of the boom endpoint.
- Boost_Sunshine.cmake: add the regex component (used by confighttp).
- tests: exclude tests/unit/platform/windows from the aggregate suite on
non-Windows hosts.
- packaging/arch-local/PKGBUILD packages the prebuilt build/ tree (the repo must be configured with CMAKE_INSTALL_PREFIX=/opt/sunshine so the compiled-in assets path matches) via cmake --install under DESTDIR. - Restore the tray status icons (sunshine-playing/pausing/locked.svg) from upstream; their install rule survived but the source files were lost in the WebUI redesign.
Adapt upstream's sunshine.install for the /opt layout: grant cap_sys_admin to the packaged binary on install and upgrade (so the cap survives package updates), reload udev rules and trigger uinput/uhid.
The Linux tray_menus layout omits the two Windows-only entries (VDD menu, advanced settings), so the fixed indices in update_menu_texts() walked past the array and dereferenced a NULL submenu on Linux - segfaulting at startup right after encoder probing. Give each platform its own index block instead.
Implement the vdd_utils surface on Linux on top of the external sunshineVD helper (sunshine-virt-display) instead of the Windows-only ZakoVDD driver: - create/destroy translate to the daemon's Unix socket commands (--connect with the cached session mode / --disconnect); - find_device_by_friendlyname reports the daemon's live virtual connector (state file + /sys status), which is what display session staging waits for; - set_vdd_session_mode / wait_for_mode_publication cache the client's requested mode and re-issue connect so the EDID matches the stream; - get_vdd_status maps the helper socket to the driver status surface. vdd_capability::query_state() now uses the same status mapping on every platform, so clients may request a virtual display on Linux whenever the helper daemon is running.
The serverinfo field and the display_control response hardcoded capability_version=0 on non-Windows, so clients never offered the virtual display option even though the Linux backend now reports ready. Send the protocol version unconditionally - the socket backend implements the same session semantics.
…n socket Replace the sunshineVD socket backend with a direct implementation of the fork's virtual display semantics: - vdd_edid: byte-for-byte port of sunshineVD's EDID generator (base block with the requested preferred timing + CEA extension carrying BT.2020 colorimetry and an HDR static metadata block); locked to the reference implementation with byte-equality unit tests. - vdd_utils (Linux): create/destroy override a spare disconnected connector's EDID via debugfs and force its status on, without touching physical displays; mode changes cycle the connector with a regenerated EDID; session teardown clears the override. - kmsgrab: when a virtual display session is live, capture follows the plane backing the virtual connector instead of the enumeration order. - packaging: grant cap_dac_read_search/cap_dac_override next to cap_sys_admin so the debugfs/sysfs writes succeed.
setcap grants permitted (not effective) capabilities, so the native virtual display backend hit EACCES on /sys/kernel/debug/dri and the filesystem_error took down the display_control endpoint. Raise the granted caps into the effective set with a libcap RAII guard (same pattern as the KMS capture path) around every privileged operation, and use the non-throwing filesystem overloads in get_vdd_status.
…d output When a virtual display session is live, kmsgrab must capture the plane backing the virtual connector even if the client supplied a stale or Windows-style display name (the previous index-fallthrough could select a physical plane first).
- kmsgrab: name displays after their DRM connector (eDP-1, DP-1, ...) and resolve the configured/client-supplied name back at init; virtual display sessions still take priority over any supplied name. - enum_available_devices (Linux): enumerate live connectors from sysfs so the headless-host guard stops skipping virtual display teardown after every stream, and stream display matching resolves by connector name. - vdd_prep: implement display_off (exclusive mode) by powering off connected physical connectors for the session and restoring them on virtual display destruction; extend-style modes are the compositor's natural state. - destroy on stream end now runs (unblocked by the enumeration fix), and create sweeps stale virtual connectors carrying our EDID signature. - toggle_display_power toggles create/destroy, which is what the tray and input command expect; tray Foundation Display menu is enabled on Linux. - main: SIGINT/SIGTERM wake the tray event loop so shutdown is graceful instead of hitting the 10s force-exit watchdog.
…uit wakes the loop - A virtual connector goes live before the compositor assigns its CRTC, so encoder probes 200ms after creation found no matching plane and failed every encoder. Poll the connector map for up to 3s when a target connector is set. - Tray Quit on Linux raised the shutdown event but left the main thread parked in the tray event loop, hanging the process forever (the 10s watchdog is only armed by signals). Wake the loop with end_tray().
…er CRTC wait - find_one_of_the_available_devices() only ever matched the virtual display, so a client selecting the physical display (e.g. eDP-1) was rejected with 'display does not exist'. Match device id, display name and friendly name against the live connector enumeration instead. - The compositor CRTC wait now bails out early when the target connector is no longer connected, instead of stalling every probe attempt.
NVIDIA does not emit a hotplug for a status-forced connector, so KWin lists the output but leaves it disabled and never assigns a CRTC - every encoder probe then failed with 'Couldn't find monitor'. After forcing the connector on, run 'kscreen-doctor output.<connector>.enable' (the session compositor on KDE) so KWin adopts and lights the display.
… compositor adoption observable - resolve_display_intent: the fork client sends the fixed placeholder '23172' for its virtual display option; treat it (and ZAKO_NAME) as an explicit VDD target on Linux, same as the Windows VDD_NAME alias. - create_vdd_monitor now retries 'kscreen-doctor output.<conn>.enable' and polls DRM until the compositor hands out a CRTC (up to 4s), with the command output captured into the log; mode switches re-run it. - kmsgrab probe wait reduced to fast-fail (the real wait lives in creation), so failed probes no longer stall for seconds.
…down restore - Port sunshineVD's force_crtc_assignment to the native backend: borrow the compositor's DRM master via pidfd_getfd (candidates found by scanning /proc fds for the card device), drop their master, create a dumb framebuffer in the connector's preferred mode and run the modeset, then restore compositor master. Replaces the KDE-specific kscreen-doctor enable (kept as a fallback). Needs cap_sys_ptrace in the packaging hook. - enum_available_devices reports connectors we forced off for an exclusive session as inactive devices, so the session teardown's headless-host guard no longer skips virtual display destruction - the physical screen is restored when the stream ends.
…ctors NVIDIA reports a status-forced-off connector as plain 'disconnected' in sysfs, so the display enumeration dropped it and the session teardown's headless-host guard concluded 'VDD is the only display' and skipped virtual display destruction - leaving the physical screen dark after exclusive streams and rejecting client selections of it afterwards. The backend now reports its offlined physical connectors (by name) and the Linux enumeration includes them as inactive devices; enumeration contents are logged for diagnosis.
The helper split the status file path after the last '/' - yielding 'status' instead of the connector name. That poisoned everything built on it: the offlined-physical list reported 'status' instead of 'eDP-1' (so the display enumeration missed the forced-off screen and teardown skipped restoring it), and the mode-switch kscreen-doctor call targeted 'output.status'. Take the parent directory name first.
The client's screen combination modes map through to_vdd_prep to vdd_as_primary / vdd_as_secondary / display_off. Windows realizes the primary/secondary distinction through topology order; KWin's equivalent is the kscreen output priority, so apply_vdd_prep now sets the virtual display to priority 1 (primary mode) or 2 behind the physical screens (secondary mode) via kscreen-doctor, and teardown hands priority 1 back to the physical screens. display_off keeps the power-off behavior. Compositors without kscreen-doctor degrade to 'virtual display enabled'.
New round section in LINUX_PORT_GAPS.md (5.21) and report item 40 describe the three defects behind the report, with the log evidence (empty "Changing display modes to:"/"Changing HDR states to:" for VDD sessions), the correction of the earlier "the blanking model is more conservative than CVT-R" claim -- cvt -r needs 658.25 MHz for 3440x1440@120, so those modes are simply not expressible in a base-block DTD -- and an honest DisplayID follow-up. The remaining-work list moves to 5.22 and the stale lock-granularity hazard is marked fixed. Test baseline updated to 532 cases / 519 passed / 0 assertion failures.
The client asked for 2400x1080@60 and got 2400x1080@144: the EDID's preferred timing carried the requested mode (so the resolution was right), but filter_stale_devices() erased every VDD entry from the mode and HDR maps by design -- the original backend assumed the EDID made compositor-side mode changes unnecessary. KDE therefore kept the mode it picked when the connector came up, which is the highest refresh rate the EDID offers. Windows applies the client's requested mode and HDR state to the VDD through CCD, so the maps now preserve the virtual display being configured while still dropping virtual displays that belong to another session or to no topology at all (the original/rollback bookkeeping keeps stripping them: a mode restore must not touch a VDD that is about to be destroyed). The "EDID guarantees the mode" tolerance after a failed set_display_modes() now checks the *current* mode instead of the advertised list: KDE lists every rate the EDID carries, so an unapplied 60 Hz request would otherwise pass while the display ran at 144 Hz. Only a display that already runs the session mode is tolerated.
Adds packaging/aur/sunshine-foundation-git (PKGBUILD, .SRCINFO, install
script, README) so the fork can be built from source with makepkg instead
of only via the local prebuilt-tree package in packaging/arch-local.
makepkg neither checks out gitlinks nor has network access in build(), so
every submodule the build consumes - including the nested ones under
third-party/{doxyconfig,moonlight-common-c,tray} - is declared as a pinned
source entry and restored into its gitlink path in prepare(). The two
oversized Windows/platform checkouts are sparse-checked out: third-party/AMF
(answer: none, Windows-only) and third-party/build-deps (this machine's
dist/Linux-<arch> slice only).
Boost is pinned to an exact version by Boost_Sunshine.cmake, so the package
depends on that exact boost/boost-libs and documents the coupling; without it
CMake silently fetches and builds Boost from GitHub inside build().
options=(!strip) keeps the file capabilities the install script sets
(same set as packaging/arch-local), which Sunshine needs for KMS capture,
connector EDID/status access and the DRM-master borrow used by the virtual
display.
…SCII name
`makepkg -si` died in package() with
CMake Error at build/cmake_install.cmake:118 (file):
file INSTALL cannot find "build/sunshine-2026.0914.211531.376057a2"
The executable name carries the configure timestamp plus the current commit
(cmake/prep/build_version.cmake) and cmake bakes it into cmake_install.cmake,
so re-running cmake after a commit without relinking -- the normal slip after
a pkgrel-only bump -- leaves the install rules naming a binary that was never
built. prepare() now detects that (install rules and the sunshine symlink
disagree) and finishes the job with cmake + ninja, or fails with the actual
configure command when build/ is not configured at all. package() no longer
hides the CMake error behind >/dev/null and explains the fix.
A dirty tree appends the "杂鱼" marker to the stamp, which libarchive cannot
translate ("bsdtar: Can't translate ... to UTF-8") and which leaves pacman
with a path it cannot reliably match; the staged binary is now renamed to an
ASCII stamp (…dirty) and the sunshine symlink re-pointed at it. The version
string compiled into the binary is unchanged.
The install hook also refuses to run setcap on a dangling symlink (a package
whose stamped binary is missing would otherwise install a capability-less
binary that cannot capture or create virtual displays).
A test box whose session is niri (Plasma available but niri starts) and a service started from SSH both blocked for a long time on client connection, with kscreen-doctor hitting the 10 s run_logged timeout. kscreen-doctor drives org.kde.KScreen over the session bus. Where there is no KScreen, asking anyway makes D-Bus activate a KScreen service that waits for a Plasma session, so the tool never returns by itself and only dies at the kill timeout; the previous code spawned it for every display query, and a session start issues dozens (enumeration, topology, modes, HDR). The "unavailable" static only suppressed the log line, not the spawn. platf::kscreen now owns both the availability decision and the runner: - session_supports_kscreen() reads NIRI_SOCKET first (niri has no KScreen even when XDG_CURRENT_DESKTOP claims KDE for toolkit compatibility) and otherwise requires a Plasma desktop string. It is evaluated per call, never cached, so a service that starts before the desktop still picks it up afterwards. A session that cannot have KScreen is never probed: 0 ms per query instead of 10 s. - queries use a 1.5 s probe timeout, and a failed probe arms a 20 s cooldown, so a broken KScreen costs at most one short probe per window rather than a 10 s hang per query. A successful query clears the cooldown. - change commands keep the 10 s bound and are only reached after a successful query (or a session that supports KScreen). Measured with the real code: KDE session 699 ms / 1 mode (unchanged), niri 0 ms, no desktop 0 ms, KDE-with-unreachable-compositor 807 ms once then 0 ms. The VDD layer's kscreen commands got the same guard: a niri session is routed to its own IPC instead of being sent a command that can only hang. tests/unit/test_kscreen_backend.cpp pins the gate (env combinations) and the cooldown transitions; it is Linux-only like the module.
package() could ship a binary whose version stamp names an older commit: the stamp is fixed at configure time, so a fresh commit does not change it, and the install-rules check alone only caught the case where cmake named a binary that was never linked. `makepkg` now compares the stamp's embedded commit (and its dirty marker) with the tree and re-configures plus relinks when they disagree, so the `Sunshine version:` line the user checks always identifies the tree that was packaged. The check is free when they already match.
The Linux nvenc entry declared AV_PIX_FMT_NONE for both 4:4:4 slots and carried no YUV444_SUPPORT flag, so 4:4:4 client requests were rejected at prepare_encoder while Windows nvenc has always supported it. - video.cpp: declare AV_PIX_FMT_YUV444P (8-bit) and AV_PIX_FMT_YUV444P16 (10-bit shifted container, NV_ENC_BUFFER_FORMAT_YUV444_10BIT) plus the YUV444_SUPPORT flag; override profile=high with AV_PROFILE_H264_HIGH_444_PREDICTIVE in the h264 sdr444 option group since the plain SDR options apply to 4:4:4 sessions too and the av_dict value would win over ctx->profile. Support stays probe-gated by real test encodes (h264 444 SDR 8-bit, hevc/av1 444 HDR 10-bit): Ampere does h264/hevc 444, AV1 444 needs Ada. - The CUDA=OFF production path needs no platform change: the generic avcodec_software_encode_device_t (swscale + av_hwframe_transfer_data) is format-agnostic. - graphics: teach egl::create_target()/sws_t about planar 4:4:4 (three full-resolution single-channel planes). U/V planes reuse the ConvertY shader driven by two extra ColorMatrix UBOs with the UV range multiply and offset folded into the matrix (identity: (dot(cu,rgb)+cu.w)*ru.x+ru.y == dot(cu*ru.x,rgb) + (cu.w*ru.x+ru.y)), so no new shader assets; convert() renders 3 full-size passes. - cuda.cpp: gl_cuda_vram_t registers/maps/copies N planes instead of the hardcoded Y+UV pair; per-plane WidthInBytes/Height still derive from the pixel-format descriptor. - video.cpp analyzer: accept AV_PIX_FMT_YUV444P16LE as MSB-aligned (>>6) like P010, so 4:4:4 HDR sessions keep producing HDR10+/DV L1 statistics; planar P10 formats keep the LSB semantics from the P13 fix. - The CUDA=ON ram fallback (cuda::sws_t RGBA_to_NV12 kernel) stays NV12-only; those capture backends fail 4:4:4 sessions via the probe. VAAPI 4:4:4 remains undeclared (LINUX_PORT_GAPS.md 2.12).
The hand-rolled sd_bus_new + set_address + start path never set the bus-client flag, so sd-bus skipped the Hello() handshake: the socket connected and authenticated but acquired no unique name, and dbus-broker reset the connection on the first method call (ECONNRESET). Every klipper clipboard call failed this way, and the KWin foreground tracker in foreground_app.cpp (vtable registration + NameHasOwner probe) was silently degraded by the same helper. Setting sd_bus_set_bus_client(1) mirrors what sd_bus_open_user() does internally; verified with a minimal repro against klipper on dbus-broker.
Port two acceptance bounds from the Rust agent (clipboard.rs): outbound reads keep only non-empty text (klipper returns an empty string for non-text content such as a copied image, which previously went out as an empty frame and blanked the client clipboard), and inbound text carrying NUL bytes or empty is dropped, matching the agent's '!s.is_empty() && !s.contains(\0)' guard. Report: entry 43 (round 20) documents the D-Bus handshake fix and this parity pass with the repro evidence.
…on Linux A client-side dynamic bitrate change reached rate control (the control-stream handler, set_dynamic_param and set_bitrate all run) but only the bitrate fields were updated, so the VBV window stayed sized for the previous rate: too small for a higher rate (the encoder is starved), too large for a lower one (bursts). The native Windows NVENC path already handles this -- nvenc_base::set_bitrate() recomputes vbvBufferSize proportionally -- so mirror it here, with the same 100 kbit floor, and log the encoder-visible values (rate + VBV) so the log shows what FFmpeg's nvenc reconfig_encoder() is about to apply (it picks the changed fields up per frame via nvEncReconfigureEncoder with resetEncoder + forceIDR).
…ff at the DRM level Reported: sometimes the physical screen did not come back after a session. The display_off prep wrote sysfs `status = off` for every connected physical, a DRM level force that (a) survives a crash, because the process that would have restored it is gone, and (b) emits no hotplug on NVIDIA, so nothing recovers it. The prep now asks the compositor to switch the physical outputs off (which owns the layout and puts it back) and refuses to touch DRM: with no compositor output backend (niri/wlroots/X11 today) it logs that the host screen stays on instead. Because a forced-off connector is indistinguishable from an unplugged one, the list of switched-off outputs is persisted to <appdata>/vdd_offlined_physicals.txt, so a later process can restore them: - teardown and the start of a non-exclusive session both restore, - a fresh process restores as soon as it sees that no virtual display is live (the crashed-session case, without waiting for a stream), - restore uses sysfs "detect" (clear any leftover force from an older build and let the kernel re-detect) plus the compositor's enable, never "on", which would fabricate a phantom output for a panel that has since been unplugged. offlined_physical_connectors() now reports connector names, keeping the enumeration's "powered down for a VDD session" view intact.
SUNSHINE_ENABLE_CUDA=ON never compiled: the fork's sync commit 468442f ("feat(video): use unified color conversion matrix generator (#4387)") merged the new upstream file *after* the old one instead of replacing it, leaving two copies of every helper and member function with unbalanced braces (final brace depth -1). Nobody noticed because the local build always ran with CUDA off. cuda.cu is now the last coherent version (468442f^, which matches this tree's cuda.h: convert()/viewport/scale/threadsPerBlock) with the one-line change the upstream PR actually made -- color_vectors_from_colorspace(colorspace, true). Verified: the CUDA object compiles with CUDA 13.4 for sm_86 and the whole CUDA-enabled binary links. Building it also needs two CMake fixes: - an explicit -DCMAKE_CUDA_ARCHITECTURES is now honoured (one real GPU builds far faster than the whole compatibility ladder), - the ladder drops architectures the toolkit itself removed (CUDA 13 no longer accepts anything below Turing; passing sm_50 made nvcc abort with "Unsupported gpu architecture").
The Linux avcodec nvenc table overrode the YUV444 profile with AV_PROFILE_H264_HIGH_444_PREDICTIVE (244). That is FFmpeg's *generic* profile constant, but h264_nvenc's "profile" option takes NVENC's own enum (0-5), so opening the encoder failed with Value 244.000000 for parameter 'profile' out of range [0 - 5] which poisoned the 4:4:4 probe and forced the fallback path on every YUV444 session. Use nv::profile_h264_e::high_444p (3), the same enum the neighbouring SDR entry already uses. The generic constant stays correct where it belongs: ctx->profile, which FFmpeg maps internally. Also make the NvFBC session failure readable: it now logs the status and says the DRM/Wayland sources are used instead. The failure itself is expected on consumer GeForce drivers (the privateData trick only works on some driver versions), but it showed up as a bare "Failed to create session:" with an empty driver message.
…ng its CRTC A status-forced connector emits a DRM uevent and current KWin probes, enables and CRTCs it itself within ~1.5s. That activation is one-shot: racing it with the pidfd master-steal SETCRTC in force_crtc_assignment fails KWin's atomic commit and it drops the output permanently, so kscreen never lists the connector and the session dies on the mode set (unknown output) with every encoder probe failing afterwards. Boot-flaky: worked 9/20 and 9/23, failed 5/5 attempts on the 9/24 boot. - creation and live mode-change now wait for kscreen to surface the connector first and skip the steal entirely when it does - the steal demotes to a fallback for compositors that ignore status-forced connectors; when it lands but kscreen still cannot see the output, the connector is recycled (status off->on, EDID override survives) to hand the compositor a fresh activation - on an observable compositor, creation fails fast when the connector never surfaces instead of reporting a bogus 'live' - kscreen-doctor exits 0 on failures; display_off prep now parses 'applying config failed!' instead of trusting the exit code - AGENTS.md gotcha revised (no-hotplug claim disproven) + new exit-code-parsing gotcha; report entry 45 added
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.