diff options
Diffstat (limited to 'src/core/frontend')
-rw-r--r-- | src/core/frontend/applets/controller.cpp | 18 | ||||
-rw-r--r-- | src/core/frontend/applets/controller.h | 12 | ||||
-rw-r--r-- | src/core/frontend/applets/error.cpp | 7 | ||||
-rw-r--r-- | src/core/frontend/applets/general_frontend.cpp | 68 | ||||
-rw-r--r-- | src/core/frontend/applets/general_frontend.h | 51 | ||||
-rw-r--r-- | src/core/frontend/applets/web_browser.cpp | 24 | ||||
-rw-r--r-- | src/core/frontend/applets/web_browser.h | 20 | ||||
-rw-r--r-- | src/core/frontend/emu_window.cpp | 10 | ||||
-rw-r--r-- | src/core/frontend/emu_window.h | 4 | ||||
-rw-r--r-- | src/core/frontend/framebuffer_layout.cpp | 8 | ||||
-rw-r--r-- | src/core/frontend/input.h | 23 | ||||
-rw-r--r-- | src/core/frontend/input_interpreter.cpp | 45 | ||||
-rw-r--r-- | src/core/frontend/input_interpreter.h | 120 |
13 files changed, 252 insertions, 158 deletions
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 4505da758..03bbedf8b 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -4,7 +4,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/core.h" #include "core/frontend/applets/controller.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/hid.h" @@ -14,32 +13,33 @@ namespace Core::Frontend { ControllerApplet::~ControllerApplet() = default; +DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) + : service_manager{service_manager_} {} + DefaultControllerApplet::~DefaultControllerApplet() = default; void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, - ControllerParameters parameters) const { + const ControllerParameters& parameters) const { LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); auto& npad = - Core::System::GetInstance() - .ServiceManager() - .GetService<Service::HID::Hid>("hid") + service_manager.GetService<Service::HID::Hid>("hid") ->GetAppletResource() ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); - auto& players = Settings::values.players; + auto& players = Settings::values.players.GetValue(); const std::size_t min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; // Disconnect Handheld first. - npad.DisconnectNPadAtIndex(8); + npad.DisconnectNpadAtIndex(8); // Deduce the best configuration based on the input parameters. for (std::size_t index = 0; index < players.size() - 2; ++index) { // First, disconnect all controllers regardless of the value of keep_controllers_connected. // This makes it easy to connect the desired controllers. - npad.DisconnectNPadAtIndex(index); + npad.DisconnectNpadAtIndex(index); // Only connect the minimum number of required players. if (index >= min_supported_players) { @@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); } } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && - !Settings::values.use_docked_mode) { + !Settings::values.use_docked_mode.GetValue()) { // We should *never* reach here under any normal circumstances. npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), index); diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index a227f15cd..dff71d8d9 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -8,6 +8,10 @@ #include "common/common_types.h" +namespace Service::SM { +class ServiceManager; +} + namespace Core::Frontend { using BorderColor = std::array<u8, 4>; @@ -34,15 +38,19 @@ public: virtual ~ControllerApplet(); virtual void ReconfigureControllers(std::function<void()> callback, - ControllerParameters parameters) const = 0; + const ControllerParameters& parameters) const = 0; }; class DefaultControllerApplet final : public ControllerApplet { public: + explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); ~DefaultControllerApplet() override; void ReconfigureControllers(std::function<void()> callback, - ControllerParameters parameters) const override; + const ControllerParameters& parameters) const override; + +private: + Service::SM::ServiceManager& service_manager; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp index 4002a9211..dceb20ff8 100644 --- a/src/core/frontend/applets/error.cpp +++ b/src/core/frontend/applets/error.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" #include "core/frontend/applets/error.h" namespace Core::Frontend { @@ -10,7 +11,7 @@ ErrorApplet::~ErrorApplet() = default; void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const { LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", - static_cast<u32>(error.module.Value()), error.description.Value(), error.raw); + error.module.Value(), error.description.Value(), error.raw); } void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, @@ -18,7 +19,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::s LOG_CRITICAL( Service_Fatal, "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", - static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count()); + error.module.Value(), error.description.Value(), error.raw, time.count()); } void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text, @@ -26,7 +27,7 @@ void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_ std::function<void()> finished) const { LOG_CRITICAL(Service_Fatal, "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", - static_cast<u32>(error.module.Value()), error.description.Value(), error.raw); + error.module.Value(), error.description.Value(), error.raw); LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text); LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text); } diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp index c30b36de7..7483ffb76 100644 --- a/src/core/frontend/applets/general_frontend.cpp +++ b/src/core/frontend/applets/general_frontend.cpp @@ -53,72 +53,4 @@ void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) con finished(); } -ECommerceApplet::~ECommerceApplet() = default; - -DefaultECommerceApplet::~DefaultECommerceApplet() = default; - -void DefaultECommerceApplet::ShowApplicationInformation( - std::function<void()> finished, u64 title_id, std::optional<u128> user_id, - std::optional<bool> full_display, std::optional<std::string> extra_parameter) { - const auto value = user_id.value_or(u128{}); - LOG_INFO(Service_AM, - "Application requested frontend show application information for EShop, " - "title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}", - title_id, value[1], value[0], - full_display.has_value() ? fmt::format("{}", *full_display) : "null", - extra_parameter.value_or("null")); - finished(); -} - -void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id, - std::optional<bool> full_display) { - const auto value = user_id.value_or(u128{}); - LOG_INFO(Service_AM, - "Application requested frontend show add on content list for EShop, " - "title_id={:016X}, user_id={:016X}{:016X}, full_display={}", - title_id, value[1], value[0], - full_display.has_value() ? fmt::format("{}", *full_display) : "null"); - finished(); -} - -void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id) { - const auto value = user_id.value_or(u128{}); - LOG_INFO(Service_AM, - "Application requested frontend show subscription list for EShop, title_id={:016X}, " - "user_id={:016X}{:016X}", - title_id, value[1], value[0]); - finished(); -} - -void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id) { - const auto value = user_id.value_or(u128{}); - LOG_INFO( - Service_AM, - "Application requested frontend show consumable item list for EShop, title_id={:016X}, " - "user_id={:016X}{:016X}", - title_id, value[1], value[0]); - finished(); -} - -void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id, - bool full_display) { - LOG_INFO(Service_AM, - "Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, " - "full_display={}", - user_id[1], user_id[0], full_display); - finished(); -} - -void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id, - bool full_display) { - LOG_INFO(Service_AM, - "Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, " - "full_display={}", - user_id[1], user_id[0], full_display); - finished(); -} - } // namespace Core::Frontend diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h index 4b63f828e..b713b14ee 100644 --- a/src/core/frontend/applets/general_frontend.h +++ b/src/core/frontend/applets/general_frontend.h @@ -58,55 +58,4 @@ public: void ShowAllPhotos(std::function<void()> finished) const override; }; -class ECommerceApplet { -public: - virtual ~ECommerceApplet(); - - // Shows a page with application icons, description, name, and price. - virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id = {}, - std::optional<bool> full_display = {}, - std::optional<std::string> extra_parameter = {}) = 0; - - // Shows a page with all of the add on content available for a game, with name, description, and - // price. - virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id = {}, - std::optional<bool> full_display = {}) = 0; - - // Shows a page with all of the subscriptions (recurring payments) for a game, with name, - // description, price, and renewal period. - virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id = {}) = 0; - - // Shows a page with a list of any additional game related purchasable items (DLC, - // subscriptions, etc) for a particular game, with name, description, type, and price. - virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id = {}) = 0; - - // Shows the home page of the shop. - virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0; - - // Shows the user settings page of the shop. - virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0; -}; - -class DefaultECommerceApplet : public ECommerceApplet { -public: - ~DefaultECommerceApplet() override; - - void ShowApplicationInformation(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id, std::optional<bool> full_display, - std::optional<std::string> extra_parameter) override; - void ShowAddOnContentList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id, - std::optional<bool> full_display) override; - void ShowSubscriptionList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id) override; - void ShowConsumableItemList(std::function<void()> finished, u64 title_id, - std::optional<u128> user_id) override; - void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override; - void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override; -}; - } // namespace Core::Frontend diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp index 528295ffc..50db6a654 100644 --- a/src/core/frontend/applets/web_browser.cpp +++ b/src/core/frontend/applets/web_browser.cpp @@ -11,14 +11,22 @@ WebBrowserApplet::~WebBrowserApplet() = default; DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; -void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename, - std::function<void()> unpack_romfs_callback, - std::function<void()> finished_callback) { - LOG_INFO(Service_AM, - "(STUBBED) called - No suitable web browser implementation found to open website page " - "at '{}'!", - filename); - finished_callback(); +void DefaultWebBrowserApplet::OpenLocalWebPage( + std::string_view local_url, std::function<void()> extract_romfs_callback, + std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const { + LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}", + local_url); + + callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); +} + +void DefaultWebBrowserApplet::OpenExternalWebPage( + std::string_view external_url, + std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const { + LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}", + external_url); + + callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); } } // namespace Core::Frontend diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index 110e33bc4..1c5ef19a9 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -7,22 +7,34 @@ #include <functional> #include <string_view> +#include "core/hle/service/am/applets/web_types.h" + namespace Core::Frontend { class WebBrowserApplet { public: virtual ~WebBrowserApplet(); - virtual void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, - std::function<void()> finished_callback) = 0; + virtual void OpenLocalWebPage( + std::string_view local_url, std::function<void()> extract_romfs_callback, + std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0; + + virtual void OpenExternalWebPage( + std::string_view external_url, + std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0; }; class DefaultWebBrowserApplet final : public WebBrowserApplet { public: ~DefaultWebBrowserApplet() override; - void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, - std::function<void()> finished_callback) override; + void OpenLocalWebPage(std::string_view local_url, std::function<void()> extract_romfs_callback, + std::function<void(Service::AM::Applets::WebExitReason, std::string)> + callback) const override; + + void OpenExternalWebPage(std::string_view external_url, + std::function<void(Service::AM::Applets::WebExitReason, std::string)> + callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 9a081fbd4..8c1193894 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -84,10 +84,12 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { return; std::lock_guard guard{touch_state->mutex}; - touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / - (framebuffer_layout.screen.right - framebuffer_layout.screen.left); - touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / - (framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); + touch_state->touch_x = + static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / + static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); + touch_state->touch_y = + static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / + static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); touch_state->touch_pressed = true; } diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 3e8780243..276d2b906 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -102,8 +102,8 @@ public: float render_surface_scale = 1.0f; }; - /// Polls window events - virtual void PollEvents() = 0; + /// Called from GPU thread when a frame is displayed. + virtual void OnFrameDisplayed() {} /** * Returns a GraphicsContext that the frontend provides to be used for rendering. diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index c1fbc235b..b9a270a55 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -14,8 +14,8 @@ namespace Layout { template <class T> static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area, float screen_aspect_ratio) { - float scale = std::min(static_cast<float>(window_area.GetWidth()), - window_area.GetHeight() / screen_aspect_ratio); + const float scale = std::min(static_cast<float>(window_area.GetWidth()), + static_cast<float>(window_area.GetHeight()) / screen_aspect_ratio); return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), static_cast<T>(std::round(scale * screen_aspect_ratio))}; } @@ -27,7 +27,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { // so just calculate them both even if the other isn't showing. FramebufferLayout res{width, height, false, {}}; - const float window_aspect_ratio = static_cast<float>(height) / width; + const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); const float emulation_aspect_ratio = EmulationAspectRatio( static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio); @@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { u32 width, height; - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { width = ScreenDocked::Width * res_scale; height = ScreenDocked::Height * res_scale; } else { diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 9da0d2829..de51a754e 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -30,7 +30,12 @@ public: virtual StatusType GetStatus() const { return {}; } - virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { + virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { + return {}; + } + virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low, + [[maybe_unused]] f32 amp_high, + [[maybe_unused]] f32 freq_high) const { return {}; } }; @@ -119,6 +124,13 @@ using ButtonDevice = InputDevice<bool>; using AnalogDevice = InputDevice<std::tuple<float, float>>; /** + * A vibration device is an input device that returns an unsigned byte as status. + * It represents whether the vibration device supports vibration or not. + * If the status returns 1, it supports vibration. Otherwise, it does not support vibration. + */ +using VibrationDevice = InputDevice<u8>; + +/** * A motion status is an object that returns a tuple of accelerometer state vector, * gyroscope state vector, rotation state vector and orientation state matrix. * @@ -151,10 +163,15 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common using MotionDevice = InputDevice<MotionStatus>; /** - * A touch device is an input device that returns a tuple of two floats and a bool. The floats are + * A touch status is an object that returns a tuple of two floats and a bool. The floats are * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed. */ -using TouchDevice = InputDevice<std::tuple<float, float, bool>>; +using TouchStatus = std::tuple<float, float, bool>; + +/** + * A touch device is an input device that returns a touch status object + */ +using TouchDevice = InputDevice<TouchStatus>; /** * A mouse device is an input device that returns a tuple of two floats and four ints. diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/frontend/input_interpreter.cpp new file mode 100644 index 000000000..66ae506cd --- /dev/null +++ b/src/core/frontend/input_interpreter.cpp @@ -0,0 +1,45 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/frontend/input_interpreter.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/sm/sm.h" + +InputInterpreter::InputInterpreter(Core::System& system) + : npad{system.ServiceManager() + .GetService<Service::HID::Hid>("hid") + ->GetAppletResource() + ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {} + +InputInterpreter::~InputInterpreter() = default; + +void InputInterpreter::PollInput() { + const u32 button_state = npad.GetAndResetPressState(); + + previous_index = current_index; + current_index = (current_index + 1) % button_states.size(); + + button_states[current_index] = button_state; +} + +bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { + const bool current_press = + (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; + const bool previous_press = + (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0; + + return current_press && !previous_press; +} + +bool InputInterpreter::IsButtonHeld(HIDButton button) const { + u32 held_buttons{button_states[0]}; + + for (std::size_t i = 1; i < button_states.size(); ++i) { + held_buttons &= button_states[i]; + } + + return (held_buttons & (1U << static_cast<u8>(button))) != 0; +} diff --git a/src/core/frontend/input_interpreter.h b/src/core/frontend/input_interpreter.h new file mode 100644 index 000000000..fea9aebe6 --- /dev/null +++ b/src/core/frontend/input_interpreter.h @@ -0,0 +1,120 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> + +#include "common/common_types.h" + +namespace Core { +class System; +} + +namespace Service::HID { +class Controller_NPad; +} + +enum class HIDButton : u8 { + A, + B, + X, + Y, + LStick, + RStick, + L, + R, + ZL, + ZR, + Plus, + Minus, + + DLeft, + DUp, + DRight, + DDown, + + LStickLeft, + LStickUp, + LStickRight, + LStickDown, + + RStickLeft, + RStickUp, + RStickRight, + RStickDown, + + LeftSL, + LeftSR, + + RightSL, + RightSR, +}; + +/** + * The InputInterpreter class interfaces with HID to retrieve button press states. + * Input is intended to be polled every 50ms so that a button is considered to be + * held down after 400ms has elapsed since the initial button press and subsequent + * repeated presses occur every 50ms. + */ +class InputInterpreter { +public: + explicit InputInterpreter(Core::System& system); + virtual ~InputInterpreter(); + + /// Gets a button state from HID and inserts it into the array of button states. + void PollInput(); + + /** + * The specified button is considered to be pressed once + * if it is currently pressed and not pressed previously. + * + * @param button The button to check. + * + * @returns True when the button is pressed once. + */ + [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const; + + /** + * Checks whether any of the buttons in the parameter list is pressed once. + * + * @tparam HIDButton The buttons to check. + * + * @returns True when at least one of the buttons is pressed once. + */ + template <HIDButton... T> + [[nodiscard]] bool IsAnyButtonPressedOnce() { + return (IsButtonPressedOnce(T) || ...); + } + + /** + * The specified button is considered to be held down if it is pressed in all 9 button states. + * + * @param button The button to check. + * + * @returns True when the button is held down. + */ + [[nodiscard]] bool IsButtonHeld(HIDButton button) const; + + /** + * Checks whether any of the buttons in the parameter list is held down. + * + * @tparam HIDButton The buttons to check. + * + * @returns True when at least one of the buttons is held down. + */ + template <HIDButton... T> + [[nodiscard]] bool IsAnyButtonHeld() { + return (IsButtonHeld(T) || ...); + } + +private: + Service::HID::Controller_NPad& npad; + + /// Stores 9 consecutive button states polled from HID. + std::array<u32, 9> button_states{}; + + std::size_t previous_index{}; + std::size_t current_index{}; +}; |