Skip to content

Integrate Linux PR 1054 and add Cinnamon X11 clipboard support - #1

Draft
BigStrongSun wants to merge 263 commits into
masterfrom
integration/linux-pr1054-20261103
Draft

BigStrongSun wants to merge 263 commits into
masterfrom
integration/linux-pr1054-20261103

Conversation

@BigStrongSun

@BigStrongSun BigStrongSun commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

Merge AlkaidLab master 5b238c11 with Linux PR AlkaidLab#1054 (bb504351) and add Cinnamon X11 clipboard support. The sole merge conflict in diagnostics route registration preserves shared MAT/VDD status routes and Windows-only Vulkan HDR routes.

Linux now uses an isolated GTK helper for bidirectional UTF-8 text and PNG clipboard synchronization, with bounded IPC, selection echo suppression, failure detection and clean shutdown. Fixes also cover NVIDIA X11 display enumeration via RandR, CUDA C++17 compilation separate from shared C++23, Boost.UUID and MiniUPnP API 17 compatibility.

Validation:

  • Full Release build on Linux Mint 22.3/Cinnamon X11, GCC 13, CUDA 13/sm_89; Web UI built with Node 26.7.
  • Xvfb CTest integration passed: Unicode, invalid UTF-8 recovery, >1 MiB PNG, fragmented input, oversize rejection, no echo and helper shutdown.
  • Real Moonlight V+ 6.5.3: 720p30 H.264 NVENC/D3D11VA stream; bidirectional Unicode text and PNG pixel checks passed, including reconnect after isolated service restart.
  • Independent prefix, config, service and ports; original packaged Sunshine remained active with unchanged PID, binary checksum and config. No system libraries, drivers or original service replaced.
  • Windows parity audit: 30 Linux sources, no shared source leakage. Initial Windows CI stopped during MSYS2 metadata download, before compilation; no Windows build success claimed.

File clipboard offers, Wayland and HDR are outside this additional runtime validation. Historical Linux branch results in the migration documents remain identified separately. Deployment remains a test installation; this PR stays draft pending broader platform checks.

…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'.
Close the remaining gaps with the Windows VDD implementation:

- EDID generator gains options for physical size (mm) and CTA-861-H HDR
  static metadata luminance (max/full/min nits), plus extra detailed
  timings advertised after the preferred mode.
- create_vdd_monitor personalizes the EDID from the per-client report:
  HDR brightness (max/full/min nits) and the client size class from the
  host client registry (small/medium/large -> cm, same mapping as
  Windows). Mode switches and fallbacks keep the personalization.
- A fallback mode ladder (1920x1080@60, 1280x720@60) is advertised so
  the compositor always has sane modes beside the requested one.
…orm-aware wizard copy

- apply_vdd_prep routes the primary/secondary hints through a
  desktop-environment dispatch (XDG_CURRENT_DESKTOP): KDE keeps the
  kscreen priority hint, GNOME and unknown desktops degrade to the
  extended layout with an explanatory log - the DRM-level CRTC force
  itself remains universal.
- SetupWizard: the virtual display prerequisite text is now
  server-platform aware. Windows keeps the ZakoVDD wording; Linux/macOS
  explain the native EDID backend (package file capabilities + debugfs),
  and the option card description no longer claims a ZakoVDD driver is
  required. New locale keys added to en*/zh.
- confighttp: getVddStatus and its /api/vdd/status route were compiled
  out on non-Windows, so the setup wizard's display step failed with
  'Failed to fetch'. The status surface is cross-platform now (the Linux
  virtual display backend reports readiness); the Vulkan HDR bridge
  routes stay Windows-only.
- adapter_names (Linux): enumerate DRM cards by kernel driver
  (nvidia/amdgpu/i915/...) instead of returning an empty list, so the
  wizard's GPU step shows the actual hardware.
…ate string

- get_vdd_status (Linux) now fills the state string through
  classify_vdd_state like the Windows implementation - it previously
  serialized as empty, breaking vddReady in the web UI and rendering the
  raw 'config.vdd_driver_state_' key. Added the missing
  vdd_driver_state_ready locale key.
- adapter_names (Linux) returns kernel driver names, and both the KMS
  capture and the virtual display card picker now honor
  config::video.adapter_name: when set to a present driver (e.g.
  'nvidia') capture and virtual display creation are constrained to that
  GPU, aligning the wizard's GPU step with the Windows behavior.
…fork

- Packaging switches from /opt/sunshine back to the global prefix: the
  binary lands in /usr/bin, assets in /usr/share/sunshine (recompiled-in
  path), systemd user unit and setcap hook unchanged in effect.
- The app icon set now uses the fork's own logo PNGs (16/256) instead of
  the upstream vector, and the tray status icons install the fork's
  playing/pausing/locked PNGs into hicolor (the upstream SVGs restored
  earlier carried upstream's artwork).
- The desktop entry drops the upstream screenshot branding in appdata
  and fixes the 'systemctl start --u' typo (--user).
Implement the physical-display half of the display_device layer that the
Windows backend provides: mode/HDR/primary/topology reads and writes are
mediated by the compositor through kscreen-doctor, with a persistence file
and a simplified apply/revert flow mirroring settings.cpp. VDD topology
stays under the session's VDD stage; setups without kscreen-doctor keep
the previous no-op behavior. Fractional refresh rates are addressed by
mode id since kscreen-doctor rejects decimal refresh arguments.
Fill the role the Windows GUI agent's clipboard module plays: a Linux
host clipboard provider that bridges the desktop clipboard (KDE klipper
over sd-bus) and streaming clients through clipboard_bridge. Speaks the
same 10-byte text frame as the agent, echoes via a TTL guard, baselines
on session start, and skips oversized payloads that the agent would
send through the blob store. bridge_t gains additional inbound
listeners so the GUI SSE sink and the provider coexist; gui_alive()
counts listeners so the capability stays advertised without the GUI.
Build is optional on libsystemd presence.
QiE2035 and others added 29 commits October 22, 2026 21:25
A real Secret Service error (no service, locked collection) returned
read_status_e::error without consulting the environment, although the
header documents that variable as a read-only fallback on this platform.
Prefer it, and warn on stderr so a broken keyring is still visible.
analyze() walked every pixel of the full-resolution frame on the encoding
thread (8.3M maxRGB reconstructions for 4K) while hdr_analysis_max_width and
hdr_analysis_max_height went unused. Sample on a grid capped at those
dimensions and divide by the sampled count, so the statistics mean the same
thing they do on the CUDA path.
init_dsu_server() only short-circuited on success, so a port already in use
made every motion event create a socket, bind, fail and log again, and
log_dsu_uninitialized() was unreachable. Remember the attempt.
* test_wlr_randr's raw string opened with R"WLR( and closed with )", so the
  compiler swallowed two whole tests plus the assertions of a third and the
  suite still went green.
* test_display_device_enum asserted "inactive" even when the sysfs property
  could not be read, contradicting its own header.
* the credential round trip left its test key in the user's keyring when an
  assertion fired, which made every later run skip.
…note

The udev rule already grants uaccess to /dev/uinput and /dev/uhid, so the
install hooks only print the manual usermod line for SSH-started services
instead of running it. The AUR README, PKGBUILD and .SRCINFO also claimed
NVENC needs the cuda package: it does not -- _use_cuda only adds the CUDA
capture path and its HDR conversions.
§六 still listed KDE replication as unimplemented (kscreen-doctor does have
a mirror verb, and §1.4 records the measurement) and described the niri
backend as pending with only wlr-foreign-toplevel left to do. Both are
stale: five display backends are in, and what remains is two protocol-level
gaps nobody on the Linux side can close.
The DRM flip rule gained two tests, so the suite is 652 cases / 647 passing.
…pastes consistent

`stop()` wrote `running` outside `write_mu`, so a poll thread could pass the
locked check and then wait for a notification that had already been sent; the
same lock now guards the flag and the wait, and `wait_retry()` releases it while
it waits.

`out_transfer_t` recorded only an fd and an offset, while `pump_out_transfers()`
always read the current `source_bytes`. Replacing the selection mid-paste
therefore spliced the new payload into a transfer that belonged to the old one.
Each transfer now owns a copy of the bytes it is serving.
A failed `start()` left `dsu_server` empty, and `motion()` retries whenever it is
empty -- so a busy port or a permission problem meant a socket bind plus several
log lines on every motion event (hundreds per second). The attempt is now
recorded and retried at most every 30s, and `log_dsu_uninitialized()` is
reachable again. It is reset on teardown so a new session starts clean.
…ackend

`entry.path()` is `/dev/dri/cardN`, a character device node, so
`.../device/driver` never existed and the read always failed -- the GPU choice
made in the setup wizard had no effect on KMS capture. The driver is now read
from `/sys/class/drm/<card>/device`.

`adapter_name` may also hold a render node path, which the VAAPI path already
treats as one; a value starting with `/` is compared by PCI device instead of by
driver name, so such a config no longer excludes every card.
`secret_password_clear_sync` returns FALSE both for "nothing matched" and for a
locked collection, and it distinguishes them only sometimes. Erase is documented
as idempotent, so the ambiguity was resolved as success -- which could tell the
user a key was removed while it was still in the keyring. The entry is now read
back: only a confirmed absence counts as success.

The stored password is released with `secret_password_free()` rather than
`memset` + `g_free`, since only that call is guaranteed to scrub the value.
Four independent problems, all found by review:

* `XRROutputInfo::modes[]` holds mode *ids*, not indices. Reading them as indices
  either dropped every mode (the ids exceeded `nmode`) or silently reported
  another mode's timing. Modes are now looked up by `id`, the way xrandr does it.
* Mirror groups were built in a single pass, so a member sorting before its leader
  (`DP-1` mirroring `HDMI-A-1`) opened its own CRTC request first and the leader
  opened a second -- the mirror degraded into a side-by-side layout that may also
  have failed for want of a free CRTC. Groups are now formed by leader first.
* The CRTC fallback ignored each output's allowed list, so a group could be given a
  CRTC that one of its members cannot use and `XRRSetCrtcConfig` rejected the whole
  request. A candidate CRTC must now be legal for every output in the group.
* A successful rollback was reported as a successful apply. The two outcomes are
  now tracked separately: a refused plan returns failure with its reason, and the
  screen returns to the snapshot size rather than the planned one.

Also documents the four bugs and adds a test for the member-sorts-first case.
The §6.21 note blamed XWayland for "handing back mode handles as indices"; the
handles were ours to interpret, and the two additional RandR bugs are recorded
next to it. Baseline is now 653 cases / 648 pass / 5 skip.
`vdd_as_secondary` used to hand the primary role to *every* physical
connector in turn. Which one ends up primary is therefore decided by the
order the compositor processes them in, not by the layout the user had
before the virtual display appeared -- the same assumption upstream
0219abe removed on Windows ("枚举结果不保证首个设备就是原主屏"), where
the fix is to put the original primary device into the topology first.

Linux has no ordered topology to reorder: each backend carries the role as
a per-output flag. The equivalent rule is therefore to name at most one
promotion, and to make it the screen that held the role before the virtual
display was prepared:

* `src/platform/linux/vdd_primary_plan.h` (pure, 7 unit tests) -- only a
  known original primary is promoted; the remaining physicals are demoted
  explicitly; when the original primary is unknown (unplugged, renamed, or
  never observed) no physical is touched at all and only the virtual
  display is demoted, leaving the compositor's own choice in place.
* the original primary is sampled once per virtual display lifetime, and
  the remembered connector survives until the last virtual display is
  destroyed so the teardown can hand the role back to it. The sample is
  taken before `state_mutex` is taken, because the question goes to the
  compositor through the shared API and that path can re-enter vdd_utils
  (`is_vdd_connector()` -> `enum_available_devices()`), and it happens
  before offlined physicals are restored, so a forced-off screen is never
  mistaken for the user's.
…ckend

Four defects, each verified against the tree before changing anything:

* Session modes were written into the shared view. `set_vdd_session_mode()`
  and `wait_for_mode_publication()` described whichever display was touched
  last, so with two clients one client's request could rewrite the other
  client's connector and EDID. Both now take the requesting client's
  identifier; a new pure helper `vdd_instances::session_mode_target()`
  resolves it to the selected view, that client's own live record, or a
  pending request kept until its display is created. Creation adopts the
  pending request before building the EDID, and teardown drops it.
* `create_vdd_monitor()` failure paths left the shared fields describing a
  display that never came up, so the next `store_current_locked()` overwrote
  the previous client's record with it. A `view_restore_t` scope guard now
  puts the entering selection back on every failure exit.
* `find_master_holder()` leaked the `pidfd_getfd()` descriptor on its success
  path: it returned before `::close(dup_fd)`.
* `SUNSHINE_BUILD_DRM` gates libdrm/libcap in cmake, but the headers,
  `elevated_caps` and every `drmIoctl`/`drmMode*`/`cap_*` use site were
  unconditional, so a build without DRM would not compile.

Tests: five new `VddInstances` cases for the client-scoped session mode (14
in the suite), full `test_sunshine` 665 cases / 660 pass / 5 skip / 0
failures, `ctest` 23/24 with the known headless Encoder SetUpTestSuite.
`tools/audit-windows-parity.py` reports zero changed lines inside Windows-only
regions.
Adds section 6.31 covering the four fixes above, unifies the documented
upstream chain (ed41502 -> 7a0995e -> c211d71 -> 0219abe) instead of
naming two different bases in one header, and moves the test baseline to
665 cases / 660 pass in both the report and AGENTS.md.
* 6.2 still described display_device as two producers (kscreen, niri); it has
  five, and the backend-selection order is now a tested pure function.
* the ABR capability row still said the foreground producer covers KDE + niri;
  it covers KWin, wlroots toplevel, niri IPC and X11.
* 6.29's "the pen device is not included" read as "pen input is unsupported".
  It means the KWin output mapping deliberately leaves tablet devices alone
  (they have their own input class and `test_kwin_input_device.cpp` guards
  against mis-mapping them), which the capability row already describes.
pa_context_load_module, unload_module, get_server_info and
set_default_source return null while the context is not ready, and alarm_t
has no timeout: the unconditional wait() never returned, so start-up or
session teardown could hang forever. Every call site now checks the
operation, logs pa_strerror(pa_context_errno(...)) and returns the
function's failure value.
makepkg runs prepare() before it reads pkgver, and prepare() relinks the
tree, so the top-level version read named the package after the stamp that
existed before the relink. Move it into a pkgver() function.

Also drop the extra sha256sums entry the AUR package carried: 27 sums
against 26 sources makes makepkg refuse the build. .SRCINFO is regenerated
from the PKGBUILD and now matches byte for byte.
kms_display_names() keeps one conn_type_count_t for every card it visits,
so on a two-GPU machine the second card's DisplayPort is advertised as
DP-2, while display_t::init() restarted the counter for each card and could
not resolve that name back to a CRTC. A virtual display's bare connector
name could also match the same name on another card.

Replay the enumeration (same card order, same skip rules) to learn where
each card starts, pin the search to the card that hosts the virtual
display, and pick the base according to where the name came from: shared
numbering for monitor-list names, card-local for the virtual display's bare
name.
Every virtual display carries the same friendly name, so
find_device_by_friendlyname(ZAKO_NAME) can return another client's monitor.
The mode-publication checks then verify a device the session is not
streaming.

Add vdd_utils::owned_virtual_display_connector(owner), which locates the
record by the EDID serial the identity produces and returns its connector
only while it is still connected, and use it in prepare_vdd() and
apply_vdd_display_stage(). The Windows half is untouched: the driver
isolates displays by client GUID.
The libcap stand-in for SUNSHINE_BUILD_DRM=OFF builds is trivially
destructible, so every scope that declares one on purpose earned a
-Wunused-variable. Give it a user-provided destructor: the declarations
then count as used and the configuration compiles clean.

Verified with the file's own command from compile_commands.json, minus
-DSUNSHINE_BUILD_DRM: rc 0, 0 warnings (was 8).
…lays work

Forcing the connector on writes its `status` node, which only emits a
connector-level uevent. KWin 6.8 builds its monitor with
filterSubsystemDevType("drm", "drm_minor") and handleUdevEvent() drops every
sysname that is not a cardN primary node (drm_backend.cpp:174,190-191), so the
output read `connected` in sysfs while KScreen never listed it: every phone
"exclusive virtual display" launch ended on
"Output with name or uuid DP-2 not found" -> "Cannot set display mode for
unknown output" -> no capturable monitor -> 503.

- notify_compositor(card) writes `change` to /sys/class/drm/<card>/uevent,
  the same synthetic event `udevadm trigger --action=change` sends, and is
  called after the status write plus once more if the first poll misses.
- Creation and live mode changes now judge success by whether the compositor
  lists the output when it is observable; a stale CRTC no longer stands in for
  it and the failure is reported where the cause is visible instead of
  surfacing later as a bogus "live" log.
- connector_has_crtc() requires the connector to be connected first: nvidia
  keeps DP-2 "enabled" while disconnected, so its leftover encoder proved
  nothing.

The master-steal fallback stays for compositors that cannot be observed (niri,
GNOME). Documented in AGENTS.md (the old gotcha claimed the opposite) and
LINUX_MIGRATION_REPORT.md 6.33.
@BigStrongSun BigStrongSun changed the title merge: integrate Linux PR #1054 with current upstream master Integrate Linux PR 1054 and add Cinnamon X11 clipboard support Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants