Skip to content

Commit

Permalink
Merge pull request #15 from LemLib/main
Browse files Browse the repository at this point in the history
🔖 v0.1.0 Release!
  • Loading branch information
ion098 authored Sep 10, 2024
2 parents 30e329f + 99897dc commit 970fed3
Show file tree
Hide file tree
Showing 12 changed files with 609 additions and 41 deletions.
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"postCreateCommand": "chmod 777 .devcontainer/postCreateCommand.bash && .devcontainer/postCreateCommand.bash"
}
"postCreateCommand": "chmod 777 .devcontainer/postCreateCommand.bash && .devcontainer/postCreateCommand.bash && source ~/.bashrc"
}
1 change: 0 additions & 1 deletion .devcontainer/postCreateCommand.bash
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ curl https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/10.3
tar -xjvf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2
echo "PATH=$PATH:/home/codespace/gcc-arm-none-eabi-10.3-2021.10/bin" >> ~/.bashrc
popd
source ~/.bashrc
21 changes: 21 additions & 0 deletions .github/workflows/clang-format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: test-clang-format

on:
push:
branches:
- '*'
pull_request:
workflow_dispatch:


jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: DoozyX/clang-format-lint-action@v0.18
with:
source: './src/gamepad ./include/gamepad'
extensions: 'hpp,cpp'
clangFormatVersion: 18
2 changes: 1 addition & 1 deletion .github/workflows/pr-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
const old_marker = new RegExp(marker.replace("\r\n", "\r?\n")).exec(old_body)?.[0] ?? marker;
body = old_body.split(old_marker)[0] + marker + body;
body = (old_body ?? "").split(old_marker)[0] + marker + body;
await github.request('PATCH /repos/{owner}/{repo}/pulls/{pull_number}', {
owner: owner,
repo: repo,
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pros-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ jobs:

steps:
- uses: actions/checkout@v4
- uses: LemLib/pros-build@v1.0.0
- uses: LemLib/pros-build@v2.0.2
with:
library-path: gamepad
4 changes: 4 additions & 0 deletions include/gamepad/api.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

#include "gamepad/event_handler.hpp" // IWYU pragma: export
#include "gamepad/controller.hpp" // IWYU pragma: export
274 changes: 274 additions & 0 deletions include/gamepad/controller.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
#pragma once

#include "pros/misc.h"
#include <cstdint>
#include <functional>
#include <string>
#ifndef PROS_USE_SIMPLE_NAMES
#define PROS_USE_SIMPLE_NAMES
#endif

#include "event_handler.hpp"
#include "pros/misc.hpp"
#include "pros/rtos.hpp"

namespace Gamepad {

enum EventType {
ON_PRESS,
ON_LONG_PRESS,
ON_RELEASE,
ON_SHORT_RELEASE,
};

class Button {
friend class Controller;
public:
/// Whether the button has just been pressed
bool rising_edge = false;
/// Whether the button has just been released
bool falling_edge = false;
/// Whether the button is currently held down
bool is_pressed = false;
/// How long the button has been held down
uint32_t time_held = 0;
/// How long the button has been released
uint32_t time_released = 0;
/// How long the threshold should be for the longPress and shortRelease events
uint32_t long_press_threshold = 500;
/**
* @brief Register a function to run when the button is pressed.
*
* @param listenerName The name of the listener, this must be a unique name
* @param func The function to run when the button is pressed, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* Gamepad::master.Down.onPress("downPress1", downPress1);
* // ...or a lambda
* Gamepad::master.Up.onPress("upPress1", []() { std::cout << "I was pressed!" << std::endl; });
* @endcode
*/
bool onPress(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run when the button is long pressed.
*
* By default, onLongPress will fire when the button has been held down for
* 500ms or more, this threshold can be adjusted by changing long_press_threshold.
*
* @warning When using this event along with onPress, both the onPress
* and onlongPress listeners may fire together.
*
* @param listenerName The name of the listener, this must be a unique name
* @param func The function to run when the button is long pressed, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* Gamepad::master.Left.onLongPress("fireCatapult", fireCatapult);
* // ...or a lambda
* Gamepad::master.Right.onLongPress("print_right", []() { std::cout << "Right button was long pressed!" <<
* std::endl; });
* @endcode
*/
bool onLongPress(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run when the button is released.
*
* @param listenerName The name of the listener, this must be a unique name
* @param func The function to run when the button is released, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* Gamepad::master.X.onRelease("stopFlywheel", stopFlywheel);
* // ...or a lambda
* Gamepad::master.Y.onRelease("stopIntake", []() { intake.move(0); });
* @endcode
*/
bool onRelease(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run when the button is short released.
*
* By default, shortRelease will fire when the button has been released before 500ms, this threshold can be
* adjusted by changing long_press_threshold.
*
* @note This event will most likely be used along with the longPress event.
*
* @param listenerName The name of the listener, this must be a unique name
* @param func The function to run when the button is short released, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* Gamepad::master.A.onShortRelease("raiseLiftOneLevel", raiseLiftOneLevel);
* // ...or a lambda
* Gamepad::master.B.onShortRelease("intakeOnePicce", []() { intake.move_relative(600, 100); });
* @endcode
*/
bool onShortRelease(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run for a given event.
*
* @param event Which event to register the listener on.
* @param listenerName The name of the listener, this must be a unique name
* @param func The function to run for the given event, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* Gamepad::master.L1.addListener(Gamepad::ON_PRESS, "start_spin", startSpin);
* // ...or a lambda
* Gamepad::master.L1.addListener(Gamepad::ON_RELEASE, "stop_spin", []() { motor1.brake(); });
* @endcode
*/
bool addListener(EventType event, std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Removes a listener from the button
* @warning Usage of this function is discouraged.
*
* @param listenerName The name of the listener to remove
* @return true The specified listener was successfully removed
* @return false The specified listener could not be removed
*
* @b Example:
* @code {.cpp}
* // Add an event listener...
* Gamepad::master.L1.addListener(Gamepad::ON_PRESS, "do_something", doSomething);
* // ...and now get rid of it
* Gamepad::master.L1.removeListener("do_something");
* @endcode
*/
bool removeListener(std::string listenerName) const;

/**
* @brief Returns a value indicating whether the button is currently being held.
*
* @return true The button is currently pressed
* @return false The button is not currently pressed
*/
explicit operator bool() const { return is_pressed; }
private:
/**
* @brief Updates the button and runs any event handlers, if necessary
*
* @param is_held Whether or not the button is currently held down
*/
void update(bool is_held);
/// he last time the update function was called
uint32_t last_update_time = pros::millis();
/// The last time the long press event was fired
uint32_t last_long_press_time = 0;
mutable _impl::EventHandler<std::string> onPressEvent {};
mutable _impl::EventHandler<std::string> onLongPressEvent {};
mutable _impl::EventHandler<std::string> onReleaseEvent {};
mutable _impl::EventHandler<std::string> onShortReleaseEvent {};
};

class Controller {
public:
/**
* @brief Updates the state of the gamepad (all joysticks and buttons), and also runs
* any registered listeners.
*
* @note This function should be called at the beginning of every loop iteration.
*
* @b Example:
* @code {.cpp}
* while (true) {
* Gamepad::master.update();
* // do robot control stuff here...
* pros::delay(25);
* }
* @endcode
*
*/
void update();
/**
* @brief Get the state of a button on the controller.
*
* @param button Which button to return
*
* @b Example:
* @code {.cpp}
* if(Gamepad::master[DIGITAL_L1]) {
* // do something here...
* }
* @endcode
*
*/
const Button& operator[](pros::controller_digital_e_t button);
/**
* @brief Get the value of a joystick axis on the controller.
*
* @param joystick Which joystick axis to return
*
* @b Example:
* @code {.cpp}
* // control a motor with a joystick
* intake.move(Gamepad::master[ANALOG_RIGHT_Y]);
* @endcode
*
*/
float operator[](pros::controller_analog_e_t joystick);
const Button& L1 {m_L1};
const Button& L2 {m_L2};
const Button& R1 {m_R1};
const Button& R2 {m_R2};
const Button& Up {m_Up};
const Button& Down {m_Down};
const Button& Left {m_Left};
const Button& Right {m_Right};
const Button& X {m_X};
const Button& B {m_B};
const Button& Y {m_Y};
const Button& A {m_A};
const float& LeftX = m_LeftX;
const float& LeftY = m_LeftY;
const float& RightX = m_RightX;
const float& RightY = m_RightY;
/// The master controller, same as @ref Gamepad::master
static Controller master;
/// The partner controller, same as @ref Gamepad::partner
static Controller partner;
private:
Controller(pros::controller_id_e_t id)
: controller(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 {};
float m_LeftX = 0, m_LeftY = 0, m_RightX = 0, m_RightY = 0;
Button Fake {};
/**
* @brief Gets a unique name for a listener that will not conflict with user listener names.
*
* @important: when using the function, you must register the listener by
* directly calling add_listener on the EventHandler, do NOT use onPress/addListener,etc.
*
* @return std::string A unique listener name
*/
static std::string unique_name();
static Button Controller::*button_to_ptr(pros::controller_digital_e_t button);
void updateButton(pros::controller_digital_e_t button_id);
pros::Controller controller;
};

inline Controller Controller::master {pros::E_CONTROLLER_MASTER};
inline Controller Controller::partner {pros::E_CONTROLLER_PARTNER};
/// The master controller
inline Controller& master = Controller::master;
/// The partner controller
inline Controller& partner = Controller::partner;

} // namespace Gamepad
Loading

0 comments on commit 970fed3

Please sign in to comment.