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 模式,請重新登入生效。
+