Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0ff228e
add KeyboardEventHandler class
jw098 Sep 11, 2025
f9c8bdb
KeyboardManager inherits KeyboardEventHandler
jw098 Sep 11, 2025
a23f5cd
ProController inherits KeyboardEventHandler::KeyboardListener
jw098 Sep 11, 2025
b486ace
add KeyboardEventHandler::report_keyboard_state_changed
jw098 Sep 11, 2025
c329ead
KeyboardListener prints whenever it detects a send or stop command.
jw098 Sep 12, 2025
1b6249b
Callbacks triggered at the beginning of function, instead of at the end.
jw098 Sep 12, 2025
c35db68
add new program RecordKeyboardController
jw098 Sep 13, 2025
6502b3f
KeyboardEventHandler: use ControllerState instead of ProControllerState
jw098 Sep 13, 2025
717aea6
add timestamp to Keyboard event callbacks
jw098 Sep 13, 2025
0ed91cf
RecordKeyboardController inherits KeyboardEventHandler::KeyboardListener
jw098 Sep 13, 2025
5cf9bfb
added AbstractController::monitor_keyboard_events(), which allows us …
jw098 Sep 13, 2025
1da91fc
add ControllerState::serialize_state(). Save the serialized state alo…
jw098 Sep 14, 2025
4be7d67
rename AbstractController::monitor_keyboard_events() to add_keyboard_…
jw098 Sep 14, 2025
d17908d
convert m_controller_history to json
jw098 Sep 15, 2025
0c41a02
add enum class ControllerCategory
jw098 Sep 16, 2025
34a5b09
add ControllerCategory to controller recording Json
jw098 Sep 16, 2025
3db52be
add MODE and JSON_FILE_NAME to UI
jw098 Sep 16, 2025
bd84b79
initial implementation of json_to_cpp_code_pro_controller, with place…
jw098 Sep 16, 2025
d40e0ea
add string_to_button() and string_to_dpad(). Finish implementation of…
jw098 Sep 17, 2025
dda406d
change CONTROLLER_CATEGORY_STRINGS from global variable to using lazy…
jw098 Sep 17, 2025
72aa64a
implemented json_to_pbf_actions()
jw098 Sep 17, 2025
fbe33c6
run different actions depending on the non_neutral_controller_field.
jw098 Sep 17, 2025
d9bb7c6
implement pbf_controller_state() for joycons
jw098 Sep 17, 2025
42bdf0a
refactor json_to_pbf_actions_pro_controller() and json_to_cpp_code_pr…
jw098 Sep 17, 2025
6b7b102
add record/playback functionality for joycons
jw098 Sep 18, 2025
69af1b8
output the generated C++ code to a text file. when recording, throw a…
jw098 Sep 18, 2025
1c847b3
fix build
jw098 Sep 18, 2025
a594779
fix build
jw098 Sep 18, 2025
146d41d
fix build
jw098 Sep 18, 2025
7c436da
minor UI updates
jw098 Sep 18, 2025
35fefea
update time duration calculation to avoid drift due to rounding error.
jw098 Sep 19, 2025
3ed31c1
minior UI change
jw098 Sep 19, 2025
ef97a19
rename ControllerCategory to ControllerClass. adjust when timestamp i…
jw098 Sep 20, 2025
80a67b9
fix typo
jw098 Sep 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Common/Cpp/Json/JsonObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions Common/Cpp/StringTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <stdint.h>
#include <stdexcept>
#include <vector>
#include "StringTools.h"

namespace PokemonAutomation{
Expand Down Expand Up @@ -48,6 +49,21 @@ size_t to_size_t(const std::string& str){
}
}

std::vector<std::string> split(const std::string& str, const std::string& delimiter) {
std::vector<std::string> 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;
}


}
}
2 changes: 2 additions & 0 deletions Common/Cpp/StringTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> split(const std::string& str, const std::string& delimiter);

}
}
#endif
18 changes: 2 additions & 16 deletions Common/Qt/Options/BoxFloatWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include "Common/Cpp/StringTools.h"
#include "BoxFloatWidget.h"

//#include <iostream>
Expand All @@ -24,21 +25,6 @@ ConfigWidget* BoxFloatOption::make_QtWidget(QWidget& parent){



std::vector<std::string> split(const std::string& str, const std::string& delimiter) {
std::vector<std::string> 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(){
Expand Down Expand Up @@ -167,7 +153,7 @@ BoxFloatWidget::BoxFloatWidget(QWidget& parent, BoxFloatOption& value)
connect(
m_array, &QLineEdit::editingFinished,
this, [this](){
std::vector<std::string> all_coords = split(m_array->text().toStdString(), ", ");
std::vector<std::string> all_coords = StringTools::split(m_array->text().toStdString(), ", ");
if (all_coords.size() != 4){
return;
}
Expand Down
4 changes: 4 additions & 0 deletions SerialPrograms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions SerialPrograms/Source/Controllers/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,6 +20,7 @@ namespace PokemonAutomation{
class RecursiveThrottler;
enum class ControllerType;
enum class ControllerPerformanceClass;
enum class ControllerClass;



Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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){};
};


Expand Down Expand Up @@ -294,6 +300,7 @@ class ControllerContext final : public CancellableScope{
};


using AbstractControllerContext = ControllerContext<AbstractController>;

using AbstractControllerContext = ControllerContext<AbstractController>;

Expand Down
10 changes: 10 additions & 0 deletions SerialPrograms/Source/Controllers/ControllerTypeStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ const EnumStringMap<ControllerType> CONTROLLER_TYPE_STRINGS{
{ControllerType::NintendoSwitch2_RightJoycon, "NS2: Right Joycon"},
};

const EnumStringMap<ControllerClass>& CONTROLLER_CLASS_STRINGS(){
static EnumStringMap<ControllerClass> database{
{ControllerClass::NONE, "none"},
{ControllerClass::LEFT_JOYCON, "left-joycon"},
{ControllerClass::RIGHT_JOYCON, "right-joycon"},
{ControllerClass::PRO_CONTROLLER, "pro-controller"},
};
return database;
}




Expand Down
1 change: 1 addition & 0 deletions SerialPrograms/Source/Controllers/ControllerTypeStrings.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace PokemonAutomation{

extern const EnumStringMap<ControllerInterface> CONTROLLER_INTERFACE_STRINGS;
extern const EnumStringMap<ControllerType> CONTROLLER_TYPE_STRINGS;
const EnumStringMap<ControllerClass>& CONTROLLER_CLASS_STRINGS();



Expand Down
8 changes: 8 additions & 0 deletions SerialPrograms/Source/Controllers/ControllerTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ enum class ControllerPerformanceClass{
SysbotBase,
};

enum class ControllerClass{
NONE,
LEFT_JOYCON,
RIGHT_JOYCON,
PRO_CONTROLLER,
KEYBOARD,
};




Expand Down
Original file line number Diff line number Diff line change
@@ -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<KeyboardListener> 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);
}


}
Original file line number Diff line number Diff line change
@@ -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<Data> m_data;

LifetimeSanitizer m_lifetime_sanitizer;
};

}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
#include <thread>
#include <condition_variable>
#include <Qt>
#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;
Expand All @@ -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();};
};


Expand Down Expand Up @@ -86,7 +90,7 @@ class KeyboardInputController{


template <typename StateType, typename DeltaType>
class KeyboardManager : public KeyboardInputController{
class KeyboardManager : public KeyboardInputController, public KeyboardEventHandler{
public:
KeyboardManager(Logger& logger, AbstractController& controller)
: KeyboardInputController(logger, true)
Expand Down Expand Up @@ -126,7 +130,9 @@ class KeyboardManager : public KeyboardInputController{
if (m_controller == nullptr){
return;
}
WallClock time_stamp = current_time();
m_controller->cancel_all_commands();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the timestamp read to just before this line since it's closer to the actual time being sent to the Switch. Having it before the lock could mean an arbitrary delay.

report_keyboard_command_stopped(time_stamp);
}
virtual void replace_on_next_command() override{
WriteSpinLock lg(m_lock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
);


}
Expand Down
Loading