diff --git a/UE4SS/include/SettingsManager.hpp b/UE4SS/include/SettingsManager.hpp index 880c025fd..fd59293a1 100644 --- a/UE4SS/include/SettingsManager.hpp +++ b/UE4SS/include/SettingsManager.hpp @@ -25,6 +25,7 @@ namespace RC bool EnableDebugKeyBindings{false}; int64_t SecondsToScanBeforeGivingUp{30}; bool UseUObjectArrayCache{true}; + StringType InputSource{STR("Default")}; } General; struct SectionEngineVersionOverride diff --git a/UE4SS/include/UE4SSProgram.hpp b/UE4SS/include/UE4SSProgram.hpp index cbe56052a..f132292c3 100644 --- a/UE4SS/include/UE4SSProgram.hpp +++ b/UE4SS/include/UE4SSProgram.hpp @@ -12,6 +12,7 @@ #include #include #include + #include #include #include @@ -98,7 +99,9 @@ namespace RC bool m_is_program_started; protected: - Input::Handler m_input_handler{L"ConsoleWindowClass", L"UnrealWindow"}; +#ifdef HAS_INPUT + Input::Handler m_input_handler; +#endif std::jthread m_event_loop; public: diff --git a/UE4SS/src/Mod/CppUserModBase.cpp b/UE4SS/src/Mod/CppUserModBase.cpp index 26be6a05b..2cacc2dc8 100644 --- a/UE4SS/src/Mod/CppUserModBase.cpp +++ b/UE4SS/src/Mod/CppUserModBase.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace RC { CppUserModBase::CppUserModBase() @@ -24,12 +26,12 @@ namespace RC } GUITabs.clear(); - auto& key_events = UE4SSProgram::get_program().m_input_handler.get_events(); - std::erase_if(key_events, [&](Input::KeySet& input_event) -> bool { - bool were_all_events_registered_from_this_mod = true; - for (auto& [key, vector_of_key_data] : input_event.key_data) - { - std::erase_if(vector_of_key_data, [&](Input::KeyData& key_data) -> bool { +#ifdef HAS_INPUT + UE4SSProgram::get_program().m_input_handler.get_events_safe([&](auto& key_set) { + std::erase_if(key_set.key_data, [&](auto& item) -> bool { + auto& [_, key_data] = item; + bool were_all_events_registered_from_this_mod = true; + std::erase_if(key_data, [&](Input::KeyData& key_data) -> bool { // custom_data == 1: Bind came from Lua, and custom_data2 is nullptr. // custom_data == 2: Bind came from C++, and custom_data2 is a pointer to KeyDownEventData. Must free it. auto event_data = static_cast(key_data.custom_data2); @@ -44,10 +46,11 @@ namespace RC return false; } }); - } - return were_all_events_registered_from_this_mod; + return were_all_events_registered_from_this_mod; + }); }); +#endif } auto CppUserModBase::register_tab(StringViewType tab_name, GUI::GUITab::RenderFunctionType render_function) -> void diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index 0bde48165..a113ea031 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -11,7 +11,9 @@ #include #include #include + #include + #include #include #include diff --git a/UE4SS/src/UE4SSProgram.cpp b/UE4SS/src/UE4SSProgram.cpp index 63f88ca9c..cc9089017 100644 --- a/UE4SS/src/UE4SSProgram.cpp +++ b/UE4SS/src/UE4SSProgram.cpp @@ -824,8 +824,7 @@ namespace RC { m_debugging_gui.get_live_view().set_listeners_allowed(false); } - - m_input_handler.register_keydown_event(Input::Key::O, {Input::ModifierKey::CONTROL}, [&]() { + register_keydown_event(Input::Key::O, {Input::ModifierKey::CONTROL}, [&]() { TRY([&] { auto was_gui_open = get_debugging_ui().is_open(); stop_render_thread(); @@ -839,7 +838,7 @@ namespace RC } #ifdef TIME_FUNCTION_MACRO_ENABLED - m_input_handler.register_keydown_event(Input::Key::Y, {Input::ModifierKey::CONTROL}, [&]() { + register_keydown_event(Input::Key::Y, {Input::ModifierKey::CONTROL}, [&]() { if (FunctionTimerFrame::s_timer_enabled) { FunctionTimerFrame::stop_profiling(); @@ -856,16 +855,14 @@ namespace RC TRY([&] { ObjectDumper::init(); - if (settings_manager.General.EnableHotReloadSystem) { - m_input_handler.register_keydown_event(Input::Key::R, {Input::ModifierKey::CONTROL}, [&]() { + register_keydown_event(Input::Key::R, {Input::ModifierKey::CONTROL}, [&]() { TRY([&] { reinstall_mods(); }); }); } - if ((settings_manager.ObjectDumper.LoadAllAssetsBeforeDumpingObjects || settings_manager.CXXHeaderGenerator.LoadAllAssetsBeforeGeneratingCXXHeaders) && Unreal::Version::IsBelow(4, 17)) { @@ -878,6 +875,21 @@ namespace RC STR("FAssetData not available, ignoring 'LoadAllAssetsBeforeDumpingObjects' & 'LoadAllAssetsBeforeGeneratingCXXHeaders'.")); } +#ifdef HAS_INPUT + m_input_handler.init(); + if (!settings_manager.General.InputSource.empty()) + { + if (m_input_handler.set_input_source(to_string(settings_manager.General.InputSource))) + { + Output::send(STR("Input source set to: {}\n"), to_generic_string(m_input_handler.get_current_input_source())); + } + else + { + Output::send(STR("Failed to set input source to: {}\n"), settings_manager.General.InputSource); + } + } +#endif + install_lua_mods(); LuaMod::on_program_start(); fire_program_start_for_cpp_mods(); @@ -886,7 +898,7 @@ namespace RC if (settings_manager.General.EnableDebugKeyBindings) { - m_input_handler.register_keydown_event(Input::Key::NUM_NINE, {Input::ModifierKey::CONTROL}, [&]() { + register_keydown_event(Input::Key::NUM_NINE, {Input::ModifierKey::CONTROL}, [&]() { generate_uht_compatible_headers(); }); } @@ -948,9 +960,9 @@ namespace RC } } //*/ - +#ifdef HAS_INPUT m_input_handler.process_event(); - +#endif { ProfilerScopeNamed("mod update processing"); @@ -1318,13 +1330,13 @@ namespace RC uninstall_mods(); - // Remove key binds that were set from Lua scripts - auto& key_events = m_input_handler.get_events(); - std::erase_if(key_events, [](Input::KeySet& input_event) -> bool { - bool were_all_events_registered_from_lua = true; - for (auto& [key, vector_of_key_data] : input_event.key_data) - { - std::erase_if(vector_of_key_data, [&](Input::KeyData& key_data) -> bool { +// Remove key binds that were set from Lua scripts +#ifdef HAS_INPUT + m_input_handler.get_events_safe([&](auto& key_set) { + std::erase_if(key_set.key_data, [&](auto& item) -> bool { + auto& [_, key_data] = item; + bool were_all_events_registered_from_lua = true; + std::erase_if(key_data, [&](Input::KeyData& key_data) -> bool { // custom_data == 1: Bind came from Lua, and custom_data2 is nullptr. // custom_data == 2: Bind came from C++, and custom_data2 is a pointer to KeyDownEventData. Must free it. if (key_data.custom_data == 1) @@ -1337,10 +1349,11 @@ namespace RC return false; } }); - } - return were_all_events_registered_from_lua; + return were_all_events_registered_from_lua; + }); }); +#endif // Remove all custom properties // Uncomment when custom properties are working @@ -1509,7 +1522,9 @@ namespace RC auto UE4SSProgram::register_keydown_event(Input::Key key, const Input::EventCallbackCallable& callback, uint8_t custom_data, void* custom_data2) -> void { +#ifdef HAS_INPUT m_input_handler.register_keydown_event(key, callback, custom_data, custom_data2); +#endif } auto UE4SSProgram::register_keydown_event(Input::Key key, @@ -1518,17 +1533,27 @@ namespace RC uint8_t custom_data, void* custom_data2) -> void { +#ifdef HAS_INPUT m_input_handler.register_keydown_event(key, modifier_keys, callback, custom_data, custom_data2); +#endif } auto UE4SSProgram::is_keydown_event_registered(Input::Key key) -> bool { +#ifdef HAS_INPUT return m_input_handler.is_keydown_event_registered(key); +#else + return false; +#endif } auto UE4SSProgram::is_keydown_event_registered(Input::Key key, const Input::Handler::ModifierKeyArray& modifier_keys) -> bool { +#ifdef HAS_INPUT return m_input_handler.is_keydown_event_registered(key, modifier_keys); +#else + return false; +#endif } auto UE4SSProgram::find_mod_by_name_internal(StringViewType mod_name, IsInstalled is_installed, IsStarted is_started, FMBNI_ExtraPredicate extra_predicate) -> Mod* diff --git a/UE4SS/xmake.lua b/UE4SS/xmake.lua index bcc66eb23..f26df6354 100644 --- a/UE4SS/xmake.lua +++ b/UE4SS/xmake.lua @@ -58,7 +58,7 @@ target(projectName) set_default(true) add_rules("ue4ss.defines.exports") add_rules("ue4ss.check.minimum.version") - add_options("ue4ssBetaIsStarted", "ue4ssIsBeta", "allowAllVersions") + add_options("ue4ssBetaIsStarted", "ue4ssIsBeta", "allowAllVersions", "ue4ssInput") add_includedirs("include", { public = true }) add_includedirs("generated_include", { public = true }) add_headerfiles("include/**.hpp") @@ -74,7 +74,7 @@ target(projectName) "ScopedTimer", "Profiler", "patternsleuth_bind", "glad", { public = true } ) - + add_packages("fmt", { public = true }) add_packages("imgui", "ImGuiTextEdit", "IconFontCppHeaders", "glfw", "opengl", { public = true }) diff --git a/UVTD/include/UVTD/UVTD.hpp b/UVTD/include/UVTD/UVTD.hpp index 4e41dd47c..efcf459ff 100644 --- a/UVTD/include/UVTD/UVTD.hpp +++ b/UVTD/include/UVTD/UVTD.hpp @@ -15,7 +15,6 @@ namespace RC::UVTD { extern bool processing_events; - extern Input::Handler input_handler; auto main(DumpSettings) -> void; } // namespace RC::UVTD diff --git a/UVTD/src/UVTD.cpp b/UVTD/src/UVTD.cpp index c1d28f83f..3b3bd09d1 100644 --- a/UVTD/src/UVTD.cpp +++ b/UVTD/src/UVTD.cpp @@ -27,16 +27,6 @@ namespace RC::UVTD { bool processing_events{false}; - Input::Handler input_handler{STR("ConsoleWindowClass"), STR("UnrealWindow")}; - - auto static event_loop_update() -> void - { - for (processing_events = true; processing_events;) - { - input_handler.process_event(); - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } - } auto main(DumpSettings dump_settings) -> void { diff --git a/deps/first/Input/include/Input/Handler.hpp b/deps/first/Input/include/Input/Handler.hpp index 784a19890..e4f0366c2 100644 --- a/deps/first/Input/include/Input/Handler.hpp +++ b/deps/first/Input/include/Input/Handler.hpp @@ -1,5 +1,4 @@ -#ifndef IO_INPUT_HANDLER_HPP -#define IO_INPUT_HANDLER_HPP +#pragma once // TODO: Abstract more... need to get rid of Windows.h from InputHandler.cpp @@ -7,6 +6,9 @@ #include #include #include +#include +#include +#include #include #include @@ -15,12 +17,16 @@ namespace RC::Input { using EventCallbackCallable = std::function; - auto is_modifier_key_required(ModifierKey, std::vector) -> bool; + struct InputEvent + { + Key key; + ModifierKeys modifier_keys{}; + }; struct KeyData { - std::vector required_modifier_keys{}; - std::vector callbacks{}; + ModifierKeys required_modifier_keys{}; + EventCallbackCallable callback{}; uint8_t custom_data{}; void* custom_data2{}; bool requires_modifier_keys{}; @@ -32,60 +38,75 @@ namespace RC::Input std::unordered_map> key_data; }; + using ModifierKeyArray = std::array; + +#ifdef HAS_INPUT + class PlatformInputSource; class RC_INPUT_API Handler { private: - std::vector m_active_window_classes{}; - std::vector m_key_sets{}; - std::unordered_map m_modifier_keys_down{}; - bool m_any_keys_are_down{}; + // std::vector m_key_sets{}; + KeySet m_key_set{}; bool m_allow_input{true}; + std::array m_subscribed_keys{}; - public: - Handler() = delete; - template - explicit Handler(WindowClasses... window_classes) - { - static_assert(std::conjunction...>::value, "WindowClasses must be of type const wchar_t*"); + std::shared_ptr m_platform_handler; + std::mutex m_event_mutex; - m_modifier_keys_down.emplace(ModifierKey::SHIFT, false); - m_modifier_keys_down.emplace(ModifierKey::CONTROL, false); - m_modifier_keys_down.emplace(ModifierKey::ALT, false); - - register_window_classes(window_classes...); - } - - private: - template - auto register_window_classes(WindowClass window_class) -> void - { - m_active_window_classes.emplace_back(window_class); - } - - template - auto register_window_classes(WindowClass window_class, WindowClasses... window_classes) -> void - { - m_active_window_classes.emplace_back(window_class); - register_window_classes(window_classes...); - } - - auto are_modifier_keys_down(const std::vector&) -> bool; - auto is_program_focused() -> bool; + public: + Handler() {}; + // Input source and event processing public: + auto set_input_source(std::string source) -> bool; auto process_event() -> void; + + // Interfaces for UE4SS and ModSystem for event registration + public: + auto init() -> void; + auto register_keydown_event(Input::Key, EventCallbackCallable, uint8_t custom_data = 0, void* custom_data2 = nullptr) -> void; - using ModifierKeyArray = std::array; + using ModifierKeyArray = Input::ModifierKeyArray; auto register_keydown_event(Input::Key, const ModifierKeyArray&, const EventCallbackCallable&, uint8_t custom_data = 0, void* custom_data2 = nullptr) -> void; auto is_keydown_event_registered(Input::Key) -> bool; auto is_keydown_event_registered(Input::Key, const ModifierKeyArray&) -> bool; - auto get_events() -> std::vector&; + auto get_events_safe(std::function) -> void; + auto clear_subscribed_keys() -> void; + auto clear_subscribed_key(Key k) -> void; + + auto has_event_on_key(Input::Key key) -> bool; + auto get_subscribed_keys() const -> const std::array& + { + return m_subscribed_keys; + } + auto get_allow_input() -> bool; auto set_allow_input(bool new_value) -> void; + + auto get_current_input_source() -> std::string; + + private: + static std::unordered_map> m_input_sources_store; + static auto register_input_source(std::shared_ptr input_source) -> void; + + public: + static auto get_input_source(std::string source) -> std::shared_ptr + { + if (m_input_sources_store.find(source) != m_input_sources_store.end()) + { + return m_input_sources_store[source]; + } + return nullptr; + } }; +#else + class Handler + { + public: + using ModifierKeyArray = Input::ModifierKeyArray; + }; +#endif // HAS_INPUT } // namespace RC::Input - -#endif // IO_INPUT_HANDLER_HPP diff --git a/deps/first/Input/include/Input/KeyDef.hpp b/deps/first/Input/include/Input/KeyDef.hpp index 66006e58c..6f6373667 100644 --- a/deps/first/Input/include/Input/KeyDef.hpp +++ b/deps/first/Input/include/Input/KeyDef.hpp @@ -1,13 +1,14 @@ #pragma once #include +#include +#include namespace RC::Input { static constexpr uint32_t max_callbacks_per_event = 30; static constexpr uint8_t max_keys = 0xFF; -#ifdef _WIN32 enum Key : uint8_t { RESERVED_START_OF_ENUM = 0x0, @@ -244,11 +245,62 @@ namespace RC::Input MODIFIER_KEYS_MAX, }; + static inline bool is_modify_key_valid(ModifierKey key) + { + return (key < MODIFIER_KEYS_MAX) && (key > MOD_KEY_START_OF_ENUM); + } + + struct ModifierKeys + { + /// SAFETY: This is a bitfield, following static_assert ensures that the bitfield is not larger than 32 bits + uint32_t keys; + + // allow ops between keys + + auto operator|(const ModifierKeys& key) -> ModifierKeys&; + auto operator|=(const ModifierKeys& key) -> ModifierKeys&; + auto operator|(const ModifierKey& key) -> ModifierKeys&; + auto operator|=(const ModifierKey& key) -> ModifierKeys&; + + auto operator==(const ModifierKeys& key) const -> bool; + auto operator!=(const ModifierKeys& key) const -> bool; + + auto operator<(const ModifierKeys& key) const -> bool; + auto operator>(const ModifierKeys& key) const -> bool; + + ModifierKeys(const ModifierKey key) : keys{is_modify_key_valid(key) ? (1u << key) : 0} {}; + ModifierKeys(const ModifierKeys& other) : keys{other.keys} {}; + + ModifierKeys() : keys{0} {}; + + template + ModifierKeys(ModifierKey key, Args... args) : keys{(is_modify_key_valid(key) ? (1 << key) : 0) | ModifierKeys(args...).keys} {}; + + ModifierKeys(std::initializer_list keys); + + template + ModifierKeys(const TArray& keys) : keys{0} + { + for (auto key : keys) + { + if (is_modify_key_valid(key)) + { + this->keys |= (1 << key); + } + } + } + + bool empty() const + { + return keys == 0; + } + }; + static constexpr uint8_t max_modifier_keys = MODIFIER_KEYS_MAX; + static_assert(max_modifier_keys < 32, "Modifier keys cannot exceed 32"); -#else - static_assert(false, "The input library only works on Windows."); -#endif + auto operator&(const ModifierKeys& keys, const ModifierKey& key) -> bool; + auto operator&(const ModifierKeys& keys, const ModifierKeys& key) -> bool; auto operator++(Input::Key& key) -> Input::Key&; } // namespace RC::Input diff --git a/deps/first/Input/include/Input/Platform/GLFW3InputSource.hpp b/deps/first/Input/include/Input/Platform/GLFW3InputSource.hpp new file mode 100644 index 000000000..333673b5f --- /dev/null +++ b/deps/first/Input/include/Input/Platform/GLFW3InputSource.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace RC::Input +{ + + class GLFW3InputSource : public QueueInputSource + { + public: + auto begin_frame() -> void; + auto receive_input(int key, int action, int mods) -> void; + auto end_frame() -> void; + + private: + Key m_translate_key[512]; + + public: + auto is_available() -> bool override; + const char* get_name() override + { + return "GLFW3"; + } + GLFW3InputSource(); + }; + +} // namespace RC::Input diff --git a/deps/first/Input/include/Input/Platform/QueueInputSource.hpp b/deps/first/Input/include/Input/Platform/QueueInputSource.hpp new file mode 100644 index 000000000..b184cc9e3 --- /dev/null +++ b/deps/first/Input/include/Input/Platform/QueueInputSource.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include + +namespace RC::Input +{ + class QueueInputSource : public PlatformInputSource + { + private: + static constexpr int max_inputs = 256; + RingBufferSPSC m_input_queue; + + protected: + bool m_activated{false}; + + private: + /// SAFETY: Only update and return m_input_events + /// in process_event and flush_events functions + std::vector m_input_events; + + public: + ~QueueInputSource() = default; + + // QueeueInputSource is not a implemented input source + // and should not be used directly + bool is_available() override + { + return false; + }; + + bool activate() override + { + return m_activated = true; + }; + + bool deactivate() override + { + m_activated = false; + return true; + }; + + std::vector& process_event(Handler* handler) override; + + int source_priority() override + { + return 0; + } + + const char* get_name() override + { + return "Queue"; + } + + public: + auto push_input_event(const InputEvent& event) -> void; + }; + +}; // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/include/Input/Platform/Win32AsyncInputSource.hpp b/deps/first/Input/include/Input/Platform/Win32AsyncInputSource.hpp new file mode 100644 index 000000000..0564de08a --- /dev/null +++ b/deps/first/Input/include/Input/Platform/Win32AsyncInputSource.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace RC::Input +{ + class Win32AsyncInputSource : public PlatformInputSource + { + private: + std::vector m_active_window_classes{}; + std::unordered_map m_modifier_keys_down{}; + bool m_any_keys_are_down{}; + bool m_activated{false}; + std::array m_key_down{}; + + private: + /// SAFETY: Only update and return m_input_events + /// in the process_event function + std::vector m_input_events{}; + + public: + template + explicit Win32AsyncInputSource(WindowClasses... window_classes) + { + static_assert(std::conjunction...>::value, "WindowClasses must be of type const wchar_t*"); + + m_modifier_keys_down.emplace(ModifierKey::SHIFT, false); + m_modifier_keys_down.emplace(ModifierKey::CONTROL, false); + m_modifier_keys_down.emplace(ModifierKey::ALT, false); + + register_window_classes(window_classes...); + } + + private: + template + auto register_window_classes(WindowClass window_class) -> void + { + m_active_window_classes.emplace_back(window_class); + } + + template + auto register_window_classes(WindowClass window_class, WindowClasses... window_classes) -> void + { + m_active_window_classes.emplace_back(window_class); + register_window_classes(window_classes...); + } + + auto are_modifier_keys_down(const std::vector&) -> bool; + auto is_program_focused() -> bool; + + public: + bool is_available() override + { + return true; + }; + + bool activate() override + { + m_key_down.fill(false); + return m_activated = true; + }; + + bool deactivate() override + { + m_activated = false; + return true; + }; + + std::vector& process_event(Handler* handler) override; + ~Win32AsyncInputSource() = default; + int source_priority() override + { + return 0; + } + + const char* get_name() override + { + return "Win32Async"; + } + }; +}; // namespace RC::Input diff --git a/deps/first/Input/include/Input/PlatformInputSource.hpp b/deps/first/Input/include/Input/PlatformInputSource.hpp new file mode 100644 index 000000000..387311b9a --- /dev/null +++ b/deps/first/Input/include/Input/PlatformInputSource.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace RC::Input +{ + + class PlatformInputSource + { + public: + /// Check if the input source is available + virtual bool is_available() + { + return false; + }; + + /// Initialize the input source + virtual bool activate() + { + return false; + }; + + /// Initialize the input source + virtual bool deactivate() + { + return false; + }; + + /// Get the priority of the input source, smaller number means higher priority + virtual int source_priority() + { + return 999; + }; + + /// Process the event and return the input events in the frame + virtual std::vector& process_event(Handler* handler) = 0; + + virtual ~PlatformInputSource() = default; + + virtual const char* get_name() + { + return "Unknown"; + } + }; + +}; // namespace RC::Input diff --git a/deps/first/Input/include/Input/RingBuffer.hpp b/deps/first/Input/include/Input/RingBuffer.hpp new file mode 100644 index 000000000..923d36ec9 --- /dev/null +++ b/deps/first/Input/include/Input/RingBuffer.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +namespace RC::Input +{ + /// SAFETY: This is ONLY a single producer, single consumer lock-free queue + template + struct RingBufferSPSC + { + T m_queue[max_buffer]; + std::atomic m_head{0}; + std::atomic m_tail{0}; + + // tail belongs to the producer + auto push(const T& event) -> bool + { + auto current_tail = m_tail.load(std::memory_order_relaxed); + int next_tail = (current_tail + 1) % max_buffer; + if (next_tail == m_head.load(std::memory_order_acquire)) + { + // Queue is full + return false; + } + + m_queue[current_tail] = event; + m_tail.store(next_tail, std::memory_order_release); + return true; + } + + // head belongs to the consumer + auto pop() -> std::optional + { + auto current_head = m_head.load(std::memory_order_relaxed); + if (current_head == m_tail.load(std::memory_order_acquire)) + { + // Queue is empty + return std::nullopt; + } + + T event = m_queue[current_head]; + m_head.store((current_head + 1) % max_buffer, std::memory_order_release); + return event; + } + }; +} // namespace RC::Input diff --git a/deps/first/Input/src/Handler.cpp b/deps/first/Input/src/Handler.cpp index 8533a9aec..abc7c0442 100644 --- a/deps/first/Input/src/Handler.cpp +++ b/deps/first/Input/src/Handler.cpp @@ -1,345 +1,212 @@ #include +#include +#include #include - -#define NOMINMAX -#include +#include +#include namespace RC::Input { - auto is_modifier_key_required(ModifierKey modifier_key, std::vector modifier_keys) -> bool - { - for (const auto& required_modifier_key : modifier_keys) - { - if (required_modifier_key == ModifierKey::MOD_KEY_START_OF_ENUM) - { - continue; - } - - if (modifier_key == required_modifier_key) - { - return true; - } - } - - return false; - } - - auto Handler::are_modifier_keys_down(const std::vector& required_modifier_keys) -> bool - { - bool are_required_modifier_keys_down = true; - - for (const auto& [modifier_key, modifier_key_is_down] : m_modifier_keys_down) - { - for (const auto& required_modifier_key : required_modifier_keys) - { - if (modifier_key == required_modifier_key && !modifier_key_is_down) - { - are_required_modifier_keys_down = false; - } - - if (modifier_key != required_modifier_key && modifier_key_is_down && !is_modifier_key_required(modifier_key, required_modifier_keys)) - { - return false; - } - } - } - - return are_required_modifier_keys_down; - } - - auto Handler::is_program_focused() -> bool - { - HWND hwnd = GetForegroundWindow(); - if (!hwnd) return false; - - wchar_t current_window_class_name[MAX_PATH]; - if (!GetClassNameW(hwnd, current_window_class_name, MAX_PATH)) return false; - - for (const auto& active_window_class : m_active_window_classes) - { - if (wcscmp(current_window_class_name, active_window_class) == 0) - { - return true; - } - } - - return false; - } + std::unordered_map> Handler::m_input_sources_store; auto Handler::process_event() -> void { - if (!is_program_focused()) + if (m_platform_handler == nullptr) { return; } std::vector callbacks_to_call{}; - bool skip_this_frame = !get_allow_input(); - bool is_any_modifier_keys_down = false; - bool any_keys_are_down = false; - - if (m_any_keys_are_down) - { - skip_this_frame = true; - } - - // Check if any modifier keys are down - for (auto& [modifier_key, key_is_down] : m_modifier_keys_down) - { - if (GetAsyncKeyState(modifier_key)) - { - is_any_modifier_keys_down = true; - key_is_down = true; - } - else - { - key_is_down = false; - } - } + auto events = m_platform_handler->process_event(this); - for (auto& key_set_data : m_key_sets) { - for (auto& [key, key_data_array] : key_set_data.key_data) + // Lock the event mutex to access the key_set + auto event_update_lock = std::lock_guard(m_event_mutex); + for (auto& event : events) { - for (auto& key_data : key_data_array) + auto key_set_array = m_key_set.key_data[event.key]; + for (auto& key_data : key_set_array) { - if (GetAsyncKeyState(key) && !key_data.is_down) + if (key_data.required_modifier_keys == event.modifier_keys) { - any_keys_are_down = true; - bool should_propagate = true; - - if (key_data.requires_modifier_keys) - { - if (!are_modifier_keys_down(key_data.required_modifier_keys)) - { - should_propagate = false; - } - } - - if (!key_data.requires_modifier_keys && is_any_modifier_keys_down) - { - should_propagate = false; - } - - if (should_propagate) - { - key_data.is_down = true; - for (const auto& callback : key_data.callbacks) - { - callbacks_to_call.emplace_back(callback); - } - } - } - else if (!GetAsyncKeyState(key) && key_data.is_down) - { - key_data.is_down = false; + callbacks_to_call.emplace_back(key_data.callback); } } } } - if (any_keys_are_down) - { - m_any_keys_are_down = true; - } - else - { - m_any_keys_are_down = false; - } - + // No need to lock the event mutex to call the callbacks + // this avoids key registration inside the callback for (const auto& callback : callbacks_to_call) { - if (skip_this_frame) - { - return; - } callback(); } } auto Handler::register_keydown_event(Input::Key key, EventCallbackCallable callback, uint8_t custom_data, void* custom_data2) -> void { - KeySet& key_set = [&]() -> KeySet& { - for (auto& key_set : m_key_sets) - { - if (key_set.key_data.contains(key)) - { - return key_set; - } - } - - return m_key_sets.emplace_back(KeySet{}); - }(); - - KeyData& key_data = key_set.key_data[key].emplace_back(); - key_data.callbacks.emplace_back(callback); + auto event_update_lock = std::lock_guard(m_event_mutex); + KeyData& key_data = m_key_set.key_data[key].emplace_back(); + key_data.callback = callback; key_data.custom_data = custom_data; key_data.custom_data2 = custom_data2; + m_subscribed_keys[key] = true; } auto Handler::register_keydown_event( Input::Key key, const ModifierKeyArray& modifier_keys, const EventCallbackCallable& callback, uint8_t custom_data, void* custom_data2) -> void { - KeySet& key_set = [&]() -> KeySet& { - for (auto& key_set : m_key_sets) - { - if (key_set.key_data.contains(key)) - { - return key_set; - } - } - - return m_key_sets.emplace_back(KeySet{}); - }(); - - KeyData& key_data = key_set.key_data[key].emplace_back(); - key_data.callbacks.emplace_back(callback); + auto event_update_lock = std::lock_guard(m_event_mutex); + KeyData& key_data = m_key_set.key_data[key].emplace_back(); + key_data.callback = callback; key_data.custom_data = custom_data; key_data.custom_data2 = custom_data2; key_data.requires_modifier_keys = true; + key_data.required_modifier_keys = modifier_keys; + m_subscribed_keys[key] = true; + } - for (const auto& modifier_key : modifier_keys) + auto Handler::is_keydown_event_registered(Input::Key key) -> bool + { + auto event_update_lock = std::lock_guard(m_event_mutex); + auto key_data = m_key_set.key_data.find(key); + if (key_data == m_key_set.key_data.end()) { - if (modifier_key != ModifierKey::MOD_KEY_START_OF_ENUM) + return false; + } + for (const auto& key_data_container : key_data->second) + { + if (key_data_container.required_modifier_keys.empty()) { - key_data.required_modifier_keys.emplace_back(modifier_key); + return true; } } + return false; } - auto Handler::is_keydown_event_registered(Input::Key key) -> bool + auto Handler::is_keydown_event_registered(Input::Key key, const ModifierKeyArray& modifier_keys_array) -> bool { - bool is_key_registered{}; - bool is_key_registered_with_no_modifier_keys = true; - for (const auto& key_set : m_key_sets) + auto event_update_lock = std::lock_guard(m_event_mutex); + auto key_data = m_key_set.key_data.find(key); + auto modifier_keys = ModifierKeys(modifier_keys_array); + if (key_data == m_key_set.key_data.end()) { - for (const auto& [key_registered, key_data_container] : key_set.key_data) - { - if (key_registered == key) - { - is_key_registered = true; - } - else - { - continue; - } - - for (const auto& key_data : key_data_container) - { - if (key_data.requires_modifier_keys) - { - is_key_registered_with_no_modifier_keys = false; - break; - } - } - - if (!is_key_registered_with_no_modifier_keys) - { - break; - } - } - - if (!is_key_registered_with_no_modifier_keys) + return false; + } + for (const auto& key_data_container : key_data->second) + { + if (key_data_container.required_modifier_keys == modifier_keys) { - break; + return true; } } - - return is_key_registered && is_key_registered_with_no_modifier_keys; + return false; } - auto Handler::is_keydown_event_registered(Input::Key key, const ModifierKeyArray& modifier_keys) -> bool + auto Handler::has_event_on_key(Input::Key key) -> bool { - bool is_key_registered{}; - bool all_modifier_keys_match{}; - for (const auto& key_set : m_key_sets) - { - for (const auto& [key_registered, key_data_container] : key_set.key_data) - { - if (key_registered == key) - { - is_key_registered = true; - } - else - { - continue; - } + return m_subscribed_keys[key]; + } - all_modifier_keys_match = false; - for (const auto& key_data : key_data_container) - { - if (!key_data.requires_modifier_keys && !modifier_keys.empty()) - { - all_modifier_keys_match = false; - continue; - } + auto Handler::get_events_safe(std::function callback) -> void + { + auto event_update_lock = std::lock_guard(m_event_mutex); + callback(m_key_set); + } - if (!key_data.requires_modifier_keys && modifier_keys.empty()) - { - all_modifier_keys_match = true; - break; - } + auto Handler::clear_subscribed_keys() -> void + { + m_subscribed_keys.fill(false); + } - for (const auto& modifier_key : key_data.required_modifier_keys) - { - for (const auto modifier_key_to_check : modifier_keys) - { - if (modifier_key_to_check == ModifierKey::MOD_KEY_START_OF_ENUM) - { - continue; - } + auto Handler::clear_subscribed_key(Key k) -> void + { + m_subscribed_keys[k] = false; + } - if (modifier_key_to_check == modifier_key) - { - all_modifier_keys_match = true; - } - else - { - all_modifier_keys_match = false; - } - } + auto Handler::get_allow_input() -> bool + { + return m_allow_input; + } - if (all_modifier_keys_match) - { - break; - } - } + auto Handler::set_allow_input(bool new_value) -> void + { + m_allow_input = new_value; + } - if (all_modifier_keys_match) - { - break; - } + /// Set the input source to the given source + /// SAFETY: Only call this function from the main thread + auto Handler::set_input_source(std::string source) -> bool + { + auto event_update_lock = std::lock_guard(m_event_mutex); + std::shared_ptr next_input_source = nullptr; + if (source == "Default") + { + // find the highest priority input source + int highest_priority = INT_MAX; + for (auto& input_source : m_input_sources_store) + { + if (!input_source.second->is_available()) + { + continue; } - - if (all_modifier_keys_match) + auto priority = input_source.second->source_priority(); + if (priority < highest_priority) { - break; + next_input_source = input_source.second; + highest_priority = priority; } } - - if (all_modifier_keys_match) + if (highest_priority == INT_MAX) { - break; + Output::send(STR("Input source: Source not found\n")); + return false; } } - - return is_key_registered && all_modifier_keys_match; - } - - auto Handler::get_events() -> std::vector& - { - return m_key_sets; + else + { + auto input_source = m_input_sources_store.find(source); + if (input_source == m_input_sources_store.end()) + { + return false; + } + next_input_source = input_source->second; + if (!next_input_source->is_available()) + { + return false; + } + } + if (next_input_source != m_platform_handler) + { + if (m_platform_handler != nullptr) + { + m_platform_handler->deactivate(); + } + next_input_source->activate(); + m_platform_handler = next_input_source; + return true; + } + return true; } - auto Handler::get_allow_input() -> bool + // register the input source to the input source store + auto Handler::register_input_source(std::shared_ptr input_source) -> void { - return m_allow_input; + std::string name = input_source->get_name(); + if (m_input_sources_store.find(name) == m_input_sources_store.end()) + { + m_input_sources_store[name] = input_source; + } } - auto Handler::set_allow_input(bool new_value) -> void + auto Handler::get_current_input_source() -> std::string { - m_allow_input = new_value; + if (m_platform_handler == nullptr) + { + return "None"; + } + return m_platform_handler->get_name(); } } // namespace RC::Input diff --git a/deps/first/Input/src/KeyDef.cpp b/deps/first/Input/src/KeyDef.cpp index 5e73dbbc1..9057adc0f 100644 --- a/deps/first/Input/src/KeyDef.cpp +++ b/deps/first/Input/src/KeyDef.cpp @@ -1,4 +1,5 @@ #include +#include namespace RC::Input { @@ -14,4 +15,71 @@ namespace RC::Input return key; } + + auto ModifierKeys::operator|(const ModifierKeys& rkeys) -> ModifierKeys& + { + keys |= rkeys.keys; + return *this; + } + + auto ModifierKeys::operator|(const ModifierKey& key) -> ModifierKeys& + { + if (is_modify_key_valid(key)) + { + keys |= (1 << key); + } + return *this; + } + + auto ModifierKeys::operator|=(const ModifierKeys& rkeys) -> ModifierKeys& + { + return *this = *this | rkeys; + } + + auto ModifierKeys::operator|=(const ModifierKey& key) -> ModifierKeys& + { + return *this = *this | key; + } + + auto ModifierKeys::operator==(const ModifierKeys& key) const -> bool + { + return keys == key.keys; + } + + auto ModifierKeys::operator<(const ModifierKeys& rkeys) const -> bool + { + return keys < rkeys.keys; + } + + auto ModifierKeys::operator>(const ModifierKeys& rkeys) const -> bool + { + return keys > rkeys.keys; + } + + auto ModifierKeys::operator!=(const ModifierKeys& key) const -> bool + { + return keys != key.keys; + } + + ModifierKeys::ModifierKeys(std::initializer_list keys) + { + for (auto key : keys) + { + if (is_modify_key_valid(key)) + { + this->keys |= (1 << key); + } + } + } + + auto operator&(const ModifierKeys& keys, const ModifierKey& key) -> bool + { + return !!(keys.keys & (1 << key)); + } + + auto operator&(const ModifierKeys& keys, const ModifierKeys& key) -> bool + { + return !!(keys.keys & key.keys); + } + } // namespace RC::Input diff --git a/deps/first/Input/src/Platform/GLFW3InputSource.cpp b/deps/first/Input/src/Platform/GLFW3InputSource.cpp new file mode 100644 index 000000000..776276513 --- /dev/null +++ b/deps/first/Input/src/Platform/GLFW3InputSource.cpp @@ -0,0 +1,349 @@ +#include + +#define GLFW_KEY_SPACE 32 + +#define GLFW_KEY_APOSTROPHE 39 /* ' */ + +#define GLFW_KEY_COMMA 44 /* , */ + +#define GLFW_KEY_MINUS 45 /* - */ + +#define GLFW_KEY_PERIOD 46 /* . */ + +#define GLFW_KEY_SLASH 47 /* / */ + +#define GLFW_KEY_0 48 + +#define GLFW_KEY_1 49 + +#define GLFW_KEY_2 50 + +#define GLFW_KEY_3 51 + +#define GLFW_KEY_4 52 + +#define GLFW_KEY_5 53 + +#define GLFW_KEY_6 54 + +#define GLFW_KEY_7 55 + +#define GLFW_KEY_8 56 + +#define GLFW_KEY_9 57 + +#define GLFW_KEY_SEMICOLON 59 /* ; */ + +#define GLFW_KEY_EQUAL 61 /* = */ + +#define GLFW_KEY_A 65 + +#define GLFW_KEY_B 66 + +#define GLFW_KEY_C 67 + +#define GLFW_KEY_D 68 + +#define GLFW_KEY_E 69 + +#define GLFW_KEY_F 70 + +#define GLFW_KEY_G 71 + +#define GLFW_KEY_H 72 + +#define GLFW_KEY_I 73 + +#define GLFW_KEY_J 74 + +#define GLFW_KEY_K 75 + +#define GLFW_KEY_L 76 + +#define GLFW_KEY_M 77 + +#define GLFW_KEY_N 78 + +#define GLFW_KEY_O 79 + +#define GLFW_KEY_P 80 + +#define GLFW_KEY_Q 81 + +#define GLFW_KEY_R 82 + +#define GLFW_KEY_S 83 + +#define GLFW_KEY_T 84 + +#define GLFW_KEY_U 85 + +#define GLFW_KEY_V 86 + +#define GLFW_KEY_W 87 + +#define GLFW_KEY_X 88 + +#define GLFW_KEY_Y 89 + +#define GLFW_KEY_Z 90 + +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ + +#define GLFW_KEY_BACKSLASH 92 /* \ */ + +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ + +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ + +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ + +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +#define GLFW_KEY_ESCAPE 256 + +#define GLFW_KEY_ENTER 257 + +#define GLFW_KEY_TAB 258 + +#define GLFW_KEY_BACKSPACE 259 + +#define GLFW_KEY_INSERT 260 + +#define GLFW_KEY_DELETE 261 + +#define GLFW_KEY_RIGHT 262 + +#define GLFW_KEY_LEFT 263 + +#define GLFW_KEY_DOWN 264 + +#define GLFW_KEY_UP 265 + +#define GLFW_KEY_PAGE_UP 266 + +#define GLFW_KEY_PAGE_DOWN 267 + +#define GLFW_KEY_HOME 268 + +#define GLFW_KEY_END 269 + +#define GLFW_KEY_CAPS_LOCK 280 + +#define GLFW_KEY_SCROLL_LOCK 281 + +#define GLFW_KEY_NUM_LOCK 282 + +#define GLFW_KEY_PRINT_SCREEN 283 + +#define GLFW_KEY_PAUSE 284 + +#define GLFW_KEY_F1 290 + +#define GLFW_KEY_F2 291 + +#define GLFW_KEY_F3 292 + +#define GLFW_KEY_F4 293 + +#define GLFW_KEY_F5 294 + +#define GLFW_KEY_F6 295 + +#define GLFW_KEY_F7 296 + +#define GLFW_KEY_F8 297 + +#define GLFW_KEY_F9 298 + +#define GLFW_KEY_F10 299 + +#define GLFW_KEY_F11 300 + +#define GLFW_KEY_F12 301 + +#define GLFW_KEY_F13 302 + +#define GLFW_KEY_F14 303 + +#define GLFW_KEY_F15 304 + +#define GLFW_KEY_F16 305 + +#define GLFW_KEY_F17 306 + +#define GLFW_KEY_F18 307 + +#define GLFW_KEY_F19 308 + +#define GLFW_KEY_F20 309 + +#define GLFW_KEY_F21 310 + +#define GLFW_KEY_F22 311 + +#define GLFW_KEY_F23 312 + +#define GLFW_KEY_F24 313 + +#define GLFW_KEY_F25 314 + +#define GLFW_KEY_KP_0 320 + +#define GLFW_KEY_KP_1 321 + +#define GLFW_KEY_KP_2 322 + +#define GLFW_KEY_KP_3 323 + +#define GLFW_KEY_KP_4 324 + +#define GLFW_KEY_KP_5 325 + +#define GLFW_KEY_KP_6 326 + +#define GLFW_KEY_KP_7 327 + +#define GLFW_KEY_KP_8 328 + +#define GLFW_KEY_KP_9 329 + +#define GLFW_KEY_KP_DECIMAL 330 + +#define GLFW_KEY_KP_DIVIDE 331 + +#define GLFW_KEY_KP_MULTIPLY 332 + +#define GLFW_KEY_KP_SUBTRACT 333 + +#define GLFW_KEY_KP_ADD 334 + +#define GLFW_KEY_KP_ENTER 335 + +#define GLFW_KEY_KP_EQUAL 336 + +#define GLFW_KEY_LEFT_SHIFT 340 + +#define GLFW_KEY_LEFT_CONTROL 341 + +#define GLFW_KEY_LEFT_ALT 342 + +#define GLFW_KEY_LEFT_SUPER 343 + +#define GLFW_KEY_RIGHT_SHIFT 344 + +#define GLFW_KEY_RIGHT_CONTROL 345 + +#define GLFW_KEY_RIGHT_ALT 346 + +#define GLFW_KEY_RIGHT_SUPER 347 + +#define GLFW_KEY_MENU 348 + +#define GLFW_KEY_LAST GLFW_KEY_MENU + +#define GLFW_MOD_SHIFT 0x0001 +#define GLFW_MOD_CONTROL 0x0002 +#define GLFW_MOD_ALT 0x0004 + +#define GLFW_RELEASE 0 +#define GLFW_PRESS 1 +#define GLFW_REPEAT 2 + +namespace RC::Input +{ + auto GLFW3InputSource::begin_frame() -> void + { + } + + auto GLFW3InputSource::receive_input(int key, int action, int mods) -> void + { + auto modifier_keys = ModifierKeys{}; + modifier_keys |= (mods & GLFW_MOD_CONTROL ? ModifierKey::CONTROL : ModifierKey::MOD_KEY_START_OF_ENUM); + modifier_keys |= (mods & GLFW_MOD_SHIFT ? ModifierKey::SHIFT : ModifierKey::MOD_KEY_START_OF_ENUM); + modifier_keys |= (mods & GLFW_MOD_ALT ? ModifierKey::ALT : ModifierKey::MOD_KEY_START_OF_ENUM); + if (action == GLFW_PRESS) + { + // translate key + Key input = m_translate_key[key]; + if (input != RESERVED_START_OF_ENUM) + { + push_input_event({input, modifier_keys}); + } + } + } + + auto GLFW3InputSource::end_frame() -> void + { + } + + auto GLFW3InputSource::is_available() -> bool + { + return true; + } + + GLFW3InputSource::GLFW3InputSource() + { + // init to 0 + for (int i = 0; i < 512; ++i) + { + m_translate_key[i] = static_cast(0); + } + // Alphanumeric keys + for (int key = GLFW_KEY_A; key <= GLFW_KEY_Z; ++key) + { + m_translate_key[key] = static_cast(key); + } + for (int key = GLFW_KEY_0; key <= GLFW_KEY_9; ++key) + { + m_translate_key[key] = static_cast(key + 22); + } + + // Symbol keys + m_translate_key[GLFW_KEY_SPACE] = Key::SPACE; + m_translate_key[GLFW_KEY_APOSTROPHE] = Key::OEM_SEVEN; + m_translate_key[GLFW_KEY_COMMA] = Key::OEM_COMMA; + m_translate_key[GLFW_KEY_MINUS] = Key::OEM_MINUS; + m_translate_key[GLFW_KEY_PERIOD] = Key::OEM_PERIOD; + m_translate_key[GLFW_KEY_SLASH] = Key::OEM_TWO; + m_translate_key[GLFW_KEY_SEMICOLON] = Key::OEM_ONE; + m_translate_key[GLFW_KEY_EQUAL] = Key::OEM_PLUS; + m_translate_key[GLFW_KEY_LEFT_BRACKET] = Key::OEM_FOUR; + m_translate_key[GLFW_KEY_BACKSLASH] = Key::OEM_FIVE; + m_translate_key[GLFW_KEY_RIGHT_BRACKET] = Key::OEM_SIX; + m_translate_key[GLFW_KEY_GRAVE_ACCENT] = Key::OEM_THREE; + + // Control keys + m_translate_key[GLFW_KEY_ESCAPE] = Key::ESCAPE; + m_translate_key[GLFW_KEY_ENTER] = Key::RETURN; + m_translate_key[GLFW_KEY_TAB] = Key::TAB; + m_translate_key[GLFW_KEY_BACKSPACE] = Key::BACKSPACE; + m_translate_key[GLFW_KEY_INSERT] = Key::INS; + m_translate_key[GLFW_KEY_DELETE] = Key::DEL; + m_translate_key[GLFW_KEY_RIGHT] = Key::RIGHT_ARROW; + m_translate_key[GLFW_KEY_LEFT] = Key::LEFT_ARROW; + m_translate_key[GLFW_KEY_DOWN] = Key::DOWN_ARROW; + m_translate_key[GLFW_KEY_UP] = Key::UP_ARROW; + m_translate_key[GLFW_KEY_PAGE_UP] = Key::PAGE_UP; + m_translate_key[GLFW_KEY_PAGE_DOWN] = Key::PAGE_DOWN; + m_translate_key[GLFW_KEY_HOME] = Key::HOME; + m_translate_key[GLFW_KEY_END] = Key::END; + m_translate_key[GLFW_KEY_CAPS_LOCK] = Key::CAPS_LOCK; + m_translate_key[GLFW_KEY_SCROLL_LOCK] = Key::SCROLL_LOCK; + m_translate_key[GLFW_KEY_NUM_LOCK] = Key::NUM_LOCK; + m_translate_key[GLFW_KEY_PRINT_SCREEN] = Key::PRINT_SCREEN; + m_translate_key[GLFW_KEY_PAUSE] = Key::PAUSE; + + // Function keys + for (int key = GLFW_KEY_F1; key <= GLFW_KEY_F25; ++key) + { + m_translate_key[key] = static_cast(key + 111); + } + + // Numeric keypad + for (int key = GLFW_KEY_KP_0; key <= GLFW_KEY_KP_EQUAL; ++key) + { + m_translate_key[key] = static_cast(key + 208); + } + } +} // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/src/Platform/QueueInputSource.cpp b/deps/first/Input/src/Platform/QueueInputSource.cpp new file mode 100644 index 000000000..c524a7200 --- /dev/null +++ b/deps/first/Input/src/Platform/QueueInputSource.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include + +namespace RC::Input +{ + auto QueueInputSource::push_input_event(const InputEvent& event) -> void + { + if (m_activated) + { + m_input_queue.push(event); + Output::send(STR("QueueInputSource::push_input_event: {}"), (int)event.key); + } + } + + // even if not activated, we still consume the remaining events in the queue + std::vector& QueueInputSource::process_event(Handler* handler) + { + m_input_events.clear(); + + auto event = m_input_queue.pop(); + auto& key_set = handler->get_subscribed_keys(); + while (event) + { + Output::send(STR("QueueInputSource::reveive key event: {}"), (int)event->key); + if (key_set[event->key]) + { + m_input_events.emplace_back(*event); + } + event = m_input_queue.pop(); + } + + if (!m_activated) + { + m_input_events.clear(); + } + + if (!handler->get_allow_input()) + { + m_input_events.clear(); + } + + return m_input_events; + } +}; // namespace RC::Input diff --git a/deps/first/Input/src/Platform/Win32AsyncInputSource.cpp b/deps/first/Input/src/Platform/Win32AsyncInputSource.cpp new file mode 100644 index 000000000..7169ae55f --- /dev/null +++ b/deps/first/Input/src/Platform/Win32AsyncInputSource.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#define NOMINMAX +#include + +namespace RC::Input +{ + + auto Win32AsyncInputSource::is_program_focused() -> bool + { + HWND hwnd = GetForegroundWindow(); + if (!hwnd) return false; + wchar_t current_window_class_name[MAX_PATH]; + if (!GetClassNameW(hwnd, current_window_class_name, MAX_PATH)) return false; + for (const auto& active_window_class : m_active_window_classes) + { + if (wcscmp(current_window_class_name, active_window_class) == 0) + { + return true; + } + } + return false; + } + + std::vector& Win32AsyncInputSource::process_event(Handler* handler) + { + m_input_events.clear(); + + if (!is_program_focused()) + { + return m_input_events; + } + + if (!m_activated) + { + return m_input_events; + } + + bool skip_this_frame = !handler->get_allow_input(); + + if (m_any_keys_are_down) + { + skip_this_frame = true; + } + + bool any_keys_are_down = false; + + // Check if any modifier keys are down + ModifierKeys modifier_keys{}; + for (auto& [modifier_key, key_is_down] : m_modifier_keys_down) + { + if (GetAsyncKeyState(modifier_key)) + { + modifier_keys |= modifier_key; + key_is_down = true; + } + else + { + key_is_down = false; + } + } + + auto& subscribed_keys = handler->get_subscribed_keys(); + + for (int key = 0; key < max_keys; ++key) + { + if (subscribed_keys[key]) + { + auto keyed = GetAsyncKeyState(key); + if (keyed && !m_key_down[key]) + { + any_keys_are_down = true; + m_key_down[key] = true; + m_input_events.emplace_back(InputEvent{static_cast(key), modifier_keys}); + } + else if (!keyed && m_key_down[key]) + { + m_key_down[key] = false; + } + } + } + + if (any_keys_are_down) + { + m_any_keys_are_down = true; + } + else + { + m_any_keys_are_down = false; + } + + if (skip_this_frame) + { + m_input_events.clear(); + } + return m_input_events; + } + +} // namespace RC::Input diff --git a/deps/first/Input/src/PlatformInit.cpp b/deps/first/Input/src/PlatformInit.cpp new file mode 100644 index 000000000..8bc45515a --- /dev/null +++ b/deps/first/Input/src/PlatformInit.cpp @@ -0,0 +1,12 @@ +#include +#include +#include + +namespace RC::Input +{ + auto Handler::init() -> void + { + register_input_source(std::make_shared(L"ConsoleWindowClass", L"UnrealWindow")); + register_input_source(std::make_shared()); + } +} // namespace RC::Input \ No newline at end of file diff --git a/deps/first/Input/xmake.lua b/deps/first/Input/xmake.lua index 2411a4804..c84cf2352 100644 --- a/deps/first/Input/xmake.lua +++ b/deps/first/Input/xmake.lua @@ -5,8 +5,21 @@ target(projectName) set_languages("cxx23") set_exceptions("cxx") add_rules("ue4ss.dependency") + add_options("ue4ssInput") add_includedirs("include", { public = true }) add_headerfiles("include/**.hpp") - add_files("src/**.cpp") \ No newline at end of file + if get_config("ue4ssInput") then + add_files("src/**.cpp|Platform/**.cpp") + end + + add_deps("DynamicOutput") + + if is_plat("windows") then + if get_config("ue4ssInput") then + add_files("src/Platform/Win32AsyncInputSource.cpp") + add_files("src/Platform/GLFW3InputSource.cpp") + add_files("src/Platform/QueueInputSource.cpp") + end + end diff --git a/xmake.lua b/xmake.lua index fc7c028b7..96a994447 100644 --- a/xmake.lua +++ b/xmake.lua @@ -65,3 +65,11 @@ option("ue4ssCross") set_values("msvc-wine", "None") set_description("Which cross-compiling toolchain to use", "msvc-wine", "None") + +option("ue4ssInput") + set_default(true) + set_showmenu(true) + + add_defines("HAS_INPUT") + + set_description("Enable the input system.")