|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include "pros/misc.h" |
| 4 | +#include <cstdint> |
| 5 | +#include <functional> |
| 6 | +#include <string> |
| 7 | +#ifndef PROS_USE_SIMPLE_NAMES |
| 8 | +#define PROS_USE_SIMPLE_NAMES |
| 9 | +#endif |
| 10 | + |
| 11 | +#include "event_handler.hpp" |
| 12 | +#include "pros/misc.hpp" |
| 13 | +#include "pros/rtos.hpp" |
| 14 | + |
| 15 | +namespace Gamepad { |
| 16 | + |
| 17 | +enum EventType { |
| 18 | + ON_PRESS, |
| 19 | + ON_LONG_PRESS, |
| 20 | + ON_RELEASE, |
| 21 | + ON_SHORT_RELEASE, |
| 22 | +}; |
| 23 | + |
| 24 | +class Button { |
| 25 | + friend class Controller; |
| 26 | + public: |
| 27 | + /// Whether the button has just been pressed |
| 28 | + bool rising_edge = false; |
| 29 | + /// Whether the button has just been released |
| 30 | + bool falling_edge = false; |
| 31 | + /// Whether the button is currently held down |
| 32 | + bool is_pressed = false; |
| 33 | + /// How long the button has been held down |
| 34 | + uint32_t time_held = 0; |
| 35 | + /// How long the button has been released |
| 36 | + uint32_t time_released = 0; |
| 37 | + /// How long the threshold should be for the longPress and shortRelease events |
| 38 | + uint32_t long_press_threshold = 500; |
| 39 | + /** |
| 40 | + * @brief Register a function to run when the button is pressed. |
| 41 | + * |
| 42 | + * @param listenerName The name of the listener, this must be a unique name |
| 43 | + * @param func The function to run when the button is pressed, the function MUST NOT block |
| 44 | + * @return true The listener was successfully registered |
| 45 | + * @return false The listener was not successfully registered (there is already a listener with this name) |
| 46 | + * |
| 47 | + * @b Example: |
| 48 | + * @code {.cpp} |
| 49 | + * // Use a function... |
| 50 | + * Gamepad::master.Down.onPress("downPress1", downPress1); |
| 51 | + * // ...or a lambda |
| 52 | + * Gamepad::master.Up.onPress("upPress1", []() { std::cout << "I was pressed!" << std::endl; }); |
| 53 | + * @endcode |
| 54 | + */ |
| 55 | + bool onPress(std::string listenerName, std::function<void(void)> func) const; |
| 56 | + /** |
| 57 | + * @brief Register a function to run when the button is long pressed. |
| 58 | + * |
| 59 | + * By default, onLongPress will fire when the button has been held down for |
| 60 | + * 500ms or more, this threshold can be adjusted by changing long_press_threshold. |
| 61 | + * |
| 62 | + * @warning When using this event along with onPress, both the onPress |
| 63 | + * and onlongPress listeners may fire together. |
| 64 | + * |
| 65 | + * @param listenerName The name of the listener, this must be a unique name |
| 66 | + * @param func The function to run when the button is long pressed, the function MUST NOT block |
| 67 | + * @return true The listener was successfully registered |
| 68 | + * @return false The listener was not successfully registered (there is already a listener with this name) |
| 69 | + * |
| 70 | + * @b Example: |
| 71 | + * @code {.cpp} |
| 72 | + * // Use a function... |
| 73 | + * Gamepad::master.Left.onLongPress("fireCatapult", fireCatapult); |
| 74 | + * // ...or a lambda |
| 75 | + * Gamepad::master.Right.onLongPress("print_right", []() { std::cout << "Right button was long pressed!" << |
| 76 | + * std::endl; }); |
| 77 | + * @endcode |
| 78 | + */ |
| 79 | + bool onLongPress(std::string listenerName, std::function<void(void)> func) const; |
| 80 | + /** |
| 81 | + * @brief Register a function to run when the button is released. |
| 82 | + * |
| 83 | + * @param listenerName The name of the listener, this must be a unique name |
| 84 | + * @param func The function to run when the button is released, the function MUST NOT block |
| 85 | + * @return true The listener was successfully registered |
| 86 | + * @return false The listener was not successfully registered (there is already a listener with this name) |
| 87 | + * |
| 88 | + * @b Example: |
| 89 | + * @code {.cpp} |
| 90 | + * // Use a function... |
| 91 | + * Gamepad::master.X.onRelease("stopFlywheel", stopFlywheel); |
| 92 | + * // ...or a lambda |
| 93 | + * Gamepad::master.Y.onRelease("stopIntake", []() { intake.move(0); }); |
| 94 | + * @endcode |
| 95 | + */ |
| 96 | + bool onRelease(std::string listenerName, std::function<void(void)> func) const; |
| 97 | + /** |
| 98 | + * @brief Register a function to run when the button is short released. |
| 99 | + * |
| 100 | + * By default, shortRelease will fire when the button has been released before 500ms, this threshold can be |
| 101 | + * adjusted by changing long_press_threshold. |
| 102 | + * |
| 103 | + * @note This event will most likely be used along with the longPress event. |
| 104 | + * |
| 105 | + * @param listenerName The name of the listener, this must be a unique name |
| 106 | + * @param func The function to run when the button is short released, the function MUST NOT block |
| 107 | + * @return true The listener was successfully registered |
| 108 | + * @return false The listener was not successfully registered (there is already a listener with this name) |
| 109 | + * |
| 110 | + * @b Example: |
| 111 | + * @code {.cpp} |
| 112 | + * // Use a function... |
| 113 | + * Gamepad::master.A.onShortRelease("raiseLiftOneLevel", raiseLiftOneLevel); |
| 114 | + * // ...or a lambda |
| 115 | + * Gamepad::master.B.onShortRelease("intakeOnePicce", []() { intake.move_relative(600, 100); }); |
| 116 | + * @endcode |
| 117 | + */ |
| 118 | + bool onShortRelease(std::string listenerName, std::function<void(void)> func) const; |
| 119 | + /** |
| 120 | + * @brief Register a function to run for a given event. |
| 121 | + * |
| 122 | + * @param event Which event to register the listener on. |
| 123 | + * @param listenerName The name of the listener, this must be a unique name |
| 124 | + * @param func The function to run for the given event, the function MUST NOT block |
| 125 | + * @return true The listener was successfully registered |
| 126 | + * @return false The listener was not successfully registered (there is already a listener with this name) |
| 127 | + * |
| 128 | + * @b Example: |
| 129 | + * @code {.cpp} |
| 130 | + * // Use a function... |
| 131 | + * Gamepad::master.L1.addListener(Gamepad::ON_PRESS, "start_spin", startSpin); |
| 132 | + * // ...or a lambda |
| 133 | + * Gamepad::master.L1.addListener(Gamepad::ON_RELEASE, "stop_spin", []() { motor1.brake(); }); |
| 134 | + * @endcode |
| 135 | + */ |
| 136 | + bool addListener(EventType event, std::string listenerName, std::function<void(void)> func) const; |
| 137 | + /** |
| 138 | + * @brief Removes a listener from the button |
| 139 | + * @warning Usage of this function is discouraged. |
| 140 | + * |
| 141 | + * @param listenerName The name of the listener to remove |
| 142 | + * @return true The specified listener was successfully removed |
| 143 | + * @return false The specified listener could not be removed |
| 144 | + * |
| 145 | + * @b Example: |
| 146 | + * @code {.cpp} |
| 147 | + * // Add an event listener... |
| 148 | + * Gamepad::master.L1.addListener(Gamepad::ON_PRESS, "do_something", doSomething); |
| 149 | + * // ...and now get rid of it |
| 150 | + * Gamepad::master.L1.removeListener("do_something"); |
| 151 | + * @endcode |
| 152 | + */ |
| 153 | + bool removeListener(std::string listenerName) const; |
| 154 | + |
| 155 | + /** |
| 156 | + * @brief Returns a value indicating whether the button is currently being held. |
| 157 | + * |
| 158 | + * @return true The button is currently pressed |
| 159 | + * @return false The button is not currently pressed |
| 160 | + */ |
| 161 | + explicit operator bool() const { return is_pressed; } |
| 162 | + private: |
| 163 | + /** |
| 164 | + * @brief Updates the button and runs any event handlers, if necessary |
| 165 | + * |
| 166 | + * @param is_held Whether or not the button is currently held down |
| 167 | + */ |
| 168 | + void update(bool is_held); |
| 169 | + /// he last time the update function was called |
| 170 | + uint32_t last_update_time = pros::millis(); |
| 171 | + /// The last time the long press event was fired |
| 172 | + uint32_t last_long_press_time = 0; |
| 173 | + mutable _impl::EventHandler<std::string> onPressEvent {}; |
| 174 | + mutable _impl::EventHandler<std::string> onLongPressEvent {}; |
| 175 | + mutable _impl::EventHandler<std::string> onReleaseEvent {}; |
| 176 | + mutable _impl::EventHandler<std::string> onShortReleaseEvent {}; |
| 177 | +}; |
| 178 | + |
| 179 | +class Controller { |
| 180 | + public: |
| 181 | + /** |
| 182 | + * @brief Updates the state of the gamepad (all joysticks and buttons), and also runs |
| 183 | + * any registered listeners. |
| 184 | + * |
| 185 | + * @note This function should be called at the beginning of every loop iteration. |
| 186 | + * |
| 187 | + * @b Example: |
| 188 | + * @code {.cpp} |
| 189 | + * while (true) { |
| 190 | + * Gamepad::master.update(); |
| 191 | + * // do robot control stuff here... |
| 192 | + * pros::delay(25); |
| 193 | + * } |
| 194 | + * @endcode |
| 195 | + * |
| 196 | + */ |
| 197 | + void update(); |
| 198 | + /** |
| 199 | + * @brief Get the state of a button on the controller. |
| 200 | + * |
| 201 | + * @param button Which button to return |
| 202 | + * |
| 203 | + * @b Example: |
| 204 | + * @code {.cpp} |
| 205 | + * if(Gamepad::master[DIGITAL_L1]) { |
| 206 | + * // do something here... |
| 207 | + * } |
| 208 | + * @endcode |
| 209 | + * |
| 210 | + */ |
| 211 | + const Button& operator[](pros::controller_digital_e_t button); |
| 212 | + /** |
| 213 | + * @brief Get the value of a joystick axis on the controller. |
| 214 | + * |
| 215 | + * @param joystick Which joystick axis to return |
| 216 | + * |
| 217 | + * @b Example: |
| 218 | + * @code {.cpp} |
| 219 | + * // control a motor with a joystick |
| 220 | + * intake.move(Gamepad::master[ANALOG_RIGHT_Y]); |
| 221 | + * @endcode |
| 222 | + * |
| 223 | + */ |
| 224 | + float operator[](pros::controller_analog_e_t joystick); |
| 225 | + const Button& L1 {m_L1}; |
| 226 | + const Button& L2 {m_L2}; |
| 227 | + const Button& R1 {m_R1}; |
| 228 | + const Button& R2 {m_R2}; |
| 229 | + const Button& Up {m_Up}; |
| 230 | + const Button& Down {m_Down}; |
| 231 | + const Button& Left {m_Left}; |
| 232 | + const Button& Right {m_Right}; |
| 233 | + const Button& X {m_X}; |
| 234 | + const Button& B {m_B}; |
| 235 | + const Button& Y {m_Y}; |
| 236 | + const Button& A {m_A}; |
| 237 | + const float& LeftX = m_LeftX; |
| 238 | + const float& LeftY = m_LeftY; |
| 239 | + const float& RightX = m_RightX; |
| 240 | + const float& RightY = m_RightY; |
| 241 | + /// The master controller, same as @ref Gamepad::master |
| 242 | + static Controller master; |
| 243 | + /// The partner controller, same as @ref Gamepad::partner |
| 244 | + static Controller partner; |
| 245 | + private: |
| 246 | + Controller(pros::controller_id_e_t id) |
| 247 | + : controller(id) {} |
| 248 | + |
| 249 | + Button m_L1 {}, m_L2 {}, m_R1 {}, m_R2 {}, m_Up {}, m_Down {}, m_Left {}, m_Right {}, m_X {}, m_B {}, m_Y {}, |
| 250 | + m_A {}; |
| 251 | + float m_LeftX = 0, m_LeftY = 0, m_RightX = 0, m_RightY = 0; |
| 252 | + Button Fake {}; |
| 253 | + /** |
| 254 | + * @brief Gets a unique name for a listener that will not conflict with user listener names. |
| 255 | + * |
| 256 | + * @important: when using the function, you must register the listener by |
| 257 | + * directly calling add_listener on the EventHandler, do NOT use onPress/addListener,etc. |
| 258 | + * |
| 259 | + * @return std::string A unique listener name |
| 260 | + */ |
| 261 | + static std::string unique_name(); |
| 262 | + static Button Controller::*button_to_ptr(pros::controller_digital_e_t button); |
| 263 | + void updateButton(pros::controller_digital_e_t button_id); |
| 264 | + pros::Controller controller; |
| 265 | +}; |
| 266 | + |
| 267 | +inline Controller Controller::master {pros::E_CONTROLLER_MASTER}; |
| 268 | +inline Controller Controller::partner {pros::E_CONTROLLER_PARTNER}; |
| 269 | +/// The master controller |
| 270 | +inline Controller& master = Controller::master; |
| 271 | +/// The partner controller |
| 272 | +inline Controller& partner = Controller::partner; |
| 273 | + |
| 274 | +} // namespace Gamepad |
0 commit comments