mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #12780 from german77/touch_resource5
service: hid: Fully implement touch resource
This commit is contained in:
commit
21138b6a86
|
@ -18,9 +18,10 @@ namespace Service::HID {
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
|
|
||||||
std::shared_ptr<HidFirmwareSettings> firmware_settings =
|
std::shared_ptr<HidFirmwareSettings> firmware_settings =
|
||||||
std::make_shared<HidFirmwareSettings>(system);
|
std::make_shared<HidFirmwareSettings>(system);
|
||||||
|
std::shared_ptr<ResourceManager> resource_manager =
|
||||||
|
std::make_shared<ResourceManager>(system, firmware_settings);
|
||||||
|
|
||||||
// TODO: Remove this hack when am is emulated properly.
|
// TODO: Remove this hack when am is emulated properly.
|
||||||
resource_manager->Initialize();
|
resource_manager->Initialize();
|
||||||
|
@ -31,7 +32,7 @@ void LoopProcess(Core::System& system) {
|
||||||
server_manager->RegisterNamedService(
|
server_manager->RegisterNamedService(
|
||||||
"hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
|
"hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
|
||||||
server_manager->RegisterNamedService(
|
server_manager->RegisterNamedService(
|
||||||
"hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager));
|
"hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager, firmware_settings));
|
||||||
server_manager->RegisterNamedService(
|
server_manager->RegisterNamedService(
|
||||||
"hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
|
"hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,37 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "core/hle/service/hid/hid_debug_server.h"
|
#include "core/hle/service/hid/hid_debug_server.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
#include "hid_core/hid_types.h"
|
||||||
#include "hid_core/resource_manager.h"
|
#include "hid_core/resource_manager.h"
|
||||||
|
#include "hid_core/resources/hid_firmware_settings.h"
|
||||||
|
|
||||||
|
#include "hid_core/resources/touch_screen/gesture.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_types.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
|
|
||||||
IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
|
IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
|
||||||
: ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
|
std::shared_ptr<HidFirmwareSettings> settings)
|
||||||
|
: ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{
|
||||||
|
settings} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "DeactivateDebugPad"},
|
{0, nullptr, "DeactivateDebugPad"},
|
||||||
{1, nullptr, "SetDebugPadAutoPilotState"},
|
{1, nullptr, "SetDebugPadAutoPilotState"},
|
||||||
{2, nullptr, "UnsetDebugPadAutoPilotState"},
|
{2, nullptr, "UnsetDebugPadAutoPilotState"},
|
||||||
{10, nullptr, "DeactivateTouchScreen"},
|
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"},
|
||||||
{11, nullptr, "SetTouchScreenAutoPilotState"},
|
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"},
|
||||||
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
|
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"},
|
||||||
{13, nullptr, "GetTouchScreenConfiguration"},
|
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"},
|
||||||
{14, nullptr, "ProcessTouchScreenAutoTune"},
|
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"},
|
||||||
{15, nullptr, "ForceStopTouchScreenManagement"},
|
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"},
|
||||||
{16, nullptr, "ForceRestartTouchScreenManagement"},
|
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"},
|
||||||
{17, nullptr, "IsTouchScreenManaged"},
|
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"},
|
||||||
{20, nullptr, "DeactivateMouse"},
|
{20, nullptr, "DeactivateMouse"},
|
||||||
{21, nullptr, "SetMouseAutoPilotState"},
|
{21, nullptr, "SetMouseAutoPilotState"},
|
||||||
{22, nullptr, "UnsetMouseAutoPilotState"},
|
{22, nullptr, "UnsetMouseAutoPilotState"},
|
||||||
|
@ -37,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
|
||||||
{60, nullptr, "ClearNpadSystemCommonPolicy"},
|
{60, nullptr, "ClearNpadSystemCommonPolicy"},
|
||||||
{61, nullptr, "DeactivateNpad"},
|
{61, nullptr, "DeactivateNpad"},
|
||||||
{62, nullptr, "ForceDisconnectNpad"},
|
{62, nullptr, "ForceDisconnectNpad"},
|
||||||
{91, nullptr, "DeactivateGesture"},
|
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"},
|
||||||
{110, nullptr, "DeactivateHomeButton"},
|
{110, nullptr, "DeactivateHomeButton"},
|
||||||
{111, nullptr, "SetHomeButtonAutoPilotState"},
|
{111, nullptr, "SetHomeButtonAutoPilotState"},
|
||||||
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
|
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
|
||||||
|
@ -150,6 +160,170 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
|
||||||
}
|
}
|
||||||
|
|
||||||
IHidDebugServer::~IHidDebugServer() = default;
|
IHidDebugServer::~IHidDebugServer() = default;
|
||||||
|
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
|
||||||
|
if (!firmware_settings->IsDeviceManaged()) {
|
||||||
|
result = GetResourceManager()->GetTouchScreen()->Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) {
|
||||||
|
AutoPilotState auto_pilot{};
|
||||||
|
auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
|
||||||
|
const auto buffer = ctx.ReadBuffer();
|
||||||
|
|
||||||
|
auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size()));
|
||||||
|
memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState));
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
|
||||||
|
|
||||||
|
const Result result =
|
||||||
|
GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
|
Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
|
||||||
|
const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
|
||||||
|
touchscreen_config, applet_resource_user_id);
|
||||||
|
|
||||||
|
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
|
||||||
|
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
|
||||||
|
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushRaw(touchscreen_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
if (!firmware_settings->IsDeviceManaged()) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
bool is_touch_active{};
|
||||||
|
bool is_gesture_active{};
|
||||||
|
auto touch_screen = GetResourceManager()->GetTouchScreen();
|
||||||
|
auto gesture = GetResourceManager()->GetGesture();
|
||||||
|
|
||||||
|
if (firmware_settings->IsTouchI2cManaged()) {
|
||||||
|
result = touch_screen->IsActive(is_touch_active);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = gesture->IsActive(is_gesture_active);
|
||||||
|
}
|
||||||
|
if (result.IsSuccess() && is_touch_active) {
|
||||||
|
result = touch_screen->Deactivate();
|
||||||
|
}
|
||||||
|
if (result.IsSuccess() && is_gesture_active) {
|
||||||
|
result = gesture->Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
struct Parameters {
|
||||||
|
u32 basic_gesture_id;
|
||||||
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
|
u64 applet_resource_user_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||||
|
|
||||||
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
|
||||||
|
parameters.basic_gesture_id, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
auto touch_screen = GetResourceManager()->GetTouchScreen();
|
||||||
|
auto gesture = GetResourceManager()->GetGesture();
|
||||||
|
|
||||||
|
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
|
||||||
|
result = gesture->Activate();
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result =
|
||||||
|
gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
|
||||||
|
}
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = touch_screen->Activate();
|
||||||
|
}
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = touch_screen->Activate(parameters.applet_resource_user_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
bool is_touch_active{};
|
||||||
|
bool is_gesture_active{};
|
||||||
|
|
||||||
|
Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.Push(is_touch_active | is_gesture_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
|
||||||
|
if (!firmware_settings->IsDeviceManaged()) {
|
||||||
|
result = GetResourceManager()->GetGesture()->Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
|
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
|
||||||
resource_manager->Initialize();
|
resource_manager->Initialize();
|
||||||
|
|
|
@ -11,16 +11,29 @@ class System;
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
class ResourceManager;
|
class ResourceManager;
|
||||||
|
class HidFirmwareSettings;
|
||||||
|
|
||||||
class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
|
class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
|
||||||
public:
|
public:
|
||||||
explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
|
||||||
|
std::shared_ptr<HidFirmwareSettings> settings);
|
||||||
~IHidDebugServer() override;
|
~IHidDebugServer() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void DeactivateTouchScreen(HLERequestContext& ctx);
|
||||||
|
void SetTouchScreenAutoPilotState(HLERequestContext& ctx);
|
||||||
|
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx);
|
||||||
|
void GetTouchScreenConfiguration(HLERequestContext& ctx);
|
||||||
|
void ProcessTouchScreenAutoTune(HLERequestContext& ctx);
|
||||||
|
void ForceStopTouchScreenManagement(HLERequestContext& ctx);
|
||||||
|
void ForceRestartTouchScreenManagement(HLERequestContext& ctx);
|
||||||
|
void IsTouchScreenManaged(HLERequestContext& ctx);
|
||||||
|
void DeactivateGesture(HLERequestContext& ctx);
|
||||||
|
|
||||||
std::shared_ptr<ResourceManager> GetResourceManager();
|
std::shared_ptr<ResourceManager> GetResourceManager();
|
||||||
|
|
||||||
std::shared_ptr<ResourceManager> resource_manager;
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
|
std::shared_ptr<HidFirmwareSettings> firmware_settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
|
@ -990,8 +990,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.IsSuccess()) {
|
if (result.IsSuccess()) {
|
||||||
// TODO: Use gesture id here
|
result = gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
|
||||||
result = gesture->Activate(parameters.applet_resource_user_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -2470,14 +2469,22 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
|
||||||
|
|
||||||
void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
|
void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
|
auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
|
LOG_INFO(Service_HID, "called, touchscreen_config={}, applet_resource_user_id={}",
|
||||||
touchscreen_mode.mode, applet_resource_user_id);
|
touchscreen_config.mode, applet_resource_user_id);
|
||||||
|
|
||||||
|
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
|
||||||
|
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
|
||||||
|
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenConfiguration(
|
||||||
|
touchscreen_config, applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
|
void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
|
||||||
|
@ -2505,11 +2512,12 @@ void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) {
|
||||||
const auto height{rp.Pop<u32>()};
|
const auto height{rp.Pop<u32>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height);
|
|
||||||
|
|
||||||
LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
|
LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
|
||||||
applet_resource_user_id);
|
applet_resource_user_id);
|
||||||
|
|
||||||
|
GetResourceManager()->GetTouchScreen()->SetTouchScreenResolution(width, height,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,9 +155,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
||||||
{1133, nullptr, "StartUsbFirmwareUpdate"},
|
{1133, nullptr, "StartUsbFirmwareUpdate"},
|
||||||
{1134, nullptr, "GetUsbFirmwareUpdateState"},
|
{1134, nullptr, "GetUsbFirmwareUpdateState"},
|
||||||
{1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
|
{1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
|
||||||
{1150, nullptr, "SetTouchScreenMagnification"},
|
{1150, &IHidSystemServer::SetTouchScreenMagnification, "SetTouchScreenMagnification"},
|
||||||
{1151, nullptr, "GetTouchScreenFirmwareVersion"},
|
{1151, &IHidSystemServer::GetTouchScreenFirmwareVersion, "GetTouchScreenFirmwareVersion"},
|
||||||
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
|
{1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"},
|
||||||
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
|
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
|
||||||
{1154, nullptr, "IsFirmwareAvailableForNotification"},
|
{1154, nullptr, "IsFirmwareAvailableForNotification"},
|
||||||
{1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
|
{1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
|
||||||
|
@ -845,12 +845,60 @@ void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContex
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
|
void IHidSystemServer::SetTouchScreenMagnification(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto point1x{rp.Pop<f32>()};
|
||||||
|
const auto point1y{rp.Pop<f32>()};
|
||||||
|
const auto point2x{rp.Pop<f32>()};
|
||||||
|
const auto point2y{rp.Pop<f32>()};
|
||||||
|
|
||||||
Core::HID::TouchScreenConfigurationForNx touchscreen_config{
|
LOG_INFO(Service_HID, "called, point1=-({},{}), point2=({},{})", point1x, point1y, point2x,
|
||||||
.mode = Core::HID::TouchScreenModeForNx::Finger,
|
point2y);
|
||||||
};
|
|
||||||
|
const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenMagnification(
|
||||||
|
point1x, point1y, point2x, point2y);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::GetTouchScreenFirmwareVersion(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
Core::HID::FirmwareVersion firmware{};
|
||||||
|
const auto result = GetResourceManager()->GetTouchScreenFirmwareVersion(firmware);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushRaw(firmware);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, touchscreen_config={}", touchscreen_config.mode);
|
||||||
|
|
||||||
|
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
|
||||||
|
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
|
||||||
|
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Result result =
|
||||||
|
GetResourceManager()->GetTouchScreen()->SetTouchScreenDefaultConfiguration(
|
||||||
|
touchscreen_config);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
|
||||||
|
const Result result =
|
||||||
|
GetResourceManager()->GetTouchScreen()->GetTouchScreenDefaultConfiguration(
|
||||||
|
touchscreen_config);
|
||||||
|
|
||||||
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
|
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
|
||||||
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
|
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
|
||||||
|
@ -858,7 +906,7 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 6};
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
rb.PushRaw(touchscreen_config);
|
rb.PushRaw(touchscreen_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,9 @@ private:
|
||||||
void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
|
void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
|
||||||
void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
|
void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
|
||||||
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
|
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
|
||||||
|
void SetTouchScreenMagnification(HLERequestContext& ctx);
|
||||||
|
void GetTouchScreenFirmwareVersion(HLERequestContext& ctx);
|
||||||
|
void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
|
||||||
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
|
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
|
||||||
void SetForceHandheldStyleVibration(HLERequestContext& ctx);
|
void SetForceHandheldStyleVibration(HLERequestContext& ctx);
|
||||||
void IsUsingCustomButtonConfig(HLERequestContext& ctx);
|
void IsUsingCustomButtonConfig(HLERequestContext& ctx);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "core/hle/service/set/setting_formats/private_settings.h"
|
#include "core/hle/service/set/setting_formats/private_settings.h"
|
||||||
#include "core/hle/service/set/settings_types.h"
|
#include "core/hle/service/set/settings_types.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_types.h"
|
||||||
|
|
||||||
namespace Service::Set {
|
namespace Service::Set {
|
||||||
|
|
||||||
|
@ -257,8 +258,7 @@ struct SystemSettings {
|
||||||
std::array<u8, 0x10> analog_stick_user_calibration_left;
|
std::array<u8, 0x10> analog_stick_user_calibration_left;
|
||||||
std::array<u8, 0x10> analog_stick_user_calibration_right;
|
std::array<u8, 0x10> analog_stick_user_calibration_right;
|
||||||
|
|
||||||
// nn::settings::system::TouchScreenMode
|
TouchScreenMode touch_screen_mode;
|
||||||
s32 touch_screen_mode;
|
|
||||||
INSERT_PADDING_BYTES(0x14); // Reserved
|
INSERT_PADDING_BYTES(0x14); // Reserved
|
||||||
|
|
||||||
TvSettings tv_settings;
|
TvSettings tv_settings;
|
||||||
|
|
|
@ -275,8 +275,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
|
||||||
{184, nullptr, "SetPlatformRegion"},
|
{184, nullptr, "SetPlatformRegion"},
|
||||||
{185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
|
{185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
|
||||||
{186, nullptr, "GetMemoryUsageRateFlag"},
|
{186, nullptr, "GetMemoryUsageRateFlag"},
|
||||||
{187, nullptr, "GetTouchScreenMode"},
|
{187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"},
|
||||||
{188, nullptr, "SetTouchScreenMode"},
|
{188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"},
|
||||||
{189, nullptr, "GetButtonConfigSettingsFull"},
|
{189, nullptr, "GetButtonConfigSettingsFull"},
|
||||||
{190, nullptr, "SetButtonConfigSettingsFull"},
|
{190, nullptr, "SetButtonConfigSettingsFull"},
|
||||||
{191, nullptr, "GetButtonConfigSettingsEmbedded"},
|
{191, nullptr, "GetButtonConfigSettingsEmbedded"},
|
||||||
|
@ -1395,6 +1395,28 @@ void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
|
||||||
rb.Push(0);
|
rb.Push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) {
|
||||||
|
TouchScreenMode touch_screen_mode{};
|
||||||
|
auto res = GetTouchScreenMode(touch_screen_mode);
|
||||||
|
|
||||||
|
LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(res);
|
||||||
|
rb.PushEnum(touch_screen_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>();
|
||||||
|
auto res = SetTouchScreenMode(touch_screen_mode);
|
||||||
|
|
||||||
|
LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(res);
|
||||||
|
}
|
||||||
|
|
||||||
void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) {
|
void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) {
|
||||||
LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag);
|
LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag);
|
||||||
|
|
||||||
|
@ -1670,4 +1692,15 @@ Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const {
|
||||||
|
touch_screen_mode = m_system_settings.touch_screen_mode;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) {
|
||||||
|
m_system_settings.touch_screen_mode = touch_screen_mode;
|
||||||
|
SetSaveNeeded();
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::Set
|
} // namespace Service::Set
|
||||||
|
|
|
@ -74,6 +74,8 @@ public:
|
||||||
Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
|
Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
|
||||||
Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
|
Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||||
const Service::PSC::Time::SteadyClockTimePoint& time_point);
|
const Service::PSC::Time::SteadyClockTimePoint& time_point);
|
||||||
|
Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const;
|
||||||
|
Result SetTouchScreenMode(TouchScreenMode touch_screen_mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetLanguageCode(HLERequestContext& ctx);
|
void SetLanguageCode(HLERequestContext& ctx);
|
||||||
|
@ -154,6 +156,8 @@ private:
|
||||||
void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
|
void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
|
||||||
void GetHomeMenuScheme(HLERequestContext& ctx);
|
void GetHomeMenuScheme(HLERequestContext& ctx);
|
||||||
void GetHomeMenuSchemeModel(HLERequestContext& ctx);
|
void GetHomeMenuSchemeModel(HLERequestContext& ctx);
|
||||||
|
void GetTouchScreenMode(HLERequestContext& ctx);
|
||||||
|
void SetTouchScreenMode(HLERequestContext& ctx);
|
||||||
void GetFieldTestingFlag(HLERequestContext& ctx);
|
void GetFieldTestingFlag(HLERequestContext& ctx);
|
||||||
void GetPanelCrcMode(HLERequestContext& ctx);
|
void GetPanelCrcMode(HLERequestContext& ctx);
|
||||||
void SetPanelCrcMode(HLERequestContext& ctx);
|
void SetPanelCrcMode(HLERequestContext& ctx);
|
||||||
|
|
|
@ -190,9 +190,9 @@ void Config::ReadTouchscreenValues() {
|
||||||
Settings::values.touchscreen.rotation_angle =
|
Settings::values.touchscreen.rotation_angle =
|
||||||
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
|
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
|
||||||
Settings::values.touchscreen.diameter_x =
|
Settings::values.touchscreen.diameter_x =
|
||||||
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15));
|
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90));
|
||||||
Settings::values.touchscreen.diameter_y =
|
Settings::values.touchscreen.diameter_y =
|
||||||
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
|
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 90));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::ReadAudioValues() {
|
void Config::ReadAudioValues() {
|
||||||
|
@ -478,9 +478,9 @@ void Config::SaveTouchscreenValues() {
|
||||||
WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
|
WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
|
||||||
std::make_optional(static_cast<u32>(0)));
|
std::make_optional(static_cast<u32>(0)));
|
||||||
WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
|
WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
|
||||||
std::make_optional(static_cast<u32>(15)));
|
std::make_optional(static_cast<u32>(90)));
|
||||||
WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
|
WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
|
||||||
std::make_optional(static_cast<u32>(15)));
|
std::make_optional(static_cast<u32>(90)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SaveMotionTouchValues() {
|
void Config::SaveMotionTouchValues() {
|
||||||
|
|
|
@ -99,9 +99,14 @@ add_library(hid_core STATIC
|
||||||
resources/system_buttons/system_button_types.h
|
resources/system_buttons/system_button_types.h
|
||||||
resources/touch_screen/gesture.cpp
|
resources/touch_screen/gesture.cpp
|
||||||
resources/touch_screen/gesture.h
|
resources/touch_screen/gesture.h
|
||||||
resources/touch_screen/gesture_types.h
|
resources/touch_screen/gesture_handler.cpp
|
||||||
|
resources/touch_screen/gesture_handler.h
|
||||||
resources/touch_screen/touch_screen.cpp
|
resources/touch_screen/touch_screen.cpp
|
||||||
resources/touch_screen/touch_screen.h
|
resources/touch_screen/touch_screen.h
|
||||||
|
resources/touch_screen/touch_screen_driver.cpp
|
||||||
|
resources/touch_screen/touch_screen_driver.h
|
||||||
|
resources/touch_screen/touch_screen_resource.cpp
|
||||||
|
resources/touch_screen/touch_screen_resource.h
|
||||||
resources/touch_screen/touch_types.h
|
resources/touch_screen/touch_types.h
|
||||||
resources/unique_pad/unique_pad.cpp
|
resources/unique_pad/unique_pad.cpp
|
||||||
resources/unique_pad/unique_pad.h
|
resources/unique_pad/unique_pad.h
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
|
|
||||||
constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
|
constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
|
||||||
|
|
||||||
|
constexpr Result ResultTouchNotInitialized{ErrorModule::HID, 41};
|
||||||
|
constexpr Result ResultTouchOverflow{ErrorModule::HID, 42};
|
||||||
|
|
||||||
constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
|
constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
|
||||||
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
|
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
|
||||||
|
|
||||||
|
@ -23,6 +27,10 @@ constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
|
||||||
constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
|
constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
|
||||||
constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
|
constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
|
||||||
constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};
|
constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};
|
||||||
|
|
||||||
|
constexpr Result ResultGestureOverflow{ErrorModule::HID, 522};
|
||||||
|
constexpr Result ResultGestureNotInitialized{ErrorModule::HID, 523};
|
||||||
|
|
||||||
constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};
|
constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};
|
||||||
|
|
||||||
constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
|
constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
|
||||||
|
|
|
@ -299,12 +299,6 @@ enum class GyroscopeZeroDriftMode : u32 {
|
||||||
Tight = 2,
|
Tight = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is nn::settings::system::TouchScreenMode
|
|
||||||
enum class TouchScreenMode : u32 {
|
|
||||||
Stylus = 0,
|
|
||||||
Standard = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::hid::TouchScreenModeForNx
|
// This is nn::hid::TouchScreenModeForNx
|
||||||
enum class TouchScreenModeForNx : u8 {
|
enum class TouchScreenModeForNx : u8 {
|
||||||
UseSystemSetting,
|
UseSystemSetting,
|
||||||
|
@ -354,18 +348,6 @@ struct TouchAttribute {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
|
static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
|
||||||
|
|
||||||
// This is nn::hid::TouchState
|
|
||||||
struct TouchState {
|
|
||||||
u64 delta_time{};
|
|
||||||
TouchAttribute attribute{};
|
|
||||||
u32 finger{};
|
|
||||||
Common::Point<u32> position{};
|
|
||||||
u32 diameter_x{};
|
|
||||||
u32 diameter_y{};
|
|
||||||
u32 rotation_angle{};
|
|
||||||
};
|
|
||||||
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
|
|
||||||
|
|
||||||
struct TouchFinger {
|
struct TouchFinger {
|
||||||
u64 last_touch{};
|
u64 last_touch{};
|
||||||
Common::Point<float> position{};
|
Common::Point<float> position{};
|
||||||
|
@ -756,4 +738,14 @@ struct UniquePadId {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
|
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::hid::system::FirmwareVersion
|
||||||
|
struct FirmwareVersion {
|
||||||
|
u8 major;
|
||||||
|
u8 minor;
|
||||||
|
u8 micro;
|
||||||
|
u8 revision;
|
||||||
|
std::array<char, 0xc> device_identifier;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(FirmwareVersion) == 0x10, "FirmwareVersion is an invalid size");
|
||||||
|
|
||||||
} // namespace Core::HID
|
} // namespace Core::HID
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "hid_core/resources/applet_resource.h"
|
#include "hid_core/resources/applet_resource.h"
|
||||||
#include "hid_core/resources/debug_pad/debug_pad.h"
|
#include "hid_core/resources/debug_pad/debug_pad.h"
|
||||||
#include "hid_core/resources/digitizer/digitizer.h"
|
#include "hid_core/resources/digitizer/digitizer.h"
|
||||||
|
#include "hid_core/resources/hid_firmware_settings.h"
|
||||||
#include "hid_core/resources/keyboard/keyboard.h"
|
#include "hid_core/resources/keyboard/keyboard.h"
|
||||||
#include "hid_core/resources/mouse/debug_mouse.h"
|
#include "hid_core/resources/mouse/debug_mouse.h"
|
||||||
#include "hid_core/resources/mouse/mouse.h"
|
#include "hid_core/resources/mouse/mouse.h"
|
||||||
|
@ -29,6 +30,8 @@
|
||||||
#include "hid_core/resources/system_buttons/sleep_button.h"
|
#include "hid_core/resources/system_buttons/sleep_button.h"
|
||||||
#include "hid_core/resources/touch_screen/gesture.h"
|
#include "hid_core/resources/touch_screen/gesture.h"
|
||||||
#include "hid_core/resources/touch_screen/touch_screen.h"
|
#include "hid_core/resources/touch_screen/touch_screen.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_driver.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_resource.h"
|
||||||
#include "hid_core/resources/unique_pad/unique_pad.h"
|
#include "hid_core/resources/unique_pad/unique_pad.h"
|
||||||
#include "hid_core/resources/vibration/gc_vibration_device.h"
|
#include "hid_core/resources/vibration/gc_vibration_device.h"
|
||||||
#include "hid_core/resources/vibration/n64_vibration_device.h"
|
#include "hid_core/resources/vibration/n64_vibration_device.h"
|
||||||
|
@ -45,12 +48,16 @@ constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; //
|
||||||
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
||||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
||||||
|
|
||||||
ResourceManager::ResourceManager(Core::System& system_)
|
ResourceManager::ResourceManager(Core::System& system_,
|
||||||
: system{system_}, service_context{system_, "hid"} {
|
std::shared_ptr<HidFirmwareSettings> settings)
|
||||||
|
: firmware_settings{settings}, system{system_}, service_context{system_, "hid"} {
|
||||||
applet_resource = std::make_shared<AppletResource>(system);
|
applet_resource = std::make_shared<AppletResource>(system);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceManager::~ResourceManager() = default;
|
ResourceManager::~ResourceManager() {
|
||||||
|
system.CoreTiming().UnscheduleEvent(touch_update_event);
|
||||||
|
input_event->Finalize();
|
||||||
|
};
|
||||||
|
|
||||||
void ResourceManager::Initialize() {
|
void ResourceManager::Initialize() {
|
||||||
if (is_initialized) {
|
if (is_initialized) {
|
||||||
|
@ -59,7 +66,9 @@ void ResourceManager::Initialize() {
|
||||||
|
|
||||||
system.HIDCore().ReloadInputDevices();
|
system.HIDCore().ReloadInputDevices();
|
||||||
|
|
||||||
handheld_config = std::make_shared<HandheldConfig>();
|
input_event = service_context.CreateEvent("ResourceManager:InputEvent");
|
||||||
|
|
||||||
|
InitializeHandheldConfig();
|
||||||
InitializeHidCommonSampler();
|
InitializeHidCommonSampler();
|
||||||
InitializeTouchScreenSampler();
|
InitializeTouchScreenSampler();
|
||||||
InitializeConsoleSixAxisSampler();
|
InitializeConsoleSixAxisSampler();
|
||||||
|
@ -154,6 +163,7 @@ Result ResourceManager::CreateAppletResource(u64 aruid) {
|
||||||
npad->Activate();
|
npad->Activate();
|
||||||
six_axis->Activate();
|
six_axis->Activate();
|
||||||
touch_screen->Activate();
|
touch_screen->Activate();
|
||||||
|
gesture->Activate();
|
||||||
|
|
||||||
return GetNpad()->ActivateNpadResource(aruid);
|
return GetNpad()->ActivateNpadResource(aruid);
|
||||||
}
|
}
|
||||||
|
@ -163,6 +173,17 @@ Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
|
||||||
return applet_resource->CreateAppletResource(aruid);
|
return applet_resource->CreateAppletResource(aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceManager::InitializeHandheldConfig() {
|
||||||
|
handheld_config = std::make_shared<HandheldConfig>();
|
||||||
|
handheld_config->is_handheld_hid_enabled = true;
|
||||||
|
handheld_config->is_joycon_rail_enabled = true;
|
||||||
|
handheld_config->is_force_handheld_style_vibration = false;
|
||||||
|
handheld_config->is_force_handheld = false;
|
||||||
|
if (firmware_settings->IsHandheldForced()) {
|
||||||
|
handheld_config->is_joycon_rail_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceManager::InitializeHidCommonSampler() {
|
void ResourceManager::InitializeHidCommonSampler() {
|
||||||
debug_pad = std::make_shared<DebugPad>(system.HIDCore());
|
debug_pad = std::make_shared<DebugPad>(system.HIDCore());
|
||||||
mouse = std::make_shared<Mouse>(system.HIDCore());
|
mouse = std::make_shared<Mouse>(system.HIDCore());
|
||||||
|
@ -170,7 +191,6 @@ void ResourceManager::InitializeHidCommonSampler() {
|
||||||
keyboard = std::make_shared<Keyboard>(system.HIDCore());
|
keyboard = std::make_shared<Keyboard>(system.HIDCore());
|
||||||
unique_pad = std::make_shared<UniquePad>(system.HIDCore());
|
unique_pad = std::make_shared<UniquePad>(system.HIDCore());
|
||||||
npad = std::make_shared<NPad>(system.HIDCore(), service_context);
|
npad = std::make_shared<NPad>(system.HIDCore(), service_context);
|
||||||
gesture = std::make_shared<Gesture>(system.HIDCore());
|
|
||||||
home_button = std::make_shared<HomeButton>(system.HIDCore());
|
home_button = std::make_shared<HomeButton>(system.HIDCore());
|
||||||
sleep_button = std::make_shared<SleepButton>(system.HIDCore());
|
sleep_button = std::make_shared<SleepButton>(system.HIDCore());
|
||||||
capture_button = std::make_shared<CaptureButton>(system.HIDCore());
|
capture_button = std::make_shared<CaptureButton>(system.HIDCore());
|
||||||
|
@ -185,7 +205,8 @@ void ResourceManager::InitializeHidCommonSampler() {
|
||||||
|
|
||||||
const auto settings =
|
const auto settings =
|
||||||
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||||
npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings);
|
npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, input_event,
|
||||||
|
&input_mutex, settings);
|
||||||
|
|
||||||
six_axis->SetAppletResource(applet_resource, &shared_mutex);
|
six_axis->SetAppletResource(applet_resource, &shared_mutex);
|
||||||
mouse->SetAppletResource(applet_resource, &shared_mutex);
|
mouse->SetAppletResource(applet_resource, &shared_mutex);
|
||||||
|
@ -196,11 +217,25 @@ void ResourceManager::InitializeHidCommonSampler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::InitializeTouchScreenSampler() {
|
void ResourceManager::InitializeTouchScreenSampler() {
|
||||||
gesture = std::make_shared<Gesture>(system.HIDCore());
|
// This is nn.hid.TouchScreenSampler
|
||||||
touch_screen = std::make_shared<TouchScreen>(system.HIDCore());
|
touch_resource = std::make_shared<TouchResource>(system);
|
||||||
|
touch_driver = std::make_shared<TouchDriver>(system.HIDCore());
|
||||||
|
touch_screen = std::make_shared<TouchScreen>(touch_resource);
|
||||||
|
gesture = std::make_shared<Gesture>(touch_resource);
|
||||||
|
|
||||||
touch_screen->SetAppletResource(applet_resource, &shared_mutex);
|
touch_update_event = Core::Timing::CreateEvent(
|
||||||
gesture->SetAppletResource(applet_resource, &shared_mutex);
|
"HID::TouchUpdateCallback",
|
||||||
|
[this](s64 time,
|
||||||
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
touch_resource->OnTouchUpdate(time);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
|
touch_resource->SetTouchDriver(touch_driver);
|
||||||
|
touch_resource->SetAppletResource(applet_resource, &shared_mutex);
|
||||||
|
touch_resource->SetInputEvent(input_event, &input_mutex);
|
||||||
|
touch_resource->SetHandheldConfig(handheld_config);
|
||||||
|
touch_resource->SetTimerEvent(touch_update_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::InitializeConsoleSixAxisSampler() {
|
void ResourceManager::InitializeConsoleSixAxisSampler() {
|
||||||
|
@ -388,13 +423,15 @@ Result ResourceManager::SendVibrationValue(u64 aruid,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ResourceManager::GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
|
void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
|
||||||
auto& core_timing = system.CoreTiming();
|
auto& core_timing = system.CoreTiming();
|
||||||
debug_pad->OnUpdate(core_timing);
|
debug_pad->OnUpdate(core_timing);
|
||||||
digitizer->OnUpdate(core_timing);
|
digitizer->OnUpdate(core_timing);
|
||||||
unique_pad->OnUpdate(core_timing);
|
unique_pad->OnUpdate(core_timing);
|
||||||
gesture->OnUpdate(core_timing);
|
|
||||||
touch_screen->OnUpdate(core_timing);
|
|
||||||
palma->OnUpdate(core_timing);
|
palma->OnUpdate(core_timing);
|
||||||
home_button->OnUpdate(core_timing);
|
home_button->OnUpdate(core_timing);
|
||||||
sleep_button->OnUpdate(core_timing);
|
sleep_button->OnUpdate(core_timing);
|
||||||
|
|
|
@ -11,6 +11,7 @@ class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core::HID {
|
||||||
|
struct FirmwareVersion;
|
||||||
struct VibrationDeviceHandle;
|
struct VibrationDeviceHandle;
|
||||||
struct VibrationValue;
|
struct VibrationValue;
|
||||||
struct VibrationDeviceInfo;
|
struct VibrationDeviceInfo;
|
||||||
|
@ -21,8 +22,9 @@ struct EventType;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
class KEvent;
|
||||||
class KSharedMemory;
|
class KSharedMemory;
|
||||||
}
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
class AppletResource;
|
class AppletResource;
|
||||||
|
@ -33,6 +35,7 @@ class DebugMouse;
|
||||||
class DebugPad;
|
class DebugPad;
|
||||||
class Digitizer;
|
class Digitizer;
|
||||||
class Gesture;
|
class Gesture;
|
||||||
|
class HidFirmwareSettings;
|
||||||
class HomeButton;
|
class HomeButton;
|
||||||
class Keyboard;
|
class Keyboard;
|
||||||
class Mouse;
|
class Mouse;
|
||||||
|
@ -42,6 +45,8 @@ class SevenSixAxis;
|
||||||
class SixAxis;
|
class SixAxis;
|
||||||
class SleepButton;
|
class SleepButton;
|
||||||
class TouchScreen;
|
class TouchScreen;
|
||||||
|
class TouchDriver;
|
||||||
|
class TouchResource;
|
||||||
class UniquePad;
|
class UniquePad;
|
||||||
class NpadVibrationBase;
|
class NpadVibrationBase;
|
||||||
class NpadN64VibrationDevice;
|
class NpadN64VibrationDevice;
|
||||||
|
@ -52,7 +57,7 @@ struct HandheldConfig;
|
||||||
class ResourceManager {
|
class ResourceManager {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ResourceManager(Core::System& system_);
|
explicit ResourceManager(Core::System& system_, std::shared_ptr<HidFirmwareSettings> settings);
|
||||||
~ResourceManager();
|
~ResourceManager();
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
@ -102,6 +107,8 @@ public:
|
||||||
Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle,
|
Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle,
|
||||||
const Core::HID::VibrationValue& value);
|
const Core::HID::VibrationValue& value);
|
||||||
|
|
||||||
|
Result GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const;
|
||||||
|
|
||||||
void UpdateControllers(std::chrono::nanoseconds ns_late);
|
void UpdateControllers(std::chrono::nanoseconds ns_late);
|
||||||
void UpdateNpad(std::chrono::nanoseconds ns_late);
|
void UpdateNpad(std::chrono::nanoseconds ns_late);
|
||||||
void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
|
void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
|
||||||
|
@ -109,6 +116,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result CreateAppletResourceImpl(u64 aruid);
|
Result CreateAppletResourceImpl(u64 aruid);
|
||||||
|
void InitializeHandheldConfig();
|
||||||
void InitializeHidCommonSampler();
|
void InitializeHidCommonSampler();
|
||||||
void InitializeTouchScreenSampler();
|
void InitializeTouchScreenSampler();
|
||||||
void InitializeConsoleSixAxisSampler();
|
void InitializeConsoleSixAxisSampler();
|
||||||
|
@ -117,37 +125,46 @@ private:
|
||||||
bool is_initialized{false};
|
bool is_initialized{false};
|
||||||
|
|
||||||
mutable std::recursive_mutex shared_mutex;
|
mutable std::recursive_mutex shared_mutex;
|
||||||
std::shared_ptr<AppletResource> applet_resource = nullptr;
|
std::shared_ptr<AppletResource> applet_resource{nullptr};
|
||||||
|
|
||||||
std::shared_ptr<CaptureButton> capture_button = nullptr;
|
mutable std::mutex input_mutex;
|
||||||
std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
|
Kernel::KEvent* input_event{nullptr};
|
||||||
std::shared_ptr<DebugMouse> debug_mouse = nullptr;
|
|
||||||
std::shared_ptr<DebugPad> debug_pad = nullptr;
|
|
||||||
std::shared_ptr<Digitizer> digitizer = nullptr;
|
|
||||||
std::shared_ptr<Gesture> gesture = nullptr;
|
|
||||||
std::shared_ptr<HomeButton> home_button = nullptr;
|
|
||||||
std::shared_ptr<Keyboard> keyboard = nullptr;
|
|
||||||
std::shared_ptr<Mouse> mouse = nullptr;
|
|
||||||
std::shared_ptr<NPad> npad = nullptr;
|
|
||||||
std::shared_ptr<Palma> palma = nullptr;
|
|
||||||
std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr;
|
|
||||||
std::shared_ptr<SixAxis> six_axis = nullptr;
|
|
||||||
std::shared_ptr<SleepButton> sleep_button = nullptr;
|
|
||||||
std::shared_ptr<TouchScreen> touch_screen = nullptr;
|
|
||||||
std::shared_ptr<UniquePad> unique_pad = nullptr;
|
|
||||||
|
|
||||||
std::shared_ptr<HandheldConfig> handheld_config = nullptr;
|
std::shared_ptr<HandheldConfig> handheld_config{nullptr};
|
||||||
|
std::shared_ptr<HidFirmwareSettings> firmware_settings{nullptr};
|
||||||
|
|
||||||
|
std::shared_ptr<CaptureButton> capture_button{nullptr};
|
||||||
|
std::shared_ptr<ConsoleSixAxis> console_six_axis{nullptr};
|
||||||
|
std::shared_ptr<DebugMouse> debug_mouse{nullptr};
|
||||||
|
std::shared_ptr<DebugPad> debug_pad{nullptr};
|
||||||
|
std::shared_ptr<Digitizer> digitizer{nullptr};
|
||||||
|
std::shared_ptr<HomeButton> home_button{nullptr};
|
||||||
|
std::shared_ptr<Keyboard> keyboard{nullptr};
|
||||||
|
std::shared_ptr<Mouse> mouse{nullptr};
|
||||||
|
std::shared_ptr<NPad> npad{nullptr};
|
||||||
|
std::shared_ptr<Palma> palma{nullptr};
|
||||||
|
std::shared_ptr<SevenSixAxis> seven_six_axis{nullptr};
|
||||||
|
std::shared_ptr<SixAxis> six_axis{nullptr};
|
||||||
|
std::shared_ptr<SleepButton> sleep_button{nullptr};
|
||||||
|
std::shared_ptr<UniquePad> unique_pad{nullptr};
|
||||||
|
|
||||||
// TODO: Create these resources
|
// TODO: Create these resources
|
||||||
// std::shared_ptr<AudioControl> audio_control = nullptr;
|
// std::shared_ptr<AudioControl> audio_control{nullptr};
|
||||||
// std::shared_ptr<ButtonConfig> button_config = nullptr;
|
// std::shared_ptr<ButtonConfig> button_config{nullptr};
|
||||||
// std::shared_ptr<Config> config = nullptr;
|
// std::shared_ptr<Config> config{nullptr};
|
||||||
// std::shared_ptr<Connection> connection = nullptr;
|
// std::shared_ptr<Connection> connection{nullptr};
|
||||||
// std::shared_ptr<CustomConfig> custom_config = nullptr;
|
// std::shared_ptr<CustomConfig> custom_config{nullptr};
|
||||||
// std::shared_ptr<Digitizer> digitizer = nullptr;
|
// std::shared_ptr<Digitizer> digitizer{nullptr};
|
||||||
// std::shared_ptr<Hdls> hdls = nullptr;
|
// std::shared_ptr<Hdls> hdls{nullptr};
|
||||||
// std::shared_ptr<PlayReport> play_report = nullptr;
|
// std::shared_ptr<PlayReport> play_report{nullptr};
|
||||||
// std::shared_ptr<Rail> rail = nullptr;
|
// std::shared_ptr<Rail> rail{nullptr};
|
||||||
|
|
||||||
|
// Touch Resources
|
||||||
|
std::shared_ptr<Gesture> gesture{nullptr};
|
||||||
|
std::shared_ptr<TouchScreen> touch_screen{nullptr};
|
||||||
|
std::shared_ptr<TouchResource> touch_resource{nullptr};
|
||||||
|
std::shared_ptr<TouchDriver> touch_driver{nullptr};
|
||||||
|
std::shared_ptr<Core::Timing::EventType> touch_update_event{nullptr};
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
KernelHelpers::ServiceContext service_context;
|
KernelHelpers::ServiceContext service_context;
|
||||||
|
@ -162,12 +179,12 @@ public:
|
||||||
private:
|
private:
|
||||||
void GetSharedMemoryHandle(HLERequestContext& ctx);
|
void GetSharedMemoryHandle(HLERequestContext& ctx);
|
||||||
|
|
||||||
std::shared_ptr<Core::Timing::EventType> npad_update_event;
|
std::shared_ptr<Core::Timing::EventType> npad_update_event{nullptr};
|
||||||
std::shared_ptr<Core::Timing::EventType> default_update_event;
|
std::shared_ptr<Core::Timing::EventType> default_update_event{nullptr};
|
||||||
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event{nullptr};
|
||||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
std::shared_ptr<Core::Timing::EventType> motion_update_event{nullptr};
|
||||||
|
|
||||||
u64 aruid;
|
u64 aruid{};
|
||||||
std::shared_ptr<ResourceManager> resource_manager;
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,12 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
|
||||||
data[index].aruid = 0;
|
data[index].aruid = 0;
|
||||||
|
|
||||||
registration_list.flag[index] = RegistrationStatus::PendingDelete;
|
registration_list.flag[index] = RegistrationStatus::PendingDelete;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||||
|
if (registration_list.flag[i] == RegistrationStatus::Initialized) {
|
||||||
|
active_aruid = registration_list.aruid[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppletResource::FreeAppletResourceId(u64 aruid) {
|
void AppletResource::FreeAppletResourceId(u64 aruid) {
|
||||||
|
|
|
@ -13,11 +13,12 @@
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
} // namespace Core
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
class KEvent;
|
||||||
class KSharedMemory;
|
class KSharedMemory;
|
||||||
}
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
struct SharedMemoryFormat;
|
struct SharedMemoryFormat;
|
||||||
|
@ -73,7 +74,8 @@ struct AppletResourceHolder {
|
||||||
std::recursive_mutex* shared_mutex{nullptr};
|
std::recursive_mutex* shared_mutex{nullptr};
|
||||||
NPadResource* shared_npad_resource{nullptr};
|
NPadResource* shared_npad_resource{nullptr};
|
||||||
std::shared_ptr<HandheldConfig> handheld_config{nullptr};
|
std::shared_ptr<HandheldConfig> handheld_config{nullptr};
|
||||||
long* handle_1;
|
Kernel::KEvent* input_event{nullptr};
|
||||||
|
std::mutex* input_mutex{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
class AppletResource {
|
class AppletResource {
|
||||||
|
|
|
@ -1070,11 +1070,14 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
|
||||||
void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
|
void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
|
||||||
std::recursive_mutex* shared_mutex,
|
std::recursive_mutex* shared_mutex,
|
||||||
std::shared_ptr<HandheldConfig> handheld_config,
|
std::shared_ptr<HandheldConfig> handheld_config,
|
||||||
|
Kernel::KEvent* input_event, std::mutex* input_mutex,
|
||||||
std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
|
std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
|
||||||
applet_resource_holder.applet_resource = resource;
|
applet_resource_holder.applet_resource = resource;
|
||||||
applet_resource_holder.shared_mutex = shared_mutex;
|
applet_resource_holder.shared_mutex = shared_mutex;
|
||||||
applet_resource_holder.shared_npad_resource = &npad_resource;
|
applet_resource_holder.shared_npad_resource = &npad_resource;
|
||||||
applet_resource_holder.handheld_config = handheld_config;
|
applet_resource_holder.handheld_config = handheld_config;
|
||||||
|
applet_resource_holder.input_event = input_event;
|
||||||
|
applet_resource_holder.input_mutex = input_mutex;
|
||||||
|
|
||||||
vibration_handler.SetSettingsService(settings);
|
vibration_handler.SetSettingsService(settings);
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,7 @@ public:
|
||||||
void SetNpadExternals(std::shared_ptr<AppletResource> resource,
|
void SetNpadExternals(std::shared_ptr<AppletResource> resource,
|
||||||
std::recursive_mutex* shared_mutex,
|
std::recursive_mutex* shared_mutex,
|
||||||
std::shared_ptr<HandheldConfig> handheld_config,
|
std::shared_ptr<HandheldConfig> handheld_config,
|
||||||
|
Kernel::KEvent* input_event, std::mutex* input_mutex,
|
||||||
std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
|
std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
|
||||||
|
|
||||||
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
|
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
|
||||||
|
@ -204,9 +205,6 @@ private:
|
||||||
std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
|
std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
|
||||||
NpadVibration vibration_handler{};
|
NpadVibration vibration_handler{};
|
||||||
|
|
||||||
Kernel::KEvent* input_event{nullptr};
|
|
||||||
std::mutex* input_mutex{nullptr};
|
|
||||||
|
|
||||||
std::atomic<u64> press_state{};
|
std::atomic<u64> press_state{};
|
||||||
std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
|
std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
|
||||||
controller_data{};
|
controller_data{};
|
||||||
|
|
|
@ -72,6 +72,12 @@ void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
|
||||||
state[aruid_index] = {};
|
state[aruid_index] = {};
|
||||||
registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
|
registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||||
|
if (registration_list.flag[i] == RegistrationStatus::Initialized) {
|
||||||
|
active_data_aruid = registration_list.aruid[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) {
|
void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) {
|
||||||
|
|
|
@ -1,366 +1,53 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "common/math_util.h"
|
|
||||||
#include "common/settings.h"
|
|
||||||
#include "core/frontend/emu_window.h"
|
|
||||||
#include "hid_core/frontend/emulated_console.h"
|
|
||||||
#include "hid_core/hid_core.h"
|
|
||||||
#include "hid_core/resources/applet_resource.h"
|
|
||||||
#include "hid_core/resources/shared_memory_format.h"
|
|
||||||
#include "hid_core/resources/touch_screen/gesture.h"
|
#include "hid_core/resources/touch_screen/gesture.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_resource.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
|
|
||||||
constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
|
|
||||||
constexpr f32 angle_threshold = 0.015f; // Threshold in radians
|
|
||||||
constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
|
|
||||||
constexpr f32 press_delay = 0.5f; // Time in seconds
|
|
||||||
constexpr f32 double_tap_delay = 0.35f; // Time in seconds
|
|
||||||
|
|
||||||
constexpr f32 Square(s32 num) {
|
Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
|
||||||
return static_cast<f32>(num * num);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
|
|
||||||
console = hid_core.GetEmulatedConsole();
|
|
||||||
}
|
|
||||||
Gesture::~Gesture() = default;
|
Gesture::~Gesture() = default;
|
||||||
|
|
||||||
void Gesture::OnInit() {
|
Result Gesture::Activate() {
|
||||||
std::scoped_lock shared_lock{*shared_mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const u64 aruid = applet_resource->GetActiveAruid();
|
|
||||||
auto* data = applet_resource->GetAruidData(aruid);
|
|
||||||
|
|
||||||
if (data == nullptr || !data->flag.is_assigned) {
|
// TODO: Result result = CreateThread();
|
||||||
return;
|
Result result = ResultSuccess;
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_memory = &data->shared_memory_format->gesture;
|
result = touch_resource->ActivateGesture();
|
||||||
shared_memory->gesture_lifo.buffer_count = 0;
|
|
||||||
shared_memory->gesture_lifo.buffer_tail = 0;
|
if (result.IsError()) {
|
||||||
force_update = true;
|
// TODO: StopThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gesture::OnRelease() {}
|
Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
return touch_resource->ActivateGesture(aruid, basic_gesture_id);
|
||||||
std::scoped_lock shared_lock{*shared_mutex};
|
|
||||||
const u64 aruid = applet_resource->GetActiveAruid();
|
|
||||||
auto* data = applet_resource->GetAruidData(aruid);
|
|
||||||
|
|
||||||
if (data == nullptr || !data->flag.is_assigned) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_memory = &data->shared_memory_format->gesture;
|
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
|
||||||
shared_memory->gesture_lifo.buffer_count = 0;
|
|
||||||
shared_memory->gesture_lifo.buffer_tail = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadTouchInput();
|
|
||||||
|
|
||||||
GestureProperties gesture = GetGestureProperties();
|
|
||||||
f32 time_difference =
|
|
||||||
static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
|
|
||||||
(1000 * 1000 * 1000);
|
|
||||||
|
|
||||||
// Only update if necessary
|
|
||||||
if (!ShouldUpdateGesture(gesture, time_difference)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
|
|
||||||
UpdateGestureSharedMemory(gesture, time_difference);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gesture::ReadTouchInput() {
|
Result Gesture::Deactivate() {
|
||||||
if (!Settings::values.touchscreen.enabled) {
|
std::scoped_lock lock{mutex};
|
||||||
fingers = {};
|
const auto result = touch_resource->DeactivateGesture();
|
||||||
return;
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto touch_status = console->GetTouch();
|
// TODO: return StopThread();
|
||||||
for (std::size_t id = 0; id < fingers.size(); ++id) {
|
return ResultSuccess;
|
||||||
fingers[id] = touch_status[id];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
|
Result Gesture::IsActive(bool& out_is_active) const {
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
out_is_active = touch_resource->IsGestureActive();
|
||||||
if (force_update) {
|
return ResultSuccess;
|
||||||
force_update = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update if coordinates change
|
|
||||||
for (size_t id = 0; id < MAX_POINTS; id++) {
|
|
||||||
if (gesture.points[id] != last_gesture.points[id]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update on press and hold event after 0.5 seconds
|
|
||||||
if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
|
|
||||||
time_difference > press_delay) {
|
|
||||||
return enable_press_and_tap;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
|
|
||||||
GestureType type = GestureType::Idle;
|
|
||||||
GestureAttribute attributes{};
|
|
||||||
|
|
||||||
const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
|
|
||||||
|
|
||||||
// Reset next state to default
|
|
||||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
|
||||||
next_state.delta = {};
|
|
||||||
next_state.vel_x = 0;
|
|
||||||
next_state.vel_y = 0;
|
|
||||||
next_state.direction = GestureDirection::None;
|
|
||||||
next_state.rotation_angle = 0;
|
|
||||||
next_state.scale = 0;
|
|
||||||
|
|
||||||
if (gesture.active_points > 0) {
|
|
||||||
if (last_gesture.active_points == 0) {
|
|
||||||
NewGesture(gesture, type, attributes);
|
|
||||||
} else {
|
|
||||||
UpdateExistingGesture(gesture, type, time_difference);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EndGesture(gesture, last_gesture, type, attributes, time_difference);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply attributes
|
|
||||||
next_state.detection_count = gesture.detection_count;
|
|
||||||
next_state.type = type;
|
|
||||||
next_state.attributes = attributes;
|
|
||||||
next_state.pos = gesture.mid_point;
|
|
||||||
next_state.point_count = static_cast<s32>(gesture.active_points);
|
|
||||||
next_state.points = gesture.points;
|
|
||||||
last_gesture = gesture;
|
|
||||||
|
|
||||||
shared_memory->gesture_lifo.WriteNextEntry(next_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
|
|
||||||
GestureAttribute& attributes) {
|
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
|
||||||
|
|
||||||
gesture.detection_count++;
|
|
||||||
type = GestureType::Touch;
|
|
||||||
|
|
||||||
// New touch after cancel is not considered new
|
|
||||||
if (last_entry.type != GestureType::Cancel) {
|
|
||||||
attributes.is_new_touch.Assign(1);
|
|
||||||
enable_press_and_tap = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
|
|
||||||
f32 time_difference) {
|
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
|
||||||
|
|
||||||
// Promote to pan type if touch moved
|
|
||||||
for (size_t id = 0; id < MAX_POINTS; id++) {
|
|
||||||
if (gesture.points[id] != last_gesture.points[id]) {
|
|
||||||
type = GestureType::Pan;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number of fingers changed cancel the last event and clear data
|
|
||||||
if (gesture.active_points != last_gesture.active_points) {
|
|
||||||
type = GestureType::Cancel;
|
|
||||||
enable_press_and_tap = false;
|
|
||||||
gesture.active_points = 0;
|
|
||||||
gesture.mid_point = {};
|
|
||||||
gesture.points.fill({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate extra parameters of panning
|
|
||||||
if (type == GestureType::Pan) {
|
|
||||||
UpdatePanEvent(gesture, last_gesture, type, time_difference);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Promote to press type
|
|
||||||
if (last_entry.type == GestureType::Touch) {
|
|
||||||
type = GestureType::Press;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, GestureAttribute& attributes, f32 time_difference) {
|
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
|
||||||
|
|
||||||
if (last_gesture_props.active_points != 0) {
|
|
||||||
switch (last_entry.type) {
|
|
||||||
case GestureType::Touch:
|
|
||||||
if (enable_press_and_tap) {
|
|
||||||
SetTapEvent(gesture, last_gesture_props, type, attributes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
type = GestureType::Cancel;
|
|
||||||
force_update = true;
|
|
||||||
break;
|
|
||||||
case GestureType::Press:
|
|
||||||
case GestureType::Tap:
|
|
||||||
case GestureType::Swipe:
|
|
||||||
case GestureType::Pinch:
|
|
||||||
case GestureType::Rotate:
|
|
||||||
type = GestureType::Complete;
|
|
||||||
force_update = true;
|
|
||||||
break;
|
|
||||||
case GestureType::Pan:
|
|
||||||
EndPanEvent(gesture, last_gesture_props, type, time_difference);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
|
|
||||||
gesture.detection_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, GestureAttribute& attributes) {
|
|
||||||
type = GestureType::Tap;
|
|
||||||
gesture = last_gesture_props;
|
|
||||||
force_update = true;
|
|
||||||
f32 tap_time_difference =
|
|
||||||
static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
|
|
||||||
last_tap_timestamp = last_update_timestamp;
|
|
||||||
if (tap_time_difference < double_tap_delay) {
|
|
||||||
attributes.is_double_tap.Assign(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, f32 time_difference) {
|
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
|
||||||
|
|
||||||
next_state.delta = gesture.mid_point - last_entry.pos;
|
|
||||||
next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
|
|
||||||
next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
|
|
||||||
last_pan_time_difference = time_difference;
|
|
||||||
|
|
||||||
// Promote to pinch type
|
|
||||||
if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
|
|
||||||
pinch_threshold) {
|
|
||||||
type = GestureType::Pinch;
|
|
||||||
next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
|
|
||||||
(1 + (gesture.angle * last_gesture_props.angle)));
|
|
||||||
// Promote to rotate type
|
|
||||||
if (std::abs(angle_between_two_lines) > angle_threshold) {
|
|
||||||
type = GestureType::Rotate;
|
|
||||||
next_state.scale = 0;
|
|
||||||
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, f32 time_difference) {
|
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
|
||||||
next_state.vel_x =
|
|
||||||
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
|
|
||||||
next_state.vel_y =
|
|
||||||
static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
|
|
||||||
const f32 curr_vel =
|
|
||||||
std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
|
|
||||||
|
|
||||||
// Set swipe event with parameters
|
|
||||||
if (curr_vel > swipe_threshold) {
|
|
||||||
SetSwipeEvent(gesture, last_gesture_props, type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// End panning without swipe
|
|
||||||
type = GestureType::Complete;
|
|
||||||
next_state.vel_x = 0;
|
|
||||||
next_state.vel_y = 0;
|
|
||||||
force_update = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type) {
|
|
||||||
const auto& last_entry = GetLastGestureEntry();
|
|
||||||
|
|
||||||
type = GestureType::Swipe;
|
|
||||||
gesture = last_gesture_props;
|
|
||||||
force_update = true;
|
|
||||||
next_state.delta = last_entry.delta;
|
|
||||||
|
|
||||||
if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
|
|
||||||
if (next_state.delta.x > 0) {
|
|
||||||
next_state.direction = GestureDirection::Right;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
next_state.direction = GestureDirection::Left;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (next_state.delta.y > 0) {
|
|
||||||
next_state.direction = GestureDirection::Down;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
next_state.direction = GestureDirection::Up;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GestureState& Gesture::GetLastGestureEntry() const {
|
|
||||||
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
|
|
||||||
}
|
|
||||||
|
|
||||||
GestureProperties Gesture::GetGestureProperties() {
|
|
||||||
GestureProperties gesture;
|
|
||||||
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
|
|
||||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
|
||||||
[](const auto& finger) { return finger.pressed; });
|
|
||||||
gesture.active_points =
|
|
||||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
|
||||||
|
|
||||||
for (size_t id = 0; id < gesture.active_points; ++id) {
|
|
||||||
const auto& [active_x, active_y] = active_fingers[id].position;
|
|
||||||
gesture.points[id] = {
|
|
||||||
.x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
|
|
||||||
.y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hack: There is no touch in docked but games still allow it
|
|
||||||
if (Settings::IsDockedMode()) {
|
|
||||||
gesture.points[id] = {
|
|
||||||
.x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
|
|
||||||
.y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
|
|
||||||
gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t id = 0; id < gesture.active_points; ++id) {
|
|
||||||
const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
|
|
||||||
Square(gesture.mid_point.y - gesture.points[id].y));
|
|
||||||
gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
|
|
||||||
}
|
|
||||||
|
|
||||||
gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
|
|
||||||
static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
|
|
||||||
|
|
||||||
gesture.detection_count = last_gesture.detection_count;
|
|
||||||
|
|
||||||
return gesture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
|
@ -1,87 +1,32 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <mutex>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "hid_core/resources/controller_base.h"
|
#include "core/hle/result.h"
|
||||||
#include "hid_core/resources/touch_screen/touch_types.h"
|
|
||||||
|
|
||||||
namespace Core::HID {
|
|
||||||
class EmulatedConsole;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
struct GestureSharedMemoryFormat;
|
class TouchResource;
|
||||||
|
|
||||||
class Gesture final : public ControllerBase {
|
/// Handles gesture request from HID interfaces
|
||||||
|
class Gesture {
|
||||||
public:
|
public:
|
||||||
explicit Gesture(Core::HID::HIDCore& hid_core_);
|
Gesture(std::shared_ptr<TouchResource> resource);
|
||||||
~Gesture() override;
|
~Gesture();
|
||||||
|
|
||||||
// Called when the controller is initialized
|
Result Activate();
|
||||||
void OnInit() override;
|
Result Activate(u64 aruid, u32 basic_gesture_id);
|
||||||
|
|
||||||
// When the controller is released
|
Result Deactivate();
|
||||||
void OnRelease() override;
|
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
Result IsActive(bool& out_is_active) const;
|
||||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Reads input from all available input engines
|
mutable std::mutex mutex;
|
||||||
void ReadTouchInput();
|
std::shared_ptr<TouchResource> touch_resource;
|
||||||
|
|
||||||
// Returns true if gesture state needs to be updated
|
|
||||||
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
|
|
||||||
|
|
||||||
// Updates the shared memory to the next state
|
|
||||||
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
|
|
||||||
|
|
||||||
// Initializes new gesture
|
|
||||||
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
|
|
||||||
|
|
||||||
// Updates existing gesture state
|
|
||||||
void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
|
|
||||||
|
|
||||||
// Terminates exiting gesture
|
|
||||||
void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, GestureAttribute& attributes, f32 time_difference);
|
|
||||||
|
|
||||||
// Set current event to a tap event
|
|
||||||
void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, GestureAttribute& attributes);
|
|
||||||
|
|
||||||
// Calculates and set the extra parameters related to a pan event
|
|
||||||
void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, f32 time_difference);
|
|
||||||
|
|
||||||
// Terminates the pan event
|
|
||||||
void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type, f32 time_difference);
|
|
||||||
|
|
||||||
// Set current event to a swipe event
|
|
||||||
void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
|
||||||
GestureType& type);
|
|
||||||
|
|
||||||
// Retrieves the last gesture entry, as indicated by shared memory indices.
|
|
||||||
[[nodiscard]] const GestureState& GetLastGestureEntry() const;
|
|
||||||
|
|
||||||
// Returns the average distance, angle and middle point of the active fingers
|
|
||||||
GestureProperties GetGestureProperties();
|
|
||||||
|
|
||||||
GestureState next_state{};
|
|
||||||
GestureSharedMemoryFormat* shared_memory;
|
|
||||||
Core::HID::EmulatedConsole* console = nullptr;
|
|
||||||
|
|
||||||
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
|
|
||||||
GestureProperties last_gesture{};
|
|
||||||
s64 last_update_timestamp{};
|
|
||||||
s64 last_tap_timestamp{};
|
|
||||||
f32 last_pan_time_difference{};
|
|
||||||
bool force_update{false};
|
|
||||||
bool enable_press_and_tap{false};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "common/math_util.h"
|
||||||
|
#include "hid_core/resources/touch_screen/gesture_handler.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
constexpr f32 Square(s32 num) {
|
||||||
|
return static_cast<f32>(num * num);
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureHandler::GestureHandler() {}
|
||||||
|
|
||||||
|
GestureHandler::~GestureHandler() {}
|
||||||
|
|
||||||
|
void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) {
|
||||||
|
gesture = {};
|
||||||
|
gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count));
|
||||||
|
|
||||||
|
for (size_t id = 0; id < gesture.active_points; ++id) {
|
||||||
|
const auto& [active_x, active_y] = touch_state[id].position;
|
||||||
|
gesture.points[id] = {
|
||||||
|
.x = static_cast<s32>(active_x),
|
||||||
|
.y = static_cast<s32>(active_y),
|
||||||
|
};
|
||||||
|
|
||||||
|
gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
|
||||||
|
gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t id = 0; id < gesture.active_points; ++id) {
|
||||||
|
const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
|
||||||
|
Square(gesture.mid_point.y - gesture.points[id].y));
|
||||||
|
gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
|
||||||
|
}
|
||||||
|
|
||||||
|
gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
|
||||||
|
static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
|
||||||
|
|
||||||
|
gesture.detection_count = last_gesture.detection_count;
|
||||||
|
|
||||||
|
if (last_update_timestamp > timestamp) {
|
||||||
|
timestamp = last_tap_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GestureHandler::NeedsUpdate() {
|
||||||
|
if (force_update) {
|
||||||
|
force_update = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update if coordinates change
|
||||||
|
for (size_t id = 0; id < MaxPoints; id++) {
|
||||||
|
if (gesture.points[id] != last_gesture.points[id]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update on press and hold event after 0.5 seconds
|
||||||
|
if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 &&
|
||||||
|
time_difference > PressDelay) {
|
||||||
|
return enable_press_and_tap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) {
|
||||||
|
last_update_timestamp = timestamp;
|
||||||
|
|
||||||
|
GestureType type = GestureType::Idle;
|
||||||
|
GestureAttribute attributes{};
|
||||||
|
|
||||||
|
// Reset next state to default
|
||||||
|
next_state.sampling_number = last_gesture_state.sampling_number + 1;
|
||||||
|
next_state.delta = {};
|
||||||
|
next_state.vel_x = 0;
|
||||||
|
next_state.vel_y = 0;
|
||||||
|
next_state.direction = GestureDirection::None;
|
||||||
|
next_state.rotation_angle = 0;
|
||||||
|
next_state.scale = 0;
|
||||||
|
|
||||||
|
if (gesture.active_points > 0) {
|
||||||
|
if (last_gesture.active_points == 0) {
|
||||||
|
NewGesture(type, attributes);
|
||||||
|
} else {
|
||||||
|
UpdateExistingGesture(next_state, type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EndGesture(next_state, type, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply attributes
|
||||||
|
next_state.detection_count = gesture.detection_count;
|
||||||
|
next_state.type = type;
|
||||||
|
next_state.attributes = attributes;
|
||||||
|
next_state.pos = gesture.mid_point;
|
||||||
|
next_state.point_count = static_cast<s32>(gesture.active_points);
|
||||||
|
next_state.points = gesture.points;
|
||||||
|
last_gesture = gesture;
|
||||||
|
last_gesture_state = next_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) {
|
||||||
|
gesture.detection_count++;
|
||||||
|
type = GestureType::Touch;
|
||||||
|
|
||||||
|
// New touch after cancel is not considered new
|
||||||
|
if (last_gesture_state.type != GestureType::Cancel) {
|
||||||
|
attributes.is_new_touch.Assign(1);
|
||||||
|
enable_press_and_tap = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) {
|
||||||
|
// Promote to pan type if touch moved
|
||||||
|
for (size_t id = 0; id < MaxPoints; id++) {
|
||||||
|
if (gesture.points[id] != last_gesture.points[id]) {
|
||||||
|
type = GestureType::Pan;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of fingers changed cancel the last event and clear data
|
||||||
|
if (gesture.active_points != last_gesture.active_points) {
|
||||||
|
type = GestureType::Cancel;
|
||||||
|
enable_press_and_tap = false;
|
||||||
|
gesture.active_points = 0;
|
||||||
|
gesture.mid_point = {};
|
||||||
|
gesture.points.fill({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate extra parameters of panning
|
||||||
|
if (type == GestureType::Pan) {
|
||||||
|
UpdatePanEvent(next_state, type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Promote to press type
|
||||||
|
if (last_gesture_state.type == GestureType::Touch) {
|
||||||
|
type = GestureType::Press;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::EndGesture(GestureState& next_state, GestureType& type,
|
||||||
|
GestureAttribute& attributes) {
|
||||||
|
if (last_gesture.active_points != 0) {
|
||||||
|
switch (last_gesture_state.type) {
|
||||||
|
case GestureType::Touch:
|
||||||
|
if (enable_press_and_tap) {
|
||||||
|
SetTapEvent(type, attributes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
type = GestureType::Cancel;
|
||||||
|
force_update = true;
|
||||||
|
break;
|
||||||
|
case GestureType::Press:
|
||||||
|
case GestureType::Tap:
|
||||||
|
case GestureType::Swipe:
|
||||||
|
case GestureType::Pinch:
|
||||||
|
case GestureType::Rotate:
|
||||||
|
type = GestureType::Complete;
|
||||||
|
force_update = true;
|
||||||
|
break;
|
||||||
|
case GestureType::Pan:
|
||||||
|
EndPanEvent(next_state, type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (last_gesture_state.type == GestureType::Complete ||
|
||||||
|
last_gesture_state.type == GestureType::Cancel) {
|
||||||
|
gesture.detection_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) {
|
||||||
|
type = GestureType::Tap;
|
||||||
|
gesture = last_gesture;
|
||||||
|
force_update = true;
|
||||||
|
f32 tap_time_difference =
|
||||||
|
static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
|
||||||
|
last_tap_timestamp = last_update_timestamp;
|
||||||
|
if (tap_time_difference < DoubleTapDelay) {
|
||||||
|
attributes.is_double_tap.Assign(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) {
|
||||||
|
next_state.delta = gesture.mid_point - last_gesture_state.pos;
|
||||||
|
next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
|
||||||
|
next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
|
||||||
|
last_pan_time_difference = time_difference;
|
||||||
|
|
||||||
|
// Promote to pinch type
|
||||||
|
if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) {
|
||||||
|
type = GestureType::Pinch;
|
||||||
|
next_state.scale = gesture.average_distance / last_gesture.average_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) /
|
||||||
|
(1 + (gesture.angle * last_gesture.angle)));
|
||||||
|
// Promote to rotate type
|
||||||
|
if (std::abs(angle_between_two_lines) > AngleThreshold) {
|
||||||
|
type = GestureType::Rotate;
|
||||||
|
next_state.scale = 0;
|
||||||
|
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) {
|
||||||
|
next_state.vel_x =
|
||||||
|
static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference);
|
||||||
|
next_state.vel_y =
|
||||||
|
static_cast<f32>(last_gesture_state.delta.y) / (last_pan_time_difference + time_difference);
|
||||||
|
const f32 curr_vel =
|
||||||
|
std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
|
||||||
|
|
||||||
|
// Set swipe event with parameters
|
||||||
|
if (curr_vel > SwipeThreshold) {
|
||||||
|
SetSwipeEvent(next_state, type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End panning without swipe
|
||||||
|
type = GestureType::Complete;
|
||||||
|
next_state.vel_x = 0;
|
||||||
|
next_state.vel_y = 0;
|
||||||
|
force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) {
|
||||||
|
type = GestureType::Swipe;
|
||||||
|
gesture = last_gesture;
|
||||||
|
force_update = true;
|
||||||
|
next_state.delta = last_gesture_state.delta;
|
||||||
|
|
||||||
|
if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
|
||||||
|
if (next_state.delta.x > 0) {
|
||||||
|
next_state.direction = GestureDirection::Right;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next_state.direction = GestureDirection::Left;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (next_state.delta.y > 0) {
|
||||||
|
next_state.direction = GestureDirection::Down;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next_state.direction = GestureDirection::Up;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -0,0 +1,55 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "hid_core/resources/touch_screen/touch_types.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
class GestureHandler {
|
||||||
|
public:
|
||||||
|
GestureHandler();
|
||||||
|
~GestureHandler();
|
||||||
|
|
||||||
|
void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp);
|
||||||
|
|
||||||
|
bool NeedsUpdate();
|
||||||
|
void UpdateGestureState(GestureState& next_state, s64 timestamp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initializes new gesture
|
||||||
|
void NewGesture(GestureType& type, GestureAttribute& attributes);
|
||||||
|
|
||||||
|
// Updates existing gesture state
|
||||||
|
void UpdateExistingGesture(GestureState& next_state, GestureType& type);
|
||||||
|
|
||||||
|
// Terminates exiting gesture
|
||||||
|
void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes);
|
||||||
|
|
||||||
|
// Set current event to a tap event
|
||||||
|
void SetTapEvent(GestureType& type, GestureAttribute& attributes);
|
||||||
|
|
||||||
|
// Calculates and set the extra parameters related to a pan event
|
||||||
|
void UpdatePanEvent(GestureState& next_state, GestureType& type);
|
||||||
|
|
||||||
|
// Terminates the pan event
|
||||||
|
void EndPanEvent(GestureState& next_state, GestureType& type);
|
||||||
|
|
||||||
|
// Set current event to a swipe event
|
||||||
|
void SetSwipeEvent(GestureState& next_state, GestureType& type);
|
||||||
|
|
||||||
|
GestureProperties gesture{};
|
||||||
|
GestureProperties last_gesture{};
|
||||||
|
GestureState last_gesture_state{};
|
||||||
|
s64 last_update_timestamp{};
|
||||||
|
s64 last_tap_timestamp{};
|
||||||
|
f32 last_pan_time_difference{};
|
||||||
|
f32 time_difference{};
|
||||||
|
bool force_update{true};
|
||||||
|
bool enable_press_and_tap{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -1,77 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include "common/bit_field.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/point.h"
|
|
||||||
|
|
||||||
namespace Service::HID {
|
|
||||||
static constexpr size_t MAX_FINGERS = 16;
|
|
||||||
static constexpr size_t MAX_POINTS = 4;
|
|
||||||
|
|
||||||
// This is nn::hid::GestureType
|
|
||||||
enum class GestureType : u32 {
|
|
||||||
Idle, // Nothing touching the screen
|
|
||||||
Complete, // Set at the end of a touch event
|
|
||||||
Cancel, // Set when the number of fingers change
|
|
||||||
Touch, // A finger just touched the screen
|
|
||||||
Press, // Set if last type is touch and the finger hasn't moved
|
|
||||||
Tap, // Fast press then release
|
|
||||||
Pan, // All points moving together across the screen
|
|
||||||
Swipe, // Fast press movement and release of a single point
|
|
||||||
Pinch, // All points moving away/closer to the midpoint
|
|
||||||
Rotate, // All points rotating from the midpoint
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::hid::GestureDirection
|
|
||||||
enum class GestureDirection : u32 {
|
|
||||||
None,
|
|
||||||
Left,
|
|
||||||
Up,
|
|
||||||
Right,
|
|
||||||
Down,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::hid::GestureAttribute
|
|
||||||
struct GestureAttribute {
|
|
||||||
union {
|
|
||||||
u32 raw{};
|
|
||||||
|
|
||||||
BitField<4, 1, u32> is_new_touch;
|
|
||||||
BitField<8, 1, u32> is_double_tap;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::hid::GestureState
|
|
||||||
struct GestureState {
|
|
||||||
s64 sampling_number{};
|
|
||||||
s64 detection_count{};
|
|
||||||
GestureType type{GestureType::Idle};
|
|
||||||
GestureDirection direction{GestureDirection::None};
|
|
||||||
Common::Point<s32> pos{};
|
|
||||||
Common::Point<s32> delta{};
|
|
||||||
f32 vel_x{};
|
|
||||||
f32 vel_y{};
|
|
||||||
GestureAttribute attributes{};
|
|
||||||
f32 scale{};
|
|
||||||
f32 rotation_angle{};
|
|
||||||
s32 point_count{};
|
|
||||||
std::array<Common::Point<s32>, 4> points{};
|
|
||||||
};
|
|
||||||
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
|
||||||
|
|
||||||
struct GestureProperties {
|
|
||||||
std::array<Common::Point<s32>, MAX_POINTS> points{};
|
|
||||||
std::size_t active_points{};
|
|
||||||
Common::Point<s32> mid_point{};
|
|
||||||
s64 detection_count{};
|
|
||||||
u64 delta_time{};
|
|
||||||
f32 average_distance{};
|
|
||||||
f32 angle{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::HID
|
|
|
@ -1,132 +1,119 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include "hid_core/hid_types.h"
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/settings.h"
|
|
||||||
#include "core/core_timing.h"
|
|
||||||
#include "core/frontend/emu_window.h"
|
|
||||||
#include "hid_core/frontend/emulated_console.h"
|
|
||||||
#include "hid_core/hid_core.h"
|
|
||||||
#include "hid_core/resources/applet_resource.h"
|
|
||||||
#include "hid_core/resources/shared_memory_format.h"
|
|
||||||
#include "hid_core/resources/touch_screen/touch_screen.h"
|
#include "hid_core/resources/touch_screen/touch_screen.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_resource.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
|
|
||||||
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_)
|
TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
|
||||||
: ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
|
|
||||||
touchscreen_height(Layout::ScreenUndocked::Height) {
|
|
||||||
console = hid_core.GetEmulatedConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchScreen::~TouchScreen() = default;
|
TouchScreen::~TouchScreen() = default;
|
||||||
|
|
||||||
void TouchScreen::OnInit() {}
|
Result TouchScreen::Activate() {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
void TouchScreen::OnRelease() {}
|
// TODO: Result result = CreateThread();
|
||||||
|
Result result = ResultSuccess;
|
||||||
void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
if (result.IsError()) {
|
||||||
const u64 aruid = applet_resource->GetActiveAruid();
|
return result;
|
||||||
auto* data = applet_resource->GetAruidData(aruid);
|
|
||||||
|
|
||||||
if (data == nullptr || !data->flag.is_assigned) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen;
|
result = touch_resource->ActivateTouch();
|
||||||
shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
|
if (result.IsError()) {
|
||||||
|
// TODO: StopThread();
|
||||||
if (!IsControllerActivated()) {
|
|
||||||
shared_memory.touch_screen_lifo.buffer_count = 0;
|
|
||||||
shared_memory.touch_screen_lifo.buffer_tail = 0;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto touch_status = console->GetTouch();
|
return result;
|
||||||
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
|
|
||||||
const auto& current_touch = touch_status[id];
|
|
||||||
auto& finger = fingers[id];
|
|
||||||
finger.id = current_touch.id;
|
|
||||||
|
|
||||||
if (finger.attribute.start_touch) {
|
|
||||||
finger.attribute.raw = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finger.attribute.end_touch) {
|
|
||||||
finger.attribute.raw = 0;
|
|
||||||
finger.pressed = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!finger.pressed && current_touch.pressed) {
|
|
||||||
// Ignore all touch fingers if disabled
|
|
||||||
if (!Settings::values.touchscreen.enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
finger.attribute.start_touch.Assign(1);
|
|
||||||
finger.pressed = true;
|
|
||||||
finger.position = current_touch.position;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finger.pressed && !current_touch.pressed) {
|
|
||||||
finger.attribute.raw = 0;
|
|
||||||
finger.attribute.end_touch.Assign(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only update position if touch is not on a special frame
|
|
||||||
finger.position = current_touch.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
|
|
||||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
|
||||||
[](const auto& finger) { return finger.pressed; });
|
|
||||||
const auto active_fingers_count =
|
|
||||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
|
||||||
|
|
||||||
const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
|
|
||||||
const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state;
|
|
||||||
|
|
||||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
|
||||||
next_state.entry_count = static_cast<s32>(active_fingers_count);
|
|
||||||
|
|
||||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
|
||||||
auto& touch_entry = next_state.states[id];
|
|
||||||
if (id < active_fingers_count) {
|
|
||||||
const auto& [active_x, active_y] = active_fingers[id].position;
|
|
||||||
touch_entry.position = {
|
|
||||||
.x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)),
|
|
||||||
.y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)),
|
|
||||||
};
|
|
||||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
|
||||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
|
||||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
|
||||||
touch_entry.delta_time = timestamp - active_fingers[id].last_touch;
|
|
||||||
fingers[active_fingers[id].id].last_touch = timestamp;
|
|
||||||
touch_entry.finger = active_fingers[id].id;
|
|
||||||
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
|
|
||||||
} else {
|
|
||||||
// Clear touch entry
|
|
||||||
touch_entry.attribute.raw = 0;
|
|
||||||
touch_entry.position = {};
|
|
||||||
touch_entry.diameter_x = 0;
|
|
||||||
touch_entry.diameter_y = 0;
|
|
||||||
touch_entry.rotation_angle = 0;
|
|
||||||
touch_entry.delta_time = 0;
|
|
||||||
touch_entry.finger = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_memory.touch_screen_lifo.WriteNextEntry(next_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
|
Result TouchScreen::Activate(u64 aruid) {
|
||||||
touchscreen_width = width;
|
std::scoped_lock lock{mutex};
|
||||||
touchscreen_height = height;
|
return touch_resource->ActivateTouch(aruid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::Deactivate() {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
const auto result = touch_resource->DeactivateTouch();
|
||||||
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: return StopThread();
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::IsActive(bool& out_is_active) const {
|
||||||
|
out_is_active = touch_resource->IsTouchActive();
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::UnsetTouchScreenAutoPilotState() {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->UnsetTouchScreenAutoPilotState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::RequestNextTouchInput() {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->RequestNextTouchInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::RequestNextDummyInput() {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->RequestNextDummyInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::ProcessTouchScreenAutoTune() {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->ProcessTouchScreenAutoTune();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
|
||||||
|
f32 point2_y) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->SetTouchScreenResolution(width, height, aruid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::SetTouchScreenConfiguration(
|
||||||
|
const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->SetTouchScreenConfiguration(mode, aruid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
|
||||||
|
u64 aruid) const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->GetTouchScreenConfiguration(out_mode, aruid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::SetTouchScreenDefaultConfiguration(
|
||||||
|
const Core::HID::TouchScreenConfigurationForNx& mode) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->SetTouchScreenDefaultConfiguration(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchScreen::GetTouchScreenDefaultConfiguration(
|
||||||
|
Core::HID::TouchScreenConfigurationForNx& out_mode) const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return touch_resource->GetTouchScreenDefaultConfiguration(out_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchScreen::OnTouchUpdate(u64 timestamp) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
touch_resource->OnTouchUpdate(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
|
@ -1,43 +1,64 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <mutex>
|
||||||
|
|
||||||
#include "hid_core/hid_types.h"
|
#include "common/common_types.h"
|
||||||
#include "hid_core/resources/controller_base.h"
|
#include "core/hle/result.h"
|
||||||
#include "hid_core/resources/touch_screen/touch_types.h"
|
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core::HID {
|
||||||
class EmulatedConsole;
|
struct TouchScreenConfigurationForNx;
|
||||||
} // namespace Core::HID
|
}
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
struct EventType;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
struct TouchScreenSharedMemoryFormat;
|
class TouchResource;
|
||||||
|
struct AutoPilotState;
|
||||||
|
|
||||||
class TouchScreen final : public ControllerBase {
|
/// Handles touch request from HID interfaces
|
||||||
|
class TouchScreen {
|
||||||
public:
|
public:
|
||||||
explicit TouchScreen(Core::HID::HIDCore& hid_core_);
|
TouchScreen(std::shared_ptr<TouchResource> resource);
|
||||||
~TouchScreen() override;
|
~TouchScreen();
|
||||||
|
|
||||||
// Called when the controller is initialized
|
Result Activate();
|
||||||
void OnInit() override;
|
Result Activate(u64 aruid);
|
||||||
|
|
||||||
// When the controller is released
|
Result Deactivate();
|
||||||
void OnRelease() override;
|
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
Result IsActive(bool& out_is_active) const;
|
||||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
|
||||||
|
|
||||||
void SetTouchscreenDimensions(u32 width, u32 height);
|
Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
|
||||||
|
Result UnsetTouchScreenAutoPilotState();
|
||||||
|
|
||||||
|
Result RequestNextTouchInput();
|
||||||
|
Result RequestNextDummyInput();
|
||||||
|
|
||||||
|
Result ProcessTouchScreenAutoTune();
|
||||||
|
|
||||||
|
Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
|
||||||
|
Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);
|
||||||
|
|
||||||
|
Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode,
|
||||||
|
u64 aruid);
|
||||||
|
Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
|
||||||
|
u64 aruid) const;
|
||||||
|
|
||||||
|
Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode);
|
||||||
|
Result GetTouchScreenDefaultConfiguration(
|
||||||
|
Core::HID::TouchScreenConfigurationForNx& out_mode) const;
|
||||||
|
|
||||||
|
void OnTouchUpdate(u64 timestamp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TouchScreenState next_state{};
|
mutable std::mutex mutex;
|
||||||
Core::HID::EmulatedConsole* console = nullptr;
|
std::shared_ptr<TouchResource> touch_resource;
|
||||||
|
std::shared_ptr<Core::Timing::EventType> touch_update_event;
|
||||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
|
|
||||||
u32 touchscreen_width;
|
|
||||||
u32 touchscreen_height;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "hid_core/hid_core.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_driver.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) {
|
||||||
|
console = hid_core.GetEmulatedConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
TouchDriver::~TouchDriver() = default;
|
||||||
|
|
||||||
|
Result TouchDriver::StartTouchSensor() {
|
||||||
|
is_running = true;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchDriver::StopTouchSensor() {
|
||||||
|
is_running = false;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TouchDriver::IsRunning() const {
|
||||||
|
return is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchDriver::ProcessTouchScreenAutoTune() const {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchDriver::WaitForDummyInput() {
|
||||||
|
touch_status = {};
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchDriver::WaitForInput() {
|
||||||
|
touch_status = {};
|
||||||
|
const auto touch_input = console->GetTouch();
|
||||||
|
for (std::size_t id = 0; id < touch_status.states.size(); id++) {
|
||||||
|
const auto& current_touch = touch_input[id];
|
||||||
|
auto& finger = fingers[id];
|
||||||
|
finger.id = current_touch.id;
|
||||||
|
|
||||||
|
if (finger.attribute.start_touch) {
|
||||||
|
finger.attribute.raw = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finger.attribute.end_touch) {
|
||||||
|
finger.attribute.raw = 0;
|
||||||
|
finger.pressed = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finger.pressed && current_touch.pressed) {
|
||||||
|
finger.attribute.start_touch.Assign(1);
|
||||||
|
finger.pressed = true;
|
||||||
|
finger.position = current_touch.position;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finger.pressed && !current_touch.pressed) {
|
||||||
|
finger.attribute.raw = 0;
|
||||||
|
finger.attribute.end_touch.Assign(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only update position if touch is not on a special frame
|
||||||
|
finger.position = current_touch.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<Core::HID::TouchFinger, MaxFingers> active_fingers;
|
||||||
|
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||||
|
[](const auto& finger) { return finger.pressed; });
|
||||||
|
const auto active_fingers_count =
|
||||||
|
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||||
|
|
||||||
|
touch_status.entry_count = static_cast<s32>(active_fingers_count);
|
||||||
|
for (std::size_t id = 0; id < MaxFingers; ++id) {
|
||||||
|
auto& touch_entry = touch_status.states[id];
|
||||||
|
if (id < active_fingers_count) {
|
||||||
|
const auto& [active_x, active_y] = active_fingers[id].position;
|
||||||
|
touch_entry.position = {
|
||||||
|
.x = static_cast<u16>(active_x * TouchSensorWidth),
|
||||||
|
.y = static_cast<u16>(active_y * TouchSensorHeight),
|
||||||
|
};
|
||||||
|
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||||
|
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||||
|
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||||
|
touch_entry.finger = active_fingers[id].id;
|
||||||
|
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const {
|
||||||
|
out_state = touch_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) {
|
||||||
|
touch_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const {
|
||||||
|
return touch_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -0,0 +1,47 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "hid_core/frontend/emulated_console.h"
|
||||||
|
#include "hid_core/hid_types.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_types.h"
|
||||||
|
|
||||||
|
namespace Core::HID {
|
||||||
|
class HIDCore;
|
||||||
|
} // namespace Core::HID
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
/// This handles all request to Ftm3bd56(TouchPanel) hardware
|
||||||
|
class TouchDriver {
|
||||||
|
public:
|
||||||
|
explicit TouchDriver(Core::HID::HIDCore& hid_core);
|
||||||
|
~TouchDriver();
|
||||||
|
|
||||||
|
Result StartTouchSensor();
|
||||||
|
Result StopTouchSensor();
|
||||||
|
bool IsRunning() const;
|
||||||
|
|
||||||
|
void ProcessTouchScreenAutoTune() const;
|
||||||
|
|
||||||
|
Result WaitForDummyInput();
|
||||||
|
Result WaitForInput();
|
||||||
|
|
||||||
|
void GetNextTouchState(TouchScreenState& out_state) const;
|
||||||
|
|
||||||
|
void SetTouchMode(Core::HID::TouchScreenModeForNx mode);
|
||||||
|
Core::HID::TouchScreenModeForNx GetTouchMode() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_running{};
|
||||||
|
TouchScreenState touch_status{};
|
||||||
|
Core::HID::TouchFingerState fingers{};
|
||||||
|
Core::HID::TouchScreenModeForNx touch_mode{};
|
||||||
|
|
||||||
|
Core::HID::EmulatedConsole* console = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -0,0 +1,579 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hle/kernel/k_event.h"
|
||||||
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
|
#include "core/hle/service/set/system_settings_server.h"
|
||||||
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
#include "hid_core/hid_result.h"
|
||||||
|
#include "hid_core/resources/applet_resource.h"
|
||||||
|
#include "hid_core/resources/shared_memory_format.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_driver.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_screen_resource.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
|
||||||
|
|
||||||
|
TouchResource::TouchResource(Core::System& system_) : system{system_} {
|
||||||
|
m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
|
||||||
|
}
|
||||||
|
|
||||||
|
TouchResource::~TouchResource() {
|
||||||
|
Finalize();
|
||||||
|
};
|
||||||
|
|
||||||
|
Result TouchResource::ActivateTouch() {
|
||||||
|
if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
|
||||||
|
touch_ref_counter == std::numeric_limits<s32>::max() - 1) {
|
||||||
|
return ResultTouchOverflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
const auto result = touch_driver->StartTouchSensor();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_initalized = true;
|
||||||
|
system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
|
||||||
|
timer_event);
|
||||||
|
current_touch_state = {};
|
||||||
|
ReadTouchInput();
|
||||||
|
gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard};
|
||||||
|
m_set_sys->GetTouchScreenMode(touch_mode);
|
||||||
|
default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode);
|
||||||
|
|
||||||
|
global_ref_counter++;
|
||||||
|
touch_ref_counter++;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::ActivateTouch(u64 aruid) {
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
|
||||||
|
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
|
||||||
|
TouchAruidData& touch_data = aruid_data[aruid_index];
|
||||||
|
|
||||||
|
if (!applet_data->flag.is_assigned) {
|
||||||
|
touch_data = {};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 aruid_id = applet_data->aruid;
|
||||||
|
if (touch_data.aruid != aruid_id) {
|
||||||
|
touch_data = {};
|
||||||
|
touch_data.aruid = aruid_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aruid != aruid_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& touch_shared = applet_data->shared_memory_format->touch_screen;
|
||||||
|
|
||||||
|
if (touch_shared.touch_screen_lifo.buffer_count == 0) {
|
||||||
|
StorePreviousTouchState(previous_touch_state, touch_data.finger_map,
|
||||||
|
current_touch_state,
|
||||||
|
applet_data->flag.enable_touchscreen.Value() != 0);
|
||||||
|
touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::ActivateGesture() {
|
||||||
|
if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
|
||||||
|
gesture_ref_counter == std::numeric_limits<s32>::max() - 1) {
|
||||||
|
return ResultGestureOverflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize first instance
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
const auto result = touch_driver->StartTouchSensor();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_initalized = true;
|
||||||
|
system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
|
||||||
|
timer_event);
|
||||||
|
current_touch_state = {};
|
||||||
|
ReadTouchInput();
|
||||||
|
gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
global_ref_counter++;
|
||||||
|
gesture_ref_counter++;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
|
||||||
|
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
|
||||||
|
TouchAruidData& touch_data = aruid_data[aruid_index];
|
||||||
|
|
||||||
|
if (!applet_data->flag.is_assigned) {
|
||||||
|
touch_data = {};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 aruid_id = applet_data->aruid;
|
||||||
|
if (touch_data.aruid != aruid_id) {
|
||||||
|
touch_data = {};
|
||||||
|
touch_data.aruid = aruid_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aruid != aruid_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& gesture_shared = applet_data->shared_memory_format->gesture;
|
||||||
|
if (touch_data.basic_gesture_id != basic_gesture_id) {
|
||||||
|
gesture_shared.gesture_lifo.buffer_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gesture_shared.gesture_lifo.buffer_count == 0) {
|
||||||
|
touch_data.basic_gesture_id = basic_gesture_id;
|
||||||
|
|
||||||
|
gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::DeactivateTouch() {
|
||||||
|
if (touch_ref_counter == 0 || global_ref_counter == 0) {
|
||||||
|
return ResultTouchNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_ref_counter--;
|
||||||
|
touch_ref_counter--;
|
||||||
|
|
||||||
|
if (touch_ref_counter + global_ref_counter != 0) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::DeactivateGesture() {
|
||||||
|
if (gesture_ref_counter == 0 || global_ref_counter == 0) {
|
||||||
|
return ResultGestureNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_ref_counter--;
|
||||||
|
gesture_ref_counter--;
|
||||||
|
|
||||||
|
if (touch_ref_counter + global_ref_counter != 0) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TouchResource::IsTouchActive() const {
|
||||||
|
return touch_ref_counter != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TouchResource::IsGestureActive() const {
|
||||||
|
return gesture_ref_counter != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) {
|
||||||
|
touch_driver = driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared,
|
||||||
|
std::recursive_mutex* mutex) {
|
||||||
|
applet_resource = shared;
|
||||||
|
shared_mutex = mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) {
|
||||||
|
input_event = event;
|
||||||
|
input_mutex = mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) {
|
||||||
|
handheld_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) {
|
||||||
|
timer_event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
return ResultTouchNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_auto_pilot_initialized) {
|
||||||
|
is_auto_pilot_initialized = true;
|
||||||
|
auto_pilot = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TouchScreenState state = {
|
||||||
|
.entry_count = static_cast<s32>(auto_pilot_state.count),
|
||||||
|
.states = auto_pilot_state.state,
|
||||||
|
};
|
||||||
|
|
||||||
|
SanitizeInput(state);
|
||||||
|
|
||||||
|
auto_pilot.count = state.entry_count;
|
||||||
|
auto_pilot.state = state.states;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::UnsetTouchScreenAutoPilotState() {
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
return ResultTouchNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_auto_pilot_initialized = false;
|
||||||
|
auto_pilot = {};
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::RequestNextTouchInput() {
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
return ResultTouchNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handheld_config->is_handheld_hid_enabled) {
|
||||||
|
const Result result = touch_driver->WaitForInput();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_initalized = true;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::RequestNextDummyInput() {
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
return ResultTouchNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handheld_config->is_handheld_hid_enabled) {
|
||||||
|
const Result result = touch_driver->WaitForDummyInput();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_initalized = false;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::ProcessTouchScreenAutoTune() {
|
||||||
|
touch_driver->ProcessTouchScreenAutoTune();
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
|
||||||
|
f32 point2_y) {
|
||||||
|
offset = {
|
||||||
|
.x = point1_x,
|
||||||
|
.y = point1_y,
|
||||||
|
};
|
||||||
|
magnification = {
|
||||||
|
.x = point2_x,
|
||||||
|
.y = point2_y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
|
||||||
|
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
|
||||||
|
TouchAruidData& data = aruid_data[aruid_index];
|
||||||
|
|
||||||
|
if (!applet_data->flag.is_assigned) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (aruid != data.aruid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data.resolution_width = static_cast<u16>(width);
|
||||||
|
data.resolution_height = static_cast<u16>(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::SetTouchScreenConfiguration(
|
||||||
|
const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) {
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
|
||||||
|
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
|
||||||
|
TouchAruidData& data = aruid_data[aruid_index];
|
||||||
|
|
||||||
|
if (!applet_data->flag.is_assigned) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (aruid != data.aruid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data.finger_map.touch_mode = touch_configuration.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::GetTouchScreenConfiguration(
|
||||||
|
Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const {
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
|
||||||
|
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
|
||||||
|
const TouchAruidData& data = aruid_data[aruid_index];
|
||||||
|
|
||||||
|
if (!applet_data->flag.is_assigned) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (aruid != data.aruid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out_touch_configuration.mode = data.finger_map.touch_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::SetTouchScreenDefaultConfiguration(
|
||||||
|
const Core::HID::TouchScreenConfigurationForNx& touch_configuration) {
|
||||||
|
default_touch_screen_mode = touch_configuration.mode;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::GetTouchScreenDefaultConfiguration(
|
||||||
|
Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const {
|
||||||
|
out_touch_configuration.mode = default_touch_screen_mode;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TouchResource::Finalize() {
|
||||||
|
is_auto_pilot_initialized = false;
|
||||||
|
auto_pilot = {};
|
||||||
|
system.CoreTiming().UnscheduleEvent(timer_event);
|
||||||
|
|
||||||
|
const auto result = touch_driver->StopTouchSensor();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_initalized = false;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch,
|
||||||
|
TouchFingerMap& out_finger_map,
|
||||||
|
const TouchScreenState& current_touch,
|
||||||
|
bool is_touch_enabled) const {
|
||||||
|
s32 finger_count{};
|
||||||
|
|
||||||
|
if (is_touch_enabled) {
|
||||||
|
finger_count = current_touch.entry_count;
|
||||||
|
if (finger_count < 1) {
|
||||||
|
out_finger_map.finger_count = 0;
|
||||||
|
out_finger_map.finger_ids = {};
|
||||||
|
out_previous_touch.sampling_number = current_touch.sampling_number;
|
||||||
|
out_previous_touch.entry_count = 0;
|
||||||
|
out_previous_touch.states = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) {
|
||||||
|
out_finger_map.finger_ids[i] = current_touch.states[i].finger;
|
||||||
|
out_previous_touch.states[i] = current_touch.states[i];
|
||||||
|
}
|
||||||
|
out_finger_map.finger_count = finger_count;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero out unused entries
|
||||||
|
for (std::size_t i = finger_count; i < MaxFingers; i++) {
|
||||||
|
out_finger_map.finger_ids[i] = 0;
|
||||||
|
out_previous_touch.states[i] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
out_previous_touch.sampling_number = current_touch.sampling_number;
|
||||||
|
out_previous_touch.entry_count = finger_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::ReadTouchInput() {
|
||||||
|
previous_touch_state = current_touch_state;
|
||||||
|
|
||||||
|
if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) {
|
||||||
|
touch_driver->WaitForDummyInput();
|
||||||
|
} else {
|
||||||
|
touch_driver->WaitForInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
touch_driver->GetNextTouchState(current_touch_state);
|
||||||
|
SanitizeInput(current_touch_state);
|
||||||
|
current_touch_state.sampling_number = sample_number;
|
||||||
|
sample_number++;
|
||||||
|
|
||||||
|
if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) {
|
||||||
|
const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count);
|
||||||
|
current_touch_state.entry_count = static_cast<s32>(finger_count);
|
||||||
|
for (std::size_t i = 0; i < finger_count; i++) {
|
||||||
|
current_touch_state.states[i] = auto_pilot.state[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (std::size_t i = 0; i < finger_count; i++) {
|
||||||
|
if (auto_pilot.state[i].attribute.end_touch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto_pilot.state[i].attribute.raw = 0;
|
||||||
|
auto_pilot.state[index] = auto_pilot.state[i];
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_pilot.count = index;
|
||||||
|
for (std::size_t i = index; i < auto_pilot.state.size(); i++) {
|
||||||
|
auto_pilot.state[i] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
|
||||||
|
auto& state = current_touch_state.states[i];
|
||||||
|
state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) +
|
||||||
|
(offset.x * static_cast<f32>(TouchSensorWidth)));
|
||||||
|
state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) +
|
||||||
|
(offset.x * static_cast<f32>(TouchSensorHeight)));
|
||||||
|
state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x));
|
||||||
|
state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
|
||||||
|
const auto& old_state = current_touch_state.states[i];
|
||||||
|
auto& state = current_touch_state.states[index];
|
||||||
|
if ((TouchSensorWidth <= old_state.position.x) ||
|
||||||
|
(TouchSensorHeight <= old_state.position.y)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
state = old_state;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
current_touch_state.entry_count = static_cast<s32>(index);
|
||||||
|
|
||||||
|
SanitizeInput(current_touch_state);
|
||||||
|
|
||||||
|
std::scoped_lock lock{*input_mutex};
|
||||||
|
if (current_touch_state.entry_count == previous_touch_state.entry_count) {
|
||||||
|
if (current_touch_state.entry_count < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool has_moved = false;
|
||||||
|
for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count);
|
||||||
|
i++) {
|
||||||
|
s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) -
|
||||||
|
static_cast<s32>(previous_touch_state.states[i].position.x));
|
||||||
|
s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) -
|
||||||
|
static_cast<s32>(previous_touch_state.states[i].position.y));
|
||||||
|
if (delta_x > 1 || delta_y > 1) {
|
||||||
|
has_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!has_moved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input_event->Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::OnTouchUpdate(s64 timestamp) {
|
||||||
|
if (global_ref_counter == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadTouchInput();
|
||||||
|
gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
std::scoped_lock lock{*shared_mutex};
|
||||||
|
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
|
||||||
|
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
|
||||||
|
TouchAruidData& data = aruid_data[aruid_index];
|
||||||
|
|
||||||
|
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
|
||||||
|
data = {};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.aruid != applet_data->aruid) {
|
||||||
|
data = {};
|
||||||
|
data.aruid = applet_data->aruid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gesture_ref_counter != 0) {
|
||||||
|
if (!applet_data->flag.enable_touchscreen) {
|
||||||
|
gesture_state = {};
|
||||||
|
}
|
||||||
|
if (gesture_handler.NeedsUpdate()) {
|
||||||
|
gesture_handler.UpdateGestureState(gesture_state, timestamp);
|
||||||
|
auto& gesture_shared = applet_data->shared_memory_format->gesture;
|
||||||
|
gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (touch_ref_counter != 0) {
|
||||||
|
auto touch_mode = data.finger_map.touch_mode;
|
||||||
|
if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) {
|
||||||
|
touch_mode = default_touch_screen_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applet_resource->GetActiveAruid() == applet_data->aruid &&
|
||||||
|
touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized &&
|
||||||
|
handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) {
|
||||||
|
touch_driver->SetTouchMode(touch_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& touch_shared = applet_data->shared_memory_format->touch_screen;
|
||||||
|
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state,
|
||||||
|
applet_data->flag.enable_touchscreen.As<bool>());
|
||||||
|
touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchResource::SanitizeInput(TouchScreenState& state) const {
|
||||||
|
for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) {
|
||||||
|
auto& entry = state.states[i];
|
||||||
|
entry.position.x =
|
||||||
|
std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1);
|
||||||
|
entry.position.y =
|
||||||
|
std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1);
|
||||||
|
entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter);
|
||||||
|
entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter);
|
||||||
|
entry.rotation_angle =
|
||||||
|
std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -0,0 +1,126 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/point.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "hid_core/hid_types.h"
|
||||||
|
#include "hid_core/resources/touch_screen/gesture_handler.h"
|
||||||
|
#include "hid_core/resources/touch_screen/touch_types.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
struct EventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KEvent;
|
||||||
|
} // namespace Kernel
|
||||||
|
|
||||||
|
namespace Service::Set {
|
||||||
|
class ISystemSettingsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
class AppletResource;
|
||||||
|
class TouchSharedMemoryManager;
|
||||||
|
class TouchDriver;
|
||||||
|
struct HandheldConfig;
|
||||||
|
|
||||||
|
class TouchResource {
|
||||||
|
public:
|
||||||
|
TouchResource(Core::System& system_);
|
||||||
|
~TouchResource();
|
||||||
|
|
||||||
|
Result ActivateTouch();
|
||||||
|
Result ActivateTouch(u64 aruid);
|
||||||
|
|
||||||
|
Result ActivateGesture();
|
||||||
|
Result ActivateGesture(u64 aruid, u32 basic_gesture_id);
|
||||||
|
|
||||||
|
Result DeactivateTouch();
|
||||||
|
Result DeactivateGesture();
|
||||||
|
|
||||||
|
bool IsTouchActive() const;
|
||||||
|
bool IsGestureActive() const;
|
||||||
|
|
||||||
|
void SetTouchDriver(std::shared_ptr<TouchDriver> driver);
|
||||||
|
void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex);
|
||||||
|
void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex);
|
||||||
|
void SetHandheldConfig(std::shared_ptr<HandheldConfig> config);
|
||||||
|
void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event);
|
||||||
|
|
||||||
|
Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
|
||||||
|
Result UnsetTouchScreenAutoPilotState();
|
||||||
|
|
||||||
|
Result RequestNextTouchInput();
|
||||||
|
Result RequestNextDummyInput();
|
||||||
|
|
||||||
|
Result ProcessTouchScreenAutoTune();
|
||||||
|
void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
|
||||||
|
Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);
|
||||||
|
|
||||||
|
Result SetTouchScreenConfiguration(
|
||||||
|
const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid);
|
||||||
|
Result GetTouchScreenConfiguration(
|
||||||
|
Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const;
|
||||||
|
|
||||||
|
Result SetTouchScreenDefaultConfiguration(
|
||||||
|
const Core::HID::TouchScreenConfigurationForNx& touch_configuration);
|
||||||
|
Result GetTouchScreenDefaultConfiguration(
|
||||||
|
Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const;
|
||||||
|
|
||||||
|
void OnTouchUpdate(s64 timestamp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result Finalize();
|
||||||
|
|
||||||
|
void StorePreviousTouchState(TouchScreenState& out_previous_touch,
|
||||||
|
TouchFingerMap& out_finger_map,
|
||||||
|
const TouchScreenState& current_touch,
|
||||||
|
bool is_touch_enabled) const;
|
||||||
|
void ReadTouchInput();
|
||||||
|
|
||||||
|
void SanitizeInput(TouchScreenState& state) const;
|
||||||
|
|
||||||
|
s32 global_ref_counter{};
|
||||||
|
s32 gesture_ref_counter{};
|
||||||
|
s32 touch_ref_counter{};
|
||||||
|
bool is_initalized{};
|
||||||
|
u64 sample_number{};
|
||||||
|
|
||||||
|
// External resources
|
||||||
|
std::shared_ptr<Core::Timing::EventType> timer_event{nullptr};
|
||||||
|
std::shared_ptr<TouchDriver> touch_driver{nullptr};
|
||||||
|
std::shared_ptr<AppletResource> applet_resource{nullptr};
|
||||||
|
std::recursive_mutex* shared_mutex{nullptr};
|
||||||
|
std::shared_ptr<HandheldConfig> handheld_config{nullptr};
|
||||||
|
Kernel::KEvent* input_event{nullptr};
|
||||||
|
std::mutex* input_mutex{nullptr};
|
||||||
|
|
||||||
|
// Internal state
|
||||||
|
TouchScreenState current_touch_state{};
|
||||||
|
TouchScreenState previous_touch_state{};
|
||||||
|
GestureState gesture_state{};
|
||||||
|
bool is_auto_pilot_initialized{};
|
||||||
|
AutoPilotState auto_pilot{};
|
||||||
|
GestureHandler gesture_handler{};
|
||||||
|
std::array<TouchAruidData, 0x20> aruid_data{};
|
||||||
|
Common::Point<f32> magnification{1.0f, 1.0f};
|
||||||
|
Common::Point<f32> offset{0.0f, 0.0f};
|
||||||
|
Core::HID::TouchScreenModeForNx default_touch_screen_mode{
|
||||||
|
Core::HID::TouchScreenModeForNx::Finger};
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -13,8 +13,20 @@
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
static constexpr std::size_t MAX_FINGERS = 16;
|
constexpr std::size_t MaxFingers = 16;
|
||||||
static constexpr size_t MAX_POINTS = 4;
|
constexpr std::size_t MaxPoints = 4;
|
||||||
|
constexpr u32 TouchSensorWidth = 1280;
|
||||||
|
constexpr u32 TouchSensorHeight = 720;
|
||||||
|
constexpr s32 MaxRotationAngle = 270;
|
||||||
|
constexpr u32 MaxTouchDiameter = 30;
|
||||||
|
constexpr u32 TouchBorders = 15;
|
||||||
|
|
||||||
|
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
|
||||||
|
constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s
|
||||||
|
constexpr f32 AngleThreshold = 0.015f; // Threshold in radians
|
||||||
|
constexpr f32 PinchThreshold = 0.5f; // Threshold in pixels
|
||||||
|
constexpr f32 PressDelay = 0.5f; // Time in seconds
|
||||||
|
constexpr f32 DoubleTapDelay = 0.35f; // Time in seconds
|
||||||
|
|
||||||
// This is nn::hid::GestureType
|
// This is nn::hid::GestureType
|
||||||
enum class GestureType : u32 {
|
enum class GestureType : u32 {
|
||||||
|
@ -28,6 +40,7 @@ enum class GestureType : u32 {
|
||||||
Swipe, // Fast press movement and release of a single point
|
Swipe, // Fast press movement and release of a single point
|
||||||
Pinch, // All points moving away/closer to the midpoint
|
Pinch, // All points moving away/closer to the midpoint
|
||||||
Rotate, // All points rotating from the midpoint
|
Rotate, // All points rotating from the midpoint
|
||||||
|
GestureTypeMax = Rotate,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is nn::hid::GestureDirection
|
// This is nn::hid::GestureDirection
|
||||||
|
@ -69,7 +82,7 @@ struct GestureState {
|
||||||
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
||||||
|
|
||||||
struct GestureProperties {
|
struct GestureProperties {
|
||||||
std::array<Common::Point<s32>, MAX_POINTS> points{};
|
std::array<Common::Point<s32>, MaxPoints> points{};
|
||||||
std::size_t active_points{};
|
std::size_t active_points{};
|
||||||
Common::Point<s32> mid_point{};
|
Common::Point<s32> mid_point{};
|
||||||
s64 detection_count{};
|
s64 detection_count{};
|
||||||
|
@ -78,13 +91,53 @@ struct GestureProperties {
|
||||||
f32 angle{};
|
f32 angle{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is nn::hid::TouchState
|
||||||
|
struct TouchState {
|
||||||
|
u64 delta_time{};
|
||||||
|
Core::HID::TouchAttribute attribute{};
|
||||||
|
u32 finger{};
|
||||||
|
Common::Point<u32> position{};
|
||||||
|
u32 diameter_x{};
|
||||||
|
u32 diameter_y{};
|
||||||
|
s32 rotation_angle{};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
|
||||||
|
|
||||||
// This is nn::hid::TouchScreenState
|
// This is nn::hid::TouchScreenState
|
||||||
struct TouchScreenState {
|
struct TouchScreenState {
|
||||||
s64 sampling_number{};
|
s64 sampling_number{};
|
||||||
s32 entry_count{};
|
s32 entry_count{};
|
||||||
INSERT_PADDING_BYTES(4); // Reserved
|
INSERT_PADDING_BYTES(4); // Reserved
|
||||||
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
|
std::array<TouchState, MaxFingers> states{};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
|
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
|
||||||
|
|
||||||
|
struct TouchFingerMap {
|
||||||
|
s32 finger_count{};
|
||||||
|
Core::HID::TouchScreenModeForNx touch_mode;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
std::array<u32, MaxFingers> finger_ids{};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size");
|
||||||
|
|
||||||
|
struct TouchAruidData {
|
||||||
|
u64 aruid;
|
||||||
|
u32 basic_gesture_id;
|
||||||
|
u64 used_1;
|
||||||
|
u64 used_2;
|
||||||
|
u64 used_3;
|
||||||
|
u64 used_4;
|
||||||
|
GestureType gesture_type;
|
||||||
|
u16 resolution_width;
|
||||||
|
u16 resolution_height;
|
||||||
|
TouchFingerMap finger_map;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size");
|
||||||
|
|
||||||
|
struct AutoPilotState {
|
||||||
|
u64 count;
|
||||||
|
std::array<TouchState, 16> state;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size");
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
Loading…
Reference in New Issue