diff --git a/graphicsdriverapplet.cpp b/graphicsdriverapplet.cpp index 352c41b..cf7e2fa 100644 --- a/graphicsdriverapplet.cpp +++ b/graphicsdriverapplet.cpp @@ -28,6 +28,9 @@ GraphicsDriverApplet::GraphicsDriverApplet(QObject *parent) , m_ready(false) , m_lastAlertTime(0) , m_tempAlertEnabled(true) + , m_switchPending(false) + , m_restoreCountdown(0) + , m_restoreTimer(nullptr) { QSettings settings("deepin", "graphics-driver-applet"); m_tempAlertEnabled = settings.value("tempAlertEnabled", true).toBool(); @@ -45,6 +48,29 @@ bool GraphicsDriverApplet::load() bool GraphicsDriverApplet::init() { detectGpus(); + + // 检查是否有未确认的 GPU 切换(上次切换后可能黑屏) + QFile backup(backupFilePath()); + if (backup.exists()) { + // 有备份文件,说明上次切换未确认,启动 30 秒回退计时器 + m_switchPending = true; + m_restoreCountdown = 30; + emit switchPendingChanged(); + emit restoreCountdownChanged(); + + m_restoreTimer = new QTimer(this); + connect(m_restoreTimer, &QTimer::timeout, this, [this]() { + m_restoreCountdown--; + emit restoreCountdownChanged(); + if (m_restoreCountdown <= 0) { + restoreFromBackup(); + } + }); + m_restoreTimer->start(1000); + + qDebug() << "Unconfirmed GPU switch detected, auto-restore in 30s"; + } + return DApplet::init(); } @@ -115,6 +141,35 @@ void GraphicsDriverApplet::setTempAlertEnabled(bool enabled) emit tempAlertEnabledChanged(); } +bool GraphicsDriverApplet::primeOffloadEnabled() const +{ + QString configPath = QDir::homePath() + "/.config/environment.d/nvidia-prime.conf"; + QFile file(configPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false; + QString content = QTextStream(&file).readAll(); + return content.contains("__NV_PRIME_RENDER_OFFLOAD=1"); +} + +bool GraphicsDriverApplet::switchPending() const +{ + return m_switchPending; +} + +int GraphicsDriverApplet::restoreCountdown() const +{ + return m_restoreCountdown; +} + +QString GraphicsDriverApplet::primeConfigPath() +{ + return QDir::homePath() + "/.config/environment.d/nvidia-prime.conf"; +} + +QString GraphicsDriverApplet::backupFilePath() +{ + return QDir::homePath() + "/.config/environment.d/.gpu-switch-backup"; +} + void GraphicsDriverApplet::refreshDeviceInfo() { detectGpus(); @@ -649,10 +704,109 @@ void GraphicsDriverApplet::readGpuProcesses() bool GraphicsDriverApplet::switchGpu(const QString &mode) { // mode: "nvidia" / "intel" / "on-demand" - QProcess process; - process.start("pkexec", {"prime-select", "set", mode}); - process.waitForFinished(30000); - return process.exitCode() == 0; + QString configPath = primeConfigPath(); + QString backupPath = backupFilePath(); + QString configDir = QDir::homePath() + "/.config/environment.d"; + QDir().mkpath(configDir); + + // 保存当前模式到备份文件(用于回退) + QString prevMode = primeOffloadEnabled() ? "nvidia" : "on-demand"; + QFile backupFile(backupPath); + if (backupFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream(&backupFile) << prevMode; + backupFile.close(); + } + + if (mode == "intel") { + QFile::remove(configPath); + } else if (mode == "nvidia") { + QFile file(configPath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream(&file) << "__NV_PRIME_RENDER_OFFLOAD=1\n" + << "__GLX_VENDOR_LIBRARY_NAME=nvidia\n"; + file.close(); + } + } else if (mode == "on-demand") { + QFile::remove(configPath); + } + + sendNotification(tr("GPU Mode Switched"), + tr("Switched to %1 mode. Please re-login to take effect.").arg(mode)); + + detectGpuMode(); + emit primeOffloadEnabledChanged(); + + return true; +} + +void GraphicsDriverApplet::confirmSwitchSuccess() +{ + // 用户确认显示正常,删除备份文件 + QFile::remove(backupFilePath()); + + m_switchPending = false; + m_restoreCountdown = 0; + emit switchPendingChanged(); + emit restoreCountdownChanged(); + + if (m_restoreTimer) { + m_restoreTimer->stop(); + m_restoreTimer->deleteLater(); + m_restoreTimer = nullptr; + } + + qDebug() << "GPU switch confirmed by user"; +} + +void GraphicsDriverApplet::restoreSwitch() +{ + // 用户手动请求回退 + restoreFromBackup(); +} + +void GraphicsDriverApplet::restoreFromBackup() +{ + // 从备份文件恢复 + QFile backupFile(backupFilePath()); + if (!backupFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_switchPending = false; + emit switchPendingChanged(); + return; + } + + QString prevMode = QTextStream(&backupFile).readAll().trimmed(); + backupFile.close(); + QFile::remove(backupFilePath()); + + QString configPath = primeConfigPath(); + + if (prevMode == "nvidia") { + QFile file(configPath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream(&file) << "__NV_PRIME_RENDER_OFFLOAD=1\n" + << "__GLX_VENDOR_LIBRARY_NAME=nvidia\n"; + file.close(); + } + } else { + QFile::remove(configPath); + } + + m_switchPending = false; + m_restoreCountdown = 0; + emit switchPendingChanged(); + emit restoreCountdownChanged(); + emit primeOffloadEnabledChanged(); + + if (m_restoreTimer) { + m_restoreTimer->stop(); + m_restoreTimer->deleteLater(); + m_restoreTimer = nullptr; + } + + sendNotification(tr("GPU Mode Restored"), + tr("Reverted to %1 mode. Please re-login to take effect.").arg(prevMode)); + + qDebug() << "GPU mode restored to" << prevMode; } D_APPLET_CLASS(GraphicsDriverApplet) diff --git a/graphicsdriverapplet.h b/graphicsdriverapplet.h index 84463ab..1a79f39 100644 --- a/graphicsdriverapplet.h +++ b/graphicsdriverapplet.h @@ -47,6 +47,9 @@ class GraphicsDriverApplet : public DApplet Q_PROPERTY(QVariantList tempHistory READ tempHistory NOTIFY tempHistoryChanged) Q_PROPERTY(QStringList gpuProcesses READ gpuProcesses NOTIFY gpuProcessesChanged) Q_PROPERTY(bool tempAlertEnabled READ tempAlertEnabled WRITE setTempAlertEnabled NOTIFY tempAlertEnabledChanged) + Q_PROPERTY(bool primeOffloadEnabled READ primeOffloadEnabled NOTIFY primeOffloadEnabledChanged) + Q_PROPERTY(bool switchPending READ switchPending NOTIFY switchPendingChanged) + Q_PROPERTY(int restoreCountdown READ restoreCountdown NOTIFY restoreCountdownChanged) public: explicit GraphicsDriverApplet(QObject *parent = nullptr); @@ -66,10 +69,15 @@ public: QStringList gpuProcesses() const; bool tempAlertEnabled() const; void setTempAlertEnabled(bool enabled); + bool primeOffloadEnabled() const; + bool switchPending() const; + int restoreCountdown() const; Q_INVOKABLE void refreshDeviceInfo(); Q_INVOKABLE void refreshStats(); Q_INVOKABLE bool switchGpu(const QString &mode); + Q_INVOKABLE void confirmSwitchSuccess(); + Q_INVOKABLE void restoreSwitch(); signals: void deviceInfoChanged(); @@ -82,6 +90,9 @@ signals: void tempHistoryChanged(); void gpuProcessesChanged(); void tempAlertEnabledChanged(); + void primeOffloadEnabledChanged(); + void switchPendingChanged(); + void restoreCountdownChanged(); private: void detectGpus(); @@ -90,6 +101,9 @@ private: void detectGpuMode(); void checkTempAlert(); void sendNotification(const QString &summary, const QString &body); + void restoreFromBackup(); + static QString primeConfigPath(); + static QString backupFilePath(); static int findDrmCard(const QString &pciId); static QStringList runCommand(const QString &cmd, const QStringList &args); static QString parseGpuName(const QString &line); @@ -110,6 +124,9 @@ private: // 温度告警冷却时间戳 qint64 m_lastAlertTime; bool m_tempAlertEnabled; + bool m_switchPending; + int m_restoreCountdown; + QTimer *m_restoreTimer; }; DS_END_NAMESPACE diff --git a/package/driverview.qml b/package/driverview.qml index 45222fb..26ba5a6 100644 --- a/package/driverview.qml +++ b/package/driverview.qml @@ -28,13 +28,8 @@ AppletItem { // 当前 PRIME 模式(nvidia/on-demand/intel) readonly property string currentPrimeMode: { - if (root.gpuMode !== "PRIME" || !root.applet || !root.applet.gpuPrimary) return "" - var lines = root.deviceInfo.split("\n").filter(function(l) { return l.trim().length > 0 }) - for (var i = 0; i < lines.length; i++) { - if (/nvidia/i.test(lines[i]) && root.applet.gpuPrimary[i] === "true") { - return "nvidia" - } - } + if (root.gpuMode !== "PRIME") return "" + if (root.applet && root.applet.primeOffloadEnabled) return "nvidia" return "on-demand" } @@ -1071,6 +1066,98 @@ AppletItem { } } + // GPU 切换回退确认覆盖层(启动时检测到未确认的切换) + Rectangle { + id: restoreOverlay + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.75) + radius: 12 + visible: root.applet && root.applet.switchPending + z: 100 + + ColumnLayout { + anchors.centerIn: parent + width: parent.width - 48 + spacing: 16 + + // 警告图标 + Text { + Layout.alignment: Qt.AlignHCenter + text: "⚠" + font.pixelSize: 36 + color: Qt.rgba(245/255, 158/255, 11/255, 1) + } + + // 标题 + Text { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Display OK?") + font.pixelSize: 18 + font.bold: true + color: "#e8e8e8" + } + + // 说明 + Text { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + text: qsTr("GPU mode was changed. If display is abnormal, it will auto-revert in %1s.").arg(root.applet ? root.applet.restoreCountdown : 0) + font.pixelSize: 12 + color: "#a0a0a0" + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + // 按钮行 + RowLayout { + Layout.fillWidth: true + spacing: 12 + + // 回退按钮 + Button { + Layout.fillWidth: true + Layout.preferredHeight: 34 + font.pixelSize: 12 + + background: Rectangle { + color: parent.hovered ? Qt.rgba(220/255, 38/255, 38/255, 0.8) : Qt.rgba(220/255, 38/255, 38/255, 0.3) + radius: 8 + border.width: 1 + border.color: Qt.rgba(220/255, 38/255, 38/255, 0.6) + } + contentItem: Text { + text: qsTr("Revert") + color: "#ff6b6b" + font.pixelSize: 12 + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + onClicked: if (root.applet) root.applet.restoreSwitch() + } + + // 确认正常按钮 + Button { + Layout.fillWidth: true + Layout.preferredHeight: 34 + font.pixelSize: 12 + + background: Rectangle { + color: parent.hovered ? Qt.darker(root.accentBlue, 1.2) : root.accentBlue + radius: 8 + } + contentItem: Text { + text: qsTr("Display OK") + color: "white" + font.pixelSize: 12 + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + onClicked: if (root.applet) root.applet.confirmSwitchSuccess() + } + } + } + } + Component.onCompleted: { DockPanelPositioner.bounding = Qt.binding(function () { const point = root.mapToItem(null, root.width / 2, root.height / 2) diff --git a/translations/org.deepin.ds.graphics-driver_zh_CN.ts b/translations/org.deepin.ds.graphics-driver_zh_CN.ts index ec2460e..9f9c60d 100644 --- a/translations/org.deepin.ds.graphics-driver_zh_CN.ts +++ b/translations/org.deepin.ds.graphics-driver_zh_CN.ts @@ -193,5 +193,45 @@ Integrated 核显 + + + Display OK? + 显示是否正常? + + + + GPU mode was changed. If display is abnormal, it will auto-revert in %1s. + GPU 模式已切换。如果显示异常,将在 %1 秒后自动回退。 + + + + Revert + 回退 + + + + Display OK + 显示正常 + + + + GPU Mode Switched + GPU 模式已切换 + + + + Switched to %1 mode. Please re-login to take effect. + 已切换到 %1 模式,请重新登录生效。 + + + + GPU Mode Restored + GPU 模式已恢复 + + + + Reverted to %1 mode. Please re-login to take effect. + 已恢复到 %1 模式,请重新登录生效。 + diff --git a/translations/org.deepin.ds.graphics-driver_zh_TW.ts b/translations/org.deepin.ds.graphics-driver_zh_TW.ts index b67bb6a..92162e3 100644 --- a/translations/org.deepin.ds.graphics-driver_zh_TW.ts +++ b/translations/org.deepin.ds.graphics-driver_zh_TW.ts @@ -193,5 +193,45 @@ Integrated 核顯 + + + Display OK? + 顯示是否正常? + + + + GPU mode was changed. If display is abnormal, it will auto-revert in %1s. + GPU 模式已切換。如果顯示異常,將在 %1 秒後自動回退。 + + + + Revert + 回退 + + + + Display OK + 顯示正常 + + + + GPU Mode Switched + GPU 模式已切換 + + + + Switched to %1 mode. Please re-login to take effect. + 已切換到 %1 模式,請重新登入生效。 + + + + GPU Mode Restored + GPU 模式已恢復 + + + + Reverted to %1 mode. Please re-login to take effect. + 已恢復到 %1 模式,請重新登入生效。 +