diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt index c201baa5b..3d3bc3be2 100644 --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -4,6 +4,7 @@ find_package(Dtk${DTK_VERSION_MAJOR} COMPONENTS Widget REQUIRED) find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Widgets Gui WaylandClient) +pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11) find_package(TreelandProtocols REQUIRED) pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client) @@ -46,6 +47,7 @@ target_link_libraries(dde-shell PRIVATE Dtk${DTK_VERSION_MAJOR}::Widget Qt${QT_VERSION_MAJOR}::DBus PkgConfig::WaylandClient + PkgConfig::X11 ) configure_file(${CMAKE_SOURCE_DIR}/misc/dde-shell-plugin@.service.in diff --git a/shell/main.cpp b/shell/main.cpp index eebd0069c..f32283d07 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include "applet.h" #include "containment.h" #include "pluginloader.h" @@ -46,6 +48,28 @@ static void disableLogOutput() QLoggingCategory::setFilterRules("*.debug=false"); } +// Handle X server disconnection gracefully. +// When X server is killed (e.g. init 3), libX11's _XDefaultIOError calls exit(), +// bypassing Qt's normal shutdown. This causes plugins to be destroyed during global +// destructors when objects are already partially destroyed, leading to SIGSEGV. +// +// By installing a custom X I/O error handler, we prevent exit() from being called +// and instead trigger a clean Qt shutdown via quit(), which fires aboutToQuit +// and allows plugins to be properly destroyed. +static int xio_error_handler(Display * /*dpy*/) +{ + qCCritical(dsLog) << "X I/O error detected, shutting down gracefully."; + QCoreApplication::quit(); + return 0; +} + +static void sigpipe_handler(int /*sig*/) +{ + // 信号处理函数中禁止调用非异步信号安全函数(如 qCCritical), + // 否则在持有相关锁时触发信号会导致死锁。 + QCoreApplication::quit(); +} + class AppletManager { public: @@ -218,5 +242,19 @@ int main(int argc, char *argv[]) manager.quit(); }); + // Install X11-specific error handlers to prevent exit() from being called + // when X server is killed (e.g. during init 3 → init 5 switch). + // On Wayland, these are not needed as the error path is different. + if (QGuiApplication::platformName().startsWith(QStringLiteral("xcb"))) { + XSetIOErrorHandler(xio_error_handler); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigpipe_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGPIPE, &sa, nullptr); + } + return a.exec(); }