From 43c54a9d6985afdc513f8ddb73d353fc373e047c Mon Sep 17 00:00:00 2001 From: nico4348 Date: Mon, 20 Apr 2026 09:40:19 -0500 Subject: [PATCH 1/2] Fix cursor color swap from wrong XCursor pixel format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xcursor crate's `Image::pixels_rgba` field is misleadingly named: bytes come straight from the XCursor file, which stores pixels as uint32 ARGB little-endian — i.e. [B, G, R, A] in memory. The matching DRM fourcc for that byte order is Argb8888 (smithay maps it to GL BGRA_EXT), not Abgr8888 (which maps to GL RGBA and causes R/B to swap on screen). niri uses the correct pairing at src/cursor.rs:255-257. Adopt the same. --- src/state/cursor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/cursor.rs b/src/state/cursor.rs index 7896c34..b6e21ac 100644 --- a/src/state/cursor.rs +++ b/src/state/cursor.rs @@ -82,7 +82,7 @@ impl CursorState { } let buffer = MemoryRenderBuffer::from_slice( &img.pixels_rgba, - Fourcc::Abgr8888, + Fourcc::Argb8888, (img.width as i32, img.height as i32), 1, Transform::Normal, From fcb5848ce1ffecc3519aa574ac409c8715b8ae77 Mon Sep 17 00:00:00 2001 From: nico4348 Date: Mon, 20 Apr 2026 09:41:01 -0500 Subject: [PATCH 2/2] Document xcursor pixels_rgba byte-order gotcha The field name suggests RGBA but the bytes are in [B, G, R, A] memory order (XCursor file layout). Record the trap so future contributors don't reintroduce the Fourcc mismatch that caused cursor R/B swap. --- docs/CAVEATS.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/CAVEATS.md b/docs/CAVEATS.md index fdfbfce..bc697ca 100644 --- a/docs/CAVEATS.md +++ b/docs/CAVEATS.md @@ -38,6 +38,14 @@ This is a deliberate semantic misuse of the protocol. The debt it incurs: cosmic-comp makes the exact same bet (`clip_floating_windows` default-on in `AppearanceConfig`, `src/shell/element/window.rs:204`) and has carried the same complexity for years. This is a settled hack in Wayland-land, not a novel misstep — but it's still a hack. If a future protocol extension exposes "suppress client chrome" as a first-class signal, migrate to it and delete all of the above. +## xcursor `pixels_rgba` is actually BGRA + +The `xcursor` crate's `Image::pixels_rgba` field is misleadingly named. The bytes come straight from the XCursor file, which stores pixels as `uint32` ARGB little-endian — i.e. `[B, G, R, A]` in memory. Interpreted as RGBA, the channels are wrong. + +The matching DRM fourcc for that byte order is `Fourcc::Argb8888` (which smithay maps to GL `BGRA_EXT`), **not** `Fourcc::Abgr8888`. Using `Abgr8888` swaps R and B on screen — a yellow cursor renders mint-blue, a red cursor renders violet, etc. + +niri gets this right in `src/cursor.rs` (`MemoryRenderBuffer::from_slice(&frame.pixels_rgba, Fourcc::Argb8888, ...)`). Do the same here. + ## What to unit test Smithay glue code (handlers, delegates) is not worth testing — it's framework boilerplate. Write tests for **your** logic: