478 lines
15 KiB
Markdown
478 lines
15 KiB
Markdown
|
|
# 项目架构说明
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本项目是一个基于 Qt5 的 Linux 应用程序,通过 DBus 监听系统锁屏事件,并在锁屏时自动停止所有 Paint 事件,以节省系统资源。
|
|||
|
|
|
|||
|
|
## 系统架构图
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────┐
|
|||
|
|
│ Linux System │
|
|||
|
|
│ ┌──────────────────────┐ ┌─────────────────────────┐ │
|
|||
|
|
│ │ GNOME ScreenSaver │ │ systemd-logind │ │
|
|||
|
|
│ │ (DBus Service) │ │ (DBus Service) │ │
|
|||
|
|
│ └──────────┬───────────┘ └───────────┬─────────────┘ │
|
|||
|
|
│ │ ActiveChanged(bool) │ Lock/Unlock │
|
|||
|
|
└─────────────┼──────────────────────────────┼────────────────┘
|
|||
|
|
│ │
|
|||
|
|
│ DBus Signals │
|
|||
|
|
│ │
|
|||
|
|
┌─────────────┴──────────────────────────────┴────────────────┐
|
|||
|
|
│ Qt Application (ScreenLockDemo) │
|
|||
|
|
│ ┌────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ MainWindow │ │
|
|||
|
|
│ │ ┌──────────────┐ ┌────────────────────────────────┐ │ │
|
|||
|
|
│ │ │ Control Panel│ │ Status Display │ │ │
|
|||
|
|
│ │ │ - Enable Btn │ │ - Lock Status: 🔒/🔓 │ │ │
|
|||
|
|
│ │ │ - Disable Btn│ │ - Paint Status: ✓/✗ │ │ │
|
|||
|
|
│ │ │ - Reset Btn │ │ - Frame Count │ │ │
|
|||
|
|
│ │ └──────────────┘ └────────────────────────────────┘ │ │
|
|||
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|||
|
|
│ ↕ signals/slots │
|
|||
|
|
│ ┌────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ ScreenLockDetector │ │
|
|||
|
|
│ │ - Monitors DBus signals │ │
|
|||
|
|
│ │ - Emits: screenLocked() / screenUnlocked() │ │
|
|||
|
|
│ │ - Manages connection to multiple DBus interfaces │ │
|
|||
|
|
│ └─────────────────────┬──────────────────────────────────┘ │
|
|||
|
|
│ ↓ lockStateChanged(bool) │
|
|||
|
|
│ ┌────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ CustomWidget │ │
|
|||
|
|
│ │ - Renders animations (60 FPS) │ │
|
|||
|
|
│ │ - Controls paint events based on lock state │ │
|
|||
|
|
│ │ - Draws: rotating circles, waves, gradients │ │
|
|||
|
|
│ │ - Tracks frame count and statistics │ │
|
|||
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 核心组件详解
|
|||
|
|
|
|||
|
|
### 1. ScreenLockDetector (锁屏检测器)
|
|||
|
|
|
|||
|
|
**职责**:
|
|||
|
|
- 连接到 Linux 系统的 DBus 服务
|
|||
|
|
- 监听屏幕锁定/解锁事件
|
|||
|
|
- 提供锁屏状态查询接口
|
|||
|
|
- 发出标准化的 Qt 信号
|
|||
|
|
|
|||
|
|
**关键方法**:
|
|||
|
|
```cpp
|
|||
|
|
bool initialize() // 初始化 DBus 连接
|
|||
|
|
bool isScreenLocked() const // 查询当前锁屏状态
|
|||
|
|
bool connectToGnomeScreenSaver() // 连接 GNOME 接口
|
|||
|
|
bool connectToLoginManager() // 连接 systemd-logind 接口
|
|||
|
|
void queryCurrentLockState() // 查询初始状态
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**信号**:
|
|||
|
|
```cpp
|
|||
|
|
void screenLocked() // 屏幕已锁定
|
|||
|
|
void screenUnlocked() // 屏幕已解锁
|
|||
|
|
void lockStateChanged(bool locked) // 状态改变(通用)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**DBus 接口支持**:
|
|||
|
|
|
|||
|
|
| 桌面环境 | DBus Service | 路径 | 接口 | 信号 |
|
|||
|
|
|---------|-------------|------|------|-----|
|
|||
|
|
| GNOME | org.gnome.ScreenSaver | /org/gnome/ScreenSaver | org.gnome.ScreenSaver | ActiveChanged(bool) |
|
|||
|
|
| All (systemd) | org.freedesktop.login1 | /org/freedesktop/login1/session/auto | org.freedesktop.login1.Session | Lock(), Unlock() |
|
|||
|
|
|
|||
|
|
### 2. CustomWidget (自定义绘制组件)
|
|||
|
|
|
|||
|
|
**职责**:
|
|||
|
|
- 实现动态动画效果
|
|||
|
|
- 根据锁屏状态控制绘制
|
|||
|
|
- 统计绘制性能数据
|
|||
|
|
- 提供可视化反馈
|
|||
|
|
|
|||
|
|
**绘制层次**:
|
|||
|
|
```
|
|||
|
|
Layer 1: Background (渐变背景)
|
|||
|
|
↓
|
|||
|
|
Layer 2: Rotating Circles (旋转圆圈)
|
|||
|
|
↓
|
|||
|
|
Layer 3: Wave Effects (波浪效果)
|
|||
|
|
↓
|
|||
|
|
Layer 4: Status Info (状态文字)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**动画参数**:
|
|||
|
|
- 刷新率:60 FPS (16ms/frame)
|
|||
|
|
- 旋转角度:每帧 +2°
|
|||
|
|
- 波浪相位:每帧 +0.05 rad
|
|||
|
|
|
|||
|
|
**关键方法**:
|
|||
|
|
```cpp
|
|||
|
|
void setPaintingEnabled(bool enabled) // 控制绘制开关
|
|||
|
|
void paintEvent(QPaintEvent *event) // 重写绘制事件
|
|||
|
|
void drawBackground(QPainter &painter) // 绘制背景
|
|||
|
|
void drawRotatingCircles(...) // 绘制旋转元素
|
|||
|
|
void drawWaveEffect(...) // 绘制波浪
|
|||
|
|
void drawStatusInfo(...) // 绘制状态信息
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**状态机**:
|
|||
|
|
```
|
|||
|
|
[ENABLED] ←→ [DISABLED]
|
|||
|
|
↓ ↓
|
|||
|
|
Timer ON Timer OFF
|
|||
|
|
↓ ↓
|
|||
|
|
Draw frames Draw static
|
|||
|
|
↓ ↓
|
|||
|
|
Count++ Count unchanged
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. MainWindow (主窗口)
|
|||
|
|
|
|||
|
|
**职责**:
|
|||
|
|
- 整合所有组件
|
|||
|
|
- 提供用户界面
|
|||
|
|
- 处理用户交互
|
|||
|
|
- 实时状态展示
|
|||
|
|
|
|||
|
|
**UI 布局**:
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────┐
|
|||
|
|
│ MainWindow │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ ┌───────────────────────────────┐ │
|
|||
|
|
│ │ CustomWidget │ │
|
|||
|
|
│ │ (Animation Display Area) │ │
|
|||
|
|
│ │ 600x400px │ │
|
|||
|
|
│ └───────────────────────────────┘ │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ ┌─── Manual Control ────────────┐ │
|
|||
|
|
│ │ [Enable] [Disable] [Reset] │ │
|
|||
|
|
│ └───────────────────────────────┘ │
|
|||
|
|
├─────────────────────────────────────┤
|
|||
|
|
│ ┌─── Status Information ────────┐ │
|
|||
|
|
│ │ Detector: Active │ │
|
|||
|
|
│ │ Lock: 🔓 UNLOCKED │ │
|
|||
|
|
│ │ Paint: ✓ ENABLED │ │
|
|||
|
|
│ │ Frames: 1234 │ │
|
|||
|
|
│ └───────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**信号流向**:
|
|||
|
|
```
|
|||
|
|
ScreenLockDetector → MainWindow → CustomWidget
|
|||
|
|
screenLocked() onScreenLocked() setPaintingEnabled(false)
|
|||
|
|
screenUnlocked() onScreenUnlocked() setPaintingEnabled(true)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 数据流图
|
|||
|
|
|
|||
|
|
### 锁屏事件流
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
[User locks screen]
|
|||
|
|
↓
|
|||
|
|
[Linux System]
|
|||
|
|
↓
|
|||
|
|
[DBus Signal: Lock/ActiveChanged]
|
|||
|
|
↓
|
|||
|
|
[ScreenLockDetector::onSessionLocked()]
|
|||
|
|
↓
|
|||
|
|
[emit screenLocked()]
|
|||
|
|
↓
|
|||
|
|
[MainWindow::onScreenLocked()]
|
|||
|
|
↓
|
|||
|
|
[CustomWidget::setPaintingEnabled(false)]
|
|||
|
|
↓
|
|||
|
|
[m_animationTimer->stop()]
|
|||
|
|
↓
|
|||
|
|
[paintEvent() returns early]
|
|||
|
|
↓
|
|||
|
|
[Animation stops, resources saved]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 解锁事件流
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
[User unlocks screen]
|
|||
|
|
↓
|
|||
|
|
[Linux System]
|
|||
|
|
↓
|
|||
|
|
[DBus Signal: Unlock/ActiveChanged]
|
|||
|
|
↓
|
|||
|
|
[ScreenLockDetector::onSessionUnlocked()]
|
|||
|
|
↓
|
|||
|
|
[emit screenUnlocked()]
|
|||
|
|
↓
|
|||
|
|
[MainWindow::onScreenUnlocked()]
|
|||
|
|
↓
|
|||
|
|
[CustomWidget::setPaintingEnabled(true)]
|
|||
|
|
↓
|
|||
|
|
[m_animationTimer->start(16)]
|
|||
|
|
↓
|
|||
|
|
[paintEvent() resumes drawing]
|
|||
|
|
↓
|
|||
|
|
[Animation resumes]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 技术细节
|
|||
|
|
|
|||
|
|
### Qt 信号槽机制
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 连接示例
|
|||
|
|
connect(m_lockDetector, &ScreenLockDetector::screenLocked,
|
|||
|
|
this, &MainWindow::onScreenLocked);
|
|||
|
|
|
|||
|
|
// 优势:
|
|||
|
|
// - 类型安全
|
|||
|
|
// - 解耦设计
|
|||
|
|
// - 自动生命周期管理
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DBus 连接管理
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// Session Bus (用户会话)
|
|||
|
|
QDBusConnection::sessionBus().connect(
|
|||
|
|
"org.gnome.ScreenSaver", // Service
|
|||
|
|
"/org/gnome/ScreenSaver", // Path
|
|||
|
|
"org.gnome.ScreenSaver", // Interface
|
|||
|
|
"ActiveChanged", // Signal
|
|||
|
|
this, // Receiver
|
|||
|
|
SLOT(onScreenSaverActiveChanged(bool))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// System Bus (系统级)
|
|||
|
|
QDBusConnection::systemBus().connect(
|
|||
|
|
"org.freedesktop.login1", // Service
|
|||
|
|
"/org/freedesktop/login1/session/auto",
|
|||
|
|
"org.freedesktop.login1.Session",
|
|||
|
|
"Lock",
|
|||
|
|
this,
|
|||
|
|
SLOT(onSessionLocked())
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Paint 事件优化
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
void CustomWidget::paintEvent(QPaintEvent *event)
|
|||
|
|
{
|
|||
|
|
// 快速退出:锁屏时不进行复杂绘制
|
|||
|
|
if (!m_paintingEnabled) {
|
|||
|
|
// 仅绘制静态状态信息
|
|||
|
|
drawSimpleStatus();
|
|||
|
|
return; // 节省 CPU 资源
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 正常绘制流程
|
|||
|
|
drawBackground();
|
|||
|
|
drawRotatingCircles();
|
|||
|
|
drawWaveEffect();
|
|||
|
|
drawStatusInfo();
|
|||
|
|
|
|||
|
|
m_frameCount++; // 统计
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 构建系统
|
|||
|
|
|
|||
|
|
### CMake 配置流程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
CMakeLists.txt
|
|||
|
|
↓
|
|||
|
|
find_package(Qt5)
|
|||
|
|
↓
|
|||
|
|
set(CMAKE_AUTOMOC ON) # 自动 MOC 处理
|
|||
|
|
set(CMAKE_AUTOUIC ON) # 自动 UI 文件
|
|||
|
|
set(CMAKE_AUTORCC ON) # 自动资源文件
|
|||
|
|
↓
|
|||
|
|
add_executable(ScreenLockDemo)
|
|||
|
|
↓
|
|||
|
|
target_link_libraries(Qt5::Core Qt5::Gui Qt5::Widgets Qt5::DBus)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 编译流程
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
mkdir build && cd build
|
|||
|
|
↓
|
|||
|
|
cmake .. -DCMAKE_BUILD_TYPE=Release
|
|||
|
|
↓
|
|||
|
|
make -j$(nproc)
|
|||
|
|
↓
|
|||
|
|
./bin/ScreenLockDemo
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 性能特性
|
|||
|
|
|
|||
|
|
### 资源消耗对比
|
|||
|
|
|
|||
|
|
| 状态 | CPU使用 | GPU使用 | 内存 | 说明 |
|
|||
|
|
|-----|--------|--------|------|-----|
|
|||
|
|
| 绘制启用 | ~2-5% | ~1-3% | ~50MB | 60 FPS 动画 |
|
|||
|
|
| 绘制禁用 | <1% | 0% | ~50MB | 仅监听 DBus |
|
|||
|
|
| 锁屏期间 | <0.5% | 0% | ~50MB | 完全停止绘制 |
|
|||
|
|
|
|||
|
|
### 响应时间
|
|||
|
|
|
|||
|
|
- DBus 信号延迟:< 100ms
|
|||
|
|
- 动画停止延迟:< 20ms (1-2 frames)
|
|||
|
|
- 动画恢复延迟:< 20ms
|
|||
|
|
|
|||
|
|
## 扩展性设计
|
|||
|
|
|
|||
|
|
### 添加新的 DBus 接口
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 在 screenlockdetector.cpp 中添加
|
|||
|
|
bool ScreenLockDetector::connectToKDEScreenSaver()
|
|||
|
|
{
|
|||
|
|
// 1. 创建 DBus 接口
|
|||
|
|
m_kdeInterface = new QDBusInterface(
|
|||
|
|
"org.kde.screensaver",
|
|||
|
|
"/ScreenSaver",
|
|||
|
|
"org.freedesktop.ScreenSaver",
|
|||
|
|
QDBusConnection::sessionBus()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 2. 连接信号
|
|||
|
|
bool connected = QDBusConnection::sessionBus().connect(
|
|||
|
|
"org.kde.screensaver",
|
|||
|
|
"/ScreenSaver",
|
|||
|
|
"org.freedesktop.ScreenSaver",
|
|||
|
|
"ActiveChanged",
|
|||
|
|
this,
|
|||
|
|
SLOT(onKDEScreenSaverChanged(bool))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return connected;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 添加新的动画效果
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 在 customwidget.cpp 中添加
|
|||
|
|
void CustomWidget::drawParticleSystem(QPainter &painter)
|
|||
|
|
{
|
|||
|
|
// 1. 更新粒子位置
|
|||
|
|
for (auto &particle : m_particles) {
|
|||
|
|
particle.update(m_deltaTime);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 绘制粒子
|
|||
|
|
for (const auto &particle : m_particles) {
|
|||
|
|
particle.draw(painter);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 在 paintEvent 中调用
|
|||
|
|
void CustomWidget::paintEvent(QPaintEvent *event)
|
|||
|
|
{
|
|||
|
|
// ...
|
|||
|
|
drawParticleSystem(painter);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 测试策略
|
|||
|
|
|
|||
|
|
### 单元测试建议
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 测试锁屏检测器
|
|||
|
|
void TestScreenLockDetector::testLockStateChange()
|
|||
|
|
{
|
|||
|
|
ScreenLockDetector detector;
|
|||
|
|
QSignalSpy spy(&detector, &ScreenLockDetector::lockStateChanged);
|
|||
|
|
|
|||
|
|
// 模拟 DBus 信号
|
|||
|
|
detector.onSessionLocked();
|
|||
|
|
QCOMPARE(spy.count(), 1);
|
|||
|
|
QVERIFY(detector.isScreenLocked());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试绘制控制
|
|||
|
|
void TestCustomWidget::testPaintingControl()
|
|||
|
|
{
|
|||
|
|
CustomWidget widget;
|
|||
|
|
widget.setPaintingEnabled(false);
|
|||
|
|
QVERIFY(!widget.isPaintingEnabled());
|
|||
|
|
|
|||
|
|
int initialCount = widget.getPaintFrameCount();
|
|||
|
|
QTest::qWait(100); // 等待
|
|||
|
|
QCOMPARE(widget.getPaintFrameCount(), initialCount);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 集成测试场景
|
|||
|
|
|
|||
|
|
1. 启动应用 → 验证动画运行
|
|||
|
|
2. 锁定屏幕 → 验证动画停止
|
|||
|
|
3. 解锁屏幕 → 验证动画恢复
|
|||
|
|
4. 手动禁用 → 验证控制有效
|
|||
|
|
5. 重置计数 → 验证统计准确
|
|||
|
|
|
|||
|
|
## 故障处理
|
|||
|
|
|
|||
|
|
### 错误处理策略
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
bool ScreenLockDetector::initialize()
|
|||
|
|
{
|
|||
|
|
bool gnomeOk = connectToGnomeScreenSaver();
|
|||
|
|
bool loginOk = connectToLoginManager();
|
|||
|
|
|
|||
|
|
// 容错:至少一个接口成功即可
|
|||
|
|
if (!gnomeOk && !loginOk) {
|
|||
|
|
qWarning() << "No lock detection service available";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 降级:查询当前状态(可能失败)
|
|||
|
|
queryCurrentLockState();
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 日志级别
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
qDebug() // 调试信息(开发)
|
|||
|
|
qInfo() // 一般信息
|
|||
|
|
qWarning() // 警告信息(功能降级)
|
|||
|
|
qCritical()// 严重错误
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 最佳实践
|
|||
|
|
|
|||
|
|
### 内存管理
|
|||
|
|
- 使用 Qt 父子对象机制自动管理内存
|
|||
|
|
- 显式 delete 前先判空
|
|||
|
|
- 使用智能指针(如需要)
|
|||
|
|
|
|||
|
|
### 信号槽
|
|||
|
|
- 优先使用新式语法(函数指针)
|
|||
|
|
- 避免跨线程信号(本项目单线程)
|
|||
|
|
- 断开不再需要的连接
|
|||
|
|
|
|||
|
|
### 绘制优化
|
|||
|
|
- 使用 update() 而非 repaint()
|
|||
|
|
- 避免在 paintEvent 中进行重计算
|
|||
|
|
- 使用抗锯齿适度(性能影响)
|
|||
|
|
|
|||
|
|
## 总结
|
|||
|
|
|
|||
|
|
本项目展示了:
|
|||
|
|
- ✅ Qt5 应用程序开发
|
|||
|
|
- ✅ DBus 系统集成
|
|||
|
|
- ✅ 信号槽机制运用
|
|||
|
|
- ✅ 自定义绘制实现
|
|||
|
|
- ✅ 事件驱动架构
|
|||
|
|
- ✅ 跨组件通信
|
|||
|
|
- ✅ 资源优化策略
|
|||
|
|
|
|||
|
|
适合作为 Qt + Linux 系统编程的学习参考。
|