GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 24 / 0 / 24
Functions: 100.0% 7 / 0 / 7
Branches: 100.0% 9 / 0 / 9

include/DetourModKit/input_codes.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_INPUT_CODES_HPP
2 #define DETOURMODKIT_INPUT_CODES_HPP
3
4 /**
5 * @file input_codes.hpp
6 * @brief Unified input code types for keyboard, mouse, and gamepad inputs.
7 * @details Provides a tagged InputCode type that identifies both the device source and button/key code, along with
8 * named key resolution for human-readable configuration strings. Gamepad codes correspond to XInput button
9 * masks. Each PascalCase constant in GamepadCode and WheelCode also has an UPPER_SNAKE_CASE spelling with
10 * the same value. Both spellings are public API.
11 */
12
13 #include <cstdint>
14 #include <functional>
15 #include <optional>
16 #include <string>
17 #include <string_view>
18
19 namespace DetourModKit
20 {
21 /**
22 * @enum InputSource
23 * @brief Identifies the device type for an input code.
24 */
25 enum class InputSource : std::uint8_t
26 {
27 Keyboard,
28 Mouse,
29 Gamepad,
30 MouseWheel
31 };
32
33 /**
34 * @brief Converts an InputSource enum to its string representation.
35 * @param source The InputSource enum value.
36 * @return std::string_view String representation of the source.
37 */
38 9 [[nodiscard]] constexpr std::string_view input_source_to_string(InputSource source) noexcept
39 {
40
5/5
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 3 times.
✓ Branch 2 → 5 taken 2 times.
✓ Branch 2 → 6 taken 2 times.
✓ Branch 2 → 7 taken 1 time.
9 switch (source)
41 {
42 1 case InputSource::Keyboard:
43 1 return "Keyboard";
44 3 case InputSource::Mouse:
45 3 return "Mouse";
46 2 case InputSource::Gamepad:
47 2 return "Gamepad";
48 2 case InputSource::MouseWheel:
49 2 return "MouseWheel";
50 }
51 1 return "Unknown";
52 }
53
54 /**
55 * @struct InputCode
56 * @brief A tagged input identifier combining a device source and a button/key code.
57 * @details For Keyboard and Mouse sources, the code is a Windows Virtual Key code (usable with GetAsyncKeyState).
58 * For Gamepad, the code is an XInput button bitmask or a synthetic trigger identifier (see GamepadCode).
59 * For MouseWheel, the code is a WheelCode direction identifier; the wheel is an event with no polled key
60 * state, so it is captured by the input layer's queue message hook and surfaced as a momentary
61 * per-direction pulse (trigger-only, never a held modifier).
62 */
63 struct InputCode
64 {
65 1197 InputSource source = InputSource::Keyboard;
66 677 int code = 0;
67
68
4/4
✓ Branch 2 → 3 taken 520 times.
✓ Branch 2 → 4 taken 677 times.
✓ Branch 4 → 5 taken 163 times.
✓ Branch 4 → 6 taken 514 times.
1197 constexpr bool operator==(const InputCode &) const noexcept = default;
69 };
70
71 /**
72 * @brief Hash functor for InputCode, enabling use in unordered containers.
73 */
74 struct InputCodeHash
75 {
76 5765 std::size_t operator()(const InputCode &ic) const noexcept
77 {
78 5765 return std::hash<int>{}(ic.code) ^ (std::hash<std::uint8_t>{}(static_cast<std::uint8_t>(ic.source)) << 16);
79 }
80 };
81
82 /**
83 * @brief Creates a keyboard InputCode from a Windows Virtual Key code.
84 * @param vk The VK code (e.g., 0x41 for 'A').
85 * @return InputCode Tagged as Keyboard.
86 */
87 1850 [[nodiscard]] constexpr InputCode keyboard_key(int vk) noexcept
88 {
89 1850 return {InputSource::Keyboard, vk};
90 }
91
92 /**
93 * @brief Creates a mouse InputCode from a Windows Virtual Key code.
94 * @param vk The VK code (e.g., 0x01 for VK_LBUTTON).
95 * @return InputCode Tagged as Mouse.
96 */
97 10 [[nodiscard]] constexpr InputCode mouse_button(int vk) noexcept
98 {
99 10 return {InputSource::Mouse, vk};
100 }
101
102 /**
103 * @brief Creates a gamepad InputCode from an XInput button code.
104 * @param code The XInput button mask or synthetic trigger code (see GamepadCode).
105 * @return InputCode Tagged as Gamepad.
106 */
107 246 [[nodiscard]] constexpr InputCode gamepad_button(int code) noexcept
108 {
109 246 return {InputSource::Gamepad, code};
110 }
111
112 /**
113 * @brief Creates a mouse-wheel InputCode from a wheel direction code.
114 * @param code The wheel direction identifier (see WheelCode).
115 * @return InputCode Tagged as MouseWheel.
116 */
117 13 [[nodiscard]] constexpr InputCode mouse_wheel(int code) noexcept
118 {
119 13 return {InputSource::MouseWheel, code};
120 }
121
122 /**
123 * @namespace GamepadCode
124 * @brief XInput-compatible gamepad button codes and synthetic analog identifiers.
125 * @details Digital button codes match XInput XINPUT_GAMEPAD_* bitmask values. LeftTrigger/RightTrigger and
126 * thumbstick direction codes are synthetic identifiers for analog inputs treated as digital with
127 * configurable deadzone thresholds.
128 */
129 namespace GamepadCode
130 {
131 inline constexpr int DpadUp = 0x0001;
132 inline constexpr int DpadDown = 0x0002;
133 inline constexpr int DpadLeft = 0x0004;
134 inline constexpr int DpadRight = 0x0008;
135 inline constexpr int Start = 0x0010;
136 inline constexpr int Back = 0x0020;
137 inline constexpr int LeftStick = 0x0040;
138 inline constexpr int RightStick = 0x0080;
139 inline constexpr int LeftBumper = 0x0100;
140 inline constexpr int RightBumper = 0x0200;
141 inline constexpr int A = 0x1000;
142 inline constexpr int B = 0x2000;
143 inline constexpr int X = 0x4000;
144 inline constexpr int Y = 0x8000;
145
146 /// Synthetic codes for analog triggers treated as digital inputs.
147 inline constexpr int LeftTrigger = 0x10000;
148 inline constexpr int RightTrigger = 0x10001;
149
150 /**
151 * @brief Synthetic codes for thumbstick axes treated as digital inputs.
152 * @details Each direction fires when the axis exceeds the stick deadzone threshold.
153 */
154 inline constexpr int LeftStickUp = 0x10002;
155 inline constexpr int LeftStickDown = 0x10003;
156 inline constexpr int LeftStickLeft = 0x10004;
157 inline constexpr int LeftStickRight = 0x10005;
158 inline constexpr int RightStickUp = 0x10006;
159 inline constexpr int RightStickDown = 0x10007;
160 inline constexpr int RightStickLeft = 0x10008;
161 inline constexpr int RightStickRight = 0x10009;
162
163 /// Default analog trigger threshold (0-255 range, values above are "pressed").
164 inline constexpr int TriggerThreshold = 30;
165
166 /**
167 * @brief Default thumbstick deadzone threshold (0-32767 range).
168 * @details Matches XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE (7849).
169 */
170 inline constexpr int StickThreshold = 7849;
171
172 // UPPER_SNAKE_CASE spellings of the constants above (A, B, X, Y are identical in both spellings).
173 inline constexpr int DPAD_UP = DpadUp;
174 inline constexpr int DPAD_DOWN = DpadDown;
175 inline constexpr int DPAD_LEFT = DpadLeft;
176 inline constexpr int DPAD_RIGHT = DpadRight;
177 inline constexpr int START = Start;
178 inline constexpr int BACK = Back;
179 inline constexpr int LEFT_STICK = LeftStick;
180 inline constexpr int RIGHT_STICK = RightStick;
181 inline constexpr int LEFT_BUMPER = LeftBumper;
182 inline constexpr int RIGHT_BUMPER = RightBumper;
183 inline constexpr int LEFT_TRIGGER = LeftTrigger;
184 inline constexpr int RIGHT_TRIGGER = RightTrigger;
185 inline constexpr int LEFT_STICK_UP = LeftStickUp;
186 inline constexpr int LEFT_STICK_DOWN = LeftStickDown;
187 inline constexpr int LEFT_STICK_LEFT = LeftStickLeft;
188 inline constexpr int LEFT_STICK_RIGHT = LeftStickRight;
189 inline constexpr int RIGHT_STICK_UP = RightStickUp;
190 inline constexpr int RIGHT_STICK_DOWN = RightStickDown;
191 inline constexpr int RIGHT_STICK_LEFT = RightStickLeft;
192 inline constexpr int RIGHT_STICK_RIGHT = RightStickRight;
193 inline constexpr int TRIGGER_THRESHOLD = TriggerThreshold;
194 inline constexpr int STICK_THRESHOLD = StickThreshold;
195 } // namespace GamepadCode
196
197 /**
198 * @namespace WheelCode
199 * @brief Mouse-wheel direction identifiers used by InputSource::MouseWheel codes.
200 * @details Values are 1-based and dense so the input layer can map a code to a zero-based direction index with
201 * `code - WheelCode::Up`. Up/Down are the vertical wheel; Left/Right are the horizontal (tilt) wheel.
202 */
203 namespace WheelCode
204 {
205 inline constexpr int Up = 1;
206 inline constexpr int Down = 2;
207 inline constexpr int Left = 3;
208 inline constexpr int Right = 4;
209
210 // UPPER_SNAKE_CASE spellings of the constants above.
211 inline constexpr int UP = Up;
212 inline constexpr int DOWN = Down;
213 inline constexpr int LEFT = Left;
214 inline constexpr int RIGHT = Right;
215 } // namespace WheelCode
216
217 /**
218 * @brief Attempts to resolve a human-readable name to an InputCode.
219 * @details Performs case-insensitive matching against a built-in table of known key, mouse button, and gamepad
220 * button names.
221 *
222 * Recognized name formats:
223 * - Keyboard: "A"-"Z", "0"-"9", "F1"-"F24", "Ctrl", "Shift", "Alt",
224 * "Space", "Enter", "Escape", "Tab", "Backspace", Windows/menu keys ("LWin", "RWin", "Apps"), and OEM
225 * punctuation ("Grave"/"Backtick"/"Tilde", "Semicolon", "Comma", "Period", "Slash", etc.)
226 * - Mouse: "Mouse1" (left) through "Mouse5" (XButton2)
227 * - Mouse wheel: "WheelUp", "WheelDown", "WheelLeft", "WheelRight"
228 * - Gamepad: "Gamepad_A", "Gamepad_B", "Gamepad_LB", "Gamepad_LT", etc.
229 * - Source-tagged hex (the inverse of format_input_code's off-table form): "Mouse:0xFE",
230 * "Gamepad:0x800", "MouseWheel:0x9", "Keyboard:0xFF".
231 *
232 * @param name The input name to resolve.
233 * @return std::optional<InputCode> The resolved code, or std::nullopt if unrecognized.
234 * @note A bare (untagged) hex token such as "0xFF" yields std::nullopt: this resolver handles named keys and
235 * source-tagged hex only. See format_input_code for the two off-table forms and their reconstruction paths.
236 */
237 [[nodiscard]] std::optional<InputCode> parse_input_name(std::string_view name);
238
239 /**
240 * @brief Returns a human-readable name for an InputCode, if one exists.
241 * @param code The input code to look up.
242 * @return std::string_view The canonical name, or an empty view if not in the table.
243 */
244 [[nodiscard]] std::string_view input_code_to_name(const InputCode &code);
245
246 /**
247 * @brief Formats an InputCode as a human-readable string.
248 * @details Returns the canonical name if the code is in the lookup table. Off-table codes fall back to hex: a
249 * Keyboard code emits bare hex ("0x72"), while any other source is tagged with its device name
250 * ("Mouse:0xFE") so the source is not lost. A source-tagged token round-trips through parse_input_name.
251 * A bare-hex Keyboard token round-trips through the config combo parser used by config::bind_combos,
252 * whose untagged-hex fallback defaults to the Keyboard source.
253 * @param code The input code to format.
254 * @return std::string Formatted string.
255 */
256 [[nodiscard]] std::string format_input_code(const InputCode &code);
257
258 } // namespace DetourModKit
259
260 #endif // DETOURMODKIT_INPUT_CODES_HPP
261