drm: fix two review-suggested changes that were wrong, and stop overclaiming in the docs

an adversarial sweep over the whole batch, aimed at the failure that
kept recurring here (a hazard identified and only some instances
fixed), found that two changes made on review advice were themselves
defects. both are reverted with the trace written down so they do not
get "fixed" again:

- the hotplug renumbering probe reads the pushed list at the CLIENT
  index again, not the service one. `bound_to` is an IDENTITY,
  (device, crtc_id), so comparing it against a slot is not a
  cross-index-space comparison; and `swap_available_displays` installs
  that same list as DRM_STATE two lines later, which IS the client
  space - display_service re-advertises it, input is mapped through
  it, the next rebuild reads `expected` out of it. Probing the service
  index answered a question nothing downstream consumes and went quiet
  in exactly the case the guard exists for: a stream whose wire_idx
  differs from its client index kept running while that index came to
  mean another monitor, so the client rendered monitor A believing it
  was monitor B and routed every click accordingly.
- the pipewire-fallback guard compares raw sizes again. BOTH sides are
  physical: `Display::width()` on the wayland variant returns
  `physical_width()`, and `try_fix_logical_size` only repairs the
  capturable's separate logical_size field. Scaling the drm side
  therefore compared logical against physical and rejected the valid
  stream on precisely the scaled outputs it was meant to rescue. The
  single-display carve-out now needs BOTH sides to be single, since a
  monitor on a card the service cannot open is missing from the drm
  list while the compositor still drives it.

also from the sweep:

- a capture build whose index is out of range of the advertised list
  now fails instead of falling back to the raw index, which the wake
  can have grown the service list back past - that bound a second
  video service to a monitor already being served and recorded its
  health under the wrong identity.
- the security doc no longer claims the privileged process never loads
  GL. That is true of the DEFAULT path and measured there, but the CPU
  fallback converts in-process, and a tiled scanout can only be
  decoded through the GPU, so libdrmtap dlopens libEGL in the calling
  process when the frame needs it. The doc now says which property
  belongs to the path and which to the process, and bounds the cases
  instead of overclaiming.
- the wake latch is described honestly: it self-clears when the
  display is next driven by anything, but nothing retries it, so a
  transient failure can leave it latched on an unattended host.
- the wake's uinput device DECLARES two axes and BTN_LEFT (libinput
  ignores a device that does not look like a mouse) while EMITTING
  only the net-zero axis round trip. the doc said one axis and no
  keys, describing the emit as if it were the declaration.
- the drm CI never ran for a change to the root Cargo.toml, where the
  top-level `drm` feature is defined, or to Cargo.lock, which every
  `--locked` build here resolves against. both triggers list them now.
- the deb assertion checks the packaged BINARY carries the libdrmtap
  dlopen path, not just that the library was staged beside it.
This commit is contained in:
Mariano Abad
2026-07-30 15:28:48 -03:00
parent 0035c89763
commit 76414c46a3
4 changed files with 126 additions and 50 deletions

View File

@@ -28,6 +28,12 @@ on:
- "libs/scrap/src/common/drmtap_dl.rs"
- "libs/scrap/src/common/mod.rs"
- "libs/scrap/Cargo.toml"
# The ROOT manifest is where the top-level `drm` feature is DEFINED, and the lock file is
# what every cargo invocation here resolves against (`--locked`). Without these two, a PR
# that changes what `drm` pulls in, or bumps a dependency the drm path uses, skips the
# entire verification this workflow exists for.
- "Cargo.toml"
- "Cargo.lock"
- "src/ipc.rs"
- "src/ipc/**"
- "src/server/drm_capturer.rs"
@@ -52,6 +58,12 @@ on:
- "libs/scrap/src/common/drmtap_dl.rs"
- "libs/scrap/src/common/mod.rs"
- "libs/scrap/Cargo.toml"
# The ROOT manifest is where the top-level `drm` feature is DEFINED, and the lock file is
# what every cargo invocation here resolves against (`--locked`). Without these two, a PR
# that changes what `drm` pulls in, or bumps a dependency the drm path uses, skips the
# entire verification this workflow exists for.
- "Cargo.toml"
- "Cargo.lock"
- "src/ipc.rs"
- "src/ipc/**"
- "src/server/drm_capturer.rs"
@@ -336,6 +348,16 @@ jobs:
echo "::notice::built $deb ($(stat -c %s "$deb") bytes)"
dpkg -c "$deb" | grep -E 'usr/lib/rustdesk/libdrmtap\.so\.0\.[0-9]+\.[0-9]+$'
dpkg -c "$deb" | grep -E 'usr/lib/rustdesk/libdrmtap\.so\.0 ->'
# The library alone does not make this a drm build: build.py stages it whenever --drm is
# passed, independently of what was compiled, and the deb name is what tells a user this
# is the consent-bypass variant. Assert the BINARY too, by the absolute dlopen path that
# only exists when the feature is compiled in -- otherwise a stock binary could ship
# under the unattended-wayland name with a library it can never reach.
rm -rf /tmp/debassert && dpkg-deb -R "$deb" /tmp/debassert
if ! grep -qF /usr/lib/rustdesk/libdrmtap.so.0 /tmp/debassert/usr/share/rustdesk/lib/librustdesk.so; then
echo "::error::the packaged librustdesk.so has no libdrmtap dlopen path; this is not a drm build"
exit 1
fi
mv "$deb" "${deb%.deb}-x86_64.deb"
# MEASURE the glibc floor rather than describing it. This job builds on the runner instead of the