From ee9055ceef3591961b3727139cbcbf1ff668f95f Mon Sep 17 00:00:00 2001 From: Nanako <469449812@qq.com> Date: Sun, 9 Feb 2025 19:24:52 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AA=97=E5=8F=A3=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmake/compile_shaders.cmake | 10 +- cmake/configure_glfw_native.cmake | 16 +- cmake/detect_os.cmake | 20 +-- cmake/retrieve_files.cmake | 190 +++++++++++++++------ example/src/main.cpp | 6 + src/core/mirage.cpp | 40 ++++- src/core/mirage.h | 28 +-- src/core/misc/mirage_type.h | 15 ++ src/core/window/desktop/desktop_window.cpp | 171 +++++++++++++++++++ src/core/window/mobile/mobile_window.cpp | 67 ++++++++ src/core/window/window.cpp | 54 ++++++ src/core/window/window.h | 120 +++++++++++++ src/core/window/window.mm | 21 +++ src/core/window/window_manager.cpp | 44 +++++ src/core/window/window_manager.h | 32 ++++ 15 files changed, 735 insertions(+), 99 deletions(-) create mode 100644 src/core/misc/mirage_type.h create mode 100644 src/core/window/desktop/desktop_window.cpp create mode 100644 src/core/window/mobile/mobile_window.cpp create mode 100644 src/core/window/window.cpp create mode 100644 src/core/window/window.h create mode 100644 src/core/window/window.mm create mode 100644 src/core/window/window_manager.cpp create mode 100644 src/core/window/window_manager.h diff --git a/cmake/compile_shaders.cmake b/cmake/compile_shaders.cmake index 539e3f6..89816de 100644 --- a/cmake/compile_shaders.cmake +++ b/cmake/compile_shaders.cmake @@ -4,12 +4,12 @@ if (NOT PYTHON_EXECUTABLE) find_program(PYTHON_EXECUTABLE python) endif () if (NOT PYTHON_EXECUTABLE) - message(FATAL_ERROR "Python executable not found") + message(FATAL_ERROR "无法找到Python解释器") endif () find_program(SLANG_COMPILER slangc REQUIRED) -message(STATUS "Python executable: ${PYTHON_EXECUTABLE}") -message(STATUS "SLANG compiler: ${SLANG_COMPILER}") +message(STATUS "Python解释器: ${PYTHON_EXECUTABLE}") +message(STATUS "SLANG编译器: ${SLANG_COMPILER}") set(SHADER_PATH_FILE ${CMAKE_CURRENT_SOURCE_DIR}/scripts/shader_paths.txt) # 删除文件 @@ -41,9 +41,11 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") list(APPEND SLANGC_ARGS "--debug") endif () +message(STATUS "着色器编译输出路径: ${SHADER_OUTPUT_DIR}") + # 添加自定义命令, 用于编译着色器, 调用scripts/compile_shaders.py add_custom_target(compile_shaders COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/compile_shaders.py --shader-list ${SHADER_PATH_FILE} --output-dir ${SHADER_OUTPUT_DIR} ${SLANGC_ARGS} - COMMENT "Compiling shaders" + COMMENT "编译着色器" VERBATIM ) diff --git a/cmake/configure_glfw_native.cmake b/cmake/configure_glfw_native.cmake index e0fed3e..7f8ace1 100644 --- a/cmake/configure_glfw_native.cmake +++ b/cmake/configure_glfw_native.cmake @@ -2,10 +2,10 @@ function(configure_glfw_native target) # 检测操作系统 if(WIN32) target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_WIN32) - message(STATUS "Exposing GLFW native Win32 API") + message(STATUS "公开 GLFW 原生 Win32 API") elseif(APPLE) target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_COCOA) - message(STATUS "Exposing GLFW native Cocoa API") + message(STATUS "公开 GLFW 原生 Cocoa API") elseif(UNIX) # 对于 Unix-like 系统,我们需要进一步检测 if(CMAKE_SYSTEM_NAME MATCHES "Linux") @@ -13,27 +13,27 @@ function(configure_glfw_native target) find_package(Wayland) if(Wayland_FOUND) target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_WAYLAND) - message(STATUS "Exposing GLFW native Wayland API") + message(STATUS "公开 GLFW 原生 Wayland API") else() # 如果没有 Wayland,默认使用 X11 target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_X11) - message(STATUS "Exposing GLFW native X11 API") + message(STATUS "公开 GLFW 原生 X11 API") endif() elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD") # BSD 系统通常使用 X11 target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_X11) - message(STATUS "Exposing GLFW native X11 API for BSD") + message(STATUS "公开 BSD 的 GLFW 原生 X11 API") else() - message(WARNING "Unknown Unix-like system, GLFW native API might not be properly exposed") + message(WARNING "未知的类 Unix 系统,GLFW 原生 API 可能无法正确暴露") endif() else() - message(WARNING "Unknown operating system, GLFW native API might not be properly exposed") + message(WARNING "未知作系统,GLFW 原生 API 可能无法正确暴露") endif() # 对于 EGL 支持,你可能需要额外的检测 # 这里我们简单地为所有非 Windows 和非 macOS 系统启用它 if(NOT WIN32 AND NOT APPLE) target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_EGL) - message(STATUS "Exposing GLFW native EGL API") + message(STATUS "公开 GLFW 原生 EGL API") endif() endfunction() diff --git a/cmake/detect_os.cmake b/cmake/detect_os.cmake index c809f6f..185d798 100644 --- a/cmake/detect_os.cmake +++ b/cmake/detect_os.cmake @@ -7,34 +7,34 @@ function(add_os_definitions target) # 检测操作系统并设置相应的宏为 1 if(WIN32) target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_WINDOWS=1) - message(STATUS "Detected Windows operating system") + message(STATUS "检测到 Windows 操作系统") list(REMOVE_ITEM PLATFORMS MIRAGE_PLATFORM_WINDOWS) elseif(APPLE AND UNIX) target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_MACOS=1) - message(STATUS "Detected macOS operating system") + message(STATUS "检测到 macOS 操作系统") list(REMOVE_ITEM PLATFORMS MIRAGE_PLATFORM_MACOS) elseif(UNIX) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_LINUX=1) - message(STATUS "Detected Linux operating system") + message(STATUS "检测到 Linux 操作系统") list(REMOVE_ITEM PLATFORMS MIRAGE_PLATFORM_LINUX) elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_FREEBSD=1) - message(STATUS "Detected FreeBSD operating system") + message(STATUS "检测到 FreeBSD 操作系统") list(REMOVE_ITEM PLATFORMS MIRAGE_PLATFORM_FREEBSD) else() - message(WARNING "Detected unknown Unix-like operating system") + message(WARNING "检测到未知的 类Unix 操作系统") endif() elseif(ANDROID) target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_ANDROID=1) - message(STATUS "Detected Android operating system") + message(STATUS "检测到 Android 操作系统") list(REMOVE_ITEM PLATFORMS MIRAGE_PLATFORM_ANDROID) elseif(IOS) target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_IOS=1) - message(STATUS "Detected iOS operating system") + message(STATUS "检测到 iOS 操作系统") list(REMOVE_ITEM PLATFORMS MIRAGE_PLATFORM_IOS) else() - message(WARNING "Unknown operating system") + message(WARNING "检测到未知的操作系统") endif() foreach(PLATFORM ${PLATFORMS}) @@ -44,10 +44,10 @@ function(add_os_definitions target) # 检测并设置架构宏 if(CMAKE_SIZEOF_VOID_P EQUAL 8) target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_ARCH_64BIT=1 MIRAGE_PLATFORM_ARCH_32BIT=0) - message(STATUS "Detected 64-bit architecture") + message(STATUS "检测到 64-bit 架构") else() target_compile_definitions(${target} PUBLIC MIRAGE_PLATFORM_ARCH_64BIT=0 MIRAGE_PLATFORM_ARCH_32BIT=1) - message(STATUS "Detected 32-bit architecture") + message(STATUS "检测到 32-bit 架构") endif() # 设置通用的 UNIX 宏 diff --git a/cmake/retrieve_files.cmake b/cmake/retrieve_files.cmake index 1f7b3ef..50014de 100644 --- a/cmake/retrieve_files.cmake +++ b/cmake/retrieve_files.cmake @@ -1,65 +1,145 @@ +#[=======================================================================[ + 平台匹配检查函数 + 参数: + platform: 平台标识符 (windows|linux|mac|mobile|desktop) + is_match: 输出变量,表示当前平台是否匹配 +#]=======================================================================] +function(is_current_platform platform is_match) + # 设置默认值为TRUE(用于未知平台) + set(matches FALSE) -function(retrieve_files_custom path extension out_files) - message(STATUS "Retrieving files in ${path}") - set(EXTENSIONS "") - foreach(ext ${extension}) - list(APPEND EXTENSIONS "${path}/*.${ext}") - endforeach () - - # 递归查找文件夹下的 .h .hpp. ini 文件保存到 HEAD_FILES - file(GLOB_RECURSE FIND_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${EXTENSIONS}) - # 将 HEDADER_FILES 和 SRC_FILES 保存到 ALL_FILES 变量 - set(ALL_FILES ${FIND_FILES}) - - set(RESULT "") - - # 对 ALL_FILES 变量里面的所有文件分类(保留资源管理器的目录结构) - foreach(fileItem ${ALL_FILES}) - # Get the directory of the source file - get_filename_component(PARENT_DIR "${fileItem}" DIRECTORY) - - # 用于检查平台的条件 - if(PARENT_DIR STREQUAL "windows") - if(WIN32) - set(RESULT "${RESULT};${fileItem}") - else() - continue() - endif() - elseif(PARENT_DIR STREQUAL "linux") - if(UNIX AND NOT APPLE) - set(RESULT "${RESULT};${fileItem}") - else() - continue() - endif() - elseif(PARENT_DIR STREQUAL "mac") - if(APPLE) - set(RESULT "${RESULT};${fileItem}") - else() - continue() - endif() - else() - # 如果文件夹名称不是平台,则始终包含 - set(RESULT "${RESULT};${fileItem}") + if(platform STREQUAL "windows") + if(WIN32) + set(matches TRUE) endif() + elseif(platform STREQUAL "linux") + if(UNIX AND NOT APPLE) + set(matches TRUE) + endif() + elseif(platform STREQUAL "mac") + if(APPLE) + set(matches TRUE) + endif() + elseif(platform STREQUAL "mobile") + if(ANDROID OR IOS) + set(matches TRUE) + endif() + elseif(platform STREQUAL "desktop") + if(WIN32 OR (UNIX AND NOT APPLE)) + set(matches TRUE) + endif() + else() + # 未知平台标识,默认匹配 + set(matches TRUE) + endif() - # Remove common directory prefix to make the group - string(REPLACE "${path}" "" GROUP "${PARENT_DIR}") - # Make sure we are using windows slashes - string(REPLACE "/" "\\" GROUP "${GROUP}") - # Group into "Source Files" and "Header Files" - set(GROUP "${GROUP}") - source_group("${GROUP}" FILES "${fileItem}") + set(${is_match} ${matches} PARENT_SCOPE) +endfunction() + +#[=======================================================================[ + 主文件检索函数 + 参数: + path: 要检索的根目录路径 + extension: 文件扩展名列表 + out_files: 输出变量名,将包含筛选后的文件列表 +#]=======================================================================] +function(retrieve_files_custom path extension out_files) + # 1. 参数验证 + if(NOT IS_DIRECTORY "${path}") + message(WARNING "错误:目录 '${path}' 不存在") + return() + endif() + + message(STATUS "正在检索目录: ${path}") + + # 2. 构建文件匹配模式 + set(file_patterns "") + foreach(ext IN LISTS extension) + list(APPEND file_patterns "${path}/*.${ext}") endforeach() - set(${out_files} ${RESULT} PARENT_SCOPE) + # 3. 递归查找所有匹配的文件 + file(GLOB_RECURSE found_files + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS ${file_patterns} + ) + + # 4. 处理找到的文件 + set(filtered_files "") + foreach(current_file IN LISTS found_files) + # 4.1 获取文件所在目录 + get_filename_component(file_dir "${current_file}" DIRECTORY) + string(REPLACE "/" ";" dir_components "${file_dir}") + + # 4.2 检查平台兼容性 + set(should_skip_file FALSE) + set(found_platform_dir FALSE) + + foreach(dir_name IN LISTS dir_components) + # 检查是否是平台相关目录 + if(dir_name MATCHES "^(windows|linux|mac|mobile|desktop)$") + set(found_platform_dir TRUE) + is_current_platform(${dir_name} platform_matches) + if(NOT platform_matches) + set(should_skip_file TRUE) + break() + endif() + endif() + endforeach() + + # 如果文件需要跳过,继续处理下一个文件 + if(should_skip_file) + continue() + endif() + + # 4.3 添加符合条件的文件 + list(APPEND filtered_files "${current_file}") + + # 4.4 设置IDE文件分组 + # 计算相对路径作为分组名称 + get_filename_component(root_abs_path "${path}" ABSOLUTE) + get_filename_component(file_dir_abs_path "${file_dir}" ABSOLUTE) + file(RELATIVE_PATH group_path "${root_abs_path}" "${file_dir_abs_path}") + + # 处理根目录的特殊情况 + if(group_path STREQUAL ".") + set(group_name "") + else() + string(REPLACE "/" "\\" group_name "${group_path}") + endif() + + # 创建IDE分组 + source_group("${group_name}" FILES "${current_file}") + endforeach() + + # 5. 设置输出变量 + set(${out_files} ${filtered_files} PARENT_SCOPE) endfunction() +#[=======================================================================[ + 便捷封装函数 + 自动处理常见文件扩展名,针对不同平台添加特定文件类型 +#]=======================================================================] function(retrieve_files path out_files) + # 设置基础文件类型 + set(file_extensions + "h" # 头文件 + "hpp" # C++头文件 + "ini" # 配置文件 + "cpp" # C++源文件 + "c" # C源文件 + "ixx" # C++20模块文件 + ) + + # 针对Mac平台添加额外文件类型 + if(APPLE) + list(APPEND file_extensions "mm") # Objective-C++源文件 + endif() + + # 执行文件检索 set(temp_files "") - if (APPLE) - retrieve_files_custom(${path} "h;hpp;ini;cpp;c;ixx;mm" temp_files) - else () - retrieve_files_custom(${path} "h;hpp;ini;cpp;c;ixx" temp_files) - endif () + retrieve_files_custom(${path} "${file_extensions}" temp_files) + + # 合并结果到输出变量 set(${out_files} ${${out_files}} ${temp_files} PARENT_SCOPE) -endfunction() +endfunction() \ No newline at end of file diff --git a/example/src/main.cpp b/example/src/main.cpp index 4baeda3..a90d3a5 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -2,5 +2,11 @@ int main(int argc, char* argv[]) { mirage::init_info init{}; + init.title = u8"测试"; + init.size = {800, 600}; + init.resizable = true; + init.centered = true; + init.visible = true; + init.fullscreen = true; return run(init); } diff --git a/src/core/mirage.cpp b/src/core/mirage.cpp index 64bf5aa..54c11e7 100644 --- a/src/core/mirage.cpp +++ b/src/core/mirage.cpp @@ -1,6 +1,7 @@ #include "mirage.h" #include "async/thread_pool.h" +#include "window/window_manager.h" mirage::duration_type delta_time = {}; mirage::time_type begin_time = {}; @@ -47,19 +48,38 @@ namespace mirage { spdlog::error("渲染器加载失败"); return false; } + bool init_window(const init_info& in_info) { + auto main_window = lazy_singleton::get().create_window(in_info); + auto window_ptr = main_window.lock(); + if (!window_ptr) { + return false; + } + + if (!window_ptr->create_swap_chain(in_info)) { + return false; + } + spdlog::info("主窗口创建成功"); + return true; + } bool init(const init_info& in_info) { spdlog::info("初始化 mirage"); if (!init_renderer(in_info)) { return false; } + if (!init_window(in_info)) { + return false; + } begin_time = std::chrono::high_resolution_clock::now(); last_time = std::chrono::high_resolution_clock::now(); return true; } void destroy() { - renderer.reset(); + if (!lazy_singleton::get().destroy()) { + spdlog::warn("窗口管理器销毋失败"); + } + LLGL::RenderSystem::Unload(std::move(renderer)); spdlog::info("mirage 销毁"); } @@ -71,13 +91,24 @@ namespace mirage { delta_time = current_time - last_time; last_time = current_time; + lazy_singleton::get().update(delta_time); + std::this_thread::yield(); } bool should_exit() { - return false; + return lazy_singleton::get().should_exit(); } + renderer_api get_default_renderer_api() { +#if MIRAGE_PLATFORM_WINDOWS + return renderer_api::dx11; +#elif MIRAGE_PLATFORM_MACOS + return renderer_api::metal; +#elif MIRAGE_PLATFORM_LINUX + return renderer_api::vulkan; +#endif + } int run(const init_info& in_init_info) { try { if (!init(in_init_info)) { @@ -105,4 +136,7 @@ namespace mirage { duration_type get_total_time() { return std::chrono::high_resolution_clock::now() - begin_time; } -} + LLGL::RenderSystem* get_renderer() { + return renderer.get(); + } +} // namespace mirage diff --git a/src/core/mirage.h b/src/core/mirage.h index 5f94e06..fdc02ad 100644 --- a/src/core/mirage.h +++ b/src/core/mirage.h @@ -1,33 +1,23 @@ #pragma once -#include #include "LLGL/LLGL.h" +#include "misc/mirage_type.h" +#include "window/window.h" +#include #define MIRAGE_VERSION_MAJOR 0 #define MIRAGE_VERSION_MINOR 1 #define MIRAGE_VERSION_PATCH 0 namespace mirage { - using time_type = decltype(std::chrono::high_resolution_clock::now()); - using duration_type = decltype(std::chrono::high_resolution_clock::now() - std::chrono::high_resolution_clock::now()); + renderer_api get_default_renderer_api(); + struct init_info : window_descriptor, swap_chain_descriptor { + renderer_api api = get_default_renderer_api(); + }; - enum class renderer_api { - dx11, - dx12, - vulkan, - opengl, - metal, - }; - struct init_info { -#if MIRAGE_PLATFORM_WINDOWS - renderer_api api = renderer_api::dx11; -#elif MIRAGE_PLATFORM_MACOS - renderer_api api = renderer_api::metal; -#elif MIRAGE_PLATFORM_LINUX - renderer_api api = renderer_api::vulkan; -#endif - }; int run(const init_info& in_init_info); const duration_type& get_delta_time(); duration_type get_total_time(); + + LLGL::RenderSystem* get_renderer(); } diff --git a/src/core/misc/mirage_type.h b/src/core/misc/mirage_type.h new file mode 100644 index 0000000..bac9ca2 --- /dev/null +++ b/src/core/misc/mirage_type.h @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace mirage { + using time_type = decltype(std::chrono::high_resolution_clock::now()); + using duration_type = decltype(std::chrono::high_resolution_clock::now() - std::chrono::high_resolution_clock::now()); + + enum class renderer_api { + dx11, + dx12, + vulkan, + opengl, + metal, + }; +} \ No newline at end of file diff --git a/src/core/window/desktop/desktop_window.cpp b/src/core/window/desktop/desktop_window.cpp new file mode 100644 index 0000000..8ec493d --- /dev/null +++ b/src/core/window/desktop/desktop_window.cpp @@ -0,0 +1,171 @@ +#include "window/window.h" +#include "window/window_manager.h" + +namespace mirage { + class desktop_window_event_listener : public LLGL::Window::EventListener { + public: + virtual void OnQuit(LLGL::Window& sender, bool& veto) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_quit(veto); + lazy_singleton::get().destroy_window(*wrapper); + } + virtual void OnKeyDown(LLGL::Window& sender, LLGL::Key keyCode) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_key_down(keyCode); + } + virtual void OnKeyUp(LLGL::Window& sender, LLGL::Key keyCode) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_key_up(keyCode); + } + virtual void OnDoubleClick(LLGL::Window& sender, LLGL::Key keyCode) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_double_click(keyCode); + } + virtual void OnChar(LLGL::Window& sender, wchar_t chr) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_char(chr); + } + virtual void OnWheelMotion(LLGL::Window& sender, int motion) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_wheel_motion(motion); + } + virtual void OnGlobalMotion(LLGL::Window& sender, const LLGL::Offset2D& motion) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_global_motion(motion); + } + virtual void OnResize(LLGL::Window& sender, const LLGL::Extent2D& clientAreaSize) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_resize(clientAreaSize); + } + virtual void OnUpdate(LLGL::Window& sender) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_update(); + } + virtual void OnGetFocus(LLGL::Window& sender) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_get_focus(); + } + virtual void OnLostFocus(LLGL::Window& sender) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_lost_focus(); + } + private: + + }; + + void window::set_title(const std::u8string& title) { + if (auto window_ptr = static_cast(surface.get())) { + window_ptr->SetTitle((char*)title.c_str()); + } + } + + std::u8string window::get_title() const { + if (const auto window_ptr = static_cast(surface.get())) { + return (char8_t*)window_ptr->GetTitle().c_str(); + } + return {}; + } + + void window::set_fullscreen(bool fullscreen) { + if (auto window_ptr = static_cast(surface.get())) { + window_ptr->AdaptForVideoMode(nullptr, &fullscreen); + const auto display = window_ptr->FindResidentDisplay(); + if (display && fullscreen) { + resize(display->GetDisplayMode().resolution); + } + } + } + + bool window::is_fullscreen() const { + if (const auto window_ptr = static_cast(surface.get())) { + if (const auto display = window_ptr->FindResidentDisplay()) { + const auto& window_client_size = window_ptr->GetSize(); + const auto& display_mode = display->GetDisplayMode(); + return is_borderless() && window_client_size == display_mode.resolution; + } + } + return false; + } + + void window::set_resizable(bool resizable) { + if (auto window_ptr = static_cast(surface.get())) { + auto desc = window_ptr->GetDesc(); + desc.flags = resizable ? desc.flags | LLGL::WindowFlags::Resizable : desc.flags & ~LLGL::WindowFlags::Resizable; + window_ptr->SetDesc(desc); + } + } + + bool window::is_resizable() const { + if (const auto window_ptr = static_cast(surface.get())) { + return window_ptr->GetDesc().flags & LLGL::WindowFlags::Resizable; + } + return false; + } + + void window::set_centered(bool centered) { + if (auto window_ptr = static_cast(surface.get())) { + auto desc = window_ptr->GetDesc(); + desc.flags = centered ? desc.flags | LLGL::WindowFlags::Centered : desc.flags & ~LLGL::WindowFlags::Centered; + window_ptr->SetDesc(desc); + } + } + + void window::set_borderless(bool borderless) { + if (auto window_ptr = static_cast(surface.get())) { + auto desc = window_ptr->GetDesc(); + desc.flags = + borderless ? desc.flags | LLGL::WindowFlags::Borderless : desc.flags & ~LLGL::WindowFlags::Borderless; + window_ptr->SetDesc(desc); + } + } + + bool window::is_borderless() const { + if (const auto window_ptr = static_cast(surface.get())) { + return window_ptr->GetDesc().flags & LLGL::WindowFlags::Borderless; + } + return false; + } + + void window::show(bool in_show) { + if (auto window_ptr = static_cast(surface.get())) { + window_ptr->Show(in_show); + } + } + + void window::move(const LLGL::Offset2D& position) { + if (auto window_ptr = static_cast(surface.get())) { + window_ptr->SetPosition(position); + } + } + + void window::move_delta(const LLGL::Offset2D& delta) { + if (const auto window_ptr = static_cast(surface.get())) { + window_ptr->SetPosition(window_ptr->GetPosition() + delta); + } + } + + void window::resize(const LLGL::Extent2D& size) { + if (get_size() == size) + return; + if (swap_chain) { + swap_chain->ResizeBuffers(size, LLGL::ResizeBuffersFlags::AdaptSurface); + return; + } + if (auto window_ptr = static_cast(surface.get())) { + window_ptr->AdaptForVideoMode((LLGL::Extent2D*)&size, nullptr); + } + } + LLGL::Extent2D window::get_size(bool in_client_size) const { + if (const auto window_ptr = static_cast(surface.get())) { + return window_ptr->GetSize(in_client_size); + } + return {}; + } + + void window::init_event_listener() { + if (auto window_ptr = static_cast(surface.get())) { + window_ptr->SetUserData(this); + window_ptr->AddEventListener(std::make_shared()); + } + } +} diff --git a/src/core/window/mobile/mobile_window.cpp b/src/core/window/mobile/mobile_window.cpp new file mode 100644 index 0000000..eb1052d --- /dev/null +++ b/src/core/window/mobile/mobile_window.cpp @@ -0,0 +1,67 @@ +#include "window/window.h" + +namespace mirage { + class mobile_window_event_listener : public LLGL::Canvas::EventListener { + public: + virtual void OnQuit(LLGL::Canvas& sender, bool& veto) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_quit(veto); + } +// virtual void OnInit(LLGL::Canvas& sender) override { +// auto wrapper = static_cast(sender.GetUserData()); +// wrapper->on_init(); +// } +// virtual void OnDestroy(LLGL::Canvas& sender) override { +// auto wrapper = static_cast(sender.GetUserData()); +// wrapper->on_destroy(); +// } +// virtual void OnDraw(LLGL::Canvas& sender) override { +// auto wrapper = static_cast(sender.GetUserData()); +// wrapper->on_draw(); +// } + virtual void OnResize(LLGL::Canvas& sender, const LLGL::Extent2D& clientAreaSize) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_resize(clientAreaSize); + } + virtual void OnTapGesture(LLGL::Canvas& sender, const LLGL::Offset2D& position, std::uint32_t numTouches) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_tap_gesture(position, numTouches); + } + virtual void OnPanGesture(LLGL::Canvas& sender, const LLGL::Offset2D& position, std::uint32_t numTouches, float dx, float dy, LLGL::EventAction action) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_pan_gesture(position, numTouches, dx, dy, action); + } + virtual void OnKeyDown(LLGL::Canvas& sender, LLGL::Key keyCode) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_key_down(keyCode); + } + virtual void OnKeyUp(LLGL::Canvas& sender, LLGL::Key keyCode) override { + auto wrapper = static_cast(sender.GetUserData()); + wrapper->on_key_up(keyCode); + } + }; + void window::show(bool in_show) { + } + void window::move(const LLGL::Offset2D& position) { + } + + void window::move_delta(const LLGL::Offset2D& delta) { + } + + void window::resize(const LLGL::Extent2D& size) { + } + + LLGL::Extent2D window::get_size() const { + if (const auto canvas_ptr = static_cast(surface.get())) { + return canvas_ptr->FindResidentDisplay()->GetDisplayMode().resolution; + } + return {}; + } + + void window::init_event_listener() { + if (auto canvas_ptr = static_cast(surface.get())) { + canvas_ptr->SetUserData(this); + canvas_ptr->AddEventListener(std::make_shared()); + } + } +} diff --git a/src/core/window/window.cpp b/src/core/window/window.cpp new file mode 100644 index 0000000..b234979 --- /dev/null +++ b/src/core/window/window.cpp @@ -0,0 +1,54 @@ +#include "window.h" + +#include "mirage.h" + +#if MIRAGE_PLATFORM_WINDOWS +#include "LLGL/Platform/Win32/Win32NativeHandle.h" +#elif MIRAGE_PLATFORM_LINUX +#include "LLGL/Platform/Linux/LinuxNativeHandle.h" +#elif MIRAGE_PLATFORM_ANDROID +#include "LLGL/Platform/Android/AndroidNativeHandle.h" +#endif + +namespace mirage { + window::window(const LLGL::WindowDescriptor& in_desc) { + surface = LLGL::Window::Create(in_desc); + if (surface == nullptr) { + throw std::runtime_error("无法创建窗口"); + } + init_event_listener(); + } + + window::~window() { + if (swap_chain) { + get_renderer()->Release(*swap_chain); + } + } + + bool window::create_swap_chain(const LLGL::SwapChainDescriptor& desc) { + auto temp = desc; + temp.resolution = get_size(); + temp.fullscreen = is_fullscreen(); + swap_chain = get_renderer()->CreateSwapChain(desc, surface); + if (swap_chain == nullptr) { + spdlog::error("无法创建交换链"); + return false; + } + return true; + } + +#if !MIRAGE_PLATFORM_IOS && !MIRAGE_PLATFORM_MACOS + void* window::get_native_handle() const { + LLGL::NativeHandle native_handle{}; + if (surface->GetNativeHandle(&native_handle, sizeof(native_handle))) { + return native_handle.window; + } + return nullptr; + } +#endif + + void window::update(const duration_type& delta_time) { + if (swap_chain) + swap_chain->Present(); + } +} diff --git a/src/core/window/window.h b/src/core/window/window.h new file mode 100644 index 0000000..b230233 --- /dev/null +++ b/src/core/window/window.h @@ -0,0 +1,120 @@ +#pragma once +#include +#include "misc/mirage_type.h" + +namespace mirage { + struct window_descriptor { + std::u8string title; + LLGL::Offset2D position; + LLGL::Extent2D size; + bool visible = true; + bool borderless = false; + bool resizable = true; + bool centered = false; + bool accept_drop_files = false; + bool fullscreen = false; + bool disable_clear_on_resize = false; + bool disable_size_scaling = false; + + operator LLGL::WindowDescriptor() const { + LLGL::WindowDescriptor desc{}; + desc.title = reinterpret_cast(title.c_str()); + desc.position = position; + desc.size = size; + desc.flags = 0; + if (visible) { + desc.flags |= LLGL::WindowFlags::Visible; + } + if (borderless) { + desc.flags |= LLGL::WindowFlags::Borderless; + } + if (resizable) { + desc.flags |= LLGL::WindowFlags::Resizable; + } + if (centered) { + desc.flags |= LLGL::WindowFlags::Centered; + } + if (accept_drop_files) { + desc.flags |= LLGL::WindowFlags::AcceptDropFiles; + } + if (fullscreen) { + desc.flags |= LLGL::WindowFlags::Borderless; + } + if (disable_clear_on_resize) { + desc.flags |= LLGL::WindowFlags::DisableClearOnResize; + } + if (disable_size_scaling) { + desc.flags |= LLGL::WindowFlags::DisableSizeScaling; + } + return desc; + } + }; + struct swap_chain_descriptor { + int color_bits = 32; + int depth_bits = 24; + int stencil_bits = 8; + std::uint32_t samples = 1; + std::uint32_t swap_buffers = 2; + + operator LLGL::SwapChainDescriptor() const { + LLGL::SwapChainDescriptor desc{}; + desc.colorBits = color_bits; + desc.depthBits = depth_bits; + desc.stencilBits = stencil_bits; + desc.samples = samples; + desc.swapBuffers = swap_buffers; + return desc; + } + }; + + class window { + friend class desktop_window_event_listener; + friend class mobile_window_event_listener; + public: + explicit window(const LLGL::WindowDescriptor& in_desc); + virtual ~window(); + + bool create_swap_chain(const LLGL::SwapChainDescriptor& desc); + [[nodiscard]] void* get_native_handle() const; + + void update(const duration_type& delta_time); + void set_title(const std::u8string& title); + std::u8string get_title() const; + void set_fullscreen(bool fullscreen); + bool is_fullscreen() const; + void set_resizable(bool resizable); + bool is_resizable() const; + void set_borderless(bool borderless); + bool is_borderless() const; + void set_centered(bool centered); + + void show(bool in_show); + void move(const LLGL::Offset2D& position); + void move_delta(const LLGL::Offset2D& delta); + void resize(const LLGL::Extent2D& size); + [[nodiscard]] LLGL::Extent2D get_size(bool in_client_size = true) const; + + [[nodiscard]] LLGL::SwapChain* get_swap_chain() const { + return swap_chain; + } + protected: + virtual void on_quit(bool& out_veto) {} + virtual void on_key_down(LLGL::Key in_key_code) {} + virtual void on_key_up(LLGL::Key in_key_code) {} + virtual void on_double_click(LLGL::Key in_key_code) {} + virtual void on_char(wchar_t in_chr) {} + virtual void on_wheel_motion(int in_motion) {} + virtual void on_local_motion(const LLGL::Offset2D& in_position) {} + virtual void on_global_motion(const LLGL::Offset2D& in_motion) {} + virtual void on_resize(const LLGL::Extent2D& in_client_area_size) {} + virtual void on_update() {} + virtual void on_get_focus() {} + virtual void on_lost_focus() {} + virtual void on_tap_gesture(const LLGL::Offset2D& in_position, std::uint32_t in_num_touches) {} + virtual void on_pan_gesture(const LLGL::Offset2D& in_position, std::uint32_t in_num_touches, float in_dx, float in_dy, LLGL::EventAction in_action) {} + private: + void init_event_listener(); + LLGL::SwapChain* swap_chain = nullptr; + std::shared_ptr surface; + }; +} // namespace mirage diff --git a/src/core/window/window.mm b/src/core/window/window.mm new file mode 100644 index 0000000..fec43f8 --- /dev/null +++ b/src/core/window/window.mm @@ -0,0 +1,21 @@ +#include "window.h" + +#if MIRAGE_PLATFORM_MACOS +#include "LLGL/Platform/MacOS/MacOSNativeHandle.h" +#elif MIRAGE_PLATFORM_IOS +#include "LLGL/Platform/iOS/iOSNativeHandle.h" +#endif + +namespace mirage { + void* window::get_native_handle() const { + LLGL::NativeHandle native_handle{}; + if (surface->GetNativeHandle(&native_handle, sizeof(native_handle))) { +#if MIRAGE_PLATFORM_MACOS + return native_handle.responder; +#elif MIRAGE_PLATFORM_IOS + return native_handle.view; +#endif + } + return nullptr; + } +} diff --git a/src/core/window/window_manager.cpp b/src/core/window/window_manager.cpp new file mode 100644 index 0000000..327a90b --- /dev/null +++ b/src/core/window/window_manager.cpp @@ -0,0 +1,44 @@ +#include "window_manager.h" + +#include + +namespace mirage { + void window_manager::update(const duration_type& in_delta_time) { + if (!LLGL::Surface::ProcessEvents()) { + return; + } + to_destroy.clear(); + for (const auto& w : windows) { + w->update(in_delta_time); + } + } + bool window_manager::destroy() { + windows.clear(); + to_destroy.clear(); + return true; + } + std::weak_ptr window_manager::create_window(const LLGL::WindowDescriptor& desc) { + auto window_ptr = std::make_shared(desc); + windows.push_back(window_ptr); + if (!window_ptr) { + spdlog::error("无法创建窗口"); + return {}; + } + return window_ptr; + } + void window_manager::destroy_window(const window& in_window) { + for (auto it = windows.begin(); it != windows.end(); ++it) { + if (it->get() == &in_window) { + to_destroy.push_back(*it); + windows.erase(it); + break; + } + } + } + std::weak_ptr window_manager::get_main_window() { + if (windows.empty()) { + return {}; + } + return windows.front(); + } +} // namespace mirage diff --git a/src/core/window/window_manager.h b/src/core/window/window_manager.h new file mode 100644 index 0000000..e32b8bc --- /dev/null +++ b/src/core/window/window_manager.h @@ -0,0 +1,32 @@ +#pragma once +#include "misc/lazy_singleton.h" +#include +#include +#include "window.h" + +namespace mirage { + class window_manager { + public: + void update(const duration_type& in_delta_time); + bool destroy(); + + [[nodiscard]] bool should_exit() const { + return windows.empty() && to_destroy.empty(); + } + + std::weak_ptr create_window(const LLGL::WindowDescriptor& desc); + void destroy_window(const window& in_window); + std::weak_ptr get_main_window(); + [[nodiscard]] auto get_windows() const { + std::vector> result; + for (const auto& w : windows) { + result.push_back(w); + } + return result; + } + + private: + std::vector> windows; + std::vector> to_destroy; + }; +} // namespace mirage