diff --git a/graphicsdriverapplet.cpp b/graphicsdriverapplet.cpp
index 34cdc45..76fcfd8 100644
--- a/graphicsdriverapplet.cpp
+++ b/graphicsdriverapplet.cpp
@@ -63,6 +63,16 @@ QStringList GraphicsDriverApplet::gpuStats() const
return m_gpuStats;
}
+QString GraphicsDriverApplet::gpuMode() const
+{
+ return m_gpuMode;
+}
+
+QStringList GraphicsDriverApplet::gpuPrimary() const
+{
+ return m_gpuPrimary;
+}
+
void GraphicsDriverApplet::refreshDeviceInfo()
{
detectGpus();
@@ -87,6 +97,7 @@ void GraphicsDriverApplet::detectGpus()
GpuInfo gpu;
// 初始化统计字段
gpu.drmCard = -1;
+ gpu.isPrimary = false;
gpu.temperature = -1;
gpu.gpuUsage = -1;
gpu.memUsage = -1;
@@ -119,6 +130,12 @@ void GraphicsDriverApplet::detectGpus()
// 查找 DRM 卡号
gpu.drmCard = findDrmCard(gpu.pciId);
+ // 读取 boot_vga 判断主副显
+ QFile bootVgaFile("/sys/bus/pci/devices/0000:" + gpu.pciId + "/boot_vga");
+ if (bootVgaFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ gpu.isPrimary = QTextStream(&bootVgaFile).readAll().trimmed() == "1";
+ }
+
m_gpus.append(gpu);
}
}
@@ -146,16 +163,27 @@ void GraphicsDriverApplet::detectGpus()
m_gpuSummary = parts.join(" | ");
m_ready = !m_gpus.isEmpty();
+ // 生成主副显标识
+ m_gpuPrimary.clear();
+ for (const GpuInfo &gpu : m_gpus) {
+ m_gpuPrimary << (gpu.isPrimary ? "true" : "false");
+ }
+
qDebug() << "Detected GPUs:" << m_deviceInfo;
qDebug() << "Current drivers:" << m_currentDriver;
emit deviceInfoChanged();
emit currentDriverChanged();
emit gpuSummaryChanged();
+ emit gpuStatsChanged();
+ emit gpuPrimaryChanged();
emit readyChanged();
// 读取实时统计信息
readGpuStats();
+
+ // 检测 GPU 切换模式
+ detectGpuMode();
}
QStringList GraphicsDriverApplet::runCommand(const QString &cmd, const QStringList &args)
@@ -238,6 +266,44 @@ QString GraphicsDriverApplet::readDriverVersion(const QString &driverName)
return QString();
}
+void GraphicsDriverApplet::detectGpuMode()
+{
+ // 单 GPU
+ if (m_gpus.size() <= 1) {
+ m_gpuMode = "Solo";
+ emit gpuModeChanged();
+ return;
+ }
+
+ // 多 GPU:检测 PRIME / Bumblebee
+ // PRIME: nvidia-drm-outputclass.conf 存在
+ if (QFile::exists("/usr/share/X11/xorg.conf.d/nvidia-drm-outputclass.conf") ||
+ QFile::exists("/etc/X11/xorg.conf.d/70-nvidia.conf")) {
+ m_gpuMode = "PRIME";
+ emit gpuModeChanged();
+ return;
+ }
+
+ // Bumblebee: 检查 bumblebeed 服务
+ QStringList lsmod = runCommand("lsmod", {});
+ bool bumblebeeLoaded = false;
+ for (const QString &line : lsmod) {
+ if (line.contains("bumblebee", Qt::CaseInsensitive)) {
+ bumblebeeLoaded = true;
+ break;
+ }
+ }
+ if (bumblebeeLoaded || QFile::exists("/usr/sbin/bumblebeed")) {
+ m_gpuMode = "Bumblebee";
+ emit gpuModeChanged();
+ return;
+ }
+
+ // 多 GPU 但无已知切换方案
+ m_gpuMode = "Hybrid";
+ emit gpuModeChanged();
+}
+
int GraphicsDriverApplet::findDrmCard(const QString &pciId)
{
// 扫描 /sys/bus/pci/devices/0000:XX:XX.X/drm/ 目录,找到 cardN 条目
diff --git a/graphicsdriverapplet.h b/graphicsdriverapplet.h
index 8c67d34..f483e69 100644
--- a/graphicsdriverapplet.h
+++ b/graphicsdriverapplet.h
@@ -18,6 +18,7 @@ struct GpuInfo {
QString driverVersion; // 驱动版本
QString pciId; // PCI ID,如 "01:00.0"
int drmCard; // DRM 卡号,如 0, 1(-1 表示未找到)
+ bool isPrimary; // 是否为主显卡(boot_vga=1)
// 实时统计
int temperature; // 温度 (°C),-1 表示不可用
@@ -35,6 +36,8 @@ class GraphicsDriverApplet : public DApplet
Q_PROPERTY(QString currentDriver READ currentDriver NOTIFY currentDriverChanged)
Q_PROPERTY(QString gpuSummary READ gpuSummary NOTIFY gpuSummaryChanged)
Q_PROPERTY(QStringList gpuStats READ gpuStats NOTIFY gpuStatsChanged)
+ Q_PROPERTY(QString gpuMode READ gpuMode NOTIFY gpuModeChanged)
+ Q_PROPERTY(QStringList gpuPrimary READ gpuPrimary NOTIFY gpuPrimaryChanged)
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
public:
@@ -48,6 +51,8 @@ public:
QString currentDriver() const;
QString gpuSummary() const;
QStringList gpuStats() const;
+ QString gpuMode() const;
+ QStringList gpuPrimary() const;
bool ready() const;
Q_INVOKABLE void refreshDeviceInfo();
@@ -58,11 +63,14 @@ signals:
void currentDriverChanged();
void gpuSummaryChanged();
void gpuStatsChanged();
+ void gpuModeChanged();
+ void gpuPrimaryChanged();
void readyChanged();
private:
void detectGpus();
void readGpuStats();
+ void detectGpuMode();
static int findDrmCard(const QString &pciId);
static QStringList runCommand(const QString &cmd, const QStringList &args);
static QString parseGpuName(const QString &line);
@@ -73,6 +81,8 @@ private:
QString m_currentDriver;
QString m_gpuSummary;
QStringList m_gpuStats;
+ QString m_gpuMode;
+ QStringList m_gpuPrimary;
bool m_ready;
};
diff --git a/package/driverview.qml b/package/driverview.qml
index 1945943..a369da3 100644
--- a/package/driverview.qml
+++ b/package/driverview.qml
@@ -23,6 +23,7 @@ AppletItem {
readonly property string gpuSummary: applet ? (applet.gpuSummary || qsTr("No GPU detected")) : qsTr("No GPU detected")
readonly property string deviceInfo: applet ? (applet.deviceInfo || qsTr("Unknown")) : qsTr("Unknown")
readonly property string currentDriver: applet ? (applet.currentDriver || qsTr("Unknown")) : qsTr("Unknown")
+ readonly property string gpuMode: applet ? (applet.gpuMode || "") : ""
property Palette basePalette: DockPalette.iconTextPalette
readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.9)
@@ -189,6 +190,65 @@ AppletItem {
Layout.fillWidth: true
elide: Text.ElideRight
}
+
+ // GPU 模式标签
+ Rectangle {
+ visible: root.gpuMode.length > 0
+ width: modeText.implicitWidth + 16
+ height: 20
+ color: root.accentBlueLight
+ radius: 10
+
+ Text {
+ id: modeText
+ anchors.centerIn: parent
+ text: root.gpuMode
+ font.pixelSize: 11
+ font.bold: true
+ color: root.accentBlue
+ }
+ }
+
+ // 帮助图标
+ Item {
+ visible: root.gpuMode.length > 0
+ width: 16
+ height: 16
+
+ Rectangle {
+ anchors.fill: parent
+ radius: width / 2
+ color: helpHover.hovered ? root.accentBlue : root.tertiaryText
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: "?"
+ font.pixelSize: 10
+ font.bold: true
+ color: "white"
+ }
+
+ HoverHandler {
+ id: helpHover
+ }
+
+ ToolTip {
+ visible: helpHover.hovered
+ delay: 200
+ text: {
+ if (root.gpuMode === "PRIME")
+ return qsTr("PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.")
+ if (root.gpuMode === "Bumblebee")
+ return qsTr("Bumblebee mode: a legacy dual-GPU solution, now deprecated.")
+ if (root.gpuMode === "Solo")
+ return qsTr("Solo mode: only one GPU detected.")
+ if (root.gpuMode === "Hybrid")
+ return qsTr("Hybrid mode: dual GPU detected, but no known switching solution found.")
+ return ""
+ }
+ }
+ }
}
// GPU 列表区域
@@ -220,6 +280,7 @@ AppletItem {
border.color: root.cardBorder
property string statsLine: root.applet && root.applet.gpuStats ? (root.applet.gpuStats[index] || "") : ""
+ property bool isPrimaryGpu: root.applet && root.applet.gpuPrimary ? (root.applet.gpuPrimary[index] === "true") : false
property string tempStr: {
var parts = statsLine.split("|")
var temp = parts[0]
@@ -300,13 +361,36 @@ AppletItem {
spacing: 4
Layout.fillWidth: true
- Text {
- text: parseGpuName(modelData)
- font.pixelSize: 13
- font.bold: true
- color: root.primaryText
+ RowLayout {
+ spacing: 6
Layout.fillWidth: true
- elide: Text.ElideRight
+
+ Text {
+ text: parseGpuName(modelData)
+ font.pixelSize: 13
+ font.bold: true
+ color: root.primaryText
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ }
+
+ // 主/副显标识
+ Rectangle {
+ visible: gpuCard.isPrimaryGpu
+ width: badgeText.implicitWidth + 12
+ height: 16
+ color: Qt.rgba(22/255, 163/255, 74/255, 0.15)
+ radius: 8
+
+ Text {
+ id: badgeText
+ anchors.centerIn: parent
+ text: qsTr("Primary")
+ font.pixelSize: 10
+ font.bold: true
+ color: Qt.rgba(22/255, 163/255, 74/255, 1)
+ }
+ }
}
Text {
diff --git a/translations/org.deepin.ds.graphics-driver_zh_CN.ts b/translations/org.deepin.ds.graphics-driver_zh_CN.ts
index c9d185e..118c9a1 100644
--- a/translations/org.deepin.ds.graphics-driver_zh_CN.ts
+++ b/translations/org.deepin.ds.graphics-driver_zh_CN.ts
@@ -43,5 +43,30 @@
Version:
版本号:
+
+
+ Primary
+ 主显
+
+
+
+ PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.
+ PRIME 模式:核显负责日常显示,独显按需渲染。运行 3D 应用时通过 PRIME Render Offload 调用独显。
+
+
+
+ Bumblebee mode: a legacy dual-GPU solution, now deprecated.
+ Bumblebee 模式:旧版双 GPU 方案,已淘汰。
+
+
+
+ Solo mode: only one GPU detected.
+ Solo 模式:仅检测到一块 GPU。
+
+
+
+ Hybrid mode: dual GPU detected, but no known switching solution found.
+ Hybrid 模式:检测到双 GPU,但未发现已知切换方案。
+
diff --git a/translations/org.deepin.ds.graphics-driver_zh_TW.ts b/translations/org.deepin.ds.graphics-driver_zh_TW.ts
index 4020ff1..4d2a205 100644
--- a/translations/org.deepin.ds.graphics-driver_zh_TW.ts
+++ b/translations/org.deepin.ds.graphics-driver_zh_TW.ts
@@ -43,5 +43,30 @@
Version:
版本號:
+
+
+ Primary
+ 主顯
+
+
+
+ PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.
+ PRIME 模式:核顯負責日常顯示,獨顯按需渲染。運行 3D 應用時通過 PRIME Render Offload 調用獨顯。
+
+
+
+ Bumblebee mode: a legacy dual-GPU solution, now deprecated.
+ Bumblebee 模式:舊版雙 GPU 方案,已淘汰。
+
+
+
+ Solo mode: only one GPU detected.
+ Solo 模式:僅偵測到一塊 GPU。
+
+
+
+ Hybrid mode: dual GPU detected, but no known switching solution found.
+ Hybrid 模式:偵測到雙 GPU,但未發現已知切換方案。
+