From: PT Linux Launcher maintainers Subject: Keep client-side decoration clicks out of nested applications Bug: https://github.com/ValveSoftware/gamescope/issues/2299 Forwarded: https://github.com/ValveSoftware/gamescope/issues/2299 On compositors without xdg-decoration support, libdecor creates client-side surfaces owned by the Gamescope Wayland connection. Pointer button callbacks for those titlebar and resize surfaces consequently also reach Gamescope's input thread. Forwarding a decoration press to the nested application can leave its button permanently held because libdecor consumes the matching release while performing a move, resize, or maximize operation. Only begin nested button sequences while the pointer is over Gamescope's actual toplevel surface. Track those presses so their releases are delivered even if the pointer or keyboard focus changes during a game-originated drag. --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -562,6 +562,7 @@ std::atomic> m_pRelativePointer = nullptr; std::unordered_set m_uScancodesHeld; + std::unordered_set m_uMouseButtonsHeld; void Wayland_Registry_Global( wl_registry *pRegistry, uint32_t uName, const char *pInterface, uint32_t uVersion ); static const wl_registry_listener s_RegistryListener; @@ -3091,13 +3092,33 @@ } void CWaylandInputThread::Wayland_Pointer_Button( wl_pointer *pPointer, uint32_t uSerial, uint32_t uTime, uint32_t uButton, uint32_t uState ) { - // Don't do any motion/movement stuff if we don't have kb focus - if ( !cv_wayland_mouse_warp_without_keyboard_focus && !m_bKeyboardEntered ) - return; + const bool bPressed = uState == WL_POINTER_BUTTON_STATE_PRESSED; + const bool bWasPressed = m_uMouseButtonsHeld.contains( uButton ); + + if ( bPressed ) + { + // libdecor's client-side decorations share our Wayland seat, but + // their button events must not be sent to the nested application. + if ( !m_bMouseEntered || bWasPressed ) + return; + + // Don't start an application click without keyboard focus. + if ( !cv_wayland_mouse_warp_without_keyboard_focus && !m_bKeyboardEntered ) + return; + } + else if ( !bWasPressed ) + { + return; + } wlserver_lock(); - wlserver_mousebutton( uButton, uState == WL_POINTER_BUTTON_STATE_PRESSED, ++m_uFakeTimestamp ); + wlserver_mousebutton( uButton, bPressed, ++m_uFakeTimestamp ); wlserver_unlock(); + + if ( bPressed ) + m_uMouseButtonsHeld.emplace( uButton ); + else + m_uMouseButtonsHeld.erase( uButton ); } void CWaylandInputThread::Wayland_Pointer_Axis( wl_pointer *pPointer, uint32_t uTime, uint32_t uAxis, wl_fixed_t fValue ) {