diff --git a/deepin-devicemanager/src/Page/MainWindow.cpp b/deepin-devicemanager/src/Page/MainWindow.cpp index ff6c1db6..a2848b6d 100644 --- a/deepin-devicemanager/src/Page/MainWindow.cpp +++ b/deepin-devicemanager/src/Page/MainWindow.cpp @@ -140,6 +140,16 @@ void MainWindow::refreshDataBaseLater() MainWindow::~MainWindow() { // 释放指针 + // 释放异步 xrandr 线程:先断开 finished 信号避免析构中途触发槽; + // wait() 无超时等待,因 runCmd 每子进程已限 5s+1s,双 runCmd 最长约 12s 内结束, + // 保证 run() 退出后再释放对象,避免 use-after-free + if (mp_XrandrThread) { + mp_XrandrThread->disconnect(this); // 避免 finished 槽在析构中途触发 + if (mp_XrandrThread->isRunning()) + mp_XrandrThread->wait(); // runCmd 每子进程已限 5s+1s,最长 ~12s 内结束 + delete mp_XrandrThread; + mp_XrandrThread = nullptr; + } if (mp_WorkingThread && mp_WorkingThread->isRunning()) mp_WorkingThread->terminate(); while (mp_WorkingThread && mp_WorkingThread->isRunning()) {} @@ -548,16 +558,21 @@ void MainWindow::slotLoadingFinish(const QString &message) void MainWindow::slotListItemClicked(const QString &itemStr) { - // xrandr would be execed later - if (tr("Monitor") == itemStr || tr("Overview") == itemStr) { //点击显示设备,执行线程加载信息 - ThreadExecXrandr tx(false, !checkWaylandMode()); - tx.start(); - tx.wait(); - } else if (tr("Display Adapter") == itemStr) { //点击显示适配器,执行线程加载信息 - ThreadExecXrandr tx(true, !checkWaylandMode()); - tx.start(); - tx.wait(); - } else if (tr("CPU") == itemStr) { //点击处理器,执行加载处理器信息线程 + // 显示相关模块:异步获取 xrandr 信息,避免在 UI 线程同步等待子进程阻塞事件循环 + if (tr("Monitor") == itemStr || tr("Overview") == itemStr || tr("Display Adapter") == itemStr) { + m_xrandrItem = itemStr; + // 上一次异步 xrandr 仍在运行时,仅记录最新待刷新项,待其结束后处理 + if (mp_XrandrThread && mp_XrandrThread->isRunning()) { + return; + } + startAsyncXrandr(itemStr); + return; + } + + // 切换到非显示模块时,取消待完成的显示模块刷新,避免异步完成后回切界面 + m_xrandrItem.clear(); + + if (tr("CPU") == itemStr) { //点击处理器,执行加载处理器信息线程 LoadCpuInfoThread lct; lct.start(); lct.wait(); @@ -572,24 +587,67 @@ void MainWindow::slotListItemClicked(const QString &itemStr) DeviceManager::instance()->correctPowerInfo(tool.getCurPowerInfo()); } - ThreadExecXrandr txgpu(true, !checkWaylandMode()); - txgpu.start(); - txgpu.wait(); - if(monitorNumber != txgpu.getMonitorNumber()) { + // 数据刷新时不处理界面刷新 + if (m_refreshing || mp_WorkingThread->isRunning()) return; - QString info; - DBusInterface::getInstance()->getInfo("is_server_running", info); - //请求后台更新信息 - if (!info.toInt()) { - refreshDataBaseLater(); - } - qCDebug(appLog)<< "Monitor refreshInfo" << __LINE__ << QDateTime::currentDateTime().toString("hh:mm:ss") << info << monitorNumber; - monitorNumber = txgpu.getMonitorNumber(); + updateDeviceForItem(itemStr); +} + +void MainWindow::startAsyncXrandr(const QString &itemStr) +{ + // 兜底:正常路径下此分支不可达(上层 isRunning() 短路、完成槽已置空), + // 仅防御极端时序下残留的线程对象 + if (mp_XrandrThread) { + mp_XrandrThread->deleteLater(); + mp_XrandrThread = nullptr; + } + bool gpu = (tr("Display Adapter") == itemStr); + mp_XrandrThread = new ThreadExecXrandr(gpu, !checkWaylandMode()); + m_xrandrStartedItem = itemStr; + connect(mp_XrandrThread, &QThread::finished, this, &MainWindow::slotXrandrFinished); + mp_XrandrThread->start(); +} + +void MainWindow::slotXrandrFinished() +{ + if (!mp_XrandrThread) { + return; + } + + // 显示器热插拔检测:monitorNumber 变化则请求后台刷新信息 + if (monitorNumber != mp_XrandrThread->getMonitorNumber()) { + QString info; + DBusInterface::getInstance()->getInfo("is_server_running", info); + //请求后台更新信息 + if (!info.toInt()) { + refreshDataBaseLater(); } + qCDebug(appLog) << "Monitor refreshInfo" << __LINE__ << QDateTime::currentDateTime().toString("hh:mm:ss") << info << monitorNumber; + monitorNumber = mp_XrandrThread->getMonitorNumber(); + } + + QString item = m_xrandrItem; + mp_XrandrThread->deleteLater(); + mp_XrandrThread = nullptr; + + // 异步执行期间切换到非显示模块:不再回切显示界面 + if (item.isEmpty()) { + return; + } + // 异步执行期间切换到其他显示模块:按最新项重新加载对应 xrandr 信息 + if (item != m_xrandrStartedItem) { + startAsyncXrandr(item); + return; + } // 数据刷新时不处理界面刷新 if (m_refreshing || mp_WorkingThread->isRunning()) return; + updateDeviceForItem(item); +} + +void MainWindow::updateDeviceForItem(const QString &itemStr) +{ QList lst; bool ret = DeviceManager::instance()->getDeviceList(itemStr, lst); diff --git a/deepin-devicemanager/src/Page/MainWindow.h b/deepin-devicemanager/src/Page/MainWindow.h index 6a60c8fd..eb233e17 100644 --- a/deepin-devicemanager/src/Page/MainWindow.h +++ b/deepin-devicemanager/src/Page/MainWindow.h @@ -23,6 +23,7 @@ class DeviceWidget; class LoadInfoThread; class PageDriverManager; class DriverScanWidget; +class ThreadExecXrandr; using namespace Dtk::Widget; @@ -129,6 +130,18 @@ class MainWindow : public DMainWindow * @brief refreshDataBaseLater:刷新设备信息 */ void refreshDataBaseLater(); + + /** + * @brief startAsyncXrandr:异步获取 xrandr 显示信息,避免阻塞 UI 线程 + * @param itemStr:当前点击的设备模块名 + */ + void startAsyncXrandr(const QString &itemStr); + + /** + * @brief updateDeviceForItem:根据模块名获取设备列表并刷新界面 + * @param itemStr:设备模块名 + */ + void updateDeviceForItem(const QString &itemStr); private slots: /** * @brief slotSetPage @@ -158,6 +171,11 @@ private slots: */ void slotExportInfo(); + /** + * @brief slotXrandrFinished:异步 xrandr 信息加载完成槽 + */ + void slotXrandrFinished(); + /** * @brief changeUI:UI界面变化,BIOS界面行高 */ @@ -177,6 +195,9 @@ private slots: DriverScanWidget *mp_DriverScanWidget; //驱动管理扫描界面 PageDriverManager *mp_DriverManager; //驱动管理主界面 LoadInfoThread *mp_WorkingThread; //信息加载线程 + ThreadExecXrandr *mp_XrandrThread = nullptr; //异步获取 xrandr 信息的线程 + QString m_xrandrItem; //异步 xrandr 完成后待刷新的模块 + QString m_xrandrStartedItem; //当前异步 xrandr 实际加载的模块 DButtonBox *mp_ButtonBox; // titlebar上添加Buttonbox bool m_refreshing = false; // 判断界面是否正在刷新 bool m_IsFirstRefresh = true; diff --git a/deepin-devicemanager/src/Tool/ThreadExecXrandr.cpp b/deepin-devicemanager/src/Tool/ThreadExecXrandr.cpp index 2d3f4e7b..ae928766 100644 --- a/deepin-devicemanager/src/Tool/ThreadExecXrandr.cpp +++ b/deepin-devicemanager/src/Tool/ThreadExecXrandr.cpp @@ -49,13 +49,23 @@ ThreadExecXrandr::ThreadExecXrandr(bool gpu, bool isDXcbPlatform) void ThreadExecXrandr::run() { if (m_Gpu) { - getGpuInfoFromXrandr(); - } else { - if(Common::boardVendorType() == "PGUV") { - QList> lstMap; - getResolutionRateFromDBus(lstMap); - }else + // X11/dxcb 环境通过 xrandr 补充显卡分辨率信息;Wayland/无 X 环境不执行 xrandr 子进程 + if (m_isDXcbPlatform) { + getGpuInfoFromXrandr(); + } + } else { + // Wayland/无 X 环境或 PGUV 机型走 DBus,避免 xrandr 子进程阻塞 UI 线程 + if (!m_isDXcbPlatform || Common::boardVendorType() == "PGUV") { + QList> lstMap; + getResolutionRateFromDBus(lstMap); + } else { getMonitorInfoFromXrandrVerbose(); + } + } + // 显示器数量用于热插拔检测,纯 DBus 调用不启动子进程 + if (m_monitorLst.isEmpty()) { + QMap tmp; + getResolutionFromDBus(tmp); } } @@ -63,7 +73,11 @@ void ThreadExecXrandr::runCmd(QString &info, const QString &cmd) { QProcess process; process.start(cmd); - process.waitForFinished(-1); + // 设置超时,避免 xrandr 在部分硬件/驱动或无 X 环境下挂起导致界面长时间无响应 + if (!process.waitForFinished(5000)) { + process.kill(); + process.waitForFinished(1000); + } info = process.readAllStandardOutput(); }