ScreenLockDetector/LOCK_SIGNAL_FIX.md

9.1 KiB
Raw Blame History

锁屏信号未触发问题修复指南

问题描述

程序已成功连接到目标系统的 DBus 服务,但是系统锁屏时 onSessionLocked() 没有被调用。 此问题在 Ubuntu 和 Deepin 系统上都存在。

问题原因

1. Session 路径问题

原代码使用了固定路径 /org/freedesktop/login1/session/auto,但这个路径在大多数 Linux 系统上不起作用。需要使用实际的会话 ID 路径,例如:

  • /org/freedesktop/login1/session/c1
  • /org/freedesktop/login1/session/2

2. 信号监听范围太窄

原代码只监听特定路径的信号,如果实际信号从其他路径发出,就无法接收到。

3. 缺少详细调试信息

无法判断到底是哪个环节出了问题:连接失败、信号未发出、还是信号发送者不匹配。

修复方案

主要改进

1. 动态获取 Session 路径

新增 getCurrentSessionPath() 方法,通过以下方式获取正确的会话路径:

QString ScreenLockDetector::getCurrentSessionPath()
{
    // 方法1: 从环境变量获取
    QString xdgSessionId = qgetenv("XDG_SESSION_ID");
    if (!xdgSessionId.isEmpty()) {
        return QString("/org/freedesktop/login1/session/%1").arg(xdgSessionId);
    }
    
    // 方法2: 通过 DBus 调用获取
    QDBusInterface managerIface(
        "org.freedesktop.login1",
        "/org/freedesktop/login1",
        "org.freedesktop.login1.Manager",
        QDBusConnection::systemBus()
    );
    
    if (managerIface.isValid()) {
        QDBusReply<QDBusObjectPath> reply = 
            managerIface.call("GetSessionByPID", 
                             (quint32)QCoreApplication::applicationPid());
        if (reply.isValid()) {
            return reply.value().path();
        }
    }
    
    return QString();
}

2. 双重信号连接策略

先尝试连接到特定会话路径,如果失败则连接到通用信号(空路径):

// 方法1: 连接到特定会话
QDBusConnection::systemBus().connect(
    "org.freedesktop.login1",
    sessionPath,              // 具体路径
    "org.freedesktop.login1.Session",
    "Lock",
    this,
    SLOT(onSessionLocked())
);

// 方法2: 连接到所有会话(备用方案)
QDBusConnection::systemBus().connect(
    "org.freedesktop.login1",
    "",                       // 空路径 = 监听所有对象
    "org.freedesktop.login1.Session",
    "Lock",
    this,
    SLOT(onSessionLocked())
);

3. 增强的调试信息

  • 继承 QDBusContext 以获取信号发送者详细信息
  • 在所有关键点添加详细日志输出
  • 显示信号的 service、path、interface 和 member
void ScreenLockDetector::onSessionLocked()
{
    qDebug() << "##################################################";
    qDebug() << "## LOCK SIGNAL RECEIVED";
    
    QDBusMessage msg = QDBusContext::message();
    if (msg.type() != QDBusMessage::InvalidMessage) {
        qDebug() << "## DBus Message Details:";
        qDebug() << "##   Service:" << msg.service();
        qDebug() << "##   Path:" << msg.path();
        qDebug() << "##   Interface:" << msg.interface();
        qDebug() << "##   Member:" << msg.member();
    }
    
    qDebug() << "##################################################";
    setLockState(true);
}

4. 改进的状态查询

增强 queryCurrentLockState() 方法,正确读取 systemd-logind 的 LockedHint 属性:

QDBusMessage msg = QDBusMessage::createMethodCall(
    "org.freedesktop.login1",
    m_loginInterface->path(),
    "org.freedesktop.DBus.Properties",
    "Get"
);
msg << "org.freedesktop.login1.Session" << "LockedHint";

QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
if (reply.isValid()) {
    bool locked = reply.value().toBool();
    setLockState(locked);
}

使用修复后的代码

1. 重新编译

./build.sh

2. 运行程序并观察日志

./run.sh

程序启动时会输出详细的连接信息:

=================================================
Initializing ScreenLockDetector...
=================================================

--- Connecting to Deepin DDE ---
Trying Deepin service: com.deepin.dde.lockFront
...

--- Connecting to GNOME ScreenSaver ---
GNOME ScreenSaver interface is valid
GNOME ActiveChanged signal connected: true
...

--- Connecting to Login Manager (systemd-logind) ---
Session path from XDG_SESSION_ID: /org/freedesktop/login1/session/c1
Current session path: /org/freedesktop/login1/session/c1
Login Manager interface is valid for session: /org/freedesktop/login1/session/c1
Session Lock signal connected: true
Session Unlock signal connected: true
...

3. 测试锁屏

锁定屏幕后,应该能看到:

##################################################
## LOCK SIGNAL RECEIVED
## Screen is now LOCKED
## DBus Message Details:
##   Service: :1.43
##   Path: /org/freedesktop/login1/session/c1
##   Interface: org.freedesktop.login1.Session
##   Member: Lock
##################################################

调试工具

工具 1: test_lock_signals.sh

这是一个全面的 DBus 信号监听脚本,用于诊断问题:

./test_lock_signals.sh

功能:

  1. 显示当前会话信息XDG_SESSION_ID 等)
  2. 检查可用的锁屏服务
  3. 列出会话支持的所有信号
  4. 实时监听 session bus 和 system bus 的锁屏信号
  5. 保存日志供后续分析

使用方法:

  1. 运行脚本
  2. 按照提示锁定/解锁屏幕
  3. 观察输出的信号
  4. 按 Ctrl+C 停止

工具 2: debug_deepin_dbus.sh

专门用于 Deepin 系统的调试脚本:

./debug_deepin_dbus.sh

验证修复

检查列表

  • 程序启动时显示"ScreenLockDetector initialized successfully"
  • 至少有一个接口显示"connected: true"
  • 显示了正确的 session 路径(不是 "auto"
  • 锁屏时看到 "LOCK SIGNAL RECEIVED" 消息
  • 解锁时看到 "UNLOCK SIGNAL RECEIVED" 消息
  • 主窗口状态正确更新

如果仍然无法接收信号

  1. 运行 test_lock_signals.sh

    ./test_lock_signals.sh
    

    观察系统实际发出了什么信号

  2. 检查会话路径

    echo $XDG_SESSION_ID
    loginctl show-session $XDG_SESSION_ID
    
  3. 手动测试 DBus 连接

    # 监听 system bus 的 Lock 信号
    dbus-monitor --system "type='signal',interface='org.freedesktop.login1.Session',member='Lock'"
    
    # 在另一个终端锁屏
    loginctl lock-session
    
  4. 检查权限 确保用户有权限访问 system bus

    groups $USER
    

    用户应该在 sudowheel 组中

  5. 对于 Deepin 系统

    # 列出所有 Deepin 相关服务
    dbus-send --session --print-reply --dest=org.freedesktop.DBus \
        /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep deepin
    
    # 查看 lockFront 的接口
    dbus-send --session --print-reply --dest=com.deepin.dde.lockFront \
        /com/deepin/dde/lockFront org.freedesktop.DBus.Introspectable.Introspect
    

不同系统的特殊情况

Ubuntu / GNOME

  • 主要通过 org.gnome.ScreenSaverActiveChanged 信号
  • 或通过 systemd-logind 的 Lock/Unlock 信号

Deepin DDE

  • 服务名可能是 com.deepin.dde.lockFrontorg.deepin.dde.lockFront
  • 信号名可能是 Locked/UnlockedLock/Unlock
  • 需要通过 session bus 连接

KDE Plasma

  • 使用 org.freedesktop.ScreenSaver
  • 也支持 systemd-logind

XFCE

  • 主要依赖 systemd-logind
  • org.xfce.ScreenSaver

常见问题

Q: 为什么连接显示成功但没有收到信号?

A: 可能的原因:

  1. 连接到了错误的会话路径
  2. 桌面环境使用了不同的锁屏机制
  3. 信号签名不匹配(参数类型不对)

解决方法:使用 test_lock_signals.sh 查看实际信号。

Q: 在虚拟机中测试是否会有问题?

A: 可能会。某些虚拟机可能没有完整的 session 管理。建议:

  • 确保虚拟机安装了完整的桌面环境
  • 使用 GUI 登录而不是 SSH
  • 检查 loginctl list-sessions 是否显示你的会话

Q: Deepin 系统上还是不工作怎么办?

A: Deepin 不同版本的接口可能不同:

  1. 运行 debug_deepin_dbus.sh 找到正确的服务名
  2. 修改 connectToDeepinDDE() 中的服务列表
  3. 添加找到的服务配置
  4. 重新编译测试

技术参考

DBus 接口文档

Qt DBus 文档

总结

修复的核心要点:

  1. 动态获取会话路径 - 不要硬编码 "auto"
  2. 多重连接策略 - 特定路径 + 通用监听
  3. 详细调试日志 - 便于诊断问题
  4. 正确读取属性 - 使用 Properties 接口
  5. 支持多种桌面环境 - Deepin/GNOME/KDE 等

这些修复应该能解决大多数 Linux 系统上的锁屏信号接收问题。