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;
|
return m_gpuStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString GraphicsDriverApplet::gpuMode() const
|
||||||
|
{
|
||||||
|
return m_gpuMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GraphicsDriverApplet::gpuPrimary() const
|
||||||
|
{
|
||||||
|
return m_gpuPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
void GraphicsDriverApplet::refreshDeviceInfo()
|
void GraphicsDriverApplet::refreshDeviceInfo()
|
||||||
{
|
{
|
||||||
detectGpus();
|
detectGpus();
|
||||||
@@ -87,6 +97,7 @@ void GraphicsDriverApplet::detectGpus()
|
|||||||
GpuInfo gpu;
|
GpuInfo gpu;
|
||||||
// 初始化统计字段
|
// 初始化统计字段
|
||||||
gpu.drmCard = -1;
|
gpu.drmCard = -1;
|
||||||
|
gpu.isPrimary = false;
|
||||||
gpu.temperature = -1;
|
gpu.temperature = -1;
|
||||||
gpu.gpuUsage = -1;
|
gpu.gpuUsage = -1;
|
||||||
gpu.memUsage = -1;
|
gpu.memUsage = -1;
|
||||||
@@ -119,6 +130,12 @@ void GraphicsDriverApplet::detectGpus()
|
|||||||
// 查找 DRM 卡号
|
// 查找 DRM 卡号
|
||||||
gpu.drmCard = findDrmCard(gpu.pciId);
|
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);
|
m_gpus.append(gpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,16 +163,27 @@ void GraphicsDriverApplet::detectGpus()
|
|||||||
m_gpuSummary = parts.join(" | ");
|
m_gpuSummary = parts.join(" | ");
|
||||||
m_ready = !m_gpus.isEmpty();
|
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() << "Detected GPUs:" << m_deviceInfo;
|
||||||
qDebug() << "Current drivers:" << m_currentDriver;
|
qDebug() << "Current drivers:" << m_currentDriver;
|
||||||
|
|
||||||
emit deviceInfoChanged();
|
emit deviceInfoChanged();
|
||||||
emit currentDriverChanged();
|
emit currentDriverChanged();
|
||||||
emit gpuSummaryChanged();
|
emit gpuSummaryChanged();
|
||||||
|
emit gpuStatsChanged();
|
||||||
|
emit gpuPrimaryChanged();
|
||||||
emit readyChanged();
|
emit readyChanged();
|
||||||
|
|
||||||
// 读取实时统计信息
|
// 读取实时统计信息
|
||||||
readGpuStats();
|
readGpuStats();
|
||||||
|
|
||||||
|
// 检测 GPU 切换模式
|
||||||
|
detectGpuMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList GraphicsDriverApplet::runCommand(const QString &cmd, const QStringList &args)
|
QStringList GraphicsDriverApplet::runCommand(const QString &cmd, const QStringList &args)
|
||||||
@@ -238,6 +266,44 @@ QString GraphicsDriverApplet::readDriverVersion(const QString &driverName)
|
|||||||
return QString();
|
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)
|
int GraphicsDriverApplet::findDrmCard(const QString &pciId)
|
||||||
{
|
{
|
||||||
// 扫描 /sys/bus/pci/devices/0000:XX:XX.X/drm/ 目录,找到 cardN 条目
|
// 扫描 /sys/bus/pci/devices/0000:XX:XX.X/drm/ 目录,找到 cardN 条目
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ struct GpuInfo {
|
|||||||
QString driverVersion; // 驱动版本
|
QString driverVersion; // 驱动版本
|
||||||
QString pciId; // PCI ID,如 "01:00.0"
|
QString pciId; // PCI ID,如 "01:00.0"
|
||||||
int drmCard; // DRM 卡号,如 0, 1(-1 表示未找到)
|
int drmCard; // DRM 卡号,如 0, 1(-1 表示未找到)
|
||||||
|
bool isPrimary; // 是否为主显卡(boot_vga=1)
|
||||||
|
|
||||||
// 实时统计
|
// 实时统计
|
||||||
int temperature; // 温度 (°C),-1 表示不可用
|
int temperature; // 温度 (°C),-1 表示不可用
|
||||||
@@ -35,6 +36,8 @@ class GraphicsDriverApplet : public DApplet
|
|||||||
Q_PROPERTY(QString currentDriver READ currentDriver NOTIFY currentDriverChanged)
|
Q_PROPERTY(QString currentDriver READ currentDriver NOTIFY currentDriverChanged)
|
||||||
Q_PROPERTY(QString gpuSummary READ gpuSummary NOTIFY gpuSummaryChanged)
|
Q_PROPERTY(QString gpuSummary READ gpuSummary NOTIFY gpuSummaryChanged)
|
||||||
Q_PROPERTY(QStringList gpuStats READ gpuStats NOTIFY gpuStatsChanged)
|
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)
|
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -48,6 +51,8 @@ public:
|
|||||||
QString currentDriver() const;
|
QString currentDriver() const;
|
||||||
QString gpuSummary() const;
|
QString gpuSummary() const;
|
||||||
QStringList gpuStats() const;
|
QStringList gpuStats() const;
|
||||||
|
QString gpuMode() const;
|
||||||
|
QStringList gpuPrimary() const;
|
||||||
bool ready() const;
|
bool ready() const;
|
||||||
|
|
||||||
Q_INVOKABLE void refreshDeviceInfo();
|
Q_INVOKABLE void refreshDeviceInfo();
|
||||||
@@ -58,11 +63,14 @@ signals:
|
|||||||
void currentDriverChanged();
|
void currentDriverChanged();
|
||||||
void gpuSummaryChanged();
|
void gpuSummaryChanged();
|
||||||
void gpuStatsChanged();
|
void gpuStatsChanged();
|
||||||
|
void gpuModeChanged();
|
||||||
|
void gpuPrimaryChanged();
|
||||||
void readyChanged();
|
void readyChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void detectGpus();
|
void detectGpus();
|
||||||
void readGpuStats();
|
void readGpuStats();
|
||||||
|
void detectGpuMode();
|
||||||
static int findDrmCard(const QString &pciId);
|
static int findDrmCard(const QString &pciId);
|
||||||
static QStringList runCommand(const QString &cmd, const QStringList &args);
|
static QStringList runCommand(const QString &cmd, const QStringList &args);
|
||||||
static QString parseGpuName(const QString &line);
|
static QString parseGpuName(const QString &line);
|
||||||
@@ -73,6 +81,8 @@ private:
|
|||||||
QString m_currentDriver;
|
QString m_currentDriver;
|
||||||
QString m_gpuSummary;
|
QString m_gpuSummary;
|
||||||
QStringList m_gpuStats;
|
QStringList m_gpuStats;
|
||||||
|
QString m_gpuMode;
|
||||||
|
QStringList m_gpuPrimary;
|
||||||
bool m_ready;
|
bool m_ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+90
-6
@@ -23,6 +23,7 @@ AppletItem {
|
|||||||
readonly property string gpuSummary: applet ? (applet.gpuSummary || qsTr("No GPU detected")) : qsTr("No GPU detected")
|
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 deviceInfo: applet ? (applet.deviceInfo || qsTr("Unknown")) : qsTr("Unknown")
|
||||||
readonly property string currentDriver: applet ? (applet.currentDriver || 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
|
property Palette basePalette: DockPalette.iconTextPalette
|
||||||
readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.9)
|
readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.9)
|
||||||
@@ -189,6 +190,65 @@ AppletItem {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
elide: Text.ElideRight
|
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 列表区域
|
// GPU 列表区域
|
||||||
@@ -220,6 +280,7 @@ AppletItem {
|
|||||||
border.color: root.cardBorder
|
border.color: root.cardBorder
|
||||||
|
|
||||||
property string statsLine: root.applet && root.applet.gpuStats ? (root.applet.gpuStats[index] || "") : ""
|
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: {
|
property string tempStr: {
|
||||||
var parts = statsLine.split("|")
|
var parts = statsLine.split("|")
|
||||||
var temp = parts[0]
|
var temp = parts[0]
|
||||||
@@ -300,13 +361,36 @@ AppletItem {
|
|||||||
spacing: 4
|
spacing: 4
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Text {
|
RowLayout {
|
||||||
text: parseGpuName(modelData)
|
spacing: 6
|
||||||
font.pixelSize: 13
|
|
||||||
font.bold: true
|
|
||||||
color: root.primaryText
|
|
||||||
Layout.fillWidth: true
|
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 {
|
Text {
|
||||||
|
|||||||
@@ -43,5 +43,30 @@
|
|||||||
<source>Version: </source>
|
<source>Version: </source>
|
||||||
<translation>版本号:</translation>
|
<translation>版本号:</translation>
|
||||||
</message>
|
</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>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|||||||
@@ -43,5 +43,30 @@
|
|||||||
<source>Version: </source>
|
<source>Version: </source>
|
||||||
<translation>版本號:</translation>
|
<translation>版本號:</translation>
|
||||||
</message>
|
</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>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|||||||
Reference in New Issue
Block a user