diff options
6 files changed, 169 insertions, 17 deletions
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 221c33b86..d383a266d 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -138,7 +138,7 @@ void IRS::RunMomentProcessor(HLERequestContext& ctx) { | |||
| 138 | 138 | ||
| 139 | if (result.IsSuccess()) { | 139 | if (result.IsSuccess()) { |
| 140 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | 140 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); |
| 141 | MakeProcessor<MomentProcessor>(parameters.camera_handle, device); | 141 | MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); |
| 142 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); | 142 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); |
| 143 | image_transfer_processor.SetConfig(parameters.processor_config); | 143 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 144 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | 144 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp index e2f4ae876..c559eb0d5 100644 --- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp +++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp | |||
| @@ -3,16 +3,18 @@ | |||
| 3 | 3 | ||
| 4 | #include <queue> | 4 | #include <queue> |
| 5 | 5 | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 6 | #include "core/hid/emulated_controller.h" | 8 | #include "core/hid/emulated_controller.h" |
| 7 | #include "core/hid/hid_core.h" | 9 | #include "core/hid/hid_core.h" |
| 8 | #include "core/hle/service/hid/irsensor/clustering_processor.h" | 10 | #include "core/hle/service/hid/irsensor/clustering_processor.h" |
| 9 | 11 | ||
| 10 | namespace Service::IRS { | 12 | namespace Service::IRS { |
| 11 | ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, | 13 | ClusteringProcessor::ClusteringProcessor(Core::System& system_, |
| 12 | Core::IrSensor::DeviceFormat& device_format, | 14 | Core::IrSensor::DeviceFormat& device_format, |
| 13 | std::size_t npad_index) | 15 | std::size_t npad_index) |
| 14 | : device{device_format} { | 16 | : device{device_format}, system{system_} { |
| 15 | npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); | 17 | npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); |
| 16 | 18 | ||
| 17 | device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; | 19 | device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; |
| 18 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | 20 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
| @@ -48,7 +50,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty | |||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | next_state = {}; | 52 | next_state = {}; |
| 51 | const auto camera_data = npad_device->GetCamera(); | 53 | const auto& camera_data = npad_device->GetCamera(); |
| 52 | auto filtered_image = camera_data.data; | 54 | auto filtered_image = camera_data.data; |
| 53 | 55 | ||
| 54 | RemoveLowIntensityData(filtered_image); | 56 | RemoveLowIntensityData(filtered_image); |
| @@ -83,7 +85,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty | |||
| 83 | } | 85 | } |
| 84 | 86 | ||
| 85 | next_state.sampling_number = camera_data.sample; | 87 | next_state.sampling_number = camera_data.sample; |
| 86 | next_state.timestamp = next_state.timestamp + 131; | 88 | next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count(); |
| 87 | next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | 89 | next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
| 88 | shared_memory->clustering_lifo.WriteNextEntry(next_state); | 90 | shared_memory->clustering_lifo.WriteNextEntry(next_state); |
| 89 | 91 | ||
| @@ -202,14 +204,14 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster( | |||
| 202 | } | 204 | } |
| 203 | 205 | ||
| 204 | u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { | 206 | u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { |
| 205 | if ((y * width) + x > data.size()) { | 207 | if ((y * width) + x >= data.size()) { |
| 206 | return 0; | 208 | return 0; |
| 207 | } | 209 | } |
| 208 | return data[(y * width) + x]; | 210 | return data[(y * width) + x]; |
| 209 | } | 211 | } |
| 210 | 212 | ||
| 211 | void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { | 213 | void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { |
| 212 | if ((y * width) + x > data.size()) { | 214 | if ((y * width) + x >= data.size()) { |
| 213 | return; | 215 | return; |
| 214 | } | 216 | } |
| 215 | data[(y * width) + x] = value; | 217 | data[(y * width) + x] = value; |
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h index dc01a8ea7..83f34734a 100644 --- a/src/core/hle/service/hid/irsensor/clustering_processor.h +++ b/src/core/hle/service/hid/irsensor/clustering_processor.h | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | #include "core/hle/service/hid/irs_ring_lifo.h" | 8 | #include "core/hle/service/hid/irs_ring_lifo.h" |
| 9 | #include "core/hle/service/hid/irsensor/processor_base.h" | 9 | #include "core/hle/service/hid/irsensor/processor_base.h" |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Core::HID { | 15 | namespace Core::HID { |
| 12 | class EmulatedController; | 16 | class EmulatedController; |
| 13 | } // namespace Core::HID | 17 | } // namespace Core::HID |
| @@ -15,8 +19,7 @@ class EmulatedController; | |||
| 15 | namespace Service::IRS { | 19 | namespace Service::IRS { |
| 16 | class ClusteringProcessor final : public ProcessorBase { | 20 | class ClusteringProcessor final : public ProcessorBase { |
| 17 | public: | 21 | public: |
| 18 | explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, | 22 | explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, |
| 19 | Core::IrSensor::DeviceFormat& device_format, | ||
| 20 | std::size_t npad_index); | 23 | std::size_t npad_index); |
| 21 | ~ClusteringProcessor() override; | 24 | ~ClusteringProcessor() override; |
| 22 | 25 | ||
| @@ -106,5 +109,7 @@ private: | |||
| 106 | Core::IrSensor::DeviceFormat& device; | 109 | Core::IrSensor::DeviceFormat& device; |
| 107 | Core::HID::EmulatedController* npad_device; | 110 | Core::HID::EmulatedController* npad_device; |
| 108 | int callback_key{}; | 111 | int callback_key{}; |
| 112 | |||
| 113 | Core::System& system; | ||
| 109 | }; | 114 | }; |
| 110 | } // namespace Service::IRS | 115 | } // namespace Service::IRS |
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp index 803a6277c..22067a591 100644 --- a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp | |||
| @@ -49,7 +49,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType | |||
| 49 | return; | 49 | return; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | const auto camera_data = npad_device->GetCamera(); | 52 | const auto& camera_data = npad_device->GetCamera(); |
| 53 | 53 | ||
| 54 | // This indicates how much ambient light is present | 54 | // This indicates how much ambient light is present |
| 55 | processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | 55 | processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp index dbaca420a..cf045bda7 100644 --- a/src/core/hle/service/hid/irsensor/moment_processor.cpp +++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp | |||
| @@ -1,24 +1,137 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hid/emulated_controller.h" | ||
| 7 | #include "core/hid/hid_core.h" | ||
| 4 | #include "core/hle/service/hid/irsensor/moment_processor.h" | 8 | #include "core/hle/service/hid/irsensor/moment_processor.h" |
| 5 | 9 | ||
| 6 | namespace Service::IRS { | 10 | namespace Service::IRS { |
| 7 | MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) | 11 | static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30; |
| 8 | : device(device_format) { | 12 | static constexpr std::size_t ImageWidth = 40; |
| 13 | static constexpr std::size_t ImageHeight = 30; | ||
| 14 | |||
| 15 | MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, | ||
| 16 | std::size_t npad_index) | ||
| 17 | : device(device_format), system{system_} { | ||
| 18 | npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); | ||
| 19 | |||
| 9 | device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; | 20 | device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; |
| 10 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | 21 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
| 11 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | 22 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
| 23 | |||
| 24 | shared_memory = std::construct_at( | ||
| 25 | reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data)); | ||
| 26 | |||
| 27 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 28 | .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, | ||
| 29 | .is_npad_service = true, | ||
| 30 | }; | ||
| 31 | callback_key = npad_device->SetCallback(engine_callback); | ||
| 12 | } | 32 | } |
| 13 | 33 | ||
| 14 | MomentProcessor::~MomentProcessor() = default; | 34 | MomentProcessor::~MomentProcessor() { |
| 35 | npad_device->DeleteCallback(callback_key); | ||
| 36 | }; | ||
| 15 | 37 | ||
| 16 | void MomentProcessor::StartProcessor() {} | 38 | void MomentProcessor::StartProcessor() { |
| 39 | device.camera_status = Core::IrSensor::IrCameraStatus::Available; | ||
| 40 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; | ||
| 41 | } | ||
| 17 | 42 | ||
| 18 | void MomentProcessor::SuspendProcessor() {} | 43 | void MomentProcessor::SuspendProcessor() {} |
| 19 | 44 | ||
| 20 | void MomentProcessor::StopProcessor() {} | 45 | void MomentProcessor::StopProcessor() {} |
| 21 | 46 | ||
| 47 | void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { | ||
| 48 | if (type != Core::HID::ControllerTriggerType::IrSensor) { | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | |||
| 52 | next_state = {}; | ||
| 53 | const auto& camera_data = npad_device->GetCamera(); | ||
| 54 | |||
| 55 | const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width); | ||
| 56 | const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height); | ||
| 57 | const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x); | ||
| 58 | const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y); | ||
| 59 | |||
| 60 | const std::size_t block_width = window_width / Columns; | ||
| 61 | const std::size_t block_height = window_height / Rows; | ||
| 62 | |||
| 63 | for (std::size_t row = 0; row < Rows; row++) { | ||
| 64 | for (std::size_t column = 0; column < Columns; column++) { | ||
| 65 | const size_t x_pos = (column * block_width) + window_start_x; | ||
| 66 | const size_t y_pos = (row * block_height) + window_start_y; | ||
| 67 | auto& statistic = next_state.statistic[column + (row * Columns)]; | ||
| 68 | statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | next_state.sampling_number = camera_data.sample; | ||
| 73 | next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 74 | next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | ||
| 75 | shared_memory->moment_lifo.WriteNextEntry(next_state); | ||
| 76 | |||
| 77 | if (!IsProcessorActive()) { | ||
| 78 | StartProcessor(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { | ||
| 83 | if ((y * ImageWidth) + x >= data.size()) { | ||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | return data[(y * ImageWidth) + x]; | ||
| 87 | } | ||
| 88 | |||
| 89 | MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data, | ||
| 90 | std::size_t start_x, | ||
| 91 | std::size_t start_y, | ||
| 92 | std::size_t width, | ||
| 93 | std::size_t height) const { | ||
| 94 | // The actual implementation is always 320x240 | ||
| 95 | static constexpr std::size_t RealWidth = 320; | ||
| 96 | static constexpr std::size_t RealHeight = 240; | ||
| 97 | static constexpr std::size_t Threshold = 30; | ||
| 98 | MomentStatistic statistic{}; | ||
| 99 | std::size_t active_points{}; | ||
| 100 | |||
| 101 | // Sum all data points on the block that meet with the threshold | ||
| 102 | for (std::size_t y = 0; y < width; y++) { | ||
| 103 | for (std::size_t x = 0; x < height; x++) { | ||
| 104 | const size_t x_pos = x + start_x; | ||
| 105 | const size_t y_pos = y + start_y; | ||
| 106 | const auto pixel = | ||
| 107 | GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight); | ||
| 108 | |||
| 109 | if (pixel < Threshold) { | ||
| 110 | continue; | ||
| 111 | } | ||
| 112 | |||
| 113 | statistic.average_intensity += pixel; | ||
| 114 | |||
| 115 | statistic.centroid.x += static_cast<float>(x_pos); | ||
| 116 | statistic.centroid.y += static_cast<float>(y_pos); | ||
| 117 | |||
| 118 | active_points++; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | // Return an empty field if no points were available | ||
| 123 | if (active_points == 0) { | ||
| 124 | return {}; | ||
| 125 | } | ||
| 126 | |||
| 127 | // Finally calculate the actual centroid and average intensity | ||
| 128 | statistic.centroid.x /= static_cast<float>(active_points); | ||
| 129 | statistic.centroid.y /= static_cast<float>(active_points); | ||
| 130 | statistic.average_intensity /= static_cast<f32>(width * height); | ||
| 131 | |||
| 132 | return statistic; | ||
| 133 | } | ||
| 134 | |||
| 22 | void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { | 135 | void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { |
| 23 | current_config.camera_config.exposure_time = config.camera_config.exposure_time; | 136 | current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
| 24 | current_config.camera_config.gain = config.camera_config.gain; | 137 | current_config.camera_config.gain = config.camera_config.gain; |
| @@ -29,6 +142,8 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf | |||
| 29 | current_config.preprocess = | 142 | current_config.preprocess = |
| 30 | static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); | 143 | static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); |
| 31 | current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; | 144 | current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; |
| 145 | |||
| 146 | npad_device->SetCameraFormat(format); | ||
| 32 | } | 147 | } |
| 33 | 148 | ||
| 34 | } // namespace Service::IRS | 149 | } // namespace Service::IRS |
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h index d4bd22e0f..398cfbdc1 100644 --- a/src/core/hle/service/hid/irsensor/moment_processor.h +++ b/src/core/hle/service/hid/irsensor/moment_processor.h | |||
| @@ -6,12 +6,22 @@ | |||
| 6 | #include "common/bit_field.h" | 6 | #include "common/bit_field.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hid/irs_types.h" | 8 | #include "core/hid/irs_types.h" |
| 9 | #include "core/hle/service/hid/irs_ring_lifo.h" | ||
| 9 | #include "core/hle/service/hid/irsensor/processor_base.h" | 10 | #include "core/hle/service/hid/irsensor/processor_base.h" |
| 10 | 11 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | class EmulatedController; | ||
| 18 | } // namespace Core::HID | ||
| 19 | |||
| 11 | namespace Service::IRS { | 20 | namespace Service::IRS { |
| 12 | class MomentProcessor final : public ProcessorBase { | 21 | class MomentProcessor final : public ProcessorBase { |
| 13 | public: | 22 | public: |
| 14 | explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); | 23 | explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, |
| 24 | std::size_t npad_index); | ||
| 15 | ~MomentProcessor() override; | 25 | ~MomentProcessor() override; |
| 16 | 26 | ||
| 17 | // Called when the processor is initialized | 27 | // Called when the processor is initialized |
| @@ -27,6 +37,9 @@ public: | |||
| 27 | void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); | 37 | void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); |
| 28 | 38 | ||
| 29 | private: | 39 | private: |
| 40 | static constexpr std::size_t Columns = 8; | ||
| 41 | static constexpr std::size_t Rows = 6; | ||
| 42 | |||
| 30 | // This is nn::irsensor::MomentProcessorConfig | 43 | // This is nn::irsensor::MomentProcessorConfig |
| 31 | struct MomentProcessorConfig { | 44 | struct MomentProcessorConfig { |
| 32 | Core::IrSensor::CameraConfig camera_config; | 45 | Core::IrSensor::CameraConfig camera_config; |
| @@ -50,12 +63,29 @@ private: | |||
| 50 | u64 timestamp; | 63 | u64 timestamp; |
| 51 | Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | 64 | Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
| 52 | INSERT_PADDING_BYTES(4); | 65 | INSERT_PADDING_BYTES(4); |
| 53 | std::array<MomentStatistic, 0x30> stadistic; | 66 | std::array<MomentStatistic, Columns * Rows> statistic; |
| 54 | }; | 67 | }; |
| 55 | static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); | 68 | static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); |
| 56 | 69 | ||
| 70 | struct MomentSharedMemory { | ||
| 71 | Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo; | ||
| 72 | }; | ||
| 73 | static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size"); | ||
| 74 | |||
| 75 | void OnControllerUpdate(Core::HID::ControllerTriggerType type); | ||
| 76 | u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const; | ||
| 77 | MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x, | ||
| 78 | std::size_t start_y, std::size_t width, std::size_t height) const; | ||
| 79 | |||
| 80 | MomentSharedMemory* shared_memory = nullptr; | ||
| 81 | MomentProcessorState next_state{}; | ||
| 82 | |||
| 57 | MomentProcessorConfig current_config{}; | 83 | MomentProcessorConfig current_config{}; |
| 58 | Core::IrSensor::DeviceFormat& device; | 84 | Core::IrSensor::DeviceFormat& device; |
| 85 | Core::HID::EmulatedController* npad_device; | ||
| 86 | int callback_key{}; | ||
| 87 | |||
| 88 | Core::System& system; | ||
| 59 | }; | 89 | }; |
| 60 | 90 | ||
| 61 | } // namespace Service::IRS | 91 | } // namespace Service::IRS |