CHGame 0.3.0
Libraries for the CHGame handheld
Loading...
Searching...
No Matches
Input.h
Go to the documentation of this file.
1/// @file Input.h
2/// @brief Buttons and frame pacing: the Arduboy-flavoured front of the CHGame board.
3///
4/// Started from the helper shared by CHSpriteView/CHMultiSprite/CHStlView and
5/// reworked for the casino games, which all used this copy:
6/// - button masks are parenthesised, so ~UP_BUTTON and A|B behave;
7/// - every query reads the state captured by pollButtons(), so a frame sees
8/// one consistent snapshot and injected input (debug protocol, simulator)
9/// behaves exactly like a real press;
10/// - nextFrame() uses a microsecond accumulator, so 60 fps is 60.0, not the
11/// 62.5 that 1000/60 = 16 ms gave;
12/// - a lockstep mode lets the debug protocol step the game frame by frame;
13/// - auto-repeat for held buttons (menus, bet adjust);
14/// - holding START for 3 s leaves for the SD game menu (startExits).
15#pragma once
16#include <stdint.h>
17
18/// @defgroup chgame_input Buttons and frame pacing
19/// @ingroup lib_chgame
20/// @brief The global @ref ::chgame object: the frame clock and the eight buttons.
21///
22/// Call chgame.boot() once in `setup()`, then in `loop()`:
23/// @code
24/// if (!chgame.nextFrame()) return; // not time for a frame yet
25/// chgame.pollButtons(); // one snapshot of the buttons for this frame
26/// if (chgame.justPressed(A_BUTTON)) { ... }
27/// if (chgame.pressed(LEFT_BUTTON | B_BUTTON)) { ... } // both held
28/// @endcode
29///
30/// The button names and the calls are the Arduboy2 library's, so code
31/// written for it reads the same here.
32/// @{
33
34#define A_BUTTON (1u << 0) ///< The A button (PB1).
35#define B_BUTTON (1u << 1) ///< The B button (PB6).
36#define UP_BUTTON (1u << 2) ///< D-pad up (PB4).
37#define DOWN_BUTTON (1u << 3) ///< D-pad down (PC14).
38#define LEFT_BUTTON (1u << 4) ///< D-pad left (PB3).
39#define RIGHT_BUTTON (1u << 5) ///< D-pad right (PC15).
40#define START_BUTTON (1u << 6) ///< START (PB8). Held 3 s, it leaves for the menu (CHGame::startExits).
41#define SELECT_BUTTON (1u << 7) ///< SELECT (PB7).
42
43/// @brief Read the buttons straight from the hardware.
44/// @return A mask of the buttons held right now (pressed = 1), `A_BUTTON` ...
45/// `SELECT_BUTTON` ORed together.
46/// @details Implemented per platform (device: GPIO registers; simulator: the
47/// scripted input). A game normally uses CHGame::pollButtons() and the
48/// queries instead, which also see injected input.
50
51/// @brief Leave the game for the bootloader's game menu. Never returns.
52/// @details A plain reset, with no boot request
53/// (platform/bootloader/shared/chgame_bootreq.h). Under a bootloader without
54/// the menu (0.2.4) the game simply starts again. In the simulator it prints
55/// a line and ends the run.
56[[noreturn]] void chgame_exitToMenu();
57
58/// @brief The board's buttons and frame clock. There is one, @ref ::chgame.
59class CHGame {
60public:
61 /// @brief Set up the button pins (inputs with pull-ups) and take a first reading.
62 /// @details Call once at the top of `setup()`. A button held at boot does
63 /// not count as a press on the first frame.
64 void boot();
65 /// @brief Set the frame rate nextFrame() paces to.
66 /// @param fps Frames per second (1-255). The default is 60.
67 void setFrameRate(uint8_t fps);
68 /// @brief Whether it is time for the next frame.
69 /// @return true once per frame period (and counts the frame in frameCount);
70 /// false otherwise, so `loop()` returns at once.
71 /// @details A game that falls more than three frames behind (a debug
72 /// pause, a save) starts afresh instead of sprinting to catch up. In
73 /// lockstep (the debug protocol's `L1`) it returns true only for the
74 /// frames the tool asked for.
75 bool nextFrame();
76
77 /// @brief Take this frame's snapshot of the buttons.
78 /// @details Call once per frame, after nextFrame(). Every query below
79 /// reads this snapshot, so a frame sees one consistent state. It ORs in
80 /// `injected` (the debug protocol's `K`), counts how long each button has
81 /// been held (for repeat()), and calls exitToMenu() when START has been
82 /// held 3 s and startExits is set.
84 /// @brief The buttons held in this frame's snapshot.
85 /// @return A mask of `A_BUTTON` ... `SELECT_BUTTON`.
86 uint8_t buttons() const { return cur; }
87 /// @brief Whether all the buttons in a mask are held.
88 /// @param b One button or several ORed together.
89 /// @return true if every one of them is held.
90 bool pressed(uint8_t b) const { return (cur & b) == b; }
91 /// @brief Whether any of the buttons in a mask is held.
92 /// @param b One button or several ORed together.
93 /// @return true if at least one of them is held.
94 bool anyPressed(uint8_t b) const { return (cur & b) != 0; }
95 /// @brief Whether a button went down this frame.
96 /// @param b One button or several ORed together.
97 /// @return true if any of them is held now and was not in the previous frame.
98 bool justPressed(uint8_t b) const { return (cur & ~prev & b) != 0; }
99 /// @brief Whether a button was let go this frame.
100 /// @param b One button or several ORed together.
101 /// @return true if any of them was held in the previous frame and is not now.
102 bool justReleased(uint8_t b) const { return (prev & ~cur & b) != 0; }
103 /// @brief Every button that went down this frame.
104 /// @return A mask of the buttons pressed now and not in the previous frame.
105 uint8_t justPressedMask() const { return (uint8_t)(cur & ~prev); }
106 /// @brief Press edge, then auto-repeat while held: for menus and adjusting a bet.
107 /// @param b One button or several ORed together.
108 /// @param delay Frames held before the repeat starts (default 18, 0.3 s at 60 fps).
109 /// @param rate Frames between repeats after that (default 5).
110 /// @return true on the frame a button went down, then every `rate` frames
111 /// once it has been held longer than `delay`.
112 bool repeat(uint8_t b, uint8_t delay = 18, uint8_t rate = 5) const;
113 /// @brief Forget this frame's presses: justPressed() and justReleased() are
114 /// false until the buttons change again.
115 /// @details Use it when a screen changes, so the press that changed it
116 /// does not also act on the new one.
117 void clearButtonState() { prev = cur; }
118
119 /// @brief Whether frameCount is a multiple of n: something to do every n frames.
120 /// @param n The period in frames (not 0).
121 /// @return true on every nth frame.
122 bool everyXFrames(uint16_t n) const { return (frameCount % n) == 0; }
123
124 /// @brief Leave the game for the SD game menu. Never returns.
125 /// @see chgame_exitToMenu()
126 [[noreturn]] void exitToMenu() { chgame_exitToMenu(); }
127
128 uint32_t frameCount = 0; ///< Frames since boot, counted by nextFrame().
129 uint8_t injected = 0; ///< Buttons ORed into the physical ones (the debug protocol's `K`).
130 /// START held 3 s calls exitToMenu(); a game that needs a long START hold
131 /// clears it in setup().
132 bool startExits = true;
133#ifdef CHSIM
134 int32_t lockstep = 0; // the simulator starts paused, driven by N
135#else
136 /// Lockstep: < 0 free-running, else the frames nextFrame() may still run
137 /// (set by the debug protocol's `L` and `N`).
138 int32_t lockstep = -1;
139#endif
140
141private:
142 uint8_t cur = 0, prev = 0;
143 uint16_t held[8] = {}; // frames each button has been held
144 uint32_t period = 16667, next = 0;
145};
146
147/// @brief The one instance: chgame.boot(), chgame.pressed() ...
148extern CHGame chgame;
149
150/// @}
The board's buttons and frame clock. There is one, chgame.
Definition Input.h:59
uint8_t injected
Buttons ORed into the physical ones (the debug protocol's K).
Definition Input.h:129
void clearButtonState()
Forget this frame's presses: justPressed() and justReleased() are false until the buttons change agai...
Definition Input.h:117
uint32_t frameCount
Frames since boot, counted by nextFrame().
Definition Input.h:128
bool anyPressed(uint8_t b) const
Whether any of the buttons in a mask is held.
Definition Input.h:94
bool justPressed(uint8_t b) const
Whether a button went down this frame.
Definition Input.h:98
void exitToMenu()
Leave the game for the SD game menu.
Definition Input.h:126
bool repeat(uint8_t b, uint8_t delay=18, uint8_t rate=5) const
Press edge, then auto-repeat while held: for menus and adjusting a bet.
int32_t lockstep
Lockstep: < 0 free-running, else the frames nextFrame() may still run (set by the debug protocol's L ...
Definition Input.h:138
bool nextFrame()
Whether it is time for the next frame.
bool pressed(uint8_t b) const
Whether all the buttons in a mask are held.
Definition Input.h:90
void pollButtons()
Take this frame's snapshot of the buttons.
bool justReleased(uint8_t b) const
Whether a button was let go this frame.
Definition Input.h:102
void boot()
Set up the button pins (inputs with pull-ups) and take a first reading.
uint8_t buttons() const
The buttons held in this frame's snapshot.
Definition Input.h:86
bool everyXFrames(uint16_t n) const
Whether frameCount is a multiple of n: something to do every n frames.
Definition Input.h:122
bool startExits
START held 3 s calls exitToMenu(); a game that needs a long START hold clears it in setup().
Definition Input.h:132
uint8_t justPressedMask() const
Every button that went down this frame.
Definition Input.h:105
void setFrameRate(uint8_t fps)
Set the frame rate nextFrame() paces to.
void chgame_exitToMenu()
Leave the game for the bootloader's game menu.
CHGame chgame
The one instance: chgame.boot(), chgame.pressed() ...
uint8_t chgame_readButtons()
Read the buttons straight from the hardware.