diff --git a/Common/Cpp/Json/JsonObject.h b/Common/Cpp/Json/JsonObject.h index 4fe2765c65..428f945e45 100644 --- a/Common/Cpp/Json/JsonObject.h +++ b/Common/Cpp/Json/JsonObject.h @@ -18,6 +18,13 @@ class JsonObject{ JsonObject() = default; JsonObject(JsonObject&& x) = default; JsonObject& operator=(JsonObject&& x) = default; + + bool operator==(const JsonObject& x){ // TODO: implement == properly. For JsonValue as well. + return dump() == x.dump(); + } + bool operator!=(const JsonObject& x){ + return !(*this == *this); + } private: // Private to avoid accidental copying. JsonObject(const JsonObject& x); diff --git a/Common/Cpp/StringTools.cpp b/Common/Cpp/StringTools.cpp index dea0b877e0..7393412b86 100644 --- a/Common/Cpp/StringTools.cpp +++ b/Common/Cpp/StringTools.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "StringTools.h" namespace PokemonAutomation{ @@ -48,6 +49,21 @@ size_t to_size_t(const std::string& str){ } } +std::vector split(const std::string& str, const std::string& delimiter) { + std::vector tokens; + size_t start = 0; + size_t end = str.find(delimiter); + + while (end != std::string::npos) { + tokens.push_back(str.substr(start, end - start)); + start = end + delimiter.length(); + end = str.find(delimiter, start); + } + + tokens.push_back(str.substr(start)); + return tokens; +} + } } diff --git a/Common/Cpp/StringTools.h b/Common/Cpp/StringTools.h index fbd0fccfce..a5c42f1520 100644 --- a/Common/Cpp/StringTools.h +++ b/Common/Cpp/StringTools.h @@ -21,6 +21,8 @@ std::string strip(const std::string& str); // Parse str to size_t. Return SIZE_MAX if parsing fails size_t to_size_t(const std::string& str); +std::vector split(const std::string& str, const std::string& delimiter); + } } #endif diff --git a/Common/Qt/Options/BoxFloatWidget.cpp b/Common/Qt/Options/BoxFloatWidget.cpp index 7e48aea3cb..9db2f7ba40 100644 --- a/Common/Qt/Options/BoxFloatWidget.cpp +++ b/Common/Qt/Options/BoxFloatWidget.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "Common/Cpp/StringTools.h" #include "BoxFloatWidget.h" //#include @@ -24,21 +25,6 @@ ConfigWidget* BoxFloatOption::make_QtWidget(QWidget& parent){ -std::vector split(const std::string& str, const std::string& delimiter) { - std::vector tokens; - size_t start = 0; - size_t end = str.find(delimiter); - - while (end != std::string::npos) { - tokens.push_back(str.substr(start, end - start)); - start = end + delimiter.length(); - end = str.find(delimiter, start); - } - - tokens.push_back(str.substr(start)); - return tokens; -} - BoxFloatWidget::~BoxFloatWidget(){ @@ -167,7 +153,7 @@ BoxFloatWidget::BoxFloatWidget(QWidget& parent, BoxFloatOption& value) connect( m_array, &QLineEdit::editingFinished, this, [this](){ - std::vector all_coords = split(m_array->text().toStdString(), ", "); + std::vector all_coords = StringTools::split(m_array->text().toStdString(), ", "); if (all_coords.size() != 4){ return; } diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 3870f6d30d..c3fb86e5f9 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -701,6 +701,8 @@ file(GLOB MAIN_SOURCES Source/Controllers/JoystickTools.h Source/Controllers/KeyboardInput/GlobalQtKeyMap.cpp Source/Controllers/KeyboardInput/GlobalQtKeyMap.h + Source/Controllers/KeyboardInput/KeyboardEventHandler.cpp + Source/Controllers/KeyboardInput/KeyboardEventHandler.h Source/Controllers/KeyboardInput/KeyboardInput.cpp Source/Controllers/KeyboardInput/KeyboardInput.h Source/Controllers/KeyboardInput/KeyboardStateTracker.cpp @@ -1119,6 +1121,8 @@ file(GLOB MAIN_SOURCES Source/NintendoSwitch/Programs/NintendoSwitch_PreventSleep.h Source/NintendoSwitch/Programs/NintendoSwitch_PushJoySticks.cpp Source/NintendoSwitch/Programs/NintendoSwitch_PushJoySticks.h + Source/NintendoSwitch/Programs/NintendoSwitch_RecordKeyboardController.cpp + Source/NintendoSwitch/Programs/NintendoSwitch_RecordKeyboardController.h Source/NintendoSwitch/Programs/NintendoSwitch_SnapshotDumper.cpp Source/NintendoSwitch/Programs/NintendoSwitch_SnapshotDumper.h Source/NintendoSwitch/Programs/NintendoSwitch_SwitchViewer.cpp diff --git a/SerialPrograms/Source/Controllers/Controller.h b/SerialPrograms/Source/Controllers/Controller.h index b411423ba9..fdde10270e 100644 --- a/SerialPrograms/Source/Controllers/Controller.h +++ b/SerialPrograms/Source/Controllers/Controller.h @@ -10,6 +10,7 @@ #include "Common/Compiler.h" #include "Common/Cpp/AbstractLogger.h" #include "Common/Cpp/Time.h" +#include "Controllers/KeyboardInput/KeyboardEventHandler.h" #include "Common/Cpp/CancellableScope.h" class QKeyEvent; @@ -19,6 +20,7 @@ namespace PokemonAutomation{ class RecursiveThrottler; enum class ControllerType; enum class ControllerPerformanceClass; +enum class ControllerClass; @@ -64,6 +66,7 @@ class AbstractController{ virtual const char* name() = 0; virtual ControllerType controller_type() const = 0; + virtual ControllerClass controller_class() const = 0; virtual ControllerPerformanceClass performance_class() const = 0; // If the controller is polled at a fixed interval, this is that interval. @@ -203,6 +206,9 @@ class AbstractController{ virtual void keyboard_release_all(){} virtual void keyboard_press(const QKeyEvent& event){} virtual void keyboard_release(const QKeyEvent& event){} + + virtual void add_keyboard_listener(KeyboardEventHandler::KeyboardListener& keyboard_listener){}; + virtual void remove_keyboard_listener(KeyboardEventHandler::KeyboardListener& keyboard_listener){}; }; @@ -294,6 +300,7 @@ class ControllerContext final : public CancellableScope{ }; +using AbstractControllerContext = ControllerContext; using AbstractControllerContext = ControllerContext; diff --git a/SerialPrograms/Source/Controllers/ControllerTypeStrings.cpp b/SerialPrograms/Source/Controllers/ControllerTypeStrings.cpp index 32dabd8766..6db5d5a748 100644 --- a/SerialPrograms/Source/Controllers/ControllerTypeStrings.cpp +++ b/SerialPrograms/Source/Controllers/ControllerTypeStrings.cpp @@ -32,6 +32,16 @@ const EnumStringMap CONTROLLER_TYPE_STRINGS{ {ControllerType::NintendoSwitch2_RightJoycon, "NS2: Right Joycon"}, }; +const EnumStringMap& CONTROLLER_CLASS_STRINGS(){ + static EnumStringMap database{ + {ControllerClass::NONE, "none"}, + {ControllerClass::LEFT_JOYCON, "left-joycon"}, + {ControllerClass::RIGHT_JOYCON, "right-joycon"}, + {ControllerClass::PRO_CONTROLLER, "pro-controller"}, + }; + return database; +} + diff --git a/SerialPrograms/Source/Controllers/ControllerTypeStrings.h b/SerialPrograms/Source/Controllers/ControllerTypeStrings.h index 49ea1b08b9..e92bc84e75 100644 --- a/SerialPrograms/Source/Controllers/ControllerTypeStrings.h +++ b/SerialPrograms/Source/Controllers/ControllerTypeStrings.h @@ -16,6 +16,7 @@ namespace PokemonAutomation{ extern const EnumStringMap CONTROLLER_INTERFACE_STRINGS; extern const EnumStringMap CONTROLLER_TYPE_STRINGS; +const EnumStringMap& CONTROLLER_CLASS_STRINGS(); diff --git a/SerialPrograms/Source/Controllers/ControllerTypes.h b/SerialPrograms/Source/Controllers/ControllerTypes.h index b1beab859e..0478e6ee6f 100644 --- a/SerialPrograms/Source/Controllers/ControllerTypes.h +++ b/SerialPrograms/Source/Controllers/ControllerTypes.h @@ -44,6 +44,14 @@ enum class ControllerPerformanceClass{ SysbotBase, }; +enum class ControllerClass{ + NONE, + LEFT_JOYCON, + RIGHT_JOYCON, + PRO_CONTROLLER, + KEYBOARD, +}; + diff --git a/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardEventHandler.cpp b/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardEventHandler.cpp new file mode 100644 index 0000000000..5b91e9b325 --- /dev/null +++ b/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardEventHandler.cpp @@ -0,0 +1,47 @@ +/* Keyboard Event Handler + * + */ +#include "Common/Cpp/ListenerSet.h" +#include "Common/Cpp/Containers/Pimpl.tpp" +#include "Controllers/KeyboardInput/KeyboardInput.h" +#include "KeyboardEventHandler.h" + +namespace PokemonAutomation{ + + + +struct KeyboardEventHandler::Data{ + ListenerSet m_listeners; +}; + +KeyboardEventHandler::KeyboardEventHandler() + : m_data(CONSTRUCT_TOKEN) +{} + +KeyboardEventHandler::~KeyboardEventHandler() = default; + +void KeyboardEventHandler::add_listener(KeyboardListener& listener){ + auto scope = m_lifetime_sanitizer.check_scope(); + Data& data = *m_data; + data.m_listeners.add(listener); +} +void KeyboardEventHandler::remove_listener(KeyboardListener& listener){ + auto scope = m_lifetime_sanitizer.check_scope(); + Data& data = *m_data; + data.m_listeners.remove(listener); +} + +void KeyboardEventHandler::report_keyboard_command_sent(WallClock time_stamp, const ControllerState& state){ + auto scope = m_lifetime_sanitizer.check_scope(); + Data& data = *m_data; + data.m_listeners.run_method_unique(&KeyboardListener::on_keyboard_command_sent, time_stamp, state); +} + +void KeyboardEventHandler::report_keyboard_command_stopped(WallClock time_stamp){ + auto scope = m_lifetime_sanitizer.check_scope(); + Data& data = *m_data; + data.m_listeners.run_method_unique(&KeyboardListener::on_keyboard_command_stopped, time_stamp); +} + + +} \ No newline at end of file diff --git a/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardEventHandler.h b/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardEventHandler.h new file mode 100644 index 0000000000..a4f68728b4 --- /dev/null +++ b/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardEventHandler.h @@ -0,0 +1,46 @@ +/* Keyboard Event Handler + * + * From: https://github.com/PokemonAutomation/ + * + */ + + +#ifndef PokemonAutomation_Controllers_KeyboardEventHandler_H +#define PokemonAutomation_Controllers_KeyboardEventHandler_H + +#include "Common/Cpp/Containers/Pimpl.h" +#include "Common/Cpp/Time.h" +#include "Common/Cpp/LifetimeSanitizer.h" + +namespace PokemonAutomation{ + +class ControllerState; // forward declaration to avoid circular dependency + +class KeyboardEventHandler{ +public: + KeyboardEventHandler(); + virtual ~KeyboardEventHandler(); + + struct KeyboardListener{ + virtual void on_keyboard_command_sent(WallClock time_stamp, const ControllerState& state) = 0; + virtual void on_keyboard_command_stopped(WallClock time_stamp) = 0; + }; + void add_listener(KeyboardListener& listener); + void remove_listener(KeyboardListener& listener); + +protected: + // Report that the keyboard state has changed. This will be pushed to + // all listeners. + void report_keyboard_command_sent(WallClock time_stamp, const ControllerState& state); + + void report_keyboard_command_stopped(WallClock time_stamp); + +private: + struct Data; + Pimpl m_data; + + LifetimeSanitizer m_lifetime_sanitizer; +}; + +} +#endif \ No newline at end of file diff --git a/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.h b/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.h index 553f9eea90..1e396ceb31 100644 --- a/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.h +++ b/SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.h @@ -11,9 +11,11 @@ #include #include #include +#include "Common/Cpp/Json/JsonObject.h" #include "Common/Cpp/Concurrency/SpinLock.h" #include "Controllers/Controller.h" #include "Controllers/KeyboardInput/GlobalQtKeyMap.h" +#include "KeyboardEventHandler.h" #include "KeyboardStateTracker.h" class QKeyEvent; @@ -36,6 +38,8 @@ class ControllerState{ virtual bool operator!=(const ControllerState& x) const{ return !(*this == x); } virtual bool is_neutral() const = 0; + + virtual JsonObject serialize_state() const {return JsonObject();}; }; @@ -86,7 +90,7 @@ class KeyboardInputController{ template -class KeyboardManager : public KeyboardInputController{ +class KeyboardManager : public KeyboardInputController, public KeyboardEventHandler{ public: KeyboardManager(Logger& logger, AbstractController& controller) : KeyboardInputController(logger, true) @@ -126,7 +130,9 @@ class KeyboardManager : public KeyboardInputController{ if (m_controller == nullptr){ return; } + WallClock time_stamp = current_time(); m_controller->cancel_all_commands(); + report_keyboard_command_stopped(time_stamp); } virtual void replace_on_next_command() override{ WriteSpinLock lg(m_lock); diff --git a/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_SerialPABotBase.h b/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_SerialPABotBase.h index 6710814615..c386d2c14a 100644 --- a/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_SerialPABotBase.h +++ b/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_SerialPABotBase.h @@ -53,6 +53,9 @@ class SerialPABotBase_Keyboard final : virtual ControllerType controller_type() const override{ return ControllerType::HID_Keyboard; } + virtual ControllerClass controller_class() const override{ + return ControllerClass::KEYBOARD; + } virtual ControllerPerformanceClass performance_class() const override{ return ControllerPerformanceClass::SerialPABotBase_Wired; } diff --git a/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.cpp b/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.cpp index 4622fdebf4..a894469561 100644 --- a/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.cpp @@ -118,6 +118,19 @@ void pbf_mash_button(JoyconContext& context, Button button, Milliseconds duratio ssf_mash1_button(context, button, duration); } +void pbf_controller_state( + JoyconContext& context, + Button button, + uint8_t x, uint8_t y, + Milliseconds duration +){ + context->issue_full_controller_state( + &context, + button, + x, y, + duration + ); +} diff --git a/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h b/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h index 72cfbef749..ed607a457d 100644 --- a/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h +++ b/SerialPrograms/Source/NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h @@ -121,7 +121,12 @@ void pbf_press_button (JoyconContext& context, Button button, Milliseconds hol void pbf_move_joystick (JoyconContext& context, uint8_t x, uint8_t y, Milliseconds hold, Milliseconds release); void pbf_mash_button (JoyconContext& context, Button button, Milliseconds duration); - +void pbf_controller_state( + JoyconContext& context, + Button button, + uint8_t x, uint8_t y, + Milliseconds duration +); } diff --git a/SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ControllerButtons.cpp b/SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ControllerButtons.cpp index d0f7d29b70..02a55feecf 100644 --- a/SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ControllerButtons.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ControllerButtons.cpp @@ -5,59 +5,156 @@ */ #include "NintendoSwitch_ControllerButtons.h" +#include "Common/Cpp/EnumStringMap.h" +#include "Common/Cpp/StringTools.h" namespace PokemonAutomation{ namespace NintendoSwitch{ +const EnumStringMap