diff --git a/.gitignore b/.gitignore index 37b2094..5419594 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ temp.errors # MacOS .DS_Store + +# Linux +debug.log diff --git a/include/gamepad/api.hpp b/include/gamepad/api.hpp index 835385a..e0fca39 100644 --- a/include/gamepad/api.hpp +++ b/include/gamepad/api.hpp @@ -1,4 +1,5 @@ #pragma once #include "gamepad/event_handler.hpp" // IWYU pragma: export -#include "gamepad/controller.hpp" // IWYU pragma: export \ No newline at end of file +#include "gamepad/gamepad.hpp" // IWYU pragma: export +#include "gamepad/screens/alertScreen.hpp" // IWYU pragma: export diff --git a/include/gamepad/controller.hpp b/include/gamepad/gamepad.hpp similarity index 53% rename from include/gamepad/controller.hpp rename to include/gamepad/gamepad.hpp index cd1aebc..5037870 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/gamepad.hpp @@ -1,8 +1,12 @@ #pragma once #include "pros/misc.h" +#include "screens/defaultScreen.hpp" +#include #include - +#include +#include +#include "screens/abstractScreen.hpp" #include "button.hpp" #include "pros/misc.hpp" @@ -26,6 +30,63 @@ class Gamepad { * */ void update(); + /** + * @brief Add a screen to the sceen update loop that can update the controller's screen + * + * @param screen the `AbstractScreen` to add to the screen queue + * + * @b Example: + * @code {.cpp} + * // initialize the alerts screen so we can have alerts on the controller + * std::shared_ptr alerts = std::make_shared(); + * + * gamepad::master.add_screen(alerts); + */ + void add_screen(std::shared_ptr screen); + /** + * @brief print a line to the console like pros (low priority) + * + * @param line the line number to print the string on (0-2) + * @param str the string to print onto the controller (\n to go to the next line) + * + * @b Example: + * @code {.cpp} + * gamepad::master.print_line(1, "This will print on the middle line"); + * gamepad::master.print_line(0, "this will print\n\naround the middle line"); + */ + void print_line(uint8_t line, std::string str); + /** + * @brief clears all lines on the controller, similar to the pros function (low priority) + * + * @b Example: + * @code {.cpp} + * // clears the whole screen on the controller + * gamepad::master.clear() + */ + void clear(); + /** + * @brief clears the specific line on the controller, similar to the pros function clear_line (low priority) + * + * @param line the line to clear (0-2) + * + * @b Example: + * @code {.cpp} + * // clears the center line on the controller + * gamepad::master.clear(1); + */ + void clear(uint8_t line); + /** + * makes the controller rumble like pros (low priority) + * + * @param rumble_pattern A string consisting of the characters '.', '-', and ' ', where dots are short rumbles, + * dashes are long rumbles, and spaces are pauses. Maximum supported length is 8 characters. + * + * @b Example: + * @code {.cpp} + * // rumbles in the folllowing pattern: short, pause, long, short short + * gamepad::master.rumble(". -.."); + */ + void rumble(std::string rumble_pattern); /** * @brief Get the state of a button on the controller. * @@ -74,8 +135,7 @@ class Gamepad { /// The partner controller, same as @ref gamepad::partner static Gamepad partner; private: - Gamepad(pros::controller_id_e_t id) - : controller(id) {} + Gamepad(pros::controller_id_e_t id); Button m_L1 {}, m_L2 {}, m_R1 {}, m_R2 {}, m_Up {}, m_Down {}, m_Left {}, m_Right {}, m_X {}, m_B {}, m_Y {}, m_A {}; @@ -92,7 +152,20 @@ class Gamepad { static std::string unique_name(); static Button Gamepad::*button_to_ptr(pros::controller_digital_e_t button); void updateButton(pros::controller_digital_e_t button_id); + + void updateScreens(); + + std::shared_ptr defaultScreen = std::make_shared(); + std::vector> screens = {}; + ScreenBuffer currentScreen = {}; + ScreenBuffer nextBuffer = {}; pros::Controller controller; + + uint8_t last_printed_line = 0; + uint32_t last_print_time = 0; + uint32_t last_update_time = 0; + bool screenCleared = false; + pros::Mutex mut {}; }; inline Gamepad Gamepad::master {pros::E_CONTROLLER_MASTER}; diff --git a/include/gamepad/screens/abstractScreen.hpp b/include/gamepad/screens/abstractScreen.hpp new file mode 100644 index 0000000..1c23b21 --- /dev/null +++ b/include/gamepad/screens/abstractScreen.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "pros/misc.h" + +namespace gamepad { + +/** + * @brief type for conveying a full screen with the first 3 being the lines + * of text on the controller screen and the last being a rumble pattern + */ +typedef std::array, 4> ScreenBuffer; + +/** + * @brief The abstract class for interacting with the controller screen + * + */ +class AbstractScreen { + public: + AbstractScreen(uint32_t priority) + : priority(priority) {} + + /** + * @brief runs every time the controller's update function is called + * use this if you need to update somthing regardless of if there is an + * available slot in the screen + * + * @param delta_time the time since the last update in milliseconds + */ + virtual void update(uint32_t delta_time) {} + + /** + * @brief runs if there is an empty line that is available to print + * + * @param visible_lines a set that contains the line numbers of all lines that + * are empty and available for printing + * + * @returns a the lines to be printed, any lines that are not available will be ignored + */ + virtual ScreenBuffer get_screen(std::set visible_lines) = 0; + + /** + * @brief a function where button events are pushed, use this to handle button events. + * + * @param button_events a set of the button events that happened this update + */ + virtual void handle_events(std::set button_events) {} + + /** + * @brief returns the priority of the screen + * + * @important it is not reccomended to override this function + */ + uint32_t get_priority() { return this->priority; } + protected: + const uint32_t priority; +}; + +} // namespace gamepad diff --git a/include/gamepad/screens/alertScreen.hpp b/include/gamepad/screens/alertScreen.hpp new file mode 100644 index 0000000..694771b --- /dev/null +++ b/include/gamepad/screens/alertScreen.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include "abstractScreen.hpp" +#include "pros/rtos.hpp" +#include "gamepad/screens/abstractScreen.hpp" + +namespace gamepad { + +/** + * @brief a screen that sends alerts to the controller, duration of alerts can be customized + * + * @note priority: UINT32_MAX - 100 + */ +class AlertScreen : public AbstractScreen { + public: + AlertScreen() + : AbstractScreen(UINT32_MAX - 100) {} + + /** + * @brief updates the alert loop + * + * @param delta_time the time since the last update + */ + void update(uint32_t delta_time); + + /** + * @brief return the next alert to print if there is space for it on the screen + * + * @param visible_lines a set that contains the line numbers of all lines that + * are empty and available for printing + * + * @returns a the lines to be printed, any lines that are not available will be ignored + */ + ScreenBuffer get_screen(std::set visible_lines); + + /** + * @brief add an alert to the alert queue, to be printed as soon as there is an available space + * + * @param line the line number to print the alert at (0-2) + * @param strs the string to print on the controller, "\n" to go to the next line + * lines that go over 2 will be cropped out + * @param duration how long the alert should persist on the screen + * @param rumble A string consisting of the characters '.', '-', and ' ', where dots are short rumbles, + * dashes are long rumbles, and spaces are pauses. Maximum supported length is 8 characters. + */ + void add_alerts(uint8_t line, std::string strs, uint32_t duration, std::string rumble = ""); + private: + struct AlertBuffer { + ScreenBuffer screen; + uint32_t duration; + }; + + std::deque screen_buffer {}; + std::optional screen_contents {}; + uint32_t line_set_time = 0; + pros::Mutex mut {}; +}; + +} // namespace gamepad diff --git a/include/gamepad/screens/defaultScreen.hpp b/include/gamepad/screens/defaultScreen.hpp new file mode 100644 index 0000000..d2a26c8 --- /dev/null +++ b/include/gamepad/screens/defaultScreen.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "gamepad/screens/abstractScreen.hpp" +#include "pros/rtos.hpp" + +namespace gamepad { + +/** + * @brief A basic screen that allows basic prints, similar to pros controller api + * + * @note The gamepad class has wrappers around this class + * @note priority: 1 + */ +class DefaultScreen : public AbstractScreen { + public: + DefaultScreen() + : AbstractScreen(1) {} + + /** + * @brief returns any lines that have space to print on the controller + * + * @param visible_lines a set that contains the line numbers of all lines that + * are empty and available for printing + * + * @returns a the lines to be printed, any lines that are not available will be ignored + */ + ScreenBuffer get_screen(std::set visible_lines); + + /** + * @brief print a line to the console like pros + * + * @param line the line number to print the string on (0-2) + * @param str the string to print onto the controller (\n to go to the next line) + */ + void print_line(uint8_t line, std::string str); + + /** + * makes the controller rumble like pros + * + * @param rumble_pattern A string consisting of the characters '.', '-', and ' ', where dots are short rumbles, + * dashes are long rumbles, and spaces are pauses. Maximum supported length is 8 characters. + */ + void rumble(std::string rumble_pattern); + private: + ScreenBuffer currentBuffer {}; + pros::Mutex mut {}; +}; + +} // namespace gamepad diff --git a/src/gamepad/button.cpp b/src/gamepad/button.cpp index 703c246..9c0bb98 100644 --- a/src/gamepad/button.cpp +++ b/src/gamepad/button.cpp @@ -2,7 +2,6 @@ #include "gamepad/todo.hpp" #include "pros/rtos.hpp" #include -#include namespace gamepad { void Button::set_long_press_threshold(uint32_t threshold) const { this->long_press_threshold = threshold; } @@ -88,4 +87,4 @@ void Button::update(const bool is_held) { if (this->falling_edge) this->time_released = 0; this->last_update_time = pros::millis(); } -} // namespace gamepad \ No newline at end of file +} // namespace gamepad diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp deleted file mode 100644 index fff75f2..0000000 --- a/src/gamepad/controller.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "gamepad/button.hpp" -#include "gamepad/controller.hpp" -#include "gamepad/todo.hpp" -#include "pros/misc.h" -#include - -namespace gamepad { -void Gamepad::updateButton(pros::controller_digital_e_t button_id) { - Button Gamepad::*button = Gamepad::button_to_ptr(button_id); - bool is_held = this->controller.get_digital(button_id); - (this->*button).update(is_held); -} - -void Gamepad::update() { - for (int i = pros::E_CONTROLLER_DIGITAL_L1; i <= pros::E_CONTROLLER_DIGITAL_A; ++i) { - this->updateButton(static_cast(i)); - } - - this->m_LeftX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_X); - this->m_LeftY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y); - this->m_RightX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_X); - this->m_RightY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y); -} - -const Button& Gamepad::operator[](pros::controller_digital_e_t button) { return this->*Gamepad::button_to_ptr(button); } - -float Gamepad::operator[](pros::controller_analog_e_t axis) { - switch (axis) { - case pros::E_CONTROLLER_ANALOG_LEFT_X: return this->LeftX; - case pros::E_CONTROLLER_ANALOG_LEFT_Y: return this->LeftY; - case pros::E_CONTROLLER_ANALOG_RIGHT_X: return this->RightX; - case pros::E_CONTROLLER_ANALOG_RIGHT_Y: return this->RightY; - default: - TODO("add error logging") - errno = EINVAL; - return 0; - } -} - -std::string Gamepad::unique_name() { - static std::atomic i = 0; - return std::to_string(i++) + "_internal"; -} - -Button Gamepad::*Gamepad::button_to_ptr(pros::controller_digital_e_t button) { - switch (button) { - case pros::E_CONTROLLER_DIGITAL_L1: return &Gamepad::m_L1; - case pros::E_CONTROLLER_DIGITAL_L2: return &Gamepad::m_L2; - case pros::E_CONTROLLER_DIGITAL_R1: return &Gamepad::m_R1; - case pros::E_CONTROLLER_DIGITAL_R2: return &Gamepad::m_R2; - case pros::E_CONTROLLER_DIGITAL_UP: return &Gamepad::m_Up; - case pros::E_CONTROLLER_DIGITAL_DOWN: return &Gamepad::m_Down; - case pros::E_CONTROLLER_DIGITAL_LEFT: return &Gamepad::m_Left; - case pros::E_CONTROLLER_DIGITAL_RIGHT: return &Gamepad::m_Right; - case pros::E_CONTROLLER_DIGITAL_X: return &Gamepad::m_X; - case pros::E_CONTROLLER_DIGITAL_B: return &Gamepad::m_B; - case pros::E_CONTROLLER_DIGITAL_Y: return &Gamepad::m_Y; - case pros::E_CONTROLLER_DIGITAL_A: return &Gamepad::m_A; - default: - TODO("add error logging") - errno = EINVAL; - return &Gamepad::Fake; - } -} -} // namespace gamepad diff --git a/src/gamepad/gamepad.cpp b/src/gamepad/gamepad.cpp new file mode 100644 index 0000000..7053d8b --- /dev/null +++ b/src/gamepad/gamepad.cpp @@ -0,0 +1,181 @@ +#include "gamepad/gamepad.hpp" +#include "gamepad/todo.hpp" +#include "pros/misc.h" +#include "pros/rtos.hpp" +#include "screens/abstractScreen.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gamepad { +Gamepad::Gamepad(pros::controller_id_e_t id) + : controller(id) { + this->add_screen(defaultScreen); +} + +void Gamepad::updateButton(pros::controller_digital_e_t button_id) { + Button Gamepad::*button = Gamepad::button_to_ptr(button_id); + bool is_held = this->controller.get_digital(button_id); + (this->*button).update(is_held); +} + +void Gamepad::updateScreens() { + // Lock Mutexes for Thread Safety + std::lock_guard guard_scheduling(this->mut); + + // Disable screen updates if the controller is disconnected + if (!this->controller.is_connected()) { + if (this->screenCleared) { + this->nextBuffer = std::move(this->currentScreen); + this->currentScreen = {}; + this->screenCleared = false; + } + return; + } + + // Clear current screen and reset last update time on reconnect + if (this->controller.is_connected() && !screenCleared) { + this->currentScreen = {}; + this->last_update_time = pros::millis(); + } + + // Get new button presses + std::set buttonUpdates; + for (int i = pros::E_CONTROLLER_DIGITAL_L1; i <= pros::E_CONTROLLER_DIGITAL_A; ++i) { + if ((this->*this->button_to_ptr(static_cast(i))).rising_edge) { + buttonUpdates.emplace(static_cast(i)); + } + } + + // Update all screens, and send new button presses, also note deltatime + for (std::shared_ptr screen : this->screens) { + screen->update(pros::millis() - this->last_update_time); + screen->handle_events(buttonUpdates); + } + this->last_update_time = pros::millis(); + + // Check if enough time has passed for the Gamepad to poll for updates + if (pros::millis() - this->last_print_time <= 50) return; + + for (std::shared_ptr screen : this->screens) { + // get all lines that arent being used by a higher priority screen + std::set visible_lines; + for (uint8_t j = 0; j < 4; j++) + if (!this->nextBuffer[j].has_value()) visible_lines.emplace(j); + + // get the buffer of the next lower priority screen and set it to be printed + ScreenBuffer buffer = screen->get_screen(visible_lines); + for (uint8_t j = 0; j < 4; j++) + if (buffer[j].has_value() && !buffer[j]->empty() && !nextBuffer[j].has_value()) + nextBuffer[j] = std::move(buffer[j]); + } + + for (int i = 0; i < 4; i++) { + // start from the line thats after the line thats been set so we dont get stuck setting the first line + int line = (this->last_printed_line + i) % 4; + + // theres nothing on this line so we can skip it + if (!this->nextBuffer[line].has_value()) continue; + + if (!this->screenCleared && line != 3) { + this->controller.clear(); + this->screenCleared = true; + this->currentScreen = {}; + this->last_print_time = pros::millis(); + return; + } + + // text on screen is the same as last frame's text so no use updating + if (this->currentScreen[line] == this->nextBuffer[line] && line != 3) { + this->nextBuffer[line] = std::nullopt; + continue; + } + + // print to screen or rumble + if (line == 3) this->controller.rumble(this->nextBuffer[line].value_or("").c_str()); + else this->controller.set_text(line, 0, this->nextBuffer[line].value_or("") + std::string(40, ' ')); + if (line != 3) this->currentScreen[line] = std::move(this->nextBuffer[line]); + this->nextBuffer[line] = std::nullopt; + this->last_printed_line = line; + this->last_print_time = pros::millis(); + return; + } +} + +void Gamepad::update() { + for (int i = pros::E_CONTROLLER_DIGITAL_L1; i <= pros::E_CONTROLLER_DIGITAL_A; ++i) { + this->updateButton(static_cast(i)); + } + + this->m_LeftX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_X); + this->m_LeftY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y); + this->m_RightX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_X); + this->m_RightY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y); + + this->updateScreens(); +} + +void Gamepad::add_screen(std::shared_ptr screen) { + uint32_t last = UINT32_MAX; + uint32_t pos = 0; + for (pos = 0; pos < this->screens.size(); pos++) { + if (this->screens[pos]->get_priority() < screen->get_priority() && last >= screen->get_priority()) break; + last = this->screens[pos]->get_priority(); + } + this->screens.emplace(this->screens.begin() + pos, screen); +} + +void Gamepad::print_line(uint8_t line, std::string str) { this->defaultScreen->print_line(line, str); } + +void Gamepad::clear() { this->defaultScreen->print_line(0, " \n \n "); } + +void Gamepad::clear(uint8_t line) { this->defaultScreen->print_line(line, " "); } + +void Gamepad::rumble(std::string rumble_pattern) { this->defaultScreen->rumble(rumble_pattern); } + +const Button& Gamepad::operator[](pros::controller_digital_e_t button) { return this->*Gamepad::button_to_ptr(button); } + +float Gamepad::operator[](pros::controller_analog_e_t axis) { + switch (axis) { + case pros::E_CONTROLLER_ANALOG_LEFT_X: return this->LeftX; + case pros::E_CONTROLLER_ANALOG_LEFT_Y: return this->LeftY; + case pros::E_CONTROLLER_ANALOG_RIGHT_X: return this->RightX; + case pros::E_CONTROLLER_ANALOG_RIGHT_Y: return this->RightY; + default: + TODO("add error logging") + errno = EINVAL; + return 0; + } +} + +std::string Gamepad::unique_name() { + static std::atomic i = 0; + return std::to_string(i++) + "_internal"; +} + +Button Gamepad::*Gamepad::button_to_ptr(pros::controller_digital_e_t button) { + switch (button) { + case pros::E_CONTROLLER_DIGITAL_L1: return &Gamepad::m_L1; + case pros::E_CONTROLLER_DIGITAL_L2: return &Gamepad::m_L2; + case pros::E_CONTROLLER_DIGITAL_R1: return &Gamepad::m_R1; + case pros::E_CONTROLLER_DIGITAL_R2: return &Gamepad::m_R2; + case pros::E_CONTROLLER_DIGITAL_UP: return &Gamepad::m_Up; + case pros::E_CONTROLLER_DIGITAL_DOWN: return &Gamepad::m_Down; + case pros::E_CONTROLLER_DIGITAL_LEFT: return &Gamepad::m_Left; + case pros::E_CONTROLLER_DIGITAL_RIGHT: return &Gamepad::m_Right; + case pros::E_CONTROLLER_DIGITAL_X: return &Gamepad::m_X; + case pros::E_CONTROLLER_DIGITAL_B: return &Gamepad::m_B; + case pros::E_CONTROLLER_DIGITAL_Y: return &Gamepad::m_Y; + case pros::E_CONTROLLER_DIGITAL_A: return &Gamepad::m_A; + default: + TODO("add error logging") + errno = EINVAL; + return &Gamepad::Fake; + } +} +} // namespace gamepad diff --git a/src/gamepad/screens/alertScreen.cpp b/src/gamepad/screens/alertScreen.cpp new file mode 100644 index 0000000..4a45394 --- /dev/null +++ b/src/gamepad/screens/alertScreen.cpp @@ -0,0 +1,61 @@ +#include "gamepad/screens/alertScreen.hpp" +#include "gamepad/todo.hpp" +#include "pros/rtos.hpp" +#include +#include +#include +#include +#include + +namespace gamepad { + +ScreenBuffer AlertScreen::get_screen(std::set visible_lines) { + std::lock_guard guard(this->mut); + if (this->screen_contents.has_value()) { + this->screen_contents->screen.at(3) = std::nullopt; + return this->screen_contents->screen; + } + if (this->screen_buffer.size() < 1) return ScreenBuffer(); + + for (uint8_t i = 0; i < 4; i++) { + if (!this->screen_buffer[0].screen[i].has_value()) continue; + if (this->screen_buffer[0].screen[i].has_value() && !visible_lines.contains(i)) return ScreenBuffer(); + } + this->screen_contents = std::move(this->screen_buffer[0]); + this->screen_buffer.pop_front(); + this->line_set_time = pros::millis(); + return this->screen_contents->screen; +} + +void AlertScreen::update(uint32_t delta_time) { + std::lock_guard guard(this->mut); + if (pros::millis() - this->line_set_time >= this->screen_contents->duration) this->screen_contents = std::nullopt; +} + +void AlertScreen::add_alerts(uint8_t line, std::string str, uint32_t duration, std::string rumble) { + TODO("change handling for off screen lines") + if (line > 2) std::exit(1); + + TODO("warn instead of throw error if there are too many lines") + if (std::ranges::count(str, '\n') > 2) std::exit(1); + + std::vector strs(3, ""); + std::stringstream ss(str); + + // split string by newlines but only take the first 3 lines + for (int i = line; i < 3; i++) { + if (!std::getline(ss, strs[i], '\n')) break; + } + + ScreenBuffer buffer; + + if (strs[0] != "") buffer[0] = std::move(strs[0]); + if (strs[1] != "") buffer[1] = std::move(strs[1]); + if (strs[2] != "") buffer[2] = std::move(strs[2]); + if (rumble != "") buffer[3] = std::move(rumble); + + std::lock_guard guard(this->mut); + this->screen_buffer.push_back({buffer, duration}); +} + +} // namespace gamepad diff --git a/src/gamepad/screens/defaultScreen.cpp b/src/gamepad/screens/defaultScreen.cpp new file mode 100644 index 0000000..1afd516 --- /dev/null +++ b/src/gamepad/screens/defaultScreen.cpp @@ -0,0 +1,57 @@ +#include "gamepad/screens/defaultScreen.hpp" +#include "gamepad/screens/abstractScreen.hpp" +#include "gamepad/todo.hpp" +#include +#include +#include +#include +#include + +namespace gamepad { + +ScreenBuffer DefaultScreen::get_screen(std::set visible_lines) { + ScreenBuffer output; + const std::lock_guard guard(this->mut); + + for (auto i = visible_lines.begin(); i != visible_lines.end(); ++i) { + output[*i] = std::move(this->currentBuffer[*i]); + this->currentBuffer[*i] = std::nullopt; + } + return output; +} + +void DefaultScreen::print_line(uint8_t line, std::string str) { + TODO("change handling for off screen lines") + if (line > 2) std::exit(1); + + const std::lock_guard guard(this->mut); + + if (str.find('\n') != std::string::npos) { + TODO("warn instead of throw error if there are too many lines") + if (std::ranges::count(str, '\n') > 2) std::exit(1); + + std::vector strs(3); + std::stringstream ss(str); + + for (int i = line; i < 3; i++) { + if (!std::getline(ss, strs[i], '\n')) break; + } + + for (uint8_t l = 0; l < 3; l++) { + if (!strs[l].empty()) this->currentBuffer[l] = (strs[l]); + } + return; + } + + this->currentBuffer[line] = std::move(str); +} + +void DefaultScreen::rumble(std::string rumble_pattern) { + TODO("change handling for too long rumble patterns") + if (rumble_pattern.size() > 8) std::exit(1); + + std::lock_guard guard(this->mut); + this->currentBuffer[3] = std::move(rumble_pattern); +} + +} // namespace gamepad \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b0bef76..2a7081d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,14 @@ #include "main.h" #include "gamepad/api.hpp" -#include "gamepad/controller.hpp" +#include "gamepad/gamepad.hpp" +#include "gamepad/screens/alertScreen.hpp" #include "pros/rtos.hpp" -#include #include +#include +#include + +// initialize the alerts screen so we can have alerts on the controller +std::shared_ptr alerts = std::make_shared(); /** * Runs initialization code. This occurs as soon as the program is started. @@ -11,47 +16,29 @@ * All other competition modes are blocked by initialize; it is recommended * to keep execution time for this mode under a few seconds. */ +void initialize() { + // VERY IMPORTANT, this actually adds the alerts screen to the gamepad + // it wouldnt work without this line + gamepad::master.add_screen(alerts); -uint32_t last_repeat_press_time = pros::millis(); - -void downPress1() { printf("Down Press!\n"); } - -void upRelease1() { printf("Up Release!\n"); } - -void leftLongPress1() { printf("Left Long Press!\n"); } - -void leftShortRelease1() { printf("Left Short Release!\n"); } + // When the A button is pressed, schedule an alert that spans all three + // lines, lasts 3 seconds and rumbles in a long-short-long pattern + gamepad::master.A.onPress("alert", []() { + alerts->add_alerts(0, "a very\nimportant alert\nat " + std::to_string(pros::millis()) + " ms", 3000, "-.-"); + }); -void leftLongRelease1() { printf("Left Long Release!\n"); } + // Normally print a string on the first and third line without overriding + // the second line when the B button is pressed + gamepad::master.B.onPress( + "print02", []() { gamepad::master.print_line(0, "the time is\n\n" + std::to_string(pros::millis()) + " ms"); }); -void aPress1() { - last_repeat_press_time = pros::millis(); - printf("A Pressed!\n"); -} + // rumbles 3 times for a short duration when the X button is pressed + gamepad::master.X.onPress("rumble", []() { gamepad::master.rumble("..."); }); -void aRepeatPress1() { - printf("A Repeat Pressed %ims after last\n", pros::millis() - last_repeat_press_time); - last_repeat_press_time = pros::millis(); -} - -void initialize() { - // We can register functions to run when buttons are pressed - gamepad::master.Down.onPress("downPress1", downPress1); - // ...or when they're released - gamepad::master.Up.onRelease("downRelease1", upRelease1); - // There's also the longPress event - gamepad::master.Left.onLongPress("leftLongPress1", leftLongPress1); - // We can have two or even more functions on one button, - // just remember to give them different names - gamepad::master.Left.onShortRelease("leftShortRelease", leftShortRelease1); - gamepad::master.Left.onLongRelease("leftLongRelease", leftLongRelease1); - // We also have the repeat press event, where we can adjust the timing - gamepad::master.A.set_long_press_threshold(1000); // in ms - gamepad::master.A.set_repeat_cooldown(100); // in ms - gamepad::master.A.onPress("aStartPress", aPress1); - gamepad::master.A.onRepeatPress("aRepeatPress", aRepeatPress1); - // And we can use lambda's too - gamepad::master.X.onShortRelease("xShortRelease1", []() { printf("X Short Release!\n"); }); + // when the Y button is pressed and held the text should show up, and when + // the button is released it should be cleared + gamepad::master.Y.onPress("print1", []() { gamepad::master.print_line(1, "this should be cleared"); }); + gamepad::master.Y.onRelease("clear1", []() { gamepad::master.clear(1); }); } /** @@ -99,17 +86,10 @@ void autonomous() {} * task, not resume it from where it left off. */ void opcontrol() { - pros::MotorGroup left_mg({1, -2, 3}); // Creates a motor group with forwards ports 1 & 3 and reversed port 2 - pros::MotorGroup right_mg({-4, 5, -6}); // Creates a motor group with forwards port 4 and reversed ports 4 & 6 - while (true) { // Remember to ALWAYS call update at the start of your while loop! gamepad::master.update(); - // We'll use the arcade control scheme - int dir = gamepad::master.LeftY; // Gets amount forward/backward from left joystick - int turn = gamepad::master.RightX; // Gets the turn left/right from right joystick - left_mg.move(dir - turn); // Sets left motor voltage - right_mg.move(dir + turn); // Sets right motor voltage + pros::delay(25); // Wait for 25 ms, then update the motor values again } -} \ No newline at end of file +}