Add cross-platform power monitor and macOS Vulkan
This commit is contained in:
parent
ad3524bd23
commit
f62f429158
|
|
@ -75,7 +75,11 @@ set(VULKAN_FOUND FALSE)
|
||||||
|
|
||||||
if(ENABLE_VULKAN)
|
if(ENABLE_VULKAN)
|
||||||
# Set Vulkan headers path
|
# Set Vulkan headers path
|
||||||
|
if(APPLE)
|
||||||
|
set(VULKAN_HEADERS_DIR "$ENV{HOME}/VulkanSDK/1.4.328.1/macOS/include")
|
||||||
|
elseif(LINUX)
|
||||||
set(VULKAN_HEADERS_DIR "$ENV{HOME}/sdk/Vulkan-Headers-1.3.302/include")
|
set(VULKAN_HEADERS_DIR "$ENV{HOME}/sdk/Vulkan-Headers-1.3.302/include")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Check if Vulkan headers exist
|
# Check if Vulkan headers exist
|
||||||
if(EXISTS "${VULKAN_HEADERS_DIR}/vulkan/vulkan.h")
|
if(EXISTS "${VULKAN_HEADERS_DIR}/vulkan/vulkan.h")
|
||||||
|
|
@ -145,6 +149,7 @@ set(SOURCES
|
||||||
src/screenlockdetector.cpp
|
src/screenlockdetector.cpp
|
||||||
src/screenlockdetector_base.cpp
|
src/screenlockdetector_base.cpp
|
||||||
src/powermonitor.cpp
|
src/powermonitor.cpp
|
||||||
|
src/powermonitor_base.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
|
@ -154,6 +159,7 @@ set(HEADERS
|
||||||
src/screenlockdetector.h
|
src/screenlockdetector.h
|
||||||
src/screenlockdetector_base.h
|
src/screenlockdetector_base.h
|
||||||
src/powermonitor.h
|
src/powermonitor.h
|
||||||
|
src/powermonitor_base.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add Vulkan widget if Vulkan is available
|
# Add Vulkan widget if Vulkan is available
|
||||||
|
|
@ -174,14 +180,20 @@ if(APPLE)
|
||||||
# macOS specific files
|
# macOS specific files
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
src/screenlockdetector_macos.mm
|
src/screenlockdetector_macos.mm
|
||||||
|
src/powermonitor_macos.mm
|
||||||
|
src/vulkanwidget_macos.mm
|
||||||
)
|
)
|
||||||
list(APPEND HEADERS
|
list(APPEND HEADERS
|
||||||
src/screenlockdetector_macos.h
|
src/screenlockdetector_macos.h
|
||||||
|
src/powermonitor_macos.h
|
||||||
|
src/vulkanwidget_macos.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enable Objective-C++ for .mm files
|
# Enable Objective-C++ for .mm files
|
||||||
set_source_files_properties(
|
set_source_files_properties(
|
||||||
src/screenlockdetector_macos.mm
|
src/screenlockdetector_macos.mm
|
||||||
|
src/powermonitor_macos.mm
|
||||||
|
src/vulkanwidget_macos.mm
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
COMPILE_FLAGS "-x objective-c++"
|
COMPILE_FLAGS "-x objective-c++"
|
||||||
)
|
)
|
||||||
|
|
@ -189,9 +201,11 @@ elseif(UNIX)
|
||||||
# Linux specific files
|
# Linux specific files
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
src/screenlockdetector_linux.cpp
|
src/screenlockdetector_linux.cpp
|
||||||
|
src/powermonitor_linux.cpp
|
||||||
)
|
)
|
||||||
list(APPEND HEADERS
|
list(APPEND HEADERS
|
||||||
src/screenlockdetector_linux.h
|
src/screenlockdetector_linux.h
|
||||||
|
src/powermonitor_linux.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
14
run_mac.sh
14
run_mac.sh
|
|
@ -21,6 +21,20 @@ if [[ "$OSTYPE" != "darwin"* ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Set up Vulkan SDK environment for MoltenVK
|
||||||
|
VULKAN_SDK="$HOME/VulkanSDK/1.4.328.1/macOS"
|
||||||
|
if [ -d "$VULKAN_SDK" ]; then
|
||||||
|
echo -e "${GREEN}Found Vulkan SDK at: $VULKAN_SDK${NC}"
|
||||||
|
export VULKAN_SDK
|
||||||
|
export DYLD_LIBRARY_PATH="$VULKAN_SDK/lib:$DYLD_LIBRARY_PATH"
|
||||||
|
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||||
|
export VK_ADD_LAYER_PATH="$VULKAN_SDK/share/vulkan/explicit_layer.d"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Vulkan SDK not found at $VULKAN_SDK${NC}"
|
||||||
|
echo "Vulkan rendering may not work. Install from: https://vulkan.lunarg.com/sdk/home"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if executable exists
|
# Check if executable exists
|
||||||
EXECUTABLE="build/bin/ScreenLockDetector"
|
EXECUTABLE="build/bin/ScreenLockDetector"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,50 @@
|
||||||
#include "powermonitor.h"
|
#include "powermonitor.h"
|
||||||
|
|
||||||
void PowerMonitor::initLogin1Dbus() {
|
#ifdef Q_OS_LINUX
|
||||||
// 连接 org.freedesktop.login1 服务(系统总线)
|
#include "powermonitor_linux.h"
|
||||||
m_login1Interface = new QDBusInterface(
|
#elif defined(Q_OS_MAC)
|
||||||
"org.freedesktop.login1", // DBus 服务名
|
#include "powermonitor_macos.h"
|
||||||
"/org/freedesktop/login1", // 对象路径
|
#endif
|
||||||
"org.freedesktop.login1.Manager", // 接口名
|
|
||||||
QDBusConnection::systemBus(), // 系统总线(关键!不是会话总线)
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
// 检查接口是否有效
|
#include <QDebug>
|
||||||
if (m_login1Interface->isValid()) {
|
|
||||||
// 监听 PrepareForSleep 信号(签名:b = bool)
|
|
||||||
bool connectOk = QDBusConnection::systemBus().connect(
|
|
||||||
"org.freedesktop.login1", // 服务名
|
|
||||||
"/org/freedesktop/login1", // 对象路径
|
|
||||||
"org.freedesktop.login1.Manager", // 接口名
|
|
||||||
"PrepareForSleep", // 信号名
|
|
||||||
this,
|
|
||||||
SLOT(onPrepareForSleep(bool)) // 接收信号的槽函数
|
|
||||||
);
|
|
||||||
|
|
||||||
if (connectOk) {
|
PowerMonitor::PowerMonitor(QObject *parent)
|
||||||
qDebug() << "=================================================";
|
: PowerMonitorBase(parent)
|
||||||
qDebug() << "✅ 成功连接 login1 电源管理接口";
|
, m_impl(nullptr)
|
||||||
qDebug() << "=================================================";
|
{
|
||||||
} else {
|
#ifdef Q_OS_LINUX
|
||||||
qDebug() << "=================================================";
|
m_impl = new PowerMonitorLinux(this);
|
||||||
qDebug() << "❌ 连接 login1 信号失败:" << QDBusConnection::systemBus().lastError().message();
|
qDebug() << "创建 Linux 电源监视器实例";
|
||||||
qDebug() << "=================================================";
|
#elif defined(Q_OS_MAC)
|
||||||
}
|
m_impl = new PowerMonitorMacOS(this);
|
||||||
} else {
|
qDebug() << "创建 macOS 电源监视器实例";
|
||||||
qDebug() << "=================================================";
|
#else
|
||||||
qDebug() << "❌ 无法创建 login1 接口:" << m_login1Interface->lastError().message();
|
qWarning() << "警告:当前平台不支持电源监视功能";
|
||||||
qDebug() << "=================================================";
|
#endif
|
||||||
|
|
||||||
|
if (m_impl) {
|
||||||
|
// 转发平台实现的信号到工厂类
|
||||||
|
connect(m_impl, &PowerMonitorBase::aboutToSleep,
|
||||||
|
this, &PowerMonitor::aboutToSleep);
|
||||||
|
connect(m_impl, &PowerMonitorBase::aboutToWakeUp,
|
||||||
|
this, &PowerMonitor::aboutToWakeUp);
|
||||||
|
connect(m_impl, &PowerMonitorBase::powerStateChanged,
|
||||||
|
this, &PowerMonitor::powerStateChanged);
|
||||||
|
|
||||||
|
// 自动初始化
|
||||||
|
m_impl->initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 槽函数:处理睡眠/唤醒事件
|
PowerMonitor::~PowerMonitor()
|
||||||
void PowerMonitor::onPrepareForSleep(bool enteringSleep) {
|
{
|
||||||
if (enteringSleep) {
|
// m_impl 会被 Qt 的父对象机制自动删除
|
||||||
// 系统即将进入 休眠/待机 状态
|
|
||||||
qDebug() << "=================================================";
|
|
||||||
qDebug() << "\n📥 系统即将进入睡眠(休眠/待机)";
|
|
||||||
emit aboutToSleep();
|
|
||||||
} else {
|
|
||||||
// 系统从睡眠中唤醒
|
|
||||||
qDebug() << "📤 系统已从睡眠中唤醒\n";
|
|
||||||
qDebug() << "=================================================";
|
|
||||||
emit aboutToWakeUp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PowerMonitor::initialize()
|
||||||
|
{
|
||||||
|
if (m_impl) {
|
||||||
|
return m_impl->initialize();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1,39 +1,38 @@
|
||||||
#pragma once
|
#ifndef POWERMONITOR_H
|
||||||
#include <QObject>
|
#define POWERMONITOR_H
|
||||||
#include <QDBusInterface>
|
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
class PowerMonitor : public QObject
|
#include "powermonitor_base.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 跨平台电源监视器工厂类
|
||||||
|
*
|
||||||
|
* 根据编译平台自动选择合适的电源监视器实现
|
||||||
|
* 提供统一的接口用于监听系统睡眠/唤醒事件
|
||||||
|
*/
|
||||||
|
class PowerMonitor : public PowerMonitorBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PowerMonitor(QObject *parent = nullptr) : QObject(parent) { initLogin1Dbus(); }
|
/**
|
||||||
|
* @brief 创建平台特定的电源监视器实例
|
||||||
signals:
|
* @param parent 父对象
|
||||||
/*
|
*
|
||||||
* @brief 发送即将唤醒的信号
|
* 根据编译时的平台宏自动创建对应的实现:
|
||||||
|
* - Linux: PowerMonitorLinux (使用 DBus login1)
|
||||||
|
* - macOS: PowerMonitorMacOS (使用 NSWorkspace 通知)
|
||||||
*/
|
*/
|
||||||
void aboutToWakeUp();
|
explicit PowerMonitor(QObject *parent = nullptr);
|
||||||
|
~PowerMonitor() override;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* @brief 发送即将进入睡眠的信号
|
* @brief 初始化电源监视器
|
||||||
|
* @return true 如果初始化成功,否则返回 false
|
||||||
*/
|
*/
|
||||||
void aboutToSleep();
|
bool initialize() override;
|
||||||
|
|
||||||
private slots:
|
|
||||||
/*
|
|
||||||
* @brief 处理睡眠/唤醒信号的槽函数
|
|
||||||
* @param enteringSleep 是否进入睡眠状态
|
|
||||||
*/
|
|
||||||
void onPrepareForSleep(bool enteringSleep);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
PowerMonitorBase *m_impl; // 平台特定的实现
|
||||||
* @brief 初始化Login1 DBus接口
|
|
||||||
*/
|
|
||||||
void initLogin1Dbus();
|
|
||||||
|
|
||||||
QDBusInterface *m_login1Interface = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // POWERMONITOR_H
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "powermonitor_base.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
PowerMonitorBase::PowerMonitorBase(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerMonitorBase::~PowerMonitorBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerMonitorBase::emitAboutToSleep()
|
||||||
|
{
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "\n📥 系统即将进入睡眠(休眠/待机)";
|
||||||
|
emit aboutToSleep();
|
||||||
|
emit powerStateChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerMonitorBase::emitAboutToWakeUp()
|
||||||
|
{
|
||||||
|
qDebug() << "📤 系统已从睡眠中唤醒\n";
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
emit aboutToWakeUp();
|
||||||
|
emit powerStateChanged(false);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef POWERMONITOR_BASE_H
|
||||||
|
#define POWERMONITOR_BASE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 电源监视器抽象基类
|
||||||
|
*
|
||||||
|
* 定义跨平台电源监视的公共接口
|
||||||
|
* 各平台的具体实现需要继承此类并实现纯虚函数
|
||||||
|
*/
|
||||||
|
class PowerMonitorBase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PowerMonitorBase(QObject *parent = nullptr);
|
||||||
|
virtual ~PowerMonitorBase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化平台特定的电源监视
|
||||||
|
* @return true 如果初始化成功,否则返回 false
|
||||||
|
*
|
||||||
|
* 纯虚函数,由子类实现平台特定的初始化逻辑
|
||||||
|
*/
|
||||||
|
virtual bool initialize() = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* @brief 发送即将唤醒的信号
|
||||||
|
* 当系统从睡眠/休眠状态唤醒时发出
|
||||||
|
*/
|
||||||
|
void aboutToWakeUp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 发送即将进入睡眠的信号
|
||||||
|
* 当系统即将进入睡眠/休眠状态时发出
|
||||||
|
*/
|
||||||
|
void aboutToSleep();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 电源状态改变信号
|
||||||
|
* @param sleeping true表示进入睡眠,false表示从睡眠唤醒
|
||||||
|
*/
|
||||||
|
void powerStateChanged(bool sleeping);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief 触发睡眠事件
|
||||||
|
* 由子类调用以发出即将进入睡眠的信号
|
||||||
|
*/
|
||||||
|
void emitAboutToSleep();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发唤醒事件
|
||||||
|
* 由子类调用以发出即将唤醒的信号
|
||||||
|
*/
|
||||||
|
void emitAboutToWakeUp();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // POWERMONITOR_BASE_H
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include "powermonitor_linux.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
PowerMonitorLinux::PowerMonitorLinux(QObject *parent)
|
||||||
|
: PowerMonitorBase(parent)
|
||||||
|
, m_login1Interface(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerMonitorLinux::~PowerMonitorLinux()
|
||||||
|
{
|
||||||
|
if (m_login1Interface) {
|
||||||
|
delete m_login1Interface;
|
||||||
|
m_login1Interface = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerMonitorLinux::initialize()
|
||||||
|
{
|
||||||
|
return initLogin1Dbus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerMonitorLinux::initLogin1Dbus()
|
||||||
|
{
|
||||||
|
// 连接 org.freedesktop.login1 服务(系统总线)
|
||||||
|
m_login1Interface = new QDBusInterface(
|
||||||
|
"org.freedesktop.login1", // DBus 服务名
|
||||||
|
"/org/freedesktop/login1", // 对象路径
|
||||||
|
"org.freedesktop.login1.Manager", // 接口名
|
||||||
|
QDBusConnection::systemBus(), // 系统总线(关键!不是会话总线)
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查接口是否有效
|
||||||
|
if (!m_login1Interface->isValid()) {
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "❌ 无法创建 login1 接口:" << m_login1Interface->lastError().message();
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 PrepareForSleep 信号(签名:b = bool)
|
||||||
|
bool connectOk = QDBusConnection::systemBus().connect(
|
||||||
|
"org.freedesktop.login1", // 服务名
|
||||||
|
"/org/freedesktop/login1", // 对象路径
|
||||||
|
"org.freedesktop.login1.Manager", // 接口名
|
||||||
|
"PrepareForSleep", // 信号名
|
||||||
|
this,
|
||||||
|
SLOT(onPrepareForSleep(bool)) // 接收信号的槽函数
|
||||||
|
);
|
||||||
|
|
||||||
|
if (connectOk) {
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "✅ 成功连接 login1 电源管理接口(Linux)";
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "❌ 连接 login1 信号失败:" << QDBusConnection::systemBus().lastError().message();
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerMonitorLinux::onPrepareForSleep(bool enteringSleep)
|
||||||
|
{
|
||||||
|
if (enteringSleep) {
|
||||||
|
// 系统即将进入 休眠/待机 状态
|
||||||
|
emitAboutToSleep();
|
||||||
|
} else {
|
||||||
|
// 系统从睡眠中唤醒
|
||||||
|
emitAboutToWakeUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Q_OS_LINUX
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef POWERMONITOR_LINUX_H
|
||||||
|
#define POWERMONITOR_LINUX_H
|
||||||
|
|
||||||
|
#include "powermonitor_base.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
|
||||||
|
#include <QDBusInterface>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Linux 电源监视器类
|
||||||
|
*
|
||||||
|
* 通过监听 DBus 的 login1 服务来检测系统睡眠/唤醒状态
|
||||||
|
* 使用 org.freedesktop.login1.Manager 的 PrepareForSleep 信号
|
||||||
|
*/
|
||||||
|
class PowerMonitorLinux : public PowerMonitorBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PowerMonitorLinux(QObject *parent = nullptr);
|
||||||
|
~PowerMonitorLinux() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化 Linux 平台的电源监视
|
||||||
|
* @return true 如果初始化成功,否则返回 false
|
||||||
|
*/
|
||||||
|
bool initialize() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
/**
|
||||||
|
* @brief 处理睡眠/唤醒信号的槽函数
|
||||||
|
* @param enteringSleep 是否进入睡眠状态
|
||||||
|
*/
|
||||||
|
void onPrepareForSleep(bool enteringSleep);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 初始化Login1 DBus接口
|
||||||
|
* @return true 如果初始化成功
|
||||||
|
*/
|
||||||
|
bool initLogin1Dbus();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QDBusInterface *m_login1Interface; // Login1 DBus 接口
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Q_OS_LINUX
|
||||||
|
|
||||||
|
#endif // POWERMONITOR_LINUX_H
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef POWERMONITOR_MACOS_H
|
||||||
|
#define POWERMONITOR_MACOS_H
|
||||||
|
|
||||||
|
#include "powermonitor_base.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief macOS 电源监视器类
|
||||||
|
*
|
||||||
|
* 通过监听 macOS 的 IOKit 电源管理通知来检测系统睡眠/唤醒状态
|
||||||
|
* 使用 NSWorkspace 的分布式通知中心监听系统睡眠和唤醒事件
|
||||||
|
*/
|
||||||
|
class PowerMonitorMacOS : public PowerMonitorBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PowerMonitorMacOS(QObject *parent = nullptr);
|
||||||
|
~PowerMonitorMacOS() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化 macOS 平台的电源监视
|
||||||
|
* @return true 如果初始化成功,否则返回 false
|
||||||
|
*/
|
||||||
|
bool initialize() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 处理系统将要睡眠通知的回调
|
||||||
|
*/
|
||||||
|
void handleWillSleep();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理系统已经唤醒通知的回调
|
||||||
|
*/
|
||||||
|
void handleDidWake();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 注册电源管理通知观察者
|
||||||
|
* @return true 如果注册成功
|
||||||
|
*/
|
||||||
|
bool registerPowerNotifications();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注销电源管理通知观察者
|
||||||
|
*/
|
||||||
|
void unregisterPowerNotifications();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *m_workspaceObserver; // NSWorkspace 通知观察者(不透明指针)
|
||||||
|
bool m_initialized; // 是否已初始化
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Q_OS_MAC
|
||||||
|
|
||||||
|
#endif // POWERMONITOR_MACOS_H
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include "powermonitor_macos.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
PowerMonitorMacOS::PowerMonitorMacOS(QObject *parent)
|
||||||
|
: PowerMonitorBase(parent)
|
||||||
|
, m_workspaceObserver(nullptr)
|
||||||
|
, m_initialized(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerMonitorMacOS::~PowerMonitorMacOS()
|
||||||
|
{
|
||||||
|
unregisterPowerNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerMonitorMacOS::initialize()
|
||||||
|
{
|
||||||
|
return registerPowerNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerMonitorMacOS::registerPowerNotifications()
|
||||||
|
{
|
||||||
|
if (m_initialized) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
|
||||||
|
NSNotificationCenter *center = [workspace notificationCenter];
|
||||||
|
|
||||||
|
if (!center) {
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "❌ 无法获取 NSWorkspace 通知中心";
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 PowerMonitorMacOS 实例指针
|
||||||
|
PowerMonitorMacOS *monitor = this;
|
||||||
|
|
||||||
|
// 注册系统将要睡眠通知
|
||||||
|
id sleepObserver = [center addObserverForName:NSWorkspaceWillSleepNotification
|
||||||
|
object:workspace
|
||||||
|
queue:nil
|
||||||
|
usingBlock:^(NSNotification *notification) {
|
||||||
|
Q_UNUSED(notification);
|
||||||
|
monitor->handleWillSleep();
|
||||||
|
}];
|
||||||
|
|
||||||
|
// 注册系统已经唤醒通知
|
||||||
|
id wakeObserver = [center addObserverForName:NSWorkspaceDidWakeNotification
|
||||||
|
object:workspace
|
||||||
|
queue:nil
|
||||||
|
usingBlock:^(NSNotification *notification) {
|
||||||
|
Q_UNUSED(notification);
|
||||||
|
monitor->handleDidWake();
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (sleepObserver && wakeObserver) {
|
||||||
|
// 将观察者保存到数组中
|
||||||
|
NSMutableArray *observers = [NSMutableArray arrayWithCapacity:2];
|
||||||
|
[observers addObject:sleepObserver];
|
||||||
|
[observers addObject:wakeObserver];
|
||||||
|
|
||||||
|
// 保持观察者数组的引用 (手动 retain)
|
||||||
|
m_workspaceObserver = (void *)CFBridgingRetain(observers);
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "✅ 成功注册 macOS 电源管理通知";
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "❌ 注册 macOS 电源管理通知失败";
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerMonitorMacOS::unregisterPowerNotifications()
|
||||||
|
{
|
||||||
|
if (!m_initialized || !m_workspaceObserver) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
|
||||||
|
NSNotificationCenter *center = [workspace notificationCenter];
|
||||||
|
|
||||||
|
// 转换回 NSArray (手动 release)
|
||||||
|
NSArray *observers = (NSArray *)CFBridgingRelease(m_workspaceObserver);
|
||||||
|
|
||||||
|
// 移除所有观察者
|
||||||
|
for (id observer in observers) {
|
||||||
|
[center removeObserver:observer];
|
||||||
|
}
|
||||||
|
|
||||||
|
m_workspaceObserver = nullptr;
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
qDebug() << "✅ 已注销 macOS 电源管理通知观察者";
|
||||||
|
qDebug() << "=================================================";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerMonitorMacOS::handleWillSleep()
|
||||||
|
{
|
||||||
|
emitAboutToSleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerMonitorMacOS::handleDidWake()
|
||||||
|
{
|
||||||
|
emitAboutToWakeUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Q_OS_MAC
|
||||||
|
|
@ -15,7 +15,11 @@
|
||||||
|
|
||||||
// Include volk for Vulkan function loading
|
// Include volk for Vulkan function loading
|
||||||
#define VK_NO_PROTOTYPES
|
#define VK_NO_PROTOTYPES
|
||||||
#include "../../third_party/volk/volk.h"
|
#include "third_party/volk/volk.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include "vulkanwidget_macos.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
|
|
@ -38,6 +42,7 @@ VulkanWidget::VulkanWidget(QWidget *parent)
|
||||||
, m_swapchain(VK_NULL_HANDLE)
|
, m_swapchain(VK_NULL_HANDLE)
|
||||||
, m_commandPool(VK_NULL_HANDLE)
|
, m_commandPool(VK_NULL_HANDLE)
|
||||||
, m_x11Display(nullptr)
|
, m_x11Display(nullptr)
|
||||||
|
, m_metalLayer(nullptr)
|
||||||
, m_initialized(false)
|
, m_initialized(false)
|
||||||
, m_renderingEnabled(false)
|
, m_renderingEnabled(false)
|
||||||
, m_needsResize(false)
|
, m_needsResize(false)
|
||||||
|
|
@ -306,9 +311,19 @@ bool VulkanWidget::createInstance()
|
||||||
createInfo.ppEnabledExtensionNames = extensions.data();
|
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||||
createInfo.enabledLayerCount = 0;
|
createInfo.enabledLayerCount = 0;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// macOS (MoltenVK) requires portability enumeration flag
|
||||||
|
createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||||
|
qDebug() << "macOS: Enabling portability enumeration for MoltenVK";
|
||||||
|
#endif
|
||||||
|
|
||||||
VkResult result = vkCreateInstance(&createInfo, nullptr, &m_instance);
|
VkResult result = vkCreateInstance(&createInfo, nullptr, &m_instance);
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
qDebug() << "Failed to create Vulkan instance, error code:" << result;
|
qDebug() << "Failed to create Vulkan instance, error code:" << result;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
qDebug() << "macOS Hint: Make sure MoltenVK is installed and DYLD_LIBRARY_PATH is set";
|
||||||
|
qDebug() << "Expected path: $VULKAN_SDK/lib (e.g., ~/VulkanSDK/1.4.328.1/macOS/lib)";
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,9 +382,19 @@ bool VulkanWidget::createSurface()
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
// macOS requires MoltenVK and Metal
|
// macOS requires MoltenVK and Metal
|
||||||
|
// Get the native view handle
|
||||||
|
void* nativeView = reinterpret_cast<void*>(window->winId());
|
||||||
|
|
||||||
|
// Setup CAMetalLayer using helper function
|
||||||
|
m_metalLayer = createMetalLayer(nativeView);
|
||||||
|
if (!m_metalLayer) {
|
||||||
|
qDebug() << "Failed to create CAMetalLayer";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
VkMetalSurfaceCreateInfoEXT createInfo = {};
|
VkMetalSurfaceCreateInfoEXT createInfo = {};
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||||
createInfo.pLayer = nullptr; // This needs proper Metal layer setup
|
createInfo.pLayer = m_metalLayer;
|
||||||
|
|
||||||
VkResult result = vkCreateMetalSurfaceEXT(m_instance, &createInfo, nullptr, &m_surface);
|
VkResult result = vkCreateMetalSurfaceEXT(m_instance, &createInfo, nullptr, &m_surface);
|
||||||
#else
|
#else
|
||||||
|
|
@ -1094,6 +1119,14 @@ void VulkanWidget::cleanupVulkan()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Release CAMetalLayer if it was created (macOS only)
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (m_metalLayer) {
|
||||||
|
releaseMetalLayer(m_metalLayer);
|
||||||
|
m_metalLayer = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Cleanup instance
|
// Cleanup instance
|
||||||
if (m_instance != VK_NULL_HANDLE) {
|
if (m_instance != VK_NULL_HANDLE) {
|
||||||
vkDestroyInstance(m_instance, nullptr);
|
vkDestroyInstance(m_instance, nullptr);
|
||||||
|
|
@ -1214,6 +1247,10 @@ std::vector<const char*> VulkanWidget::getRequiredInstanceExtensions()
|
||||||
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||||||
|
// MoltenVK requires portability enumeration extension
|
||||||
|
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||||
|
extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||||
|
qDebug() << "macOS: Adding MoltenVK required extensions";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return extensions;
|
return extensions;
|
||||||
|
|
@ -1223,6 +1260,13 @@ std::vector<const char*> VulkanWidget::getRequiredDeviceExtensions()
|
||||||
{
|
{
|
||||||
std::vector<const char*> extensions;
|
std::vector<const char*> extensions;
|
||||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// MoltenVK requires portability subset extension for device
|
||||||
|
extensions.push_back("VK_KHR_portability_subset");
|
||||||
|
qDebug() << "macOS: Adding portability_subset device extension for MoltenVK";
|
||||||
|
#endif
|
||||||
|
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,7 @@ private:
|
||||||
VkSwapchainKHR m_swapchain;
|
VkSwapchainKHR m_swapchain;
|
||||||
VkCommandPool m_commandPool;
|
VkCommandPool m_commandPool;
|
||||||
void* m_x11Display; // X11 Display pointer (Linux only), stored as void* to avoid X11 header dependency
|
void* m_x11Display; // X11 Display pointer (Linux only), stored as void* to avoid X11 header dependency
|
||||||
|
void* m_metalLayer; // CAMetalLayer pointer (macOS only), stored as void* to avoid Objective-C header dependency
|
||||||
std::vector<VkCommandBuffer> m_commandBuffers;
|
std::vector<VkCommandBuffer> m_commandBuffers;
|
||||||
std::vector<VkSemaphore> m_imageAvailableSemaphores;
|
std::vector<VkSemaphore> m_imageAvailableSemaphores;
|
||||||
std::vector<VkSemaphore> m_renderFinishedSemaphores;
|
std::vector<VkSemaphore> m_renderFinishedSemaphores;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef VULKANWIDGET_MACOS_H
|
||||||
|
#define VULKANWIDGET_MACOS_H
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a CAMetalLayer for the given native view
|
||||||
|
* @param nativeViewPtr Pointer to NSView (cast to void*)
|
||||||
|
* @return Retained pointer to CAMetalLayer (must be released with releaseMetalLayer)
|
||||||
|
*/
|
||||||
|
void* createMetalLayer(void* nativeViewPtr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release the CAMetalLayer
|
||||||
|
* @param metalLayerPtr Pointer to CAMetalLayer (cast to void*)
|
||||||
|
*/
|
||||||
|
void releaseMetalLayer(void* metalLayerPtr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
|
#endif // VULKANWIDGET_MACOS_H
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include "vulkanwidget_macos.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
|
#import <AppKit/NSWindow.h>
|
||||||
|
#import <AppKit/NSView.h>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
void* createMetalLayer(void* nativeViewPtr)
|
||||||
|
{
|
||||||
|
@autoreleasepool {
|
||||||
|
NSView* view = (__bridge NSView*)nativeViewPtr;
|
||||||
|
if (!view) {
|
||||||
|
qDebug() << "createMetalLayer: Invalid NSView";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create CAMetalLayer
|
||||||
|
CAMetalLayer* metalLayer = [CAMetalLayer layer];
|
||||||
|
if (!metalLayer) {
|
||||||
|
qDebug() << "createMetalLayer: Failed to create CAMetalLayer";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the metal layer
|
||||||
|
metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||||
|
metalLayer.framebufferOnly = YES;
|
||||||
|
|
||||||
|
// Get the content scale factor for retina displays
|
||||||
|
NSWindow* window = [view window];
|
||||||
|
CGFloat scaleFactor = window ? [window backingScaleFactor] : 1.0;
|
||||||
|
metalLayer.contentsScale = scaleFactor;
|
||||||
|
|
||||||
|
// Set the layer's drawable size to match the view
|
||||||
|
CGSize drawableSize = view.bounds.size;
|
||||||
|
drawableSize.width *= scaleFactor;
|
||||||
|
drawableSize.height *= scaleFactor;
|
||||||
|
metalLayer.drawableSize = drawableSize;
|
||||||
|
|
||||||
|
// Set view properties
|
||||||
|
[view setWantsLayer:YES];
|
||||||
|
[view setLayer:metalLayer];
|
||||||
|
|
||||||
|
qDebug() << "createMetalLayer: Successfully created CAMetalLayer"
|
||||||
|
<< "size:" << drawableSize.width << "x" << drawableSize.height
|
||||||
|
<< "scale:" << scaleFactor;
|
||||||
|
|
||||||
|
// Return retained pointer (caller must release)
|
||||||
|
return (void*)CFBridgingRetain(metalLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseMetalLayer(void* metalLayerPtr)
|
||||||
|
{
|
||||||
|
if (metalLayerPtr) {
|
||||||
|
CFRelease(metalLayerPtr);
|
||||||
|
qDebug() << "releaseMetalLayer: CAMetalLayer released";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
||||||
Loading…
Reference in New Issue