From d115ce3c05b601e936598c1e93eedbb2275d13c4 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Fri, 15 Apr 2022 13:18:55 +0100 Subject: [PATCH] Stub the controller applet Mostly based off of yuzu's implementation, this will need to be extended in the future to open up a UI for configuring controllers according to the applications requirements. --- app/CMakeLists.txt | 1 + .../cpp/skyline/applet/applet_creator.cpp | 3 + .../cpp/skyline/applet/controller_applet.cpp | 111 +++++++++++++++ .../cpp/skyline/applet/controller_applet.h | 126 ++++++++++++++++++ 4 files changed, 241 insertions(+) create mode 100644 app/src/main/cpp/skyline/applet/controller_applet.cpp create mode 100644 app/src/main/cpp/skyline/applet/controller_applet.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 633986f3..458e9eb8 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -248,6 +248,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/am/applet/ILibraryAppletAccessor.cpp ${source_DIR}/skyline/services/am/applet/IApplet.cpp ${source_DIR}/skyline/applet/applet_creator.cpp + ${source_DIR}/skyline/applet/controller_applet.cpp ${source_DIR}/skyline/services/codec/IHardwareOpusDecoder.cpp ${source_DIR}/skyline/services/codec/IHardwareOpusDecoderManager.cpp ${source_DIR}/skyline/services/hid/IHidServer.cpp diff --git a/app/src/main/cpp/skyline/applet/applet_creator.cpp b/app/src/main/cpp/skyline/applet/applet_creator.cpp index b7d97cad..f10d39a0 100644 --- a/app/src/main/cpp/skyline/applet/applet_creator.cpp +++ b/app/src/main/cpp/skyline/applet/applet_creator.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include "controller_applet.h" #include "applet_creator.h" namespace skyline::applet { @@ -11,6 +12,8 @@ namespace skyline::applet { std::shared_ptr onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode) { switch (appletId) { + case AppletId::LibraryAppletController: + return std::make_shared(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode); default: throw exception("Unimplemented Applet: 0x{:X} ({})", static_cast(appletId), ToString(appletId)); } diff --git a/app/src/main/cpp/skyline/applet/controller_applet.cpp b/app/src/main/cpp/skyline/applet/controller_applet.cpp new file mode 100644 index 00000000..75348439 --- /dev/null +++ b/app/src/main/cpp/skyline/applet/controller_applet.cpp @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Copyright 2020 yuzu Emulator Project + +#include +#include +#include +#include +#include "controller_applet.h" + +namespace skyline::applet { + ControllerApplet::ControllerApplet(const DeviceState &state, + service::ServiceManager &manager, + std::shared_ptr onAppletStateChanged, + std::shared_ptr onNormalDataPushFromApplet, + std::shared_ptr onInteractiveDataPushFromApplet, + service::applet::LibraryAppletMode appletMode) + : IApplet(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode) {} + + void ControllerApplet::HandleShowControllerSupport(input::NpadStyleSet styleSet, ControllerAppletVersion version, span arg) { + // Generic macro due to both versions of arguments sharing the same fields but having different layouts + auto handle{[&](auto controllerSupportModeArg) { + Logger::Info("playerCountMin: {}, playerCountMax: {}, " + "enableTakeOverConnection: {}, enableLeftJustify: {}, enablePermitJoyDual: {}, enableSingleMode: {}, " + "enableIdentificationColor: {}, enableExplainText: {}", + controllerSupportModeArg.playerCountMin, controllerSupportModeArg.playerCountMax, + controllerSupportModeArg.enableTakeOverConnection, controllerSupportModeArg.enableLeftJustify, controllerSupportModeArg.enablePermitJoyDual, controllerSupportModeArg.enableSingleMode, + controllerSupportModeArg.enableIdentificationColor, controllerSupportModeArg.enableExplainText); + + // Here is where we would trigger the applet UI + + auto &npad{state.input->npad}; + std::scoped_lock lock{npad.mutex}; + + PushNormalDataAndSignal(std::make_shared>(state, manager, ControllerSupportResultInfo{ + .playerCount = static_cast(controllerSupportModeArg.enableSingleMode ? 1 : npad.GetConnectedControllerCount()), + .selectedId = [&npad]() { + if (npad.controllers[0].device) { + return npad.controllers[0].device->id; + } else { + Logger::Warn("Controller requested but none connected!"); + return input::NpadId::Player1; // Fallback to player 1 + } + }(), + .result = {} + })); + }}; + + switch (version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: + handle(arg.as()); + break; + case ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: + handle(arg.as()); + break; + default: + Logger::Warn("Unsupported controller applet version {}", static_cast(version)); + break; + } + } + Result ControllerApplet::Start() { + auto commonArg{PopNormalInput()}; + ControllerAppletVersion appletVersion{commonArg.apiVersion}; + + auto argPrivate{PopNormalInput()}; + + // Some games such as Cave Story+ set invalid values for the ControllerSupportMode so use argSize to derive it if necessary (from yuzu) + if (argPrivate.mode >= ControllerSupportMode::MaxControllerSupportMode) { + switch (argPrivate.argSize) { + case sizeof(ControllerSupportArgOld): + case sizeof(ControllerSupportArgNew): + argPrivate.mode = ControllerSupportMode::ShowControllerSupport; + break; + default: + // TODO: when we support other modes make sure to add them here too + break; + } + } + + switch (argPrivate.mode) { + case ControllerSupportMode::ShowControllerSupport: + HandleShowControllerSupport(argPrivate.styleSet, appletVersion, normalInputData.front()->GetSpan()); + normalInputData.pop(); + break; + default: + Logger::Warn("Controller applet mode {} is unimplemented", static_cast(argPrivate.mode)); + normalInputData.pop(); + + // Return empty result + PushNormalDataAndSignal(std::make_shared>(state, manager, Result{})); + break; + } + + // Notify the guest that we've finished running + onAppletStateChanged->Signal(); + return {}; + }; + + Result ControllerApplet::GetResult() { + return {}; + } + + void ControllerApplet::PushNormalDataToApplet(std::shared_ptr data) { + normalInputData.emplace(std::move(data)); + } + + void ControllerApplet::PushInteractiveDataToApplet(std::shared_ptr data) {} +} \ No newline at end of file diff --git a/app/src/main/cpp/skyline/applet/controller_applet.h b/app/src/main/cpp/skyline/applet/controller_applet.h new file mode 100644 index 00000000..226332d2 --- /dev/null +++ b/app/src/main/cpp/skyline/applet/controller_applet.h @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once +#include +#include +#include + +namespace skyline::applet { + /** + * @brief The Controller applet is responsible for notifiying the user of a games controller requirements and for allowing user management og controllers + */ + class ControllerApplet : public service::am::IApplet { + private: + /** + * @brief The version of the controller applet interface that an application supports + */ + enum class ControllerAppletVersion : u32 { + Version3 = 0x3, // 1.0.0 - 2.3.0 + Version4 = 0x4, // 3.0.0 - 5.1.0 + Version5 = 0x5, // 6.0.0 - 7.0.1 + // No version 6 + Version7 = 0x7, // 8.0.0 - 10.2.0 + Version8 = 0x8, // 11.0.0+ + }; + + /** + * @brief The requested mode of the controller applet, determines the specific UI that should be shown to the user + */ + enum class ControllerSupportMode : u8 { + ShowControllerSupport = 0, + ShowControllerStrapGuide = 1, + ShowControllerFirmwareUpdate = 2, + ShowControllerKeyRemappingForSystem = 3, + + MaxControllerSupportMode + }; + + /** + * @brief The caller that is requesting the controller applet + */ + enum class ControllerSupportCaller : u8 { + Application = 1, + System = 2 + }; + + /** + * @brief Common set of arguments supplied for all controller applet invocations + */ + struct ControllerSupportArgPrivate { + u32 argPrivateSize; + u32 argSize; + bool flag0; + bool flag1; + ControllerSupportMode mode; + ControllerSupportCaller caller; + input::NpadStyleSet styleSet; + u32 joyHoldType; + }; + static_assert(sizeof(ControllerSupportArgPrivate) == 0x14); + + /** + * @brief Set of arguments required for the ShowControllerSupport mode, templated since the number of controller supported varies based on applet version + */ + template + struct ControllerSupportArg { + using IdentificationColor = std::array; // RGBA colour code + using ExplainText = std::array; // 128 chars + null terminator + + i8 playerCountMin{}; + i8 playerCountMax{}; + bool enableTakeOverConnection{}; + bool enableLeftJustify{}; + bool enablePermitJoyDual{}; + bool enableSingleMode{}; + bool enableIdentificationColor{}; + std::array identification_colors{}; + bool enableExplainText{}; + std::array explain_text{}; + }; + + // Applet versions 3-5 inclusive allow 4 controllers maximum + using ControllerSupportArgOld = ControllerSupportArg<4>; + static_assert(sizeof(ControllerSupportArgOld) == 0x21C); + + // Applet versions 6-8 allow 8 controllers maximum + using ControllerSupportArgNew = ControllerSupportArg<8>; + static_assert(sizeof(ControllerSupportArgNew) == 0x430); + + /** + * @brief The result type of the controller applet controller support mode + */ + struct ControllerSupportResultInfo { + i8 playerCount; + u8 _pad_[3]; + input::NpadId selectedId; + Result result; + }; + static_assert(sizeof(ControllerSupportResultInfo) == 0xC); + + std::queue> normalInputData; + + template + T PopNormalInput() { + auto data{normalInputData.front()->GetSpan().as()}; + normalInputData.pop(); + return static_cast(data); + } + + /** + * @brief Handles the 'ShowControllerSupport' mode of the controller applet + */ + void HandleShowControllerSupport(input::NpadStyleSet styleSet, ControllerAppletVersion version, span arg); + + public: + ControllerApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr onAppletStateChanged, std::shared_ptr onNormalDataPushFromApplet, std::shared_ptr onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode); + + Result Start() override; + + Result GetResult() override; + + void PushNormalDataToApplet(std::shared_ptr data) override; + + void PushInteractiveDataToApplet(std::shared_ptr data) override; + }; +} \ No newline at end of file