窗口管理
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 宏
|
||||
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<window_manager>::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<window_manager>::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<window_manager>::get().update(delta_time);
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
bool should_exit() {
|
||||
return false;
|
||||
return lazy_singleton<window_manager>::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
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
#pragma once
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "LLGL/LLGL.h"
|
||||
#include "misc/mirage_type.h"
|
||||
#include "window/window.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
15
src/core/misc/mirage_type.h
Normal file
15
src/core/misc/mirage_type.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
171
src/core/window/desktop/desktop_window.cpp
Normal file
171
src/core/window/desktop/desktop_window.cpp
Normal file
@@ -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<window*>(sender.GetUserData());
|
||||
wrapper->on_quit(veto);
|
||||
lazy_singleton<window_manager>::get().destroy_window(*wrapper);
|
||||
}
|
||||
virtual void OnKeyDown(LLGL::Window& sender, LLGL::Key keyCode) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_key_down(keyCode);
|
||||
}
|
||||
virtual void OnKeyUp(LLGL::Window& sender, LLGL::Key keyCode) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_key_up(keyCode);
|
||||
}
|
||||
virtual void OnDoubleClick(LLGL::Window& sender, LLGL::Key keyCode) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_double_click(keyCode);
|
||||
}
|
||||
virtual void OnChar(LLGL::Window& sender, wchar_t chr) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_char(chr);
|
||||
}
|
||||
virtual void OnWheelMotion(LLGL::Window& sender, int motion) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_wheel_motion(motion);
|
||||
}
|
||||
virtual void OnGlobalMotion(LLGL::Window& sender, const LLGL::Offset2D& motion) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_global_motion(motion);
|
||||
}
|
||||
virtual void OnResize(LLGL::Window& sender, const LLGL::Extent2D& clientAreaSize) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_resize(clientAreaSize);
|
||||
}
|
||||
virtual void OnUpdate(LLGL::Window& sender) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_update();
|
||||
}
|
||||
virtual void OnGetFocus(LLGL::Window& sender) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_get_focus();
|
||||
}
|
||||
virtual void OnLostFocus(LLGL::Window& sender) override {
|
||||
auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
wrapper->on_lost_focus();
|
||||
}
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
void window::set_title(const std::u8string& title) {
|
||||
if (auto window_ptr = static_cast<LLGL::Window*>(surface.get())) {
|
||||
window_ptr->SetTitle((char*)title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::u8string window::get_title() const {
|
||||
if (const auto window_ptr = static_cast<LLGL::Window*>(surface.get())) {
|
||||
return (char8_t*)window_ptr->GetTitle().c_str();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void window::set_fullscreen(bool fullscreen) {
|
||||
if (auto window_ptr = static_cast<LLGL::Window*>(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<LLGL::Window*>(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<LLGL::Window*>(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<LLGL::Window*>(surface.get())) {
|
||||
return window_ptr->GetDesc().flags & LLGL::WindowFlags::Resizable;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void window::set_centered(bool centered) {
|
||||
if (auto window_ptr = static_cast<LLGL::Window*>(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<LLGL::Window*>(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<LLGL::Window*>(surface.get())) {
|
||||
return window_ptr->GetDesc().flags & LLGL::WindowFlags::Borderless;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void window::show(bool in_show) {
|
||||
if (auto window_ptr = static_cast<LLGL::Window*>(surface.get())) {
|
||||
window_ptr->Show(in_show);
|
||||
}
|
||||
}
|
||||
|
||||
void window::move(const LLGL::Offset2D& position) {
|
||||
if (auto window_ptr = static_cast<LLGL::Window*>(surface.get())) {
|
||||
window_ptr->SetPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
void window::move_delta(const LLGL::Offset2D& delta) {
|
||||
if (const auto window_ptr = static_cast<LLGL::Window*>(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<LLGL::Window*>(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<LLGL::Window*>(surface.get())) {
|
||||
return window_ptr->GetSize(in_client_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void window::init_event_listener() {
|
||||
if (auto window_ptr = static_cast<LLGL::Window*>(surface.get())) {
|
||||
window_ptr->SetUserData(this);
|
||||
window_ptr->AddEventListener(std::make_shared<desktop_window_event_listener>());
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/core/window/mobile/mobile_window.cpp
Normal file
67
src/core/window/mobile/mobile_window.cpp
Normal file
@@ -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<window*>(sender.GetUserData());
|
||||
wrapper->on_quit(veto);
|
||||
}
|
||||
// virtual void OnInit(LLGL::Canvas& sender) override {
|
||||
// auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
// wrapper->on_init();
|
||||
// }
|
||||
// virtual void OnDestroy(LLGL::Canvas& sender) override {
|
||||
// auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
// wrapper->on_destroy();
|
||||
// }
|
||||
// virtual void OnDraw(LLGL::Canvas& sender) override {
|
||||
// auto wrapper = static_cast<window*>(sender.GetUserData());
|
||||
// wrapper->on_draw();
|
||||
// }
|
||||
virtual void OnResize(LLGL::Canvas& sender, const LLGL::Extent2D& clientAreaSize) override {
|
||||
auto wrapper = static_cast<window*>(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<window*>(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<window*>(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<window*>(sender.GetUserData());
|
||||
wrapper->on_key_down(keyCode);
|
||||
}
|
||||
virtual void OnKeyUp(LLGL::Canvas& sender, LLGL::Key keyCode) override {
|
||||
auto wrapper = static_cast<window*>(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<LLGL::Canvas*>(surface.get())) {
|
||||
return canvas_ptr->FindResidentDisplay()->GetDisplayMode().resolution;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void window::init_event_listener() {
|
||||
if (auto canvas_ptr = static_cast<LLGL::Canvas*>(surface.get())) {
|
||||
canvas_ptr->SetUserData(this);
|
||||
canvas_ptr->AddEventListener(std::make_shared<mobile_window_event_listener>());
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/core/window/window.cpp
Normal file
54
src/core/window/window.cpp
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
120
src/core/window/window.h
Normal file
120
src/core/window/window.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
#include <LLGL/LLGL.h>
|
||||
#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<const char*>(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<LLGL::Surface> surface;
|
||||
};
|
||||
} // namespace mirage
|
||||
21
src/core/window/window.mm
Normal file
21
src/core/window/window.mm
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
44
src/core/window/window_manager.cpp
Normal file
44
src/core/window/window_manager.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "window_manager.h"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
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> window_manager::create_window(const LLGL::WindowDescriptor& desc) {
|
||||
auto window_ptr = std::make_shared<window>(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> window_manager::get_main_window() {
|
||||
if (windows.empty()) {
|
||||
return {};
|
||||
}
|
||||
return windows.front();
|
||||
}
|
||||
} // namespace mirage
|
||||
32
src/core/window/window_manager.h
Normal file
32
src/core/window/window_manager.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "misc/lazy_singleton.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#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<window> create_window(const LLGL::WindowDescriptor& desc);
|
||||
void destroy_window(const window& in_window);
|
||||
std::weak_ptr<window> get_main_window();
|
||||
[[nodiscard]] auto get_windows() const {
|
||||
std::vector<std::weak_ptr<window>> result;
|
||||
for (const auto& w : windows) {
|
||||
result.push_back(w);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<window>> windows;
|
||||
std::vector<std::shared_ptr<window>> to_destroy;
|
||||
};
|
||||
} // namespace mirage
|
||||
Reference in New Issue
Block a user