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.
This commit is contained in:
Billy Laws 2022-04-15 13:18:55 +01:00 committed by PixelyIon
parent 9a8e39cba1
commit d115ce3c05
4 changed files with 241 additions and 0 deletions

View File

@ -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

View File

@ -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<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode) {
switch (appletId) {
case AppletId::LibraryAppletController:
return std::make_shared<ControllerApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
default:
throw exception("Unimplemented Applet: 0x{:X} ({})", static_cast<u32>(appletId), ToString(appletId));
}

View File

@ -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 <input.h>
#include <input/npad.h>
#include <services/applet/common_arguments.h>
#include <services/am/storage/ObjIStorage.h>
#include "controller_applet.h"
namespace skyline::applet {
ControllerApplet::ControllerApplet(const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> 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<u8> 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<service::am::ObjIStorage<ControllerSupportResultInfo>>(state, manager, ControllerSupportResultInfo{
.playerCount = static_cast<i8>(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<ControllerSupportArgOld>());
break;
case ControllerAppletVersion::Version7:
case ControllerAppletVersion::Version8:
handle(arg.as<ControllerSupportArgNew>());
break;
default:
Logger::Warn("Unsupported controller applet version {}", static_cast<u32>(version));
break;
}
}
Result ControllerApplet::Start() {
auto commonArg{PopNormalInput<service::applet::CommonArguments>()};
ControllerAppletVersion appletVersion{commonArg.apiVersion};
auto argPrivate{PopNormalInput<ControllerSupportArgPrivate>()};
// 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<u32>(argPrivate.mode));
normalInputData.pop();
// Return empty result
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<Result>>(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<service::am::IStorage> data) {
normalInputData.emplace(std::move(data));
}
void ControllerApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
}

View File

@ -0,0 +1,126 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/am/applet/IApplet.h>
#include <services/applet/common_arguments.h>
#include <input/npad_device.h>
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<u8 NumControllersSupported>
struct ControllerSupportArg {
using IdentificationColor = std::array<u8, 4>; // RGBA colour code
using ExplainText = std::array<char, 128 + 1>; // 128 chars + null terminator
i8 playerCountMin{};
i8 playerCountMax{};
bool enableTakeOverConnection{};
bool enableLeftJustify{};
bool enablePermitJoyDual{};
bool enableSingleMode{};
bool enableIdentificationColor{};
std::array<IdentificationColor, NumControllersSupported> identification_colors{};
bool enableExplainText{};
std::array<ExplainText, NumControllersSupported> 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<std::shared_ptr<service::am::IStorage>> normalInputData;
template<typename T>
T PopNormalInput() {
auto data{normalInputData.front()->GetSpan().as<T>()};
normalInputData.pop();
return static_cast<T>(data);
}
/**
* @brief Handles the 'ShowControllerSupport' mode of the controller applet
*/
void HandleShowControllerSupport(input::NpadStyleSet styleSet, ControllerAppletVersion version, span<u8> arg);
public:
ControllerApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}