Compare commits
2 commits
a88b2ed08b
...
39824de3a5
| Author | SHA1 | Date | |
|---|---|---|---|
| 39824de3a5 | |||
| 6b4a87dd76 |
10 changed files with 1082 additions and 866 deletions
|
|
@ -123,8 +123,6 @@ if(WIN32)
|
||||||
requests.cppm
|
requests.cppm
|
||||||
events.cppm
|
events.cppm
|
||||||
components.cppm
|
components.cppm
|
||||||
SOURCES
|
|
||||||
platform_windows.cpp
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
ecs
|
ecs
|
||||||
app
|
app
|
||||||
|
|
@ -147,8 +145,6 @@ elseif(UNIX)
|
||||||
requests.cppm
|
requests.cppm
|
||||||
events.cppm
|
events.cppm
|
||||||
components.cppm
|
components.cppm
|
||||||
SOURCES
|
|
||||||
platform_linux.cpp
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
ecs
|
ecs
|
||||||
app
|
app
|
||||||
|
|
@ -163,7 +159,6 @@ elseif(UNIX)
|
||||||
time
|
time
|
||||||
TESTS
|
TESTS
|
||||||
system.test.cpp
|
system.test.cpp
|
||||||
platform_linux.test.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -235,12 +230,12 @@ add_module(
|
||||||
PRIVATE_DEPENDENCIES
|
PRIVATE_DEPENDENCIES
|
||||||
surface
|
surface
|
||||||
TESTS
|
TESTS
|
||||||
_tests/buffer.cpp
|
# _tests/buffer.cpp
|
||||||
_tests/debugger.cpp
|
# _tests/debugger.cpp
|
||||||
_tests/device.cpp
|
# _tests/device.cpp
|
||||||
_tests/pass.cpp
|
# _tests/pass.cpp
|
||||||
_tests/renderer.cpp
|
# _tests/renderer.cpp
|
||||||
_tests/surface.cpp
|
# _tests/surface.cpp
|
||||||
_tests/system.cpp
|
_tests/system.cpp
|
||||||
TEST_INTERFACES
|
TEST_INTERFACES
|
||||||
_tests/utils.cppm
|
_tests/utils.cppm
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug("Nulling 0x{:x}", (std::size_t)other.m_value);
|
|
||||||
m_value = other.m_value;
|
m_value = other.m_value;
|
||||||
other.m_value = null_value;
|
other.m_value = null_value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,111 @@
|
||||||
|
import time;
|
||||||
import renderer.frontend;
|
import renderer.frontend;
|
||||||
import renderer.test_utils;
|
import renderer.test_utils;
|
||||||
|
|
||||||
struct SurfaceContext
|
struct SurfaceContext
|
||||||
{
|
{
|
||||||
lt::surface::System system;
|
lt::surface::System system;
|
||||||
|
|
||||||
lt::ecs::Entity entity;
|
lt::ecs::Entity entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RendererContext
|
struct RendererContext
|
||||||
{
|
{
|
||||||
lt::memory::Ref<lt::ecs::Registry> registry;
|
lt::memory::Ref<lt::ecs::Registry> registry;
|
||||||
|
|
||||||
lt::renderer::System system;
|
lt::renderer::System system;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Suite raii = "system_raii"_suite = [] {
|
Suite raii = "system_raii"_suite = [] {
|
||||||
Case { "happy path won't throw" } = [] {
|
Case { "sandbox" } = [] {
|
||||||
ignore = Fixture_RendererSystem {};
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "happy path has no errors" } = [] {
|
|
||||||
auto fixture = Fixture_RendererSystem {};
|
auto fixture = Fixture_RendererSystem {};
|
||||||
expect_false(fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::error));
|
auto &surface_system = fixture.surface_system();
|
||||||
expect_false(
|
auto &renderer_system = fixture.renderer_system();
|
||||||
fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::warning)
|
|
||||||
);
|
auto timer = lt::time::Timer {};
|
||||||
|
lt::log::trace("Ticking for 3 seconds...");
|
||||||
|
|
||||||
|
while (timer.elapsed_time() < std::chrono::seconds { 3 })
|
||||||
|
{
|
||||||
|
surface_system.tick({});
|
||||||
|
renderer_system.tick({});
|
||||||
|
}
|
||||||
|
|
||||||
|
lt::log::trace("Three seconds passed, quitting...");
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "unhappy path throws" } = [] {
|
// Case { "happy path won't throw" } = [] {
|
||||||
auto fixture = Fixture_SurfaceSystem {};
|
// ignore = Fixture_RendererSystem {};
|
||||||
auto empty_entity = lt::ecs::Entity { fixture.registry(),
|
//
|
||||||
fixture.registry()->create_entity() };
|
//
|
||||||
auto info = fixture.renderer_system_create_info();
|
// auto timer = lt::time::Timer {};
|
||||||
|
// lt::log::trace("Ticking for 3 seconds...");
|
||||||
expect_throw([=] mutable {
|
// while (timer.elapsed_time() < std::chrono::seconds { 3 })
|
||||||
info.registry = nullptr;
|
// {
|
||||||
ignore = lt::renderer::System { info };
|
// system.tick({});
|
||||||
});
|
// }
|
||||||
|
//
|
||||||
expect_throw([=] mutable {
|
// lt::log::trace("Three seconds passed, quitting...");
|
||||||
info.surface_entity = lt::ecs::Entity({}, {});
|
// };
|
||||||
ignore = lt::renderer::System { info };
|
//
|
||||||
});
|
// Case { "happy path has no errors" } = [] {
|
||||||
|
// auto fixture = Fixture_RendererSystem {};
|
||||||
expect_throw([=] mutable {
|
// expect_false(fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::error));
|
||||||
info.config.target_api = lt::renderer::Api::none;
|
// expect_false(
|
||||||
ignore = lt::renderer::System { info };
|
// fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::warning)
|
||||||
});
|
// );
|
||||||
|
// };
|
||||||
// unsupported Apis
|
//
|
||||||
expect_throw([=] mutable {
|
// Case { "unhappy path throws" } = [] {
|
||||||
info.config.target_api = lt::renderer::Api::direct_x;
|
// auto fixture = Fixture_SurfaceSystem {};
|
||||||
ignore = lt::renderer::System { info };
|
// auto empty_entity = lt::ecs::Entity { fixture.registry(),
|
||||||
});
|
// fixture.registry()->create_entity() };
|
||||||
|
// auto info = fixture.renderer_system_create_info();
|
||||||
expect_throw([=] mutable {
|
//
|
||||||
info.config.target_api = lt::renderer::Api::metal;
|
// expect_throw([=] mutable {
|
||||||
ignore = lt::renderer::System { info };
|
// info.registry = nullptr;
|
||||||
});
|
// ignore = lt::renderer::System { info };
|
||||||
|
// });
|
||||||
expect_throw([=] mutable {
|
//
|
||||||
constexpr auto limit = lt::renderer::System::frames_in_flight_upper_limit;
|
// expect_throw([=] mutable {
|
||||||
info.config.max_frames_in_flight = limit + 1u;
|
// info.surface_entity = lt::ecs::Entity({}, {});
|
||||||
ignore = lt::renderer::System { info };
|
// ignore = lt::renderer::System { info };
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
expect_throw([=] mutable {
|
// expect_throw([=] mutable {
|
||||||
constexpr auto limit = lt::renderer::System::frames_in_flight_lower_limit;
|
// info.config.target_api = lt::renderer::Api::none;
|
||||||
info.config.max_frames_in_flight = limit - 1u;
|
// ignore = lt::renderer::System { info };
|
||||||
ignore = lt::renderer::System { info };
|
// });
|
||||||
});
|
//
|
||||||
|
// // unsupported Apis
|
||||||
expect_throw([=] mutable {
|
// expect_throw([=] mutable {
|
||||||
info.debug_callback_info = lt::renderer::IDebugger::CreateInfo {};
|
// info.config.target_api = lt::renderer::Api::direct_x;
|
||||||
ignore = lt::renderer::System { info };
|
// ignore = lt::renderer::System { info };
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
// Make sure the base info is not at fault for unhappiness.
|
// expect_throw([=] mutable {
|
||||||
ignore = lt::renderer::System { info };
|
// info.config.target_api = lt::renderer::Api::metal;
|
||||||
};
|
// ignore = lt::renderer::System { info };
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// expect_throw([=] mutable {
|
||||||
|
// constexpr auto limit = lt::renderer::System::frames_in_flight_upper_limit;
|
||||||
|
// info.config.max_frames_in_flight = limit + 1u;
|
||||||
|
// ignore = lt::renderer::System { info };
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// expect_throw([=] mutable {
|
||||||
|
// constexpr auto limit = lt::renderer::System::frames_in_flight_lower_limit;
|
||||||
|
// info.config.max_frames_in_flight = limit - 1u;
|
||||||
|
// ignore = lt::renderer::System { info };
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// expect_throw([=] mutable {
|
||||||
|
// info.debug_callback_info = lt::renderer::IDebugger::CreateInfo {};
|
||||||
|
// ignore = lt::renderer::System { info };
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// // Make sure the base info is not at fault for unhappiness.
|
||||||
|
// ignore = lt::renderer::System { info };
|
||||||
|
// };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3207,19 +3207,7 @@ Surface::Surface(const Instance &instance, const CreateInfo &info)
|
||||||
.surface = info.surface,
|
.surface = info.surface,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
log::debug(
|
|
||||||
"Display proxy's version: {}",
|
|
||||||
wl_proxy_get_version(std::bit_cast<wl_proxy *>(info.display))
|
|
||||||
);
|
|
||||||
|
|
||||||
log::debug(
|
|
||||||
"Surface proxy's version: {}",
|
|
||||||
wl_proxy_get_version(std::bit_cast<wl_proxy *>(info.surface))
|
|
||||||
);
|
|
||||||
|
|
||||||
vkc(api::create_wayland_surface_khr(instance.get_vk_handle(), &vk_info, nullptr, &m_surface));
|
vkc(api::create_wayland_surface_khr(instance.get_vk_handle(), &vk_info, nullptr, &m_surface));
|
||||||
log::debug("Wayland surface vulkan handle id is: {}", (size_t)m_surface);
|
|
||||||
#elif defined(LIGHT_PLATFORM_WINDOWS)
|
#elif defined(LIGHT_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
const auto vk_info = VkWin32SurfaceCreateInfoKHR {
|
const auto vk_info = VkWin32SurfaceCreateInfoKHR {
|
||||||
|
|
@ -3868,17 +3856,13 @@ Device::Device(const Gpu &gpu, CreateInfo info)
|
||||||
}
|
}
|
||||||
|
|
||||||
vkc(api::create_device(gpu.m_physical_device, &vk_info, nullptr, &m_device));
|
vkc(api::create_device(gpu.m_physical_device, &vk_info, nullptr, &m_device));
|
||||||
log::debug("Created device: 0x{:x}", (size_t)m_device, (size_t)m_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
{
|
{
|
||||||
if (m_device)
|
if (m_device)
|
||||||
{
|
{
|
||||||
log::debug("Destroying device {:x}...", (size_t)m_device);
|
|
||||||
api::destroy_device(m_device, nullptr);
|
api::destroy_device(m_device, nullptr);
|
||||||
log::debug("...Destroyed device");
|
|
||||||
std::cout << "D" << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4587,9 +4571,6 @@ Swapchain::Swapchain(Device &device, Surface &surface, CreateInfo info)
|
||||||
: m_device(device.m_device.get())
|
: m_device(device.m_device.get())
|
||||||
, m_swapchain()
|
, m_swapchain()
|
||||||
{
|
{
|
||||||
log::debug("Wayland surface vulkan handle id is now: 0x{:x}", (size_t)surface.m_surface);
|
|
||||||
log::debug("Got device for swapchain: 0x{:x}", (size_t)m_device);
|
|
||||||
|
|
||||||
auto vk_info = VkSwapchainCreateInfoKHR {
|
auto vk_info = VkSwapchainCreateInfoKHR {
|
||||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||||
.surface = surface.m_surface,
|
.surface = surface.m_surface,
|
||||||
|
|
@ -4608,36 +4589,20 @@ Swapchain::Swapchain(Device &device, Surface &surface, CreateInfo info)
|
||||||
.clipped = VK_TRUE,
|
.clipped = VK_TRUE,
|
||||||
.oldSwapchain = nullptr,
|
.oldSwapchain = nullptr,
|
||||||
};
|
};
|
||||||
log::debug("Creating swapchain: 0x{:x}", (size_t)m_swapchain);
|
|
||||||
vkc(api::create_swapchain_khr(m_device, &vk_info, nullptr, &m_swapchain));
|
vkc(api::create_swapchain_khr(m_device, &vk_info, nullptr, &m_swapchain));
|
||||||
log::debug("Created swapchain: 0x{:x}", (size_t)m_swapchain);
|
|
||||||
|
|
||||||
if (info.name.empty())
|
if (info.name.empty())
|
||||||
{
|
{
|
||||||
info.name = "<unnamed>";
|
info.name = "<unnamed>";
|
||||||
}
|
}
|
||||||
device.name(*this, "{}", info.name);
|
device.name(*this, "{}", info.name);
|
||||||
|
|
||||||
log::debug("Still got device for swapchain: 0x{:x}", (size_t)m_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Swapchain::~Swapchain()
|
Swapchain::~Swapchain()
|
||||||
{
|
{
|
||||||
if (m_device)
|
if (m_device)
|
||||||
{
|
{
|
||||||
log::debug("Destroyig swapchain...");
|
|
||||||
log::debug("device: 0x{:x}", (size_t)m_device);
|
|
||||||
log::debug("swapchain: 0x{:x}", (size_t)m_swapchain);
|
|
||||||
log::debug("vkDestroySwapchainKHR: 0x{:x}", (size_t)api::destroy_swapchain_khr);
|
|
||||||
api::destroy_swapchain_khr(m_device, m_swapchain, nullptr);
|
api::destroy_swapchain_khr(m_device, m_swapchain, nullptr);
|
||||||
log::debug("...Destroyed swapchain");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log::debug(
|
|
||||||
"Skipped destruction of Swapchain due to nulled device: 0{:x}",
|
|
||||||
(size_t)m_device
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4694,9 +4659,10 @@ Buffer::Buffer(Device &device, CreateInfo info): m_device(device.m_device.get())
|
||||||
|
|
||||||
Buffer::~Buffer()
|
Buffer::~Buffer()
|
||||||
{
|
{
|
||||||
std::cout << "B" << std::endl;
|
if (m_device)
|
||||||
|
{
|
||||||
api::destroy_buffer(m_device, m_buffer, nullptr);
|
api::destroy_buffer(m_device, m_buffer, nullptr);
|
||||||
std::cout << "C" << std::endl;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto Buffer::get_memory_requirements() const -> MemoryRequirements
|
[[nodiscard]] auto Buffer::get_memory_requirements() const -> MemoryRequirements
|
||||||
|
|
@ -5103,9 +5069,11 @@ Messenger::Messenger(Instance &instance, CreateInfo info): m_instance(instance.g
|
||||||
}
|
}
|
||||||
|
|
||||||
Messenger::~Messenger()
|
Messenger::~Messenger()
|
||||||
|
{
|
||||||
|
if (m_instance)
|
||||||
{
|
{
|
||||||
api::destroy_debug_messenger(m_instance, m_messenger, nullptr);
|
api::destroy_debug_messenger(m_instance, m_messenger, nullptr);
|
||||||
std::cout << "C1" << std::endl;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ Renderer::Renderer(
|
||||||
frame_fence.reset();
|
frame_fence.reset();
|
||||||
|
|
||||||
map_buffers(frame_idx);
|
map_buffers(frame_idx);
|
||||||
submit_scene();
|
// submit_scene();
|
||||||
record_cmd(cmd, image_idx);
|
record_cmd(cmd, image_idx);
|
||||||
|
|
||||||
auto &submit_semaphore = m_submit_semaphores[image_idx];
|
auto &submit_semaphore = m_submit_semaphores[image_idx];
|
||||||
|
|
@ -250,35 +250,35 @@ void Renderer::record_cmd(vk::CommandBuffer &cmd, std::uint32_t image_idx)
|
||||||
|
|
||||||
m_staging_buffer.unmap();
|
m_staging_buffer.unmap();
|
||||||
|
|
||||||
if (m_current_sprite_idx)
|
// if (m_current_sprite_idx)
|
||||||
{
|
// {
|
||||||
cmd.copy(
|
// cmd.copy(
|
||||||
{
|
// {
|
||||||
.src_buffer = &m_staging_buffer.vk(),
|
// .src_buffer = &m_staging_buffer.vk(),
|
||||||
.dst_buffer = &m_vertex_buffer.vk(),
|
// .dst_buffer = &m_vertex_buffer.vk(),
|
||||||
.src_offset = m_staging_offset,
|
// .src_offset = m_staging_offset,
|
||||||
.dst_offset = m_staging_offset,
|
// .dst_offset = m_staging_offset,
|
||||||
.size = m_current_sprite_idx * sizeof(components::Sprite::Vertex),
|
// .size = m_current_sprite_idx * sizeof(components::Sprite::Vertex),
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
cmd.push_constants(
|
// cmd.push_constants(
|
||||||
{
|
// {
|
||||||
.layout = &m_pass->get_pipeline_layout(),
|
// .layout = &m_pass->get_pipeline_layout(),
|
||||||
.shader_stages = vk::ShaderStageFlags::vertex_bit,
|
// .shader_stages = vk::ShaderStageFlags::vertex_bit,
|
||||||
.offset = 0u,
|
// .offset = 0u,
|
||||||
.size = sizeof(FrameConstants),
|
// .size = sizeof(FrameConstants),
|
||||||
.data = &m_frame_constants,
|
// .data = &m_frame_constants,
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
cmd.bind_descriptor_set(
|
// cmd.bind_descriptor_set(
|
||||||
m_global_set,
|
// m_global_set,
|
||||||
vk::Pipeline::BindPoint::graphics,
|
// vk::Pipeline::BindPoint::graphics,
|
||||||
m_pass->get_pipeline_layout(),
|
// m_pass->get_pipeline_layout(),
|
||||||
0
|
// 0
|
||||||
);
|
// );
|
||||||
|
|
||||||
using AccessFlagBits = vk::CommandBuffer::ImageBarrierInfo::AccessFlagBits;
|
using AccessFlagBits = vk::CommandBuffer::ImageBarrierInfo::AccessFlagBits;
|
||||||
cmd.image_barrier(
|
cmd.image_barrier(
|
||||||
|
|
@ -310,25 +310,25 @@ void Renderer::record_cmd(vk::CommandBuffer &cmd, std::uint32_t image_idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
cmd.bind_pipeline(m_pass->get_pipeline(), vk::Pipeline::BindPoint::graphics);
|
// cmd.bind_pipeline(m_pass->get_pipeline(), vk::Pipeline::BindPoint::graphics);
|
||||||
cmd.set_viewport(
|
// cmd.set_viewport(
|
||||||
{
|
// {
|
||||||
.origin = {},
|
// .origin = {},
|
||||||
.extent = { static_cast<float>(m_resolution.x), static_cast<float>(m_resolution.y) },
|
// .extent = { static_cast<float>(m_resolution.x), static_cast<float>(m_resolution.y) },
|
||||||
.min_depth = 0.0f,
|
// .min_depth = 0.0f,
|
||||||
.max_depth = 1.0f,
|
// .max_depth = 1.0f,
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
cmd.set_scissor({ .offset = {}, .extent = m_resolution });
|
// cmd.set_scissor({ .offset = {}, .extent = m_resolution });
|
||||||
cmd.draw(
|
// cmd.draw(
|
||||||
{
|
// {
|
||||||
.vertex_count = static_cast<std::uint32_t>(m_current_sprite_idx),
|
// .vertex_count = static_cast<std::uint32_t>(m_current_sprite_idx),
|
||||||
.instance_count = 1u,
|
// .instance_count = 1u,
|
||||||
.first_vertex = 0u,
|
// .first_vertex = 0u,
|
||||||
.first_instance = 0u,
|
// .first_instance = 0u,
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
cmd.end_rendering();
|
cmd.end_rendering();
|
||||||
cmd.image_barrier(
|
cmd.image_barrier(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import test.test;
|
|
||||||
import test.expects;
|
|
||||||
import surface.system;
|
|
||||||
import surface.events;
|
|
||||||
import surface.requests;
|
|
||||||
import ecs.registry;
|
|
||||||
import memory.scope;
|
|
||||||
import memory.reference;
|
|
||||||
import logger;
|
|
||||||
import math.vec2;
|
|
||||||
import app.system;
|
|
||||||
import std;
|
|
||||||
|
|
||||||
using ::lt::surface::SurfaceComponent;
|
|
||||||
using ::lt::surface::System;
|
|
||||||
using ::lt::test::Case;
|
|
||||||
using ::lt::test::expect_eq;
|
|
||||||
using ::lt::test::expect_ne;
|
|
||||||
using ::lt::test::expect_not_nullptr;
|
|
||||||
using ::lt::test::expect_throw;
|
|
||||||
using ::lt::test::Suite;
|
|
||||||
using ::std::ignore;
|
|
||||||
using ::lt::test::operator""_suite;
|
|
||||||
|
|
||||||
Suite raii = "platform_linux_raii"_suite = [] {
|
|
||||||
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
|
||||||
std::ignore = System { registry };
|
|
||||||
};
|
|
||||||
|
|
@ -1,527 +0,0 @@
|
||||||
module;
|
|
||||||
#include <Windows.h>
|
|
||||||
module surface.system;
|
|
||||||
import surface.constants;
|
|
||||||
import debug.assertions;
|
|
||||||
import memory.reference;
|
|
||||||
import surface.requests;
|
|
||||||
import surface.events;
|
|
||||||
import logger;
|
|
||||||
import ecs.registry;
|
|
||||||
import ecs.entity;
|
|
||||||
import time;
|
|
||||||
import std;
|
|
||||||
|
|
||||||
namespace lt::surface {
|
|
||||||
|
|
||||||
template<class... Ts>
|
|
||||||
struct overloads: Ts...
|
|
||||||
{
|
|
||||||
using Ts::operator()...;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ensure_component_sanity(const SurfaceComponent &component);
|
|
||||||
|
|
||||||
auto CALLBACK native_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT;
|
|
||||||
|
|
||||||
System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry))
|
|
||||||
{
|
|
||||||
debug::ensure(m_registry, "Failed to initialize surface system: null registry");
|
|
||||||
|
|
||||||
debug::ensure(
|
|
||||||
m_registry->view<SurfaceComponent>().get_size() == 0,
|
|
||||||
"Failed to initialize surface system: registry has surface component(s)"
|
|
||||||
);
|
|
||||||
|
|
||||||
m_registry->connect_on_destruct<SurfaceComponent>(
|
|
||||||
[this](ecs::Registry ®istry, ecs::EntityId entity) {
|
|
||||||
on_surface_destruct(registry, entity);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
auto window_class = WNDCLASS {
|
|
||||||
.lpfnWndProc = native_window_proc,
|
|
||||||
.hInstance = GetModuleHandle(nullptr),
|
|
||||||
.lpszClassName = constants::class_name,
|
|
||||||
};
|
|
||||||
RegisterClass(&window_class);
|
|
||||||
}
|
|
||||||
|
|
||||||
System::~System()
|
|
||||||
{
|
|
||||||
if (!m_registry)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO(Light): make registry.remove not invalidate iterators
|
|
||||||
auto entities_to_remove = std::vector<ecs::EntityId> {};
|
|
||||||
for (auto &[entity, surface] : m_registry->view<SurfaceComponent>())
|
|
||||||
{
|
|
||||||
entities_to_remove.emplace_back(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto entity : entities_to_remove)
|
|
||||||
{
|
|
||||||
m_registry->remove<SurfaceComponent>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_registry->disconnect_on_construct<SurfaceComponent>();
|
|
||||||
m_registry->disconnect_on_destruct<SurfaceComponent>();
|
|
||||||
}
|
|
||||||
catch (const std::exception &exp)
|
|
||||||
{
|
|
||||||
log::error("Uncaught exception in surface::~System:");
|
|
||||||
log::error("\twhat: {}", exp.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::on_register()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::on_unregister()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::CreateInfo info)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto &component = m_registry->add<SurfaceComponent>(entity, info);
|
|
||||||
auto &surface = m_registry->get<SurfaceComponent>(entity);
|
|
||||||
const auto &resolution = surface.get_resolution();
|
|
||||||
const auto &position = surface.get_position();
|
|
||||||
ensure_component_sanity(surface);
|
|
||||||
|
|
||||||
surface.m_native_data.window = CreateWindowEx(
|
|
||||||
0,
|
|
||||||
constants::class_name,
|
|
||||||
info.title.data(),
|
|
||||||
WS_OVERLAPPEDWINDOW,
|
|
||||||
CW_USEDEFAULT,
|
|
||||||
CW_USEDEFAULT,
|
|
||||||
CW_USEDEFAULT,
|
|
||||||
CW_USEDEFAULT,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
GetModuleHandle(nullptr),
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
debug::ensure(surface.m_native_data.window, "Failed to create Windows surface component");
|
|
||||||
|
|
||||||
ShowWindow(surface.m_native_data.window, SW_NORMAL);
|
|
||||||
|
|
||||||
// TODO(Light): refactor "environment" into standalone module
|
|
||||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
||||||
// auto *display_env = std::getenv("DISPLAY");
|
|
||||||
// debug::ensure(display_env != nullptr, "DISPLAY env var not found!");
|
|
||||||
//
|
|
||||||
// auto *display = XOpenDisplay(display_env);
|
|
||||||
// debug::ensure(display, "Failed to open XDisplay with DISPLAY: {}", display_env);
|
|
||||||
//
|
|
||||||
// auto root_window = XDefaultRootWindow(display);
|
|
||||||
//
|
|
||||||
// auto border_width = 0;
|
|
||||||
// auto depth = std::int32_t { CopyFromParent };
|
|
||||||
// auto window_class = CopyFromParent;
|
|
||||||
// auto *visual = (Visual *)CopyFromParent;
|
|
||||||
//
|
|
||||||
// auto attribute_value_mask = CWBackPixel | CWEventMask;
|
|
||||||
// auto attributes = XSetWindowAttributes {
|
|
||||||
// .background_pixel = 0xffafe9af,
|
|
||||||
// .event_mask = all_events_mask,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// typedef struct Hints
|
|
||||||
// {
|
|
||||||
// unsigned long flags;
|
|
||||||
// unsigned long functions;
|
|
||||||
// unsigned long decorations;
|
|
||||||
// long inputMode;
|
|
||||||
// unsigned long status;
|
|
||||||
// } Hints;
|
|
||||||
//
|
|
||||||
// auto main_window = XCreateWindow(
|
|
||||||
// display,
|
|
||||||
// root_window,
|
|
||||||
// position.x,
|
|
||||||
// position.y,
|
|
||||||
// resolution.x,
|
|
||||||
// resolution.y,
|
|
||||||
// border_width,
|
|
||||||
// depth,
|
|
||||||
// window_class,
|
|
||||||
// visual,
|
|
||||||
// attribute_value_mask,
|
|
||||||
// &attributes
|
|
||||||
// );
|
|
||||||
// surface.m_native_data.display = display;
|
|
||||||
// surface.m_native_data.window = main_window;
|
|
||||||
//
|
|
||||||
// surface.m_native_data.wm_delete_message = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
|
||||||
// XSetWMProtocols(display, main_window, &surface.m_native_data.wm_delete_message, 1);
|
|
||||||
//
|
|
||||||
// // code to remove decoration
|
|
||||||
// auto hints = std::array<const unsigned char, 5> { 2, 0, 0, 0, 0 };
|
|
||||||
// const auto motif_hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);
|
|
||||||
//
|
|
||||||
// XChangeProperty(
|
|
||||||
// display,
|
|
||||||
// surface.m_native_data.window,
|
|
||||||
// motif_hints,
|
|
||||||
// motif_hints,
|
|
||||||
// 32,
|
|
||||||
// PropModeReplace,
|
|
||||||
// hints.data(),
|
|
||||||
// 5
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// XMapWindow(display, main_window);
|
|
||||||
// XStoreName(display, main_window, surface.m_title.c_str());
|
|
||||||
// XFlush(display);
|
|
||||||
//
|
|
||||||
// if (!surface.is_visible())
|
|
||||||
// {
|
|
||||||
// XUnmapWindow(display, main_window);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
catch (const std::exception &exp)
|
|
||||||
{
|
|
||||||
log::error("Exception thrown when on_constructing surface component");
|
|
||||||
log::error("\tentity: {}", std::uint32_t { entity });
|
|
||||||
log::error("\twhat: {}", exp.what());
|
|
||||||
m_registry->remove<SurfaceComponent>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::on_surface_destruct(ecs::Registry ®istry, ecs::EntityId entity)
|
|
||||||
{
|
|
||||||
auto *window = registry.get<SurfaceComponent>(entity).get_native_data().window;
|
|
||||||
if (!window)
|
|
||||||
{
|
|
||||||
log::warn("Surface component destroyed with null window handle");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DestroyWindow(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::handle_events(SurfaceComponent &surface)
|
|
||||||
{
|
|
||||||
auto &queue = surface.m_event_queue;
|
|
||||||
queue.clear();
|
|
||||||
|
|
||||||
auto message = MSG {};
|
|
||||||
while (PeekMessage(&message, 0, {}, {}, PM_REMOVE))
|
|
||||||
{
|
|
||||||
switch (message.message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug("Window message type: {}", std::uint32_t { message.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
// auto event = XEvent {};
|
|
||||||
// auto &[display, window, wm_delete_message] = surface.m_native_data;
|
|
||||||
//
|
|
||||||
// XFlush(display);
|
|
||||||
// while (XEventsQueued(display, QueuedAlready) != 0)
|
|
||||||
// {
|
|
||||||
// XNextEvent(surface.m_native_data.display, &event);
|
|
||||||
//
|
|
||||||
// switch (event.type)
|
|
||||||
// {
|
|
||||||
// case KeyPress:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<KeyPressedEvent>(
|
|
||||||
// static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
|
|
||||||
// );
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case KeyRelease:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<KeyReleasedEvent>(
|
|
||||||
// static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
|
|
||||||
// );
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case ButtonPress:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<ButtonPressedEvent>(static_cast<int>(event.xbutton.button));
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case ButtonRelease:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<ButtonReleasedEvent>(static_cast<int>(event.xbutton.button));
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case FocusIn:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<GainFocusEvent>({});
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case FocusOut:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<LostFocusEvent>({});
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case ClientMessage:
|
|
||||||
// {
|
|
||||||
// if (event.xclient.data.l[0] == wm_delete_message)
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<ClosedEvent>({});
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case MotionNotify:
|
|
||||||
// {
|
|
||||||
// queue.emplace_back<MouseMovedEvent>(MouseMovedEvent {
|
|
||||||
// static_cast<float>(event.xmotion.x),
|
|
||||||
// static_cast<float>(event.xmotion.y),
|
|
||||||
// });
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case ConfigureNotify:
|
|
||||||
// {
|
|
||||||
// const auto [prev_width, prev_height] = surface.get_resolution();
|
|
||||||
// const auto new_width = event.xconfigure.width;
|
|
||||||
// const auto new_height = event.xconfigure.height;
|
|
||||||
// if (prev_width != new_width || prev_height != new_height)
|
|
||||||
// {
|
|
||||||
// surface.m_resolution.x = new_width;
|
|
||||||
// surface.m_resolution.y = new_height;
|
|
||||||
// queue.emplace_back<ResizedEvent>(ResizedEvent {
|
|
||||||
// static_cast<std::uint32_t>(new_width),
|
|
||||||
// static_cast<std::uint32_t>(new_height),
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const auto [prev_x, prev_y] = surface.get_position();
|
|
||||||
// const auto new_x = event.xconfigure.x;
|
|
||||||
// const auto new_y = event.xconfigure.y;
|
|
||||||
// if (prev_x != new_x || prev_y != new_y)
|
|
||||||
// {
|
|
||||||
// surface.m_position.x = new_x;
|
|
||||||
// surface.m_position.y = new_y;
|
|
||||||
// queue.emplace_back<MovedEvent>(MovedEvent {
|
|
||||||
// new_x,
|
|
||||||
// new_y,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// default: break; /* pass */
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::handle_requests(SurfaceComponent &surface)
|
|
||||||
{
|
|
||||||
const auto visitor = overloads {
|
|
||||||
[&](const ModifyTitleRequest &request) { modify_title(surface, request); },
|
|
||||||
[&](const ModifyResolutionRequest &request) { modify_resolution(surface, request); },
|
|
||||||
[&](const ModifyPositionRequest &request) { modify_position(surface, request); },
|
|
||||||
[&](const ModifyVisibilityRequest &request) { modify_visiblity(surface, request); },
|
|
||||||
[&](const auto &) { log::error("Unknown surface request"); },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto &request : surface.peek_requests())
|
|
||||||
{
|
|
||||||
std::visit(visitor, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
surface.m_requests.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request)
|
|
||||||
{
|
|
||||||
surface.m_title = request.title;
|
|
||||||
|
|
||||||
// const auto &[display, window, _] = surface.get_native_data();
|
|
||||||
// XStoreName(display, window, request.title.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request)
|
|
||||||
{
|
|
||||||
// surface.m_resolution = request.resolution;
|
|
||||||
|
|
||||||
// auto &[display, window, _] = surface.m_native_data;
|
|
||||||
// const auto &[width, height] = request.resolution;
|
|
||||||
// // XResizeWindow(display, window, width, height);
|
|
||||||
//
|
|
||||||
// // get baseline serial number for X requests generated from XResizeWindow
|
|
||||||
// auto serial = NextRequest(display);
|
|
||||||
//
|
|
||||||
// // request a new window size from the X server
|
|
||||||
// XResizeWindow(
|
|
||||||
// display,
|
|
||||||
// window,
|
|
||||||
// static_cast<std::uint32_t>(width),
|
|
||||||
// static_cast<std::uint32_t>(height)
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // flush output queue and wait for X server to processes the request
|
|
||||||
// XSync(display, False);
|
|
||||||
// // The documentation for XResizeWindow includes this important note:
|
|
||||||
// //
|
|
||||||
// // If the override-redirect flag of the window is False and some
|
|
||||||
// // other client has selected SubstructureRedirectMask on the parent,
|
|
||||||
// // the X server generates a ConfigureRequest event, and no further
|
|
||||||
// // processing is performed.
|
|
||||||
// //
|
|
||||||
// // What this means, essentially, is that if this window is a top-level
|
|
||||||
// // window, then it's the window manager (the "other client") that is
|
|
||||||
// // responsible for changing this window's size. So when we call
|
|
||||||
// // XResizeWindow() on a top-level window, then instead of resizing
|
|
||||||
// // the window immediately, the X server informs the window manager,
|
|
||||||
// // and then the window manager sets our new size (usually it will be
|
|
||||||
// // the size we asked for). We receive a ConfigureNotify event when
|
|
||||||
// // our new size has been set.
|
|
||||||
// constexpr auto lifespan = std::chrono::milliseconds { 10 };
|
|
||||||
// auto timer = time::Timer {};
|
|
||||||
// auto event = XEvent {};
|
|
||||||
// while (!XCheckIfEvent(
|
|
||||||
// display,
|
|
||||||
// &event,
|
|
||||||
// XEventTypeEquals<ConfigureNotify>,
|
|
||||||
// reinterpret_cast<XPointer>(&window) // NOLINT
|
|
||||||
// )
|
|
||||||
// || event.xconfigure.serial < serial)
|
|
||||||
// {
|
|
||||||
// std::this_thread::sleep_for(std::chrono::microseconds { 100 });
|
|
||||||
// if (timer.elapsed_time() > lifespan)
|
|
||||||
// {
|
|
||||||
// log::error("Timed out waiting for XResizeWindow's event");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // We don't need to update the component's state and handle the event in this funcion.
|
|
||||||
// // Since handle_requests is called before handle_events.
|
|
||||||
// // So we just put the event back into the queue and move on.
|
|
||||||
// XPutBackEvent(display, &event);
|
|
||||||
// XSync(display, False);
|
|
||||||
// XFlush(display);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
|
||||||
{
|
|
||||||
// surface.m_position = request.position;
|
|
||||||
|
|
||||||
// auto &[display, window, _] = surface.m_native_data;
|
|
||||||
// const auto &[x, y] = request.position;
|
|
||||||
//
|
|
||||||
// // get baseline serial number for X requests generated from XResizeWindow
|
|
||||||
// auto serial = NextRequest(display);
|
|
||||||
// XMoveWindow(display, window, static_cast<int>(x), static_cast<int>(y));
|
|
||||||
//
|
|
||||||
// // flush output queue and wait for X server to processes the request
|
|
||||||
// XSync(display, False);
|
|
||||||
// constexpr auto lifespan = std::chrono::milliseconds { 10 };
|
|
||||||
// auto timer = time::Timer {};
|
|
||||||
// auto event = XEvent {};
|
|
||||||
// while (!XCheckIfEvent(
|
|
||||||
// display,
|
|
||||||
// &event,
|
|
||||||
// XEventTypeEquals<ConfigureNotify>,
|
|
||||||
// reinterpret_cast<XPointer>(&window) // NOLINT
|
|
||||||
// )
|
|
||||||
// || event.xconfigure.serial < serial)
|
|
||||||
// {
|
|
||||||
// std::this_thread::sleep_for(std::chrono::microseconds { 100 });
|
|
||||||
// if (timer.elapsed_time() > lifespan)
|
|
||||||
// {
|
|
||||||
// log::error("Timed out waiting for XMoveWindow's event");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // We don't need to update the component's state and handle the event in this funcion.
|
|
||||||
// // Since handle_requests is called before handle_events.
|
|
||||||
// // So we just put the event back into the queue and move on.
|
|
||||||
// XPutBackEvent(display, &event);
|
|
||||||
// XSync(display, False);
|
|
||||||
// XFlush(display);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request)
|
|
||||||
{
|
|
||||||
// const auto &[display, window, _] = surface.get_native_data();
|
|
||||||
// surface.m_visible = request.visible;
|
|
||||||
|
|
||||||
// if (request.visible)
|
|
||||||
// {
|
|
||||||
// XMapWindow(display, window);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// XUnmapWindow(display, window);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::tick(app::TickInfo tick)
|
|
||||||
{
|
|
||||||
for (auto &[id, surface] : m_registry->view<SurfaceComponent>())
|
|
||||||
{
|
|
||||||
handle_requests(surface);
|
|
||||||
handle_events(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
m_last_tick_result = app::TickResult {
|
|
||||||
.info = tick,
|
|
||||||
.duration = now - tick.start_time,
|
|
||||||
.end_time = now,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ensure_component_sanity(const SurfaceComponent &component)
|
|
||||||
{
|
|
||||||
auto [width, height] = component.get_resolution();
|
|
||||||
|
|
||||||
debug::ensure(width != 0u, "Received bad values for surface component: width({}) == 0", width);
|
|
||||||
|
|
||||||
debug::ensure(
|
|
||||||
height != 0u,
|
|
||||||
"Received bad values for surface component: height({}) == 0",
|
|
||||||
height
|
|
||||||
);
|
|
||||||
|
|
||||||
debug::ensure(
|
|
||||||
width < SurfaceComponent::max_dimension,
|
|
||||||
"Received bad values for surface component: width({}) > max_dimension({})",
|
|
||||||
width,
|
|
||||||
SurfaceComponent::max_dimension
|
|
||||||
);
|
|
||||||
|
|
||||||
debug::ensure(
|
|
||||||
height < SurfaceComponent::max_dimension,
|
|
||||||
"Received bad values for surface component: height({}) > max_dimension({})",
|
|
||||||
height,
|
|
||||||
SurfaceComponent::max_dimension
|
|
||||||
);
|
|
||||||
|
|
||||||
debug::ensure(
|
|
||||||
component.get_title().size() < SurfaceComponent::max_title_length,
|
|
||||||
"Received bad values for surface component: title.size({}) > max_title_length({})",
|
|
||||||
component.get_title().size(),
|
|
||||||
SurfaceComponent::max_title_length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CALLBACK native_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT
|
|
||||||
{
|
|
||||||
switch (uMsg)
|
|
||||||
{
|
|
||||||
case WM_DESTROY:
|
|
||||||
{
|
|
||||||
PostQuitMessage(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace lt::surface
|
|
||||||
|
|
@ -6,7 +6,6 @@ module;
|
||||||
#error "Unsupported platform"
|
#error "Unsupported platform"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
import logger;
|
|
||||||
export module surface.system;
|
export module surface.system;
|
||||||
export import :components;
|
export import :components;
|
||||||
import debug.assertions;
|
import debug.assertions;
|
||||||
|
|
@ -16,6 +15,7 @@ import math.vec2;
|
||||||
import surface.requests;
|
import surface.requests;
|
||||||
import memory.reference;
|
import memory.reference;
|
||||||
import memory.null_on_move;
|
import memory.null_on_move;
|
||||||
|
import logger;
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
export namespace lt::surface {
|
export namespace lt::surface {
|
||||||
|
|
@ -50,13 +50,82 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if defined(LIGHT_PLATFORM_LINUX)
|
#if defined(LIGHT_PLATFORM_LINUX)
|
||||||
static void handle_globals(
|
static void wayland_registry_listener(
|
||||||
void *data,
|
void *data,
|
||||||
wl_registry *registry,
|
wl_registry *registry,
|
||||||
std::uint32_t name,
|
std::uint32_t name,
|
||||||
const char *interface,
|
const char *interface,
|
||||||
std::uint32_t version
|
std::uint32_t version
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static void wayland_seat_capabilities_listener(
|
||||||
|
void *data,
|
||||||
|
wl_seat *seat,
|
||||||
|
std::uint32_t capabilities
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_leave_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
wl_surface *surface
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_enter_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
wl_surface *surface,
|
||||||
|
wl_fixed_t surface_x,
|
||||||
|
wl_fixed_t surface_y
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_motion_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *listener,
|
||||||
|
std::uint32_t time,
|
||||||
|
wl_fixed_t surface_x,
|
||||||
|
wl_fixed_t surface_y
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_button_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
std::uint32_t time,
|
||||||
|
std::uint32_t button,
|
||||||
|
std::uint32_t state
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_axis_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t time,
|
||||||
|
std::uint32_t axis,
|
||||||
|
wl_fixed_t value
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_axis_source_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t axis_source
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_axis_stop_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t time,
|
||||||
|
std::uint32_t axis_source
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_axis_discrete_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t axis,
|
||||||
|
std::int32_t discrete
|
||||||
|
);
|
||||||
|
|
||||||
|
static void wayland_pointer_frame_listener(void *data, wl_pointer *pointer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void on_surface_destruct(ecs::Registry ®istry, ecs::EntityId entity);
|
void on_surface_destruct(ecs::Registry ®istry, ecs::EntityId entity);
|
||||||
|
|
@ -81,18 +150,32 @@ private:
|
||||||
|
|
||||||
memory::Ref<ecs::Registry> m_registry;
|
memory::Ref<ecs::Registry> m_registry;
|
||||||
|
|
||||||
app::TickResult m_last_tick_result;
|
app::TickResult m_last_tick_result {};
|
||||||
|
|
||||||
#if defined(LIGHT_PLATFORM_LINUX)
|
#if defined(LIGHT_PLATFORM_LINUX)
|
||||||
memory::NullOnMove<wl_display *> m_wl_display {};
|
memory::NullOnMove<wl_display *> m_wl_display {};
|
||||||
|
|
||||||
memory::NullOnMove<wl_registry *> m_wl_registry {};
|
wl_registry *m_wl_registry {};
|
||||||
|
|
||||||
wl_registry_listener m_wl_registry_listener {};
|
wl_registry_listener m_wl_registry_listener {};
|
||||||
|
|
||||||
memory::NullOnMove<wl_compositor *> m_wl_compositor {};
|
wl_seat_listener m_wl_seat_listener {};
|
||||||
|
|
||||||
|
wl_pointer_listener m_wl_pointer_listener {};
|
||||||
|
|
||||||
|
wl_compositor *m_wl_compositor {};
|
||||||
|
|
||||||
|
xdg_wm_base *m_shell = {};
|
||||||
|
|
||||||
|
wl_seat *m_wl_seat {};
|
||||||
|
|
||||||
|
wl_keyboard *m_wl_keyboard {};
|
||||||
|
|
||||||
|
wl_pointer *m_wl_pointer {};
|
||||||
|
|
||||||
|
wl_touch *m_wl_touch {}; // TODO(Light): Add touch support
|
||||||
|
|
||||||
|
|
||||||
memory::NullOnMove<xdg_wm_base *> m_shell = {};
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -106,7 +189,6 @@ namespace lt::surface {
|
||||||
void handle_shell_ping(void *data, xdg_wm_base *shell, std::uint32_t serial)
|
void handle_shell_ping(void *data, xdg_wm_base *shell, std::uint32_t serial)
|
||||||
{
|
{
|
||||||
std::ignore = data;
|
std::ignore = data;
|
||||||
|
|
||||||
xdg_wm_base_pong(shell, serial);
|
xdg_wm_base_pong(shell, serial);
|
||||||
}
|
}
|
||||||
const auto shell_listener = xdg_wm_base_listener {
|
const auto shell_listener = xdg_wm_base_listener {
|
||||||
|
|
@ -142,7 +224,143 @@ const auto toplevel_listener = xdg_toplevel_listener {
|
||||||
.close = &handle_toplevel_close,
|
.close = &handle_toplevel_close,
|
||||||
};
|
};
|
||||||
|
|
||||||
void System::handle_globals(
|
void wayland_pointer_leave_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
wl_surface *surface
|
||||||
|
)
|
||||||
|
{
|
||||||
|
void *system = std::bit_cast<System *>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_seat_capabilities_listener(
|
||||||
|
void *data,
|
||||||
|
wl_seat *seat,
|
||||||
|
std::uint32_t capabilities
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::ignore = seat;
|
||||||
|
|
||||||
|
auto *system = std::bit_cast<System *>(data);
|
||||||
|
const auto have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
|
||||||
|
|
||||||
|
if (have_pointer && !system->m_wl_pointer)
|
||||||
|
{
|
||||||
|
system->m_wl_pointer = wl_seat_get_pointer(system->m_wl_seat);
|
||||||
|
wl_pointer_add_listener(system->m_wl_pointer, &system->m_wl_pointer_listener, system);
|
||||||
|
log::info(
|
||||||
|
"Added Wayland pointer (0x{:x})",
|
||||||
|
std::bit_cast<std::size_t>(system->m_wl_pointer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (!have_pointer && system->m_wl_pointer)
|
||||||
|
{
|
||||||
|
wl_pointer_release(system->m_wl_pointer);
|
||||||
|
system->m_wl_pointer = nullptr;
|
||||||
|
|
||||||
|
log::info(
|
||||||
|
"Released Wayland pointer (0x{:x})",
|
||||||
|
std::bit_cast<std::size_t>(system->m_wl_pointer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_leave_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
wl_surface *surface
|
||||||
|
)
|
||||||
|
{
|
||||||
|
log::debug("Pointer leave...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_enter_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
wl_surface *surface,
|
||||||
|
wl_fixed_t surface_x,
|
||||||
|
wl_fixed_t surface_y
|
||||||
|
)
|
||||||
|
{
|
||||||
|
log::debug("Pointer enter...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_motion_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *listener,
|
||||||
|
std::uint32_t time,
|
||||||
|
wl_fixed_t surface_x,
|
||||||
|
wl_fixed_t surface_y
|
||||||
|
)
|
||||||
|
{
|
||||||
|
log::debug("Pointer motion: [{} - {}]", surface_x, surface_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_button_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t serial,
|
||||||
|
std::uint32_t time,
|
||||||
|
std::uint32_t button,
|
||||||
|
std::uint32_t state
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_axis_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t time,
|
||||||
|
std::uint32_t axis,
|
||||||
|
wl_fixed_t value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_axis_source_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t axis_source
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_axis_stop_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t time,
|
||||||
|
std::uint32_t axis_source
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_axis_discrete_listener(
|
||||||
|
void *data,
|
||||||
|
wl_pointer *pointer,
|
||||||
|
std::uint32_t axis,
|
||||||
|
std::int32_t discrete
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void System::wayland_pointer_frame_listener(void *data, wl_pointer *pointer)
|
||||||
|
{
|
||||||
|
log::debug("Pointer frame...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void seat_name_listener(void *data, wl_seat *seat, const char *name)
|
||||||
|
{
|
||||||
|
std::ignore = data;
|
||||||
|
|
||||||
|
log::info("Wayland seat:");
|
||||||
|
log::info("\tname: {}", name);
|
||||||
|
log::info("\taddr: 0x{:x}", std::bit_cast<std::size_t>(seat));
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::wayland_registry_listener(
|
||||||
void *data,
|
void *data,
|
||||||
wl_registry *registry,
|
wl_registry *registry,
|
||||||
std::uint32_t name,
|
std::uint32_t name,
|
||||||
|
|
@ -151,6 +369,8 @@ void System::handle_globals(
|
||||||
)
|
)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
std::ignore = version;
|
||||||
|
|
||||||
auto *system = std::bit_cast<System *>(data);
|
auto *system = std::bit_cast<System *>(data);
|
||||||
|
|
||||||
if (std::strcmp(interface, wl_compositor_interface.name) == 0)
|
if (std::strcmp(interface, wl_compositor_interface.name) == 0)
|
||||||
|
|
@ -166,9 +386,18 @@ void System::handle_globals(
|
||||||
system->m_shell = std::bit_cast<xdg_wm_base *>(
|
system->m_shell = std::bit_cast<xdg_wm_base *>(
|
||||||
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)
|
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)
|
||||||
);
|
);
|
||||||
xdg_wm_base_add_listener(system->m_shell, &shell_listener, {});
|
xdg_wm_base_add_listener(system->m_shell, &shell_listener, system);
|
||||||
log::info("Bound successfuly to the xdg_wm_base global");
|
log::info("Bound successfuly to the xdg_wm_base global");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::strcmp(interface, wl_seat_interface.name) == 0)
|
||||||
|
{
|
||||||
|
system->m_wl_seat = std::bit_cast<wl_seat *>(
|
||||||
|
wl_registry_bind(registry, name, &wl_seat_interface, 7u)
|
||||||
|
);
|
||||||
|
wl_seat_add_listener(system->m_wl_seat, &system->m_wl_seat_listener, system);
|
||||||
|
log::info("Bound successfuly to the wl_seat_interface global");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void registry_handle_global_remove(void *data, wl_registry *registry, std::uint32_t name)
|
void registry_handle_global_remove(void *data, wl_registry *registry, std::uint32_t name)
|
||||||
|
|
@ -180,11 +409,30 @@ void registry_handle_global_remove(void *data, wl_registry *registry, std::uint3
|
||||||
System::System(memory::Ref<ecs::Registry> registry)
|
System::System(memory::Ref<ecs::Registry> registry)
|
||||||
: m_wl_registry_listener(
|
: m_wl_registry_listener(
|
||||||
{
|
{
|
||||||
.global = handle_globals,
|
.global = wayland_registry_listener,
|
||||||
.global_remove = registry_handle_global_remove,
|
.global_remove = registry_handle_global_remove,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
, m_wl_seat_listener(
|
||||||
|
wl_seat_listener {
|
||||||
|
.capabilities = &wayland_seat_capabilities_listener,
|
||||||
|
.name = &seat_name_listener,
|
||||||
|
}
|
||||||
|
)
|
||||||
, m_registry(std::move(registry))
|
, m_registry(std::move(registry))
|
||||||
|
, m_wl_pointer_listener(
|
||||||
|
{
|
||||||
|
.enter = &wayland_pointer_enter_listener,
|
||||||
|
.leave = &wayland_pointer_leave_listener,
|
||||||
|
.motion = &wayland_pointer_motion_listener,
|
||||||
|
.button = &wayland_pointer_button_listener,
|
||||||
|
.axis = &wayland_pointer_axis_listener,
|
||||||
|
.frame = &wayland_pointer_frame_listener,
|
||||||
|
.axis_source = &wayland_pointer_axis_source_listener,
|
||||||
|
.axis_stop = &wayland_pointer_axis_stop_listener,
|
||||||
|
.axis_discrete = &wayland_pointer_axis_discrete_listener,
|
||||||
|
}
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
m_wl_display = wl_display_connect({});
|
m_wl_display = wl_display_connect({});
|
||||||
|
|
@ -198,30 +446,30 @@ System::System(memory::Ref<ecs::Registry> registry)
|
||||||
wl_registry_add_listener(m_wl_registry, &m_wl_registry_listener, this);
|
wl_registry_add_listener(m_wl_registry, &m_wl_registry_listener, this);
|
||||||
wl_display_roundtrip(m_wl_display);
|
wl_display_roundtrip(m_wl_display);
|
||||||
|
|
||||||
|
// Wayland seat gets named after the second roundtrip....
|
||||||
|
// For reasons beyond my fragile comprehension :(
|
||||||
|
wl_display_roundtrip(m_wl_display);
|
||||||
|
|
||||||
debug::ensure(m_wl_compositor, "Failed to bind to the Wayland's compositor global");
|
debug::ensure(m_wl_compositor, "Failed to bind to the Wayland's compositor global");
|
||||||
debug::ensure(m_shell, "Failed to bind to the Wayland's XDG-shell global");
|
debug::ensure(m_shell, "Failed to bind to the Wayland's XDG-shell global");
|
||||||
}
|
}
|
||||||
|
|
||||||
System::~System()
|
System::~System()
|
||||||
{
|
{
|
||||||
if (m_wl_display)
|
if (!m_wl_display)
|
||||||
{
|
{
|
||||||
log::debug("Closing Wayland display...");
|
return;
|
||||||
wl_display_disconnect(m_wl_display);
|
|
||||||
log::debug("Closed Wayland display");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log::debug("Wayland display nulled on move!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::on_register()
|
void System::on_register()
|
||||||
{
|
{
|
||||||
|
log::info("surface::System::on_register");
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::on_unregister()
|
void System::on_unregister()
|
||||||
{
|
{
|
||||||
|
log::info("surface::System::on_unregister");
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::CreateInfo info)
|
void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::CreateInfo info)
|
||||||
|
|
@ -259,11 +507,538 @@ void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::Cr
|
||||||
|
|
||||||
void System::tick(app::TickInfo tick)
|
void System::tick(app::TickInfo tick)
|
||||||
{
|
{
|
||||||
|
wl_display_roundtrip(m_wl_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
module;
|
||||||
|
#include <Windows.h>
|
||||||
|
module surface.system;
|
||||||
|
import surface.constants;
|
||||||
|
import debug.assertions;
|
||||||
|
import memory.reference;
|
||||||
|
import surface.requests;
|
||||||
|
import surface.events;
|
||||||
|
import logger;
|
||||||
|
import ecs.registry;
|
||||||
|
import ecs.entity;
|
||||||
|
import time;
|
||||||
|
import std;
|
||||||
|
|
||||||
|
namespace lt::surface {
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
struct overloads: Ts...
|
||||||
|
{
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ensure_component_sanity(const SurfaceComponent &component);
|
||||||
|
|
||||||
|
auto CALLBACK native_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT;
|
||||||
|
|
||||||
|
System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry))
|
||||||
|
{
|
||||||
|
debug::ensure(m_registry, "Failed to initialize surface system: null registry");
|
||||||
|
|
||||||
|
debug::ensure(
|
||||||
|
m_registry->view<SurfaceComponent>().get_size() == 0,
|
||||||
|
"Failed to initialize surface system: registry has surface component(s)"
|
||||||
|
);
|
||||||
|
|
||||||
|
m_registry->connect_on_destruct<SurfaceComponent>(
|
||||||
|
[this](ecs::Registry ®istry, ecs::EntityId entity) {
|
||||||
|
on_surface_destruct(registry, entity);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
auto window_class = WNDCLASS {
|
||||||
|
.lpfnWndProc = native_window_proc,
|
||||||
|
.hInstance = GetModuleHandle(nullptr),
|
||||||
|
.lpszClassName = constants::class_name,
|
||||||
|
};
|
||||||
|
RegisterClass(&window_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
System::~System()
|
||||||
|
{
|
||||||
|
if (!m_registry)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO(Light): make registry.remove not invalidate iterators
|
||||||
|
auto entities_to_remove = std::vector<ecs::EntityId> {};
|
||||||
|
for (auto &[entity, surface] : m_registry->view<SurfaceComponent>())
|
||||||
|
{
|
||||||
|
entities_to_remove.emplace_back(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto entity : entities_to_remove)
|
||||||
|
{
|
||||||
|
m_registry->remove<SurfaceComponent>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_registry->disconnect_on_construct<SurfaceComponent>();
|
||||||
|
m_registry->disconnect_on_destruct<SurfaceComponent>();
|
||||||
|
}
|
||||||
|
catch (const std::exception &exp)
|
||||||
|
{
|
||||||
|
log::error("Uncaught exception in surface::~System:");
|
||||||
|
log::error("\twhat: {}", exp.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::on_register()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::on_unregister()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::create_surface_component(ecs::EntityId entity, SurfaceComponent::CreateInfo info)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto &component = m_registry->add<SurfaceComponent>(entity, info);
|
||||||
|
auto &surface = m_registry->get<SurfaceComponent>(entity);
|
||||||
|
const auto &resolution = surface.get_resolution();
|
||||||
|
const auto &position = surface.get_position();
|
||||||
|
ensure_component_sanity(surface);
|
||||||
|
|
||||||
|
surface.m_native_data.window = CreateWindowEx(
|
||||||
|
0,
|
||||||
|
constants::class_name,
|
||||||
|
info.title.data(),
|
||||||
|
WS_OVERLAPPEDWINDOW,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
GetModuleHandle(nullptr),
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
debug::ensure(surface.m_native_data.window, "Failed to create Windows surface component");
|
||||||
|
|
||||||
|
ShowWindow(surface.m_native_data.window, SW_NORMAL);
|
||||||
|
|
||||||
|
// TODO(Light): refactor "environment" into standalone module
|
||||||
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||||
|
// auto *display_env = std::getenv("DISPLAY");
|
||||||
|
// debug::ensure(display_env != nullptr, "DISPLAY env var not found!");
|
||||||
|
//
|
||||||
|
// auto *display = XOpenDisplay(display_env);
|
||||||
|
// debug::ensure(display, "Failed to open XDisplay with DISPLAY: {}", display_env);
|
||||||
|
//
|
||||||
|
// auto root_window = XDefaultRootWindow(display);
|
||||||
|
//
|
||||||
|
// auto border_width = 0;
|
||||||
|
// auto depth = std::int32_t { CopyFromParent };
|
||||||
|
// auto window_class = CopyFromParent;
|
||||||
|
// auto *visual = (Visual *)CopyFromParent;
|
||||||
|
//
|
||||||
|
// auto attribute_value_mask = CWBackPixel | CWEventMask;
|
||||||
|
// auto attributes = XSetWindowAttributes {
|
||||||
|
// .background_pixel = 0xffafe9af,
|
||||||
|
// .event_mask = all_events_mask,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// typedef struct Hints
|
||||||
|
// {
|
||||||
|
// unsigned long flags;
|
||||||
|
// unsigned long functions;
|
||||||
|
// unsigned long decorations;
|
||||||
|
// long inputMode;
|
||||||
|
// unsigned long status;
|
||||||
|
// } Hints;
|
||||||
|
//
|
||||||
|
// auto main_window = XCreateWindow(
|
||||||
|
// display,
|
||||||
|
// root_window,
|
||||||
|
// position.x,
|
||||||
|
// position.y,
|
||||||
|
// resolution.x,
|
||||||
|
// resolution.y,
|
||||||
|
// border_width,
|
||||||
|
// depth,
|
||||||
|
// window_class,
|
||||||
|
// visual,
|
||||||
|
// attribute_value_mask,
|
||||||
|
// &attributes
|
||||||
|
// );
|
||||||
|
// surface.m_native_data.display = display;
|
||||||
|
// surface.m_native_data.window = main_window;
|
||||||
|
//
|
||||||
|
// surface.m_native_data.wm_delete_message = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||||
|
// XSetWMProtocols(display, main_window, &surface.m_native_data.wm_delete_message, 1);
|
||||||
|
//
|
||||||
|
// // code to remove decoration
|
||||||
|
// auto hints = std::array<const unsigned char, 5> { 2, 0, 0, 0, 0 };
|
||||||
|
// const auto motif_hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);
|
||||||
|
//
|
||||||
|
// XChangeProperty(
|
||||||
|
// display,
|
||||||
|
// surface.m_native_data.window,
|
||||||
|
// motif_hints,
|
||||||
|
// motif_hints,
|
||||||
|
// 32,
|
||||||
|
// PropModeReplace,
|
||||||
|
// hints.data(),
|
||||||
|
// 5
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// XMapWindow(display, main_window);
|
||||||
|
// XStoreName(display, main_window, surface.m_title.c_str());
|
||||||
|
// XFlush(display);
|
||||||
|
//
|
||||||
|
// if (!surface.is_visible())
|
||||||
|
// {
|
||||||
|
// XUnmapWindow(display, main_window);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
catch (const std::exception &exp)
|
||||||
|
{
|
||||||
|
log::error("Exception thrown when on_constructing surface component");
|
||||||
|
log::error("\tentity: {}", std::uint32_t { entity });
|
||||||
|
log::error("\twhat: {}", exp.what());
|
||||||
|
m_registry->remove<SurfaceComponent>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::on_surface_destruct(ecs::Registry ®istry, ecs::EntityId entity)
|
||||||
|
{
|
||||||
|
auto *window = registry.get<SurfaceComponent>(entity).get_native_data().window;
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
log::warn("Surface component destroyed with null window handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::handle_events(SurfaceComponent &surface)
|
||||||
|
{
|
||||||
|
auto &queue = surface.m_event_queue;
|
||||||
|
queue.clear();
|
||||||
|
|
||||||
|
auto message = MSG {};
|
||||||
|
while (PeekMessage(&message, 0, {}, {}, PM_REMOVE))
|
||||||
|
{
|
||||||
|
switch (message.message) {}
|
||||||
|
|
||||||
|
log::debug("Window message type: {}", std::uint32_t { message.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
// auto event = XEvent {};
|
||||||
|
// auto &[display, window, wm_delete_message] = surface.m_native_data;
|
||||||
|
//
|
||||||
|
// XFlush(display);
|
||||||
|
// while (XEventsQueued(display, QueuedAlready) != 0)
|
||||||
|
// {
|
||||||
|
// XNextEvent(surface.m_native_data.display, &event);
|
||||||
|
//
|
||||||
|
// switch (event.type)
|
||||||
|
// {
|
||||||
|
// case KeyPress:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<KeyPressedEvent>(
|
||||||
|
// static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
|
||||||
|
// );
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case KeyRelease:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<KeyReleasedEvent>(
|
||||||
|
// static_cast<std::uint32_t>(XLookupKeysym(&event.xkey, 0))
|
||||||
|
// );
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case ButtonPress:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<ButtonPressedEvent>(static_cast<int>(event.xbutton.button));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case ButtonRelease:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<ButtonReleasedEvent>(static_cast<int>(event.xbutton.button));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case FocusIn:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<GainFocusEvent>({});
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case FocusOut:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<LostFocusEvent>({});
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case ClientMessage:
|
||||||
|
// {
|
||||||
|
// if (event.xclient.data.l[0] == wm_delete_message)
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<ClosedEvent>({});
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case MotionNotify:
|
||||||
|
// {
|
||||||
|
// queue.emplace_back<MouseMovedEvent>(MouseMovedEvent {
|
||||||
|
// static_cast<float>(event.xmotion.x),
|
||||||
|
// static_cast<float>(event.xmotion.y),
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case ConfigureNotify:
|
||||||
|
// {
|
||||||
|
// const auto [prev_width, prev_height] = surface.get_resolution();
|
||||||
|
// const auto new_width = event.xconfigure.width;
|
||||||
|
// const auto new_height = event.xconfigure.height;
|
||||||
|
// if (prev_width != new_width || prev_height != new_height)
|
||||||
|
// {
|
||||||
|
// surface.m_resolution.x = new_width;
|
||||||
|
// surface.m_resolution.y = new_height;
|
||||||
|
// queue.emplace_back<ResizedEvent>(ResizedEvent {
|
||||||
|
// static_cast<std::uint32_t>(new_width),
|
||||||
|
// static_cast<std::uint32_t>(new_height),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const auto [prev_x, prev_y] = surface.get_position();
|
||||||
|
// const auto new_x = event.xconfigure.x;
|
||||||
|
// const auto new_y = event.xconfigure.y;
|
||||||
|
// if (prev_x != new_x || prev_y != new_y)
|
||||||
|
// {
|
||||||
|
// surface.m_position.x = new_x;
|
||||||
|
// surface.m_position.y = new_y;
|
||||||
|
// queue.emplace_back<MovedEvent>(MovedEvent {
|
||||||
|
// new_x,
|
||||||
|
// new_y,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// default: break; /* pass */
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::handle_requests(SurfaceComponent &surface)
|
||||||
|
{
|
||||||
|
const auto visitor = overloads {
|
||||||
|
[&](const ModifyTitleRequest &request) { modify_title(surface, request); },
|
||||||
|
[&](const ModifyResolutionRequest &request) { modify_resolution(surface, request); },
|
||||||
|
[&](const ModifyPositionRequest &request) { modify_position(surface, request); },
|
||||||
|
[&](const ModifyVisibilityRequest &request) { modify_visiblity(surface, request); },
|
||||||
|
[&](const auto &) { log::error("Unknown surface request"); },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &request : surface.peek_requests())
|
||||||
|
{
|
||||||
|
std::visit(visitor, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
surface.m_requests.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request)
|
||||||
|
{
|
||||||
|
surface.m_title = request.title;
|
||||||
|
|
||||||
|
// const auto &[display, window, _] = surface.get_native_data();
|
||||||
|
// XStoreName(display, window, request.title.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request)
|
||||||
|
{
|
||||||
|
// surface.m_resolution = request.resolution;
|
||||||
|
|
||||||
|
// auto &[display, window, _] = surface.m_native_data;
|
||||||
|
// const auto &[width, height] = request.resolution;
|
||||||
|
// // XResizeWindow(display, window, width, height);
|
||||||
|
//
|
||||||
|
// // get baseline serial number for X requests generated from XResizeWindow
|
||||||
|
// auto serial = NextRequest(display);
|
||||||
|
//
|
||||||
|
// // request a new window size from the X server
|
||||||
|
// XResizeWindow(
|
||||||
|
// display,
|
||||||
|
// window,
|
||||||
|
// static_cast<std::uint32_t>(width),
|
||||||
|
// static_cast<std::uint32_t>(height)
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// // flush output queue and wait for X server to processes the request
|
||||||
|
// XSync(display, False);
|
||||||
|
// // The documentation for XResizeWindow includes this important note:
|
||||||
|
// //
|
||||||
|
// // If the override-redirect flag of the window is False and some
|
||||||
|
// // other client has selected SubstructureRedirectMask on the parent,
|
||||||
|
// // the X server generates a ConfigureRequest event, and no further
|
||||||
|
// // processing is performed.
|
||||||
|
// //
|
||||||
|
// // What this means, essentially, is that if this window is a top-level
|
||||||
|
// // window, then it's the window manager (the "other client") that is
|
||||||
|
// // responsible for changing this window's size. So when we call
|
||||||
|
// // XResizeWindow() on a top-level window, then instead of resizing
|
||||||
|
// // the window immediately, the X server informs the window manager,
|
||||||
|
// // and then the window manager sets our new size (usually it will be
|
||||||
|
// // the size we asked for). We receive a ConfigureNotify event when
|
||||||
|
// // our new size has been set.
|
||||||
|
// constexpr auto lifespan = std::chrono::milliseconds { 10 };
|
||||||
|
// auto timer = time::Timer {};
|
||||||
|
// auto event = XEvent {};
|
||||||
|
// while (!XCheckIfEvent(
|
||||||
|
// display,
|
||||||
|
// &event,
|
||||||
|
// XEventTypeEquals<ConfigureNotify>,
|
||||||
|
// reinterpret_cast<XPointer>(&window) // NOLINT
|
||||||
|
// )
|
||||||
|
// || event.xconfigure.serial < serial)
|
||||||
|
// {
|
||||||
|
// std::this_thread::sleep_for(std::chrono::microseconds { 100 });
|
||||||
|
// if (timer.elapsed_time() > lifespan)
|
||||||
|
// {
|
||||||
|
// log::error("Timed out waiting for XResizeWindow's event");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // We don't need to update the component's state and handle the event in this funcion.
|
||||||
|
// // Since handle_requests is called before handle_events.
|
||||||
|
// // So we just put the event back into the queue and move on.
|
||||||
|
// XPutBackEvent(display, &event);
|
||||||
|
// XSync(display, False);
|
||||||
|
// XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
||||||
|
{
|
||||||
|
// surface.m_position = request.position;
|
||||||
|
|
||||||
|
// auto &[display, window, _] = surface.m_native_data;
|
||||||
|
// const auto &[x, y] = request.position;
|
||||||
|
//
|
||||||
|
// // get baseline serial number for X requests generated from XResizeWindow
|
||||||
|
// auto serial = NextRequest(display);
|
||||||
|
// XMoveWindow(display, window, static_cast<int>(x), static_cast<int>(y));
|
||||||
|
//
|
||||||
|
// // flush output queue and wait for X server to processes the request
|
||||||
|
// XSync(display, False);
|
||||||
|
// constexpr auto lifespan = std::chrono::milliseconds { 10 };
|
||||||
|
// auto timer = time::Timer {};
|
||||||
|
// auto event = XEvent {};
|
||||||
|
// while (!XCheckIfEvent(
|
||||||
|
// display,
|
||||||
|
// &event,
|
||||||
|
// XEventTypeEquals<ConfigureNotify>,
|
||||||
|
// reinterpret_cast<XPointer>(&window) // NOLINT
|
||||||
|
// )
|
||||||
|
// || event.xconfigure.serial < serial)
|
||||||
|
// {
|
||||||
|
// std::this_thread::sleep_for(std::chrono::microseconds { 100 });
|
||||||
|
// if (timer.elapsed_time() > lifespan)
|
||||||
|
// {
|
||||||
|
// log::error("Timed out waiting for XMoveWindow's event");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // We don't need to update the component's state and handle the event in this funcion.
|
||||||
|
// // Since handle_requests is called before handle_events.
|
||||||
|
// // So we just put the event back into the queue and move on.
|
||||||
|
// XPutBackEvent(display, &event);
|
||||||
|
// XSync(display, False);
|
||||||
|
// XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request)
|
||||||
|
{
|
||||||
|
// const auto &[display, window, _] = surface.get_native_data();
|
||||||
|
// surface.m_visible = request.visible;
|
||||||
|
|
||||||
|
// if (request.visible)
|
||||||
|
// {
|
||||||
|
// XMapWindow(display, window);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// XUnmapWindow(display, window);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::tick(app::TickInfo tick)
|
||||||
|
{
|
||||||
|
for (auto &[id, surface] : m_registry->view<SurfaceComponent>())
|
||||||
|
{
|
||||||
|
handle_requests(surface);
|
||||||
|
handle_events(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
m_last_tick_result = app::TickResult {
|
||||||
|
.info = tick,
|
||||||
|
.duration = now - tick.start_time,
|
||||||
|
.end_time = now,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensure_component_sanity(const SurfaceComponent &component)
|
||||||
|
{
|
||||||
|
auto [width, height] = component.get_resolution();
|
||||||
|
|
||||||
|
debug::ensure(width != 0u, "Received bad values for surface component: width({}) == 0", width);
|
||||||
|
|
||||||
|
debug::ensure(
|
||||||
|
height != 0u,
|
||||||
|
"Received bad values for surface component: height({}) == 0",
|
||||||
|
height
|
||||||
|
);
|
||||||
|
|
||||||
|
debug::ensure(
|
||||||
|
width < SurfaceComponent::max_dimension,
|
||||||
|
"Received bad values for surface component: width({}) > max_dimension({})",
|
||||||
|
width,
|
||||||
|
SurfaceComponent::max_dimension
|
||||||
|
);
|
||||||
|
|
||||||
|
debug::ensure(
|
||||||
|
height < SurfaceComponent::max_dimension,
|
||||||
|
"Received bad values for surface component: height({}) > max_dimension({})",
|
||||||
|
height,
|
||||||
|
SurfaceComponent::max_dimension
|
||||||
|
);
|
||||||
|
|
||||||
|
debug::ensure(
|
||||||
|
component.get_title().size() < SurfaceComponent::max_title_length,
|
||||||
|
"Received bad values for surface component: title.size({}) > max_title_length({})",
|
||||||
|
component.get_title().size(),
|
||||||
|
SurfaceComponent::max_title_length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CALLBACK native_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT
|
||||||
|
{
|
||||||
|
switch (uMsg)
|
||||||
|
{
|
||||||
|
case WM_DESTROY:
|
||||||
|
{
|
||||||
|
PostQuitMessage(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lt::surface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace lt::surface
|
} // namespace lt::surface
|
||||||
|
|
|
||||||
|
|
@ -1,104 +1,109 @@
|
||||||
// Suite raii = "raii"_suite = [] {
|
import test.test;
|
||||||
// Case { "happy path won't throw" } = [] {
|
import time;
|
||||||
// auto fixture = Fixture {};
|
import test.expects;
|
||||||
// ignore = System { fixture.registry() };
|
import surface.system;
|
||||||
// };
|
import surface.events;
|
||||||
//
|
import surface.requests;
|
||||||
// import test.test;
|
import ecs.registry;
|
||||||
// import test.expects;
|
import memory.scope;
|
||||||
// import surface.system;
|
import memory.reference;
|
||||||
// import surface.events;
|
import logger;
|
||||||
// import surface.requests;
|
import math.vec2;
|
||||||
// import ecs.registry;
|
import app.system;
|
||||||
// import memory.scope;
|
import std;
|
||||||
// import memory.reference;
|
|
||||||
// import logger;
|
using ::lt::surface::SurfaceComponent;
|
||||||
// import math.vec2;
|
using ::lt::surface::System;
|
||||||
// import app.system;
|
using ::lt::test::Case;
|
||||||
// import std;
|
using ::lt::test::expect_eq;
|
||||||
//
|
using ::lt::test::expect_ne;
|
||||||
// using ::lt::surface::SurfaceComponent;
|
using ::lt::test::expect_not_nullptr;
|
||||||
// using ::lt::surface::System;
|
using ::lt::test::expect_throw;
|
||||||
// using ::lt::test::Case;
|
using ::lt::test::Suite;
|
||||||
// using ::lt::test::expect_eq;
|
using ::std::ignore;
|
||||||
// using ::lt::test::expect_ne;
|
using ::lt::test::operator""_suite;
|
||||||
// using ::lt::test::expect_not_nullptr;
|
|
||||||
// using ::lt::test::expect_throw;
|
[[nodiscard]] auto tick_info() -> lt::app::TickInfo
|
||||||
// using ::lt::test::Suite;
|
{
|
||||||
// using ::std::ignore;
|
return {
|
||||||
// using ::lt::test::operator""_suite;
|
.delta_time = std::chrono::milliseconds { 16 },
|
||||||
//
|
.budget = std::chrono::milliseconds { 10 },
|
||||||
// [[nodiscard]] auto tick_info() -> lt::app::TickInfo
|
.start_time = std::chrono::steady_clock::now(),
|
||||||
// {
|
};
|
||||||
// return {
|
}
|
||||||
// .delta_time = std::chrono::milliseconds { 16 },
|
|
||||||
// .budget = std::chrono::milliseconds { 10 },
|
constexpr auto title = "TestWindow";
|
||||||
// .start_time = std::chrono::steady_clock::now(),
|
constexpr auto width = 800u;
|
||||||
// };
|
constexpr auto height = 600u;
|
||||||
// }
|
constexpr auto vsync = true;
|
||||||
//
|
constexpr auto visible = false;
|
||||||
// constexpr auto title = "TestWindow";
|
|
||||||
// constexpr auto width = 800u;
|
template<class... Ts>
|
||||||
// constexpr auto height = 600u;
|
struct overloads: Ts...
|
||||||
// constexpr auto vsync = true;
|
{
|
||||||
// constexpr auto visible = false;
|
using Ts::operator()...;
|
||||||
//
|
};
|
||||||
// template<class... Ts>
|
|
||||||
// struct overloads: Ts...
|
class Fixture
|
||||||
// {
|
{
|
||||||
// using Ts::operator()...;
|
public:
|
||||||
// };
|
[[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry>
|
||||||
//
|
{
|
||||||
// class Fixture
|
return m_registry;
|
||||||
// {
|
}
|
||||||
// public:
|
|
||||||
// [[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry>
|
auto create_component(
|
||||||
// {
|
SurfaceComponent::CreateInfo info = SurfaceComponent::CreateInfo {
|
||||||
// return m_registry;
|
.title = title,
|
||||||
// }
|
.resolution = { width, height },
|
||||||
//
|
.vsync = vsync,
|
||||||
// auto create_component(
|
.visible = visible,
|
||||||
// SurfaceComponent::CreateInfo info = SurfaceComponent::CreateInfo {
|
}
|
||||||
// .title = title,
|
) -> std::optional<SurfaceComponent *>
|
||||||
// .resolution = { width, height },
|
{
|
||||||
// .vsync = vsync,
|
auto entity = m_registry->create_entity();
|
||||||
// .visible = visible,
|
m_system.create_surface_component(entity, info);
|
||||||
// }
|
|
||||||
// ) -> std::optional<SurfaceComponent *>
|
return &m_registry->get<SurfaceComponent>(entity);
|
||||||
// {
|
}
|
||||||
// auto entity = m_registry->create_entity();
|
|
||||||
// m_system.create_surface_component(entity, info);
|
void check_values(SurfaceComponent *component)
|
||||||
//
|
{
|
||||||
// return &m_registry->get<SurfaceComponent>(entity);
|
#ifdef LIGHT_PLATFORM_LINUX
|
||||||
// }
|
expect_not_nullptr(component->get_native_data().display);
|
||||||
//
|
expect_not_nullptr(component->get_native_data().surface);
|
||||||
// void check_values(SurfaceComponent *component)
|
#endif
|
||||||
// {
|
|
||||||
// #ifdef LIGHT_PLATFORM_LINUX
|
expect_eq(component->get_resolution().x, width);
|
||||||
// expect_not_nullptr(component->get_native_data().display);
|
expect_eq(component->get_resolution().y, height);
|
||||||
// expect_ne(component->get_native_data().window, 0);
|
expect_eq(component->get_title(), title);
|
||||||
// #endif
|
expect_eq(component->is_vsync(), vsync);
|
||||||
//
|
expect_eq(component->is_visible(), visible);
|
||||||
// expect_eq(component->get_resolution().x, width);
|
}
|
||||||
// expect_eq(component->get_resolution().y, height);
|
|
||||||
// expect_eq(component->get_title(), title);
|
private:
|
||||||
// expect_eq(component->is_vsync(), vsync);
|
lt::memory::Ref<lt::ecs::Registry> m_registry = lt::memory::create_ref<lt::ecs::Registry>();
|
||||||
// expect_eq(component->is_visible(), visible);
|
|
||||||
// }
|
System m_system { m_registry };
|
||||||
//
|
};
|
||||||
// private:
|
|
||||||
// lt::memory::Ref<lt::ecs::Registry> m_registry = lt::memory::create_ref<lt::ecs::Registry>();
|
|
||||||
//
|
Suite raii = "raii"_suite = [] {
|
||||||
// System m_system { m_registry };
|
Case { "happy path won't throw" } = [] {
|
||||||
// };
|
auto fixture = Fixture {};
|
||||||
//
|
auto system = System { fixture.registry() };
|
||||||
//
|
|
||||||
// Suite raii = "raii"_suite = [] {
|
auto timer = lt::time::Timer {};
|
||||||
// Case { "happy path won't throw" } = [] {
|
lt::log::trace("Ticking for 3 seconds...");
|
||||||
// auto fixture = Fixture {};
|
while (timer.elapsed_time() < std::chrono::seconds { 3 })
|
||||||
// ignore = System { fixture.registry() };
|
{
|
||||||
// };
|
system.tick({});
|
||||||
//
|
}
|
||||||
|
|
||||||
|
lt::log::trace("Three seconds passed, quitting...");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Case { "many won't freeze/throw" } = [] {
|
// Case { "many won't freeze/throw" } = [] {
|
||||||
// auto fixture = Fixture {};
|
// auto fixture = Fixture {};
|
||||||
// for (auto idx : std::views::iota(0, 250))
|
// for (auto idx : std::views::iota(0, 250))
|
||||||
|
|
@ -127,8 +132,8 @@
|
||||||
// system.reset();
|
// system.reset();
|
||||||
// expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
|
// expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
|
||||||
// };
|
// };
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// Suite system_events = "system_events"_suite = [] {
|
// Suite system_events = "system_events"_suite = [] {
|
||||||
// Case { "on_register won't throw" } = [] {
|
// Case { "on_register won't throw" } = [] {
|
||||||
// auto fixture = Fixture {};
|
// auto fixture = Fixture {};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue