blah/include/blah_input.h
2022-10-22 23:18:52 -07:00

812 lines
19 KiB
C++

#pragma once
#include <blah_common.h>
#include <blah_spatial.h>
#include <blah_string.h>
#include <blah_stackvector.h>
// These are generally copied from the SDL2 Scancode Keys,
// which are in turn based on the USB standards:
// https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
#define BLAH_KEY_DEFINITIONS \
DEFINE_KEY(Unknown, 0) \
\
DEFINE_KEY(A, 4) \
DEFINE_KEY(B, 5) \
DEFINE_KEY(C, 6) \
DEFINE_KEY(D, 7) \
DEFINE_KEY(E, 8) \
DEFINE_KEY(F, 9) \
DEFINE_KEY(G, 10) \
DEFINE_KEY(H, 11) \
DEFINE_KEY(I, 12) \
DEFINE_KEY(J, 13) \
DEFINE_KEY(K, 14) \
DEFINE_KEY(L, 15) \
DEFINE_KEY(M, 16) \
DEFINE_KEY(N, 17) \
DEFINE_KEY(O, 18) \
DEFINE_KEY(P, 19) \
DEFINE_KEY(Q, 20) \
DEFINE_KEY(R, 21) \
DEFINE_KEY(S, 22) \
DEFINE_KEY(T, 23) \
DEFINE_KEY(U, 24) \
DEFINE_KEY(V, 25) \
DEFINE_KEY(W, 26) \
DEFINE_KEY(X, 27) \
DEFINE_KEY(Y, 28) \
DEFINE_KEY(Z, 29) \
\
DEFINE_KEY(D1, 30) \
DEFINE_KEY(D2, 31) \
DEFINE_KEY(D3, 32) \
DEFINE_KEY(D4, 33) \
DEFINE_KEY(D5, 34) \
DEFINE_KEY(D6, 35) \
DEFINE_KEY(D7, 36) \
DEFINE_KEY(D8, 37) \
DEFINE_KEY(D9, 38) \
DEFINE_KEY(D0, 39) \
\
DEFINE_KEY(Enter, 40) \
DEFINE_KEY(Escape, 41) \
DEFINE_KEY(Backspace, 42) \
DEFINE_KEY(Tab, 43) \
DEFINE_KEY(Space, 44) \
\
DEFINE_KEY(Minus, 45) \
DEFINE_KEY(Equals, 46) \
DEFINE_KEY(LeftBracket, 47) \
DEFINE_KEY(RightBracket, 48) \
DEFINE_KEY(Backslash, 49) \
DEFINE_KEY(Semicolon, 51) \
DEFINE_KEY(Apostrophe, 52) \
DEFINE_KEY(Tilde, 53) \
DEFINE_KEY(Comma, 54) \
DEFINE_KEY(Period, 55) \
DEFINE_KEY(Slash, 56) \
\
DEFINE_KEY(Capslock, 57) \
\
DEFINE_KEY(F1, 58) \
DEFINE_KEY(F2, 59) \
DEFINE_KEY(F3, 60) \
DEFINE_KEY(F4, 61) \
DEFINE_KEY(F5, 62) \
DEFINE_KEY(F6, 63) \
DEFINE_KEY(F7, 64) \
DEFINE_KEY(F8, 65) \
DEFINE_KEY(F9, 66) \
DEFINE_KEY(F10, 67) \
DEFINE_KEY(F11, 68) \
DEFINE_KEY(F12, 69) \
DEFINE_KEY(F13, 104) \
DEFINE_KEY(F14, 105) \
DEFINE_KEY(F15, 106) \
DEFINE_KEY(F16, 107) \
DEFINE_KEY(F17, 108) \
DEFINE_KEY(F18, 109) \
DEFINE_KEY(F19, 110) \
DEFINE_KEY(F20, 111) \
DEFINE_KEY(F21, 112) \
DEFINE_KEY(F22, 113) \
DEFINE_KEY(F23, 114) \
DEFINE_KEY(F24, 115) \
\
DEFINE_KEY(PrintScreen, 70) \
DEFINE_KEY(ScrollLock, 71) \
DEFINE_KEY(Pause, 72) \
DEFINE_KEY(Insert, 73) \
DEFINE_KEY(Home, 74) \
DEFINE_KEY(PageUp, 75) \
DEFINE_KEY(Delete, 76) \
DEFINE_KEY(End, 77) \
DEFINE_KEY(PageDown, 78) \
DEFINE_KEY(Right, 79) \
DEFINE_KEY(Left, 80) \
DEFINE_KEY(Down, 81) \
DEFINE_KEY(Up, 82) \
\
DEFINE_KEY(Numlock, 83) \
\
DEFINE_KEY(Application, 101) \
\
DEFINE_KEY(Execute, 116) \
DEFINE_KEY(Help, 117) \
DEFINE_KEY(Menu, 118) \
DEFINE_KEY(Select, 119) \
DEFINE_KEY(Stop, 120) \
DEFINE_KEY(Redo, 121) \
DEFINE_KEY(Undo, 122) \
DEFINE_KEY(Cut, 123) \
DEFINE_KEY(Copy, 124) \
DEFINE_KEY(Paste, 125) \
DEFINE_KEY(Find, 126) \
DEFINE_KEY(Mute, 127) \
DEFINE_KEY(VolumeUp, 128) \
DEFINE_KEY(VolumeDown, 129) \
\
DEFINE_KEY(AltErase, 153) \
DEFINE_KEY(SysReq, 154) \
DEFINE_KEY(Cancel, 155) \
DEFINE_KEY(Clear, 156) \
DEFINE_KEY(Prior, 157) \
DEFINE_KEY(Enter2, 158) \
DEFINE_KEY(Separator, 159) \
DEFINE_KEY(Out, 160) \
DEFINE_KEY(Oper, 161) \
DEFINE_KEY(ClearAgain, 162) \
\
DEFINE_KEY(KeypadA, 188) \
DEFINE_KEY(KeypadB, 189) \
DEFINE_KEY(KeypadC, 190) \
DEFINE_KEY(KeypadD, 191) \
DEFINE_KEY(KeypadE, 192) \
DEFINE_KEY(KeypadF, 193) \
DEFINE_KEY(Keypad0, 98) \
DEFINE_KEY(Keypad00, 176) \
DEFINE_KEY(Keypad000, 177) \
DEFINE_KEY(Keypad1, 89) \
DEFINE_KEY(Keypad2, 90) \
DEFINE_KEY(Keypad3, 91) \
DEFINE_KEY(Keypad4, 92) \
DEFINE_KEY(Keypad5, 93) \
DEFINE_KEY(Keypad6, 94) \
DEFINE_KEY(Keypad7, 95) \
DEFINE_KEY(Keypad8, 96) \
DEFINE_KEY(Keypad9, 97) \
DEFINE_KEY(KeypadDivide, 84) \
DEFINE_KEY(KeypadMultiply, 85) \
DEFINE_KEY(KeypadMinus, 86) \
DEFINE_KEY(KeypadPlus, 87) \
DEFINE_KEY(KeypadEnter, 88) \
DEFINE_KEY(KeypadPeroid, 99) \
DEFINE_KEY(KeypadEquals, 103) \
DEFINE_KEY(KeypadComma, 133) \
DEFINE_KEY(KeypadLeftParen, 182) \
DEFINE_KEY(KeypadRightParen, 183) \
DEFINE_KEY(KeypadLeftBrace, 184) \
DEFINE_KEY(KeypadRightBrace, 185) \
DEFINE_KEY(KeypadTab, 186) \
DEFINE_KEY(KeypadBackspace, 187) \
DEFINE_KEY(KeypadXor, 194) \
DEFINE_KEY(KeypadPower, 195) \
DEFINE_KEY(KeypadPercent, 196) \
DEFINE_KEY(KeypadLess, 197) \
DEFINE_KEY(KeypadGreater, 198) \
DEFINE_KEY(KeypadAmpersand, 199) \
DEFINE_KEY(KeypadColon, 203) \
DEFINE_KEY(KeypadHash, 204) \
DEFINE_KEY(KeypadSpace, 205) \
DEFINE_KEY(KeypadClear, 216) \
\
DEFINE_KEY(LeftControl, 224) \
DEFINE_KEY(LeftShift, 225) \
DEFINE_KEY(LeftAlt, 226) \
DEFINE_KEY(LeftOS, 227) \
DEFINE_KEY(RightControl, 228) \
DEFINE_KEY(RightShift, 229) \
DEFINE_KEY(RightAlt, 230) \
DEFINE_KEY(RightOS, 231)
#define BLAH_BUTTON_DEFINITIONS \
DEFINE_BTN(None, -1) \
DEFINE_BTN(A, 0) \
DEFINE_BTN(B, 1) \
DEFINE_BTN(X, 2) \
DEFINE_BTN(Y, 3) \
DEFINE_BTN(Back, 4) \
DEFINE_BTN(Select, 5) \
DEFINE_BTN(Start, 6) \
DEFINE_BTN(LeftStick, 7) \
DEFINE_BTN(RightStick, 8) \
DEFINE_BTN(LeftShoulder, 9) \
DEFINE_BTN(RightShoulder, 10) \
DEFINE_BTN(Up, 11) \
DEFINE_BTN(Down, 12) \
DEFINE_BTN(Left, 13) \
DEFINE_BTN(Right, 14)
namespace Blah
{
namespace Input
{
// maximum number of controllers the input can handle
constexpr int max_controllers = 8;
// maximum number of buttons the input will track
constexpr int max_controller_buttons = 64;
// maximum number of controller axis the input will track
constexpr int max_controller_axis = 16;
// maximum number of mouse buttons the input will track
constexpr int max_mouse_buttons = 16;
// maximum number of keys the input will track
constexpr int max_keyboard_keys = 512;
}
// Keyboard Keys
enum class Key
{
#define DEFINE_KEY(name, value) name = value,
BLAH_KEY_DEFINITIONS
#undef DEFINE_KEY
};
// Game Controller Buttons
enum class Button
{
#define DEFINE_BTN(name, value) name = value,
BLAH_BUTTON_DEFINITIONS
#undef DEFINE_BTN
};
// Game Controller Axis
enum class Axis
{
None = -1,
LeftX = 0,
LeftY = 1,
RightX = 2,
RightY = 3,
LeftTrigger = 4,
RightTrigger = 5,
};
// Mouse Buttons
enum class MouseButton
{
None = -1,
Left = 0,
Middle = 1,
Right = 2,
};
// Controller State
struct ControllerState
{
// The name of the gamepad, or NULL if not connected
String name;
// Whether this gamepad is connected
bool is_connected;
// Whether this gamepad is a standard Game Controller
bool is_gamepad;
// The total button count for this controller
int button_count;
// The total axis count for this controller
int axis_count;
// An array holding the pressed state of each button
bool pressed[Input::max_controller_buttons];
// An array holding the down state of each button
bool down[Input::max_controller_buttons];
// An array holding the released state of each button
bool released[Input::max_controller_buttons];
// An array holding the value state of each axis
float axis[Input::max_controller_axis];
// Timestamp, in milliseconds, since each button was last pressed
u64 button_timestamp[Input::max_controller_buttons];
// Timestamp, in milliseconds, since each axis last had a value set
u64 axis_timestamp[Input::max_controller_axis];
// The USB Vendor ID
u16 vendor;
// The USB Product ID
u16 product;
// the Product Version
u16 version;
// marks the controller as connected
void on_connect(const String& name, bool is_gamepad, int button_count, int axis_count, u16 vendor, u16 product, u16 version);
// marks the controller as disconnected
void on_disconnect();
// invokes a button press
void on_press(Button button);
// invokes a button release
void on_release(Button button);
// invokes an axis movement
void on_axis(Axis axis, float value);
};
// Keyboard State
struct KeyboardState
{
// whether a key was pressed this frame
bool pressed[Input::max_keyboard_keys];
// whether a key is currently held
bool down[Input::max_keyboard_keys];
// whether a key was released this frame
bool released[Input::max_keyboard_keys];
// the timestamp for the key being pressed
u64 timestamp[Input::max_keyboard_keys];
// current text input this frame
String text;
// Checks if the Left or Right Ctrl Key is down
bool ctrl();
// Checks if the Left or Right Shift Key is down
bool shift();
// Checks if the Left or Right Alt Key is down
bool alt();
// invokes a key press
void on_press(Key key);
// invokes a key release
void on_release(Key key);
};
// Mouse State
struct MouseState
{
// whether a button was pressed this frame
bool pressed[Input::max_mouse_buttons];
// whether a button was held this frame
bool down[Input::max_mouse_buttons];
// whether a button was released this frame
bool released[Input::max_mouse_buttons];
// the timestamp for the button being pressed
u64 timestamp[Input::max_mouse_buttons];
// mouse position in screen coordinates
Vec2f screen_position;
// mouse position in pixel coordinates
Vec2f draw_position;
// mouse position on the window
Vec2f position;
// mouse wheel value this frame
Point wheel;
// invokes a key press
void on_press(MouseButton button);
// invokes a mouse movement
void on_move(const Vec2f& position, const Vec2f& screen_position);
// invokes a key release
void on_release(MouseButton button);
};
// An Input State, which stores the state for gamepads, keyboard, and mouse
struct InputState
{
// All the Gamepads. Note that not all gamepads are necessarily connected,
// and each one must be checked before use.
ControllerState controllers[Input::max_controllers];
// The current Keyboard state
KeyboardState keyboard;
// The current Mouse state
MouseState mouse;
};
class ButtonBinding;
using ButtonBindingRef = Ref<ButtonBinding>;
class AxisBinding;
using AxisBindingRef = Ref<AxisBinding>;
class StickBinding;
using StickBindingRef = Ref<StickBinding>;
// Single input Binding
// You must call Binding::update() every frame to poll the input state.
// Alternatively, bindings can be registered to Input which will
// automatically update them.
class ButtonBinding
{
public:
// Represents a Controller Trigger or a single direction of a Controller Axis.
struct TriggerBind
{
// Controller Index we're bound to
int controller = 0;
// The Axis we're bound to
Axis axis = Axis::None;
// Minimum value of the axis
float threshold = 0.01f;
// requires a positive value
// otherwise requires a negative value
bool positive = true;
TriggerBind() = default;
TriggerBind(Axis axis);
TriggerBind(int controller, Axis axis, float threshold, bool positive);
bool is_down(float axis_value) const;
};
// Represents a Controller Button.
struct ButtonBind
{
// Controller Index we're bound to
int controller = 0;
// Button we're bound to
Button button = Button::None;
ButtonBind() = default;
ButtonBind(Button button);
ButtonBind(int controller, Button button);
};
// Input Buffer for press events
float press_buffer = 0;
// Input Buffer for release events
float release_buffer = 0;
// List of bound Keys
StackVector<Key, 16> keys;
// List of bound Buttons
StackVector<ButtonBind, 16> buttons;
// List of bound Triggers / Axis
StackVector<TriggerBind, 16> triggers;
// List of bound Mouse buttons
StackVector<MouseButton, 16> mouse;
ButtonBinding() = default;
ButtonBinding(float press_buffer)
: press_buffer(press_buffer)
{
}
template<typename ... Args>
ButtonBinding(float press_buffer, const Args&... args)
: press_buffer(press_buffer)
{
add(args...);
}
// if the binding has been pressed
bool pressed() const;
// if the binding has been released
bool released() const;
// if the binding is currently held
bool down() const;
// returns the binding's value from 0-1
float value() const;
// returns the bindings signed value (0 or 1)
int sign() const;
// returns the timestamp of the last time the binding was pressed
double timestamp() const;
// updates the binding state
void update();
// consumes the current press, and pressed() will return false until the next press
void consume_press();
// consumes the current release, and released() will return false until the next release
void consume_release();
// adds a key to the binding
ButtonBinding& add(Key key);
// adds a button to the binding
ButtonBinding& add(ButtonBind button);
// adds an trigger to the binding
ButtonBinding& add(TriggerBind trigger);
// adds a mouse button to the binding
ButtonBinding& add(MouseButton mouse);
// adds an input to the binding
template<typename T, typename T2, typename ... Args>
ButtonBinding& add(T first, T2 second, const Args&... args)
{
add(first);
add(second, args...);
return *this;
}
// adds the left trigger to the binding
ButtonBinding& add_left_trigger(int controller, float threshold);
// adds the right trigger to the binding
ButtonBinding& add_right_trigger(int controller, float threshold);
// assigns all the bindings to the specific controller
ButtonBinding& set_controller(int index);
// clears values (except for last input timestamps)
void zero_out();
// removes all bindings
void clear();
private:
double m_last_timestamp = 0;
double m_last_press_time = -1;
double m_last_release_time = -1;
float m_value = 0.0f;
bool m_pressed = false;
bool m_released = false;
bool m_down = false;
bool m_press_consumed = false;
bool m_release_consumed = false;
bool get_pressed() const;
bool get_released() const;
bool get_down() const;
float get_value() const;
};
// Axis Binding (ex. Left/Right movement, or a Trigger)
// You must call AxisBinding::update() every frame to poll the input state.
// Alternatively, bindings can be registered to Input which will
// automatically update them.
class AxisBinding
{
public:
enum class Overlap
{
Newer,
Older,
Cancel
};
// Negative Value Binding
ButtonBinding negative;
// Positive Value Binding
ButtonBinding positive;
// How to handle overlaps (ex. Left and Right are both held)
Overlap overlap = Overlap::Newer;
AxisBinding() = default;
AxisBinding(const ButtonBinding& negative, const ButtonBinding& positive, Overlap overlap = Overlap::Newer)
: negative(negative)
, positive(positive)
, overlap(overlap)
{}
// Current Value from -1 to 1
float value() const;
// Current value, either -1, 0, or 1
int sign() const;
// updates the Binding
void update();
// checks if the axis was pressed this frame (ie. went from 0 to -1 or 1)
bool pressed() const { return negative.pressed() || positive.pressed(); }
// consumes the press buffer
void consume_press();
// consumes the release buffer
void consume_release();
// Adds a negative & positive binding pair
template<typename NegativeT, typename PositiveT>
AxisBinding& add(NegativeT negative, PositiveT positive)
{
this->negative.add(negative);
this->positive.add(positive);
return *this;
}
// Adds a Stick binding
AxisBinding& add_left_stick_x(int controller, float threshold);
AxisBinding& add_left_stick_y(int controller, float threshold);
AxisBinding& add_right_stick_x(int controller, float threshold);
AxisBinding& add_right_stick_y(int controller, float threshold);
// assigns all the bindings to the specific controller
AxisBinding& set_controller(int index);
// clears values (except for last input timestamps)
void zero_out() { negative.zero_out(); positive.zero_out(); }
// Clears all Bindings
void clear();
};
// Stick Binding (ex. Joystick, Dpad, Arrow Keys, WASD, etc)
// You must call StickBinding::update() every frame to poll the input state.
// Alternatively, bindings can be registered to Input which will
// automatically update them.
class StickBinding
{
public:
// X Axis Binding
AxisBinding x;
// Y Axis Binding
AxisBinding y;
// An optional threshold for circular thresholds
float round_threshold = 0.0f;
StickBinding() = default;
StickBinding(const AxisBinding& x, const AxisBinding& y, float round_threshold = 0)
: x(x)
, y(y)
, round_threshold(round_threshold)
{}
// Current Value, -1 to 1
Vec2f value() const;
// Current value, either -1, 0, or 1
Point sign() const;
// Updates the Binding
void update();
// checks if the stick was pressed this frame
bool pressed() const { return x.pressed() || y.pressed(); }
// Consumes the Press Buffer
void consume_press();
// Consumes the Release Buffer
void consume_release();
// Adds directional bindings
template<typename LeftT, typename RightT, typename UpT, typename DownT>
StickBinding& add(LeftT left, RightT right, UpT up, DownT down)
{
x.negative.add(left);
x.positive.add(right);
y.negative.add(up);
y.positive.add(down);
return *this;
}
// Adds the dpad binding
StickBinding& add_dpad(int controller);
// Adds the left stick binding
StickBinding& add_left_stick(int controller, float threshold);
// Adds the right stick binding
StickBinding& add_right_stick(int controller, float threshold);
// assigns all the bindings to the specific controller
StickBinding& set_controller(int index);
// clears values (except for last input timestamps)
void zero_out() { x.zero_out(); y.zero_out(); }
// Clears all the bindings
void clear();
};
namespace Input
{
// Input State for the current frame
extern InputState state;
// Input State for the previous frame
extern InputState last_state;
// Key-Repeating intervals
extern float repeat_delay;
extern float repeat_interval;
// Gets the Mouse Position
Vec2f mouse();
// Gets the Draw Mouse Position (Mouse Position / Window Size * Draw Size)
Vec2f mouse_draw();
// Gets the Mouse Position in Screen Coordinates
Vec2f mouse_screen();
// Checks if the given Mouse Button is pressed
bool pressed(MouseButton button);
// Checks if the given Mouse Button is down
bool down(MouseButton button);
// Checks if the given Mouse Button is released
bool released(MouseButton button);
// Gets the Mouse Wheel
Point mouse_wheel();
// Checks if the keyboard key was pressed this frame
bool pressed(Key key);
// Checks if the keyboard key was held this frame
bool down(Key key);
// Checks if the keyboard key was released this frame
bool released(Key key);
// Pressed or repeating on-interval
bool repeating(Key key);
// Checks if the Left or Right Ctrl Key is down
bool ctrl();
// Checks if the Left or Right Shift Key is down
bool shift();
// Checks if the Left or Right Alt Key is down
bool alt();
// Checks if the given Controller Button is pressed
bool pressed(int controller_index, Button button);
// Checks if the given Controller Button is down
bool down(int controller_index, Button button);
// Checks if the given Controller Button is released
bool released(int controller_index, Button button);
// returns a string name of the key
const char* name_of(Key key);
// returns a string name of the button
const char* name_of(Button button);
// gets the string contents of the clipboard
const String& get_clipboard();
// sets the string contents of the clipboard
void set_clipboard(const String& text);
// registers a new binding and returns a handle to it
ButtonBindingRef register_binding(const ButtonBinding& binding_data);
// registers a new binding and returns a handle to it
AxisBindingRef register_binding(const AxisBinding& binding_data);
// registers a new binding and returns a handle to it
StickBindingRef register_binding(const StickBinding& binding_data);
}
}