diff --git a/CMakeLists.txt b/CMakeLists.txt index d989396..c31c55d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 "========================================") diff --git a/README.md b/README.md index 441f5d5..0e429b1 100644 --- a/README.md +++ b/README.md @@ -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 监听系统锁屏信号 - - 支持 GNOME ScreenSaver、systemd-logind 和 Deepin DDE 接口 +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 # 锁屏检测器实现 - ├── customwidget.h # 自定义组件头文件 - └── customwidget.cpp # 自定义组件实现 + ├── main.cpp # 程序入口 + ├── mainwindow.h # 主窗口头文件 + ├── mainwindow.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) 锁屏检测支持 - ✅ 优化多桌面环境兼容性 diff --git a/build.sh b/build.sh index b50c3fe..f56ee42 100755 --- a/build.sh +++ b/build.sh @@ -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" diff --git a/build_mac.sh b/build_mac.sh new file mode 100755 index 0000000..69eea2b --- /dev/null +++ b/build_mac.sh @@ -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 diff --git a/run_mac.sh b/run_mac.sh new file mode 100755 index 0000000..04a7edd --- /dev/null +++ b/run_mac.sh @@ -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." diff --git a/src/screenlockdetector.cpp b/src/screenlockdetector.cpp index 390b7ea..7a46ec3 100644 --- a/src/screenlockdetector.cpp +++ b/src/screenlockdetector.cpp @@ -1,23 +1,36 @@ #include "screenlockdetector.h" + +#ifdef Q_OS_MAC +#include "screenlockdetector_mac.h" +#endif + +#ifdef Q_OS_LINUX #include #include #include #include +#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 \ No newline at end of file diff --git a/src/screenlockdetector.h b/src/screenlockdetector.h index 4e59bc8..ee1e54d 100644 --- a/src/screenlockdetector.h +++ b/src/screenlockdetector.h @@ -2,20 +2,34 @@ #define SCREENLOCKDETECTOR_H #include + +#ifdef Q_OS_LINUX #include #include #include #include #include +#endif + #include +// 前向声明 +#ifdef Q_OS_MAC +class ScreenLockDetectorMac; +#endif + /** - * @brief 屏幕锁定检测器类 + * @brief 跨平台屏幕锁定检测器类 * - * 通过监听Linux系统的DBus信号来检测屏幕锁定/解锁状态 - * 支持多种桌面环境:Deepin DDE, GNOME, KDE, XFCE等 + * 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 \ No newline at end of file diff --git a/src/screenlockdetector_mac.h b/src/screenlockdetector_mac.h new file mode 100644 index 0000000..07c76c3 --- /dev/null +++ b/src/screenlockdetector_mac.h @@ -0,0 +1,100 @@ +#ifndef SCREENLOCKDETECTOR_MAC_H +#define SCREENLOCKDETECTOR_MAC_H + +#include + +#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 \ No newline at end of file diff --git a/src/screenlockdetector_mac.mm b/src/screenlockdetector_mac.mm new file mode 100644 index 0000000..a260d36 --- /dev/null +++ b/src/screenlockdetector_mac.mm @@ -0,0 +1,243 @@ +#include "screenlockdetector_mac.h" + +#ifdef Q_OS_MAC + +#import +#import +#include + +// 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