窗口管理

This commit is contained in:
2025-02-09 19:24:52 +08:00
parent df4e8d2aae
commit ee9055ceef
15 changed files with 735 additions and 99 deletions

View File

@@ -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
)

View File

@@ -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()

View File

@@ -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 宏

View File

@@ -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()

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}

View 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,
};
}

View 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>());
}
}
}

View 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>());
}
}
}

View 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
View 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
View 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;
}
}

View 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

View 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