gui: Fix crash when wayland display goes away

The WaylandUtils class had to be rewritten to not cache the seat or
display as they can change.

The crash is resolved with a hack that removes the registry proxy
immediately after it's been used.
This commit is contained in:
Tasos Sahanidis
2025-11-05 16:34:41 +02:00
parent 0607013268
commit e14475b787
4 changed files with 87 additions and 100 deletions
+4 -2
View File
@@ -52,13 +52,13 @@ public:
};
WaylandIdleTimer::WaylandIdleTimer()
: m_supported{WaylandUtils::hasInterface("ext_idle_notifier_v1")}
: wl_utils(new WaylandUtils), m_supported(wl_utils->hasInterface("ext_idle_notifier_v1"))
{
if (m_supported) {
auto seat = WaylandUtils::seat();
m_notifier.reset(new WaylandIdleNotifier());
#ifdef USE_WAYLAND_INPUT_IDLE_NOTIFY
if (WaylandUtils::hasInterface("ext_idle_notifier_v1", 2)) {
if (wl_utils->hasInterface("ext_idle_notifier_v1", 2)) {
m_notification.reset(new WaylandIdleNotification(m_notifier->get_input_idle_notification(0, seat)));
} else {
m_notification.reset(new WaylandIdleNotification(m_notifier->get_idle_notification(0, seat)));
@@ -88,4 +88,6 @@ bool WaylandIdleTimer::isSupported() const
{
return m_supported;
}
#endif
+2
View File
@@ -3,6 +3,7 @@
#ifdef USE_WAYLAND_IDLE_NOTIFY
#include "idletimer.h"
#include "waylandutils.h"
#include <QScopedPointer>
@@ -17,6 +18,7 @@ public:
bool isSupported() const override;
private:
QScopedPointer<WaylandUtils> wl_utils;
bool m_supported;
QScopedPointer<WaylandIdleNotifier> m_notifier;
QScopedPointer<WaylandIdleNotification> m_notification;
+31 -57
View File
@@ -3,7 +3,6 @@
#include <QGuiApplication>
#ifdef USE_WAYLAND
#include <QGuiApplication>
#if QT_VERSION < QT_VERSION_CHECK(6, 5 ,0)
#include <qpa/qplatformnativeinterface.h>
#endif
@@ -11,17 +10,9 @@
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#if QT_VERSION >= QT_VERSION_CHECK(6, 3 ,0)
#include <qapplicationstatic.h>
#else
#include <QGlobalStatic>
#endif
#endif
namespace WaylandUtils
{
bool isWayland()
bool WaylandUtils::isWayland()
{
if (qGuiApp->platformName() == "wayland") {
return true;
@@ -39,51 +30,29 @@ bool isWayland()
}
#ifdef USE_WAYLAND
struct RegistryData {
QString interfaceName;
quint32 version;
};
class WaylandUtilsHelper {
public:
wl_display* m_display = nullptr;
wl_registry* m_registry = nullptr;
wl_seat *m_seat = nullptr;
QList<RegistryData> m_registerData;
WaylandUtilsHelper()
WaylandUtils::WaylandUtils()
{
wl_display* m_display = nullptr;
#if QT_VERSION < QT_VERSION_CHECK(6, 5 ,0)
QPlatformNativeInterface* nativeInterface = qGuiApp->platformNativeInterface();
if (!nativeInterface) {
if (!nativeInterface)
return;
}
m_display = static_cast<wl_display *>(nativeInterface->nativeResourceForIntegration("wl_display"));
if (!m_display) {
return;
}
m_seat = static_cast<wl_seat *>(nativeInterface->nativeResourceForIntegration("m_seat"));
if (!m_seat) {
m_display = nullptr;
return;
}
#else
auto nativeInterface = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
if (!nativeInterface) {
if (!nativeInterface)
return;
}
m_display = nativeInterface->display();
m_seat = nativeInterface->seat();
#endif
if (!m_display)
return;
m_registry = wl_display_get_registry(m_display);
if (!m_registry) {
wl_registry* m_registry = wl_display_get_registry(m_display);
if (!m_registry)
return;
}
auto global_handler = [](void* data_ptr, wl_registry*, uint32_t name, const char* interface, uint32_t version) {
auto registerData = static_cast<QList<RegistryData>*>(data_ptr);
@@ -101,34 +70,39 @@ public:
wl_display_dispatch(m_display);
wl_display_roundtrip(m_display);
// HACK: Destroy the registry proxy to indirectly remove the listener
// This way we avoid crashing when the display disappears/changes
wl_proxy_destroy(reinterpret_cast<wl_proxy*>(m_registry));
}
};
#if QT_VERSION >= QT_VERSION_CHECK(6, 3 ,0)
Q_APPLICATION_STATIC(WaylandUtilsHelper, g_helper,)
wl_seat* WaylandUtils::seat() {
#if QT_VERSION < QT_VERSION_CHECK(6, 5 ,0)
QPlatformNativeInterface* nativeInterface = qGuiApp->platformNativeInterface();
if (!nativeInterface)
return nullptr;
wl_seat* s = static_cast<wl_seat*>(nativeInterface->nativeResourceForIntegration("m_seat"));
#else
Q_GLOBAL_STATIC(WaylandUtilsHelper, g_helper)
#endif
auto nativeInterface = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
if (!nativeInterface)
return nullptr;
bool hasInterface(const QString &name, quint32 version)
wl_seat* s = nativeInterface->seat();
#endif
return s;
}
bool WaylandUtils::hasInterface(const QString &name, quint32 version)
{
auto it = std::find_if(g_helper->m_registerData.constBegin(), g_helper->m_registerData.constEnd(), [&name, &version](const RegistryData& data){
auto it = std::find_if(m_registerData.constBegin(), m_registerData.constEnd(), [&name, &version](const RegistryData& data){
const bool matched = data.interfaceName == name;
if (version == 0) {
return matched;
}
return matched && data.version == version;
});
return it != g_helper->m_registerData.constEnd();
return it != m_registerData.constEnd();
}
wl_display *display() {
return g_helper->m_display;
}
wl_seat *seat() {
return g_helper->m_seat;
}
#endif
} // WaylandUtils
+14 -5
View File
@@ -6,20 +6,29 @@
#ifdef USE_WAYLAND
class wl_display;
class wl_seat;
#include <QList>
#endif
namespace WaylandUtils
class WaylandUtils
{
bool isWayland();
public:
static bool isWayland();
#ifdef USE_WAYLAND
WaylandUtils();
static wl_seat* seat();
bool hasInterface(const QString& name, quint32 version = 0);
wl_display *display();
wl_seat *seat();
private:
struct RegistryData {
QString interfaceName;
quint32 version;
};
QList<RegistryData> m_registerData;
#endif
} // WaylandUtils
}; // WaylandUtils
#endif // WAYLANDUTILS_HPP