Add macOS support and cross-platform CMake

This commit is contained in:
hoenking 2025-11-08 16:57:58 +08:00
parent 9248f3945d
commit 471b4c7b3b
9 changed files with 942 additions and 70 deletions

View File

@ -12,32 +12,94 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# Set Qt5 path
set(Qt5_DIR "$ENV{HOME}/sdk/qt-5.15.2/lib/cmake/Qt5")
# Platform detection
if(APPLE)
message(STATUS "Building for macOS")
set(PLATFORM_NAME "macOS")
elseif(UNIX)
message(STATUS "Building for Linux")
set(PLATFORM_NAME "Linux")
else()
message(WARNING "Unsupported platform")
endif()
# Set Qt5 path (customize this based on your installation)
if(APPLE)
# macOS: Try common Qt installation paths
if(NOT DEFINED Qt5_DIR)
# Check MacPorts installation
if(EXISTS "/opt/local/lib/cmake/Qt5")
set(Qt5_DIR "/opt/local/lib/cmake/Qt5")
# Check Homebrew installation (Intel)
elseif(EXISTS "/usr/local/opt/qt@5/lib/cmake/Qt5")
set(Qt5_DIR "/usr/local/opt/qt@5/lib/cmake/Qt5")
# Check Homebrew installation (Apple Silicon)
elseif(EXISTS "/opt/homebrew/opt/qt@5/lib/cmake/Qt5")
set(Qt5_DIR "/opt/homebrew/opt/qt@5/lib/cmake/Qt5")
# Check official Qt installer
elseif(EXISTS "$ENV{HOME}/Qt/5.15.2/clang_64/lib/cmake/Qt5")
set(Qt5_DIR "$ENV{HOME}/Qt/5.15.2/clang_64/lib/cmake/Qt5")
# Check custom installation
elseif(EXISTS "$ENV{HOME}/sdk/qt-5.15.2/lib/cmake/Qt5")
set(Qt5_DIR "$ENV{HOME}/sdk/qt-5.15.2/lib/cmake/Qt5")
endif()
endif()
else()
# Linux: Use custom path
if(NOT DEFINED Qt5_DIR)
set(Qt5_DIR "$ENV{HOME}/sdk/qt-5.15.2/lib/cmake/Qt5")
endif()
endif()
# Find Qt5 packages
find_package(Qt5 REQUIRED COMPONENTS
Core
Gui
Widgets
DBus
)
set(QT_COMPONENTS Core Gui Widgets)
# Source files
if(UNIX AND NOT APPLE)
list(APPEND QT_COMPONENTS DBus)
endif()
find_package(Qt5 REQUIRED COMPONENTS ${QT_COMPONENTS})
# Common source files
set(SOURCES
src/main.cpp
src/mainwindow.cpp
src/screenlockdetector.cpp
src/customwidget.cpp
)
# Header files
set(HEADERS
src/mainwindow.h
src/screenlockdetector.h
src/customwidget.h
)
# Platform-specific source files
if(APPLE)
# macOS specific files
list(APPEND SOURCES
src/screenlockdetector.cpp
src/screenlockdetector_mac.mm
)
list(APPEND HEADERS
src/screenlockdetector.h
src/screenlockdetector_mac.h
)
# Enable Objective-C++ for .mm files
set_source_files_properties(
src/screenlockdetector_mac.mm
PROPERTIES
COMPILE_FLAGS "-x objective-c++"
)
elseif(UNIX)
# Linux specific files
list(APPEND SOURCES
src/screenlockdetector.cpp
)
list(APPEND HEADERS
src/screenlockdetector.h
)
endif()
# Add executable
add_executable(${PROJECT_NAME}
${SOURCES}
@ -49,14 +111,31 @@ target_link_libraries(${PROJECT_NAME}
Qt5::Core
Qt5::Gui
Qt5::Widgets
Qt5::DBus
)
# Platform-specific linking
if(UNIX AND NOT APPLE)
target_link_libraries(${PROJECT_NAME} Qt5::DBus)
elseif(APPLE)
# Link macOS frameworks
target_link_libraries(${PROJECT_NAME}
"-framework Foundation"
"-framework Cocoa"
)
endif()
# Set output directory
set_target_properties(${PROJECT_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
# macOS specific settings
if(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE FALSE
)
endif()
# Install target
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
@ -66,6 +145,8 @@ install(TARGETS ${PROJECT_NAME}
message(STATUS "========================================")
message(STATUS "Build Configuration:")
message(STATUS "========================================")
message(STATUS "Platform: ${PLATFORM_NAME}")
message(STATUS "Qt5_DIR: ${Qt5_DIR}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Qt Components: ${QT_COMPONENTS}")
message(STATUS "========================================")

232
README.md
View File

@ -1,23 +1,33 @@
# Qt Screen Lock Detection Demo
一个基于 Qt5 + CMake 的 Linux 应用程序,用于检测 Ubuntu 系统的锁屏状态,并在锁屏时自动停止所有 Paint 事件。
一个基于 Qt5 + CMake 的跨平台应用程序,用于检测系统的锁屏状态,并在锁屏时自动停止所有 Paint 事件。
**支持平台:**
- ✅ **Linux** (Ubuntu, Deepin, Fedora 等)
- ✅ **macOS** (10.12 及以上版本)
## 功能特性
- ✅ **自动检测锁屏状态**:通过 DBus 监听 Linux 系统的锁屏/解锁事件
- ✅ **自动检测锁屏状态**
- Linux: 通过 DBus 监听系统的锁屏/解锁事件
- macOS: 通过 NSDistributedNotificationCenter 监听系统通知
- ✅ **自动停止绘制**:屏幕锁定时自动停止所有 Paint 事件,节省系统资源
- ✅ **实时动画演示**:持续的动画效果,直观展示绘制的启用/禁用状态
- ✅ **手动控制**:提供手动启用/禁用绘制的按钮
- ✅ **状态监控**:实时显示锁屏状态、绘制状态和帧数统计
- ✅ **多桌面环境支持**:支持 GNOME、KDE、XFCE、Deepin DDE 等主流桌面环境
- ✅ **多平台支持**
- Linux: 支持 GNOME、KDE、XFCE、Deepin DDE 等主流桌面环境
- macOS: 原生系统通知支持
## 技术架构
### 核心组件
1. **ScreenLockDetector** - 锁屏检测器
- 通过 Qt DBus 监听系统锁屏信号
1. **ScreenLockDetector** - 跨平台锁屏检测器
- **Linux**: 通过 Qt DBus 监听系统锁屏信号
- 支持 GNOME ScreenSaver、systemd-logind 和 Deepin DDE 接口
- **macOS**: 通过 NSDistributedNotificationCenter 监听系统通知
- 监听 com.apple.screenIsLocked/Unlocked 通知
- 发出锁屏/解锁信号供其他组件订阅
2. **CustomWidget** - 自定义绘制组件
@ -33,69 +43,142 @@
### 技术栈
- **语言**C++11
- **GUI 框架**Qt 5.15.2
- **语言**C++11 / Objective-C++ (macOS)
- **GUI 框架**Qt 5.15.2 (或更高版本)
- **构建系统**CMake 3.10+
- **系统接口**Qt DBus
- **平台**Linux Ubuntu
- **系统接口**
- Linux: Qt DBus
- macOS: Foundation Framework (NSDistributedNotificationCenter)
- **平台**Linux Ubuntu / macOS 10.12+
## 系统要求
- Linux Ubuntu 18.04 或更高版本(也支持 Deepin OS
- Qt 5.15.2(安装在 `$HOME/sdk/qt-5.15.2`
### Linux
- Linux Ubuntu 18.04 或更高版本(也支持 Deepin OS、Fedora 等)
- Qt 5.15.2 或更高版本
- CMake 3.10 或更高版本
- GCC/G++ 编译器(支持 C++11
- DBus 系统服务
### macOS
- macOS 10.12 (Sierra) 或更高版本
- Qt 5.15.2 或更高版本(可通过 Homebrew 安装)
- CMake 3.10 或更高版本
- Xcode Command Line Tools
- Foundation 和 Cocoa 框架(系统自带)
## 安装与编译
### 1. 确保 Qt5 已安装
### macOS 平台
#### 1. 安装依赖
使用 Homebrew 安装 Qt5
```bash
# 检查 Qt5 目录是否存在
# 安装 Homebrew如果尚未安装
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装 Qt5
brew install qt@5
# 安装 CMake如果尚未安装
brew install cmake
```
或者从 Qt 官网下载安装器https://www.qt.io/download
#### 2. 编译项目
```bash
# 赋予脚本执行权限
chmod +x build_mac.sh run_mac.sh
# 编译
./build_mac.sh
```
脚本会自动搜索 Qt 安装路径。如果需要手动指定,可以设置环境变量:
```bash
export Qt5_DIR=/path/to/qt/lib/cmake/Qt5
./build_mac.sh
```
### Linux 平台
#### 1. 确保 Qt5 已安装
```bash
# Ubuntu/Debian
sudo apt-get install qt5-default qtbase5-dev libqt5dbus5
# Fedora
sudo dnf install qt5-qtbase-devel qt5-qtbase-gui
# 或使用自定义安装的 Qt
ls $HOME/sdk/qt-5.15.2
```
如果 Qt5 安装在其他位置,请修改 `CMakeLists.txt` 中的 Qt5_DIR 路径:
如果 Qt5 安装在其他位置,请修改 `CMakeLists.txt` 中的 Qt5_DIR 路径或设置环境变量。
```cmake
set(Qt5_DIR "$ENV{HOME}/sdk/qt-5.15.2/lib/cmake/Qt5")
```
### 2. 赋予脚本执行权限
#### 2. 赋予脚本执行权限
```bash
chmod +x build.sh run.sh
```
### 3. 编译项目
#### 3. 编译项目
```bash
./build.sh
```
编译成功后,可执行文件将生成在 `build/bin/ScreenLockDemo`
编译成功后,可执行文件将生成在 `build/bin/ScreenLockDetector`
## 运行应用
### 方法 1使用运行脚本推荐
### macOS 平台
#### 方法 1使用运行脚本推荐
```bash
./run_mac.sh
```
#### 方法 2直接运行可执行文件
```bash
./build/bin/ScreenLockDetector
```
#### 方法 3手动设置环境变量并运行
```bash
export DYLD_LIBRARY_PATH=/path/to/qt/lib:$DYLD_LIBRARY_PATH
./build/bin/ScreenLockDetector
```
### Linux 平台
#### 方法 1使用运行脚本推荐
```bash
./run.sh
```
### 方法 2直接运行可执行文件
#### 方法 2直接运行可执行文件
```bash
cd build/bin
./ScreenLockDemo
./ScreenLockDetector
```
### 方法 3手动设置环境变量并运行
#### 方法 3手动设置环境变量并运行
```bash
export LD_LIBRARY_PATH=$HOME/sdk/qt-5.15.2/lib:$LD_LIBRARY_PATH
./build/bin/ScreenLockDemo
./build/bin/ScreenLockDetector
```
## 使用说明
@ -103,7 +186,8 @@ export LD_LIBRARY_PATH=$HOME/sdk/qt-5.15.2/lib:$LD_LIBRARY_PATH
1. **启动应用**:运行应用后,会看到一个带有动态动画的窗口
2. **测试锁屏检测**
- 使用快捷键锁定屏幕:`Ctrl + Alt + L` 或 `Super + L`
- **macOS**: 使用快捷键 `Ctrl + Cmd + Q` 或从菜单栏选择"锁定屏幕"
- **Linux**: 使用快捷键 `Ctrl + Alt + L``Super + L`
- 观察动画是否停止
- 解锁后动画应自动恢复
@ -120,7 +204,7 @@ export LD_LIBRARY_PATH=$HOME/sdk/qt-5.15.2/lib:$LD_LIBRARY_PATH
## 工作原理
### DBus 监听机制
### Linux: DBus 监听机制
应用程序通过 Qt DBus 连接到 Linux 系统的锁屏服务:
@ -148,6 +232,29 @@ export LD_LIBRARY_PATH=$HOME/sdk/qt-5.15.2/lib:$LD_LIBRARY_PATH
Signals: Lock(), Unlock()
```
### macOS: 分布式通知中心
应用程序通过 NSDistributedNotificationCenter 监听 macOS 系统通知:
1. **屏幕锁定通知**
```
Notification: com.apple.screenIsLocked
当用户锁定屏幕时发送
```
2. **屏幕解锁通知**
```
Notification: com.apple.screenIsUnlocked
当用户解锁屏幕时发送
```
3. **屏幕保护程序通知**(辅助检测)
```
Notifications:
- com.apple.screensaver.didstart
- com.apple.screensaver.didstop
```
### Paint 事件控制
当检测到锁屏时:
@ -165,24 +272,72 @@ export LD_LIBRARY_PATH=$HOME/sdk/qt-5.15.2/lib:$LD_LIBRARY_PATH
## 项目结构
```
qt_screan_lock/
├── CMakeLists.txt # CMake 构建配置
ScreenLockDetector/
├── CMakeLists.txt # CMake 构建配置(跨平台)
├── README.md # 项目文档
├── build.sh # 编译脚本
├── run.sh # 运行脚本
├── build.sh # Linux 编译脚本
├── run.sh # Linux 运行脚本
├── build_mac.sh # macOS 编译脚本
├── run_mac.sh # macOS 运行脚本
└── src/
├── main.cpp # 程序入口
├── mainwindow.h # 主窗口头文件
├── mainwindow.cpp # 主窗口实现
├── screenlockdetector.h # 锁屏检测器头文件
├── screenlockdetector.cpp # 锁屏检测器实现
├── screenlockdetector.h # 跨平台锁屏检测器头文件
├── screenlockdetector.cpp # 跨平台锁屏检测器实现
├── screenlockdetector_mac.h # macOS 特定实现头文件
├── screenlockdetector_mac.mm # macOS 特定实现Objective-C++
├── customwidget.h # 自定义组件头文件
└── customwidget.cpp # 自定义组件实现
```
## 故障排除
### 问题 1锁屏检测不工作
### macOS 平台
#### 问题 1应用无法检测到锁屏
**可能原因**
- 权限问题
- 系统通知未正常工作
**解决方案**
```bash
# 查看应用日志
./run_mac.sh 2>&1 | grep "ScreenLock"
# 检查系统完整性保护SIP状态
csrutil status
# 确保应用有必要的权限
```
#### 问题 2编译错误 - Qt5 not found
**解决方案**
```bash
# 使用 Homebrew 安装 Qt5
brew install qt@5
# 设置 Qt5_DIR 环境变量
export Qt5_DIR=$(brew --prefix qt@5)/lib/cmake/Qt5
./build_mac.sh
```
#### 问题 3运行时找不到 Qt 库
**解决方案**
```bash
# 设置 DYLD_LIBRARY_PATH
export DYLD_LIBRARY_PATH=$(brew --prefix qt@5)/lib:$DYLD_LIBRARY_PATH
# 或使用 run_mac.sh 脚本(已包含此配置)
./run_mac.sh
```
### Linux 平台
#### 问题 1锁屏检测不工作
**可能原因**
- DBus 服务未运行
@ -272,6 +427,13 @@ Qt Screen Lock Detection Demo
## 更新日志
### v2.0.0 (2024)
- ✅ **重大更新**: 新增 macOS 平台支持
- ✅ 实现跨平台架构Linux + macOS
- ✅ macOS: 使用 NSDistributedNotificationCenter 监听系统通知
- ✅ 创建平台特定的构建和运行脚本
- ✅ 更新文档,包含 macOS 使用说明
### v1.1.0 (2024)
- ✅ 新增 Deepin OS (DDE) 锁屏检测支持
- ✅ 优化多桌面环境兼容性

View File

@ -17,15 +17,15 @@ echo "========================================"
echo ""
# Check if Qt5 directory exists
QT5_DIR="$HOME/sdk/qt-5.15.2"
if [ ! -d "$QT5_DIR" ]; then
echo -e "${RED}Error: Qt5 directory not found at $QT5_DIR${NC}"
echo "Please install Qt5 or update the path in CMakeLists.txt"
exit 1
fi
# QT5_DIR="$HOME/sdk/qt-5.15.2"
# if [ ! -d "$QT5_DIR" ]; then
# echo -e "${RED}Error: Qt5 directory not found at $QT5_DIR${NC}"
# echo "Please install Qt5 or update the path in CMakeLists.txt"
# exit 1
# fi
echo -e "${GREEN}✓ Qt5 directory found: $QT5_DIR${NC}"
echo ""
# echo -e "${GREEN}✓ Qt5 directory found: $QT5_DIR${NC}"
# echo ""
# Create build directory
BUILD_DIR="build"

117
build_mac.sh Executable file
View File

@ -0,0 +1,117 @@
#!/bin/bash
# macOS Build Script for ScreenLockDetector
# This script builds the project on macOS
set -e # Exit on error
echo "=========================================="
echo "Building ScreenLockDetector for macOS"
echo "=========================================="
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if we're on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
echo -e "${RED}Error: This script is for macOS only${NC}"
exit 1
fi
# Function to find Qt installation
find_qt() {
# Try to find Qt in common locations
QT_PATHS=(
"/opt/local" # MacPorts
"/usr/local/opt/qt@5" # Homebrew (Intel)
"/opt/homebrew/opt/qt@5" # Homebrew (Apple Silicon)
"$HOME/Qt/5.15.2/clang_64" # Official Qt installer
"$HOME/sdk/qt-5.15.2" # Custom installation
)
for path in "${QT_PATHS[@]}"; do
if [ -d "$path" ]; then
# Check if Qt cmake files exist
if [ -f "$path/lib/cmake/Qt5/Qt5Config.cmake" ]; then
echo "$path"
return 0
fi
fi
done
return 1
}
# Find Qt installation
echo "Searching for Qt installation..."
QT_PATH=$(find_qt)
if [ -z "$QT_PATH" ]; then
echo -e "${YELLOW}Warning: Qt not found in common locations${NC}"
echo "Please install Qt 5 using one of these methods:"
echo " 1. MacPorts: sudo port install qt5"
echo " 2. Homebrew: brew install qt@5"
echo " 3. Official installer: https://www.qt.io/download"
echo ""
echo "Or set Qt5_DIR environment variable manually:"
echo " export Qt5_DIR=/path/to/qt/lib/cmake/Qt5"
# Ask user if they want to continue anyway
read -p "Do you want to continue and let CMake search for Qt? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
else
echo -e "${GREEN}Found Qt at: $QT_PATH${NC}"
export Qt5_DIR="$QT_PATH/lib/cmake/Qt5"
fi
# Create build directory
BUILD_DIR="build"
if [ -d "$BUILD_DIR" ]; then
echo "Cleaning existing build directory..."
rm -rf "$BUILD_DIR"
fi
echo "Creating build directory..."
mkdir -p "$BUILD_DIR"
# Run CMake
echo ""
echo "Running CMake configuration..."
cd "$BUILD_DIR"
if [ -n "$Qt5_DIR" ]; then
cmake .. -DCMAKE_BUILD_TYPE=Release -DQt5_DIR="$Qt5_DIR"
else
cmake .. -DCMAKE_BUILD_TYPE=Release
fi
# Build the project
echo ""
echo "Building project..."
make -j$(sysctl -n hw.ncpu)
# Check if build succeeded
if [ $? -eq 0 ]; then
echo ""
echo -e "${GREEN}=========================================="
echo "Build completed successfully!"
echo "==========================================${NC}"
echo ""
echo "Executable location: $BUILD_DIR/bin/ScreenLockDetector"
echo ""
echo "To run the application:"
echo " ./run_mac.sh"
echo " or"
echo " ./build/bin/ScreenLockDetector"
else
echo -e "${RED}=========================================="
echo "Build failed!"
echo "==========================================${NC}"
exit 1
fi

82
run_mac.sh Executable file
View File

@ -0,0 +1,82 @@
#!/bin/bash
# macOS Run Script for ScreenLockDetector
# This script runs the application on macOS
set -e # Exit on error
echo "=========================================="
echo "Running ScreenLockDetector on macOS"
echo "=========================================="
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if we're on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
echo -e "${RED}Error: This script is for macOS only${NC}"
exit 1
fi
# Check if executable exists
EXECUTABLE="build/bin/ScreenLockDetector"
if [ ! -f "$EXECUTABLE" ]; then
echo -e "${RED}Error: Executable not found at $EXECUTABLE${NC}"
echo ""
echo "Please build the project first:"
echo " ./build_mac.sh"
exit 1
fi
# Function to find Qt installation
find_qt() {
# Try to find Qt in common locations
QT_PATHS=(
"/opt/local" # MacPorts
"/usr/local/opt/qt@5" # Homebrew (Intel)
"/opt/homebrew/opt/qt@5" # Homebrew (Apple Silicon)
"$HOME/Qt/5.15.2/clang_64" # Official Qt installer
"$HOME/sdk/qt-5.15.2" # Custom installation
)
for path in "${QT_PATHS[@]}"; do
if [ -d "$path" ]; then
# Check if Qt cmake files exist
if [ -f "$path/lib/cmake/Qt5/Qt5Config.cmake" ]; then
echo "$path"
return 0
fi
fi
done
return 1
}
# Find Qt installation and set up library path
QT_PATH=$(find_qt)
if [ -n "$QT_PATH" ]; then
echo -e "${GREEN}Found Qt at: $QT_PATH${NC}"
export DYLD_LIBRARY_PATH="$QT_PATH/lib:$DYLD_LIBRARY_PATH"
export DYLD_FRAMEWORK_PATH="$QT_PATH/lib:$DYLD_FRAMEWORK_PATH"
else
echo -e "${YELLOW}Warning: Qt not found in common locations${NC}"
echo "If the application fails to run, please set DYLD_LIBRARY_PATH manually:"
echo " export DYLD_LIBRARY_PATH=/path/to/qt/lib:\$DYLD_LIBRARY_PATH"
echo ""
fi
# Run the application
echo ""
echo "Starting ScreenLockDetector..."
echo "=========================================="
echo ""
./"$EXECUTABLE"
echo ""
echo "Application exited."

View File

@ -1,23 +1,36 @@
#include "screenlockdetector.h"
#ifdef Q_OS_MAC
#include "screenlockdetector_mac.h"
#endif
#ifdef Q_OS_LINUX
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusReply>
#endif
ScreenLockDetector::ScreenLockDetector(QObject *parent)
: QObject(parent)
, m_isLocked(false)
#ifdef Q_OS_LINUX
, m_gnomeInterface(nullptr)
, m_loginInterface(nullptr)
, m_deepinInterface(nullptr)
, m_gnomeConnected(false)
, m_loginConnected(false)
, m_deepinConnected(false)
#endif
#ifdef Q_OS_MAC
, m_macDetector(nullptr)
#endif
{
}
ScreenLockDetector::~ScreenLockDetector()
{
#ifdef Q_OS_LINUX
if (m_gnomeInterface) {
delete m_gnomeInterface;
m_gnomeInterface = nullptr;
@ -32,6 +45,14 @@ ScreenLockDetector::~ScreenLockDetector()
delete m_deepinInterface;
m_deepinInterface = nullptr;
}
#endif
#ifdef Q_OS_MAC
if (m_macDetector) {
delete m_macDetector;
m_macDetector = nullptr;
}
#endif
}
bool ScreenLockDetector::isScreenLocked() const
@ -41,8 +62,40 @@ bool ScreenLockDetector::isScreenLocked() const
bool ScreenLockDetector::initialize()
{
#ifdef Q_OS_MAC
qDebug() << "=================================================";
qDebug() << "Initializing ScreenLockDetector...";
qDebug() << "Initializing ScreenLockDetector for macOS...";
qDebug() << "=================================================";
// 创建 macOS 特定的检测器
m_macDetector = new ScreenLockDetectorMac(this);
// 连接信号
connect(m_macDetector, &ScreenLockDetectorMac::screenLocked,
this, &ScreenLockDetector::screenLocked);
connect(m_macDetector, &ScreenLockDetectorMac::screenUnlocked,
this, &ScreenLockDetector::screenUnlocked);
connect(m_macDetector, &ScreenLockDetectorMac::lockStateChanged,
this, [this](bool locked) {
m_isLocked = locked;
emit lockStateChanged(locked);
});
bool success = m_macDetector->initialize();
if (success) {
qDebug() << "=================================================";
qDebug() << "ScreenLockDetector initialized successfully on macOS";
qDebug() << "=================================================";
} else {
qWarning() << "Failed to initialize ScreenLockDetector on macOS";
}
return success;
#elif defined(Q_OS_LINUX)
qDebug() << "=================================================";
qDebug() << "Initializing ScreenLockDetector for Linux...";
qDebug() << "=================================================";
// 尝试连接到不同的DBus接口
@ -60,13 +113,18 @@ bool ScreenLockDetector::initialize()
queryCurrentLockState();
qDebug() << "=================================================";
qDebug() << "ScreenLockDetector initialized successfully";
qDebug() << "ScreenLockDetector initialized successfully on Linux";
qDebug() << "Deepin DDE connected:" << m_deepinConnected;
qDebug() << "GNOME ScreenSaver connected:" << m_gnomeConnected;
qDebug() << "Login Manager connected:" << m_loginConnected;
qDebug() << "=================================================";
return true;
#else
qWarning() << "ScreenLockDetector: Unsupported platform";
return false;
#endif
}
void ScreenLockDetector::setLockState(bool locked)
@ -88,6 +146,8 @@ void ScreenLockDetector::setLockState(bool locked)
}
}
#ifdef Q_OS_LINUX
bool ScreenLockDetector::connectToGnomeScreenSaver()
{
qDebug() << "\n--- Connecting to GNOME ScreenSaver ---";
@ -451,3 +511,5 @@ void ScreenLockDetector::onSessionUnlocked()
qDebug() << "##################################################";
setLockState(false);
}
#endif // Q_OS_LINUX

View File

@ -2,20 +2,34 @@
#define SCREENLOCKDETECTOR_H
#include <QObject>
#ifdef Q_OS_LINUX
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDBusContext>
#include <QCoreApplication>
#endif
#include <QDebug>
// 前向声明
#ifdef Q_OS_MAC
class ScreenLockDetectorMac;
#endif
/**
* @brief
* @brief
*
* Linux系统的DBus信号来检测屏幕锁定/
* Linux: DBus信号来检测屏幕锁定/
* Deepin DDE, GNOME, KDE, XFCE等
*
* macOS: NSDistributedNotificationCenter的系统通知
*/
class ScreenLockDetector : public QObject, protected QDBusContext
class ScreenLockDetector : public QObject
#ifdef Q_OS_LINUX
, protected QDBusContext
#endif
{
Q_OBJECT
@ -30,7 +44,7 @@ public:
bool isScreenLocked() const;
/**
* @brief DBus连接
* @brief
* @return true false
*/
bool initialize();
@ -54,6 +68,7 @@ signals:
*/
void lockStateChanged(bool locked);
#ifdef Q_OS_LINUX
private slots:
/**
* @brief GNOME屏幕保护程序的DBus信号
@ -63,7 +78,7 @@ private slots:
/**
* @brief DEEPIN屏幕保护程序的DBus信号
* @param active
* @param visible
*/
void onLockFrontVisible(bool visible);
@ -76,6 +91,7 @@ private slots:
* @brief
*/
void onSessionUnlocked();
#endif
private:
/**
@ -84,6 +100,7 @@ private:
*/
void setLockState(bool locked);
#ifdef Q_OS_LINUX
/**
* @brief GNOME屏幕保护程序的DBus接口
* @return true
@ -112,15 +129,23 @@ private:
* @return
*/
QString getCurrentSessionPath();
#endif
private:
bool m_isLocked; // 当前锁屏状态
#ifdef Q_OS_LINUX
QDBusInterface *m_gnomeInterface; // GNOME屏幕保护程序接口
QDBusInterface *m_loginInterface; // 登录管理器接口
QDBusInterface *m_deepinInterface; // Deepin DDE接口
bool m_gnomeConnected; // GNOME接口是否连接成功
bool m_loginConnected; // 登录管理器接口是否连接成功
bool m_deepinConnected; // Deepin DDE接口是否连接成功
#endif
#ifdef Q_OS_MAC
ScreenLockDetectorMac *m_macDetector; // macOS 特定的检测器
#endif
};
#endif // SCREENLOCKDETECTOR_H

View File

@ -0,0 +1,100 @@
#ifndef SCREENLOCKDETECTOR_MAC_H
#define SCREENLOCKDETECTOR_MAC_H
#include <QObject>
#ifdef Q_OS_MAC
/**
* @brief macOS
*
* macOS /
* 使 NSDistributedNotificationCenter
*/
class ScreenLockDetectorMac : public QObject
{
Q_OBJECT
public:
explicit ScreenLockDetectorMac(QObject *parent = nullptr);
~ScreenLockDetectorMac();
/**
* @brief
* @return true false
*/
bool isScreenLocked() const;
/**
* @brief
* @return true false
*/
bool initialize();
signals:
/**
* @brief
*
*/
void screenLocked();
/**
* @brief
*
*/
void screenUnlocked();
/**
* @brief
* @param locked true表示已锁定false表示已解锁
*/
void lockStateChanged(bool locked);
public:
/**
* @brief
*/
void handleScreenLocked();
/**
* @brief
*/
void handleScreenUnlocked();
/**
* @brief
*/
void handleScreenSaverStarted();
/**
* @brief
*/
void handleScreenSaverStopped();
private:
/**
* @brief
* @param locked
*/
void setLockState(bool locked);
/**
* @brief
* @return true
*/
bool registerNotificationObservers();
/**
* @brief
*/
void unregisterNotificationObservers();
private:
bool m_isLocked; // 当前锁屏状态
void *m_observerToken; // Objective-C 观察者令牌(不透明指针)
bool m_initialized; // 是否已初始化
};
#endif // Q_OS_MAC
#endif // SCREENLOCKDETECTOR_MAC_H

View File

@ -0,0 +1,243 @@
#include "screenlockdetector_mac.h"
#ifdef Q_OS_MAC
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#include <QDebug>
// Objective-C 观察者类,用于接收通知
@interface ScreenLockObserver : NSObject
@property (nonatomic, assign) ScreenLockDetectorMac *detector;
- (instancetype)initWithDetector:(ScreenLockDetectorMac *)detector;
- (void)screenLocked:(NSNotification *)notification;
- (void)screenUnlocked:(NSNotification *)notification;
- (void)screenSaverStarted:(NSNotification *)notification;
- (void)screenSaverStopped:(NSNotification *)notification;
@end
@implementation ScreenLockObserver
- (instancetype)initWithDetector:(ScreenLockDetectorMac *)detector
{
self = [super init];
if (self) {
_detector = detector;
}
return self;
}
- (void)screenLocked:(NSNotification *)notification
{
NSLog(@"[ScreenLockObserver] Screen locked notification received");
if (_detector) {
_detector->handleScreenLocked();
}
}
- (void)screenUnlocked:(NSNotification *)notification
{
NSLog(@"[ScreenLockObserver] Screen unlocked notification received");
if (_detector) {
_detector->handleScreenUnlocked();
}
}
- (void)screenSaverStarted:(NSNotification *)notification
{
NSLog(@"[ScreenLockObserver] Screen saver started notification received");
if (_detector) {
_detector->handleScreenSaverStarted();
}
}
- (void)screenSaverStopped:(NSNotification *)notification
{
NSLog(@"[ScreenLockObserver] Screen saver stopped notification received");
if (_detector) {
_detector->handleScreenSaverStopped();
}
}
@end
ScreenLockDetectorMac::ScreenLockDetectorMac(QObject *parent)
: QObject(parent)
, m_isLocked(false)
, m_observerToken(nullptr)
, m_initialized(false)
{
}
ScreenLockDetectorMac::~ScreenLockDetectorMac()
{
unregisterNotificationObservers();
}
bool ScreenLockDetectorMac::isScreenLocked() const
{
return m_isLocked;
}
bool ScreenLockDetectorMac::initialize()
{
qDebug() << "=================================================";
qDebug() << "Initializing ScreenLockDetectorMac...";
qDebug() << "=================================================";
if (m_initialized) {
qWarning() << "ScreenLockDetectorMac already initialized";
return true;
}
bool success = registerNotificationObservers();
if (success) {
m_initialized = true;
qDebug() << "=================================================";
qDebug() << "ScreenLockDetectorMac initialized successfully";
qDebug() << "Listening for macOS screen lock notifications";
qDebug() << "=================================================";
} else {
qWarning() << "Failed to initialize ScreenLockDetectorMac";
}
return success;
}
bool ScreenLockDetectorMac::registerNotificationObservers()
{
@autoreleasepool {
qDebug() << "\n--- Registering macOS notification observers ---";
// 创建观察者对象
ScreenLockObserver *observer = [[ScreenLockObserver alloc] initWithDetector:this];
// 使用 CFBridgingRetain 保持对象,并保存指针
m_observerToken = (void *)CFBridgingRetain(observer);
// 获取分布式通知中心
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
// 注册屏幕锁定通知
// macOS 使用 "com.apple.screenIsLocked" 通知
[center addObserver:observer
selector:@selector(screenLocked:)
name:@"com.apple.screenIsLocked"
object:nil
suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
qDebug() << "Registered observer for: com.apple.screenIsLocked";
// 注册屏幕解锁通知
// macOS 使用 "com.apple.screenIsUnlocked" 通知
[center addObserver:observer
selector:@selector(screenUnlocked:)
name:@"com.apple.screenIsUnlocked"
object:nil
suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
qDebug() << "Registered observer for: com.apple.screenIsUnlocked";
// 注册屏幕保护程序启动通知(可选,作为备用检测方式)
[center addObserver:observer
selector:@selector(screenSaverStarted:)
name:@"com.apple.screensaver.didstart"
object:nil
suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
qDebug() << "Registered observer for: com.apple.screensaver.didstart";
// 注册屏幕保护程序停止通知(可选,作为备用检测方式)
[center addObserver:observer
selector:@selector(screenSaverStopped:)
name:@"com.apple.screensaver.didstop"
object:nil
suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
qDebug() << "Registered observer for: com.apple.screensaver.didstop";
qDebug() << "Successfully registered all notification observers";
return true;
}
}
void ScreenLockDetectorMac::unregisterNotificationObservers()
{
@autoreleasepool {
if (m_observerToken) {
qDebug() << "Unregistering macOS notification observers";
// 使用 CFBridgingRelease 释放对象
ScreenLockObserver *observer = (ScreenLockObserver *)CFBridgingRelease(m_observerToken);
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
// 移除所有通知观察者
[center removeObserver:observer];
m_observerToken = nullptr;
m_initialized = false;
qDebug() << "Notification observers unregistered";
}
}
}
void ScreenLockDetectorMac::setLockState(bool locked)
{
if (m_isLocked != locked) {
m_isLocked = locked;
qDebug() << "##################################################";
qDebug() << "## Screen lock state changed:" << (locked ? "LOCKED" : "UNLOCKED");
qDebug() << "##################################################";
emit lockStateChanged(locked);
if (locked) {
emit screenLocked();
} else {
emit screenUnlocked();
}
}
}
void ScreenLockDetectorMac::handleScreenLocked()
{
qDebug() << "##################################################";
qDebug() << "## macOS LOCK NOTIFICATION RECEIVED";
qDebug() << "## Screen is now LOCKED";
qDebug() << "##################################################";
setLockState(true);
}
void ScreenLockDetectorMac::handleScreenUnlocked()
{
qDebug() << "##################################################";
qDebug() << "## macOS UNLOCK NOTIFICATION RECEIVED";
qDebug() << "## Screen is now UNLOCKED";
qDebug() << "##################################################";
setLockState(false);
}
void ScreenLockDetectorMac::handleScreenSaverStarted()
{
qDebug() << "##################################################";
qDebug() << "## macOS SCREEN SAVER STARTED";
qDebug() << "## Note: Screen saver started (may not be locked yet)";
qDebug() << "##################################################";
// 屏幕保护程序启动不一定意味着锁定
// 只有在设置中启用了"从睡眠或屏幕保护程序恢复时需要密码"时才会锁定
// 我们可以选择在这里设置锁定状态,或者等待真正的锁定通知
// 为了保守起见,这里不设置锁定状态
}
void ScreenLockDetectorMac::handleScreenSaverStopped()
{
qDebug() << "##################################################";
qDebug() << "## macOS SCREEN SAVER STOPPED";
qDebug() << "## Note: Screen saver stopped";
qDebug() << "##################################################";
// 屏幕保护程序停止,但解锁通知会单独发送
}
#endif // Q_OS_MAC