mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 12:41:50 +01:00
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:
parent
9a8e39cba1
commit
d115ce3c05
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
111
app/src/main/cpp/skyline/applet/controller_applet.cpp
Normal file
111
app/src/main/cpp/skyline/applet/controller_applet.cpp
Normal 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) {}
|
||||
}
|
126
app/src/main/cpp/skyline/applet/controller_applet.h
Normal file
126
app/src/main/cpp/skyline/applet/controller_applet.h
Normal 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;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user