feat: 添加GPU切换模式检测和主副显标识
- C++后端:GpuInfo添加isPrimary字段,读取boot_vga判断主副显 - C++后端:detectGpuMode()检测PRIME/Bumblebee/Solo/Hybrid模式 - C++后端:新增gpuMode和gpuPrimary属性 - QML前端:卡片显示绿色主显标签(boot_vga=1的GPU) - QML前端:弹窗显示GPU模式标签(PRIME等)和帮助图标 - 帮助图标悬停显示模式说明文字 - 翻译更新:主显/主顯 + 四种模式的中文说明
This commit is contained in:
@@ -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 条目
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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,6 +361,10 @@ AppletItem {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 6
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: parseGpuName(modelData)
|
||||
font.pixelSize: 13
|
||||
@@ -309,6 +374,25 @@ AppletItem {
|
||||
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 {
|
||||
text: parseDriverInfo(modelData)
|
||||
font.pixelSize: 11
|
||||
|
||||
@@ -43,5 +43,30 @@
|
||||
<source>Version: </source>
|
||||
<translation>版本号:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Primary</source>
|
||||
<translation>主显</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source>
|
||||
<translation>PRIME 模式:核显负责日常显示,独显按需渲染。运行 3D 应用时通过 PRIME Render Offload 调用独显。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
|
||||
<translation>Bumblebee 模式:旧版双 GPU 方案,已淘汰。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Solo mode: only one GPU detected.</source>
|
||||
<translation>Solo 模式:仅检测到一块 GPU。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
|
||||
<translation>Hybrid 模式:检测到双 GPU,但未发现已知切换方案。</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
||||
@@ -43,5 +43,30 @@
|
||||
<source>Version: </source>
|
||||
<translation>版本號:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Primary</source>
|
||||
<translation>主顯</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source>
|
||||
<translation>PRIME 模式:核顯負責日常顯示,獨顯按需渲染。運行 3D 應用時通過 PRIME Render Offload 調用獨顯。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
|
||||
<translation>Bumblebee 模式:舊版雙 GPU 方案,已淘汰。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Solo mode: only one GPU detected.</source>
|
||||
<translation>Solo 模式:僅偵測到一塊 GPU。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../package/driverview.qml"/>
|
||||
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
|
||||
<translation>Hybrid 模式:偵測到雙 GPU,但未發現已知切換方案。</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
||||
Reference in New Issue
Block a user