Two July fixes that were never merged: rpm FFmpeg modules + the released-pointer double cursor #71

Merged
enricobuehler merged 2 commits from worktree-july-rpm-and-cursor into main 2026-08-06 13:13:36 +00:00
Owner

Rescued from a July branch triage. Both fixes were verified absent from main today, and both cherry-pick clean onto it.

build(rpm): declare all seven FFmpeg pkg-config modules, not three

main declares only libavcodec, libavformat and libavutil. But ffmpeg-next is pulled with default features, so ffmpeg-sys-next's build script pkg-config-probes codec/device/filter/format/util/resampling/scaling and panics on the first one missing.

RPM Fusion's ffmpeg-devel ships the lot in one package, which hid the gap. On a box where these resolve to Fedora's split libav*-free-devel packages instead, dnf installs only the three named here and the build dies in build.rs on libavfilter.

Adds libavdevice, libavfilter, libswresample, libswscale. No code change.

fix(clients/cursor): the host must not composite a pointer under a released client's own cursor

Streaming a KDE desktop showed two cursors: the one the user was moving, and a second one underneath it that never moved. It was not KWin's — in cursor-as-metadata mode it never paints a pointer into the stream. It was ours.

Both clients declared the render model as captured && desktop, so any released pointer handed compositing back to the host. But releasing does not remove the local cursor — it restores the ordinary window arrow over the video. The host then blends its own pointer underneath, and since a released client forwards no motion, nothing drives it: it stays frozen wherever the host pointer was last left.

The host may composite only while the client holds a grabbed, hidden pointer — the capture model, engaged — which is the single state with no local cursor on screen. Released now counts as "the client draws it": the host stops compositing and keeps forwarding shape/state, so re-engaging is seamless.

One-line condition flip in each client (!captured || desktopMouse). The host side already supports it — the CursorRenderMode handler in native/control.rs and the live render split in native/stream.rs.

Verification

From the Mac:

  • cargo fmt --check -p pf-presenter — clean
  • cargo check -p pf-presenter — clean
  • cargo clippy -p pf-presenter --all-targets -- -D warnings — clean
  • swift build in clients/appleStreamView.swift compiles clean

Note the Swift build does not go green overall, for a reason that predates this branch and reproduces identically on main: the checked-in PunktfunkCore.xcframework header predates the current ABI, so PunktfunkConnection.swift fails on punktfunk_connection_report_phase / punktfunk_connection_end_reason. Unrelated to these commits.

Still owed: on-glass confirmation of the cursor fix against a KDE desktop session, and an actual Fedora/RPM build for the spec change.

Rescued from a July branch triage. Both fixes were verified absent from `main` today, and both cherry-pick clean onto it. ## build(rpm): declare all seven FFmpeg pkg-config modules, not three `main` declares only `libavcodec`, `libavformat` and `libavutil`. But `ffmpeg-next` is pulled with default features, so `ffmpeg-sys-next`'s build script pkg-config-probes codec/device/filter/format/util/resampling/scaling and panics on the first one missing. RPM Fusion's `ffmpeg-devel` ships the lot in one package, which hid the gap. On a box where these resolve to Fedora's split `libav*-free-devel` packages instead, dnf installs only the three named here and the build dies in `build.rs` on `libavfilter`. Adds `libavdevice`, `libavfilter`, `libswresample`, `libswscale`. No code change. ## fix(clients/cursor): the host must not composite a pointer under a released client's own cursor Streaming a KDE desktop showed two cursors: the one the user was moving, and a second one underneath it that never moved. It was not KWin's — in cursor-as-metadata mode it never paints a pointer into the stream. It was ours. Both clients declared the render model as `captured && desktop`, so **any** released pointer handed compositing back to the host. But releasing does not remove the local cursor — it restores the ordinary window arrow over the video. The host then blends its own pointer underneath, and since a released client forwards no motion, nothing drives it: it stays frozen wherever the host pointer was last left. The host may composite **only** while the client holds a grabbed, hidden pointer — the capture model, engaged — which is the single state with no local cursor on screen. Released now counts as "the client draws it": the host stops compositing and keeps forwarding shape/state, so re-engaging is seamless. One-line condition flip in each client (`!captured || desktopMouse`). The host side already supports it — the `CursorRenderMode` handler in `native/control.rs` and the live render split in `native/stream.rs`. ## Verification From the Mac: - `cargo fmt --check -p pf-presenter` — clean - `cargo check -p pf-presenter` — clean - `cargo clippy -p pf-presenter --all-targets -- -D warnings` — clean - `swift build` in `clients/apple` — `StreamView.swift` compiles clean Note the Swift build does not go green overall, for a reason that predates this branch and reproduces identically on `main`: the checked-in `PunktfunkCore.xcframework` header predates the current ABI, so `PunktfunkConnection.swift` fails on `punktfunk_connection_report_phase` / `punktfunk_connection_end_reason`. Unrelated to these commits. **Still owed:** on-glass confirmation of the cursor fix against a KDE desktop session, and an actual Fedora/RPM build for the spec change.
enricobuehler added 2 commits 2026-08-06 13:08:49 +00:00
`ffmpeg-next` is pulled with default features, so `ffmpeg-sys-next`'s build script
pkg-config-probes codec/device/filter/format/util/resampling/scaling and panics on
the first one missing. The spec named three.

RPM Fusion's `ffmpeg-devel` ships all seven in one package, which hid it. On a host
where those three instead resolve to Fedora's split `libav*-free-devel` packages,
`dnf builddep` installs exactly three and the build dies in a build script:

    The system library `libavfilter` required by crate `ffmpeg-sys-next` was not found.
fix(clients/cursor): the host must not composite a pointer under a released client's own cursor
apple / swift (pull_request) Successful in 1m26s
apple / screenshots (pull_request) Skipped
windows / build (aarch64-pc-windows-msvc) (pull_request) Failing after 1m9s
android / android (pull_request) Successful in 4m19s
windows / build (x86_64-pc-windows-msvc) (pull_request) Failing after 1m36s
ci / web (pull_request) Successful in 4m31s
ci / docs-site (pull_request) Successful in 4m43s
ci / rust-arm64 (pull_request) Successful in 6m50s
ci / rust (pull_request) Failing after 12m8s
2a67c02f7e
Streaming a KDE desktop showed two cursors: the one the user was moving, and a
second one sitting underneath it that never moved. It was not KDE's — KWin 6.7.3
in cursor-as-metadata mode calls `setRenderCursor(false)` on every recorded buffer
and hands the cursor item to an exclusive `ItemTreeView`, so `shouldRenderItem()`
skips it and no pointer is ever painted into that stream. It was ours.

Both clients declared the render model as `captured && desktop`, so ANY released
pointer handed compositing back to the host. But releasing does not remove the
local cursor — it restores the ordinary window arrow over the video. The host then
blends its own pointer in underneath, and since a released client forwards no
motion, nothing drives it: it stays frozen wherever the host pointer was last left.
Caught live on the host with the render-model diag:

    cursor diag: client_draws=false blended=true live=Some((-1, 622, true))

x = -1 — parked on the streamed output's left edge, unchanged sample after sample,
while the user moved their own cursor around freely. Engaging capture flipped it to
`client_draws=true blended=false` and the duplicate vanished, which is why it only
looked "stuck when not dragging": dragging means engaged, and engaged was the one
state that behaved.

The host may composite ONLY while the client holds a grabbed, hidden pointer — the
capture model, engaged — which is the single state with no local cursor on screen.
Released now counts as "the client draws it": the host stops compositing and keeps
forwarding shape/state over the channel (the forwarder ticks on this side of the
flip), so re-engaging is seamless and the client's cached shape stays warm.
enricobuehler merged commit c48e60fbb7 into main 2026-08-06 13:13:36 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#71