feat: windows support for surface module
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
9b0acc1cc3
commit
6f007c47ed
2 changed files with 326 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
|||
if (NOT WIN32)
|
||||
add_library_module(surface linux/system.cpp)
|
||||
else()
|
||||
else(WIN32)
|
||||
add_library_module(surface windows/system.cpp)
|
||||
endif()
|
||||
|
||||
target_link_libraries(surface PUBLIC
|
||||
|
|
324
modules/surface/private/windows/system.cpp
Normal file
324
modules/surface/private/windows/system.cpp
Normal file
|
@ -0,0 +1,324 @@
|
|||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GLFW/glfw3native.h>
|
||||
#include <surface/system.hpp>
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
// This class is to ensure glfwInit/glfwTerminate is called only once and exactly when needed during
|
||||
// entire application runtime
|
||||
class GlfwSingleton
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static auto get() -> GlfwSingleton &
|
||||
{
|
||||
static auto instance = GlfwSingleton {};
|
||||
return instance;
|
||||
}
|
||||
|
||||
GlfwSingleton(GlfwSingleton &&) = delete;
|
||||
|
||||
GlfwSingleton(const GlfwSingleton &) = delete;
|
||||
|
||||
auto operator=(GlfwSingleton &&) -> GlfwSingleton & = delete;
|
||||
|
||||
auto operator=(const GlfwSingleton &) -> GlfwSingleton & = delete;
|
||||
|
||||
private:
|
||||
GlfwSingleton()
|
||||
{
|
||||
log_inf("Initializing glfw...");
|
||||
ensure(glfwInit(), "Failed to initialize 'glfw'");
|
||||
log_inf("...Finished");
|
||||
}
|
||||
|
||||
~GlfwSingleton()
|
||||
{
|
||||
log_inf("Terminating glfw...");
|
||||
glfwTerminate();
|
||||
log_inf("...Finished");
|
||||
}
|
||||
};
|
||||
|
||||
void glfw_error_callbac(int32_t code, const char *description)
|
||||
{
|
||||
log_err("GLFW ERROR: {} -> {}", code, description);
|
||||
}
|
||||
|
||||
void handle_event(GLFWwindow *window, const SurfaceComponent::Event &event)
|
||||
{
|
||||
auto &callbacks = *static_cast<std::vector<SurfaceComponent::EventCallback> *>(
|
||||
glfwGetWindowUserPointer(window)
|
||||
);
|
||||
|
||||
for (auto &callback : callbacks)
|
||||
{
|
||||
if (callback(event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bind_glfw_events(GLFWwindow *handle)
|
||||
{
|
||||
glfwSetWindowPosCallback(handle, [](GLFWwindow *window, int xpos, int ypos) {
|
||||
handle_event(window, MovedEvent { xpos, ypos });
|
||||
});
|
||||
|
||||
glfwSetWindowSizeCallback(handle, [](GLFWwindow *window, int width, int height) {
|
||||
handle_event(
|
||||
window,
|
||||
ResizedEvent { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }
|
||||
);
|
||||
});
|
||||
|
||||
glfwSetWindowCloseCallback(handle, [](GLFWwindow *window) {
|
||||
handle_event(window, ClosedEvent {});
|
||||
});
|
||||
|
||||
glfwSetWindowFocusCallback(handle, [](GLFWwindow *window, int focus) {
|
||||
if (focus == GLFW_TRUE)
|
||||
{
|
||||
handle_event(window, GainFocusEvent {});
|
||||
}
|
||||
else
|
||||
{
|
||||
handle_event(window, LostFocusEvent {});
|
||||
}
|
||||
});
|
||||
|
||||
glfwSetCursorPosCallback(handle, [](GLFWwindow *window, double xpos, double ypos) {
|
||||
handle_event(
|
||||
window,
|
||||
MouseMovedEvent { static_cast<float>(xpos), static_cast<float>(ypos) }
|
||||
);
|
||||
});
|
||||
|
||||
glfwSetMouseButtonCallback(
|
||||
handle,
|
||||
[](GLFWwindow *window, int button, int action, int /*mods*/) {
|
||||
if (action == GLFW_PRESS)
|
||||
{
|
||||
handle_event(window, ButtonPressedEvent { button });
|
||||
}
|
||||
else if (action == GLFW_RELEASE)
|
||||
{
|
||||
handle_event(window, ButtonReleasedEvent { button });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
glfwSetScrollCallback(handle, [](GLFWwindow *window, double /*xoffset*/, double yoffset) {
|
||||
handle_event(window, WheelScrolledEvent { static_cast<float>(yoffset) });
|
||||
});
|
||||
|
||||
glfwSetKeyCallback(
|
||||
handle,
|
||||
[](GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/) {
|
||||
if (action == GLFW_PRESS)
|
||||
{
|
||||
handle_event(window, KeyPressedEvent { key });
|
||||
}
|
||||
else if (action == GLFW_RELEASE)
|
||||
{
|
||||
handle_event(window, KeyReleasedEvent { key });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
glfwSetCharCallback(handle, [](GLFWwindow *window, unsigned int character) {
|
||||
handle_event(window, KeySetCharEvent { character });
|
||||
});
|
||||
}
|
||||
|
||||
System::System(Ref<ecs::Registry> registry): m_registry(std::move(registry))
|
||||
{
|
||||
glfwSetErrorCallback(&glfw_error_callbac);
|
||||
|
||||
// will call `glfwInit()` only the first time
|
||||
auto &glfw_instance = GlfwSingleton::get();
|
||||
ensure(m_registry, "Failed to initialize surface system: null registry");
|
||||
|
||||
ensure(
|
||||
m_registry->view<SurfaceComponent>().size() == 0,
|
||||
"Failed to initialize surface system: registry has surface component(s)"
|
||||
);
|
||||
|
||||
m_registry->get_entt_registry()
|
||||
.on_construct<SurfaceComponent>()
|
||||
.connect<&System::on_surface_construct>(this);
|
||||
|
||||
m_registry->get_entt_registry()
|
||||
.on_update<SurfaceComponent>()
|
||||
.connect<&System::on_surface_update>(this);
|
||||
|
||||
m_registry->get_entt_registry()
|
||||
.on_destroy<SurfaceComponent>()
|
||||
.connect<&System::on_surface_destroy>(this);
|
||||
}
|
||||
|
||||
System::~System()
|
||||
{
|
||||
m_registry->get_entt_registry()
|
||||
.on_construct<SurfaceComponent>()
|
||||
.disconnect<&System::on_surface_construct>(this);
|
||||
|
||||
m_registry->get_entt_registry()
|
||||
.on_update<SurfaceComponent>()
|
||||
.connect<&System::on_surface_update>(this);
|
||||
|
||||
m_registry->get_entt_registry()
|
||||
.on_destroy<SurfaceComponent>()
|
||||
.disconnect<&System::on_surface_destroy>(this);
|
||||
|
||||
|
||||
m_registry->view<SurfaceComponent>().each([&](const entt::entity entity, SurfaceComponent &) {
|
||||
m_registry->get_entt_registry().remove<SurfaceComponent>(entity);
|
||||
});
|
||||
}
|
||||
|
||||
void System::on_surface_construct(entt::registry ®istry, entt::entity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto &surface = registry.get<SurfaceComponent>(entity);
|
||||
ensure_component_sanity(surface);
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
surface.m_glfw_handle = glfwCreateWindow(
|
||||
static_cast<int>(surface.get_resolution().x),
|
||||
static_cast<int>(surface.get_resolution().y),
|
||||
surface.get_title().begin(),
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
ensure(surface.m_glfw_handle, "Failed to create 'GLFWwindow'");
|
||||
|
||||
glfwSetWindowUserPointer(surface.m_glfw_handle, &surface.m_event_callbacks);
|
||||
surface.m_native_handle = glfwGetX11Window(surface.m_glfw_handle);
|
||||
bind_glfw_events(surface.m_glfw_handle);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
registry.remove<SurfaceComponent>(entity);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void System::on_surface_update(entt::registry ®istry, entt::entity entity)
|
||||
{
|
||||
auto &surface = registry.get<SurfaceComponent>(entity);
|
||||
glfwSetWindowUserPointer(surface.m_glfw_handle, &surface.m_event_callbacks);
|
||||
}
|
||||
|
||||
void System::on_surface_destroy(entt::registry ®istry, entt::entity entity)
|
||||
{
|
||||
auto &surface = registry.get<SurfaceComponent>(entity);
|
||||
|
||||
if (surface.m_glfw_handle)
|
||||
{
|
||||
glfwDestroyWindow(surface.m_glfw_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void System::set_title(ecs::Entity entity, std::string_view new_title)
|
||||
{
|
||||
auto &surface = entity.get_component<SurfaceComponent>();
|
||||
|
||||
surface.m_title = new_title;
|
||||
glfwSetWindowTitle(surface.m_glfw_handle, surface.m_title.c_str());
|
||||
}
|
||||
|
||||
auto System::tick() -> bool
|
||||
{
|
||||
m_registry->view<SurfaceComponent>().each([](SurfaceComponent &surface) {
|
||||
glfwSwapBuffers(surface.m_glfw_handle);
|
||||
});
|
||||
|
||||
glfwPollEvents();
|
||||
return false;
|
||||
}
|
||||
|
||||
void System::set_size(ecs::Entity surface_entity, const math::uvec2 &new_size)
|
||||
{
|
||||
auto &surface = surface_entity.get_component<SurfaceComponent>();
|
||||
surface.m_resolution = new_size;
|
||||
|
||||
glfwSetWindowSize(
|
||||
surface.m_glfw_handle,
|
||||
static_cast<int>(new_size.x),
|
||||
static_cast<int>(new_size.y)
|
||||
);
|
||||
}
|
||||
|
||||
void System::set_v_sync(ecs::Entity surface_entity, bool vsync)
|
||||
{
|
||||
auto &surface = surface_entity.get_component<SurfaceComponent>();
|
||||
surface.m_vsync = vsync;
|
||||
|
||||
glfwSwapInterval(vsync);
|
||||
}
|
||||
|
||||
void System::set_visibility(ecs::Entity surface_entity, bool visible)
|
||||
{
|
||||
auto &surface = surface_entity.get_component<SurfaceComponent>();
|
||||
surface.m_visible = visible;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
glfwShowWindow(surface.m_glfw_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
glfwHideWindow(surface.m_glfw_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void System::add_event_listener(
|
||||
ecs::Entity surface_entity,
|
||||
SurfaceComponent::EventCallback callback
|
||||
)
|
||||
{
|
||||
auto &surface = surface_entity.get_component<SurfaceComponent>();
|
||||
surface.m_event_callbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
|
||||
void System::ensure_component_sanity(const SurfaceComponent &component)
|
||||
{
|
||||
auto [width, height] = component.get_resolution();
|
||||
|
||||
ensure(width != 0u, "Received bad values for surface component: width({}) == 0", width);
|
||||
|
||||
ensure(height != 0u, "Received bad values for surface component: height({}) == 0", height);
|
||||
|
||||
ensure(
|
||||
width < SurfaceComponent::max_dimension,
|
||||
"Received bad values for surface component: width({}) > max_dimension({})",
|
||||
width,
|
||||
SurfaceComponent::max_dimension
|
||||
);
|
||||
|
||||
ensure(
|
||||
height < SurfaceComponent::max_dimension,
|
||||
"Received bad values for surface component: height({}) > max_dimension({})",
|
||||
height,
|
||||
SurfaceComponent::max_dimension
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace lt::surface
|
||||
|
||||
namespace lt {
|
||||
|
||||
} // namespace lt
|
Loading…
Add table
Reference in a new issue