ScreenLockDetector/docs/ARCHITECTURE.md

15 KiB
Raw Blame History

项目架构说明

概述

本项目是一个基于 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 信号

关键方法

bool initialize()                    // 初始化 DBus 连接
bool isScreenLocked() const          // 查询当前锁屏状态
bool connectToGnomeScreenSaver()     // 连接 GNOME 接口
bool connectToLoginManager()         // 连接 systemd-logind 接口
void queryCurrentLockState()         // 查询初始状态

信号

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

关键方法

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 信号槽机制

// 连接示例
connect(m_lockDetector, &ScreenLockDetector::screenLocked,
        this, &MainWindow::onScreenLocked);

// 优势:
// - 类型安全
// - 解耦设计
// - 自动生命周期管理

DBus 连接管理

// 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 事件优化

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)

编译流程

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 接口

// 在 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;
}

添加新的动画效果

// 在 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);
}

测试策略

单元测试建议

// 测试锁屏检测器
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. 重置计数 → 验证统计准确

故障处理

错误处理策略

bool ScreenLockDetector::initialize()
{
    bool gnomeOk = connectToGnomeScreenSaver();
    bool loginOk = connectToLoginManager();

    // 容错:至少一个接口成功即可
    if (!gnomeOk && !loginOk) {
        qWarning() << "No lock detection service available";
        return false;
    }

    // 降级:查询当前状态(可能失败)
    queryCurrentLockState();

    return true;
}

日志级别

qDebug()   // 调试信息(开发)
qInfo()    // 一般信息
qWarning() // 警告信息(功能降级)
qCritical()// 严重错误

最佳实践

内存管理

  • 使用 Qt 父子对象机制自动管理内存
  • 显式 delete 前先判空
  • 使用智能指针(如需要)

信号槽

  • 优先使用新式语法(函数指针)
  • 避免跨线程信号(本项目单线程)
  • 断开不再需要的连接

绘制优化

  • 使用 update() 而非 repaint()
  • 避免在 paintEvent 中进行重计算
  • 使用抗锯齿适度(性能影响)

总结

本项目展示了:

  • Qt5 应用程序开发
  • DBus 系统集成
  • 信号槽机制运用
  • 自定义绘制实现
  • 事件驱动架构
  • 跨组件通信
  • 资源优化策略

适合作为 Qt + Linux 系统编程的学习参考。