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

View File

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

View File

@ -9,6 +9,11 @@
#include <direct.h> #include <direct.h>
#include <windows.h> #include <windows.h>
#define mkdir_impl(path, mode) _mkdir(path) #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 #else
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>

View File

@ -1,41 +1,38 @@
#include <platform/window_module.h>
#include <config/app_config.h> #include <config/app_config.h>
#include <event/events.h> #include <event/events.h>
#include <memory> #include <memory>
#include <platform/window_module.h>
#include <utils/logger.h> #include <utils/logger.h>
// SDL Vulkan support // OpenGL ES 3.2 with GLAD
#include <SDL_vulkan.h> #include <glad/glad.h>
// Vulkan
#include <vulkan/vulkan.h>
namespace extra2d { namespace extra2d {
WindowModule::WindowModule() = default; WindowModule::WindowModule() = default;
WindowModule::~WindowModule() { WindowModule::~WindowModule() { shutdown(); }
shutdown();
}
WindowModule::WindowModule(WindowModule&& other) noexcept WindowModule::WindowModule(WindowModule &&other) noexcept
: window_(other.window_) : window_(other.window_), glContext_(other.glContext_),
, shouldClose_(other.shouldClose_) shouldClose_(other.shouldClose_), onClose_(std::move(other.onClose_)),
, onClose_(std::move(other.onClose_)) onResize_(std::move(other.onResize_)),
, onResize_(std::move(other.onResize_)) configListener_(std::move(other.configListener_)) {
, configListener_(std::move(other.configListener_)) {
other.window_ = nullptr; other.window_ = nullptr;
other.glContext_ = nullptr;
} }
WindowModule& WindowModule::operator=(WindowModule&& other) noexcept { WindowModule &WindowModule::operator=(WindowModule &&other) noexcept {
if (this != &other) { if (this != &other) {
shutdown(); shutdown();
window_ = other.window_; window_ = other.window_;
glContext_ = other.glContext_;
shouldClose_ = other.shouldClose_; shouldClose_ = other.shouldClose_;
onClose_ = std::move(other.onClose_); onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_); onResize_ = std::move(other.onResize_);
configListener_ = std::move(other.configListener_); configListener_ = std::move(other.configListener_);
other.window_ = nullptr; other.window_ = nullptr;
other.glContext_ = nullptr;
} }
return *this; return *this;
} }
@ -47,26 +44,27 @@ bool WindowModule::init() {
} }
// 监听模块配置事件 // 监听模块配置事件
configListener_ = std::make_unique<events::OnModuleConfig<AppConfig>::Listener>(); configListener_ =
configListener_->bind([this](const AppConfig& config) { std::make_unique<events::OnModuleConfig<AppConfig>::Listener>();
this->onModuleConfig(config); configListener_->bind(
}); [this](const AppConfig &config) { this->onModuleConfig(config); });
return true; return true;
} }
void WindowModule::shutdown() { void WindowModule::shutdown() {
destroyGLContext();
if (window_) { if (window_) {
SDL_DestroyWindow(window_); SDL_DestroyWindow(window_);
window_ = nullptr; window_ = nullptr;
} }
} }
bool WindowModule::create(const WindowCfg& cfg) { bool WindowModule::create(const WindowCfg &cfg) {
if (window_) if (window_)
return true; return true;
uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN; uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
if (cfg.resizable) { if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_RESIZABLE;
} }
@ -74,7 +72,8 @@ bool WindowModule::create(const WindowCfg& cfg) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} }
window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED, window_ =
SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags); SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
if (!window_) { if (!window_) {
@ -108,10 +107,16 @@ bool WindowModule::pollEvents() {
break; break;
} }
} }
#ifdef __SWITCH__
// Switch 平台:每帧更新控制台,确保日志实时显示
consoleUpdate(NULL);
#endif
return !shouldClose_; return !shouldClose_;
} }
void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) { void WindowModule::handleWindowEvent(const SDL_WindowEvent &evt) {
switch (evt.event) { switch (evt.event) {
case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_RESIZED:
// 发送窗口大小改变事件 // 发送窗口大小改变事件
@ -181,7 +186,7 @@ void WindowModule::setSize(int32 w, int32 h) {
} }
} }
void WindowModule::setTitle(const std::string& title) { void WindowModule::setTitle(const std::string &title) {
if (window_) { if (window_) {
SDL_SetWindowTitle(window_, title.c_str()); SDL_SetWindowTitle(window_, title.c_str());
} }
@ -222,25 +227,52 @@ bool WindowModule::isVisible() const {
return (flags & SDL_WINDOW_SHOWN) != 0; return (flags & SDL_WINDOW_SHOWN) != 0;
} }
std::vector<const char*> WindowModule::getVulkanExtensions() const { bool WindowModule::createGLContext() {
std::vector<const char*> extensions;
if (!window_) { if (!window_) {
return extensions;
}
uint32 count = 0;
SDL_Vulkan_GetInstanceExtensions(window_, &count, nullptr);
extensions.resize(count);
SDL_Vulkan_GetInstanceExtensions(window_, &count, extensions.data());
return extensions;
}
bool WindowModule::createVulkanSurface(void* instance, void** surface) const {
if (!window_ || !instance || !surface) {
return false; return false;
} }
return SDL_Vulkan_CreateSurface(window_, static_cast<VkInstance>(instance), reinterpret_cast<VkSurfaceKHR*>(surface));
// 创建 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);
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;
} }
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 } // namespace extra2d

View File

@ -1,12 +1,13 @@
#include <plugin/plugin_loader.h>
#include <event/event_bus.h> #include <event/event_bus.h>
#include <algorithm> #include <plugin/plugin_loader.h>
#include <queue> #include <queue>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#elif defined(__SWITCH__)
// Nintendo Switch 平台使用 libnx不支持传统动态库加载
#else #else
#include <dlfcn.h> #include <dlfcn.h>
#endif #endif
namespace extra2d { namespace extra2d {
@ -19,7 +20,7 @@ PluginLoader::~PluginLoader() {
} }
// 卸载所有动态库插件 // 卸载所有动态库插件
for (auto& pair : plugins_) { for (auto &pair : plugins_) {
if (pair.second.isDynamic && pair.second.handle) { if (pair.second.isDynamic && pair.second.handle) {
if (pair.second.plugin) { if (pair.second.plugin) {
pair.second.plugin->unload(); pair.second.plugin->unload();
@ -32,25 +33,24 @@ PluginLoader::~PluginLoader() {
} }
} }
PluginLoader::PluginLoader(PluginLoader&&) noexcept = default; PluginLoader::PluginLoader(PluginLoader &&) noexcept = default;
PluginLoader& PluginLoader::operator=(PluginLoader&&) noexcept = default; PluginLoader &PluginLoader::operator=(PluginLoader &&) noexcept = default;
bool PluginLoader::loadFromLibrary(const char* path) { bool PluginLoader::loadFromLibrary(const char *path) {
if (!path) { if (!path) {
return false; return false;
} }
// 加载动态库 // 加载动态库
void* handle = loadDynamicLibrary(path); void *handle = loadDynamicLibrary(path);
if (!handle) { if (!handle) {
return false; return false;
} }
// 获取创建函数 // 获取创建函数
using CreateFunc = IPlugin* (*)(); using CreateFunc = IPlugin *(*)();
CreateFunc createFunc = reinterpret_cast<CreateFunc>( CreateFunc createFunc =
getSymbol(handle, "extra2d_create_plugin") reinterpret_cast<CreateFunc>(getSymbol(handle, "extra2d_create_plugin"));
);
if (!createFunc) { if (!createFunc) {
unloadDynamicLibrary(handle); unloadDynamicLibrary(handle);
@ -58,14 +58,14 @@ bool PluginLoader::loadFromLibrary(const char* path) {
} }
// 创建插件实例 // 创建插件实例
IPlugin* plugin = createFunc(); IPlugin *plugin = createFunc();
if (!plugin) { if (!plugin) {
unloadDynamicLibrary(handle); unloadDynamicLibrary(handle);
return false; return false;
} }
// 检查是否已存在 // 检查是否已存在
const char* name = plugin->getInfo().name.c_str(); const char *name = plugin->getInfo().name.c_str();
if (hasPlugin(name)) { if (hasPlugin(name)) {
delete plugin; delete plugin;
unloadDynamicLibrary(handle); unloadDynamicLibrary(handle);
@ -83,7 +83,8 @@ bool PluginLoader::loadFromLibrary(const char* path) {
sortedPlugins_.clear(); sortedPlugins_.clear();
// 插件加载事件(暂不发送,避免依赖 events.h // 插件加载事件(暂不发送,避免依赖 events.h
// event::broadcast<events::OnPluginLoaded>(name, plugin->getInfo().version.c_str()); // event::broadcast<events::OnPluginLoaded>(name,
// plugin->getInfo().version.c_str());
// 如果已经初始化,立即加载插件 // 如果已经初始化,立即加载插件
if (inited_) { if (inited_) {
@ -96,12 +97,12 @@ bool PluginLoader::loadFromLibrary(const char* path) {
return true; return true;
} }
void PluginLoader::registerPlugin(IPlugin* plugin) { void PluginLoader::registerPlugin(IPlugin *plugin) {
if (!plugin) { if (!plugin) {
return; return;
} }
const char* name = plugin->getInfo().name.c_str(); const char *name = plugin->getInfo().name.c_str();
if (!name) { if (!name) {
return; return;
} }
@ -121,7 +122,8 @@ void PluginLoader::registerPlugin(IPlugin* plugin) {
sortedPlugins_.clear(); sortedPlugins_.clear();
// 插件加载事件(暂不发送,避免依赖 events.h // 插件加载事件(暂不发送,避免依赖 events.h
// event::broadcast<events::OnPluginLoaded>(name, plugin->getInfo().version.c_str()); // event::broadcast<events::OnPluginLoaded>(name,
// plugin->getInfo().version.c_str());
// 如果已经初始化,立即加载插件 // 如果已经初始化,立即加载插件
if (inited_) { if (inited_) {
@ -129,7 +131,7 @@ void PluginLoader::registerPlugin(IPlugin* plugin) {
} }
} }
void PluginLoader::unloadPlugin(const char* name) { void PluginLoader::unloadPlugin(const char *name) {
if (!name) { if (!name) {
return; return;
} }
@ -139,7 +141,7 @@ void PluginLoader::unloadPlugin(const char* name) {
return; return;
} }
PluginEntry& entry = it->second; PluginEntry &entry = it->second;
// 如果已初始化,先卸载 // 如果已初始化,先卸载
if (inited_ && entry.plugin) { if (inited_ && entry.plugin) {
@ -160,7 +162,7 @@ void PluginLoader::unloadPlugin(const char* name) {
sortedPlugins_.clear(); sortedPlugins_.clear();
} }
IPlugin* PluginLoader::getPlugin(const char* name) const { IPlugin *PluginLoader::getPlugin(const char *name) const {
if (!name) { if (!name) {
return nullptr; return nullptr;
} }
@ -169,16 +171,16 @@ IPlugin* PluginLoader::getPlugin(const char* name) const {
return it != plugins_.end() ? it->second.plugin : nullptr; return it != plugins_.end() ? it->second.plugin : nullptr;
} }
bool PluginLoader::hasPlugin(const char* name) const { bool PluginLoader::hasPlugin(const char *name) const {
if (!name) { if (!name) {
return false; return false;
} }
return plugins_.find(name) != plugins_.end(); return plugins_.find(name) != plugins_.end();
} }
std::vector<IPlugin*> PluginLoader::getAllPlugins() const { std::vector<IPlugin *> PluginLoader::getAllPlugins() const {
std::vector<IPlugin*> result; std::vector<IPlugin *> result;
for (const auto& pair : plugins_) { for (const auto &pair : plugins_) {
if (pair.second.plugin) { if (pair.second.plugin) {
result.push_back(pair.second.plugin); result.push_back(pair.second.plugin);
} }
@ -192,8 +194,9 @@ bool PluginLoader::initAll() {
} }
// 检查所有插件的依赖是否满足 // 检查所有插件的依赖是否满足
for (const auto& pair : plugins_) { for (const auto &pair : plugins_) {
if (pair.second.plugin && !checkDependencies(pair.second.plugin->getDependencies())) { if (pair.second.plugin &&
!checkDependencies(pair.second.plugin->getDependencies())) {
return false; return false;
} }
} }
@ -202,7 +205,7 @@ bool PluginLoader::initAll() {
sortPluginsByDependencies(); sortPluginsByDependencies();
// 加载所有插件 // 加载所有插件
for (auto* plugin : sortedPlugins_) { for (auto *plugin : sortedPlugins_) {
if (plugin && !plugin->load()) { if (plugin && !plugin->load()) {
// 加载失败,卸载已加载的插件 // 加载失败,卸载已加载的插件
shutdownAll(); shutdownAll();
@ -229,23 +232,21 @@ void PluginLoader::shutdownAll() {
inited_ = false; inited_ = false;
} }
size_t PluginLoader::getPluginCount() const { size_t PluginLoader::getPluginCount() const { return plugins_.size(); }
return plugins_.size();
}
void PluginLoader::addSearchPath(const char* path) { void PluginLoader::addSearchPath(const char *path) {
if (path) { if (path) {
searchPaths_.push_back(path); searchPaths_.push_back(path);
} }
} }
size_t PluginLoader::loadPluginsFromDirectory(const char* directory) { size_t PluginLoader::loadPluginsFromDirectory(const char *directory) {
// TODO: 实现目录扫描加载 // TODO: 实现目录扫描加载
// 需要平台相关的文件系统操作 // 需要平台相关的文件系统操作
return 0; return 0;
} }
bool PluginLoader::resolveDependencies(IPlugin* plugin) { bool PluginLoader::resolveDependencies(IPlugin *plugin) {
if (!plugin) { if (!plugin) {
return false; return false;
} }
@ -253,8 +254,9 @@ bool PluginLoader::resolveDependencies(IPlugin* plugin) {
return checkDependencies(plugin->getDependencies()); return checkDependencies(plugin->getDependencies());
} }
bool PluginLoader::checkDependencies(const std::vector<std::string>& dependencies) { bool PluginLoader::checkDependencies(
for (const auto& dep : dependencies) { const std::vector<std::string> &dependencies) {
for (const auto &dep : dependencies) {
if (!hasPlugin(dep.c_str())) { if (!hasPlugin(dep.c_str())) {
return false; return false;
} }
@ -272,15 +274,16 @@ void PluginLoader::sortPluginsByDependencies() {
std::unordered_map<std::string, std::vector<std::string>> graph; std::unordered_map<std::string, std::vector<std::string>> graph;
// 初始化 // 初始化
for (const auto& pair : plugins_) { for (const auto &pair : plugins_) {
inDegree[pair.first] = 0; inDegree[pair.first] = 0;
} }
// 构建图 // 构建图
for (const auto& pair : plugins_) { for (const auto &pair : plugins_) {
if (!pair.second.plugin) continue; if (!pair.second.plugin)
continue;
for (const auto& dep : pair.second.plugin->getDependencies()) { for (const auto &dep : pair.second.plugin->getDependencies()) {
graph[dep].push_back(pair.first); graph[dep].push_back(pair.first);
inDegree[pair.first]++; inDegree[pair.first]++;
} }
@ -288,7 +291,7 @@ void PluginLoader::sortPluginsByDependencies() {
// 拓扑排序 // 拓扑排序
std::queue<std::string> q; std::queue<std::string> q;
for (const auto& pair : inDegree) { for (const auto &pair : inDegree) {
if (pair.second == 0) { if (pair.second == 0) {
q.push(pair.first); q.push(pair.first);
} }
@ -300,7 +303,7 @@ void PluginLoader::sortPluginsByDependencies() {
q.pop(); q.pop();
sortedNames.push_back(name); sortedNames.push_back(name);
for (const auto& next : graph[name]) { for (const auto &next : graph[name]) {
inDegree[next]--; inDegree[next]--;
if (inDegree[next] == 0) { if (inDegree[next] == 0) {
q.push(next); q.push(next);
@ -310,7 +313,7 @@ void PluginLoader::sortPluginsByDependencies() {
// 构建排序后的插件列表 // 构建排序后的插件列表
sortedPlugins_.clear(); sortedPlugins_.clear();
for (const auto& name : sortedNames) { for (const auto &name : sortedNames) {
auto it = plugins_.find(name); auto it = plugins_.find(name);
if (it != plugins_.end() && it->second.plugin) { if (it != plugins_.end() && it->second.plugin) {
sortedPlugins_.push_back(it->second.plugin); sortedPlugins_.push_back(it->second.plugin);
@ -318,29 +321,44 @@ void PluginLoader::sortPluginsByDependencies() {
} }
} }
void* PluginLoader::loadDynamicLibrary(const char* path) { void *PluginLoader::loadDynamicLibrary(const char *path) {
#ifdef _WIN32 #ifdef _WIN32
return LoadLibraryA(path); return LoadLibraryA(path);
#elif defined(__SWITCH__)
// Nintendo Switch 平台不支持动态库加载
(void)path;
return nullptr;
#else #else
return dlopen(path, RTLD_LAZY); return dlopen(path, RTLD_LAZY);
#endif #endif
} }
void PluginLoader::unloadDynamicLibrary(void* handle) { void PluginLoader::unloadDynamicLibrary(void *handle) {
if (!handle) return; if (!handle)
return;
#ifdef _WIN32 #ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(handle)); FreeLibrary(static_cast<HMODULE>(handle));
#elif defined(__SWITCH__)
// Nintendo Switch 平台不支持动态库加载
(void)handle;
#else #else
dlclose(handle); dlclose(handle);
#endif #endif
} }
void* PluginLoader::getSymbol(void* handle, const char* name) { void *PluginLoader::getSymbol(void *handle, const char *name) {
if (!handle || !name) return nullptr; if (!handle || !name)
return nullptr;
#ifdef _WIN32 #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 #else
return dlsym(handle, name); return dlsym(handle, name);
#endif #endif

View File

@ -9,6 +9,25 @@ bool Logger::consoleOutput_ = true;
bool Logger::fileOutput_ = false; bool Logger::fileOutput_ = false;
std::string Logger::logFile_; 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 * @brief
* @param level * @param level
@ -41,11 +60,17 @@ void Logger::init() {
return; return;
} }
// 设置 SDL 日志级别为详细模式(允许所有级别的日志) #ifdef __SWITCH__
// Switch 平台:初始化控制台并设置 SDL 日志重定向
consoleInit(NULL);
SDL_LogSetOutputFunction(SwitchLogOutput, nullptr);
#else
// 其他平台:设置 SDL 日志级别为详细模式
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE); SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE);
#endif
initialized_ = true; initialized_ = true;
log(LogLevel::Info, "Logger initialized with SDL2"); log(LogLevel::Info, "Logger initialized");
} }
/** /**
@ -54,6 +79,9 @@ void Logger::init() {
void Logger::shutdown() { void Logger::shutdown() {
if (initialized_) { if (initialized_) {
log(LogLevel::Info, "Logger shutting down"); log(LogLevel::Info, "Logger shutting down");
#ifdef __SWITCH__
consoleExit(NULL);
#endif
} }
initialized_ = false; 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 if target_plat == "mingw" then
add_requires("glm", "libsdl2", "libsdl2_mixer", "vulkansdk") add_requires("glm", "libsdl2", "libsdl2_mixer")
end end
-- ============================================== -- ==============================================

View File

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

View File

@ -34,7 +34,7 @@ function define_switch_toolchain()
add_includedirs(path.join(devkitPro, "libnx/include")) add_includedirs(path.join(devkitPro, "libnx/include"))
add_linkdirs(path.join(devkitPro, "libnx/lib")) 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"))
add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2")) add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2"))
add_linkdirs(path.join(devkitPro, "portlibs/switch/lib")) add_linkdirs(path.join(devkitPro, "portlibs/switch/lib"))