diff options
| author | Hannes Schulze <haschu0103@outlook.de> | 2025-02-01 23:32:55 +0100 |
|---|---|---|
| committer | Hannes Schulze <haschu0103@outlook.de> | 2025-02-01 23:32:55 +0100 |
| commit | 8062afc25e895cb49e76f6e26e51a7d4a1ad2df2 (patch) | |
| tree | f64e6522a84a8f58912b9e58a66c187fbcaadd78 /lib/nes/app/input | |
| parent | bf88e17d552d85cd18bc31757ef9d73049b327c4 (diff) | |
revamp input system
Diffstat (limited to 'lib/nes/app/input')
| -rw-r--r-- | lib/nes/app/input/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | lib/nes/app/input/input-buffer.cc | 56 | ||||
| -rw-r--r-- | lib/nes/app/input/input-buffer.hh | 41 | ||||
| -rw-r--r-- | lib/nes/app/input/input-device-keyboard.hh | 35 | ||||
| -rw-r--r-- | lib/nes/app/input/input-device.hh | 23 | ||||
| -rw-r--r-- | lib/nes/app/input/input-event.hh | 46 | ||||
| -rw-r--r-- | lib/nes/app/input/input-manager.cc | 9 | ||||
| -rw-r--r-- | lib/nes/app/input/key.cc | 88 | ||||
| -rw-r--r-- | lib/nes/app/input/key.hh | 90 | ||||
| -rw-r--r-- | lib/nes/app/input/modifier-mask.hh | 39 |
10 files changed, 402 insertions, 31 deletions
diff --git a/lib/nes/app/input/CMakeLists.txt b/lib/nes/app/input/CMakeLists.txt index 7150745..632bfcd 100644 --- a/lib/nes/app/input/CMakeLists.txt +++ b/lib/nes/app/input/CMakeLists.txt @@ -1,6 +1,12 @@ target_sources( nes PRIVATE + key.hh + key.cc + modifier-mask.hh + input-event.hh + input-buffer.hh + input-buffer.cc input-device.hh input-device-keyboard.hh input-device-controller.hh diff --git a/lib/nes/app/input/input-buffer.cc b/lib/nes/app/input/input-buffer.cc new file mode 100644 index 0000000..e8da51c --- /dev/null +++ b/lib/nes/app/input/input-buffer.cc @@ -0,0 +1,56 @@ +#include "nes/app/input/input-buffer.hh" +#include "nes/app/input/input-event.hh" + +namespace nes::app +{ + auto input_buffer::key_down(key const k) -> void + { + set_pressed(k, true); + } + + auto input_buffer::key_up(key const k) -> void + { + set_pressed(k, false); + } + + auto input_buffer::read_key(key const k) -> bool + { + return get_event(k).pressed; + } + + auto input_buffer::poll_event() -> input_event + { + auto it = event_queue_.begin(); + if (it == event_queue_.end()) { return input_event{}; } + + auto const event = *it; + auto const k = get_key(*event); + auto const pressed = event->pressed; + event_queue_.remove(it); + + return pressed ? input_event::key_down(k) : input_event::key_up(k); + } + + auto input_buffer::get_event(key const k) -> event& + { + return key_events_[static_cast<u32>(k)]; + } + + auto input_buffer::get_key(event const& ev) -> key + { + return static_cast<key>(&ev - key_events_); + } + + auto input_buffer::set_pressed(key const k, bool const pressed) -> void + { + auto& event = get_event(k); + if (event.pressed == pressed) { return; } + event.pressed = pressed; + + // Remove the event if it was not previously fetched (skipping it entirely), otherwise add a new event. + if (!event_queue_.remove(event)) + { + event_queue_.append(event); + } + } +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/input/input-buffer.hh b/lib/nes/app/input/input-buffer.hh new file mode 100644 index 0000000..68fca6b --- /dev/null +++ b/lib/nes/app/input/input-buffer.hh @@ -0,0 +1,41 @@ +#pragma once + +#include "nes/app/input/key.hh" +#include "nes/common/list.hh" + +namespace nes::app +{ + class input_event; + + class input_buffer + { + struct event final : list<event>::node + { + bool pressed{ false }; + }; + + event key_events_[key_count]{}; + list<event> event_queue_; + + public: + explicit input_buffer() = default; + + input_buffer(input_buffer const&) = delete; + input_buffer(input_buffer&&) = delete; + auto operator=(input_buffer const&) -> input_buffer& = delete; + auto operator=(input_buffer&&) -> input_buffer& = delete; + + /// Record a key-down event. + auto key_down(key) -> void; + /// Record a key-up event. + auto key_up(key) -> void; + + auto read_key(key) -> bool; + auto poll_event() -> input_event; + + private: + auto get_event(key) -> event&; + auto get_key(event const&) -> key; + auto set_pressed(key, bool) -> void; + }; +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/input/input-device-keyboard.hh b/lib/nes/app/input/input-device-keyboard.hh index dddcab6..23ee65c 100644 --- a/lib/nes/app/input/input-device-keyboard.hh +++ b/lib/nes/app/input/input-device-keyboard.hh @@ -1,6 +1,9 @@ #pragma once #include "nes/app/input/input-device.hh" +#include "nes/app/input/modifier-mask.hh" +#include "nes/app/input/input-event.hh" +#include "nes/app/input/key.hh" namespace nes::app { @@ -8,7 +11,37 @@ namespace nes::app class input_device_keyboard : public input_device { public: - virtual auto read_key_escape() -> bool = 0; + /// Read the current state of a key. + virtual auto read_key(key) -> bool = 0; + + /// Poll for events. + /// + /// As long as there are any buffered key-down or key-up events, they will be returned. After that, this + /// function will return input_event::type::none until there is another event. + virtual auto poll_event() -> input_event = 0; + + auto read_buttons() -> sys::button_mask final + { + auto res = sys::button_mask{}; + if (read_key(key::letter_w)) { res.add(sys::buttons::up); } + if (read_key(key::letter_a)) { res.add(sys::buttons::left); } + if (read_key(key::letter_s)) { res.add(sys::buttons::down); } + if (read_key(key::letter_d)) { res.add(sys::buttons::right); } + if (read_key(key::letter_k)) { res.add(sys::buttons::b); } + if (read_key(key::letter_l)) { res.add(sys::buttons::a); } + if (read_key(key::space)) { res.add(sys::buttons::select); } + if (read_key(key::enter)) { res.add(sys::buttons::start); } + return res; + } + + auto read_modifiers() -> modifier_mask + { + auto res = modifier_mask{}; + if (read_key(key::control_left) || read_key(key::control_right)) { res.add(modifiers::control); } + if (read_key(key::shift_left) || read_key(key::shift_right)) { res.add(modifiers::shift); } + if (read_key(key::alt_left) || read_key(key::alt_right)) { res.add(modifiers::alt); } + return res; + } protected: explicit input_device_keyboard() = default; diff --git a/lib/nes/app/input/input-device.hh b/lib/nes/app/input/input-device.hh index 8e54f61..cc03057 100644 --- a/lib/nes/app/input/input-device.hh +++ b/lib/nes/app/input/input-device.hh @@ -15,28 +15,7 @@ namespace nes::app auto operator=(input_device const&) -> input_device& = delete; auto operator=(input_device&&) -> input_device& = delete; - virtual auto read_key_up() -> bool = 0; - virtual auto read_key_down() -> bool = 0; - virtual auto read_key_left() -> bool = 0; - virtual auto read_key_right() -> bool = 0; - virtual auto read_key_a() -> bool = 0; - virtual auto read_key_b() -> bool = 0; - virtual auto read_key_select() -> bool = 0; - virtual auto read_key_start() -> bool = 0; - - auto read_mask() -> sys::button_mask - { - auto res = sys::button_mask{}; - if (read_key_up()) { res.add(sys::buttons::up); } - if (read_key_down()) { res.add(sys::buttons::down); } - if (read_key_left()) { res.add(sys::buttons::left); } - if (read_key_right()) { res.add(sys::buttons::right); } - if (read_key_a()) { res.add(sys::buttons::a); } - if (read_key_b()) { res.add(sys::buttons::b); } - if (read_key_select()) { res.add(sys::buttons::select); } - if (read_key_start()) { res.add(sys::buttons::start); } - return res; - } + virtual auto read_buttons() -> sys::button_mask = 0; protected: explicit input_device() = default; diff --git a/lib/nes/app/input/input-event.hh b/lib/nes/app/input/input-event.hh new file mode 100644 index 0000000..90e67b0 --- /dev/null +++ b/lib/nes/app/input/input-event.hh @@ -0,0 +1,46 @@ +#pragma once + +#include "nes/app/input/key.hh" + +namespace nes::app +{ + class input_event + { + public: + enum class type + { + none, + key_down, + key_up, + }; + + private: + type type_{ type::none }; + key key_{}; + + public: + constexpr explicit input_event() = default; + + static constexpr auto key_down(key const key) -> input_event { return input_event{ type::key_down, key }; } + static constexpr auto key_up(key const key) -> input_event { return input_event{ type::key_up, key }; } + + constexpr operator bool() const { return type_ != type::none; } + + constexpr auto get_type() const -> type { return type_; } + constexpr auto get_key() const -> key { return key_; } + + private: + constexpr explicit input_event(type const type, key const key) + : type_{ type } + , key_{ key } + { + } + }; + + constexpr auto operator==(input_event const lhs, input_event const rhs) + { + return lhs.get_type() == rhs.get_type() && lhs.get_key() == rhs.get_key(); + } + + constexpr auto operator!=(input_event const lhs, input_event const rhs) { return !(lhs == rhs); } +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/input/input-manager.cc b/lib/nes/app/input/input-manager.cc index 8131283..1af0f5b 100644 --- a/lib/nes/app/input/input-manager.cc +++ b/lib/nes/app/input/input-manager.cc @@ -15,14 +15,7 @@ namespace nes::app return res; } - auto read_key_up() -> bool override { return false; } - auto read_key_down() -> bool override { return false; } - auto read_key_left() -> bool override { return false; } - auto read_key_right() -> bool override { return false; } - auto read_key_a() -> bool override { return false; } - auto read_key_b() -> bool override { return false; } - auto read_key_select() -> bool override { return false; } - auto read_key_start() -> bool override { return false; } + auto read_buttons() -> sys::button_mask override { return sys::button_mask{}; } }; } // namespace diff --git a/lib/nes/app/input/key.cc b/lib/nes/app/input/key.cc new file mode 100644 index 0000000..9cd7104 --- /dev/null +++ b/lib/nes/app/input/key.cc @@ -0,0 +1,88 @@ +#include "nes/app/input/key.hh" +#include "nes/app/input/modifier-mask.hh" + +namespace nes::app +{ + auto to_ascii(key const k, modifier_mask const m) -> char + { + switch (k) + { + case key::digit_0: return m.contains(modifiers::shift) ? ')' : '0'; + case key::digit_1: return m.contains(modifiers::shift) ? '!' : '1'; + case key::digit_2: return m.contains(modifiers::shift) ? '@' : '2'; + case key::digit_3: return m.contains(modifiers::shift) ? '#' : '3'; + case key::digit_4: return m.contains(modifiers::shift) ? '$' : '4'; + case key::digit_5: return m.contains(modifiers::shift) ? '%' : '5'; + case key::digit_6: return m.contains(modifiers::shift) ? '^' : '6'; + case key::digit_7: return m.contains(modifiers::shift) ? '&' : '7'; + case key::digit_8: return m.contains(modifiers::shift) ? '*' : '8'; + case key::digit_9: return m.contains(modifiers::shift) ? '(' : '9'; + case key::letter_a: return m.contains(modifiers::shift) ? 'A' : 'a'; + case key::letter_b: return m.contains(modifiers::shift) ? 'B' : 'b'; + case key::letter_c: return m.contains(modifiers::shift) ? 'C' : 'c'; + case key::letter_d: return m.contains(modifiers::shift) ? 'D' : 'd'; + case key::letter_e: return m.contains(modifiers::shift) ? 'E' : 'e'; + case key::letter_f: return m.contains(modifiers::shift) ? 'F' : 'f'; + case key::letter_g: return m.contains(modifiers::shift) ? 'G' : 'g'; + case key::letter_h: return m.contains(modifiers::shift) ? 'H' : 'h'; + case key::letter_i: return m.contains(modifiers::shift) ? 'I' : 'i'; + case key::letter_j: return m.contains(modifiers::shift) ? 'J' : 'j'; + case key::letter_k: return m.contains(modifiers::shift) ? 'K' : 'k'; + case key::letter_l: return m.contains(modifiers::shift) ? 'L' : 'l'; + case key::letter_m: return m.contains(modifiers::shift) ? 'M' : 'm'; + case key::letter_n: return m.contains(modifiers::shift) ? 'N' : 'n'; + case key::letter_o: return m.contains(modifiers::shift) ? 'O' : 'o'; + case key::letter_p: return m.contains(modifiers::shift) ? 'P' : 'p'; + case key::letter_q: return m.contains(modifiers::shift) ? 'Q' : 'q'; + case key::letter_r: return m.contains(modifiers::shift) ? 'R' : 'r'; + case key::letter_s: return m.contains(modifiers::shift) ? 'S' : 's'; + case key::letter_t: return m.contains(modifiers::shift) ? 'T' : 't'; + case key::letter_u: return m.contains(modifiers::shift) ? 'U' : 'u'; + case key::letter_v: return m.contains(modifiers::shift) ? 'V' : 'v'; + case key::letter_w: return m.contains(modifiers::shift) ? 'W' : 'w'; + case key::letter_x: return m.contains(modifiers::shift) ? 'X' : 'x'; + case key::letter_y: return m.contains(modifiers::shift) ? 'Y' : 'y'; + case key::letter_z: return m.contains(modifiers::shift) ? 'Z' : 'z'; + case key::dash: return m.contains(modifiers::shift) ? '_' : '-'; + case key::equal: return m.contains(modifiers::shift) ? '+' : '='; + case key::semicolon: return m.contains(modifiers::shift) ? ':' : ';'; + case key::apostrophe: return m.contains(modifiers::shift) ? '"' : '\''; + case key::backslash: return m.contains(modifiers::shift) ? '|' : '\\'; + case key::comma: return m.contains(modifiers::shift) ? '<' : ','; + case key::period: return m.contains(modifiers::shift) ? '>' : '.'; + case key::slash: return m.contains(modifiers::shift) ? '?' : '/'; + case key::bracket_open: return m.contains(modifiers::shift) ? '{' : '['; + case key::bracket_close: return m.contains(modifiers::shift) ? '}' : ']'; + case key::tab: return '\t'; + case key::enter: return '\n'; + case key::space: return ' '; + case key::escape: return 0; + case key::backspace: return 0; + case key::caps_lock: return 0; + case key::control_left: return 0; + case key::control_right: return 0; + case key::shift_left: return 0; + case key::shift_right: return 0; + case key::alt_left: return 0; + case key::alt_right: return 0; + case key::arrow_left: return 0; + case key::arrow_right: return 0; + case key::arrow_up: return 0; + case key::arrow_down: return 0; + case key::f1: return 0; + case key::f2: return 0; + case key::f3: return 0; + case key::f4: return 0; + case key::f5: return 0; + case key::f6: return 0; + case key::f7: return 0; + case key::f8: return 0; + case key::f9: return 0; + case key::f10: return 0; + case key::f11: return 0; + case key::f12: return 0; + } + + return 0; + } +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/input/key.hh b/lib/nes/app/input/key.hh new file mode 100644 index 0000000..3156e65 --- /dev/null +++ b/lib/nes/app/input/key.hh @@ -0,0 +1,90 @@ +#pragma once + +#include "nes/common/types.hh" + +namespace nes::app +{ + class modifier_mask; + + enum class key + { + digit_0, + digit_1, + digit_2, + digit_3, + digit_4, + digit_5, + digit_6, + digit_7, + digit_8, + digit_9, + letter_a, + letter_b, + letter_c, + letter_d, + letter_e, + letter_f, + letter_g, + letter_h, + letter_i, + letter_j, + letter_k, + letter_l, + letter_m, + letter_n, + letter_o, + letter_p, + letter_q, + letter_r, + letter_s, + letter_t, + letter_u, + letter_v, + letter_w, + letter_x, + letter_y, + letter_z, + escape, + dash, + equal, + backspace, + tab, + enter, + semicolon, + apostrophe, + backslash, + comma, + period, + slash, + space, + caps_lock, + bracket_open, + bracket_close, + control_left, + control_right, + shift_left, + shift_right, + alt_left, + alt_right, + arrow_left, + arrow_right, + arrow_up, + arrow_down, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + }; + + inline constexpr auto key_count = static_cast<u32>(key::f12) + 1; + + auto to_ascii(key, modifier_mask) -> char; +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/input/modifier-mask.hh b/lib/nes/app/input/modifier-mask.hh new file mode 100644 index 0000000..e18cddb --- /dev/null +++ b/lib/nes/app/input/modifier-mask.hh @@ -0,0 +1,39 @@ +#pragma once + +#include "nes/common/types.hh" + +namespace nes::app +{ + /// Keyboard modifiers. + class modifier_mask + { + u8 raw_value_{ 0 }; + + public: + constexpr explicit modifier_mask() = default; + + static constexpr auto from_raw_value(u8 const raw_value) -> modifier_mask + { + return modifier_mask{ raw_value }; + } + + constexpr auto add(modifier_mask const other) -> void { raw_value_ |= other.raw_value_; } + constexpr auto remove(modifier_mask const other) -> void { raw_value_ ^= other.raw_value_; } + constexpr auto contains(modifier_mask const other) const -> bool { return (raw_value_ & other.raw_value_) == other.raw_value_; } + constexpr auto is_empty() const -> bool { return raw_value_ == 0; } + constexpr auto get_raw_value() const -> u8 { return raw_value_; } + + private: + constexpr explicit modifier_mask(u8 const raw_value) + : raw_value_{ raw_value } + { + } + }; + + namespace modifiers + { + inline constexpr auto control = modifier_mask::from_raw_value(1 << 0); + inline constexpr auto shift = modifier_mask::from_raw_value(1 << 1); + inline constexpr auto alt = modifier_mask::from_raw_value(1 << 2); + } // namespace modifiers +} // namespace nes::app
\ No newline at end of file |
