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.
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.
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.
`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.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Rescued from a July branch triage. Both fixes were verified absent from
maintoday, and both cherry-pick clean onto it.build(rpm): declare all seven FFmpeg pkg-config modules, not three
maindeclares onlylibavcodec,libavformatandlibavutil. Butffmpeg-nextis pulled with default features, soffmpeg-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-develships the lot in one package, which hid the gap. On a box where these resolve to Fedora's splitlibav*-free-develpackages instead, dnf installs only the three named here and the build dies inbuild.rsonlibavfilter.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 — theCursorRenderModehandler innative/control.rsand the live render split innative/stream.rs.Verification
From the Mac:
cargo fmt --check -p pf-presenter— cleancargo check -p pf-presenter— cleancargo clippy -p pf-presenter --all-targets -- -D warnings— cleanswift buildinclients/apple—StreamView.swiftcompiles cleanNote the Swift build does not go green overall, for a reason that predates this branch and reproduces identically on
main: the checked-inPunktfunkCore.xcframeworkheader predates the current ABI, soPunktfunkConnection.swiftfails onpunktfunk_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.
`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.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.