feat(surface/linux): replace glfw with xlib
This commit is contained in:
parent
320cc66267
commit
21e9933a42
9 changed files with 554 additions and 452 deletions
|
@ -1,14 +1,16 @@
|
|||
if (NOT WIN32)
|
||||
add_library_module(surface linux/system.cpp)
|
||||
target_link_libraries(surface PRIVATE X11)
|
||||
|
||||
else(WIN32)
|
||||
add_library_module(surface windows/system.cpp)
|
||||
|
||||
endif()
|
||||
|
||||
target_link_libraries(surface PUBLIC
|
||||
ecs
|
||||
app
|
||||
PRIVATE
|
||||
glfw
|
||||
logger
|
||||
lt_debug
|
||||
)
|
||||
|
|
|
@ -1,143 +1,42 @@
|
|||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GLFW/glfw3native.h>
|
||||
#include <ecs/components.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/events/mouse.hpp>
|
||||
#include <surface/requests/surface.hpp>
|
||||
#include <surface/system.hpp>
|
||||
|
||||
//
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/keysymdef.h>
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
// This class is to ensure glfwInit/glfwTerminate is called only once and exactly when needed during
|
||||
// entire application runtime
|
||||
class GlfwSingleton
|
||||
template<class... Ts>
|
||||
struct overloads: Ts...
|
||||
{
|
||||
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");
|
||||
}
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
void glfw_error_callbac(int32_t code, const char *description)
|
||||
{
|
||||
log_err("GLFW ERROR: {} -> {}", code, description);
|
||||
}
|
||||
void ensure_component_sanity(const SurfaceComponent &component);
|
||||
|
||||
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 });
|
||||
});
|
||||
}
|
||||
constexpr auto all_events_mask = KeyPressMask | //
|
||||
KeyReleaseMask | //
|
||||
ButtonPressMask | //
|
||||
ButtonReleaseMask | //
|
||||
EnterWindowMask | //
|
||||
LeaveWindowMask | //
|
||||
PointerMotionMask | //
|
||||
KeymapStateMask | //
|
||||
ExposureMask | //
|
||||
VisibilityChangeMask | //
|
||||
StructureNotifyMask | //
|
||||
FocusChangeMask | //
|
||||
ColormapChangeMask | //
|
||||
OwnerGrabButtonMask;
|
||||
|
||||
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(
|
||||
|
@ -178,29 +77,94 @@ System::~System()
|
|||
});
|
||||
}
|
||||
|
||||
void System::on_register()
|
||||
{
|
||||
}
|
||||
|
||||
void System::on_unregister()
|
||||
{
|
||||
}
|
||||
|
||||
void System::on_surface_construct(entt::registry ®istry, entt::entity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto &surface = registry.get<SurfaceComponent>(entity);
|
||||
const auto &resolution = surface.get_resolution();
|
||||
const auto &position = surface.get_position();
|
||||
ensure_component_sanity(surface);
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
// TODO(Light): refactor "environment" into standalone module
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
auto *display_env = std::getenv("DISPLAY");
|
||||
ensure(display_env != nullptr, "DISPLAY env var not found!");
|
||||
|
||||
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
|
||||
auto *display = XOpenDisplay(display_env);
|
||||
auto root_window = XDefaultRootWindow(display);
|
||||
|
||||
auto border_width = 0;
|
||||
auto depth = 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
|
||||
);
|
||||
ensure(surface.m_glfw_handle, "Failed to create 'GLFWwindow'");
|
||||
surface.m_native_data.display = display;
|
||||
surface.m_native_data.window = main_window;
|
||||
|
||||
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);
|
||||
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
|
||||
long hints[5] = { 2, 0, 0, 0, 0 };
|
||||
Atom motif_hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);
|
||||
|
||||
XChangeProperty(
|
||||
display,
|
||||
surface.m_native_data.window,
|
||||
motif_hints,
|
||||
motif_hints,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)&hints,
|
||||
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 (...)
|
||||
{
|
||||
|
@ -212,82 +176,213 @@ void System::on_surface_construct(entt::registry ®istry, entt::entity entity)
|
|||
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)
|
||||
const auto &[display, window, _] = registry.get<SurfaceComponent>(entity).get_native_data();
|
||||
if (!display)
|
||||
{
|
||||
glfwDestroyWindow(surface.m_glfw_handle);
|
||||
log_wrn("Surface component destroyed with null display");
|
||||
return;
|
||||
}
|
||||
|
||||
XDestroyWindow(display, window);
|
||||
}
|
||||
|
||||
void System::handle_events(SurfaceComponent &surface)
|
||||
{
|
||||
auto &queue = surface.m_event_queue;
|
||||
queue.clear();
|
||||
|
||||
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<uint32_t>(XLookupKeysym(&event.xkey, 0))
|
||||
);
|
||||
break;
|
||||
}
|
||||
case KeyRelease:
|
||||
{
|
||||
queue.emplace_back<KeyReleasedEvent>(
|
||||
static_cast<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<uint32_t>(new_width),
|
||||
static_cast<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;
|
||||
}
|
||||
|
||||
case Expose: break;
|
||||
case GraphicsExpose: break;
|
||||
case NoExpose: break;
|
||||
case CirculateRequest: break;
|
||||
case ConfigureRequest: break;
|
||||
case MapRequest: break;
|
||||
case ResizeRequest: break;
|
||||
case CirculateNotify: break;
|
||||
case CreateNotify: break;
|
||||
case DestroyNotify: break;
|
||||
case GravityNotify: break;
|
||||
case MapNotify: break;
|
||||
case MappingNotify: break;
|
||||
case ReparentNotify: break;
|
||||
case UnmapNotify: break;
|
||||
case VisibilityNotify: break;
|
||||
case ColormapNotify: break;
|
||||
case PropertyNotify: break;
|
||||
case SelectionClear: break;
|
||||
case SelectionNotify: break;
|
||||
case SelectionRequest: break;
|
||||
default: log_inf("Unknown X Event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void System::set_title(ecs::Entity entity, std::string_view new_title)
|
||||
void System::handle_requests(SurfaceComponent &surface)
|
||||
{
|
||||
auto &surface = entity.get_component<SurfaceComponent>();
|
||||
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_err("Unknown surface request"); },
|
||||
};
|
||||
|
||||
surface.m_title = new_title;
|
||||
glfwSetWindowTitle(surface.m_glfw_handle, surface.m_title.c_str());
|
||||
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;
|
||||
|
||||
const auto &[display, window, _] = surface.get_native_data();
|
||||
const auto &[width, height] = request.resolution;
|
||||
XResizeWindow(display, window, width, height);
|
||||
}
|
||||
|
||||
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
||||
{
|
||||
surface.m_position = request.position;
|
||||
|
||||
const auto &[display, window, _] = surface.get_native_data();
|
||||
const auto &[x, y] = request.position;
|
||||
XMoveWindow(display, window, static_cast<int>(x), static_cast<int>(y));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto System::tick() -> bool
|
||||
{
|
||||
m_registry->view<SurfaceComponent>().each([](SurfaceComponent &surface) {
|
||||
glfwSwapBuffers(surface.m_glfw_handle);
|
||||
m_registry->view<SurfaceComponent>().each([this](SurfaceComponent &surface) {
|
||||
handle_requests(surface);
|
||||
|
||||
handle_events(surface);
|
||||
});
|
||||
|
||||
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)
|
||||
void ensure_component_sanity(const SurfaceComponent &component)
|
||||
{
|
||||
auto [width, height] = component.get_resolution();
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include <ecs/entity.hpp>
|
||||
#include <ecs/scene.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/fuzz.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
enum class Action : uint8_t
|
||||
enum class FuzzAction : uint8_t
|
||||
{
|
||||
create_entity,
|
||||
|
||||
|
@ -13,7 +15,22 @@ enum class Action : uint8_t
|
|||
|
||||
destroy_surface_component,
|
||||
|
||||
tick,
|
||||
push_request,
|
||||
|
||||
push_event,
|
||||
|
||||
tick_system,
|
||||
|
||||
count,
|
||||
};
|
||||
|
||||
enum class EventType : uint8_t
|
||||
{
|
||||
Closed,
|
||||
Moved,
|
||||
Resized,
|
||||
LostFocus,
|
||||
GainFocus,
|
||||
};
|
||||
|
||||
void create_surface_component(test::FuzzDataProvider &provider, ecs::Registry ®istry)
|
||||
|
@ -55,6 +72,14 @@ void remove_surface_component(ecs::Registry ®istry)
|
|||
}
|
||||
}
|
||||
|
||||
void push_request(ecs::Registry ®istry)
|
||||
{
|
||||
}
|
||||
|
||||
void push_event(ecs::Registry ®istry)
|
||||
{
|
||||
}
|
||||
|
||||
void check_invariants()
|
||||
{
|
||||
}
|
||||
|
@ -67,9 +92,14 @@ test::FuzzHarness harness = [](const uint8_t *data, size_t size) {
|
|||
|
||||
while (auto action = provider.consume<uint8_t>())
|
||||
{
|
||||
switch (static_cast<Action>(action.value()))
|
||||
if (*action > std::to_underlying(FuzzAction::count))
|
||||
{
|
||||
case Action::create_entity:
|
||||
*action = *action % std::to_underlying(FuzzAction::count);
|
||||
}
|
||||
|
||||
switch (static_cast<FuzzAction>(action.value()))
|
||||
{
|
||||
case FuzzAction::create_entity:
|
||||
{
|
||||
const auto length = std::min(provider.consume<uint32_t>().value_or(16), 255u);
|
||||
const auto tag = provider.consume_string(length).value_or("");
|
||||
|
@ -77,17 +107,36 @@ test::FuzzHarness harness = [](const uint8_t *data, size_t size) {
|
|||
|
||||
break;
|
||||
}
|
||||
case Action::create_surface_component:
|
||||
case FuzzAction::create_surface_component:
|
||||
{
|
||||
create_surface_component(provider, *registry);
|
||||
break;
|
||||
}
|
||||
case Action::destroy_surface_component:
|
||||
case FuzzAction::destroy_surface_component:
|
||||
{
|
||||
remove_surface_component(*registry);
|
||||
break;
|
||||
}
|
||||
case Action::tick:
|
||||
case FuzzAction::push_event:
|
||||
{
|
||||
const auto view = registry->get_entt_registry().view<SurfaceComponent>();
|
||||
|
||||
if (!view->empty())
|
||||
{
|
||||
view.each([&](auto entity, SurfaceComponent &surface) {
|
||||
provider.consume<uint8_t>().value_or(0);
|
||||
});
|
||||
|
||||
registry->get_entt_registry().remove<SurfaceComponent>(*view.begin());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case FuzzAction::push_request:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case FuzzAction::tick_system:
|
||||
{
|
||||
system.tick();
|
||||
break;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#include <ecs/entity.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/requests/surface.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
|
@ -18,6 +21,13 @@ constexpr auto height = 600u;
|
|||
constexpr auto vsync = true;
|
||||
constexpr auto visible = false;
|
||||
|
||||
template<class... Ts>
|
||||
struct overloads: Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
|
||||
class Fixture
|
||||
{
|
||||
public:
|
||||
|
@ -41,7 +51,11 @@ public:
|
|||
|
||||
void check_values(const SurfaceComponent &component)
|
||||
{
|
||||
expect_ne(std::get<SurfaceComponent::X11NativeHandle>(component.get_native_handle()), 0);
|
||||
#ifdef LIGHT_PLATFORM_LINUX
|
||||
expect_not_nullptr(component.get_native_data().display);
|
||||
expect_ne(component.get_native_data().window, 0);
|
||||
#endif
|
||||
|
||||
expect_eq(component.get_resolution().x, width);
|
||||
expect_eq(component.get_resolution().y, height);
|
||||
expect_eq(component.get_title(), title);
|
||||
|
@ -61,9 +75,7 @@ Suite raii = [] {
|
|||
|
||||
Case { "many won't freeze/throw" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
|
||||
/* range is small since glfw init/terminate is slow. */
|
||||
for (auto idx : std::views::iota(0, 100))
|
||||
for (auto idx : std::views::iota(0, 250))
|
||||
{
|
||||
ignore = System { fixture.registry() };
|
||||
}
|
||||
|
@ -74,7 +86,7 @@ Suite raii = [] {
|
|||
|
||||
auto fixture = Fixture {};
|
||||
fixture.add_surface_component();
|
||||
expect_throw([&] { ignore = System { { fixture.registry() } }; });
|
||||
expect_throw([&] { ignore = System { fixture.registry() }; });
|
||||
};
|
||||
|
||||
Case { "post construct has correct state" } = [] {
|
||||
|
@ -107,8 +119,8 @@ Suite system_events = [] {
|
|||
Case { "on_unregister won't throw" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
auto system = System { fixture.registry() };
|
||||
system.on_register();
|
||||
|
||||
system.on_register();
|
||||
system.on_unregister();
|
||||
expect_eq(fixture.registry()->view<SurfaceComponent>().size(), 0);
|
||||
};
|
||||
|
@ -185,14 +197,73 @@ Suite tick = [] {
|
|||
fixture.add_surface_component();
|
||||
system.tick();
|
||||
};
|
||||
};
|
||||
|
||||
Case { "ticking on chaotic registry won't throw" } = [] {
|
||||
Suite tick_handles_events = [] {
|
||||
Case { "ticking clears previous tick's events" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
auto system = System { fixture.registry() };
|
||||
auto &surface = fixture.add_surface_component();
|
||||
|
||||
// flush window-creation events
|
||||
system.tick();
|
||||
expect_eq(surface.peek_events().size(), 0);
|
||||
|
||||
surface.push_event(surface::MovedEvent({}, {}));
|
||||
expect_eq(surface.peek_events().size(), 1);
|
||||
|
||||
surface.push_event(surface::ButtonPressedEvent({}));
|
||||
expect_eq(surface.peek_events().size(), 2);
|
||||
|
||||
system.tick();
|
||||
expect_eq(surface.peek_events().size(), 0);
|
||||
};
|
||||
};
|
||||
|
||||
Suite property_setters = [] {
|
||||
Suite tick_handles_requests = [] {
|
||||
Case { "ticking clears requests" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
auto system = System { fixture.registry() };
|
||||
auto &surface = fixture.add_surface_component();
|
||||
|
||||
};
|
||||
constexpr auto title = "ABC";
|
||||
constexpr auto position = math::ivec2 { 50, 50 };
|
||||
constexpr auto resolution = math::uvec2 { 50, 50 };
|
||||
|
||||
Suite listeners = [] {
|
||||
expect_eq(surface.peek_requests().size(), 0);
|
||||
|
||||
surface.push_request(surface::ModifyVisibilityRequest(true));
|
||||
expect_eq(surface.peek_requests().size(), 1);
|
||||
system.tick();
|
||||
expect_eq(surface.peek_requests().size(), 0);
|
||||
|
||||
surface.push_request(surface::ModifyTitleRequest(title));
|
||||
expect_eq(surface.peek_requests().size(), 1);
|
||||
|
||||
surface.push_request(surface::ModifyResolutionRequest(resolution));
|
||||
surface.push_request(surface::ModifyPositionRequest(position));
|
||||
expect_eq(surface.peek_requests().size(), 1 + 2);
|
||||
|
||||
surface.push_request(surface::ModifyVisibilityRequest(false));
|
||||
surface.push_request(surface::ModifyVisibilityRequest(true));
|
||||
surface.push_request(surface::ModifyVisibilityRequest(false));
|
||||
expect_eq(surface.peek_requests().size(), 1 + 2 + 3);
|
||||
|
||||
system.tick();
|
||||
expect_eq(surface.peek_requests().size(), 0);
|
||||
|
||||
expect_eq(surface.get_title(), title);
|
||||
expect_eq(surface.get_position(), position);
|
||||
expect_eq(surface.get_resolution(), resolution);
|
||||
|
||||
log_dbg("EVENT COUNT: {}", surface.peek_events().size());
|
||||
for (const auto &event : surface.peek_events())
|
||||
{
|
||||
const auto visitor = overloads {
|
||||
[&](auto event) { log_dbg("event: {}", event.to_string()); },
|
||||
};
|
||||
|
||||
std::visit(visitor, event);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,143 +1,12 @@
|
|||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GLFW/glfw3native.h>
|
||||
#include <surface/system.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
// This class is to ensure glfwInit/glfwTerminate is called only once and exactly when needed during
|
||||
// entire application runtime
|
||||
class GlfwSingleton
|
||||
System::System(Ref<ecs::Registry> registry, Ref<app::EventMediator> event_mediator)
|
||||
: m_registry(std::move(registry))
|
||||
, m_event_mediator(std::move(event_mediator))
|
||||
{
|
||||
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(
|
||||
|
@ -183,24 +52,9 @@ void System::on_surface_construct(entt::registry ®istry, entt::entity entity)
|
|||
try
|
||||
{
|
||||
auto &surface = registry.get<SurfaceComponent>(entity);
|
||||
const auto &resolution = surface.get_resolution();
|
||||
const auto &position = surface.get_position();
|
||||
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 (...)
|
||||
{
|
||||
|
@ -212,17 +66,11 @@ void System::on_surface_construct(entt::registry ®istry, entt::entity entity)
|
|||
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)
|
||||
|
@ -230,16 +78,10 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -247,20 +89,12 @@ 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)
|
||||
|
@ -270,23 +104,12 @@ void System::set_visibility(ecs::Entity surface_entity, bool 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();
|
||||
|
|
|
@ -4,16 +4,14 @@
|
|||
#include <surface/events/keyboard.hpp>
|
||||
#include <surface/events/mouse.hpp>
|
||||
#include <surface/events/surface.hpp>
|
||||
#include <surface/requests/surface.hpp>
|
||||
#include <variant>
|
||||
|
||||
struct GLFWwindow;
|
||||
typedef struct _XDisplay Display;
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
/** Represents a platform's surface (eg. a Window).
|
||||
*
|
||||
* @note Read-only component, should only be modified through a system.
|
||||
*/
|
||||
/** Represents a platform's surface (eg. a Window). */
|
||||
class SurfaceComponent
|
||||
{
|
||||
public:
|
||||
|
@ -29,23 +27,27 @@ public:
|
|||
|
||||
// keyboard events
|
||||
KeyPressedEvent,
|
||||
KeyRepeatEvent,
|
||||
KeyReleasedEvent,
|
||||
KeySetCharEvent,
|
||||
|
||||
// mouse events
|
||||
MouseMovedEvent,
|
||||
WheelScrolledEvent,
|
||||
ButtonPressedEvent,
|
||||
ButtonReleasedEvent>;
|
||||
|
||||
using EventCallback = std::function<bool(const Event &)>;
|
||||
using Request = std::variant<
|
||||
ModifyTitleRequest,
|
||||
ModifyResolutionRequest,
|
||||
ModifyPositionRequest,
|
||||
ModifyVisibilityRequest>;
|
||||
|
||||
using WindowsNativeHandle = void *;
|
||||
|
||||
using X11NativeHandle = unsigned long;
|
||||
|
||||
using NativeHandle = std::variant<WindowsNativeHandle, X11NativeHandle>;
|
||||
#ifdef LIGHT_PLATFORM_LINUX
|
||||
struct NativeData
|
||||
{
|
||||
Display *display;
|
||||
uint32_t window;
|
||||
unsigned long wm_delete_message;
|
||||
};
|
||||
#endif
|
||||
|
||||
static constexpr auto max_dimension = 4096;
|
||||
|
||||
|
@ -67,6 +69,7 @@ public:
|
|||
, m_resolution(info.resolution)
|
||||
, m_vsync(info.vsync)
|
||||
, m_visible(info.visible)
|
||||
, m_native_data({})
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -80,6 +83,11 @@ public:
|
|||
return m_resolution;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_position() const -> const math::ivec2 &
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_vsync() const -> bool
|
||||
{
|
||||
return m_vsync;
|
||||
|
@ -90,30 +98,48 @@ public:
|
|||
return m_visible;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_native_handle() const -> NativeHandle
|
||||
[[nodiscard]] auto get_native_data() const -> const NativeData &
|
||||
{
|
||||
return m_native_handle;
|
||||
return m_native_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto peek_events() const -> const std::vector<Event> &
|
||||
{
|
||||
return m_event_queue;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto peek_requests() const -> const std::vector<Request> &
|
||||
{
|
||||
return m_requests;
|
||||
};
|
||||
|
||||
void push_request(const Request &request)
|
||||
{
|
||||
m_requests.emplace_back(request);
|
||||
}
|
||||
|
||||
/** @note: Only the surface system and tests should push events */
|
||||
void push_event(const Event &event)
|
||||
{
|
||||
m_event_queue.emplace_back(event);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto get_glfw_handle() const -> GLFWwindow *
|
||||
{
|
||||
return m_glfw_handle;
|
||||
}
|
||||
|
||||
std::string m_title;
|
||||
|
||||
math::uvec2 m_resolution;
|
||||
|
||||
math::ivec2 m_position;
|
||||
|
||||
bool m_vsync;
|
||||
|
||||
bool m_visible;
|
||||
|
||||
NativeHandle m_native_handle;
|
||||
NativeData m_native_data;
|
||||
|
||||
GLFWwindow *m_glfw_handle {};
|
||||
std::vector<Event> m_event_queue;
|
||||
|
||||
std::vector<EventCallback> m_event_callbacks;
|
||||
std::vector<Request> m_requests;
|
||||
};
|
||||
|
||||
} // namespace lt::surface
|
||||
|
|
|
@ -7,11 +7,11 @@ namespace lt::surface {
|
|||
class KeyPressedEvent
|
||||
{
|
||||
public:
|
||||
KeyPressedEvent(int32_t key): m_key(key)
|
||||
KeyPressedEvent(uint32_t key): m_key(key)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_key() const -> int32_t
|
||||
[[nodiscard]] auto get_key() const -> uint32_t
|
||||
{
|
||||
return m_key;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
int32_t m_key;
|
||||
uint32_t m_key;
|
||||
};
|
||||
|
||||
class KeyRepeatEvent
|
||||
|
@ -32,7 +32,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_key() const -> int32_t
|
||||
[[nodiscard]] auto get_key() const -> uint32_t
|
||||
{
|
||||
return m_key;
|
||||
}
|
||||
|
@ -43,17 +43,17 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
int32_t m_key;
|
||||
uint32_t m_key;
|
||||
};
|
||||
|
||||
class KeyReleasedEvent
|
||||
{
|
||||
public:
|
||||
KeyReleasedEvent(int key): m_key(key)
|
||||
KeyReleasedEvent(uint32_t key): m_key(key)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_key() const -> int32_t
|
||||
[[nodiscard]] auto get_key() const -> uint32_t
|
||||
{
|
||||
return m_key;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
int32_t m_key;
|
||||
uint32_t m_key;
|
||||
};
|
||||
|
||||
class KeySetCharEvent
|
||||
|
|
27
modules/surface/public/requests/surface.hpp
Normal file
27
modules/surface/public/requests/surface.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec2.hpp>
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
struct ModifyTitleRequest
|
||||
{
|
||||
std::string title;
|
||||
};
|
||||
|
||||
struct ModifyResolutionRequest
|
||||
{
|
||||
math::uvec2 resolution;
|
||||
};
|
||||
|
||||
struct ModifyPositionRequest
|
||||
{
|
||||
math::ivec2 position;
|
||||
};
|
||||
|
||||
struct ModifyVisibilityRequest
|
||||
{
|
||||
bool visible;
|
||||
};
|
||||
|
||||
}; // namespace lt::surface
|
|
@ -1,9 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <app/system.hpp>
|
||||
#include <ecs/entity.hpp>
|
||||
#include <ecs/scene.hpp>
|
||||
#include <surface/components.hpp>
|
||||
|
||||
namespace lt::surface {
|
||||
|
||||
|
@ -22,26 +20,12 @@ public:
|
|||
|
||||
auto operator=(const System &) -> System & = delete;
|
||||
|
||||
void on_register() override
|
||||
{
|
||||
}
|
||||
void on_register() override;
|
||||
|
||||
void on_unregister() override
|
||||
{
|
||||
}
|
||||
void on_unregister() override;
|
||||
|
||||
auto tick() -> bool override;
|
||||
|
||||
static void set_title(ecs::Entity surface_entity, std::string_view new_title);
|
||||
|
||||
void set_size(ecs::Entity surface_entity, const math::uvec2 &new_size);
|
||||
|
||||
void set_v_sync(ecs::Entity surface_entity, bool vsync);
|
||||
|
||||
void set_visibility(ecs::Entity surface_entity, bool visible);
|
||||
|
||||
void add_event_listener(ecs::Entity surface_entity, SurfaceComponent::EventCallback callback);
|
||||
|
||||
private:
|
||||
void on_surface_construct(entt::registry ®istry, entt::entity entity);
|
||||
|
||||
|
@ -49,10 +33,35 @@ private:
|
|||
|
||||
void on_surface_destroy(entt::registry ®istry, entt::entity entity);
|
||||
|
||||
void ensure_component_sanity(const SurfaceComponent &component);
|
||||
void handle_requests(struct SurfaceComponent &surface);
|
||||
|
||||
void handle_events(struct SurfaceComponent &surface);
|
||||
|
||||
void modify_title(struct SurfaceComponent &surface, const struct ModifyTitleRequest &request);
|
||||
|
||||
void modify_resolution(
|
||||
struct SurfaceComponent &surface,
|
||||
const struct ModifyResolutionRequest &request
|
||||
);
|
||||
|
||||
void modify_position(
|
||||
struct SurfaceComponent &surface,
|
||||
const struct ModifyPositionRequest &request
|
||||
);
|
||||
|
||||
void modify_visiblity(
|
||||
struct SurfaceComponent &surface,
|
||||
const struct ModifyVisibilityRequest &request
|
||||
);
|
||||
|
||||
void modify_position(ecs::Entity surface_entity, const math::ivec2 &new_size);
|
||||
|
||||
void modify_position(ecs::Entity surface_entity, const math::uvec2 &new_size);
|
||||
|
||||
void set_visibility(ecs::Entity surface_entity, bool visible);
|
||||
|
||||
|
||||
Ref<ecs::Registry> m_registry;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lt::surface
|
||||
|
|
Loading…
Add table
Reference in a new issue