From: PaganDE Subject: [PATCH] gles2: fix R/B swap when reading DRM_FORMAT_BGR888 GLES2 has no GL_BGR, so wlroots maps DRM_FORMAT_BGR888 to glReadPixels(GL_RGB, GL_UNSIGNED_BYTE). GL_RGB returns bytes in R,G,B order while the BGR888 layout expects B,G,R - the resulting buffer ends up with red and blue swapped. On llvmpipe the implementation read format is GL_RGB, so wlr_output_preferred_read_format() reports BGR888 and screencopy clients receive a buffer with inverted channels. Origin: PaganDE fork of wlroots 0.20.2 --- render/gles2/texture.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) --- a/render/gles2/texture.c 2026-07-07 23:46:09.000000000 +0200 +++ b/render/gles2/texture.c 2026-08-31 14:04:58.836879583 +0200 @@ -225,6 +225,25 @@ } } + /* + * DRM_FORMAT_BGR888 has no GLES2 equivalent: we read it as GL_RGB, + * which returns bytes in R,G,B order, while the BGR888 layout + * expects B,G,R. Swap the channels so the buffer actually contains + * BGR data. Otherwise screencopy/screenshot clients see red and + * blue swapped (visible on llvmpipe, where the preferred read + * format is BGR888). + */ + if (drm_fmt->drm_format == DRM_FORMAT_BGR888) { + for (int32_t i = 0; i < src.height; ++i) { + unsigned char *row = p + (size_t)i * options->stride; + for (int32_t x = 0; x < src.width; ++x) { + unsigned char tmp = row[x * 3]; + row[x * 3] = row[x * 3 + 2]; + row[x * 3 + 2] = tmp; + } + } + } + wlr_egl_restore_context(&prev_ctx); pop_gles2_debug(texture->renderer);