Skip to content

fix: declare the cursor hotspot cap, or a virtualized driver hides its cursor plane (0.5.4) - #50

Merged
fxd0h merged 3 commits into
mainfrom
fix/virtio-cursor-plane-hotspot
Aug 8, 2026
Merged

fix: declare the cursor hotspot cap, or a virtualized driver hides its cursor plane (0.5.4)#50
fxd0h merged 3 commits into
mainfrom
fix/virtio-cursor-plane-hotspot

Conversation

@fxd0h

@fxd0h fxd0h commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Closes #49.

A para-virtualized driver (virtio-gpu, vmwgfx, qxl, vboxvideo) hides its cursor plane from a client
that enabled DRM_CLIENT_CAP_ATOMIC and did not also enable DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT.
We enable the atomic cap to read a connector's CRTC_ID, so since then the cursor read has found no
plane on those drivers and reported the cursor hidden on every call. The full measurement chain is in
the issue.

The fix is one drmSetClientCap after the atomic one, with an #ifndef so a build against
pre-6.6 headers still declares it. Cursor reads already honor HOTSPOT_X / HOTSPOT_Y, which is
exactly what the cap asserts.

Also in here, all of it documentation the bug proved wrong:

1- the README known-limitation entry explained that only virtualized drivers export the hotspot
properties, without saying what asking for them costs.
2- the drmtap_get_cursor header claimed -ENOENT when there is no cursor plane. It never returned
that: a hidden cursor clears the plane binding, and an error there would make a consumer keep
painting a stale cursor, so it answers success with visible = 0. Same class as the frame->data
header bug in #36.
3- docs/research/02 listed the universal-planes cap as all a cursor read needs.
4- tools/drmtap-report.sh now reports the plane count and how many are bound, so the next cursor
report from a tester answers this without a round trip.

Verified: 11/11 tests, check-version.sh coherent at 0.5.4, soname still libdrmtap.so.0, and on a
real virtio-gpu VM the remote cursor is drawn again where 0.5.2 showed none.

fxd0h added 2 commits August 8, 2026 15:15
fufesou reported "the cursor does not show" on three VMs, at a greeter and in a
session alike, while two physical machines were fine. Reproduced on virtio-gpu and
measured end to end.

`drmtap_open` asks for `DRM_CLIENT_CAP_ATOMIC` to read a connector's `CRTC_ID`.
Since kernel 6.6 a para-virtualized driver (virtio-gpu, vmwgfx, qxl, vboxvideo)
answers that cap by hiding its cursor plane from the client, unless the client also
sets `DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT` to say it honors the hotspot properties.
`find_cursor_plane` therefore found nothing, `drmtap_get_cursor` reported the cursor
hidden on every call, and the consumer hid it for the whole session.

Measured on virtio-gpu with a standalone probe: `drmModeGetPlaneResources` returns
two planes with no atomic cap, one with it, two again with this one. In the live
service: 811 of 811 cursor reads found no plane before, and with the cap the remote
cursor is drawn again -- the client shows the host's arrow where it showed nothing.
On i915 the cap is refused with `EOPNOTSUPP`, as documented, and the plane count is
twenty-four either way.

Cursor reads already read `HOTSPOT_X` / `HOTSPOT_Y`, which is what the cap asserts,
so there is nothing else to honor. The helper binary never had the bug: it does not
ask for the atomic cap, so only an in-process privileged caller was affected.
Version stamp, changelog, and the documentation the bug proved wrong.

- README known-limitations already explained that only virtualized drivers export
  the cursor hotspot properties; it now says what those properties cost, which is
  that the driver hides the whole plane from an atomic client that has not declared
  it honors them.
- The `drmtap_get_cursor` header said it returns `-ENOENT` when there is no cursor
  plane. It never did: a hidden cursor clears the plane binding, and reporting that
  as an error would make a consumer keep painting a stale cursor, so it answers
  success with `visible = 0`. Same class as the `frame->data` header bug in #36.
- docs/research/02 listed the universal-planes cap as all a cursor read needs.
- `drmtap-report.sh` reports the plane count and how many are bound, so the next
  cursor report from a tester answers this without a round trip.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fxd0h, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7b784acb-1d0c-4e98-a115-af3f1293a808

📥 Commits

Reviewing files that changed from the base of the PR and between 798d469 and b0db107.

📒 Files selected for processing (3)
  • docs/research/02_drm_kms_mechanism.md
  • tests/test_capture.c
  • tools/drmtap-report.sh
📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved cursor-plane visibility on affected virtual GPU drivers when atomic modesetting is enabled.
    • Cursor queries now report unbound planes as hidden instead of returning an error.
    • Compatibility with drivers that do not support cursor hotspot capabilities is preserved.
  • Diagnostics

    • Added reporting of available and CRTC-bound cursor planes per graphics device.
  • Documentation

    • Documented cursor-plane requirements and troubleshooting guidance.
  • Release

    • Updated the library and Rust package version to 0.5.4.

Walkthrough

libdrmtap 0.5.4 enables DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT after atomic modesetting. It adds compatibility handling, updates cursor API documentation and version metadata, and adds cursor-plane diagnostics and documentation.

Changes

Cursor plane support

Layer / File(s) Summary
DRM capability initialization
src/drmtap.c, bindings/rust/libdrmtap-sys/csrc/drmtap.c
Both C implementations request cursor-plane hotspot support after atomic modesetting and log unsupported-driver responses. Older DRM headers receive a compatibility definition.
Public contract and release metadata
include/drmtap.h, bindings/rust/libdrmtap-sys/csrc/drmtap.h, meson.build, bindings/rust/*/Cargo.toml
Version metadata advances to 0.5.4. drmtap_get_cursor documents hidden unbound cursor planes as successful results with visible = 0.
Diagnostics and behavior documentation
README.md, docs/research/02_drm_kms_mechanism.md, tools/drmtap-report.sh, CHANGELOG.md
Documentation describes virtual-driver capability requirements and harmless EOPNOTSUPP responses. The report tool counts total and CRTC-bound DRM planes.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant libdrmtap
  participant DRMDriver
  Client->>libdrmtap: Open DRM context
  libdrmtap->>DRMDriver: Enable atomic capability
  libdrmtap->>DRMDriver: Enable cursor-plane hotspot capability
  DRMDriver-->>libdrmtap: Accept or return EOPNOTSUPP
  libdrmtap-->>Client: Continue context initialization
Loading

Possibly related PRs

  • fxd0h/libdrmtap#29: Adds the atomic DRM client capability that this change complements with cursor-plane hotspot support.

Poem

A rabbit found a cursor plane,
Hidden deep in virtual rain.
“Request the hotspot,” said the hare,
And planes appeared in proper pair.
Version four brings sight anew.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the cursor hotspot capability fix and its effect on virtualized drivers.
Description check ✅ Passed The description directly explains the cursor-plane issue, implementation, documentation updates, diagnostics, and verification.
Linked Issues check ✅ Passed The changes satisfy issue #49 by enabling the hotspot capability, preserving bare-metal behavior, and correcting cursor documentation and diagnostics.
Out of Scope Changes check ✅ Passed The version updates, documentation changes, and diagnostics support the cursor-plane fix and release objective without unrelated code changes.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/virtio-cursor-plane-hotspot

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/research/02_drm_kms_mechanism.md`:
- Around line 166-173: Update the cursor-plane paragraph in
02_drm_kms_mechanism.md to include the observation date and turn
DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT into a link to the kernel UAPI documentation
or another upstream source. Preserve any existing file-wide date while ensuring
this research observation has an explicit date and source.

In `@include/drmtap.h`:
- Around line 490-496: Update the cursor regression test in test_capture.c
around drmtap_get_cursor to reject -ENOENT and require a successful return; when
no cursor plane is bound, assert the documented hidden-cursor result with
visible = 0 instead of skipping the test.

In `@tools/drmtap-report.sh`:
- Around line 82-83: Update the bound-plane count in the report logic near the
planes and bound assignments to parse each plane’s crtc= field and exclude only
crtc=(null). Remove the name-prefix matching for “crtc” or “pipe” so
non-prefixed CRTC names are counted correctly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6da13786-73a9-4288-8d8d-05551b8ffe2d

📥 Commits

Reviewing files that changed from the base of the PR and between d32e447 and 798d469.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • README.md
  • bindings/rust/libdrmtap-sys/Cargo.toml
  • bindings/rust/libdrmtap-sys/csrc/drmtap.c
  • bindings/rust/libdrmtap-sys/csrc/drmtap.h
  • bindings/rust/libdrmtap/Cargo.toml
  • docs/research/02_drm_kms_mechanism.md
  • include/drmtap.h
  • meson.build
  • src/drmtap.c
  • tools/drmtap-report.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Static Analysis
  • GitHub Check: Build & Test (Ubuntu 22.04)
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (6)
meson.build

📄 CodeRabbit inference engine (AGENTS.md)

The project must compile with Meson using -Wall -Wextra -Werror and produce zero warnings.

Files:

  • meson.build
**/*

📄 CodeRabbit inference engine (AGENTS.md)

Keep the version synchronized across the C library, Meson, libdrmtap-sys, and libdrmtap; the canonical version is in include/drmtap.h, verified by tools/check-version.sh.

Files:

  • meson.build
  • bindings/rust/libdrmtap-sys/Cargo.toml
  • bindings/rust/libdrmtap-sys/csrc/drmtap.h
  • README.md
  • CHANGELOG.md
  • tools/drmtap-report.sh
  • src/drmtap.c
  • bindings/rust/libdrmtap/Cargo.toml
  • bindings/rust/libdrmtap-sys/csrc/drmtap.c
  • include/drmtap.h
  • docs/research/02_drm_kms_mechanism.md
**/*.{c,h}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{c,h}: Use C11 style: 4-space indentation with no tabs, snake_case names, same-line braces (1TBS), and // or /* */ comments as appropriate.
Keep lines within a 100-character soft limit and 120-character hard limit.
Every C source and header file must begin with the specified libdrmtap copyright, license, @file, and @brief header block.

Files:

  • bindings/rust/libdrmtap-sys/csrc/drmtap.h
  • src/drmtap.c
  • bindings/rust/libdrmtap-sys/csrc/drmtap.c
  • include/drmtap.h
src/**/*.c

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.c: Prefix public C API functions with drmtap_; use snake_case for variables, UPPER_SNAKE_CASE with DRMTAP_ prefixes for macros/constants, and appropriate internal/public type naming.
Organize implementation files as grouped includes, private types/constants, static functions, then public functions in header declaration order.
Return negative errno values on failure, never abort, set human-readable context errors with set_error, and clean up resources on all error paths using a goto cleanup pattern.
Always check allocation results and release allocated resources during cleanup.

Files:

  • src/drmtap.c
include/drmtap.h

📄 CodeRabbit inference engine (AGENTS.md)

include/drmtap.h: Use DRMTAP_MODULE_H-style header guards and document every public API function with Doxygen comments including parameters and return values.
Update public API comments whenever the public API changes.

Files:

  • include/drmtap.h
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Write documentation in English; research documents must include dates and sources, and DRM API references must link to kernel documentation or upstream sources.

Files:

  • docs/research/02_drm_kms_mechanism.md
🪛 LanguageTool
CHANGELOG.md

[style] ~17-~17: Consider using “who” when you are referring to a person instead of an object.
Context: ...or hidden on every call, and a consumer that trusts that answer showed no cursor any...

(THAT_WHO)


[style] ~21-~21: Consider an alternative for the overused word “exactly”.
Context: ...nor HOTSPOT_X / HOTSPOT_Y, which is exactly what the new cap asserts, so nothing el...

(EXACTLY_PRECISELY)

🔇 Additional comments (12)
bindings/rust/libdrmtap-sys/csrc/drmtap.h (1)

34-34: LGTM!

Also applies to: 490-496

include/drmtap.h (1)

34-34: LGTM!

meson.build (1)

4-4: LGTM!

bindings/rust/libdrmtap-sys/Cargo.toml (1)

3-3: LGTM!

bindings/rust/libdrmtap/Cargo.toml (1)

3-3: LGTM!

Also applies to: 16-16

bindings/rust/libdrmtap-sys/csrc/drmtap.c (1)

35-39: LGTM!

src/drmtap.c (2)

35-39: LGTM!


373-383: 🎯 Functional Correctness

Use kernel-aware wording for capability rejection.

The compatibility fallback allows builds with pre-6.6 headers, but a pre-6.6 kernel can also reject capability 6. Both implementations currently treat every refusal as evidence of a non-virtualized driver. The upstream UAPI guarantees this capability for atomic-capable virtualized drivers only starting with kernel 6.6. (raw.githubusercontent.com)

  • src/drmtap.c#L373-L383: change the comment and log to say “unsupported by driver or kernel,” unless the supported kernel floor is confirmed to be 6.6 or newer.
  • bindings/rust/libdrmtap-sys/csrc/drmtap.c#L373-L383: apply the same kernel-aware wording.

Confirm the project's supported kernel floor before merge.

Source: MCP tools

README.md (1)

378-378: LGTM!

tools/drmtap-report.sh (2)

75-81: LGTM!

Also applies to: 84-86


82-83: 🩺 Stability & Availability

No change needed.

grep -c still returns 0 as the count when grep -uo pipefail is active and the command is not used as the final command in a pipeline. The concern only applies with set -e.

			> Likely an incorrect or invalid review comment.
CHANGELOG.md (1)

8-28: LGTM!

Also applies to: 660-660

Comment thread docs/research/02_drm_kms_mechanism.md Outdated
Comment thread include/drmtap.h
Comment thread tools/drmtap-report.sh Outdated
1- the cursor test accepted `-ENOENT` and skipped, so it would have passed against a
   return to the contract this PR corrects. It now asserts that value never comes back.
2- the research note had no date and no upstream source: it quotes the cap's kerneldoc
   in `include/uapi/drm/drm.h` and dates both measurements, on virtio-gpu and on i915.
3- the report counted a bound plane by matching CRTC names we happen to have seen
   (`crtc-`, `pipe `). Anything other than `(null)` is bound.
@fxd0h
fxd0h merged commit ad9c70d into main Aug 8, 2026
10 checks passed
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.

no cursor on virtio-gpu, vmwgfx, qxl and vboxvideo: the cursor plane is hidden from an atomic client

1 participant