https://bugs.kde.org/show_bug.cgi?id=522547

[email protected] changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |[email protected]

--- Comment #13 from [email protected] ---
Root cause found, with a deterministic reproducer (client side, KWindowSystem).

Environment: Plasma 6.7.5, KWin 6.7.5, KWindowSystem 6.30.0, Qt 6.11.2,
Wayland,
widget style Kvantum 1.1.8 (through qt6ct-style) with translucent, blurred
menus.

REPRODUCER (plasmashell dies every time)
1. Put the stock Global Menu widget in full view (not compact) on a panel.
2. Open KWrite and leave it on its Welcome page: its Edit menu then has only
hidden entries.
3. Click "File" in the Global Menu, move the pointer to "Edit" (the applet
shows its shared
   proxy menu with no visible entries, a 9x9 frame), click outside to close the
menu,
   click "File" again.
4. plasmashell is disconnected with "tried to set blur region on destroyed
surface".

WAYLAND_DEBUG=client of plasmashell (object ids from one run):

  -> ext_background_effect_surface_v1#236.destroy()
  -> ext_background_effect_manager_v1#43.get_background_effect(new id
ext_background_effect_surface_v1#250, wl_surface#228)
  -> ext_background_effect_surface_v1#250.set_blur_region(nil)
     ... the menu is closed, which destroys its QWindow's platform window:
  -> wl_surface#228.destroy()                 (#250 is never destroyed)
     ... the menu is shown again:
  -> ext_background_effect_surface_v1#250.set_blur_region(wl_region#240)
  wl_display#1.error(ext_background_effect_surface_v1#250, 0, "tried to set
blur region on destroyed surface")

The first three lines are one KWindowEffects::enableBlurBehind(window, false)
call. Kvantum
makes it when a menu has no blur region left (BlurHelper::update() -> clear(),
in
Kvantum/style/blur/blurhelper.cpp), which is the case for the empty 9x9 menu.
In
src/platforms/wayland/windoweffects.cpp:

    void WindowEffects::enableBlurBehind(QWindow *window, bool enable, const
QRegion &region)
    {
        if (enable) {
            ...
        } else {
            if (!m_blurRegions.remove(window)) {
                return;
            }
            resetBlur(window);      // destroys the old
ext_background_effect_surface_v1 (#236)
            releaseWindow(window);  // stops watching the window and its
surfaceDestroyed()
        }

        installBlur(window, enable, region);
    }

With the ext_background_effect manager, installBlur(window, false, ...) does
`auto &effect = m_backgroundEffects[window]; if (!effect) effect =
...get_background_effect(surface);`
and sets a NULL region: it creates a new object (#250) and keeps it in
m_backgroundEffects for a
window that is no longer tracked. When that window's surface is destroyed
(QWindow::close()
destroys the platform window: a menu closed by Escape, by an activated item or
by the
compositor), no surfaceDestroyed watcher resets it. The next
enableBlurBehind(window, true, ...)
tracks the window again, finds the stale object in m_backgroundEffects and
calls
set_blur_region on it, which is the fatal error.

This also explains the other triggers in this report: any code that switches
the blur of a
window off (Kvantum does it for menus without a blur region and, with "blur
only the active
window", for inactive windows) and then closes and reopens the same QWindow. No
pointer reuse
is needed for it.

Possible fix: do not create an object just to remove the blur. Destroying the
old object already
removes it ("The effect regions will be removed on the next commit"):

    --- a/src/platforms/wayland/windoweffects.cpp
    +++ b/src/platforms/wayland/windoweffects.cpp
    @@ void WindowEffects::installBlur(QWindow *window, bool enable, const
QRegion &region)
         if (m_backgroundEffectManager->isActive()) {
             replaceValue(m_blurs, window, nullptr);
    +        if (!enable) {
    +            m_backgroundEffects.erase(window);
    +            return;
    +        }
             if (!m_backgroundEffectManager->supportsBlur) {

This is also why the client-side attempt in comment 10 did not help: the object
is created for
the window's surface while that surface is still alive, and becomes stale only
when the surface
is destroyed, which nothing observes any more. The reopened window's new
surface can get the
same address, so comparing surface pointers cannot tell them apart. The KWin
change from
comment 11 (https://invent.kde.org/plasma/kwin/-/merge_requests/10004) stops
the disconnects, and the change above removes the stale object on the client
side, so the
reopened popup gets a new, working object and keeps its blur.

Workaround for code that owns the menus: hide popups instead of letting
QWindow::close()
destroy their platform window (ignore the QCloseEvent of the QMenu and hide()
it), so their
surface lives as long as the QWindow. The Application Title Bar with App Menu
widget
(https://github.com/uselessfire/application-title-bar-appmenu) does that and
survives the
reproducer above.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to