feat: reimplement input system
This commit is contained in:
		
							parent
							
								
									21e9933a42
								
							
						
					
					
						commit
						d69315c6aa
					
				
					 8 changed files with 432 additions and 283 deletions
				
			
		| 
						 | 
				
			
			@ -1,2 +1,4 @@
 | 
			
		|||
add_library_module(input input.cpp)
 | 
			
		||||
target_link_libraries(input PUBLIC surface math imgui::imgui logger)
 | 
			
		||||
add_library_module(input system.cpp)
 | 
			
		||||
target_link_libraries(input PUBLIC surface math  logger)
 | 
			
		||||
 | 
			
		||||
add_test_module(input system.test.cpp)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,159 +0,0 @@
 | 
			
		|||
#include <imgui.h>
 | 
			
		||||
#include <input/input.hpp>
 | 
			
		||||
#include <logger/logger.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
Input::Input(): m_mouse_position {}, m_mouse_delta {}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	restart_input_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::receive_user_interface_events_impl(bool receive, bool toggle /* = false */)
 | 
			
		||||
{
 | 
			
		||||
	m_user_interface_events = toggle ? !m_user_interface_events : receive;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::receieve_game_events_impl(bool receive, bool toggle /*= false*/)
 | 
			
		||||
{
 | 
			
		||||
	auto prev = m_game_events;
 | 
			
		||||
	m_game_events = toggle ? !m_user_interface_events : receive;
 | 
			
		||||
 | 
			
		||||
	if (m_game_events != prev)
 | 
			
		||||
	{
 | 
			
		||||
		restart_input_state();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::restart_input_state()
 | 
			
		||||
{
 | 
			
		||||
	m_keyboad_keys.fill(false);
 | 
			
		||||
	m_mouse_buttons.fill(false);
 | 
			
		||||
 | 
			
		||||
	m_mouse_position = math::vec2(0.0f);
 | 
			
		||||
	m_mouse_delta = math::vec2(0.0f);
 | 
			
		||||
	m_mouse_wheel_delta = 0.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::on_event(const Event &inputEvent)
 | 
			
		||||
{
 | 
			
		||||
	auto &io = ImGui::GetIO();
 | 
			
		||||
	switch (inputEvent.get_event_type())
 | 
			
		||||
	{
 | 
			
		||||
		//** MOUSE_EVENTS **//
 | 
			
		||||
	case EventType::MouseMoved:
 | 
			
		||||
	{
 | 
			
		||||
		const auto &event = dynamic_cast<const MouseMovedEvent &>(inputEvent);
 | 
			
		||||
 | 
			
		||||
		if (m_game_events)
 | 
			
		||||
		{
 | 
			
		||||
			m_mouse_delta = event.get_position() - m_mouse_position;
 | 
			
		||||
			m_mouse_position = event.get_position();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			io.MousePos = ImVec2(event.get_x(), event.get_y());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	case EventType::ButtonPressed:
 | 
			
		||||
	{
 | 
			
		||||
		const auto &event = dynamic_cast<const ButtonPressedEvent &>(inputEvent);
 | 
			
		||||
 | 
			
		||||
		if (m_game_events)
 | 
			
		||||
		{
 | 
			
		||||
			m_mouse_buttons[event.get_button()] = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			io.MouseDown[event.get_button()] = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	case EventType::ButtonReleased:
 | 
			
		||||
	{
 | 
			
		||||
		const auto &event = dynamic_cast<const ButtonReleasedEvent &>(inputEvent);
 | 
			
		||||
 | 
			
		||||
		if (m_game_events)
 | 
			
		||||
		{
 | 
			
		||||
			m_mouse_buttons[event.get_button()] = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			io.MouseDown[event.get_button()] = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	case EventType::WheelScrolled:
 | 
			
		||||
	{
 | 
			
		||||
		const auto &event = dynamic_cast<const WheelScrolledEvent &>(inputEvent);
 | 
			
		||||
 | 
			
		||||
		if (m_game_events)
 | 
			
		||||
		{
 | 
			
		||||
			m_mouse_wheel_delta = event.get_offset();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			io.MouseWheel = event.get_offset();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	//** KEYBOARD_EVENTS **//
 | 
			
		||||
	case EventType::KeyPressed:
 | 
			
		||||
	{
 | 
			
		||||
		const auto &event = dynamic_cast<const KeyPressedEvent &>(inputEvent);
 | 
			
		||||
 | 
			
		||||
		if (m_game_events)
 | 
			
		||||
		{
 | 
			
		||||
			m_keyboad_keys[event.get_key()] = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			// io.AddKeyEvent(event.get_key(), true);
 | 
			
		||||
			// if (event.get_key() == Key::BackSpace)
 | 
			
		||||
			//	io.AddInputCharacter(Key::BackSpace);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	case EventType::KeyReleased:
 | 
			
		||||
	{
 | 
			
		||||
		const auto &event = dynamic_cast<const KeyReleasedEvent &>(inputEvent);
 | 
			
		||||
 | 
			
		||||
		if (m_game_events)
 | 
			
		||||
		{
 | 
			
		||||
			m_keyboad_keys[event.get_key()] = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			// io.AddKeyEvent(event.get_key(), false);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	case EventType::SetChar:
 | 
			
		||||
	{
 | 
			
		||||
		if (m_user_interface_events)
 | 
			
		||||
		{
 | 
			
		||||
			const auto &event = dynamic_cast<const SetCharEvent &>(inputEvent);
 | 
			
		||||
			io.AddInputCharacter(event.get_character());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	default: log_trc("Dropped event");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,138 @@
 | 
			
		|||
#include <input/components.hpp>
 | 
			
		||||
#include <input/system.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::input {
 | 
			
		||||
 | 
			
		||||
template<class... Ts>
 | 
			
		||||
struct overloads: Ts...
 | 
			
		||||
{
 | 
			
		||||
	using Ts::operator()...;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
System::System(Ref<ecs::Registry> registry): m_registry(std::move(registry))
 | 
			
		||||
{
 | 
			
		||||
	ensure(m_registry, "Failed to initialize input system: null registry");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto System::tick() -> bool
 | 
			
		||||
{
 | 
			
		||||
	m_registry->view<surface::SurfaceComponent>().each([&](const entt::entity,
 | 
			
		||||
	                                                       surface::SurfaceComponent &surface) {
 | 
			
		||||
		for (const auto &event : surface.peek_events())
 | 
			
		||||
		{
 | 
			
		||||
			handle_event(event);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	m_registry->view<InputComponent>().each([&](const entt::entity, InputComponent &input) {
 | 
			
		||||
		// TODO(Light): instead of iterating over all actions each frame,
 | 
			
		||||
		// make a list of "dirty" actions to reset
 | 
			
		||||
		// and a surface_input->input_action mapping to get to action through input
 | 
			
		||||
		// instead of brute-force checking all of them.
 | 
			
		||||
		for (auto &action : input.m_actions)
 | 
			
		||||
		{
 | 
			
		||||
			auto code = action.trigger.mapped_keycode;
 | 
			
		||||
			if (code < m_keys.size() && m_keys[code])
 | 
			
		||||
			{
 | 
			
		||||
				if (action.state == InputAction::State::triggered)
 | 
			
		||||
				{
 | 
			
		||||
					action.state = InputAction::State::active;
 | 
			
		||||
				}
 | 
			
		||||
				else if (action.state == InputAction::State::inactive)
 | 
			
		||||
				{
 | 
			
		||||
					action.state = InputAction::State::triggered;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				action.state = InputAction::State::inactive;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_register()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_unregister()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::handle_event(const surface::SurfaceComponent::Event &event)
 | 
			
		||||
{
 | 
			
		||||
	const auto visitor = overloads {
 | 
			
		||||
		[this](const surface::ClosedEvent &) { on_surface_lost_focus(); },
 | 
			
		||||
		[this](const surface::LostFocusEvent &) { on_surface_lost_focus(); },
 | 
			
		||||
		[this](const surface::KeyPressedEvent &event) { on_key_press(event); },
 | 
			
		||||
		[this](const surface::KeyReleasedEvent &event) { on_key_release(event); },
 | 
			
		||||
		[this](const surface::MouseMovedEvent &event) { on_pointer_move(event); },
 | 
			
		||||
		[this](const surface::ButtonPressedEvent &event) { on_button_press(event); },
 | 
			
		||||
		[this](const surface::ButtonReleasedEvent &event) { on_button_release(event); },
 | 
			
		||||
		[this](auto) {},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	std::visit(visitor, event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_surface_lost_focus()
 | 
			
		||||
{
 | 
			
		||||
	for (auto &key : m_keys)
 | 
			
		||||
	{
 | 
			
		||||
		key = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (auto &button : m_buttons)
 | 
			
		||||
	{
 | 
			
		||||
		button = false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_key_press(const lt::surface::KeyPressedEvent &event)
 | 
			
		||||
{
 | 
			
		||||
	if (event.get_key() > m_keys.size())
 | 
			
		||||
	{
 | 
			
		||||
		log_dbg(
 | 
			
		||||
		    "Key code larger than key container size, implement platform-dependant "
 | 
			
		||||
		    "key-code-mapping!"
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_keys[event.get_key()] = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_key_release(const lt::surface::KeyReleasedEvent &event)
 | 
			
		||||
{
 | 
			
		||||
	if (event.get_key() > m_keys.size())
 | 
			
		||||
	{
 | 
			
		||||
		log_dbg(
 | 
			
		||||
		    "Key code larger than key container size, implement platform-dependant "
 | 
			
		||||
		    "key-code-mapping!"
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_keys[event.get_key()] = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_pointer_move(const lt::surface::MouseMovedEvent &event)
 | 
			
		||||
{
 | 
			
		||||
	m_pointer_position = event.get_position();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_button_press(const lt::surface::ButtonPressedEvent &event)
 | 
			
		||||
{
 | 
			
		||||
	m_buttons[event.get_button()] = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::on_button_release(const lt::surface::ButtonReleasedEvent &event)
 | 
			
		||||
{
 | 
			
		||||
	m_buttons[event.get_button()] = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace lt::input
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										169
									
								
								modules/input/private/system.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								modules/input/private/system.test.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
#include <ecs/entity.hpp>
 | 
			
		||||
#include <input/components.hpp>
 | 
			
		||||
#include <input/system.hpp>
 | 
			
		||||
#include <test/test.hpp>
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN
 | 
			
		||||
using namespace lt;
 | 
			
		||||
using input::InputComponent;
 | 
			
		||||
using input::System;
 | 
			
		||||
using std::ignore;
 | 
			
		||||
using test::Case;
 | 
			
		||||
using test::expect_eq;
 | 
			
		||||
using test::expect_false;
 | 
			
		||||
using test::expect_ne;
 | 
			
		||||
using test::expect_not_nullptr;
 | 
			
		||||
using test::expect_throw;
 | 
			
		||||
using test::Suite;
 | 
			
		||||
// NOLINTEND
 | 
			
		||||
 | 
			
		||||
class Fixture
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	[[nodiscard]] auto registry() -> Ref<ecs::Registry>
 | 
			
		||||
	{
 | 
			
		||||
		return m_registry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto add_input_component() -> ecs::Entity
 | 
			
		||||
	{
 | 
			
		||||
		auto entity = m_registry->create_entity("");
 | 
			
		||||
		entity.add_component<InputComponent>();
 | 
			
		||||
 | 
			
		||||
		return entity;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto add_surface_component() -> ecs::Entity
 | 
			
		||||
	{
 | 
			
		||||
		auto entity = m_registry->create_entity("");
 | 
			
		||||
		entity.add_component<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {});
 | 
			
		||||
 | 
			
		||||
		return entity;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<ecs::Registry> m_registry = create_ref<ecs::Registry>();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite raii = [] {
 | 
			
		||||
	Case { "happy path won't throw" } = [&] {
 | 
			
		||||
		System { Fixture {}.registry() };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "many won't freeze/throw" } = [&] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
			
		||||
		{
 | 
			
		||||
			ignore = System { fixture.registry() };
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "unhappy path throws" } = [] {
 | 
			
		||||
		expect_throw([] { ignore = System { {} }; });
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite system_events = [] {
 | 
			
		||||
	Case { "on_register won't throw" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = System { fixture.registry() };
 | 
			
		||||
 | 
			
		||||
		system.on_register();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 0);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "on_unregister won't throw" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = System { fixture.registry() };
 | 
			
		||||
 | 
			
		||||
		system.on_register();
 | 
			
		||||
		system.on_unregister();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 0);
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite registry_events = [] {
 | 
			
		||||
	Case { "on_construct<InputComnent>" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = System { fixture.registry() };
 | 
			
		||||
 | 
			
		||||
		const auto &entity = fixture.add_input_component();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 1);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "on_destrroy<InputComponent>" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = create_scope<System>(fixture.registry());
 | 
			
		||||
 | 
			
		||||
		auto entity_a = fixture.add_input_component();
 | 
			
		||||
		auto entity_b = fixture.add_input_component();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 2);
 | 
			
		||||
 | 
			
		||||
		entity_a.remove_component<InputComponent>();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 1);
 | 
			
		||||
 | 
			
		||||
		system.reset();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 1);
 | 
			
		||||
 | 
			
		||||
		entity_b.remove_component<InputComponent>();
 | 
			
		||||
		expect_eq(fixture.registry()->view<InputComponent>().size(), 0);
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Suite tick = [] {
 | 
			
		||||
	Case { "Empty tick won't throw" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = System { fixture.registry() };
 | 
			
		||||
 | 
			
		||||
		expect_false(system.tick());
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "Tick triggers input action" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = System { fixture.registry() };
 | 
			
		||||
 | 
			
		||||
		auto &surface = fixture.add_surface_component().get_component<surface::SurfaceComponent>();
 | 
			
		||||
		auto &input = fixture.add_input_component().get_component<InputComponent>();
 | 
			
		||||
 | 
			
		||||
		auto action_key = input.add_action(
 | 
			
		||||
		    {
 | 
			
		||||
		        .name { "test" },
 | 
			
		||||
		        .trigger = { .mapped_keycode = 69 },
 | 
			
		||||
		    }
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
			
		||||
		system.tick();
 | 
			
		||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
			
		||||
 | 
			
		||||
		surface.push_event(surface::KeyPressedEvent(69));
 | 
			
		||||
		system.tick();
 | 
			
		||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered);
 | 
			
		||||
 | 
			
		||||
		system.tick();
 | 
			
		||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
			
		||||
 | 
			
		||||
		system.tick();
 | 
			
		||||
		system.tick();
 | 
			
		||||
		system.tick();
 | 
			
		||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
			
		||||
 | 
			
		||||
		surface.push_event(surface::KeyPressedEvent(69));
 | 
			
		||||
		system.tick();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Case { "Tick triggers" } = [] {
 | 
			
		||||
		auto fixture = Fixture {};
 | 
			
		||||
		auto system = System { fixture.registry() };
 | 
			
		||||
 | 
			
		||||
		auto &surface = fixture.add_surface_component().get_component<surface::SurfaceComponent>();
 | 
			
		||||
		auto &input = fixture.add_input_component().get_component<InputComponent>();
 | 
			
		||||
 | 
			
		||||
		auto action_key = input.add_action(
 | 
			
		||||
		    {
 | 
			
		||||
		        .name { "test" },
 | 
			
		||||
		        .trigger = { .mapped_keycode = 69 },
 | 
			
		||||
		    }
 | 
			
		||||
		);
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										57
									
								
								modules/input/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								modules/input/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace lt::input {
 | 
			
		||||
 | 
			
		||||
struct Trigger
 | 
			
		||||
{
 | 
			
		||||
	uint32_t mapped_keycode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct InputAction
 | 
			
		||||
{
 | 
			
		||||
	using Key = size_t;
 | 
			
		||||
 | 
			
		||||
	enum class State : uint8_t
 | 
			
		||||
	{
 | 
			
		||||
		inactive,
 | 
			
		||||
		active,
 | 
			
		||||
		triggered,
 | 
			
		||||
		cancelled,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	std::string name;
 | 
			
		||||
 | 
			
		||||
	State state;
 | 
			
		||||
 | 
			
		||||
	Trigger trigger;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class InputComponent
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	InputComponent() = default;
 | 
			
		||||
 | 
			
		||||
	auto add_action(InputAction action) -> size_t
 | 
			
		||||
	{
 | 
			
		||||
		m_actions.emplace_back(std::move(action));
 | 
			
		||||
		return m_actions.size() - 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto get_action(auto idx) -> const InputAction &
 | 
			
		||||
	{
 | 
			
		||||
		return m_actions[idx];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	friend class System;
 | 
			
		||||
 | 
			
		||||
	void push_event()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::vector<InputAction> m_actions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::input
 | 
			
		||||
							
								
								
									
										44
									
								
								modules/input/public/events.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								modules/input/public/events.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <math/vec2.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::input {
 | 
			
		||||
 | 
			
		||||
class AnalogEvent
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	AnalogEvent(uint32_t input_code, math::uvec2 pointer_position)
 | 
			
		||||
	    : m_input_code(input_code)
 | 
			
		||||
	    , m_pointer_position(pointer_position)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_code() const -> uint32_t
 | 
			
		||||
	{
 | 
			
		||||
		return m_input_code;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto get_pointer_position() const -> math::uvec2
 | 
			
		||||
	{
 | 
			
		||||
		return m_pointer_position;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto to_string() const -> std::string
 | 
			
		||||
	{
 | 
			
		||||
		auto stream = std::stringstream {};
 | 
			
		||||
		const auto &[x, y] = m_pointer_position;
 | 
			
		||||
		stream << "input::AnalogEvent: " << m_input_code << " @ " << x << ", " << y;
 | 
			
		||||
		return stream.str();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	uint32_t m_input_code;
 | 
			
		||||
 | 
			
		||||
	math::uvec2 m_pointer_position;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AxisEvent
 | 
			
		||||
{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt::input
 | 
			
		||||
| 
						 | 
				
			
			@ -1,80 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <math/vec2.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt {
 | 
			
		||||
 | 
			
		||||
class Event;
 | 
			
		||||
 | 
			
		||||
class Input
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static auto instance() -> Input &
 | 
			
		||||
	{
 | 
			
		||||
		static auto instance = Input {};
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void receive_user_interface_events(bool receive, bool toggle = false)
 | 
			
		||||
	{
 | 
			
		||||
		instance().receive_user_interface_events_impl(receive, toggle);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void receive_game_events(bool receive, bool toggle = false)
 | 
			
		||||
	{
 | 
			
		||||
		instance().receieve_game_events_impl(receive, toggle);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static auto get_keyboard_key(int code) -> bool
 | 
			
		||||
	{
 | 
			
		||||
		return instance().m_keyboad_keys[code];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static auto get_mouse_button(int code) -> bool
 | 
			
		||||
	{
 | 
			
		||||
		return instance().m_mouse_buttons[code];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static auto get_mouse_position(int /*code*/) -> const math::vec2 &
 | 
			
		||||
	{
 | 
			
		||||
		return instance().m_mouse_position;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void on_event(const Event &inputEvent);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto is_receiving_input_events() const -> bool
 | 
			
		||||
	{
 | 
			
		||||
		return m_user_interface_events;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] auto is_receiving_game_events() const -> bool
 | 
			
		||||
	{
 | 
			
		||||
		return m_game_events;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Input();
 | 
			
		||||
 | 
			
		||||
	void receive_user_interface_events_impl(bool receive, bool toggle = false);
 | 
			
		||||
 | 
			
		||||
	void receieve_game_events_impl(bool receive, bool toggle = false);
 | 
			
		||||
 | 
			
		||||
	void restart_input_state();
 | 
			
		||||
 | 
			
		||||
	std::array<bool, 348> m_keyboad_keys {};
 | 
			
		||||
 | 
			
		||||
	std::array<bool, 8> m_mouse_buttons {};
 | 
			
		||||
 | 
			
		||||
	math::vec2 m_mouse_position;
 | 
			
		||||
 | 
			
		||||
	math::vec2 m_mouse_delta;
 | 
			
		||||
 | 
			
		||||
	float m_mouse_wheel_delta {};
 | 
			
		||||
 | 
			
		||||
	bool m_user_interface_events { true };
 | 
			
		||||
 | 
			
		||||
	bool m_game_events { true };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace lt
 | 
			
		||||
| 
						 | 
				
			
			@ -1,63 +1,46 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <surface/system.hpp>
 | 
			
		||||
#include <app/system.hpp>
 | 
			
		||||
#include <ecs/scene.hpp>
 | 
			
		||||
#include <surface/components.hpp>
 | 
			
		||||
#include <surface/events/keyboard.hpp>
 | 
			
		||||
#include <surface/events/mouse.hpp>
 | 
			
		||||
 | 
			
		||||
namespace lt::input {
 | 
			
		||||
 | 
			
		||||
template<class... Ts>
 | 
			
		||||
struct overloads: Ts...
 | 
			
		||||
{
 | 
			
		||||
	using Ts::operator()...;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @note If this system is attached, it will always consume the input events f rom surface.
 | 
			
		||||
 * Therefore if you want any input detection mechanism, callbacks should be setup with this
 | 
			
		||||
 * system and not directly with surface.
 | 
			
		||||
 */
 | 
			
		||||
class System
 | 
			
		||||
class System: public app::ISystem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	System(lt::surface::System &surface_system)
 | 
			
		||||
	{
 | 
			
		||||
		surface_system.add_event_listener([this](auto &&event) {
 | 
			
		||||
			return handle_event(std::forward<decltype(event)>(event));
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
	System(Ref<ecs::Registry> registry);
 | 
			
		||||
 | 
			
		||||
	auto tick() -> bool override;
 | 
			
		||||
 | 
			
		||||
	void on_register() override;
 | 
			
		||||
 | 
			
		||||
	void on_unregister() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	auto handle_event(const lt::surface::System::Event &event) -> bool
 | 
			
		||||
	{
 | 
			
		||||
		const auto visitor = overloads {
 | 
			
		||||
			[this](const lt::surface::KeyPressedEvent &event) {
 | 
			
		||||
			    m_keys[event.get_key()] = true;
 | 
			
		||||
			    return true;
 | 
			
		||||
			},
 | 
			
		||||
	void handle_event(const surface::SurfaceComponent::Event &event);
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::KeyRepeatEvent &) { return false; },
 | 
			
		||||
	void on_surface_lost_focus();
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::KeyReleasedEvent &) { return false; },
 | 
			
		||||
	void on_key_press(const lt::surface::KeyPressedEvent &event);
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::KeySetCharEvent &) { return false; },
 | 
			
		||||
	void on_key_release(const lt::surface::KeyReleasedEvent &event);
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::MouseMovedEvent &) { return false; },
 | 
			
		||||
	void on_pointer_move(const lt::surface::MouseMovedEvent &event);
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::WheelScrolledEvent &) { return false; },
 | 
			
		||||
	void on_button_press(const lt::surface::ButtonPressedEvent &event);
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::ButtonPressedEvent &) { return false; },
 | 
			
		||||
	void on_button_release(const lt::surface::ButtonReleasedEvent &event);
 | 
			
		||||
 | 
			
		||||
			[](const lt::surface::ButtonReleasedEvent &) { return false; },
 | 
			
		||||
 | 
			
		||||
			[](const auto &) { return false; },
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		return std::visit(visitor, event);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void setup_callbacks(GLFWwindow *handle);
 | 
			
		||||
	Ref<ecs::Registry> m_registry;
 | 
			
		||||
 | 
			
		||||
	std::array<bool, 512> m_keys {};
 | 
			
		||||
 | 
			
		||||
	std::array<bool, 512> m_buttons {};
 | 
			
		||||
 | 
			
		||||
	math::vec2 m_pointer_position;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue