diff --git a/changelog/next.md b/changelog/next.md index cbfd51b..3969d55 100644 --- a/changelog/next.md +++ b/changelog/next.md @@ -58,6 +58,7 @@ set shell id. - Fixed ToplevelManager not clearing activeToplevel on deactivation. - Desktop action order is now preserved. - Fixed partial socket reads in greetd and hyprland on slow machines. +- Worked around Qt bug causing crashes when plugging and unplugging monitors. ## Packaging Changes diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index db53f37..13e648a 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -73,6 +73,7 @@ endfunction() # ----- qt_add_library(quickshell-wayland STATIC + wl_proxy_safe_deref.cpp platformmenu.cpp popupanchor.cpp xdgshell.cpp @@ -80,6 +81,13 @@ qt_add_library(quickshell-wayland STATIC output_tracking.cpp ) +# required for wl_proxy_safe_deref +target_link_libraries(quickshell-wayland PRIVATE ${CMAKE_DL_LIBS}) +target_link_options(quickshell PRIVATE + "LINKER:--export-dynamic-symbol=wl_proxy_get_listener" + "LINKER:--require-defined=wl_proxy_get_listener" +) + # required to make sure the constructor is linked add_library(quickshell-wayland-init OBJECT init.cpp) diff --git a/src/wayland/init.cpp b/src/wayland/init.cpp index e56eee3..790cebb 100644 --- a/src/wayland/init.cpp +++ b/src/wayland/init.cpp @@ -10,6 +10,7 @@ #include "wlr_layershell/wlr_layershell.hpp" #endif +void installWlProxySafeDeref(); // NOLINT(misc-use-internal-linkage) void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage) void installPopupPositioner(); // NOLINT(misc-use-internal-linkage) @@ -33,6 +34,7 @@ class WaylandPlugin: public QsEnginePlugin { } void init() override { + installWlProxySafeDeref(); installPlatformMenuHook(); installPopupPositioner(); } diff --git a/src/wayland/windowmanager/windowset.cpp b/src/wayland/windowmanager/windowset.cpp index 796cfe2..74e273d 100644 --- a/src/wayland/windowmanager/windowset.cpp +++ b/src/wayland/windowmanager/windowset.cpp @@ -8,9 +8,9 @@ #include #include +#include "../../windowmanager/screenprojection.hpp" #include "../../windowmanager/windowmanager.hpp" #include "../../windowmanager/windowset.hpp" -#include "../../windowmanager/screenprojection.hpp" #include "ext_workspace.hpp" namespace qs::wm::wayland { diff --git a/src/wayland/wl_proxy_safe_deref.cpp b/src/wayland/wl_proxy_safe_deref.cpp new file mode 100644 index 0000000..0ebc258 --- /dev/null +++ b/src/wayland/wl_proxy_safe_deref.cpp @@ -0,0 +1,42 @@ + +#include +#include +#include +#include +#include + +#include "../core/logcat.hpp" + +namespace { +QS_LOGGING_CATEGORY(logDeref, "quickshell.wayland.safederef", QtWarningMsg); +using wl_proxy_get_listener_t = const void* (*) (wl_proxy*); +wl_proxy_get_listener_t original_wl_proxy_get_listener = nullptr; // NOLINT +} // namespace + +extern "C" { +WL_EXPORT const void* wl_proxy_get_listener(struct wl_proxy* proxy) { + // Avoid null derefs of protocol objects in qtbase. + // https://qt-project.atlassian.net/browse/QTBUG-145022 + if (!proxy) [[unlikely]] { + qCCritical(logDeref) << "wl_proxy_get_listener called with a null proxy!"; + return nullptr; + } + + return original_wl_proxy_get_listener(proxy); +} +} + +// NOLINTBEGIN (concurrency-mt-unsafe) +void installWlProxySafeDeref() { + dlerror(); // clear old errors + + original_wl_proxy_get_listener = + reinterpret_cast(dlsym(RTLD_NEXT, "wl_proxy_get_listener")); + + if (auto* error = dlerror()) { + qCCritical(logDeref) << "Failed to find wl_proxy_get_listener for hooking:" << error; + } else { + qCInfo(logDeref) << "Installed wl_proxy_get_listener hook."; + } +} +// NOLINTEND