feat(switch): 添加 Nintendo Switch 平台支持

- 添加 Switch 平台专用的日志输出和初始化处理
- 修改窗口模块以支持 OpenGL ES 3.2 渲染
- 更新构建系统以支持 Switch 平台编译
- 添加 Switch 平台文件系统操作支持
- 移除 Vulkan 相关依赖,改用 OpenGL ES
- 更新插件加载器以适配 Switch 平台限制
This commit is contained in:
ChestnutYueyue 2026-03-01 03:48:51 +08:00
parent fb11f2a71e
commit 6717015e28
11 changed files with 4474 additions and 27686 deletions

View File

@ -20,9 +20,9 @@ using ResizeCb = std::function<void(int32 w, int32 h)>;
using CloseCb = std::function<void()>;
/**
* @brief - Vulkan
* @brief - OpenGL ES 3.2
*
* SDL2 Vulkan
* SDL2 OpenGL ES 3.2
* 使 Module
*/
class WindowModule : public Module {
@ -122,18 +122,26 @@ public:
bool shouldClose() const { return shouldClose_; }
/**
* @brief Vulkan
* @return
*/
std::vector<const char *> getVulkanExtensions() const;
/**
* @brief Vulkan
* @param instance Vulkan
* @param surface
* @brief OpenGL ES
* @return
*/
bool createVulkanSurface(void *instance, void **surface) const;
bool createGLContext();
/**
* @brief OpenGL ES
*/
void destroyGLContext();
/**
* @brief
*/
void swapBuffers();
/**
* @brief OpenGL ES
* @return SDL_GLContext
*/
void* getGLContext() const;
private:
void handleWindowEvent(const SDL_WindowEvent &evt);
@ -147,6 +155,7 @@ private:
void onModuleConfig(const AppConfig &config);
SDL_Window *window_ = nullptr;
SDL_GLContext glContext_ = nullptr;
bool shouldClose_ = false;
CloseCb onClose_;

View File

@ -6,6 +6,11 @@
#include <type_traits>
#include <types/base/types.h>
// Switch 平台支持
#ifdef __SWITCH__
#include <switch.h>
#endif
#ifndef TEST_BUILD
// SDL2 日志头文件(测试构建时不使用)
#include <SDL.h>

View File

@ -9,6 +9,11 @@
#include <direct.h>
#include <windows.h>
#define mkdir_impl(path, mode) _mkdir(path)
#elif defined(__SWITCH__)
#include <dirent.h>
#include <unistd.h>
// Switch 使用 ::mkdir 避免与类成员函数冲突
#define mkdir_impl(path, mode) ::mkdir(path, mode)
#else
#include <dirent.h>
#include <unistd.h>

View File

@ -1,246 +1,278 @@
#include <platform/window_module.h>
#include <config/app_config.h>
#include <event/events.h>
#include <memory>
#include <platform/window_module.h>
#include <utils/logger.h>
// SDL Vulkan support
#include <SDL_vulkan.h>
// Vulkan
#include <vulkan/vulkan.h>
// OpenGL ES 3.2 with GLAD
#include <glad/glad.h>
namespace extra2d {
WindowModule::WindowModule() = default;
WindowModule::~WindowModule() {
WindowModule::~WindowModule() { shutdown(); }
WindowModule::WindowModule(WindowModule &&other) noexcept
: window_(other.window_), glContext_(other.glContext_),
shouldClose_(other.shouldClose_), onClose_(std::move(other.onClose_)),
onResize_(std::move(other.onResize_)),
configListener_(std::move(other.configListener_)) {
other.window_ = nullptr;
other.glContext_ = nullptr;
}
WindowModule &WindowModule::operator=(WindowModule &&other) noexcept {
if (this != &other) {
shutdown();
}
WindowModule::WindowModule(WindowModule&& other) noexcept
: window_(other.window_)
, shouldClose_(other.shouldClose_)
, onClose_(std::move(other.onClose_))
, onResize_(std::move(other.onResize_))
, configListener_(std::move(other.configListener_)) {
window_ = other.window_;
glContext_ = other.glContext_;
shouldClose_ = other.shouldClose_;
onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_);
configListener_ = std::move(other.configListener_);
other.window_ = nullptr;
}
WindowModule& WindowModule::operator=(WindowModule&& other) noexcept {
if (this != &other) {
shutdown();
window_ = other.window_;
shouldClose_ = other.shouldClose_;
onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_);
configListener_ = std::move(other.configListener_);
other.window_ = nullptr;
}
return *this;
other.glContext_ = nullptr;
}
return *this;
}
bool WindowModule::init() {
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError());
return false;
}
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError());
return false;
}
// 监听模块配置事件
configListener_ = std::make_unique<events::OnModuleConfig<AppConfig>::Listener>();
configListener_->bind([this](const AppConfig& config) {
this->onModuleConfig(config);
});
// 监听模块配置事件
configListener_ =
std::make_unique<events::OnModuleConfig<AppConfig>::Listener>();
configListener_->bind(
[this](const AppConfig &config) { this->onModuleConfig(config); });
return true;
return true;
}
void WindowModule::shutdown() {
if (window_) {
SDL_DestroyWindow(window_);
window_ = nullptr;
}
destroyGLContext();
if (window_) {
SDL_DestroyWindow(window_);
window_ = nullptr;
}
}
bool WindowModule::create(const WindowCfg& cfg) {
if (window_)
return true;
uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN;
if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE;
}
if (cfg.fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
if (!window_) {
E2D_LOG_ERROR("Failed to create window: {}", SDL_GetError());
return false;
}
// 发送窗口显示事件
events::OnShow::emit();
E2D_LOG_INFO("Window created: {}x{}", cfg.width, cfg.height);
bool WindowModule::create(const WindowCfg &cfg) {
if (window_)
return true;
uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE;
}
if (cfg.fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
window_ =
SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
if (!window_) {
E2D_LOG_ERROR("Failed to create window: {}", SDL_GetError());
return false;
}
// 发送窗口显示事件
events::OnShow::emit();
E2D_LOG_INFO("Window created: {}x{}", cfg.width, cfg.height);
return true;
}
bool WindowModule::pollEvents() {
SDL_Event evt;
while (SDL_PollEvent(&evt)) {
switch (evt.type) {
case SDL_QUIT:
shouldClose_ = true;
// 发送窗口关闭事件
events::OnClose::emit();
if (onClose_)
onClose_();
break;
case SDL_WINDOWEVENT:
handleWindowEvent(evt.window);
break;
default:
// 输入事件由 InputModule 处理
break;
}
SDL_Event evt;
while (SDL_PollEvent(&evt)) {
switch (evt.type) {
case SDL_QUIT:
shouldClose_ = true;
// 发送窗口关闭事件
events::OnClose::emit();
if (onClose_)
onClose_();
break;
case SDL_WINDOWEVENT:
handleWindowEvent(evt.window);
break;
default:
// 输入事件由 InputModule 处理
break;
}
return !shouldClose_;
}
#ifdef __SWITCH__
// Switch 平台:每帧更新控制台,确保日志实时显示
consoleUpdate(NULL);
#endif
return !shouldClose_;
}
void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) {
switch (evt.event) {
case SDL_WINDOWEVENT_RESIZED:
// 发送窗口大小改变事件
events::OnResize::emit(evt.data1, evt.data2);
if (onResize_) {
onResize_(evt.data1, evt.data2);
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
// 发送窗口获得焦点事件
events::OnFocus::emit();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
// 发送窗口失去焦点事件
events::OnBlur::emit();
break;
case SDL_WINDOWEVENT_SHOWN:
// 发送窗口显示事件
events::OnShow::emit();
break;
case SDL_WINDOWEVENT_HIDDEN:
// 发送窗口隐藏事件
events::OnHide::emit();
break;
case SDL_WINDOWEVENT_CLOSE:
shouldClose_ = true;
// 发送窗口关闭事件
events::OnClose::emit();
if (onClose_)
onClose_();
break;
void WindowModule::handleWindowEvent(const SDL_WindowEvent &evt) {
switch (evt.event) {
case SDL_WINDOWEVENT_RESIZED:
// 发送窗口大小改变事件
events::OnResize::emit(evt.data1, evt.data2);
if (onResize_) {
onResize_(evt.data1, evt.data2);
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
// 发送窗口获得焦点事件
events::OnFocus::emit();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
// 发送窗口失去焦点事件
events::OnBlur::emit();
break;
case SDL_WINDOWEVENT_SHOWN:
// 发送窗口显示事件
events::OnShow::emit();
break;
case SDL_WINDOWEVENT_HIDDEN:
// 发送窗口隐藏事件
events::OnHide::emit();
break;
case SDL_WINDOWEVENT_CLOSE:
shouldClose_ = true;
// 发送窗口关闭事件
events::OnClose::emit();
if (onClose_)
onClose_();
break;
}
}
void WindowModule::onModuleConfig(const AppConfig &config) {
WindowCfg cfg;
cfg.title = config.title;
cfg.width = config.width;
cfg.height = config.height;
cfg.fullscreen = config.fullscreen;
cfg.resizable = config.resizable;
WindowCfg cfg;
cfg.title = config.title;
cfg.width = config.width;
cfg.height = config.height;
cfg.fullscreen = config.fullscreen;
cfg.resizable = config.resizable;
if (create(cfg)) {
setVisible(true);
}
if (create(cfg)) {
setVisible(true);
}
}
Size WindowModule::getSize() const {
if (!window_)
return Size(0, 0);
int w, h;
SDL_GetWindowSize(window_, &w, &h);
return Size(w, h);
if (!window_)
return Size(0, 0);
int w, h;
SDL_GetWindowSize(window_, &w, &h);
return Size(w, h);
}
Vec2 WindowModule::getPosition() const {
if (!window_)
return Vec2(0, 0);
int x, y;
SDL_GetWindowPosition(window_, &x, &y);
return Vec2(static_cast<float>(x), static_cast<float>(y));
if (!window_)
return Vec2(0, 0);
int x, y;
SDL_GetWindowPosition(window_, &x, &y);
return Vec2(static_cast<float>(x), static_cast<float>(y));
}
void WindowModule::setSize(int32 w, int32 h) {
if (window_) {
SDL_SetWindowSize(window_, w, h);
}
if (window_) {
SDL_SetWindowSize(window_, w, h);
}
}
void WindowModule::setTitle(const std::string& title) {
if (window_) {
SDL_SetWindowTitle(window_, title.c_str());
}
void WindowModule::setTitle(const std::string &title) {
if (window_) {
SDL_SetWindowTitle(window_, title.c_str());
}
}
void WindowModule::setFullscreen(bool fullscreen) {
if (window_) {
SDL_SetWindowFullscreen(window_,
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
if (window_) {
SDL_SetWindowFullscreen(window_,
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
}
bool WindowModule::isFullscreen() const {
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}
void WindowModule::setVisible(bool visible) {
if (window_) {
if (visible) {
SDL_ShowWindow(window_);
// 发送窗口显示事件
events::OnShow::emit();
} else {
SDL_HideWindow(window_);
// 发送窗口隐藏事件
events::OnHide::emit();
}
if (window_) {
if (visible) {
SDL_ShowWindow(window_);
// 发送窗口显示事件
events::OnShow::emit();
} else {
SDL_HideWindow(window_);
// 发送窗口隐藏事件
events::OnHide::emit();
}
}
}
bool WindowModule::isVisible() const {
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_SHOWN) != 0;
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_SHOWN) != 0;
}
std::vector<const char*> WindowModule::getVulkanExtensions() const {
std::vector<const char*> extensions;
if (!window_) {
return extensions;
}
bool WindowModule::createGLContext() {
if (!window_) {
return false;
}
uint32 count = 0;
SDL_Vulkan_GetInstanceExtensions(window_, &count, nullptr);
extensions.resize(count);
SDL_Vulkan_GetInstanceExtensions(window_, &count, extensions.data());
// 创建 OpenGL ES 3.2 上下文
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
return extensions;
glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) {
E2D_LOG_ERROR("Failed to create OpenGL ES context: {}", SDL_GetError());
return false;
}
// 初始化 GLAD
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
E2D_LOG_ERROR("Failed to initialize GLAD");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
E2D_LOG_INFO("OpenGL ES context created: {}.{}", GLVersion.major,
GLVersion.minor);
return true;
}
bool WindowModule::createVulkanSurface(void* instance, void** surface) const {
if (!window_ || !instance || !surface) {
return false;
}
return SDL_Vulkan_CreateSurface(window_, static_cast<VkInstance>(instance), reinterpret_cast<VkSurfaceKHR*>(surface));
void WindowModule::destroyGLContext() {
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
}
}
void WindowModule::swapBuffers() {
if (window_) {
SDL_GL_SwapWindow(window_);
}
}
void *WindowModule::getGLContext() const { return glContext_; }
} // namespace extra2d

View File

@ -1,12 +1,13 @@
#include <plugin/plugin_loader.h>
#include <event/event_bus.h>
#include <algorithm>
#include <plugin/plugin_loader.h>
#include <queue>
#ifdef _WIN32
#include <windows.h>
#include <windows.h>
#elif defined(__SWITCH__)
// Nintendo Switch 平台使用 libnx不支持传统动态库加载
#else
#include <dlfcn.h>
#include <dlfcn.h>
#endif
namespace extra2d {
@ -14,335 +15,352 @@ namespace extra2d {
PluginLoader::PluginLoader() = default;
PluginLoader::~PluginLoader() {
if (inited_) {
shutdownAll();
}
if (inited_) {
shutdownAll();
}
// 卸载所有动态库插件
for (auto& pair : plugins_) {
if (pair.second.isDynamic && pair.second.handle) {
if (pair.second.plugin) {
pair.second.plugin->unload();
if (pair.second.owned) {
delete pair.second.plugin;
}
}
unloadDynamicLibrary(pair.second.handle);
// 卸载所有动态库插件
for (auto &pair : plugins_) {
if (pair.second.isDynamic && pair.second.handle) {
if (pair.second.plugin) {
pair.second.plugin->unload();
if (pair.second.owned) {
delete pair.second.plugin;
}
}
unloadDynamicLibrary(pair.second.handle);
}
}
}
PluginLoader::PluginLoader(PluginLoader&&) noexcept = default;
PluginLoader& PluginLoader::operator=(PluginLoader&&) noexcept = default;
PluginLoader::PluginLoader(PluginLoader &&) noexcept = default;
PluginLoader &PluginLoader::operator=(PluginLoader &&) noexcept = default;
bool PluginLoader::loadFromLibrary(const char* path) {
if (!path) {
return false;
bool PluginLoader::loadFromLibrary(const char *path) {
if (!path) {
return false;
}
// 加载动态库
void *handle = loadDynamicLibrary(path);
if (!handle) {
return false;
}
// 获取创建函数
using CreateFunc = IPlugin *(*)();
CreateFunc createFunc =
reinterpret_cast<CreateFunc>(getSymbol(handle, "extra2d_create_plugin"));
if (!createFunc) {
unloadDynamicLibrary(handle);
return false;
}
// 创建插件实例
IPlugin *plugin = createFunc();
if (!plugin) {
unloadDynamicLibrary(handle);
return false;
}
// 检查是否已存在
const char *name = plugin->getInfo().name.c_str();
if (hasPlugin(name)) {
delete plugin;
unloadDynamicLibrary(handle);
return false;
}
// 添加到插件列表
PluginEntry entry;
entry.plugin = plugin;
entry.handle = handle;
entry.isDynamic = true;
entry.owned = true;
plugins_[name] = entry;
sortedPlugins_.clear();
// 插件加载事件(暂不发送,避免依赖 events.h
// event::broadcast<events::OnPluginLoaded>(name,
// plugin->getInfo().version.c_str());
// 如果已经初始化,立即加载插件
if (inited_) {
if (!plugin->load()) {
unloadPlugin(name);
return false;
}
}
// 加载动态库
void* handle = loadDynamicLibrary(path);
if (!handle) {
return false;
}
// 获取创建函数
using CreateFunc = IPlugin* (*)();
CreateFunc createFunc = reinterpret_cast<CreateFunc>(
getSymbol(handle, "extra2d_create_plugin")
);
if (!createFunc) {
unloadDynamicLibrary(handle);
return false;
}
// 创建插件实例
IPlugin* plugin = createFunc();
if (!plugin) {
unloadDynamicLibrary(handle);
return false;
}
// 检查是否已存在
const char* name = plugin->getInfo().name.c_str();
if (hasPlugin(name)) {
delete plugin;
unloadDynamicLibrary(handle);
return false;
}
// 添加到插件列表
PluginEntry entry;
entry.plugin = plugin;
entry.handle = handle;
entry.isDynamic = true;
entry.owned = true;
plugins_[name] = entry;
sortedPlugins_.clear();
// 插件加载事件(暂不发送,避免依赖 events.h
// event::broadcast<events::OnPluginLoaded>(name, plugin->getInfo().version.c_str());
// 如果已经初始化,立即加载插件
if (inited_) {
if (!plugin->load()) {
unloadPlugin(name);
return false;
}
}
return true;
return true;
}
void PluginLoader::registerPlugin(IPlugin* plugin) {
if (!plugin) {
return;
}
void PluginLoader::registerPlugin(IPlugin *plugin) {
if (!plugin) {
return;
}
const char* name = plugin->getInfo().name.c_str();
if (!name) {
return;
}
const char *name = plugin->getInfo().name.c_str();
if (!name) {
return;
}
// 如果已存在同名插件,先卸载旧的
if (hasPlugin(name)) {
unloadPlugin(name);
}
// 如果已存在同名插件,先卸载旧的
if (hasPlugin(name)) {
unloadPlugin(name);
}
PluginEntry entry;
entry.plugin = plugin;
entry.handle = nullptr;
entry.isDynamic = false;
entry.owned = false;
PluginEntry entry;
entry.plugin = plugin;
entry.handle = nullptr;
entry.isDynamic = false;
entry.owned = false;
plugins_[name] = entry;
sortedPlugins_.clear();
plugins_[name] = entry;
sortedPlugins_.clear();
// 插件加载事件(暂不发送,避免依赖 events.h
// event::broadcast<events::OnPluginLoaded>(name, plugin->getInfo().version.c_str());
// 插件加载事件(暂不发送,避免依赖 events.h
// event::broadcast<events::OnPluginLoaded>(name,
// plugin->getInfo().version.c_str());
// 如果已经初始化,立即加载插件
if (inited_) {
plugin->load();
}
// 如果已经初始化,立即加载插件
if (inited_) {
plugin->load();
}
}
void PluginLoader::unloadPlugin(const char* name) {
if (!name) {
return;
}
void PluginLoader::unloadPlugin(const char *name) {
if (!name) {
return;
}
auto it = plugins_.find(name);
if (it == plugins_.end()) {
return;
}
auto it = plugins_.find(name);
if (it == plugins_.end()) {
return;
}
PluginEntry& entry = it->second;
PluginEntry &entry = it->second;
// 如果已初始化,先卸载
if (inited_ && entry.plugin) {
entry.plugin->unload();
}
// 如果已初始化,先卸载
if (inited_ && entry.plugin) {
entry.plugin->unload();
}
// 如果是动态加载且由加载器管理,删除实例
if (entry.isDynamic && entry.owned && entry.plugin) {
delete entry.plugin;
}
// 如果是动态加载且由加载器管理,删除实例
if (entry.isDynamic && entry.owned && entry.plugin) {
delete entry.plugin;
}
// 卸载动态库
if (entry.handle) {
unloadDynamicLibrary(entry.handle);
}
// 卸载动态库
if (entry.handle) {
unloadDynamicLibrary(entry.handle);
}
plugins_.erase(it);
sortedPlugins_.clear();
plugins_.erase(it);
sortedPlugins_.clear();
}
IPlugin* PluginLoader::getPlugin(const char* name) const {
if (!name) {
return nullptr;
}
IPlugin *PluginLoader::getPlugin(const char *name) const {
if (!name) {
return nullptr;
}
auto it = plugins_.find(name);
return it != plugins_.end() ? it->second.plugin : nullptr;
auto it = plugins_.find(name);
return it != plugins_.end() ? it->second.plugin : nullptr;
}
bool PluginLoader::hasPlugin(const char* name) const {
if (!name) {
return false;
}
return plugins_.find(name) != plugins_.end();
bool PluginLoader::hasPlugin(const char *name) const {
if (!name) {
return false;
}
return plugins_.find(name) != plugins_.end();
}
std::vector<IPlugin*> PluginLoader::getAllPlugins() const {
std::vector<IPlugin*> result;
for (const auto& pair : plugins_) {
if (pair.second.plugin) {
result.push_back(pair.second.plugin);
}
std::vector<IPlugin *> PluginLoader::getAllPlugins() const {
std::vector<IPlugin *> result;
for (const auto &pair : plugins_) {
if (pair.second.plugin) {
result.push_back(pair.second.plugin);
}
return result;
}
return result;
}
bool PluginLoader::initAll() {
if (inited_) {
return true;
}
// 检查所有插件的依赖是否满足
for (const auto& pair : plugins_) {
if (pair.second.plugin && !checkDependencies(pair.second.plugin->getDependencies())) {
return false;
}
}
// 按依赖顺序排序
sortPluginsByDependencies();
// 加载所有插件
for (auto* plugin : sortedPlugins_) {
if (plugin && !plugin->load()) {
// 加载失败,卸载已加载的插件
shutdownAll();
return false;
}
}
inited_ = true;
if (inited_) {
return true;
}
// 检查所有插件的依赖是否满足
for (const auto &pair : plugins_) {
if (pair.second.plugin &&
!checkDependencies(pair.second.plugin->getDependencies())) {
return false;
}
}
// 按依赖顺序排序
sortPluginsByDependencies();
// 加载所有插件
for (auto *plugin : sortedPlugins_) {
if (plugin && !plugin->load()) {
// 加载失败,卸载已加载的插件
shutdownAll();
return false;
}
}
inited_ = true;
return true;
}
void PluginLoader::shutdownAll() {
if (!inited_) {
return;
}
if (!inited_) {
return;
}
// 按依赖逆序卸载
for (auto it = sortedPlugins_.rbegin(); it != sortedPlugins_.rend(); ++it) {
if (*it) {
(*it)->unload();
}
// 按依赖逆序卸载
for (auto it = sortedPlugins_.rbegin(); it != sortedPlugins_.rend(); ++it) {
if (*it) {
(*it)->unload();
}
}
inited_ = false;
inited_ = false;
}
size_t PluginLoader::getPluginCount() const {
return plugins_.size();
size_t PluginLoader::getPluginCount() const { return plugins_.size(); }
void PluginLoader::addSearchPath(const char *path) {
if (path) {
searchPaths_.push_back(path);
}
}
void PluginLoader::addSearchPath(const char* path) {
if (path) {
searchPaths_.push_back(path);
size_t PluginLoader::loadPluginsFromDirectory(const char *directory) {
// TODO: 实现目录扫描加载
// 需要平台相关的文件系统操作
return 0;
}
bool PluginLoader::resolveDependencies(IPlugin *plugin) {
if (!plugin) {
return false;
}
return checkDependencies(plugin->getDependencies());
}
bool PluginLoader::checkDependencies(
const std::vector<std::string> &dependencies) {
for (const auto &dep : dependencies) {
if (!hasPlugin(dep.c_str())) {
return false;
}
}
size_t PluginLoader::loadPluginsFromDirectory(const char* directory) {
// TODO: 实现目录扫描加载
// 需要平台相关的文件系统操作
return 0;
}
bool PluginLoader::resolveDependencies(IPlugin* plugin) {
if (!plugin) {
return false;
}
return checkDependencies(plugin->getDependencies());
}
bool PluginLoader::checkDependencies(const std::vector<std::string>& dependencies) {
for (const auto& dep : dependencies) {
if (!hasPlugin(dep.c_str())) {
return false;
}
}
return true;
}
return true;
}
void PluginLoader::sortPluginsByDependencies() {
if (!sortedPlugins_.empty()) {
return;
if (!sortedPlugins_.empty()) {
return;
}
// 拓扑排序处理依赖关系
std::unordered_map<std::string, int> inDegree;
std::unordered_map<std::string, std::vector<std::string>> graph;
// 初始化
for (const auto &pair : plugins_) {
inDegree[pair.first] = 0;
}
// 构建图
for (const auto &pair : plugins_) {
if (!pair.second.plugin)
continue;
for (const auto &dep : pair.second.plugin->getDependencies()) {
graph[dep].push_back(pair.first);
inDegree[pair.first]++;
}
}
// 拓扑排序处理依赖关系
std::unordered_map<std::string, int> inDegree;
std::unordered_map<std::string, std::vector<std::string>> graph;
// 初始化
for (const auto& pair : plugins_) {
inDegree[pair.first] = 0;
// 拓扑排序
std::queue<std::string> q;
for (const auto &pair : inDegree) {
if (pair.second == 0) {
q.push(pair.first);
}
}
// 构建图
for (const auto& pair : plugins_) {
if (!pair.second.plugin) continue;
std::vector<std::string> sortedNames;
while (!q.empty()) {
std::string name = q.front();
q.pop();
sortedNames.push_back(name);
for (const auto& dep : pair.second.plugin->getDependencies()) {
graph[dep].push_back(pair.first);
inDegree[pair.first]++;
}
for (const auto &next : graph[name]) {
inDegree[next]--;
if (inDegree[next] == 0) {
q.push(next);
}
}
}
// 拓扑排序
std::queue<std::string> q;
for (const auto& pair : inDegree) {
if (pair.second == 0) {
q.push(pair.first);
}
}
std::vector<std::string> sortedNames;
while (!q.empty()) {
std::string name = q.front();
q.pop();
sortedNames.push_back(name);
for (const auto& next : graph[name]) {
inDegree[next]--;
if (inDegree[next] == 0) {
q.push(next);
}
}
}
// 构建排序后的插件列表
sortedPlugins_.clear();
for (const auto& name : sortedNames) {
auto it = plugins_.find(name);
if (it != plugins_.end() && it->second.plugin) {
sortedPlugins_.push_back(it->second.plugin);
}
// 构建排序后的插件列表
sortedPlugins_.clear();
for (const auto &name : sortedNames) {
auto it = plugins_.find(name);
if (it != plugins_.end() && it->second.plugin) {
sortedPlugins_.push_back(it->second.plugin);
}
}
}
void* PluginLoader::loadDynamicLibrary(const char* path) {
void *PluginLoader::loadDynamicLibrary(const char *path) {
#ifdef _WIN32
return LoadLibraryA(path);
return LoadLibraryA(path);
#elif defined(__SWITCH__)
// Nintendo Switch 平台不支持动态库加载
(void)path;
return nullptr;
#else
return dlopen(path, RTLD_LAZY);
return dlopen(path, RTLD_LAZY);
#endif
}
void PluginLoader::unloadDynamicLibrary(void* handle) {
if (!handle) return;
void PluginLoader::unloadDynamicLibrary(void *handle) {
if (!handle)
return;
#ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(handle));
FreeLibrary(static_cast<HMODULE>(handle));
#elif defined(__SWITCH__)
// Nintendo Switch 平台不支持动态库加载
(void)handle;
#else
dlclose(handle);
dlclose(handle);
#endif
}
void* PluginLoader::getSymbol(void* handle, const char* name) {
if (!handle || !name) return nullptr;
void *PluginLoader::getSymbol(void *handle, const char *name) {
if (!handle || !name)
return nullptr;
#ifdef _WIN32
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name));
return reinterpret_cast<void *>(
GetProcAddress(static_cast<HMODULE>(handle), name));
#elif defined(__SWITCH__)
// Nintendo Switch 平台不支持动态库加载
(void)handle;
(void)name;
return nullptr;
#else
return dlsym(handle, name);
return dlsym(handle, name);
#endif
}

View File

@ -9,6 +9,25 @@ bool Logger::consoleOutput_ = true;
bool Logger::fileOutput_ = false;
std::string Logger::logFile_;
// Switch 平台:自定义 SDL 日志输出函数
#ifdef __SWITCH__
/**
* @brief Switch
* @param userdata
* @param category
* @param priority
* @param message
*/
static void SwitchLogOutput(void *userdata, int category,
SDL_LogPriority priority, const char *message) {
(void)userdata;
(void)category;
(void)priority;
// 输出到 Switch 控制台
printf("%s\n", message);
}
#endif
/**
* @brief
* @param level
@ -41,11 +60,17 @@ void Logger::init() {
return;
}
// 设置 SDL 日志级别为详细模式(允许所有级别的日志)
#ifdef __SWITCH__
// Switch 平台:初始化控制台并设置 SDL 日志重定向
consoleInit(NULL);
SDL_LogSetOutputFunction(SwitchLogOutput, nullptr);
#else
// 其他平台:设置 SDL 日志级别为详细模式
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE);
#endif
initialized_ = true;
log(LogLevel::Info, "Logger initialized with SDL2");
log(LogLevel::Info, "Logger initialized");
}
/**
@ -54,6 +79,9 @@ void Logger::init() {
void Logger::shutdown() {
if (initialized_) {
log(LogLevel::Info, "Logger shutting down");
#ifdef __SWITCH__
consoleExit(NULL);
#endif
}
initialized_ = false;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -78,7 +78,7 @@ end
-- ==============================================
if target_plat == "mingw" then
add_requires("glm", "libsdl2", "libsdl2_mixer", "vulkansdk")
add_requires("glm", "libsdl2", "libsdl2_mixer")
end
-- ==============================================

View File

@ -13,24 +13,27 @@ function define_extra2d_engine()
target("extra2d")
set_kind("static")
add_files("src/**.cpp|core/*.cpp|module/module_manager.cpp|plugin/plugin_manager.cpp|platform/window.cpp|platform/input.cpp")
add_files("src/**.cpp")
add_files("third_party/glad/src/glad.c")
add_includedirs("include", {public = true})
add_includedirs("third_party", {public = true})
add_includedirs("third_party/glad/include", {public = true})
local plat = get_current_plat()
if plat == "mingw" then
add_defines("_UNICODE", "UNICODE")
-- 使用 xmake 官方包
add_packages("glm", "libsdl2", "libsdl2_mixer", "vulkansdk", {public = true})
add_packages("glm", "libsdl2", "libsdl2_mixer", {public = true})
add_syslinks("winmm", "imm32", "version", "setupapi", {public = true})
elseif plat == "switch" then
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true})
add_linkdirs(devkitPro .. "/portlibs/switch/lib")
-- Switch 平台可能需要不同的图形库
-- Switch 平台使用 OpenGL ES + EGL + Mesa 驱动
-- 注意:链接顺序很重要,被依赖的库要放在后面
add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "drm_nouveau",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
{public = true})
end

View File

@ -34,7 +34,7 @@ function define_switch_toolchain()
add_includedirs(path.join(devkitPro, "libnx/include"))
add_linkdirs(path.join(devkitPro, "libnx/lib"))
-- portlibs 路径(EGL + 桌面 OpenGL + SDL2
-- portlibs 路径(OpenGL ES + EGL + SDL2
add_includedirs(path.join(devkitPro, "portlibs/switch/include"))
add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2"))
add_linkdirs(path.join(devkitPro, "portlibs/switch/lib"))