7 Commits

Author SHA1 Message Date
Jokul 9e474de717 feat: GPU切换回退保护逻辑
- switchGpu切换前写入备份文件(.gpu-switch-backup)记录之前模式
- init()检测未确认的切换,启动30秒自动回退计时器
- QML弹出'显示是否正常'确认覆盖层,支持手动确认/回退/超时自动回退
- confirmSwitchSuccess()用户确认正常后删除备份
- restoreFromBackup()自动恢复之前模式并发送通知
- primeOffloadEnabled属性检测当前PRIME offload状态
- switchGpu改用environment.d方式,不需要root/pkexec
- 翻译更新:回退确认相关8条新增
2026-07-14 22:15:52 +08:00
Jokul 64408bf842 feat: GPU切换确认弹窗、按钮选中状态、文字对比度优化
- GPU切换按钮:核显厂商自动识别(AMD/Intel),当前模式高亮选中
- 切换确认覆盖层:15秒倒计时圆环,确认/取消按钮,超时自动取消
- 文字对比度提升:primaryText 0.95, secondaryText 0.8, tertiaryText 0.65
- 图表刻度文字 0.25->0.5,网格线 0.06->0.1
- 删除卡片内频率/功耗/风扇行,驱动名版本号两端对齐
- 卡片高度78,进度条宽度180,弹窗宽度480
- 翻译更新:切换确认相关8条新增
2026-07-14 21:50:35 +08:00
Jokul f7c6801793 feat: 设置窗口、合并温度趋势图、UI优化
- 新增设置窗口(右键打开):无边框毛玻璃风格,温度告警开关,QSettings持久化
- 合并温度趋势图:多GPU多色折线,图例,Y轴刻度,填充区域,最新温度标注
- 显卡排序:核显在前独显在后(std::stable_sort by boot_vga)
- 卡片优化:删除温度行和频率/功耗行,驱动名+版本两端对齐,高度压缩至78
- 弹窗优化:宽度480,右上角刷新图标替代底部按钮
- tooltip修复:删除自定义Rectangle tooltip改用标准ToolTip
- 翻译更新:设置/常规设置/温度告警/温度趋势等
2026-07-14 21:31:00 +08:00
Jokul 2570a028ea feat: 添加GPU频率/功耗/风扇监控、温度告警、趋势图、右键菜单、进程列表、GPU切换
第二、三阶段功能:

数据增强(第一阶段):
- GPU频率: NVIDIA nvidia-smi clocks.gr, Intel gt/gt0/rps_act_freq_mhz
- 显存频率: NVIDIA nvidia-smi clocks.mem
- 功耗: NVIDIA nvidia-smi power.draw, AMD hwmon power1_average
- 风扇转速: NVIDIA nvidia-smi fan.speed, AMD hwmon fan1_input
- 任务栏图标根据最高温度变色(≥80°C红/≥60°C橙)
- readGpuStats 每次刷新前重置统计字段,避免残留旧值

交互提升(第二阶段):
- 温度告警: GPU温度≥80°C时通过D-Bus发送桌面通知,30秒冷却
- 右键菜单: 使用Qt.labs.platform原生菜单+MenuHelper,支持刷新和GPU切换
- 迷你趋势图: Canvas绘制近60秒温度走势折线
- 英文翻译: 新增en.ts,23条全部翻译

高级功能(第三阶段):
- NVIDIA进程列表: nvidia-smi --query-compute-apps查询,弹框中显示进程名和显存占用
- GPU切换: pkexec prime-select set <mode>,弹框按钮+右键菜单,仅PRIME模式显示

其他改进:
- 自定义深色主题tooltip替代系统默认白色tooltip
- 第一张卡片tooltip显示在下方避免被裁剪
- CMakeLists.txt lupdate添加C++源文件提取tr()翻译
- gpuStats格式扩展为9字段: temp|gpuUsage|memUsage|memUsed|memTotal|gpuClock|memClock|powerDraw|fanSpeed
2026-07-14 17:32:46 +08:00
Jokul cda61d9d72 刷新时间调整成1秒 2026-07-14 15:45:46 +08:00
Jokul dd6b5ed8b9 feat: Intel核显回退数据源,支持温度和共享内存显示
Intel 核显无 hwmon/gpu_busy_percent/mem_info_vram 等 sysfs 接口,
增加回退逻辑:
- 温度:扫描 thermal_zone 匹配 B0D4(Intel GPU ACPI 标识)
- 显存:读取 /proc/meminfo 获取系统共享内存总量和已用量
- GPU 使用率:Skylake i915 不支持,仍显示占位符
2026-07-14 10:40:56 +08:00
Jokul e771a160ab fix: 修复Intel显卡图标误显示为AMD及插件加载失败
1. CMakeLists.txt 链接 libdde-shell 库 (Dde::Shell),修复 undefined symbol:
   ds::DApplet::staticMetaObject 导致 .so 加载失败、弹框空白的问题

2. parseGpuVendorShort 用正则单词边界匹配厂商名称,修复 indexOf('ati')
   误匹配 'corporation' 中的子串导致 Intel 显卡显示 AMD 图标的问题
2026-07-14 10:01:46 +08:00
8 changed files with 1851 additions and 121 deletions
+1 -1
View File
@@ -73,7 +73,7 @@ All GPU data is collected directly from sysfs/command-line tools (no D-Bus servi
- Colors derived from `DockPalette.iconTextPalette` (basePalette): `primaryText`, `secondaryText`, `tertiaryText`, `cardBackground`, `cardBorder`, `accentBlue`, `accentBlueLight`.
- Existing JS helper functions in driverview.qml: `parseGpuName()`, `parseDriverInfo()`, `parseDriverName()`, `parseDriverVersion()`, `parseGpuVendorShort()`, `buildToolTipText()`.
- `deviceInfo` format: each line = `"GPU Name (driver version)"`. Parsed by splitting on `(` and `)`.
- Timer pattern: `statsRefreshTimer` (2s interval) starts on popup open AND tooltip hover, stops on close/leave.
- Timer pattern: `statsRefreshTimer` (1s interval) starts on popup open AND tooltip hover, stops on close/leave.
## No Tests
+6 -1
View File
@@ -12,6 +12,7 @@ set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Core Quick DBus LinguistTools)
find_package(Dtk6 REQUIRED COMPONENTS Core)
find_package(DDEShell REQUIRED)
# 插件 ID
set(PLUGIN_ID "org.deepin.ds.graphics-driver")
@@ -23,25 +24,28 @@ set(TRANSLATIONS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/translations)
set(TS_FILES
${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_CN.ts
${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_TW.ts
${TRANSLATIONS_DIR}/${PLUGIN_ID}_en.ts
)
# 生成 .qm 编译翻译文件
set(QM_FILES
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_CN.qm
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_TW.qm
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_en.qm
)
add_custom_command(
OUTPUT ${QM_FILES}
COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_CN.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_CN.qm
COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_TW.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_TW.qm
COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_en.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_en.qm
DEPENDS ${TS_FILES}
COMMENT "Compiling translations to .qm files"
)
# 手动更新翻译源文件(不加入 ALL,仅在需要时手动 make update_translations
add_custom_target(update_translations
COMMAND /usr/lib/qt6/bin/lupdate ${CMAKE_CURRENT_SOURCE_DIR}/package -ts ${TS_FILES}
COMMAND /usr/lib/qt6/bin/lupdate ${CMAKE_CURRENT_SOURCE_DIR}/package ${CMAKE_CURRENT_SOURCE_DIR}/graphicsdriverapplet.cpp -ts ${TS_FILES}
COMMENT "Updating translation source files"
)
@@ -63,6 +67,7 @@ target_link_libraries(org.deepin.ds.graphics-driver PRIVATE
Qt6::Quick
Qt6::DBus
Dtk6::Core
Dde::Shell
)
# 安装到dde-shell插件目录
+404 -6
View File
@@ -12,6 +12,12 @@
#include <QTextStream>
#include <QRegularExpression>
#include <QFileInfo>
#include <QSettings>
#include <algorithm>
#include <QDateTime>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QVariantList>
DCORE_USE_NAMESPACE
@@ -20,7 +26,14 @@ DS_BEGIN_NAMESPACE
GraphicsDriverApplet::GraphicsDriverApplet(QObject *parent)
: DApplet(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();
}
GraphicsDriverApplet::~GraphicsDriverApplet()
@@ -35,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();
}
@@ -73,6 +109,67 @@ QStringList GraphicsDriverApplet::gpuPrimary() const
return m_gpuPrimary;
}
QVariantList GraphicsDriverApplet::tempHistory() const
{
QVariantList result;
for (const QList<int> &history : m_tempHistory) {
QVariantList gpuHistory;
for (int temp : history) {
gpuHistory << temp;
}
result << QVariant(gpuHistory);
}
return result;
}
QStringList GraphicsDriverApplet::gpuProcesses() const
{
return m_gpuProcesses;
}
bool GraphicsDriverApplet::tempAlertEnabled() const
{
return m_tempAlertEnabled;
}
void GraphicsDriverApplet::setTempAlertEnabled(bool enabled)
{
if (m_tempAlertEnabled == enabled) return;
m_tempAlertEnabled = enabled;
QSettings settings("deepin", "graphics-driver-applet");
settings.setValue("tempAlertEnabled", 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();
@@ -86,6 +183,7 @@ void GraphicsDriverApplet::refreshStats()
void GraphicsDriverApplet::detectGpus()
{
m_gpus.clear();
m_tempHistory.clear();
// 通过 lspci 检测显卡
QStringList lines = runCommand("lspci", {"-mm"});
@@ -103,6 +201,10 @@ void GraphicsDriverApplet::detectGpus()
gpu.memUsage = -1;
gpu.memTotal = -1;
gpu.memUsed = -1;
gpu.gpuClock = -1;
gpu.memClock = -1;
gpu.powerDraw = -1;
gpu.fanSpeed = -1;
// lspci -mm 格式: "Class" "Vendor" "Device" ...
// 提取 PCI ID(行首的 01:00.0 等)
@@ -140,6 +242,11 @@ void GraphicsDriverApplet::detectGpus()
}
}
// 排序:核显(isPrimary)排在前面
std::stable_sort(m_gpus.begin(), m_gpus.end(), [](const GpuInfo &a, const GpuInfo &b) {
return a.isPrimary > b.isPrimary;
});
// 生成摘要信息
QStringList parts;
QStringList driverParts;
@@ -326,10 +433,11 @@ int GraphicsDriverApplet::findDrmCard(const QString &pciId)
void GraphicsDriverApplet::readGpuStats()
{
// 先处理 NVIDIA GPU(单次 nvidia-smi 调用获取所有 NVIDIA GPU 数据)
// nvidia-smi 输出格式: pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used
// nvidia-smi 输出格式: pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,
// memory.total,memory.used,clocks.gr,clocks.mem,power.draw,fan.speed
QMap<QString, QStringList> nvidiaData; // pciId -> stats
QStringList nvLines = runCommand("nvidia-smi", {
"--query-gpu=pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used",
"--query-gpu=pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used,clocks.gr,clocks.mem,power.draw,fan.speed",
"--format=csv,noheader,nounits"
});
for (const QString &line : nvLines) {
@@ -344,6 +452,17 @@ void GraphicsDriverApplet::readGpuStats()
// 逐个 GPU 读取统计
for (GpuInfo &gpu : m_gpus) {
// 每次刷新前重置实时统计字段,确保数据源不可用时不会残留旧值
gpu.temperature = -1;
gpu.gpuUsage = -1;
gpu.memUsage = -1;
gpu.memTotal = -1;
gpu.memUsed = -1;
gpu.gpuClock = -1;
gpu.memClock = -1;
gpu.powerDraw = -1;
gpu.fanSpeed = -1;
if (gpu.driver == "nvidia" && nvidiaData.contains(gpu.pciId)) {
// NVIDIA: 使用 nvidia-smi 数据
QStringList &parts = nvidiaData[gpu.pciId];
@@ -352,6 +471,19 @@ void GraphicsDriverApplet::readGpuStats()
gpu.memUsage = parts[3].trimmed().toInt();
gpu.memTotal = parts[4].trimmed().toInt();
gpu.memUsed = parts[5].trimmed().toInt();
// 频率 (parts[6]=GPU, parts[7]=显存)
if (parts.size() > 6) gpu.gpuClock = parts[6].trimmed().toInt();
if (parts.size() > 7) gpu.memClock = parts[7].trimmed().toInt();
// 功耗 (parts[8])[N/A] 时保持 -1
if (parts.size() > 8) {
QString pwr = parts[8].trimmed();
if (pwr != "[N/A]" && !pwr.isEmpty()) gpu.powerDraw = pwr.toDouble();
}
// 风扇转速 (parts[9])[N/A] 时保持 -1
if (parts.size() > 9) {
QString fan = parts[9].trimmed();
if (fan != "[N/A]" && !fan.isEmpty()) gpu.fanSpeed = fan.toInt();
}
} else if (gpu.drmCard >= 0) {
// AMD / Intel / 其他: 使用 sysfs
QString cardPath = QString("/sys/class/drm/card%1/device").arg(gpu.drmCard);
@@ -384,31 +516,297 @@ void GraphicsDriverApplet::readGpuStats()
if (nameFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString name = QTextStream(&nameFile).readAll().trimmed();
if (name == gpu.driver || name == "amdgpu" || name == "radeon") {
QFile tempFile(hwmonDir.absoluteFilePath(hwmon + "/temp1_input"));
QString hwmonPath = hwmonDir.absoluteFilePath(hwmon);
// 温度
QFile tempFile(hwmonPath + "/temp1_input");
if (tempFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
int tempMilli = QTextStream(&tempFile).readAll().trimmed().toInt();
gpu.temperature = tempMilli / 1000;
}
// 功耗 (µW -> W)
QFile powerFile(hwmonPath + "/power1_average");
if (powerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qint64 microWatts = QTextStream(&powerFile).readAll().trimmed().toLongLong();
gpu.powerDraw = microWatts / 1000000.0;
}
// 风扇转速 (RPM)
QFile fanFile(hwmonPath + "/fan1_input");
if (fanFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
gpu.fanSpeed = QTextStream(&fanFile).readAll().trimmed().toInt();
}
break;
}
}
}
}
// GPU 频率: Intel i915 使用 gt/gt0/rps_act_freq_mhzAMD 使用 hwmon freq1_input
QString gtPath = QString("/sys/class/drm/card%1/gt/gt0/rps_act_freq_mhz").arg(gpu.drmCard);
QFile gtFreqFile(gtPath);
if (gtFreqFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
gpu.gpuClock = QTextStream(&gtFreqFile).readAll().trimmed().toInt();
}
}
// --- 以下为 Intel 核显等无 hwmon/vram sysfs 接口时的回退逻辑 ---
// 温度回退:扫描 thermal_zone,匹配 Intel GPU 的 ACPI 标识 (B0D4)
// B0D4 是 Intel GPU 在 ACPI 规范中的标准热区标识
if (gpu.temperature < 0) {
QDir thermalDir("/sys/class/thermal");
QStringList zones = thermalDir.entryList(QStringList() << "thermal_zone*", QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &zone : zones) {
QFile typeFile(thermalDir.absoluteFilePath(zone + "/type"));
if (typeFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString type = QTextStream(&typeFile).readAll().trimmed();
if (type == "B0D4") {
QFile tempFile(thermalDir.absoluteFilePath(zone + "/temp"));
if (tempFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
int tempMilli = QTextStream(&tempFile).readAll().trimmed().toInt();
gpu.temperature = tempMilli / 1000;
}
break;
}
}
}
}
// 显存回退:Intel 核显使用共享系统内存,读取 /proc/meminfo 作为参考
// 注意:这是系统内存使用量,非核显独占,仅作参考
if (gpu.memTotal < 0) {
QFile meminfo("/proc/meminfo");
if (meminfo.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString content = QTextStream(&meminfo).readAll();
QRegularExpression totalRe("MemTotal:\\s*(\\d+)\\s*kB");
QRegularExpression availRe("MemAvailable:\\s*(\\d+)\\s*kB");
auto totalMatch = totalRe.match(content);
auto availMatch = availRe.match(content);
if (totalMatch.hasMatch()) {
gpu.memTotal = totalMatch.captured(1).toLongLong() / 1024; // kB -> MB
}
if (availMatch.hasMatch() && gpu.memTotal > 0) {
qint64 availMB = availMatch.captured(1).toLongLong() / 1024;
gpu.memUsed = gpu.memTotal - availMB;
if (gpu.memUsed >= 0) {
gpu.memUsage = gpu.memUsed * 100 / gpu.memTotal;
}
}
}
}
}
// 生成统计数据 (格式: temp|gpuUsage|memUsage|memUsed|memTotal)
// 生成统计数据 (格式: temp|gpuUsage|memUsage|memUsed|memTotal|gpuClock|memClock|powerDraw|fanSpeed)
m_gpuStats.clear();
for (const GpuInfo &gpu : m_gpus) {
m_gpuStats << QString("%1|%2|%3|%4|%5")
m_gpuStats << QString("%1|%2|%3|%4|%5|%6|%7|%8|%9")
.arg(gpu.temperature)
.arg(gpu.gpuUsage)
.arg(gpu.memUsage)
.arg(gpu.memUsed)
.arg(gpu.memTotal);
.arg(gpu.memTotal)
.arg(gpu.gpuClock)
.arg(gpu.memClock)
.arg(gpu.powerDraw, 0, 'f', 1)
.arg(gpu.fanSpeed);
}
emit gpuStatsChanged();
// 更新温度历史(保留最近 60 个采样点)
if (m_tempHistory.size() != m_gpus.size()) {
m_tempHistory.resize(m_gpus.size());
}
for (int i = 0; i < m_gpus.size(); ++i) {
m_tempHistory[i].append(m_gpus[i].temperature);
if (m_tempHistory[i].size() > 60) {
m_tempHistory[i].removeFirst();
}
}
emit tempHistoryChanged();
// 温度告警检测
checkTempAlert();
// 读取 NVIDIA 进程列表
readGpuProcesses();
}
// 温度告警:检测是否有 GPU 温度超过 80°C,30 秒内不重复告警
void GraphicsDriverApplet::checkTempAlert()
{
if (!m_tempAlertEnabled) return;
qint64 now = QDateTime::currentSecsSinceEpoch();
if (now - m_lastAlertTime < 30) return;
for (const GpuInfo &gpu : m_gpus) {
if (gpu.temperature >= 80) {
sendNotification(
tr("GPU Temperature Alert"),
tr("%1 temperature reached %2°C, please check cooling")
.arg(gpu.name)
.arg(gpu.temperature)
);
m_lastAlertTime = now;
break;
}
}
}
// 通过 D-Bus 发送桌面通知
void GraphicsDriverApplet::sendNotification(const QString &summary, const QString &body)
{
QDBusInterface notify("org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
QDBusConnection::sessionBus());
if (notify.isValid()) {
notify.call("Notify", "dde-shell-graphics-driver", 0u,
"dialog-warning", summary, body,
QStringList(), QVariantMap(), 5000);
}
}
// 读取 NVIDIA GPU 上运行的进程列表
void GraphicsDriverApplet::readGpuProcesses()
{
m_gpuProcesses.clear();
// 仅当存在 NVIDIA GPU 时查询
bool hasNvidia = false;
for (const GpuInfo &gpu : m_gpus) {
if (gpu.driver == "nvidia") { hasNvidia = true; break; }
}
if (!hasNvidia) {
emit gpuProcessesChanged();
return;
}
// nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv,noheader,nounits
QStringList lines = runCommand("nvidia-smi", {
"--query-compute-apps=pid,process_name,used_memory",
"--format=csv,noheader,nounits"
});
for (const QString &line : lines) {
QStringList parts = line.split(",");
if (parts.size() >= 3) {
m_gpuProcesses << QString("%1|%2|%3")
.arg(parts[0].trimmed())
.arg(parts[1].trimmed())
.arg(parts[2].trimmed());
}
}
emit gpuProcessesChanged();
}
// GPU 切换:调用 prime-select 切换 PRIME 模式(需要 polkit 提权)
bool GraphicsDriverApplet::switchGpu(const QString &mode)
{
// mode: "nvidia" / "intel" / "on-demand"
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)
+43
View File
@@ -9,6 +9,7 @@
#include <QString>
#include <QStringList>
#include <QTimer>
#include <QVariantList>
DS_BEGIN_NAMESPACE
@@ -26,6 +27,10 @@ struct GpuInfo {
int memUsage; // 显存使用率 (%),-1 表示不可用
int memTotal; // 显存总量 (MB),-1 表示不可用
int memUsed; // 显存已用 (MB),-1 表示不可用
int gpuClock; // GPU 频率 (MHz)-1 表示不可用
int memClock; // 显存频率 (MHz),-1 表示不可用
double powerDraw; // 功耗 (W)-1 表示不可用
int fanSpeed; // 风扇转速 (%),-1 表示不可用
};
class GraphicsDriverApplet : public DApplet
@@ -39,6 +44,12 @@ class GraphicsDriverApplet : public DApplet
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(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);
@@ -54,9 +65,19 @@ public:
QString gpuMode() const;
QStringList gpuPrimary() const;
bool ready() const;
QVariantList tempHistory() const;
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();
@@ -66,11 +87,23 @@ signals:
void gpuModeChanged();
void gpuPrimaryChanged();
void readyChanged();
void tempHistoryChanged();
void gpuProcessesChanged();
void tempAlertEnabledChanged();
void primeOffloadEnabledChanged();
void switchPendingChanged();
void restoreCountdownChanged();
private:
void detectGpus();
void readGpuStats();
void readGpuProcesses();
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);
@@ -84,6 +117,16 @@ private:
QString m_gpuMode;
QStringList m_gpuPrimary;
bool m_ready;
// 温度历史:每个 GPU 保留最近 60 个温度采样
QVector<QList<int>> m_tempHistory;
// NVIDIA 进程列表:每行 "pid|name|usedMemory"
QStringList m_gpuProcesses;
// 温度告警冷却时间戳
qint64 m_lastAlertTime;
bool m_tempAlertEnabled;
bool m_switchPending;
int m_restoreCountdown;
QTimer *m_restoreTimer;
};
DS_END_NAMESPACE
+909 -87
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>GraphicsDriverApplet</name>
<message>
<location filename="../graphicsdriverapplet.cpp" line="564"/>
<source>GPU Temperature Alert</source>
<translation>GPU Temperature Alert</translation>
</message>
<message>
<location filename="../graphicsdriverapplet.cpp" line="565"/>
<source>%1 temperature reached %2°C, please check cooling</source>
<translation>%1 temperature reached %2°C, please check cooling</translation>
</message>
</context>
<context>
<name>driverview</name>
<message>
<location filename="../package/driverview.qml" line="23"/>
<location filename="../package/driverview.qml" line="77"/>
<location filename="../package/driverview.qml" line="615"/>
<location filename="../package/driverview.qml" line="830"/>
<source>No GPU detected</source>
<translation>No GPU detected</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="24"/>
<location filename="../package/driverview.qml" line="25"/>
<location filename="../package/driverview.qml" line="364"/>
<location filename="../package/driverview.qml" line="802"/>
<source>Unknown</source>
<translation>Unknown</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="175"/>
<source>Graphics Driver Manager</source>
<translation>Graphics Driver Manager</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="198"/>
<source>Current Driver:</source>
<translation>Current Driver:</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="259"/>
<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 mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="261"/>
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
<translation>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="263"/>
<source>Solo mode: only one GPU detected.</source>
<translation>Solo mode: only one GPU detected.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="265"/>
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
<translation>Hybrid mode: dual GPU detected, but no known switching solution found.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="362"/>
<source>GPU: </source>
<translation>GPU: </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="363"/>
<source>Driver: </source>
<translation>Driver: </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="364"/>
<source>Version: </source>
<translation>Version: </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="426"/>
<source>Primary</source>
<translation>Primary</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="527"/>
<source>Fan</source>
<translation>Fan</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="640"/>
<source>GPU Processes</source>
<translation>GPU Processes</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="681"/>
<source>NVIDIA</source>
<translation>NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="702"/>
<source>Intel</source>
<translation>Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="723"/>
<source>On-Demand</source>
<translation>On-Demand</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="744"/>
<location filename="../package/driverview.qml" line="885"/>
<source>Refresh</source>
<translation>Refresh</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="892"/>
<source>Switch to NVIDIA</source>
<translation>Switch to NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="900"/>
<source>Switch to Intel</source>
<translation>Switch to Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="908"/>
<source>Switch to On-Demand</source>
<translation>Switch to On-Demand</translation>
</message>
</context>
</TS>
@@ -1,72 +1,237 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>GraphicsDriverApplet</name>
<message>
<location filename="../graphicsdriverapplet.cpp" line="564"/>
<source>GPU Temperature Alert</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../graphicsdriverapplet.cpp" line="565"/>
<source>%1 temperature reached %2°C, please check cooling</source>
<translation>%1 %2°C</translation>
</message>
</context>
<context>
<name>driverview</name>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="24"/>
<location filename="../package/driverview.qml" line="25"/>
<location filename="../package/driverview.qml" line="364"/>
<location filename="../package/driverview.qml" line="802"/>
<source>Unknown</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="23"/>
<location filename="../package/driverview.qml" line="77"/>
<location filename="../package/driverview.qml" line="615"/>
<location filename="../package/driverview.qml" line="830"/>
<source>No GPU detected</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="175"/>
<source>Graphics Driver Manager</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="198"/>
<source>Current Driver:</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="640"/>
<source>GPU Processes</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="681"/>
<source>NVIDIA</source>
<translation>NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="702"/>
<source>Intel</source>
<translation>Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="723"/>
<source>On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="744"/>
<location filename="../package/driverview.qml" line="885"/>
<source>Refresh</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="892"/>
<source>Switch to NVIDIA</source>
<translation> NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="900"/>
<source>Switch to Intel</source>
<translation> Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="908"/>
<source>Switch to On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="362"/>
<source>GPU: </source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="363"/>
<source>Driver: </source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="364"/>
<source>Version: </source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="426"/>
<source>Primary</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="259"/>
<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"/>
<location filename="../package/driverview.qml" line="261"/>
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
<translation>Bumblebee GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="263"/>
<source>Solo mode: only one GPU detected.</source>
<translation>Solo GPU</translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="265"/>
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
<translation>Hybrid GPU</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="527"/>
<source>Fan</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Settings</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>General Settings</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Show notification when GPU temperature exceeds threshold</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Alert Temperature Threshold</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Temperature Trend</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switch to</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switch to NVIDIA discrete GPU for maximum performance. Higher power consumption.</source>
<translation> NVIDIA </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switch to integrated GPU for power saving. NVIDIA GPU will be disabled.</source>
<translation>NVIDIA </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Hybrid mode: integrated GPU for display, NVIDIA for on-demand rendering.</source>
<translation>NVIDIA </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Auto-cancel if not confirmed</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Confirm</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Integrated</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Display OK?</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>GPU mode was changed. If display is abnormal, it will auto-revert in %1s.</source>
<translation>GPU %1 退</translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Revert</source>
<translation>退</translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Display OK</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>GPU Mode Switched</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switched to %1 mode. Please re-login to take effect.</source>
<translation> %1 </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>GPU Mode Restored</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Reverted to %1 mode. Please re-login to take effect.</source>
<translation> %1 </translation>
</message>
</context>
</TS>
@@ -1,72 +1,237 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_TW">
<context>
<name>GraphicsDriverApplet</name>
<message>
<location filename="../graphicsdriverapplet.cpp" line="564"/>
<source>GPU Temperature Alert</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../graphicsdriverapplet.cpp" line="565"/>
<source>%1 temperature reached %2°C, please check cooling</source>
<translation>%1 %2°C</translation>
</message>
</context>
<context>
<name>driverview</name>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="24"/>
<location filename="../package/driverview.qml" line="25"/>
<location filename="../package/driverview.qml" line="364"/>
<location filename="../package/driverview.qml" line="802"/>
<source>Unknown</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="23"/>
<location filename="../package/driverview.qml" line="77"/>
<location filename="../package/driverview.qml" line="615"/>
<location filename="../package/driverview.qml" line="830"/>
<source>No GPU detected</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="175"/>
<source>Graphics Driver Manager</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="198"/>
<source>Current Driver:</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="640"/>
<source>GPU Processes</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="681"/>
<source>NVIDIA</source>
<translation>NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="702"/>
<source>Intel</source>
<translation>Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="723"/>
<source>On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="744"/>
<location filename="../package/driverview.qml" line="885"/>
<source>Refresh</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="892"/>
<source>Switch to NVIDIA</source>
<translation> NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="900"/>
<source>Switch to Intel</source>
<translation> Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="908"/>
<source>Switch to On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="362"/>
<source>GPU: </source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="363"/>
<source>Driver: </source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="364"/>
<source>Version: </source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="426"/>
<source>Primary</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="259"/>
<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"/>
<location filename="../package/driverview.qml" line="261"/>
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
<translation>Bumblebee GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="263"/>
<source>Solo mode: only one GPU detected.</source>
<translation>Solo GPU</translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<location filename="../package/driverview.qml" line="265"/>
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
<translation>Hybrid GPU</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="527"/>
<source>Fan</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Settings</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>General Settings</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Show notification when GPU temperature exceeds threshold</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Alert Temperature Threshold</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Temperature Trend</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switch to</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switch to NVIDIA discrete GPU for maximum performance. Higher power consumption.</source>
<translation> NVIDIA </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switch to integrated GPU for power saving. NVIDIA GPU will be disabled.</source>
<translation>NVIDIA </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Hybrid mode: integrated GPU for display, NVIDIA for on-demand rendering.</source>
<translation>NVIDIA </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Auto-cancel if not confirmed</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Confirm</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Integrated</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Display OK?</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>GPU mode was changed. If display is abnormal, it will auto-revert in %1s.</source>
<translation>GPU %1 退</translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Revert</source>
<translation>退</translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Display OK</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>GPU Mode Switched</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Switched to %1 mode. Please re-login to take effect.</source>
<translation> %1 </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>GPU Mode Restored</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml"/>
<source>Reverted to %1 mode. Please re-login to take effect.</source>
<translation> %1 </translation>
</message>
</context>
</TS>