mirror of
https://github.com/SpeedBreakerProject/speedbreaker.git
synced 2026-10-24 07:03:01 +02:00
Co-authored-by: love-gamer <340996855+love-gamer@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
110 lines
5.2 KiB
C++
110 lines
5.2 KiB
C++
// SpeedBreaker runtime. GPL-3.0-or-later (see COPYING).
|
|
//
|
|
// The keys a Controls row can hold (Settings > Controls), named for
|
|
// settings.toml: "w", "left_shift", "keypad_1", or "none" for no key. The
|
|
// code is the key's USB HID usage, which is what SDL's scancodes are (A is
|
|
// 4), so a binding is the physical key whatever the layout; SDL itself
|
|
// stays out of this header and of settings.cpp, and tests/key_names_test.cpp
|
|
// checks every row against SDL's name for the code (hid/input.cpp pins a
|
|
// few at compile time). SDL's own names won't do for the file: some change
|
|
// once video starts (on a Mac Left Alt becomes "Left Option"), while the
|
|
// settings load before that, and some keys share one. The table is also
|
|
// the list of what can be bound: F1 (it opens the menu), Cmd / Windows and
|
|
// the media keys are left out on purpose. Header only.
|
|
#pragma once
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <string_view>
|
|
|
|
namespace settings::keynames
|
|
{
|
|
constexpr uint16_t kNone = 0; // no key; "none" in the file
|
|
constexpr uint16_t kUnknown = 0xFFFF; // Code(): not a name in the table
|
|
|
|
struct Key
|
|
{
|
|
uint16_t code;
|
|
const char* token;
|
|
};
|
|
|
|
// Ascending by code. The tokens are SDL's scancode constants in lower
|
|
// case with underscores: SDL_SCANCODE_LEFTBRACKET is "left_bracket",
|
|
// SDL_SCANCODE_KP_1 "keypad_1", SDL_SCANCODE_NUMLOCKCLEAR "num_lock".
|
|
constexpr Key kKeys[] = {
|
|
{ 4, "a" }, { 5, "b" }, { 6, "c" }, { 7, "d" }, { 8, "e" }, { 9, "f" }, { 10, "g" }, { 11, "h" }, { 12, "i" },
|
|
{ 13, "j" }, { 14, "k" }, { 15, "l" }, { 16, "m" }, { 17, "n" }, { 18, "o" }, { 19, "p" }, { 20, "q" }, { 21, "r" },
|
|
{ 22, "s" }, { 23, "t" }, { 24, "u" }, { 25, "v" }, { 26, "w" }, { 27, "x" }, { 28, "y" }, { 29, "z" },
|
|
{ 30, "1" }, { 31, "2" }, { 32, "3" }, { 33, "4" }, { 34, "5" }, { 35, "6" }, { 36, "7" }, { 37, "8" }, { 38, "9" }, { 39, "0" },
|
|
{ 40, "return" }, { 41, "escape" }, { 42, "backspace" }, { 43, "tab" }, { 44, "space" },
|
|
{ 45, "minus" }, { 46, "equals" }, { 47, "left_bracket" }, { 48, "right_bracket" }, { 49, "backslash" },
|
|
{ 50, "non_us_hash" }, { 51, "semicolon" }, { 52, "apostrophe" }, { 53, "grave" }, { 54, "comma" },
|
|
{ 55, "period" }, { 56, "slash" }, { 57, "caps_lock" },
|
|
{ 59, "f2" }, { 60, "f3" }, { 61, "f4" }, { 62, "f5" }, { 63, "f6" }, { 64, "f7" }, { 65, "f8" }, { 66, "f9" },
|
|
{ 67, "f10" }, { 68, "f11" }, { 69, "f12" },
|
|
{ 70, "print_screen" }, { 71, "scroll_lock" }, { 72, "pause" }, { 73, "insert" }, { 74, "home" },
|
|
{ 75, "page_up" }, { 76, "delete" }, { 77, "end" }, { 78, "page_down" },
|
|
{ 79, "right" }, { 80, "left" }, { 81, "down" }, { 82, "up" },
|
|
{ 83, "num_lock" }, { 84, "keypad_divide" }, { 85, "keypad_multiply" }, { 86, "keypad_minus" },
|
|
{ 87, "keypad_plus" }, { 88, "keypad_enter" },
|
|
{ 89, "keypad_1" }, { 90, "keypad_2" }, { 91, "keypad_3" }, { 92, "keypad_4" }, { 93, "keypad_5" },
|
|
{ 94, "keypad_6" }, { 95, "keypad_7" }, { 96, "keypad_8" }, { 97, "keypad_9" }, { 98, "keypad_0" },
|
|
{ 99, "keypad_period" }, { 100, "non_us_backslash" }, { 101, "application" }, { 103, "keypad_equals" },
|
|
{ 104, "f13" }, { 105, "f14" }, { 106, "f15" }, { 107, "f16" }, { 108, "f17" }, { 109, "f18" },
|
|
{ 110, "f19" }, { 111, "f20" }, { 112, "f21" }, { 113, "f22" }, { 114, "f23" }, { 115, "f24" },
|
|
{ 133, "keypad_comma" },
|
|
{ 135, "international_1" }, { 136, "international_2" }, { 137, "international_3" },
|
|
{ 138, "international_4" }, { 139, "international_5" }, { 140, "international_6" },
|
|
{ 224, "left_ctrl" }, { 225, "left_shift" }, { 226, "left_alt" },
|
|
{ 228, "right_ctrl" }, { 229, "right_shift" }, { 230, "right_alt" },
|
|
};
|
|
constexpr size_t kKeyCount = std::size(kKeys);
|
|
|
|
namespace detail
|
|
{
|
|
constexpr char Lower(char c)
|
|
{
|
|
return c >= 'A' && c <= 'Z' ? char(c - 'A' + 'a') : c;
|
|
}
|
|
|
|
// `text` in any case against a lower-case token.
|
|
constexpr bool Same(std::string_view text, const char* token)
|
|
{
|
|
size_t i = 0;
|
|
for (; i < text.size() && token[i]; i++)
|
|
if (Lower(text[i]) != token[i])
|
|
return false;
|
|
return i == text.size() && token[i] == '\0';
|
|
}
|
|
}
|
|
|
|
// The file's name for a code: "none" for kNone; nullptr for a code that
|
|
// isn't a key here.
|
|
constexpr const char* Token(uint16_t code)
|
|
{
|
|
if (code == kNone)
|
|
return "none";
|
|
for (const Key& k : kKeys)
|
|
if (k.code == code)
|
|
return k.token;
|
|
return nullptr;
|
|
}
|
|
|
|
// The code for a name in the file, in any case; kUnknown when it isn't one.
|
|
constexpr uint16_t Code(std::string_view token)
|
|
{
|
|
if (detail::Same(token, "none"))
|
|
return kNone;
|
|
for (const Key& k : kKeys)
|
|
if (detail::Same(token, k.token))
|
|
return k.code;
|
|
return kUnknown;
|
|
}
|
|
|
|
// A value a Key option can hold: no key, or a key of the table.
|
|
constexpr bool Known(uint16_t code)
|
|
{
|
|
return code == kNone || Token(code) != nullptr;
|
|
}
|
|
}
|