Conversation
pcmid
force-pushed
the
screencopy-private-output-listener
branch
from
September 7, 2026 16:06
8c08464 to
fbff2e4
Compare
…'s wl_output sharing Qt's listener OutputTransformQuery bound a second wl_output for the captured screen through the generated QtWayland::wl_output class, so the proxy carried Qt's own m_wl_output_listener. Compositors send wl_surface.enter for every wl_output resource a client holds (smithay and wlroots both do), and QWaylandScreen::fromWlOutput() decides whether an output is one of its screens purely by that listener. It therefore accepted our proxy, static_cast the OutputTransformQuery to a QWaylandScreen and appended the result to QWaylandSurface::m_screens. A released proxy never gets a leave, so the entry stayed behind after the context was deleted and was dereferenced on the next screensChanged, taking the whole shell down. Bind the proxy by hand with a private wl_output_listener instead, which fromWlOutput() rejects. Reading QWaylandScreen::mTransform through the existing reflector is not an alternative: Qt resets it to -1 in updateOutputProperties(), and QScreen::orientation() drops the flipped transforms. Fixes quickshell-mirror#1094, and by all appearances quickshell-mirror#876 and quickshell-mirror#193.
pcmid
force-pushed
the
screencopy-private-output-listener
branch
from
September 8, 2026 14:27
fbff2e4 to
851ab0e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
WlrScreencopyContext::OutputTransformQuerybinds a secondwl_outputfor the captured screen to learn its transform, and did so through Qt's generatedQtWayland::wl_output, i.e. with Qt's ownm_wl_output_listeneron the proxy. Compositors sendwl_surface.enterfor everywl_outputresource a client holds (smithay and wlroots both do), andQWaylandScreen::fromWlOutput()decides whether an output is one of its screens purely by comparing that listener. So it accepted our proxy,static_casttheOutputTransformQueryto aQWaylandScreenand appended the result toQWaylandSurface::m_screens. Arelease()d proxy never receivesleave, so the entry outlived the context and was dereferenced on the nextscreensChanged—QPlatformScreen::screen()on freed memory, whole shell down.The fix
Keep the extra bind, but do it by hand (
wl_registry_bind+ a privatewl_output_listener), sofromObject()rejects the proxy andsurface_enter()ignores it. No behaviour change otherwise:transformis still read fromwl_output.geometry,updateTransform()/submitFrame()are untouched.Reading
QWaylandScreen::mTransformthrough the existing reflector instead (no second bind at all) was tried first and does not work: Qt resets it to-1inupdateOutputProperties(), andQScreen::orientation()drops the flipped transforms. That variant produced an unrotated capture on atransform 90output.Verification (niri 26.04, Qt 6.11.2)
WAYLAND_DEBUGstill shows the second bind and the duplicateenter— now harmless.dms screenshotof the same moment (RMSE 0.013 at 480px).transform 90: 2160×3840, upright, matches the reference (the mTransform variant did not).masterand on v0.3.1;clang-formatclean.Disclosure
To be upfront: I am not really familiar with Qt or C++, and this code was written entirely by an AI assistant working with me — the analysis in #1094, the reproducer, the patch and the verification all came out of that session. If anything here is wrong, unidiomatic, or simply not how you would want it done, please say so; I am happy to rework it however you prefer. What I can vouch for is that it fixes the crash on my machine.
I am aware this is not a great fix. It keeps the original "cursed hack" intact — a second
wl_outputbind per capture, and a reflector subclass poking atQWaylandScreen's protectedm_outputIdto know which global to bind — and only changes whose listener the proxy carries. It removes the crash without removing the reason the hack exists: QtWayland does not expose the raw output transform anywhere (mTransformis transient,QScreen::orientation()drops flips). If there is a better way to get at the transform without a second bind or private Qt state, I would much rather do that — please say so and I will rework it.Fix #1094